コード例 #1
0
ファイル: fft.go プロジェクト: mingzhi/gomath
func XCorrFFT(x1, x2 []float64, circular bool) []float64 {
	// zero padding.
	ftlength := len(x1)
	if !circular {
		length := len(x1) * 2
		var i uint32 = 1
		for ftlength < length {
			ftlength = 1 << i
			i++
		}
	}

	datax1 := make([]complex128, ftlength)
	datax2 := make([]complex128, ftlength)
	for i := 0; i < len(x1); i++ {
		datax1[i] = complex(x2[i%len(x2)], 0)
		datax2[i] = complex(x1[i%len(x1)], 0)
	}

	v1 := fft.FFT(datax1)
	v2 := fft.FFT(datax2)
	temp := []complex128{}
	for i := 0; i < len(v1); i++ {
		v := v1[i] * cmplx.Conj(v2[i])
		temp = append(temp, v)
	}
	v3 := fft.IFFT(temp)

	res := []float64{}
	for i := 0; i < len(x1); i++ {
		res = append(res, real(v3[i]))
	}
	return res
}
コード例 #2
0
// This basically takes the inner product of the FFT of the path treated as a
// complex signal and a linear function with positive values for
// high frequencies and negative values for low frequencies.
func ComputeFrequencyRating(points [][2]float64) float64 {
	// First, find the "center" of the path
	var centerX, centerY float64

	for i := 0; i < len(points); i++ {
		centerX += points[i][0]
		centerY += points[i][1]
	}

	centerX /= float64(len(points))
	centerY /= float64(len(points))

	// Estimate the scale of the path using the mean distance from the center
	var scale float64 = 0
	for i := 0; i < len(points); i++ {
		var xDistance = points[i][0] - centerX
		var yDistance = points[i][1] - centerY
		scale += math.Sqrt(xDistance*xDistance + yDistance*yDistance)
	}
	scale /= float64(len(points))

	// Now turn the path into a complex signal
	// This is distance-naive
	var signal []complex128
	for i := 0; i < len(points); i++ {
		signal = append(signal, complex((points[i][0]-centerX)/scale, (points[i][0]-centerY)/scale))
	}

	// Now take the FFT
	var signalFFT = fft.FFT(signal)

	// Now add up the frequency magnitudes, weighted by frequency
	var frequencyRating float64 = 0
	for i := 0; i < len(signalFFT); i++ {
		var frequencyMagnitude float64
		var frequencyWeight float64

		// Use a simple linear function ranging from -1 to 1
		frequencyWeight = 2 * float64(i) / (float64(len(signalFFT)-1) - 1)

		// The frequency magnitude is the complex magnitude
		var realAmplitude = real(signalFFT[i])
		var imaginaryAmplitude = imag(signalFFT[i])
		frequencyMagnitude = math.Sqrt(realAmplitude*realAmplitude + imaginaryAmplitude*imaginaryAmplitude)

		// Add it to the total
		frequencyRating += frequencyMagnitude * frequencyWeight
	}

	return frequencyRating
}
コード例 #3
0
ファイル: kernel.go プロジェクト: mantyr/go-sound
// TODO - clean up a lot.
func NewCQKernel(params CQParams) *CQKernel {
	// Constructor
	p := Properties{}
	p.sampleRate = params.sampleRate
	p.maxFrequency = params.maxFrequency
	p.binsPerOctave = params.binsPerOctave

	// GenerateKernel
	q := params.q
	atomHopFactor := params.atomHopFactor
	thresh := params.threshold
	bpo := params.binsPerOctave

	p.minFrequency = float64(math.Pow(2, 1/float64(bpo)) * float64(params.maxFrequency) / 2.0)
	p.Q = q / (math.Pow(2, 1.0/float64(bpo)) - 1.0)

	maxNK := float64(int(math.Floor(p.Q*p.sampleRate/p.minFrequency + 0.5)))
	minNK := float64(int(math.Floor(p.Q*p.sampleRate/
		(p.minFrequency*math.Pow(2.0, (float64(bpo)-1.0)/float64(bpo))) + 0.5)))

	if minNK == 0 || maxNK == 0 {
		panic("Kernal minNK or maxNK is 0, can't make kernel")
	}

	p.atomSpacing = round(minNK*atomHopFactor + 0.5)
	p.firstCentre = p.atomSpacing * roundUp(math.Ceil(maxNK/2.0)/float64(p.atomSpacing))
	p.fftSize = nextPowerOf2(p.firstCentre + roundUp(maxNK/2.0))
	p.atomsPerFrame = roundDown(1.0 + (float64(p.fftSize)-math.Ceil(maxNK/2.0)-float64(p.firstCentre))/float64(p.atomSpacing))

	if DEBUG {
		fmt.Printf("atomsPerFrame = %v (q = %v, Q = %v, atomHopFactor = %v, atomSpacing = %v, fftSize = %v, maxNK = %v, firstCentre = %v)\n",
			p.atomsPerFrame, q, p.Q, atomHopFactor, p.atomSpacing, p.fftSize, maxNK, p.firstCentre)
	}

	p.lastCentre = p.firstCentre + (p.atomsPerFrame-1)*p.atomSpacing
	p.fftHop = (p.lastCentre + p.atomSpacing) - p.firstCentre

	if DEBUG {
		fmt.Printf("fftHop = %v\n", p.fftHop)
	}

	dataSize := p.binsPerOctave * p.atomsPerFrame

	kernel := Kernel{
		make([]int, 0, dataSize),
		make([][]complex128, 0, dataSize),
	}

	for k := 1; k <= p.binsPerOctave; k++ {
		nk := int(p.Q*p.sampleRate/(p.minFrequency*math.Pow(2, ((float64(k)-1.0)/float64(bpo)))) + 0.5)

		win := makeWindow(params.window, nk)

		fk := float64(p.minFrequency * math.Pow(2, ((float64(k)-1.0)/float64(bpo))))

		cmplxs := make([]complex128, nk, nk)
		for i := 0; i < nk; i++ {
			arg := (2.0 * math.Pi * fk * float64(i)) / p.sampleRate
			cmplxs[i] = cmplx.Rect(win[i], arg)
		}

		atomOffset := p.firstCentre - roundUp(float64(nk)/2.0)

		for i := 0; i < p.atomsPerFrame; i++ {
			shift := atomOffset + (i * p.atomSpacing)
			cin := make([]complex128, p.fftSize, p.fftSize)
			for j := 0; j < nk; j++ {
				cin[j+shift] = cmplxs[j]
			}

			cout := fft.FFT(cin)

			// Keep this dense for the moment (until after normalisation calculations)
			for j := 0; j < p.fftSize; j++ {
				if cmplx.Abs(cout[j]) < thresh {
					cout[j] = complex(0, 0)
				} else {
					cout[j] = complexTimes(cout[j], 1.0/float64(p.fftSize))
				}
			}

			kernel.origin = append(kernel.origin, 0)
			kernel.data = append(kernel.data, cout)
		}
	}

	if DEBUG {
		fmt.Printf("size = %v * %v (fft size = %v)\n", len(kernel.data), len(kernel.data[0]), p.fftSize)
	}

	// finalizeKernel

	// calculate weight for normalisation
	wx1 := maxidx(kernel.data[0])
	wx2 := maxidx(kernel.data[len(kernel.data)-1])

	subset := make([][]complex128, len(kernel.data), len(kernel.data))
	for i := 0; i < len(kernel.data); i++ {
		subset[i] = make([]complex128, 0, wx2-wx1+1)
	}
	for j := wx1; j <= wx2; j++ {
		for i := 0; i < len(kernel.data); i++ {
			subset[i] = append(subset[i], kernel.data[i][j])
		}
	}

	// Massive hack - precalculate above instead :(
	nrows, ncols := len(subset), len(subset[0])

	square := make([][]complex128, ncols, ncols) // conjugate transpose of subset * subset
	for i := 0; i < ncols; i++ {
		square[i] = make([]complex128, ncols, ncols)
	}

	for j := 0; j < ncols; j++ {
		for i := 0; i < ncols; i++ {
			v := complex(0, 0)
			for k := 0; k < nrows; k++ {
				v += subset[k][i] * cmplx.Conj(subset[k][j])
			}
			square[i][j] = v
		}
	}

	wK := []float64{}
	for i := int(1.0/q + 0.5); i < ncols-int(1.0/q+0.5)-2; i++ {
		wK = append(wK, cmplx.Abs(square[i][i]))
	}

	weight := float64(p.fftHop) / float64(p.fftSize)
	if len(wK) > 0 {
		weight /= mean(wK)
	}
	weight = math.Sqrt(weight)

	if DEBUG {
		fmt.Printf("weight = %v (from %v elements in wK, ncols = %v, q = %v)\n",
			weight, len(wK), ncols, q)
	}

	// apply normalisation weight, make sparse, and store conjugate
	// (we use the adjoint or conjugate transpose of the kernel matrix
	// for the forward transform, the plain kernel for the inverse
	// which we expect to be less common)

	sk := Kernel{
		make([]int, len(kernel.data), len(kernel.data)),
		make([][]complex128, len(kernel.data), len(kernel.data)),
	}
	for i := 0; i < len(kernel.data); i++ {
		sk.origin[i] = 0
		sk.data[i] = []complex128{}

		lastNZ := 0
		for j := len(kernel.data[i]) - 1; j >= 0; j-- {
			if cmplx.Abs(kernel.data[i][j]) != 0 {
				lastNZ = j
				break
			}
		}

		haveNZ := false
		for j := 0; j <= lastNZ; j++ {
			if haveNZ || cmplx.Abs(kernel.data[i][j]) != 0 {
				if !haveNZ {
					sk.origin[i] = j
				}
				haveNZ = true
				sk.data[i] = append(sk.data[i],
					complexTimes(cmplx.Conj(kernel.data[i][j]), weight))
			}
		}
	}

	return &CQKernel{p, &sk}
}
コード例 #4
0
ファイル: fft.go プロジェクト: postfix/gocomm
func ExtFFT_C(samples vlib.VectorC, N int) vlib.VectorC {

	y := vlib.VectorC(fft.FFT(samples))
	y = y.Scale(math.Sqrt(float64(N)))
	return y
}
コード例 #5
0
ファイル: main.go プロジェクト: tescalada/go-KX3-panadapter
func (self *Server) startAudio() error {
	in := make([]int32, self.Config.FftSize)
	stream, err := self.openAudioStream(in)
	if err = stream.Start(); err != nil {
		return err
	}

	go func() {
		defer stream.Close()
		defer stream.Stop()

		fftSize := self.Config.FftSize
		halfFftSize := fftSize / 2
		phaseI := make([]float64, fftSize)
		phaseQ := make([]float64, fftSize)
		complexIQ := make([]complex128, fftSize)
		fftResult := make([]float32, fftSize)
		fftCorrection := func(freq float64) float64 {
			return math.Pow(2.0, freq/41000)
		}
		fftBinBandWidth := stream.Info().SampleRate / float64(fftSize)

		for self.running {
			if err = stream.Read(); err != nil {
				log.Printf("portaudio: stream.Read() failed: %s", err)
				continue
			}

			if len(self.sessions) == 0 {
				continue
			}

			waitingClient := 0
			now := time.Now().UnixNano()
			for _, session := range self.sessions {
				if !session.initialized {
					continue
				}
				if now-session.lastTime < session.rateLimit {
					continue
				}
				waitingClient++
			}

			if waitingClient == 0 {
				continue
			}

			for i := 0; i < len(in); i += 2 {
				// left
				phaseI[i/2] = float64(in[i]) / 0x1000000
				// right
				phaseQ[i/2] = float64(in[i+1]) / 0x1000000
			}

			windowFunc := window.Hamming
			window.Apply(phaseI, windowFunc)
			window.Apply(phaseQ, windowFunc)

			for i := 0; i < fftSize; i++ {
				complexIQ[i] = complex(phaseI[i], phaseQ[i])
			}

			result := fft.FFT(complexIQ)
			// real
			for i := 0; i < halfFftSize; i++ {
				power := math.Sqrt(real(result[i])*real(result[i]) + imag(result[i])*imag(result[i]))
				fftResult[i+halfFftSize] = float32(20 * math.Log10(power*fftCorrection(float64(i)*fftBinBandWidth)))
			}
			// imag
			for i := halfFftSize; i < fftSize; i++ {
				power := math.Sqrt(real(result[i])*real(result[i]) + imag(result[i])*imag(result[i]))
				fftResult[i-halfFftSize] = float32(20 * math.Log10(power*fftCorrection(float64(fftSize-i)*fftBinBandWidth)))
			}

			self.fftResult <- fftResult
		}
	}()
	return nil
}