def make(self, kind='IQ'): signal_type = SignalType(kind=kind, sample_rate=1000) m = MonitorSink( context=self.context, signal_type=signal_type) self.tb.connect(blocks.null_source(signal_type.get_itemsize()), m) return m
class _OsmoSDRTXDriver(ExportedState, gr.hier_block2): def __init__(self, osmo_device, rx, name, tuning, sample_rate): gr.hier_block2.__init__( self, b'TX ' + str(name), gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) self.__osmo_device = osmo_device self.__rx_driver = rx self.__tuning = tuning self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__sink = None self.__placeholder = blocks.null_sink(gr.sizeof_gr_complex) self.__state_while_inactive = {} self.connect(self, self.__placeholder) # implement ITXDriver def get_input_type(self): return self.__signal_type # implement ITXDriver def close(self): self.disconnect_all() self.__rx_driver = None self.__sink = None self.__tuning = None # implement ITXDriver def notify_reconnecting_or_restarting(self): pass # implement ITXDriver def set_transmitting(self, value, midpoint_hook): self.disconnect_all() if value: self.__rx_driver._stop_rx() midpoint_hook() self.__sink = osmosdr.sink(self.__osmo_device) self.__sink.set_sample_rate(self.__signal_type.get_sample_rate()) self.__tuning.set_block(self.__sink) self.connect(self, self.__sink) self.state_from_json(self.__state_while_inactive) else: self.__state_while_inactive = self.state_to_json() self.__tuning.set_block(None) self.__sink = None self.connect(self, self.__placeholder) midpoint_hook() self.__rx_driver._start_rx()
def __init__(self, source, device_type, lna_path, name, tuning, sample_rate): gr.hier_block2.__init__( self, defaultstr('RX ' + name), gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) self.__source = source self.__name = name self.__tuning = tuning self.connect(self.__source, self) # State of the source that there are no getters for, so we must keep our own copy of self.__track_gain = 50. source.set_gain(int(self.__track_gain), ch) self.__track_bandwidth = max(sample_rate / 2, 1.5e6) source.set_analog_filter(True, self.__track_bandwidth, ch) self.__lna_path_type = EnumT({ LNANONE: 'None', LNAH: 'LNAH', LNAL: 'LNAL', LNAW: 'LNAW', }) if device_type == LimeSDRMini: self.__lna_path_type = EnumT({ LNAH: 'LNAH', LNAW: 'LNAW', }) self.__track_lna_path = lna_path self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__usable_bandwidth = tuning.calc_usable_bandwidth(sample_rate)
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)
def __init__(self, osmo_device, source, profile, name, tuning): gr.hier_block2.__init__( self, 'RX ' + 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.connect(self.__source, self) self.__gains = Gains(source) # Misc state self.dc_state = DCOffsetOff self.iq_state = IQBalanceOff source.set_dc_offset_mode(self.dc_state, ch) # no getter, set to known state source.set_iq_balance_mode(self.iq_state, ch) # no getter, set to known state # 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)
def __init__(self, osmo_device, rx, name, tuning, sample_rate): gr.hier_block2.__init__( self, b'TX ' + str(name), gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) self.__osmo_device = osmo_device self.__rx_driver = rx self.__tuning = tuning self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__sink = None self.__placeholder = blocks.null_sink(gr.sizeof_gr_complex) self.__state_while_inactive = {} self.connect(self, self.__placeholder)
class _LimeSDRRXDriver(ExportedState, gr.hier_block2): # Note: Docs for gr-limesdr are in comments at gr-limesdr/include/limesdr/source.h def __init__(self, source, device_type, lna_path, name, tuning, sample_rate): gr.hier_block2.__init__( self, defaultstr('RX ' + name), gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) self.__source = source self.__name = name self.__tuning = tuning self.connect(self.__source, self) # State of the source that there are no getters for, so we must keep our own copy of self.__track_gain = 50. source.set_gain(int(self.__track_gain), ch) self.__track_bandwidth = max(sample_rate / 2, 1.5e6) source.set_analog_filter(True, self.__track_bandwidth, ch) self.__lna_path_type = EnumT({ LNANONE: 'None', LNAH: 'LNAH', LNAL: 'LNAL', LNAW: 'LNAW', }) if device_type == LimeSDRMini: self.__lna_path_type = EnumT({ LNAH: 'LNAH', LNAW: 'LNAW', }) self.__track_lna_path = lna_path 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): # TODO: Measure this. return 0.07 # implement IRXDriver def get_usable_bandwidth(self): if self.__track_bandwidth: return self.__tuning.calc_usable_bandwidth(self.__track_bandwidth) return self.__usable_bandwidth # implement IRXDriver def close(self): self.disconnect_all() self.__source = None self.__tuning = None @exported_value( type_fn=lambda self: self.__lna_path_type, changes='this_setter', label='LNA Path') def get_lna_path(self): return self.__track_lna_path @setter def set_lna_path(self, lna_path): self.__track_lna_path = int(lna_path) self.__source.set_lna_path(int(lna_path), ch) @exported_value( type_fn=lambda self: RangeT([(0, 70)], unit=units.dB, strict=False), changes='this_setter', label='Gain') def get_gain(self): if self.__source is None: return 0.0 return self.__track_gain @setter def set_gain(self, value): self.__track_gain = int(value) self.__source.set_gain(int(value), ch) # zero means no filter @exported_value( type_fn=lambda self: RangeT([(1e3, min(130e6, self.__signal_type.get_sample_rate()))], unit=units.Hz), changes='this_setter', label='Hardware filter', description='Bandwidth of the analog and digital filters.') def get_bandwidth(self): if self.__source is None: return 0.0 return self.__track_bandwidth @setter def set_bandwidth(self, value): self.__track_bandwidth = float(value) if value == self.__signal_type.get_sample_rate(): self.__source.set_analog_filter(False, 0, ch) self.__source.set_digital_filter(False, 0, ch) return # Analog filter goes down to 1.5e6, digital filter goes arbitrarily low. analog = max(value, 1.5e6) self.__source.set_analog_filter(True, float(analog), ch) self.__source.set_digital_filter(True, float(value), ch) def notify_reconnecting_or_restarting(self): pass
def make(self, kind='IQ'): signal_type = SignalType(kind=kind, sample_rate=1000) m = MonitorSink(context=self.context, signal_type=signal_type) self.tb.connect(blocks.null_source(signal_type.get_itemsize()), m) return m
def get_output_type(self): """implement IDemodulator""" return SignalType(kind='MONO', sample_rate=self.__demod_rate)
def get_output_type(self): return SignalType(kind='IQ', sample_rate=self.__sample_rate_out)
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, defaultstr('RX ' + 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( { six.text_type(name): six.text_type(name) for name in self.__source.get_antennas() }, strict=True) # TODO: is this correct in py3 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 six.text_type(self.__source.get_antenna(ch), 'ascii') @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 get_input_type(self): return SignalType(kind='MONO', sample_rate=self.__audio_rate)
def __init__(self, devices={}, audio_config=None, features=_stub_features): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError('Must have at least one RF device') gr.top_block.__init__(self, "SDR top block") self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = { k: d for k, d in devices.iteritems() if d.can_receive() } self._accessories = accessories = { k: d for k, d in devices.iteritems() if not d.can_receive() } self.source_name = self._sources.keys()[ 0] # arbitrary valid initial value self.__rx_device_type = Enum( {k: v.get_name() or k for (k, v) in self._sources.iteritems()}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType( sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe( self.__start_or_stop_later) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = {} self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(self._sources) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(accessories) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} self.__cpu_calculator = LazyRateCalculator(lambda: time.clock()) # Initialization def hookup_vfo_callback( k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe(lambda: self.__device_vfo_callback(k)) for k, d in devices.iteritems(): hookup_vfo_callback(k, d) self._do_connect()
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 get_input_type(self): return SignalType('IQ', 1)
def get_input_type(self): return SignalType(kind='IQ', sample_rate=10000)
def test_sample_rate(self): self.assertIsInstance(SignalType(kind='IQ', sample_rate=1).get_sample_rate(), float) self.assertIsInstance(no_signal.get_sample_rate(), float) self.assertEquals(123, SignalType(kind='IQ', sample_rate=123).get_sample_rate()) self.assertEquals(0, no_signal.get_sample_rate())
def test_constants(self): self.assertEquals( no_signal, SignalType(kind='NONE', sample_rate=0))
def test_validation(self): self.assertRaises(TypeError, lambda: SignalType(kind='NONE', sample_rate=None)) self.assertRaises(ValueError, lambda: SignalType(kind='NONE', sample_rate=0.1)) self.assertRaises(ValueError, lambda: SignalType(kind='FOO', sample_rate=1.0)) self.assertRaises(ValueError, lambda: SignalType(kind='IQ', sample_rate=-1.0)) self.assertRaises(ValueError, lambda: SignalType(kind='IQ', sample_rate=0.0))
class _OsmoSDRRXDriver(ExportedState, gr.hier_block2): implements(IRXDriver) # 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, 'RX ' + 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.connect(self.__source, self) self.__gains = Gains(source) # Misc state self.dc_state = DCOffsetOff self.iq_state = IQBalanceOff source.set_dc_offset_mode(self.dc_state, ch) # no getter, set to known state source.set_iq_balance_mode(self.iq_state, ch) # no getter, set to known state # 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) def get_output_type(self): return self.__signal_type # implement IRXDriver def get_tune_delay(self): return 0.25 # TODO: make configurable and/or account for as many factors as we can # 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=float) 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_block() def get_gains(self): return self.__gains @exported_value(type_fn=lambda self: convert_osmosdr_range( self.__source.get_gain_range(ch), strict=False)) 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) @exported_value(type_fn=lambda self: bool if self.__profile.agc else Constant(False)) 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: Enum( {unicode(name): unicode(name) for name in self.__source.get_antennas()})) def get_antenna(self): if self.__source is None: return '' return unicode(self.__source.get_antenna(ch)) # TODO review whether set_antenna is safe to expose # Note: dc_cancel has a 'manual' mode we are not yet exposing @exported_value(type_fn=lambda self: bool if self.__profile.dc_cancel else Constant(False)) def get_dc_cancel(self): return bool(self.dc_state) @setter def set_dc_cancel(self, value): if value: mode = DCOffsetAutomatic else: mode = DCOffsetOff self.dc_state = self.__source.set_dc_offset_mode(mode, ch) # Note: iq_balance has a 'manual' mode we are not yet exposing @exported_value(type=bool) # TODO: detect gr-iqbal def get_iq_balance(self): return bool(self.iq_state) @setter def set_iq_balance(self, value): if value: mode = IQBalanceAutomatic else: mode = IQBalanceOff self.iq_state = self.__source.set_iq_balance_mode(mode, ch) # 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), add_zero=True)) 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.connect(self.__source, self) self.state_from_json(self.__state_while_inactive)
class _OsmoSDRTXDriver(ExportedState, gr.hier_block2): implements(ITXDriver) def __init__(self, osmo_device, rx, name, tuning, sample_rate): gr.hier_block2.__init__( self, 'TX ' + name, gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) self.__osmo_device = osmo_device self.__rx_driver = rx self.__tuning = tuning self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__sink = None self.__placeholder = blocks.null_sink(gr.sizeof_gr_complex) self.__state_while_inactive = {} self.connect(self, self.__placeholder) # implement ITXDriver def get_input_type(self): return self.__signal_type # implement ITXDriver def close(self): self.disconnect_all() self.__rx_driver = None self.__sink = None self.__tuning = None # implement ITXDriver def notify_reconnecting_or_restarting(self): pass # implement ITXDriver def set_transmitting(self, value, midpoint_hook): self.disconnect_all() if value: self.__rx_driver._stop_rx() midpoint_hook() self.__sink = osmosdr.sink(self.__osmo_device) self.__sink.set_sample_rate(self.__signal_type.get_sample_rate()) self.__tuning.set_block(self.__sink) self.connect(self, self.__sink) self.state_from_json(self.__state_while_inactive) else: self.__state_while_inactive = self.state_to_json() self.__tuning.set_block(None) self.__sink = None self.connect(self, self.__placeholder) midpoint_hook() self.__rx_driver._start_rx()
def __init__(self, devices={}, audio_config=None, stereo=True): if not len(devices) > 0: raise ValueError('Must have at least one RF device') #for key, audio_device in audio_devices.iteritems(): # if key == CLIENT_AUDIO_DEVICE: # raise ValueError('The name %r for an audio device is reserved' % (key,)) # if not audio_device.can_transmit(): # raise ValueError('Audio device %r is not an output' % (key,)) if audio_config is not None: # quick kludge placeholder -- currently a Device-device can't be stereo so we have a placeholder thing audio_device_name, audio_sample_rate = audio_config audio_devices = { 'server': (audio_sample_rate, audio.sink(audio_sample_rate, audio_device_name, False)) } else: audio_devices = {} gr.top_block.__init__(self, "SDR top block") self.__unpaused = True # user state self.__running = False # actually started # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = { k: d for k, d in devices.iteritems() if d.can_receive() } accessories = { k: d for k, d in devices.iteritems() if not d.can_receive() } self.source_name = self._sources.keys()[ 0] # arbitrary valid initial value # Audio early setup self.__audio_devices = audio_devices # must be before contexts # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) self.source = None self.__rx_driver = None self.__source_tune_subscription = None self.monitor = MonitorSink( signal_type=SignalType( sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = {} self._receiver_valid = {} self.__shared_objects = {} # kludge for using collection like block - TODO: better architecture self.sources = CollectionState(self._sources) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(accessories) # TODO: better name than "shared objects" self.shared_objects = CollectionState(self.__shared_objects, dynamic=True) # Audio stream bits audio_destination_dict = { key: 'Server' or key for key, device in audio_devices.iteritems() } # temp name till we have proper device objects audio_destination_dict[ CLIENT_AUDIO_DEVICE] = 'Client' # TODO reconsider name self.__audio_destination_type = Enum(audio_destination_dict, strict=True) self.__audio_channels = 2 if stereo else 1 self.audio_queue_sinks = {} self.__audio_buses = { key: BusPlumber(self, self.__audio_channels) for key in audio_destination_dict } # Flags, other state self.__needs_reconnect = True self.input_rate = None self.input_freq = None self.receiver_key_counter = 0 self.receiver_default_state = {} self.last_wall_time = time.time() self.last_cpu_time = time.clock() self.last_cpu_use = 0 self._do_connect()
def __init__(self, name): gr.hier_block2.__init__( self, name, gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) rf_rate = self.rf_rate audio_rate = self.audio_rate self.__noise_level = -22 self.__transmitters = {} self.__transmitters_cs = CollectionState(self.__transmitters, dynamic=True) self.__bus = blocks.add_vcc(1) self.__channel_model = channels.channel_model( noise_voltage=dB(self.__noise_level), frequency_offset=0, epsilon=1.01, # TODO: expose this parameter # taps=..., # TODO: apply something here? ) self.__rotator = blocks.rotator_cc() self.__throttle = blocks.throttle(gr.sizeof_gr_complex, rf_rate) self.connect(self.__bus, self.__throttle, self.__channel_model, self.__rotator, self) signals = [] def add_modulator(freq, key, mode_or_modulator_ctor, **kwargs): if isinstance(mode_or_modulator_ctor, type): mode = None ctor = mode_or_modulator_ctor else: mode = mode_or_modulator_ctor mode_def = lookup_mode(mode) if mode_def is None: # missing plugin, say return ctor = mode_def.mod_class context = None # TODO implement context modulator = ctor(context=context, mode=mode, **kwargs) tx = _SimulatedTransmitter(modulator, audio_rate, rf_rate, freq) self.connect(audio_signal, tx) signals.append(tx) self.__transmitters[key] = tx # Audio input signal pitch = analog.sig_source_f(audio_rate, analog.GR_SAW_WAVE, -1, 2000, 1000) audio_signal = vco = blocks.vco_f(audio_rate, 1, 1) self.connect(pitch, vco) # Channels add_modulator(0.0, 'usb', 'USB') add_modulator(10e3, 'am', 'AM') add_modulator(30e3, 'fm', 'NFM') add_modulator(-30e3, 'vor1', 'VOR', angle=0) add_modulator(-60e3, 'vor2', 'VOR', angle=math.pi / 2) add_modulator( 50e3, 'rtty', 'RTTY', message='The quick brown fox jumped over the lazy dog.\n') add_modulator(80e3, 'chirp', ChirpModulator) bus_input = 0 for signal in signals: self.connect(signal, (self.__bus, bus_input)) bus_input = bus_input + 1 self.__signal_type = SignalType(kind='IQ', sample_rate=rf_rate) self.__usable_bandwidth = Range([(-rf_rate / 2, rf_rate / 2)])
def get_output_type(self): return SignalType(kind='MONO', sample_rate=pipe_rate)
class _LimeSDRRXDriver(ExportedState, gr.hier_block2): # Note: Docs for gr-limesdr are in comments at gr-limesdr/include/limesdr/source.h def __init__(self, source, device_type, lna_path, name, tuning, sample_rate): gr.hier_block2.__init__( self, defaultstr('RX ' + name), gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) self.__source = source self.__name = name self.__tuning = tuning self.connect(self.__source, self) # State of the source that there are no getters for, so we must keep our own copy of self.__track_gain = 50. source.set_gain(int(self.__track_gain), ch) self.__track_bandwidth = max(sample_rate / 2, 1.5e6) source.set_analog_filter(True, self.__track_bandwidth, ch) self.__lna_path_type = EnumT({ LNANONE: 'None', LNAH: 'LNAH', LNAL: 'LNAL', LNAW: 'LNAW', }) if device_type == LimeSDRMini: self.__lna_path_type = EnumT({ LNAH: 'LNAH', LNAW: 'LNAW', }) self.__track_lna_path = lna_path 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): # TODO: Measure this. return 0.07 # implement IRXDriver def get_usable_bandwidth(self): if self.__track_bandwidth: return self.__tuning.calc_usable_bandwidth(self.__track_bandwidth) return self.__usable_bandwidth # implement IRXDriver def close(self): self.disconnect_all() self.__source = None self.__tuning = None @exported_value(type_fn=lambda self: self.__lna_path_type, changes='this_setter', label='LNA Path') def get_lna_path(self): return self.__track_lna_path @setter def set_lna_path(self, lna_path): self.__track_lna_path = int(lna_path) self.__source.set_lna_path(int(lna_path), ch) @exported_value( type_fn=lambda self: RangeT([(0, 70)], unit=units.dB, strict=False), changes='this_setter', label='Gain') def get_gain(self): if self.__source is None: return 0.0 return self.__track_gain @setter def set_gain(self, value): self.__track_gain = int(value) self.__source.set_gain(int(value), ch) # zero means no filter @exported_value(type_fn=lambda self: RangeT([( 1e3, min(130e6, self.__signal_type.get_sample_rate()))], unit=units.Hz), changes='this_setter', label='Hardware filter', description='Bandwidth of the analog and digital filters.') def get_bandwidth(self): if self.__source is None: return 0.0 return self.__track_bandwidth @setter def set_bandwidth(self, value): self.__track_bandwidth = float(value) if value == self.__signal_type.get_sample_rate(): self.__source.set_analog_filter(False, 0, ch) self.__source.set_digital_filter(False, 0, ch) return # Analog filter goes down to 1.5e6, digital filter goes arbitrarily low. analog = max(value, 1.5e6) self.__source.set_analog_filter(True, float(analog), ch) self.__source.set_digital_filter(True, float(value), ch) def notify_reconnecting_or_restarting(self): pass
class _OsmoSDRRXDriver(ExportedState, gr.hier_block2): implements(IRXDriver) # 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, 'RX ' + 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 = Enum({unicode(name): unicode(name) for name in self.__source.get_antennas()}, strict=True) self.connect(self.__source, self) self.__gains = Gains(source) # Misc state self.dc_state = DCOffsetOff self.iq_state = IQBalanceOff source.set_dc_offset_mode(self.dc_state, ch) # no getter, set to known state source.set_iq_balance_mode(self.iq_state, ch) # no getter, set to known state # 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) 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=float) 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_block() def get_gains(self): return self.__gains @exported_value(type_fn=lambda self: convert_osmosdr_range( self.__source.get_gain_range(ch), strict=False)) 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) @exported_value(type_fn=lambda self: bool if self.__profile.agc else Constant(False)) 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) 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_cancel has a 'manual' mode we are not yet exposing @exported_value(type_fn=lambda self: bool if self.__profile.dc_cancel else Constant(False)) def get_dc_cancel(self): return bool(self.dc_state) @setter def set_dc_cancel(self, value): if value: mode = DCOffsetAutomatic else: mode = DCOffsetOff self.dc_state = self.__source.set_dc_offset_mode(mode, ch) # Note: iq_balance has a 'manual' mode we are not yet exposing @exported_value(type=bool) # TODO: detect gr-iqbal def get_iq_balance(self): return bool(self.iq_state) @setter def set_iq_balance(self, value): if value: mode = IQBalanceAutomatic else: mode = IQBalanceOff self.iq_state = self.__source.set_iq_balance_mode(mode, ch) # 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), add_zero=True)) 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.connect(self.__source, self) self.state_from_json(self.__state_while_inactive)
def __init__(self, devices={}, audio_config=None, features=_STUB_FEATURES): # pylint: disable=dangerous-default-value if len(devices) <= 0: raise ValueError( 'Must be configured with at least one RF device! (This should normally be caught by the configuration validator.)' ) gr.top_block.__init__(self, type(self).__name__) self.__running = False # duplicate of GR state we can't reach, see __start_or_stop self.__has_a_useful_receiver = False # Configuration # TODO: device refactoring: Remove vestigial 'accessories' self._sources = CellDict( {k: d for k, d in six.iteritems(devices) if d.can_receive()}) self._accessories = accessories = { k: d for k, d in six.iteritems(devices) if not d.can_receive() } for key in self._sources: # arbitrary valid initial value self.source_name = key break self.__rx_device_type = EnumT( {k: v.get_name() or k for (k, v) in six.iteritems(self._sources)}) # Audio early setup self.__audio_manager = AudioManager( # must be before contexts graph=self, audio_config=audio_config, stereo=features['stereo']) # Blocks etc. # TODO: device refactoring: remove 'source' concept (which is currently a device) # TODO: remove legacy no-underscore names, maybe get rid of self.source self.source = None self.__monitor_rx_driver = None self.monitor = MonitorSink( signal_type=SignalType( sample_rate=10000, kind='IQ'), # dummy value will be updated in _do_connect context=Context(self)) self.monitor.get_interested_cell().subscribe2( self.__start_or_stop_later, the_subscription_context) self.__clip_probe = MaxProbe() # Receiver blocks (multiple, eventually) self._receivers = CellDict(dynamic=True) self._receiver_valid = {} # collections # TODO: No longer necessary to have these non-underscore names self.sources = CollectionState(CellDict(self._sources)) self.receivers = ReceiverCollection(self._receivers, self) self.accessories = CollectionState(CellDict(accessories)) self.__telemetry_store = TelemetryStore() # Flags, other state self.__needs_reconnect = [u'initialization'] self.__in_reconnect = False self.receiver_key_counter = 0 self.receiver_default_state = {} # Initialization def hookup_vfo_callback( k, d): # function so as to not close over loop variable d.get_vfo_cell().subscribe2( lambda value: self.__device_vfo_callback(k), the_subscription_context) for k, d in six.iteritems(devices): hookup_vfo_callback(k, d) device_context = DeviceContext(self.__telemetry_store.receive) for device in six.itervalues(devices): device.attach_context(device_context) self._do_connect()