class _OsmoSDRRXDriver(ExportedState, gr.hier_block2): # Note: Docs for gr-osmosdr are in comments at gr-osmosdr/lib/source_iface.h def __init__(self, osmo_device, source, profile, name, tuning): gr.hier_block2.__init__( self, b'RX ' + str(name), gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) self.__osmo_device = osmo_device self.__source = source self.__profile = profile self.__name = name self.__tuning = tuning self.__antenna_type = EnumT({unicode(name): unicode(name) for name in self.__source.get_antennas()}, strict=True) self.connect(self.__source, self) self.__gains = Gains(source, self) # State of the source that there are no getters for, so we must keep our own copy of self.__track_dc_offset_mode = DCOffsetOff self.__track_iq_balance_mode = IQBalanceOff source.set_dc_offset_mode(self.__track_dc_offset_mode, ch) source.set_iq_balance_mode(self.__track_iq_balance_mode, ch) # Blocks self.__state_while_inactive = {} self.__placeholder = blocks.vector_source_c([]) sample_rate = float(source.get_sample_rate()) self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__usable_bandwidth = tuning.calc_usable_bandwidth(sample_rate) @exported_value(type=SignalType, changes='never') def get_output_type(self): return self.__signal_type # implement IRXDriver def get_tune_delay(self): return self.__profile.tune_delay # implement IRXDriver def get_usable_bandwidth(self): return self.__usable_bandwidth # implement IRXDriver def close(self): self._stop_rx() self.__tuning = None @exported_value( type=QuantityT(unit=units.ppm), changes='this_setter', label='Freq.corr.') def get_correction_ppm(self): return self.__tuning.get_correction_ppm() @setter def set_correction_ppm(self, value): self.__tuning.set_correction_ppm(value) @exported_value(type=ReferenceT(), changes='never') def get_gains(self): return self.__gains @exported_value( type_fn=lambda self: convert_osmosdr_range( self.__source.get_gain_range(ch), unit=units.dB, strict=False), changes='this_setter', label='Gain') def get_gain(self): if self.__source is None: return 0.0 return self.__source.get_gain(ch) @setter def set_gain(self, value): self.__source.set_gain(float(value), ch) # The single gain and individual-stage gain controls have an unspecified relationship to each other. Thus, changing one must poll the other. self.__gains.state_changed() @exported_value( type_fn=lambda self: bool if self.__profile.agc else ConstantT(False), changes='this_setter', label='AGC on') def get_agc(self): if self.__source is None: return False return bool(self.__source.get_gain_mode(ch)) @setter def set_agc(self, value): self.__source.set_gain_mode(bool(value), ch) @exported_value( type_fn=lambda self: self.__antenna_type, changes='this_setter', label='Antenna') def get_antenna(self): if self.__source is None: return '' return unicode(self.__source.get_antenna(ch)) @setter def set_antenna(self, value): # TODO we should have a provision for restricting antenna selection when transmit is possible to avoid hardware damage self.__source.set_antenna(str(self.__antenna_type(value)), ch) # Note: dc_offset_mode has a 'manual' mode we are not yet exposing, which is why the internal tracking is an enum integer but the exported value is a boolean @exported_value( type_fn=lambda self: bool if self.__profile.dc_cancel else ConstantT(False), changes='this_setter', label='Use DC cancellation') def get_dc_cancel(self): return bool(self.__track_dc_offset_mode) @setter def set_dc_cancel(self, value): if value: mode = DCOffsetAutomatic else: mode = DCOffsetOff self.__source.set_dc_offset_mode(mode, ch) self.__track_dc_offset_mode = mode # Note: iq_balance_mode has a 'manual' mode we are not yet exposing, which is why the internal tracking is an enum integer but the exported value is a boolean @exported_value(type=bool, # TODO: detect gr-iqbal changes='this_setter', label='Use IQ balancer') def get_iq_balance(self): return bool(self.__track_iq_balance_mode) @setter def set_iq_balance(self, value): if value: mode = IQBalanceAutomatic else: mode = IQBalanceOff self.__source.set_iq_balance_mode(mode, ch) self.__track_iq_balance_mode = mode # add_zero because zero means automatic setting based on sample rate. # TODO: Display automaticness in the UI rather than having a zero value. @exported_value( type_fn=lambda self: convert_osmosdr_range( self.__source.get_bandwidth_range(ch), unit=units.Hz, add_zero=True), changes='this_setter', label='Analog bandwidth', description='Bandwidth of the analog antialiasing filter.') def get_bandwidth(self): if self.__source is None: return 0.0 return self.__source.get_bandwidth(ch) @setter def set_bandwidth(self, value): self.__source.set_bandwidth(float(value), ch) def notify_reconnecting_or_restarting(self): pass # link to tx driver def _stop_rx(self): self.disconnect_all() self.__state_while_inactive = self.state_to_json() self.__tuning.set_block(None) self.__gains.close() self.__source = None self.connect(self.__placeholder, self) # link to tx driver def _start_rx(self): self.disconnect_all() self.__source = osmosdr.source('numchan=1 ' + self.__osmo_device) self.__source.set_sample_rate(self.__signal_type.get_sample_rate()) self.__tuning.set_block(self.__source) self.__gains = Gains(self.__source, self) self.connect(self.__source, self) self.state_from_json(self.__state_while_inactive)
def test_serial(self): self.assertEqual({u'type': u'ConstantT', u'value': 1}, ConstantT(1).to_json())
def test_run(self): _test_coerce_cases(self, ConstantT(1), [1, 1.0, (None, 1), ('foo', 1)], [])
def test_run(self): _testType(self, ConstantT(1), [1, 1.0, (None, 1), ('foo', 1)], [])