Przeglądaj źródła

Update streamer

master
Jan Svabenik 22 godzin temu
rodzic
commit
145c964679
1 zmienionych plików z 24 dodań i 25 usunięć
  1. +24
    -25
      internal/recorder/streamer.go

+ 24
- 25
internal/recorder/streamer.go Wyświetl plik

@@ -758,39 +758,38 @@ func (sess *streamSession) processSnippet(snippet []complex64, snipRate int) ([]
if decim1 < 1 {
decim1 = 1
}
// WFM override: force decim1=2 (256kHz) instead of round(512k/192k)=3 (170kHz).
// At decim1=3, Nyquist is 85kHz which clips FM broadcast ±75kHz deviation.
// At decim1=2, Nyquist is 128kHz → full FM deviation + stereo pilot + guard band.
// Bonus: 256000→48000 resampler ratio is L=3/M=16 (96 taps, 1kB) instead of
// the pathological L=24000/M=85333 (768k taps, 6MB) from 170666→48000.
if isWFM && decim1 > 2 && snipRate/2 >= 200000 {
decim1 = 2
}
actualDemodRate := snipRate / decim1
logging.Debug("demod", "rates", "snipRate", snipRate, "decim1", decim1, "actual", actualDemodRate)

var dec []complex64
if decim1 > 1 {
// For WFM: skip the pre-demod anti-alias FIR entirely.
// FM broadcast has ±75kHz deviation. The old cutoff at
// actualDemodRate/2*0.8 = 68kHz was BELOW the FM deviation,
// clipping the modulation and causing amplitude dips that
// the FM discriminator turned into audible clicks (4-5/sec).
// The extraction stage already bandlimited to ±125kHz (BW/2 * bwMult),
// which is sufficient anti-alias protection for the 512k→170k decimation.
//
// For NFM/other: use a cutoff that preserves the full signal bandwidth.
// FIR cutoff: for WFM, use 90kHz (above ±75kHz FM deviation + guard).
// For NFM/other: use standard Nyquist*0.8 cutoff.
cutoff := float64(actualDemodRate) / 2.0 * 0.8
if isWFM {
// WFM: decimate without FIR — extraction filter is sufficient
dec = dsp.DecimateStateful(fullSnip, decim1, &sess.preDemodDecimPhase)
} else {
cutoff := float64(actualDemodRate) / 2.0 * 0.8

// Lazy-init or reinit stateful FIR if parameters changed
if sess.preDemodFIR == nil || sess.preDemodRate != snipRate || sess.preDemodCutoff != cutoff {
taps := dsp.LowpassFIR(cutoff, snipRate, 101)
sess.preDemodFIR = dsp.NewStatefulFIRComplex(taps)
sess.preDemodRate = snipRate
sess.preDemodCutoff = cutoff
sess.preDemodDecim = decim1
sess.preDemodDecimPhase = 0
}
cutoff = 90000
}

filtered := sess.preDemodFIR.ProcessInto(fullSnip, sess.growIQ(len(fullSnip)))
dec = dsp.DecimateStateful(filtered, decim1, &sess.preDemodDecimPhase)
// Lazy-init or reinit stateful FIR if parameters changed
if sess.preDemodFIR == nil || sess.preDemodRate != snipRate || sess.preDemodCutoff != cutoff {
taps := dsp.LowpassFIR(cutoff, snipRate, 101)
sess.preDemodFIR = dsp.NewStatefulFIRComplex(taps)
sess.preDemodRate = snipRate
sess.preDemodCutoff = cutoff
sess.preDemodDecim = decim1
sess.preDemodDecimPhase = 0
}

filtered := sess.preDemodFIR.ProcessInto(fullSnip, sess.growIQ(len(fullSnip)))
dec = dsp.DecimateStateful(filtered, decim1, &sess.preDemodDecimPhase)
} else {
dec = fullSnip
}


Ładowanie…
Anuluj
Zapisz