implement wfm demodulator chain

This commit is contained in:
Jakob Ketterl 2020-08-08 21:29:25 +02:00
parent da3f59fb9b
commit 448e266097
6 changed files with 67 additions and 5 deletions

View File

@ -165,6 +165,7 @@ class dsp(object):
def __init__(self, output): def __init__(self, output):
self.samp_rate = 250000 self.samp_rate = 250000
self.output_rate = 11025 self.output_rate = 11025
self.hd_output_rate = 44100
self.fft_size = 1024 self.fft_size = 1024
self.fft_fps = 5 self.fft_fps = 5
self.center_freq = 0 self.center_freq = 0
@ -273,6 +274,13 @@ class dsp(object):
] ]
else: else:
chain += ["csdr convert_f_s16"] chain += ["csdr convert_f_s16"]
elif which == "wfm":
chain += ["csdr fmdemod_quadri_cf", "csdr limit_ff"]
chain += last_decimation_block
chain += [
"csdr deemphasis_wfm_ff {audio_rate} 50e-6",
"csdr convert_f_s16"
]
elif self.isDigitalVoice(which): elif self.isDigitalVoice(which):
chain += ["csdr fmdemod_quadri_cf", "dc_block "] chain += ["csdr fmdemod_quadri_cf", "dc_block "]
chain += last_decimation_block chain += last_decimation_block
@ -305,7 +313,7 @@ class dsp(object):
chain += ["csdr amdemod_cf", "csdr fastdcblock_ff"] chain += ["csdr amdemod_cf", "csdr fastdcblock_ff"]
chain += last_decimation_block chain += last_decimation_block
chain += ["csdr agc_ff", "csdr limit_ff", "csdr convert_f_s16"] chain += ["csdr agc_ff", "csdr limit_ff", "csdr convert_f_s16"]
elif which == "freedv": elif self.isFreeDV(which):
chain += ["csdr realpart_cf"] chain += ["csdr realpart_cf"]
chain += last_decimation_block chain += last_decimation_block
chain += [ chain += [
@ -585,13 +593,18 @@ class dsp(object):
def get_output_rate(self): def get_output_rate(self):
return self.output_rate return self.output_rate
def get_hd_output_rate(self):
return self.hd_output_rate
def get_audio_rate(self): def get_audio_rate(self):
if self.isDigitalVoice() or self.isPacket() or self.isPocsag(): if self.isDigitalVoice() or self.isPacket() or self.isPocsag():
return 48000 return 48000
elif self.isWsjtMode() or self.isJs8(): elif self.isWsjtMode() or self.isJs8():
return 12000 return 12000
elif self.get_demodulator() == "freedv": elif self.isFreeDV():
return 8000 return 8000
elif self.isHdAudio():
return self.get_hd_output_rate()
return self.get_output_rate() return self.get_output_rate()
def isDigitalVoice(self, demodulator=None): def isDigitalVoice(self, demodulator=None):
@ -619,6 +632,16 @@ class dsp(object):
demodulator = self.get_secondary_demodulator() demodulator = self.get_secondary_demodulator()
return demodulator == "pocsag" return demodulator == "pocsag"
def isFreeDV(self, demodulator=None):
if demodulator is None:
demodulator = self.get_demodulator()
return demodulator == "freedv"
def isHdAudio(self, demodulator=None):
if demodulator is None:
demodulator = self.get_demodulator()
return demodulator == "wfm"
def set_output_rate(self, output_rate): def set_output_rate(self, output_rate):
if self.output_rate == output_rate: if self.output_rate == output_rate:
return return
@ -626,6 +649,12 @@ class dsp(object):
self.calculate_decimation() self.calculate_decimation()
self.restart() self.restart()
def set_hd_output_rate(self, hd_output_rate):
if self.hd_output_rate == hd_output_rate:
return
self.hd_output_rate = hd_output_rate
self.restart()
def set_demodulator(self, demodulator): def set_demodulator(self, demodulator):
if demodulator in ["usb", "lsb", "cw"]: if demodulator in ["usb", "lsb", "cw"]:
demodulator = "ssb" demodulator = "ssb"
@ -685,7 +714,7 @@ class dsp(object):
def set_squelch_level(self, squelch_level): def set_squelch_level(self, squelch_level):
self.squelch_level = squelch_level self.squelch_level = squelch_level
# no squelch required on digital voice modes # no squelch required on digital voice modes
actual_squelch = -150 if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.get_demodulator() == "freedv" else self.squelch_level actual_squelch = -150 if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isFreeDV() else self.squelch_level
if self.running: if self.running:
self.pipes["squelch_pipe"].write("%g\n" % (self.convertToLinear(actual_squelch))) self.pipes["squelch_pipe"].write("%g\n" % (self.convertToLinear(actual_squelch)))
@ -818,9 +847,10 @@ class dsp(object):
threading.Thread(target=watch_thread).start() threading.Thread(target=watch_thread).start()
if self.output.supports_type("audio"): audio_type = "hd_audio" if self.isHdAudio() else "audio"
if self.output.supports_type(audio_type):
self.output.send_output( self.output.send_output(
"audio", audio_type,
partial( partial(
self.process.stdout.read, self.process.stdout.read,
self.get_fft_bytes_to_read() if self.demodulator == "fft" else self.get_audio_bytes_to_read(), self.get_fft_bytes_to_read() if self.demodulator == "fft" else self.get_audio_bytes_to_read(),

View File

@ -238,6 +238,28 @@ AudioEngine.prototype.pushAudio = function(data) {
} }
}; };
AudioEngine.prototype.pushHdAudio = function(data) {
if (!this.audioNode) return;
this.audioBytes.add(data.byteLength);
var buffer;
if (this.compression === "adpcm") {
//resampling & ADPCM
buffer = this.audioCodec.decode(new Uint8Array(data));
} else {
buffer = new Int16Array(data);
}
buffer = this.hdResampler.process(buffer);
if (this.audioNode.port) {
// AudioWorklets supported
this.audioNode.port.postMessage(buffer);
} else {
// silently drop excess samples
if (this.getBuffersize() + buffer.length <= this.maxBufferSize) {
this.audioBuffers.push(buffer);
}
}
}
AudioEngine.prototype.setCompression = function(compression) { AudioEngine.prototype.setCompression = function(compression) {
this.compression = compression; this.compression = compression;
}; };

View File

@ -829,6 +829,10 @@ function on_ws_recv(evt) {
secondary_demod_waterfall_add(waterfall_f32); secondary_demod_waterfall_add(waterfall_f32);
} }
break; break;
case 4:
// hd audio data
audioEngine.pushHdAudio(data);
break;
default: default:
console.warn('unknown type of binary message: ' + type) console.warn('unknown type of binary message: ' + type)
} }

