Пример #1
0
 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
Пример #2
0
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()
Пример #3
0
    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)
Пример #4
0
 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)
Пример #5
0
 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)
Пример #6
0
 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)
Пример #7
0
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
Пример #8
0
 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
Пример #9
0
 def get_output_type(self):
     """implement IDemodulator"""
     return SignalType(kind='MONO', sample_rate=self.__demod_rate)
Пример #10
0
 def get_output_type(self):
     return SignalType(kind='IQ', sample_rate=self.__sample_rate_out)
Пример #11
0
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)
Пример #12
0
 def get_input_type(self):
     return SignalType(kind='MONO', sample_rate=self.__audio_rate)
Пример #13
0
    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()
Пример #14
0
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)
Пример #15
0
 def get_input_type(self):
     return SignalType('IQ', 1)
Пример #16
0
 def get_input_type(self):
     return SignalType(kind='IQ', sample_rate=10000)
Пример #17
0
 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())
Пример #18
0
 def test_constants(self):
     self.assertEquals(
         no_signal,
         SignalType(kind='NONE', sample_rate=0))
Пример #19
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))
Пример #20
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)
Пример #21
0
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()
Пример #22
0
    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()
Пример #23
0
    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)])
Пример #24
0
 def get_output_type(self):
     return SignalType(kind='MONO', sample_rate=pipe_rate)
Пример #25
0
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
Пример #26
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.__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)
Пример #27
0
    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()