| @@ -0,0 +1,25 @@ | |||||
| package recorder | |||||
| import ( | |||||
| "math" | |||||
| "sdr-visual-suite/internal/demod" | |||||
| "sdr-visual-suite/internal/dsp" | |||||
| ) | |||||
| func demodAudioCPU(d demod.Demodulator, iq []complex64, sampleRate int, offset float64, bw float64) ([]float32, int) { | |||||
| shifted := dsp.FreqShift(iq, sampleRate, offset) | |||||
| cutoff := bw / 2 | |||||
| if cutoff < 200 { | |||||
| cutoff = 200 | |||||
| } | |||||
| taps := dsp.LowpassFIR(cutoff, sampleRate, 101) | |||||
| filtered := dsp.ApplyFIR(shifted, taps) | |||||
| decim := int(math.Round(float64(sampleRate) / float64(d.OutputSampleRate()))) | |||||
| if decim < 1 { | |||||
| decim = 1 | |||||
| } | |||||
| dec := dsp.Decimate(filtered, decim) | |||||
| inputRate := sampleRate / decim | |||||
| return d.Demod(dec, inputRate), inputRate | |||||
| } | |||||
| @@ -3,13 +3,11 @@ package recorder | |||||
| import ( | import ( | ||||
| "errors" | "errors" | ||||
| "log" | "log" | ||||
| "math" | |||||
| "path/filepath" | "path/filepath" | ||||
| "sdr-visual-suite/internal/classifier" | "sdr-visual-suite/internal/classifier" | ||||
| "sdr-visual-suite/internal/demod" | "sdr-visual-suite/internal/demod" | ||||
| "sdr-visual-suite/internal/detector" | "sdr-visual-suite/internal/detector" | ||||
| "sdr-visual-suite/internal/dsp" | |||||
| ) | ) | ||||
| func (m *Manager) demodAndWrite(dir string, ev detector.Event, iq []complex64, files map[string]any) error { | func (m *Manager) demodAndWrite(dir string, ev detector.Event, iq []complex64, files map[string]any) error { | ||||
| @@ -44,20 +42,7 @@ func (m *Manager) demodAndWrite(dir string, ev detector.Event, iq []complex64, f | |||||
| } else { | } else { | ||||
| log.Printf("gpudemod: CPU demod fallback used for event %d (%s)", ev.ID, name) | log.Printf("gpudemod: CPU demod fallback used for event %d (%s)", ev.ID, name) | ||||
| } | } | ||||
| shifted := dsp.FreqShift(iq, m.sampleRate, offset) | |||||
| cutoff := bw / 2 | |||||
| if cutoff < 200 { | |||||
| cutoff = 200 | |||||
| } | |||||
| taps := dsp.LowpassFIR(cutoff, m.sampleRate, 101) | |||||
| filtered := dsp.ApplyFIR(shifted, taps) | |||||
| decim := int(math.Round(float64(m.sampleRate) / float64(d.OutputSampleRate()))) | |||||
| if decim < 1 { | |||||
| decim = 1 | |||||
| } | |||||
| dec := dsp.Decimate(filtered, decim) | |||||
| inputRate = m.sampleRate / decim | |||||
| audio = d.Demod(dec, inputRate) | |||||
| audio, inputRate = demodAudioCPU(d, iq, m.sampleRate, offset, bw) | |||||
| } | } | ||||
| wav := filepath.Join(dir, "audio.wav") | wav := filepath.Join(dir, "audio.wav") | ||||
| if err := writeWAV(wav, audio, inputRate, d.Channels()); err != nil { | if err := writeWAV(wav, audio, inputRate, d.Channels()); err != nil { | ||||
| @@ -4,11 +4,9 @@ import ( | |||||
| "bytes" | "bytes" | ||||
| "errors" | "errors" | ||||
| "log" | "log" | ||||
| "math" | |||||
| "time" | "time" | ||||
| "sdr-visual-suite/internal/demod" | "sdr-visual-suite/internal/demod" | ||||
| "sdr-visual-suite/internal/dsp" | |||||
| ) | ) | ||||
| // DemodLive demodulates a recent window and returns WAV bytes. | // DemodLive demodulates a recent window and returns WAV bytes. | ||||
| @@ -61,20 +59,7 @@ func (m *Manager) DemodLive(centerHz float64, bw float64, mode string, seconds i | |||||
| } else { | } else { | ||||
| log.Printf("gpudemod: CPU live demod fallback used (%s)", name) | log.Printf("gpudemod: CPU live demod fallback used (%s)", name) | ||||
| } | } | ||||
| shifted := dsp.FreqShift(segment, m.sampleRate, offset) | |||||
| cutoff := bw / 2 | |||||
| if cutoff < 200 { | |||||
| cutoff = 200 | |||||
| } | |||||
| taps := dsp.LowpassFIR(cutoff, m.sampleRate, 101) | |||||
| filtered := dsp.ApplyFIR(shifted, taps) | |||||
| decim := int(math.Round(float64(m.sampleRate) / float64(d.OutputSampleRate()))) | |||||
| if decim < 1 { | |||||
| decim = 1 | |||||
| } | |||||
| dec := dsp.Decimate(filtered, decim) | |||||
| inputRate = m.sampleRate / decim | |||||
| audio = d.Demod(dec, inputRate) | |||||
| audio, inputRate = demodAudioCPU(d, segment, m.sampleRate, offset, bw) | |||||
| } | } | ||||
| buf := &bytes.Buffer{} | buf := &bytes.Buffer{} | ||||
| if err := writeWAVTo(buf, audio, inputRate, d.Channels()); err != nil { | if err := writeWAVTo(buf, audio, inputRate, d.Channels()); err != nil { | ||||