def testUpdateValidator(self): pv = PropertyValidator(PropertyLayer(), {"testkey": StringValidator()}) # this should pass pv["testkey"] = "text" pv.setValidator("testkey", NumberValidator()) # this should raise with self.assertRaises(PropertyValidationError): pv["testkey"] = "text"
def testPassesUnvalidated(self): pm = PropertyLayer() pv = PropertyValidator(pm) pv["testkey"] = "testvalue" self.assertEqual(pv["testkey"], "testvalue") self.assertEqual(pm["testkey"], "testvalue")
def testSetValidator(self): pv = PropertyValidator(PropertyLayer()) pv.setValidator("testkey", NumberValidator()) with self.assertRaises(PropertyValidationError): pv["testkey"] = "text"
def testThrowsErrorOnInvalidValue(self): pv = PropertyValidator(PropertyLayer(), {"testkey": NumberValidator()}) with self.assertRaises(PropertyValidationError): pv["testkey"] = "text"
def testPassesValidValue(self): pv = PropertyValidator(PropertyLayer(), {"testkey": NumberValidator()}) pv["testkey"] = 42 self.assertEqual(pv["testkey"], 42)
def __init__(self, handler, sdrSource): self.handler = handler self.sdrSource = sdrSource self.parsers = { "meta": MetaParser(self.handler), "wsjt_demod": WsjtParser(self.handler), "packet_demod": AprsParser(self.handler), "pocsag_demod": PocsagParser(self.handler), "js8_demod": Js8Parser(self.handler), } self.props = PropertyStack() # local demodulator properties not forwarded to the sdr # ensure strict validation since these can be set from the client # and are used to build executable commands validators = { "output_rate": "int", "hd_output_rate": "int", "squelch_level": "num", "secondary_mod": ModulationValidator(), "low_cut": "num", "high_cut": "num", "offset_freq": "int", "mod": ModulationValidator(), "secondary_offset_freq": "int", "dmr_filter": "int", } self.localProps = PropertyValidator( PropertyLayer().filter(*validators.keys()), validators) self.props.addLayer(0, self.localProps) # properties that we inherit from the sdr self.props.addLayer( 1, self.sdrSource.getProps().filter( "audio_compression", "fft_compression", "digimodes_fft_size", "csdr_dynamic_bufsize", "csdr_print_bufsizes", "csdr_through", "digimodes_enable", "samp_rate", "digital_voice_unvoiced_quality", "temporary_directory", "center_freq", "start_mod", "start_freq", "wfm_deemphasis_tau", )) self.dsp = csdr.dsp(self) self.dsp.nc_port = self.sdrSource.getPort() def set_low_cut(cut): bpf = self.dsp.get_bpf() bpf[0] = cut self.dsp.set_bpf(*bpf) def set_high_cut(cut): bpf = self.dsp.get_bpf() bpf[1] = cut self.dsp.set_bpf(*bpf) def set_dial_freq(key, value): if self.props["center_freq"] is None or self.props[ "offset_freq"] is None: return freq = self.props["center_freq"] + self.props["offset_freq"] for parser in self.parsers.values(): parser.setDialFrequency(freq) if "start_mod" in self.props: self.dsp.set_demodulator(self.props["start_mod"]) mode = Modes.findByModulation(self.props["start_mod"]) if mode and mode.bandpass: self.dsp.set_bpf(mode.bandpass.low_cut, mode.bandpass.high_cut) else: self.dsp.set_bpf(-4000, 4000) if "start_freq" in self.props and "center_freq" in self.props: self.dsp.set_offset_freq(self.props["start_freq"] - self.props["center_freq"]) else: self.dsp.set_offset_freq(0) self.subscriptions = [ self.props.wireProperty("audio_compression", self.dsp.set_audio_compression), self.props.wireProperty("fft_compression", self.dsp.set_fft_compression), 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("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("center_freq", self.dsp.set_center_freq), self.props.wireProperty("squelch_level", self.dsp.set_squelch_level), self.props.wireProperty("low_cut", set_low_cut), self.props.wireProperty("high_cut", set_high_cut), self.props.wireProperty("mod", self.dsp.set_demodulator), self.props.wireProperty("digital_voice_unvoiced_quality", self.dsp.set_unvoiced_quality), self.props.wireProperty("dmr_filter", self.dsp.set_dmr_filter), self.props.wireProperty("temporary_directory", self.dsp.set_temporary_directory), self.props.wireProperty("wfm_deemphasis_tau", self.dsp.set_wfm_deemphasis_tau), self.props.filter("center_freq", "offset_freq").wire(set_dial_freq), ] self.dsp.csdr_dynamic_bufsize = self.props["csdr_dynamic_bufsize"] self.dsp.csdr_print_bufsizes = self.props["csdr_print_bufsizes"] self.dsp.csdr_through = self.props["csdr_through"] if self.props["digimodes_enable"]: def set_secondary_mod(mod): if mod == False: mod = None self.dsp.set_secondary_demodulator(mod) if mod is not None: self.handler.write_secondary_dsp_config({ "secondary_fft_size": self.props["digimodes_fft_size"], "if_samp_rate": self.dsp.if_samp_rate(), "secondary_bw": self.dsp.secondary_bw(), }) self.subscriptions += [ self.props.wireProperty("secondary_mod", set_secondary_mod), self.props.wireProperty("secondary_offset_freq", self.dsp.set_secondary_offset_freq), ] self.startOnAvailable = False self.sdrSource.addClient(self) super().__init__()
def __init__(self, handler, sdrSource): self.handler = handler self.sdrSource = sdrSource self.props = PropertyStack() # current audio mode. should be "audio" or "hd_audio" depending on what demodulatur is in use. self.audioOutput = None # local demodulator properties not forwarded to the sdr # ensure strict validation since these can be set from the client # and are used to build executable commands validators = { "output_rate": "int", "hd_output_rate": "int", "squelch_level": "num", "secondary_mod": ModulationValidator(), "low_cut": "num", "high_cut": "num", "offset_freq": "int", "mod": ModulationValidator(), "secondary_offset_freq": "int", "dmr_filter": "int", } self.localProps = PropertyValidator( PropertyLayer().filter(*validators.keys()), validators) self.props.addLayer(0, self.localProps) # properties that we inherit from the sdr self.props.addLayer( 1, self.sdrSource.getProps().filter( "audio_compression", "fft_compression", "digimodes_fft_size", "samp_rate", "center_freq", "start_mod", "start_freq", "wfm_deemphasis_tau", "digital_voice_codecserver", ), ) # defaults for values that may not be set self.props.addLayer( 2, PropertyLayer( output_rate=12000, hd_output_rate=48000, digital_voice_codecserver="", ).readonly()) self.chain = ClientDemodulatorChain(self._getDemodulator("nfm"), self.props["samp_rate"], self.props["output_rate"], self.props["hd_output_rate"], self.props["audio_compression"], self) self.readers = {} if "start_mod" in self.props: mode = Modes.findByModulation(self.props["start_mod"]) if mode: self.setDemodulator(mode.get_modulation()) if isinstance(mode, DigitalMode): self.setSecondaryDemodulator(mode.modulation) if mode.bandpass: bpf = [mode.bandpass.low_cut, mode.bandpass.high_cut] self.chain.setBandpass(*bpf) else: # TODO modes should be mandatory self.setDemodulator(self.props["start_mod"]) if "start_freq" in self.props and "center_freq" in self.props: self.chain.setFrequencyOffset(self.props["start_freq"] - self.props["center_freq"]) else: self.chain.setFrequencyOffset(0) self.subscriptions = [ self.props.wireProperty("audio_compression", self.setAudioCompression), self.props.wireProperty("fft_compression", self.chain.setSecondaryFftCompression), self.props.wireProperty("fft_voverlap_factor", self.chain.setSecondaryFftOverlapFactor), self.props.wireProperty("fft_fps", self.chain.setSecondaryFftFps), self.props.wireProperty("digimodes_fft_size", self.setSecondaryFftSize), self.props.wireProperty("samp_rate", self.chain.setSampleRate), self.props.wireProperty("output_rate", self.chain.setOutputRate), self.props.wireProperty("hd_output_rate", self.chain.setHdOutputRate), self.props.wireProperty("offset_freq", self.chain.setFrequencyOffset), self.props.wireProperty("center_freq", self.chain.setCenterFrequency), self.props.wireProperty("squelch_level", self.chain.setSquelchLevel), self.props.wireProperty("low_cut", self.chain.setLowCut), self.props.wireProperty("high_cut", self.chain.setHighCut), self.props.wireProperty("mod", self.setDemodulator), self.props.wireProperty("dmr_filter", self.chain.setSlotFilter), self.props.wireProperty("wfm_deemphasis_tau", self.chain.setWfmDeemphasisTau), self.props.wireProperty("secondary_mod", self.setSecondaryDemodulator), self.props.wireProperty("secondary_offset_freq", self.chain.setSecondaryFrequencyOffset), ] # wire power level output buffer = Buffer(Format.FLOAT) self.chain.setPowerWriter(buffer) self.wireOutput("smeter", buffer) # wire meta output buffer = Buffer(Format.CHAR) self.chain.setMetaWriter(buffer) self.wireOutput("meta", buffer) # wire secondary FFT buffer = Buffer(self.chain.getSecondaryFftOutputFormat()) self.chain.setSecondaryFftWriter(buffer) self.wireOutput("secondary_fft", buffer) # wire secondary demodulator buffer = Buffer(Format.CHAR) self.chain.setSecondaryWriter(buffer) self.wireOutput("secondary_demod", buffer) self.startOnAvailable = False self.sdrSource.addClient(self)