View File

@ -299,6 +299,9 @@ class OpenWebRxReceiverClient(OpenWebRxClient):
def write_dsp_data(self, data): def write_dsp_data(self, data):
self.send(bytes([0x02]) + data) self.send(bytes([0x02]) + data)
def write_hd_audio(self, data):
self.send(bytes([0x04]) + data)
def write_s_meter_level(self, level): def write_s_meter_level(self, level):
self.send({"type": "smeter", "value": level}) self.send({"type": "smeter", "value": level})

View File

@ -95,6 +95,7 @@ class DspManager(csdr.output):
self.props.wireProperty("digimodes_fft_size", self.dsp.set_secondary_fft_size), self.props.wireProperty("digimodes_fft_size", self.dsp.set_secondary_fft_size),
self.props.wireProperty("samp_rate", self.dsp.set_samp_rate), self.props.wireProperty("samp_rate", self.dsp.set_samp_rate),
self.props.wireProperty("output_rate", self.dsp.set_output_rate), self.props.wireProperty("output_rate", self.dsp.set_output_rate),
self.props.wireProperty("hd_output_rate", self.dsp.set_hd_output_rate),
self.props.wireProperty("offset_freq", self.dsp.set_offset_freq), self.props.wireProperty("offset_freq", self.dsp.set_offset_freq),
self.props.wireProperty("center_freq", self.dsp.set_center_freq), self.props.wireProperty("center_freq", self.dsp.set_center_freq),
self.props.wireProperty("squelch_level", self.dsp.set_squelch_level), self.props.wireProperty("squelch_level", self.dsp.set_squelch_level),
@ -147,6 +148,7 @@ class DspManager(csdr.output):
logger.debug("adding new output of type %s", t) logger.debug("adding new output of type %s", t)
writers = { writers = {
"audio": self.handler.write_dsp_data, "audio": self.handler.write_dsp_data,
"hd_audio": self.handler.write_hd_audio,
"smeter": self.handler.write_s_meter_level, "smeter": self.handler.write_s_meter_level,
"secondary_fft": self.handler.write_secondary_fft, "secondary_fft": self.handler.write_secondary_fft,
"secondary_demod": self.handler.write_secondary_demod, "secondary_demod": self.handler.write_secondary_demod,

View File

@ -40,6 +40,7 @@ class DigitalMode(Mode):
class Modes(object): class Modes(object):
mappings = [ mappings = [
AnalogMode("nfm", "FM", bandpass=Bandpass(-4000, 4000)), AnalogMode("nfm", "FM", bandpass=Bandpass(-4000, 4000)),
AnalogMode("wfm", "WFM", bandpass=Bandpass(-16000, 16000)),
AnalogMode("am", "AM", bandpass=Bandpass(-4000, 4000)), AnalogMode("am", "AM", bandpass=Bandpass(-4000, 4000)),
AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)), AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)),
AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)), AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)),