diff --git a/internal/demod/fm.go b/internal/demod/fm.go index 521e716..e5d7b18 100644 --- a/internal/demod/fm.go +++ b/internal/demod/fm.go @@ -88,11 +88,21 @@ func wfmStereo(iq []complex64, sampleRate int) []float32 { return out } +type RDSBasebandResult struct { + Samples []float32 + SampleRate int +} + // RDSBaseband returns a rough 57k baseband (not decoded). func RDSBaseband(iq []complex64, sampleRate int) []float32 { + return RDSBasebandDecimated(iq, sampleRate).Samples +} + +// RDSBasebandDecimated returns the 57 kHz RDS baseband mixed to near-DC and decimated. +func RDSBasebandDecimated(iq []complex64, sampleRate int) RDSBasebandResult { base := wfmMonoBase(iq) - if len(base) == 0 { - return nil + if len(base) == 0 || sampleRate <= 0 { + return RDSBasebandResult{} } bpHi := dsp.LowpassFIR(60000, sampleRate, 101) bpLo := dsp.LowpassFIR(54000, sampleRate, 101) @@ -104,13 +114,24 @@ func RDSBaseband(iq []complex64, sampleRate int) []float32 { } phase := 0.0 inc := 2 * math.Pi * 57000 / float64(sampleRate) - out := make([]float32, len(base)) + mixed := make([]float32, len(base)) for i := range bpf { phase += inc - out[i] = bpf[i] * float32(math.Cos(phase)) + mixed[i] = bpf[i] * float32(math.Cos(phase)) } lp := dsp.LowpassFIR(2400, sampleRate, 101) - return dsp.ApplyFIRReal(out, lp) + filtered := dsp.ApplyFIRReal(mixed, lp) + targetRate := 4800 + decim := sampleRate / targetRate + if decim < 1 { + decim = 1 + } + actualRate := sampleRate / decim + out := make([]float32, 0, len(filtered)/decim+1) + for i := 0; i < len(filtered); i += decim { + out = append(out, filtered[i]) + } + return RDSBasebandResult{Samples: out, SampleRate: actualRate} } func deemphasis(x []float32, sampleRate int, tau float64) []float32 { diff --git a/internal/recorder/wfm_hybrid.go b/internal/recorder/wfm_hybrid.go index f37460f..c7f942e 100644 --- a/internal/recorder/wfm_hybrid.go +++ b/internal/recorder/wfm_hybrid.go @@ -12,12 +12,12 @@ type wfmHybridResult struct { func demodWFMStereoHybrid(iq []complex64, sampleRate int, offset float64, bw float64) wfmHybridResult { audio, rate := demodAudioCPU(demod.Get("WFM_STEREO"), iq, sampleRate, offset, bw) - rds := demod.RDSBaseband(iq, sampleRate) + rds := demod.RDSBasebandDecimated(iq, sampleRate) return wfmHybridResult{ Audio: audio, AudioRate: rate, Channels: 2, - RDS: rds, - RDSRate: sampleRate, + RDS: rds.Samples, + RDSRate: rds.SampleRate, } }