예제 #1
0
 def test_vfos(self):
     d = merge_devices([
         Device(vfo_cell=_ConstantVFOCell(1)),
         Device(vfo_cell=LooseCell(
             value=0, type=RangeT([(10, 20)]), writable=True))
     ])
     self.assertTrue(d.get_vfo_cell().isWritable())
예제 #2
0
 def test_components_conflict(self):
     d = merge_devices([
         Device(components={'a': ExportedState()}),
         Device(components={'a': ExportedState()})
     ])
     self.assertEqual(d, IDevice(d))
     self.assertEqual(sorted(d.get_components().keys()), ['0-a', '1-a'])
예제 #3
0
 def test_components_disjoint(self):
     d = merge_devices([
         Device(components={'a': ExportedState()}),
         Device(components={'b': ExportedState()})
     ])
     self.assertEqual(d, IDevice(d))
     self.assertEqual(sorted(d.get_components().keys()), ['a', 'b'])
예제 #4
0
 def test_components_conflict(self):
     d = merge_devices([
         Device(components={'a': StubComponent()}),
         Device(components={'a': StubComponent()})
     ])
     self.assertEqual(d, IDevice(d))
     self.assertEqual(sorted(d.get_components_dict().iterkeys()), ['0-a', '1-a'])
예제 #5
0
 def test_tx_mode_noop(self):
     """
     With no TX driver, set_transmitting is a noop.
     
     This was chosen as the most robust handling of the erroneous operation.
     """
     d = Device(rx_driver=StubRXDriver())
     d.set_transmitting(True)
     d.set_transmitting(False)
