|
- package dsp
-
- import "math"
-
- // LowpassFIR returns windowed-sinc lowpass taps (Hann).
- func LowpassFIR(cutoffHz float64, sampleRate int, taps int) []float64 {
- if taps%2 == 0 {
- taps++
- }
- out := make([]float64, taps)
- fc := cutoffHz / float64(sampleRate)
- if fc <= 0 {
- return out
- }
- m := float64(taps-1) / 2.0
- for n := 0; n < taps; n++ {
- x := float64(n) - m
- var sinc float64
- if x == 0 {
- sinc = 2 * fc
- } else {
- sinc = math.Sin(2*math.Pi*fc*x) / (math.Pi * x)
- }
- w := 0.5 * (1 - math.Cos(2*math.Pi*float64(n)/float64(taps-1)))
- out[n] = float64(sinc) * w
- }
- return out
- }
-
- // ApplyFIR applies real FIR taps to complex IQ.
- func ApplyFIR(iq []complex64, taps []float64) []complex64 {
- if len(iq) == 0 || len(taps) == 0 {
- return nil
- }
- out := make([]complex64, len(iq))
- n := len(taps)
- for i := 0; i < len(iq); i++ {
- var accR, accI float64
- for k := 0; k < n; k++ {
- idx := i - k
- if idx < 0 {
- break
- }
- v := iq[idx]
- w := taps[k]
- accR += float64(real(v)) * w
- accI += float64(imag(v)) * w
- }
- out[i] = complex(float32(accR), float32(accI))
- }
- return out
- }
-
- // Decimate keeps every nth sample.
- func Decimate(iq []complex64, factor int) []complex64 {
- if factor <= 1 {
- out := make([]complex64, len(iq))
- copy(out, iq)
- return out
- }
- out := make([]complex64, 0, len(iq)/factor+1)
- for i := 0; i < len(iq); i += factor {
- out = append(out, iq[i])
- }
- return out
- }
-
- // DecimateStateful keeps every nth sample, preserving the decimation phase
- // across calls. *phase is the index offset into the next frame where the
- // first output sample should be taken. It is updated on return so that
- // consecutive calls produce a continuous, gap-free decimated stream.
- //
- // Initial value of *phase should be 0.
- func DecimateStateful(iq []complex64, factor int, phase *int) []complex64 {
- if factor <= 1 || phase == nil {
- out := make([]complex64, len(iq))
- copy(out, iq)
- return out
- }
- n := len(iq)
- p := *phase
- if p < 0 {
- p = 0
- }
- out := make([]complex64, 0, (n-p)/factor+1)
- for i := p; i < n; i += factor {
- out = append(out, iq[i])
- }
- // Compute phase for next frame: how many samples past the end of this
- // frame until the next decimation point.
- if n > 0 && p < n {
- lastTaken := p + ((n-1-p)/factor)*factor
- *phase = lastTaken + factor - n
- } else if p >= n {
- // Entire frame was skipped (very short snippet)
- *phase = p - n
- }
- return out
- }
|