예제 #6
0
 def test_tx_mode_actual(self):
     log = []
     txd = _TestTXDriver(log)
     d = Device(rx_driver=_TestRXDriver(), tx_driver=txd)
     def midpoint_hook():
         log.append('H')
     # Either TX driver receives the hook (!= case) or the hook is called directly (== case)
     d.set_transmitting(True, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook)])
     d.set_transmitting(True, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H'])
     d.set_transmitting(False, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H', (False, midpoint_hook)])
     d.set_transmitting(False, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H', (False, midpoint_hook), 'H'])
예제 #7
0
 def test_state_smoke_full(self):
     state_smoke_test(
         Device(name=u'x',
                rx_driver=StubRXDriver(),
                tx_driver=StubTXDriver(),
                vfo_cell=_ConstantVFOCell(1),
                components={'c': StubComponent()}))
예제 #8
0
def APRSISRXDevice(reactor, client, name=None, filter=None):
    """
    client: an aprs.APRS object (see <https://pypi.python.org/pypi/aprs>)
    name: device label
    filter: filter on incoming data (see <http://www.aprs-is.net/javAPRSFilter.aspx>)
    """
    # pylint: disable=redefined-builtin
    if name is None:
        name = 'APRS-IS ' + filter
    info = TelemetryStore(
    )  # TODO this is wrong, need to be able to output_message.

    def main_callback(line):
        # TODO: This print-both-formats code is duplicated from multimon.py; it should be a utility in this module instead. Also, we should maybe have a try block.
        log.msg(u'APRS: %r' % (line, ))
        message = parse_tnc2(line, time.time())
        log.msg(u'   -> %s' % (message, ))
        parsed = parse_tnc2(line, time.time())
        info.receive(message)

    # client blocks in a loop, so set up a thread
    alive = True

    def threaded_callback(line):
        if not alive:
            raise StopIteration()
        reactor.callFromThread(main_callback, line)

    reactor.callInThread(client.receive,
                         callback=threaded_callback,
                         filter=filter)

    # TODO: Arrange so we can get close() callbacks and set alive=false
    # TODO: Allow the filter to be changed at runtime
    return Device(name=name, components={'aprs-is': info})
예제 #9
0
def OsmoSDRDevice(
        osmo_device,
        name=None,
        profile=OsmoSDRProfile(),
        sample_rate=None,
        external_freq_shift=0.0,  # deprecated
        correction_ppm=0.0):
    '''
    osmo_device: gr-osmosdr device string
    name: block name (usually not specified)
    profile: an OsmoSDRProfile (see docs)
    sample_rate: desired sample rate, or None == guess a good rate
    external_freq_shift: external (down|up)converter frequency (Hz) -- DEPRECATED, use shinysdr.devices.FrequencyShift
    correction_ppm: oscillator frequency calibration (parts-per-million)
    '''
    # The existence of the correction_ppm parameter is a workaround for the current inability to dynamically change an exported field's type (the frequency range), allowing them to be initialized early enough, in the configuration, to take effect. (Well, it's also nice to hardcode them in the config if you want to.)
    if name is None:
        name = 'OsmoSDR %s' % osmo_device

    source = osmosdr.source('numchan=1 ' + osmo_device)
    if source.get_num_channels() < 1:
        # osmosdr.source doesn't throw an exception, allegedly because gnuradio can't handle it in a hier_block2 initializer. But we want to fail understandably, so recover by detecting it (sample rate = 0, which is otherwise nonsense)
        raise LookupError('OsmoSDR device not found (device string = %r)' %
                          osmo_device)
    elif source.get_num_channels() > 1:
        raise LookupError(
            'Too many devices/channels; need exactly one (device string = %r)'
            % osmo_device)

    tuning = _OsmoSDRTuning(profile, correction_ppm, source)
    vfo_cell = tuning.get_vfo_cell()

    if sample_rate is None:
        # If sample_rate is unspecified, we pick the closest available rate to a reasonable value. (Reasonable in that it's within the data handling capabilities of this software and of USB 2.0 connections.) Previously, we chose the maximum sample rate, but that may be too high for the connection the RF hardware, or too high for the CPU to FFT/demodulate.
        source.set_sample_rate(
            convert_osmosdr_range(source.get_sample_rates())(2.4e6))
    else:
        source.set_sample_rate(sample_rate)

    rx_driver = _OsmoSDRRXDriver(source=source,
                                 name=name,
                                 sample_rate=sample_rate,
                                 tuning=tuning)

    hw_initial_freq = source.get_center_freq()
    if hw_initial_freq == 0.0:
        # If the hardware/driver isn't providing a reasonable default (RTLs don't), do it ourselves; go to the middle of the FM broadcast band (rounded up or down to what the hardware reports it supports).
        vfo_cell.set(100e6)
    else:
        print hw_initial_freq
        vfo_cell.set(tuning.from_hardware_freq(hw_initial_freq))

    self = Device(name=name, vfo_cell=vfo_cell, rx_driver=rx_driver)

    # implement legacy option in terms of new devices
    if external_freq_shift == 0.0:
        return self
    else:
        return merge_devices([self, FrequencyShift(-external_freq_shift)])
예제 #10
0
 def test_close(self):
     l = set()
     Device(rx_driver=_ShutdownDetectorRX(l, 'rx'),
            tx_driver=_ShutdownDetectorTX(l, 'tx'),
            components={
                'c': _ShutdownDetector(l, 'c')
            }).close()
     self.assertEqual(l, set(['rx', 'tx', 'c']))
예제 #11
0
def SimulatedDevice(name='Simulated RF', freq=0.0):
    return Device(name=name,
                  vfo_cell=LooseCell(key='freq',
                                     value=freq,
                                     ctor=Range([(freq, freq)]),
                                     writable=True,
                                     persists=False),
                  rx_driver=_SimulatedRXDriver(name))
예제 #12
0
def _RetuningTestDevice(freq, has_dc_offset):
    return Device(
        rx_driver=_RetuningTestRXDriver(has_dc_offset),
        vfo_cell=LooseCell(
            value=freq,
            type=RangeT([(-1e9, 1e9)]),  # TODO kludge magic numbers
            writable=True,
            persists=False))
예제 #13
0
 def test_close(self):
     log = []
     top = Top(devices={'m':
         merge_devices([
             SimulatedDeviceForTest(),
             Device(components={'c': _DeviceShutdownDetector(log)})])})
     top.close_all_devices()
     self.assertEqual(log, ['close'])
예제 #14
0
def LimeSDRDevice(serial,
                  device_type,
                  lna_path=LNAW,
                  sample_rate=2.4e6,
                  name=None,
                  calibration=True):
    """
    serial: device serial number
    device_type: LimeSDRMini or LimeSDRUSB
    lna_path: LNANONE, LNAL, LNAH, or LNAW
    name: block name (usually not specified)
    sample_rate: desired sample rate, or None == guess a good rate
    
    See documentation in shinysdr/i/webstatic/manual/configuration.html.
    """

    if not _available:
        raise Exception(
            'LimeSDRDevice: gr-limesdr Python bindings not found; cannot create device'
        )

    serial = defaultstr(serial)
    if name is None:
        name = 'LimeSDR %s' % serial

    # TODO: High gain might be unsafe, but low gain might result in failed calibration.
    # Ideally we'd load these initial values from the saved state?
    freq = 1e9
    gain = 50
    if_bandwidth = 3e6

    source = create_source(serial,
                           device_type,
                           lna_path,
                           sample_rate,
                           freq,
                           if_bandwidth,
                           gain,
                           calibration=calibration)

    tuning = _LimeSDRTuning(source)
    vfo_cell = tuning.get_vfo_cell()

    rx_driver = _LimeSDRRXDriver(device_type=device_type,
                                 source=source,
                                 lna_path=lna_path,
                                 name=name,
                                 tuning=tuning,
                                 sample_rate=sample_rate)

    vfo_cell.set(freq)

    return Device(name=name,
                  vfo_cell=vfo_cell,
                  rx_driver=rx_driver,
                  tx_driver=nullExportedState)
예제 #15
0
 def test_tx_mode_actual(self):
     log = []
     txd = _TestTXDriver(log)
     d = Device(rx_driver=_TestRXDriver(), tx_driver=txd)
     def midpoint_hook():
         log.append('H')
     # Either TX driver receives the hook (!= case) or the hook is called directly (== case)
     d.set_transmitting(True, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook)])
     d.set_transmitting(True, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H'])
     d.set_transmitting(False, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H', (False, midpoint_hook)])
     d.set_transmitting(False, midpoint_hook)
     self.assertEqual(log, [(True, midpoint_hook), 'H', (False, midpoint_hook), 'H'])
예제 #16
0
def SimulatedDevice(name='Simulated RF', freq=0.0, allow_tuning=False):
    return Device(
        name=name,
        vfo_cell=LooseCell(
            key='freq',
            value=freq,
            ctor=Range([(-1e9, 1e9)]) if allow_tuning else Range([(freq, freq)]),  # TODO kludge magic numbers
            writable=True,
            persists=False),
        rx_driver=_SimulatedRXDriver(name))
예제 #17
0
def connect_to_rotctld(reactor, host='localhost', port=4533):
    """
    Connect to an existing rotctld process.
    """
    proxy = yield _connect_to_daemon(reactor=reactor,
                                     host=host,
                                     port=port,
                                     server_name='rotctld',
                                     proxy_ctor=_HamlibRotator)
    defer.returnValue(Device(components={'rotator': proxy}))
예제 #18
0
def APRSISRXDevice(reactor, client, name=None, aprs_filter=None):
    """
    client: an aprs.APRS object (see <https://pypi.python.org/pypi/aprs>)
    name: device label
    aprs_filter: filter on incoming data (see <http://www.aprs-is.net/javAPRSFilter.aspx>)
    """
    if name is None:
        name = 'APRS-IS ' + aprs_filter if aprs_filter else 'APRS-IS'
    component = _APRSISComponent(reactor, client, name, aprs_filter)
    return Device(name=name, components={'aprs-is': component})
예제 #19
0
def connect_to_rigctld(reactor, host='localhost', port=4532):
    """
    Connect to an existing rigctld process.
    """
    proxy = yield _connect_to_daemon(reactor=reactor,
                                     host=host,
                                     port=port,
                                     server_name='rigctld',
                                     proxy_ctor=_HamlibRig)
    defer.returnValue(
        Device(vfo_cell=proxy.state()['freq'], components={'rig': proxy}))
예제 #20
0
 def test_close(self):
     l = set()
     top = Top(
         devices={
             'm':
             Device(rx_driver=ShutdownMockDriver(l, 'rx'),
                    tx_driver=ShutdownMockDriver(l, 'tx'),
                    components={'c': ShutdownMockDriver(l, 'c')})
         })
     top.close_all_devices()
     # TODO: Add support for closing non-driver components (making this set [rx,tx,c]).
     self.assertEqual(l, set(['rx', 'tx']))
예제 #21
0
def SimulatedDevice(name='Simulated RF', freq=0.0, allow_tuning=False):
    rx_driver = _SimulatedRXDriver(name)
    return Device(
        name=name,
        vfo_cell=LooseCell(
            key='freq',
            value=freq,
            type=RangeT([(-1e9, 1e9)]) if allow_tuning else RangeT(
                [(freq, freq)]),  # TODO kludge magic numbers
            writable=True,
            persists=False,
            post_hook=rx_driver._set_sim_freq),
        rx_driver=rx_driver)
예제 #22
0
def Controller(reactor, key='controller', endpoint=None, elements=None, encoding='US-ASCII'):
    """Create a controller device.
  
    key: Component ID. TODO point at device merging documentation once it exists
    endpoint: Endpoint to connect to (such as a shinysdr.twisted_ext.SerialPortEndpoint).
    elements: List of elements (objects which define commands to send).
    encoding: Character encoding to use when Unicode text is given.
    """
    return Device(components={unicode(key): _ControllerProxy(
        reactor=reactor,
        endpoint=endpoint,
        elements=elements or [],
        encoding=encoding)})
예제 #23
0
파일: __init__.py 프로젝트: tashby/shinysdr
def connect_to_rig(reactor, port, baudrate=38400):
    """
    Connect to Elecraft radio over a serial port.
    
    port: Serial port device name.
    baudrate: Serial data rate; must match that set on the radio.
    """
    endpoint = SerialPortEndpoint(port, reactor, baudrate=baudrate)
    factory = FactoryWithArgs.forProtocol(_ElecraftClientProtocol, reactor)
    protocol = yield endpoint.connect(factory)
    proxy = protocol._proxy()

    defer.returnValue(
        Device(vfo_cell=proxy.iq_center_cell(), components={'rig': proxy}))
예제 #24
0
 def test_tx_mode_noop(self):
     """
     With no TX driver, set_transmitting is a noop.
     
     This was chosen as the most robust handling of the erroneous operation.
     """
     d = Device(rx_driver=StubRXDriver())
     d.set_transmitting(True)
     d.set_transmitting(False)
예제 #25
0
def SimulatedDevice(name='Simulated RF', freq=0.0, allow_tuning=False):
    """
    See documentation in shinysdr/i/webstatic/manual/configuration.html.
    """
    rx_driver = _SimulatedRXDriver(name)
    return Device(
        name=name,
        vfo_cell=LooseCell(
            value=freq,
            type=RangeT([(-1e9, 1e9)]) if allow_tuning else RangeT(
                [(freq, freq)]),  # TODO kludge magic numbers
            writable=True,
            persists=False,
            post_hook=rx_driver._set_sim_freq),
        rx_driver=rx_driver)
예제 #26
0
def SimulatedDeviceForTest(name='Simulated RF',
                           freq=0.0,
                           allow_tuning=False,
                           add_transmitters=False):
    """Identical to SimulatedDevice except that the defaults are arranged to be minimal for fast testing rather than to provide a rich simulation."""
    rx_driver = _SimulatedRXDriver(name, add_transmitters=add_transmitters)
    return Device(
        name=name,
        vfo_cell=LooseCell(
            value=freq,
            type=RangeT([(-1e9, 1e9)]) if allow_tuning else RangeT(
                [(freq, freq)]),  # TODO kludge magic numbers
            writable=True,
            persists=False,
            post_hook=rx_driver._set_sim_freq),
        rx_driver=rx_driver)
예제 #27
0
 def test_name(self):
     self.assertEqual(
         'a',
         merge_devices([Device(), Device(name='a')]).get_name())
     self.assertEqual(
         'a',
         merge_devices([Device(name='a'), Device()]).get_name())
     self.assertEqual(
         'a+b',
         merge_devices([Device(name='a'),
                        Device(name='b')]).get_name())
예제 #28
0
 def test_tx_present(self):
     txd = _TestTXDriver([])
     d = Device(tx_driver=txd)
     self.assertEqual(True, d.can_transmit())
     self.assertEqual(txd, d.get_tx_driver())
예제 #29
0
 def test_tx_absent(self):
     d = Device()
     self.assertEqual(False, d.can_receive())
     self.assertEqual(nullExportedState, d.get_tx_driver())
예제 #30
0
 def test_rx_present(self):
     rxd = StubRXDriver()
     d = Device(rx_driver=rxd)
     self.assertEqual(True, d.can_receive())
     self.assertEqual(rxd, d.get_rx_driver())
예제 #31
0
 def test_name(self):
     self.assertEqual(u'x', Device(name='x').get_name())
     self.assertEqual(None, Device().get_name())
예제 #32
0
	def test_rx_some(self):
		rxd = _TestRXDriver()
		d = Device(rx_driver=rxd)
		self.assertEqual(True, d.can_receive())
		self.assertEqual(rxd, d.get_rx_driver())
예제 #33
0
 def test_tx_absent(self):
     d = Device()
     self.assertEqual(False, d.can_receive())
     self.assertEqual(nullExportedState, d.get_tx_driver())
예제 #34
0
 def test_tx_present(self):
     txd = _TestTXDriver([])
     d = Device(tx_driver=txd)
     self.assertEqual(True, d.can_transmit())
     self.assertEqual(txd, d.get_tx_driver())
예제 #35
0
def OsmoSDRDevice(
        osmo_device,
        name=None,
        profile=None,
        sample_rate=None,
        correction_ppm=0.0):
    """
    osmo_device: gr-osmosdr device string
    name: block name (usually not specified)
    profile: an OsmoSDRProfile (see docs)
    sample_rate: desired sample rate, or None == guess a good rate
    correction_ppm: oscillator frequency calibration (parts-per-million)
    
    See documentation in shinysdr/i/webstatic/manual/configuration.html.
    """
    # The existence of the correction_ppm parameter is a workaround for the current inability to dynamically change an exported field's type (the frequency range), allowing them to be initialized early enough, in the configuration, to take effect. (Well, it's also nice to hardcode them in the config if you want to.)
    osmo_device = str(osmo_device)  # ensure not unicode type as we talk to byte-oriented C++
    if name is None:
        name = 'OsmoSDR %s' % osmo_device
    if profile is None:
        profile = profile_from_device_string(osmo_device)
    
    source = osmosdr.source(b'numchan=1 ' + osmo_device)
    if source.get_num_channels() < 1:
        # osmosdr.source doesn't throw an exception, allegedly because gnuradio can't handle it in a hier_block2 initializer. But we want to fail understandably, so recover by detecting it (sample rate = 0, which is otherwise nonsense)
        raise LookupError('OsmoSDR device not found (device string = %r)' % osmo_device)
    elif source.get_num_channels() > 1:
        raise LookupError('Too many devices/channels; need exactly one (device string = %r)' % osmo_device)
    
    tuning = _OsmoSDRTuning(profile, correction_ppm, source)
    vfo_cell = tuning.get_vfo_cell()
    
    if sample_rate is None:
        # If sample_rate is unspecified, we pick the closest available rate to a reasonable value. (Reasonable in that it's within the data handling capabilities of this software and of USB 2.0 connections.) Previously, we chose the maximum sample rate, but that may be too high for the connection the RF hardware, or too high for the CPU to FFT/demodulate.
        source.set_sample_rate(convert_osmosdr_range(source.get_sample_rates())(2.4e6))
    else:
        source.set_sample_rate(sample_rate)
    
    rx_driver = _OsmoSDRRXDriver(
        osmo_device=osmo_device,
        source=source,
        profile=profile,
        name=name,
        tuning=tuning)
    
    if profile.tx:
        tx_sample_rate = 2000000  # TODO KLUDGE NOT GENERAL need to use profile
        tx_driver = _OsmoSDRTXDriver(
            osmo_device=osmo_device,
            rx=rx_driver,
            name=name,
            tuning=tuning,
            sample_rate=tx_sample_rate)
    else:
        tx_driver = nullExportedState
    
    hw_initial_freq = source.get_center_freq()
    if hw_initial_freq == 0.0:
        # If the hardware/driver isn't providing a reasonable default (RTLs don't), do it ourselves; go to the middle of the FM broadcast band (rounded up or down to what the hardware reports it supports).
        vfo_cell.set(100e6)
    else:
        print hw_initial_freq
        vfo_cell.set(tuning.from_hardware_freq(hw_initial_freq))
    
    return Device(
        name=name,
        vfo_cell=vfo_cell,
        rx_driver=rx_driver,
        tx_driver=tx_driver)
예제 #36
0
 def test_rx_present(self):
     rxd = StubRXDriver()
     d = Device(rx_driver=rxd)
     self.assertEqual(True, d.can_receive())
     self.assertEqual(rxd, d.get_rx_driver())