Example #1
0
 def filename(self, start_time):
     # TODO: We should be using the same frequency as __start_frequency but
     # there is no guarantee in the interface about the timing of calls to
     # this method vs. fileOpened.
     time_str = time.strftime(defaultstr('%y%m%d_%H%M.wav'), time.gmtime(start_time))
     filename = defaultstr('%s_%s' % (self.context.get_absolute_frequency_cell().get(), time_str))
     return os.path.join(self.directory, filename)
Example #2
0
 def filename(self, start_time):
     # TODO: We should be using the same frequency as __start_frequency but
     # there is no guarantee in the interface about the timing of calls to
     # this method vs. fileOpened.
     time_str = time.strftime(defaultstr('%y%m%d_%H%M.wav'),
                              time.gmtime(start_time))
     filename = defaultstr(
         '%s_%s' %
         (self.context.get_absolute_frequency_cell().get(), time_str))
     return os.path.join(self.directory, filename)
Example #3
0
def AudioDevice(
        rx_device='',  # may be used positionally, not recommented
        tx_device=None,
        name=None,
        sample_rate=44100,
        channel_mapping=None,
        usable_bandwidth=None,
        _module=gr_audio):  # parameter for testing only
    """System audio ("sound card") device.
    
    See documentation in shinysdr/i/webstatic/manual/configuration.html.
    """
    if _module == 'UNAVAILABLE':
        raise ImportError('gr-audio not loaded, cannot create audio device')
    
    rx_device = defaultstr(rx_device)
    if tx_device is not None:
        tx_device = defaultstr(tx_device)
    channel_mapping = _coerce_channel_mapping(channel_mapping)
    
    if name is None:
        full_name = u'Audio ' + six.text_type(rx_device)
        if tx_device is not None:
            full_name += '/' + six.text_type(tx_device)
    else:
        full_name = six.text_type(name)

    rx_driver = _AudioRXDriver(
        device_name=rx_device,
        sample_rate=sample_rate,
        channel_mapping=channel_mapping,
        usable_bandwidth=usable_bandwidth,
        audio_module=_module)
    if tx_device is not None:
        tx_driver = _AudioTXDriver(
            device_name=tx_device,
            sample_rate=sample_rate,
            channel_mapping=channel_mapping,
            audio_module=_module)
    else:
        tx_driver = nullExportedState
    
    return Device(
        name=full_name,
        vfo_cell=LooseCell(
            value=0.0,
            type=RangeT([(0.0, 0.0)]),
            writable=True,
            persists=False),
        rx_driver=rx_driver,
        tx_driver=tx_driver)
Example #4
0
    def __init__(
            self,
            input_rate,
            # parameters provided by subclasses
            demod_rate,
            audio_rate,
            band_filter,
            band_filter_transition,
            stereo=False,
            # standard demodulator parameters which we want to allow usage without
            mode=None,
            context=None):
        """All arguments should be passed as keywords."""

        self.__channels = channels = 2 if stereo else 1
        self.__signal_type = SignalType(kind='STEREO' if stereo else 'MONO',
                                        sample_rate=audio_rate)

        # TODO: Review which of these attributes are actually used
        self.mode = mode
        self.context = context
        self.input_rate = input_rate
        self.demod_rate = demod_rate
        self.audio_rate = audio_rate

        gr.hier_block2.__init__(
            self, defaultstr(u'%s(mode=%r)' % (type(self).__name__, mode)),
            gr.io_signature(1, 1, gr.sizeof_gr_complex),
            gr.io_signature(1, 1, gr.sizeof_float * channels))
        ChannelFilterMixin.__init__(self,
                                    input_rate=self.input_rate,
                                    demod_rate=demod_rate,
                                    cutoff_freq=band_filter,
                                    transition_width=band_filter_transition)
Example #5
0
    def __init__(self,
            interval,
            duration,
            listener,
            sample_rate,

            _callLater=reactor.callLater,
            _time=time.time,
            _deferToThread=threads.deferToThread):
        assert interval > duration

        gr.hier_block2.__init__(
            self, type(self).__name__,
            gr.io_signature(1, 1, gr.sizeof_float),
            gr.io_signature(0, 0, 0))

        self._callLater = _callLater
        self._time = _time
        self._deferToThread = _deferToThread

        self.interval = interval
        self.listener = listener
        self.duration = duration

        self._sink = wavfile_sink(
            # There doesn't seem to be a way to create a sink without
            # immediately opening a file :(
            filename=defaultstr('/dev/null'),
            n_channels=1,
            sample_rate=sample_rate,
            bits_per_sample=16)

        self.connect(self, self._sink)
Example #6
0
 def __init__(self, 
         input_rate,
         # parameters provided by subclasses
         demod_rate, audio_rate, band_filter, band_filter_transition, stereo=False,
         # standard demodulator parameters which we want to allow usage without
         mode=None, context=None):
     """All arguments should be passed as keywords."""
     
     self.__channels = channels = 2 if stereo else 1
     self.__signal_type = SignalType(
         kind='STEREO' if stereo else 'MONO',
         sample_rate=audio_rate)
     
     # TODO: Review which of these attributes are actually used
     self.mode = mode
     self.context = context
     self.input_rate = input_rate
     self.demod_rate = demod_rate
     self.audio_rate = audio_rate
     
     gr.hier_block2.__init__(
         self, defaultstr(u'%s(mode=%r)' % (type(self).__name__, mode)),
         gr.io_signature(1, 1, gr.sizeof_gr_complex),
         gr.io_signature(1, 1, gr.sizeof_float * channels))
     ChannelFilterMixin.__init__(self,
         input_rate=self.input_rate,
         demod_rate=demod_rate,
         cutoff_freq=band_filter,
         transition_width=band_filter_transition)
Example #7
0
 def __dispatch_url(self):
     loc = self.transport.location.decode(
         'utf-8')  # TODO centralize url decoding
     self.__log.info('Stream connection to {url}', url=loc)
     path = [urllib.parse.unquote(x) for x in loc.split('/')]
     assert path[0] == ''
     path[0:1] = []
     cap_string = path[0]
     if cap_string in self._caps:
         root_object = self._caps[cap_string]
         path[0:1] = []
     else:
         raise Exception('Unknown cap')  # TODO better error reporting
     audio_prefix = defaultstr('audio?rate=')
     if len(path) == 1 and path[0].startswith(audio_prefix):
         rate = int(
             json.loads(urllib.parse.unquote(path[0][len(audio_prefix):])))
         self.inner = AudioStreamInner(the_reactor, self.__send,
                                       root_object, rate)
     elif len(path) >= 1 and path[0] == CAP_OBJECT_PATH_ELEMENT:
         # note _lookup_block may throw. TODO: Better error reporting
         root_object = _lookup_block(root_object, path[1:])
         self.inner = StateStreamInner(
             self.__send, root_object, loc, self.__subscription_context
         )  # note reuse of loc as HTTP path; probably will regret this
     else:
         raise Exception('Unknown path: %r' % (path, ))
Example #8
0
    def __init__(self,
            interval,
            duration,
            listener,
            sample_rate,

            _callLater=reactor.callLater,
            _time=time.time,
            _deferToThread=threads.deferToThread):
        assert interval > duration

        gr.hier_block2.__init__(
            self, type(self).__name__,
            gr.io_signature(1, 1, gr.sizeof_float),
            gr.io_signature(0, 0, 0))

        self._callLater = _callLater
        self._time = _time
        self._deferToThread = _deferToThread

        self.interval = interval
        self.listener = listener
        self.duration = duration

        self._sink = wavfile_sink(
            # There doesn't seem to be a way to create a sink without
            # immediately opening a file :(
            filename=defaultstr('/dev/null'),
            n_channels=1,
            sample_rate=sample_rate,
            bits_per_sample=16)

        self.connect(self, self._sink)
Example #9
0
 def __init__(self, mode, input_rate=0, context=None):
     assert input_rate > 0
     assert context is not None
     gr.hier_block2.__init__(
         self, defaultstr(mode + ' (FM + Multimon-NG) demodulator'),
         gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
         gr.io_signature(1, 1, gr.sizeof_float * 1),
     )
     self.mode = mode
     self.input_rate = input_rate
     
     # FM demod
     # TODO: Retry telling the NFMDemodulator to have its output rate be pipe_rate instead of using a resampler. Something went wrong when trying that before. Same thing is done in dsd.py
     self.fm_demod = NFMDemodulator(
         mode='NFM',
         input_rate=input_rate,
         no_audio_filter=True,  # don't remove CTCSS tone
         tau=None)  # no deemphasis
     assert self.fm_demod.get_output_type().get_kind() == 'MONO'
     fm_audio_rate = self.fm_demod.get_output_type().get_sample_rate()
     
     # Subprocess
     self.mm_demod = APRSDemodulator(context=context)
     mm_audio_rate = self.mm_demod.get_input_type().get_sample_rate()
     
     # Output
     self.connect(
         self,
         self.fm_demod,
         make_resampler(fm_audio_rate, mm_audio_rate),
         self.mm_demod,
         self)
Example #10
0
 def __init__(self, protocol, context, multimon_demod_args):
     gr.hier_block2.__init__(
         self, defaultstr('{}({!r}, {!r})'.format(type(self).__name__, multimon_demod_args, protocol)),
         gr.io_signature(1, 1, gr.sizeof_float * 1),
         gr.io_signature(1, 1, gr.sizeof_float * 1),
     )
     
     # Subprocess
     # using /usr/bin/env because twisted spawnProcess doesn't support path search
     self.__process = reactor.spawnProcess(
         protocol,
         '/usr/bin/env',
         env=None,  # inherit environment
         args=['env', 'multimon-ng', '-t', 'raw'] + multimon_demod_args + ['-v', '10', '-'],
         childFDs={
             0: 'w',
             1: 'r',
             2: 2
         })
     sink = make_sink_to_process_stdin(self.__process, itemsize=gr.sizeof_short)
     
     # Output
     to_short = blocks.float_to_short(vlen=1, scale=int_scale)
     self.connect(
         self,
         # blocks.complex_to_float(),
         to_short,
         sink)
     # Audio copy output
     unconverter = blocks.short_to_float(vlen=1, scale=int_scale)
     self.connect(to_short, unconverter)
     self.connect(unconverter, self)
Example #11
0
def AudioDevice(
        rx_device='',  # may be used positionally, not recommented
        tx_device=None,
        name=None,
        sample_rate=44100,
        channel_mapping=None,
        usable_bandwidth=None,
        _module=gr_audio):  # parameter for testing only
    """System audio ("sound card") device.
    
    See documentation in shinysdr/i/webstatic/manual/configuration.html.
    """
    if _module == 'UNAVAILABLE':
        raise ImportError('gr-audio not loaded, cannot create audio device')

    rx_device = defaultstr(rx_device)
    if tx_device is not None:
        tx_device = defaultstr(tx_device)
    channel_mapping = _coerce_channel_mapping(channel_mapping)

    if name is None:
        full_name = u'Audio ' + six.text_type(rx_device)
        if tx_device is not None:
            full_name += '/' + six.text_type(tx_device)
    else:
        full_name = six.text_type(name)

    rx_driver = _AudioRXDriver(device_name=rx_device,
                               sample_rate=sample_rate,
                               channel_mapping=channel_mapping,
                               usable_bandwidth=usable_bandwidth,
                               audio_module=_module)
    if tx_device is not None:
        tx_driver = _AudioTXDriver(device_name=tx_device,
                                   sample_rate=sample_rate,
                                   channel_mapping=channel_mapping,
                                   audio_module=_module)
    else:
        tx_driver = nullExportedState

    return Device(name=full_name,
                  vfo_cell=LooseCell(value=0.0,
                                     type=RangeT([(0.0, 0.0)]),
                                     writable=True,
                                     persists=False),
                  rx_driver=rx_driver,
                  tx_driver=tx_driver)
Example #12
0
def create_source(serial,
                  device_type,
                  lna_path,
                  sample_rate,
                  freq,
                  if_bandwidth,
                  gain,
                  calibration=True):
    # TODO: Consider using NCO to avoid DC spur.
    # TODO: Support choosing the channel
    # TODO: Use sample_rate or if_bandwidth as calibr_bandw?
    if hasattr(limesdr._limesdr_swig, 'source_set_antenna'):
        # API changed drastically in gr-limesdr 3.0.1; this is the only way to detect the new API.
        s = limesdr.source(serial, ch, '')
        s.set_sample_rate(sample_rate)
        s.set_center_freq(freq)
        s.set_bandwidth(if_bandwidth, ch)
        s.set_digital_filter(if_bandwidth, ch)
        s.set_gain(gain, ch)
        s.set_antenna(lna_path, ch)

        if calibration:
            s.calibrate(60e6, ch)

        return s

    return limesdr.source(
        serial=serial,
        device_type=device_type,  # LimeSDR-USB
        chip_mode=SISO,  # SISO(1),MIMO(2)
        channel=ch,  # A(0),B(1)
        file_switch=0,  # Don't load settings from a file
        filename=defaultstr(''),
        rf_freq=freq,  # Center frequency in Hz
        samp_rate=sample_rate,
        oversample=0,  # 0(default),1,2,4,8,16,32
        calibration_ch0=1 if calibration and ch == 0 else 0,
        calibr_bandw_ch0=60e6,
        calibration_ch1=1 if calibration and ch == 1 else 0,
        calibr_bandw_ch1=60e6,
        lna_path_mini=lna_path,  # LNAH(1),LNAW(3)
        lna_path_ch0=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        lna_path_ch1=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        analog_filter_ch0=1,
        analog_bandw_ch0=if_bandwidth,
        analog_filter_ch1=1,
        analog_bandw_ch1=if_bandwidth,
        digital_filter_ch0=1,
        digital_bandw_ch0=if_bandwidth,
        digital_filter_ch1=1,
        digital_bandw_ch1=if_bandwidth,
        gain_dB_ch0=gain,
        gain_dB_ch1=gain,
        nco_freq_ch0=0,
        nco_freq_ch1=0,
        cmix_mode_ch0=0,  # UPCONVERT(0), DOWNCONVERT(1)
        cmix_mode_ch1=0,  # UPCONVERT(0), DOWNCONVERT(1)
    )
Example #13
0
 def test_manifest(self):
     response, data = yield http_get(
         the_reactor,
         urljoin(self.url, defaultstr('/client/web-app-manifest.json')))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'),
                      ['application/manifest+json'])
     manifest = json.loads(data)
     self.assertEqual(manifest['name'], 'test title')
Example #14
0
def find_audio_rx_names(_module=gr_audio):
    # TODO: request that gnuradio support device enumeration
    if _module == 'UNAVAILABLE':
        return []
    try:
        AudioDevice(rx_device='', _module=_module)
        return [defaultstr('')]
    except RuntimeError:  # thrown by gnuradio
        return []
Example #15
0
def find_audio_rx_names(_module=gr_audio):
    # TODO: request that gnuradio support device enumeration
    if _module == 'UNAVAILABLE':
        return []
    try:
        AudioDevice(rx_device='', _module=_module)
        return [defaultstr('')]
    except RuntimeError:  # thrown by gnuradio
        return []
Example #16
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)
Example #17
0
 def __connect_with_resampling(self, from_endpoint, from_rate, to_endpoint, to_rate, complex):
     # pylint: disable=redefined-builtin
     
     if from_rate == to_rate:
         self.connect(from_endpoint, to_endpoint)
     else:
         gr.log.info(defaultstr('{}: Resampling {} to {}'.format(
             type(self).__name__, from_rate, to_rate)))
         resampler = make_resampler(from_rate, to_rate, complex=complex)
         self.connect(from_endpoint, resampler, to_endpoint)
Example #18
0
 def setUp(self):
     # TODO: arrange so we don't need to pass as many bogus strings
     self._service = WebService(reactor=the_reactor,
                                http_endpoint='tcp:0',
                                ws_endpoint='tcp:3333',
                                root_cap=u'ROOT',
                                cap_table={u'ROOT': SiteStateStub()},
                                title='test title')
     self._service.startService()
     self.url = defaultstr(self._service.get_url())
Example #19
0
 def __init__(self, mode, input_rate, output_rate, demod_class=None, freq=0.0, quiet=False):
     gr.hier_block2.__init__(
         self, type(self).__name__,
         gr.io_signature(1, 1, gr.sizeof_gr_complex),
         gr.io_signature(2, 2, gr.sizeof_float))
     
     if demod_class is None:
         mode_def = lookup_mode(mode)
         if mode_def is None:
             raise Exception('{}: No demodulator registered for mode {!r}, only {!r}'.format(
                 type(self).__name__, mode, [md.mode for md in get_modes()]))
         demod_class = mode_def.demod_class
     
     context = _DemodulatorAdapterContext(adapter=self, freq=freq)
     
     demod = self.__demodulator = IDemodulator(demod_class(
         mode=mode,
         input_rate=input_rate,
         context=context))
     self.connect(self, demod)
     
     output_type = demod.get_output_type()
     demod_output_rate = output_type.get_sample_rate()
     same_rate = demod_output_rate == output_rate
     stereo = output_type.get_kind() == 'STEREO'
     
     # connect outputs, resampling and adapting mono/stereo as needed
     # TODO: Make the logic for this in receiver.py reusable?
     if output_type.get_kind() == 'NONE':
         # TODO: produce correct sample rate of zeroes and maybe a warning
         dummy = blocks.vector_source_f([])
         self.connect(dummy, (self, 0))
         self.connect(dummy, (self, 1))
     else:
         if stereo:
             splitter = blocks.vector_to_streams(gr.sizeof_float, 2)
             self.connect(demod, splitter)
         if same_rate:
             if stereo:
                 self.connect((splitter, 0), (self, 0))
                 self.connect((splitter, 1), (self, 1))
             else:
                 self.connect(demod, (self, 0))
                 self.connect(demod, (self, 1))
         else:
             if not quiet:
                 gr.log.info(defaultstr('{}: Native {} demodulated rate is {}; resampling to {}'.format(
                     type(self).__name__, mode, demod_output_rate, output_rate)))
             if stereo:
                 self.connect((splitter, 0), make_resampler(demod_output_rate, output_rate), (self, 0))
                 self.connect((splitter, 1), make_resampler(demod_output_rate, output_rate), (self, 1))
             else:
                 resampler = make_resampler(demod_output_rate, output_rate)
                 self.connect(demod, resampler, (self, 0))
                 self.connect(resampler, (self, 1))
Example #20
0
 def setUp(self):
     # TODO: arrange so we don't need to pass as many bogus strings
     self._service = WebService(
         reactor=the_reactor,
         http_endpoint='tcp:0',
         ws_endpoint='tcp:3333',
         root_cap=u'ROOT',
         cap_table={u'ROOT': SiteStateStub()},
         title='test title')
     self._service.startService()
     self.url = defaultstr(self._service.get_url())
Example #21
0
 def test_plugin_index(self):
     response, data = yield http_get(
         the_reactor,
         urljoin(self.url, defaultstr('/client/plugin-index.json')))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'),
                      ['application/json'])
     index = json.loads(data)
     self.assertIn('css', index)
     self.assertIn('js', index)
     self.assertIn('modes', index)
Example #22
0
    def __connect_with_resampling(self, from_endpoint, from_rate, to_endpoint,
                                  to_rate, complex):
        # pylint: disable=redefined-builtin

        if from_rate == to_rate:
            self.connect(from_endpoint, to_endpoint)
        else:
            gr.log.info(
                defaultstr('{}: Resampling {} to {}'.format(
                    type(self).__name__, from_rate, to_rate)))
            resampler = make_resampler(from_rate, to_rate, complex=complex)
            self.connect(from_endpoint, resampler, to_endpoint)
Example #23
0
 def test_client_configuration(self):
     response, data = yield http_get(the_reactor, urljoin(self.url, defaultstr('/client/client-configuration')))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'), ['application/json'])
     configuration = json.loads(data)
     plugin_index = configuration['plugins']
     self.assertIn('css', plugin_index)
     self.assertIn('js', plugin_index)
     self.assertIn('modes', plugin_index)
     self.assertEqual(
         'ws://localhost:3333/shared_test_objects/' + CAP_OBJECT_PATH_ELEMENT,
         configuration['shared_test_objects_url'])
Example #24
0
 def render_POST(self, request):
     """currently only meaningful to create children of CollectionResources"""
     block = self._block
     if not IWritableCollection.providedBy(block):
         raise Exception('Block is not a writable collection')
     assert request.getHeader(b'Content-Type') == b'application/json'
     reqjson = json.load(request.content)
     key = block.create_child(reqjson)  # note may fail
     url = request.prePathURL() + defaultstr('/receivers/') + urllib.parse.quote(key, safe='')
     request.setResponseCode(201)  # Created
     request.setHeader(b'Location', url)
     # TODO consider a more useful response
     return serialize(url).encode('utf-8')
Example #25
0
    def __init__(self, squelch_rate, squelch_threshold=-100):
        alpha = 80.0 / squelch_rate

        self.__squelch = analog.simple_squelch_cc(squelch_threshold, alpha)
        self.__probe = analog.probe_avg_mag_sqrd_c(0, alpha=alpha)

        self.squelch_block = gr.hier_block2(
            defaultstr('SquelchMixin bundle'),
            gr.io_signature(1, 1, gr.sizeof_gr_complex),
            gr.io_signature(1, 1, gr.sizeof_gr_complex))
        self.squelch_block.connect(self.squelch_block, self.__squelch,
                                   self.squelch_block)
        self.squelch_block.connect(self.squelch_block, self.__probe)
Example #26
0
    def __init__(self, mode, input_rate=0, context=None):
        assert input_rate > 0

        # early init because we're going to invoke get_output_type()
        self.mode = mode
        self.input_rate = input_rate
        self.context = context

        self.__channels = channels = 2 if self.get_output_type().get_kind(
        ) == 'STEREO' else 1
        gr.hier_block2.__init__(
            self, defaultstr(u'%s(mode=%r)' % (type(self).__name__, mode)),
            gr.io_signature(1, 1, gr.sizeof_gr_complex),
            gr.io_signature(1, 1, gr.sizeof_float * channels))
Example #27
0
 def render_POST(self, request):
     """currently only meaningful to create children of CollectionResources"""
     block = self._block
     if not IWritableCollection.providedBy(block):
         raise Exception('Block is not a writable collection')
     assert request.getHeader(b'Content-Type') == b'application/json'
     reqjson = json.load(request.content)
     key = block.create_child(reqjson)  # note may fail
     url = request.prePathURL() + defaultstr(
         '/receivers/') + urllib.parse.quote(key, safe='')
     request.setResponseCode(201)  # Created
     request.setHeader(b'Location', url)
     # TODO consider a more useful response
     return serialize(url).encode('utf-8')
Example #28
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)
Example #29
0
 def test_client_configuration(self):
     response, data = yield http_get(
         the_reactor,
         urljoin(self.url, defaultstr('/client/client-configuration')))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'),
                      ['application/json'])
     configuration = json.loads(data)
     plugin_index = configuration['plugins']
     self.assertIn('css', plugin_index)
     self.assertIn('js', plugin_index)
     self.assertIn('modes', plugin_index)
     self.assertEqual(
         'ws://localhost:3333/shared_test_objects/' +
         CAP_OBJECT_PATH_ELEMENT, configuration['shared_test_objects_url'])
Example #30
0
    def __init__(self, osmo_device, rx, name, tuning, sample_rate):
        gr.hier_block2.__init__(self, defaultstr('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)
Example #31
0
    def __init__(self,
                 name='MultistageChannelFilter',
                 input_rate=0,
                 output_rate=0,
                 cutoff_freq=0,
                 transition_width=0,
                 center_freq=0):
        # cf. firdes.sanity_check_1f (which is private)
        # TODO better errors for other cases
        cutoff_freq = float(cutoff_freq)
        # TODO coerce output_rate to integer-or-float
        if cutoff_freq > output_rate / 2:
            # early check for better errors since our cascaded filters might be cryptically nonsense
            raise ValueError(
                'cutoff_freq (%s) is too high for output_rate (%s)' %
                (cutoff_freq, output_rate))

        plan = _make_filter_plan_1(input_rate=input_rate,
                                   output_rate=output_rate)
        plan = plan.replace(cutoff_freq=cutoff_freq,
                            transition_width=transition_width)
        self.__plan = plan

        gr.hier_block2.__init__(
            self,
            defaultstr(name),
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
        )

        self.stages = []

        prev_block = self
        for stage_design, taps in plan.get_stage_designs_and_taps():
            stage_filter = stage_design.create_block(taps)

            self.stages.append(stage_filter)
            if stage_filter is not None:
                self.connect(prev_block, stage_filter)
                prev_block = stage_filter

        # loop takes care of all connections but the n+1th
        self.connect(prev_block, self)

        self.freq_filter_block = self.stages[plan.get_freq_xlate_stage()]
        assert self.freq_filter_block is not None
        self.freq_filter_block.set_center_freq(center_freq)
Example #32
0
    def __init__(self,
            name='MultistageChannelFilter',
            input_rate=0,
            output_rate=0,
            cutoff_freq=0,
            transition_width=0,
            center_freq=0):
        # cf. firdes.sanity_check_1f (which is private)
        # TODO better errors for other cases
        cutoff_freq = float(cutoff_freq)
        # TODO coerce output_rate to integer-or-float
        if cutoff_freq > output_rate / 2:
            # early check for better errors since our cascaded filters might be cryptically nonsense
            raise ValueError('cutoff_freq (%s) is too high for output_rate (%s)' % (cutoff_freq, output_rate))
    
        plan = _make_filter_plan_1(
            input_rate=input_rate,
            output_rate=output_rate)
        plan = plan.replace(
            cutoff_freq=cutoff_freq,
            transition_width=transition_width)
        self.__plan = plan
        
        gr.hier_block2.__init__(
            self, defaultstr(name),
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
            gr.io_signature(1, 1, gr.sizeof_gr_complex * 1),
        )
        
        self.stages = []
        
        prev_block = self
        for stage_design, taps in plan.get_stage_designs_and_taps():
            stage_filter = stage_design.create_block(taps)
            
            self.stages.append(stage_filter)
            if stage_filter is not None:
                self.connect(prev_block, stage_filter)
                prev_block = stage_filter

        # loop takes care of all connections but the n+1th
        self.connect(prev_block, self)
        
        self.freq_filter_block = self.stages[plan.get_freq_xlate_stage()]
        assert self.freq_filter_block is not None
        self.freq_filter_block.set_center_freq(center_freq)
Example #33
0
 def __init__(self, squelch_rate, squelch_threshold=-100):
     alpha = 80.0 / squelch_rate
     
     self.__squelch = analog.simple_squelch_cc(squelch_threshold, alpha)
     self.__probe = analog.probe_avg_mag_sqrd_c(0, alpha=alpha)
     
     self.squelch_block = gr.hier_block2(
         defaultstr('SquelchMixin bundle'),
         gr.io_signature(1, 1, gr.sizeof_gr_complex),
         gr.io_signature(1, 1, gr.sizeof_gr_complex))
     self.squelch_block.connect(
         self.squelch_block,
         self.__squelch,
         self.squelch_block)
     self.squelch_block.connect(
         self.squelch_block,
         self.__probe)
Example #34
0
    def __poll(self):
        receiver = self._get_receiver()
        while True:
            frame = self.__splitter.get()
            if frame is None:
                break
            ((freq, sample_rate), fft) = frame
            if self._req_width is None:
                break
            print('Sending frame', self._req_width,
                  sample_rate)  # TODO: Remove debugging
            msg = struct.pack(
                'BBBHHHIh' + str(self._req_width) + 's',
                0,
                2,
                1,
                self._req_width,  # short
                0,  # meter
                0,  # subrx meter
                sample_rate,
                receiver.get_rec_freq() - freq,  # lo_offset
                ''.join([chr(int(max(1, min(255, -(x - 20))))) for x in fft]))
            self.transport.write(msg)

        # audio
        size_in_bytes = 2000 * 4
        if len(self.__audio_buffer) > size_in_bytes:
            abuf = self.__audio_buffer[:size_in_bytes]
            self.__audio_buffer = self.__audio_buffer[size_in_bytes:]
            print('Sending audio', len(abuf))  # TODO: Remove debugging
            # str() for Python 2.7.6 & 3 compatibility under unicode_literals
            unpacker = array.array(defaultstr('f'))
            unpacker.fromstring(abuf)
            nsamples = len(unpacker)
            msg = struct.pack(
                'BBBH' + str(nsamples) + 'B',
                1,
                2,
                1,
                nsamples,
                # TODO tweak
                *[
                    int(max(0, min(255, x * 127 + 127)))
                    for x in unpacker.tolist()
                ])
Example #35
0
def create_source(serial,
                  device_type,
                  lna_path,
                  sample_rate,
                  freq,
                  if_bandwidth,
                  gain,
                  calibration=True):
    # TODO: Consider using NCO to avoid DC spur.
    # TODO: Support choosing the channel
    # TODO: Use sample_rate or if_bandwidth as calibr_bandw?
    return limesdr.source(
        serial=serial,
        device_type=device_type,  # LimeSDR-USB
        chip_mode=SISO,  # SISO(1),MIMO(2)
        channel=ch,  # A(0),B(1)
        file_switch=0,  # Don't load settings from a file
        filename=defaultstr(''),
        rf_freq=freq,  # Center frequency in Hz
        samp_rate=sample_rate,
        oversample=0,  # 0(default),1,2,4,8,16,32
        calibration_ch0=1 if calibration and ch == 0 else 0,
        calibr_bandw_ch0=60e6,
        calibration_ch1=1 if calibration and ch == 1 else 0,
        calibr_bandw_ch1=60e6,
        lna_path_mini=lna_path,  # LNAH(1),LNAW(3)
        lna_path_ch0=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        lna_path_ch1=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        analog_filter_ch0=1,
        analog_bandw_ch0=if_bandwidth,
        analog_filter_ch1=1,
        analog_bandw_ch1=if_bandwidth,
        digital_filter_ch0=1,
        digital_bandw_ch0=if_bandwidth,
        digital_filter_ch1=1,
        digital_bandw_ch1=if_bandwidth,
        gain_dB_ch0=gain,
        gain_dB_ch1=gain,
        nco_freq_ch0=0,
        nco_freq_ch1=0,
        cmix_mode_ch0=0,  # UPCONVERT(0), DOWNCONVERT(1)
        cmix_mode_ch1=0,  # UPCONVERT(0), DOWNCONVERT(1)
    )
Example #36
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)
Example #37
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)
        try:
            source.set_bandwidth(self.__track_bandwidth, ch)
        except AttributeError:
            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)
Example #38
0
    def __poll(self):
        receiver = self._get_receiver()
        while True:
            frame = self.__splitter.get()
            if frame is None:
                break
            ((freq, sample_rate), fft) = frame
            if self._req_width is None:
                break
            print('Sending frame', self._req_width, sample_rate)  # TODO: Remove debugging
            msg = struct.pack('BBBHHHIh' + str(self._req_width) + 's',
                0,
                2,
                1,
                self._req_width,  # short
                0,  # meter
                0,  # subrx meter
                sample_rate,
                receiver.get_rec_freq() - freq,  # lo_offset
                ''.join([chr(int(max(1, min(255, -(x - 20))))) for x in fft]))
            self.transport.write(msg)

        # audio
        size_in_bytes = 2000 * 4
        if len(self.__audio_buffer) > size_in_bytes:
            abuf = self.__audio_buffer[:size_in_bytes]
            self.__audio_buffer = self.__audio_buffer[size_in_bytes:]
            print('Sending audio', len(abuf))  # TODO: Remove debugging
            # str() for Python 2.7.6 & 3 compatibility under unicode_literals
            unpacker = array.array(defaultstr('f'))
            unpacker.fromstring(abuf)
            nsamples = len(unpacker)
            msg = struct.pack('BBBH' + str(nsamples) + 'B',
                1,
                2,
                1,
                nsamples,
                # TODO tweak
                *[int(max(0, min(255, x * 127 + 127))) for x in unpacker.tolist()])
Example #39
0
 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)
Example #40
0
    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)
Example #41
0
def create_source(serial, device_type, lna_path, sample_rate, freq, if_bandwidth, gain, calibration=True):
    # TODO: Consider using NCO to avoid DC spur.
    # TODO: Support choosing the channel
    # TODO: Use sample_rate or if_bandwidth as calibr_bandw?
    return limesdr.source(
        serial=serial,
        device_type=device_type,  # LimeSDR-USB
        chip_mode=SISO,  # SISO(1),MIMO(2)
        channel=ch,  # A(0),B(1)
        file_switch=0,  # Don't load settings from a file
        filename=defaultstr(''),
        rf_freq=freq,  # Center frequency in Hz
        samp_rate=sample_rate,
        oversample=0,  # 0(default),1,2,4,8,16,32
        calibration_ch0=1 if calibration and ch == 0 else 0,
        calibr_bandw_ch0=60e6,
        calibration_ch1=1 if calibration and ch == 1 else 0,
        calibr_bandw_ch1=60e6,
        lna_path_mini=lna_path,  # LNAH(1),LNAW(3)
        lna_path_ch0=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        lna_path_ch1=lna_path,  # no path(0),LNAH(1),LNAL(2),LNAW(3)
        analog_filter_ch0=1,
        analog_bandw_ch0=if_bandwidth,
        analog_filter_ch1=1,
        analog_bandw_ch1=if_bandwidth,
        digital_filter_ch0=1,
        digital_bandw_ch0=if_bandwidth,
        digital_filter_ch1=1,
        digital_bandw_ch1=if_bandwidth,
        gain_dB_ch0=gain,
        gain_dB_ch1=gain,
        nco_freq_ch0=0,
        nco_freq_ch1=0,
        cmix_mode_ch0=0,  # UPCONVERT(0), DOWNCONVERT(1)
        cmix_mode_ch1=0,  # UPCONVERT(0), DOWNCONVERT(1)
    )
Example #42
0
 def __init__(self,
         osmo_device,
         rx,
         name,
         tuning,
         sample_rate):
     gr.hier_block2.__init__(
         self, defaultstr('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)
Example #43
0
 def __init__(self, info_format, array_format):
     # TODO: Document the format parameters
     self.__info_format = info_format
     self.__array_format = defaultstr(array_format)
Example #44
0
 def to_json(self):
     unpacker = array.array(defaultstr('b'))
     unpacker.fromstring(self.data)
     return [self.info, unpacker.tolist()]
Example #45
0
 def __init__(self, name, add_transmitters):
     gr.hier_block2.__init__(
         self, defaultstr(type(self).__name__ + ' ' + 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 = CellDict(dynamic=True)
     
     self.__transmitters_cs = CollectionState(self.__transmitters)
     
     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
     if add_transmitters:
         add_modulator(0.0, 'usb', 'USB')
         add_modulator(10e3, 'am', 'AM')
         add_modulator(30e3, 'fm', 'NFM')
         add_modulator(50e3, 'rtty', 'RTTY', message='The quick brown fox jumped over the lazy dog.\n')
         add_modulator(80e3, 'chirp', ChirpModulator)
     
     if signals:
         for bus_input, signal in enumerate(signals):
             self.connect(signal, (self.__bus, bus_input))
     else:
         # kludge up a correct-sample-rate no-op
         self.connect(
             audio_signal,
             blocks.multiply_const_ff(0),
             make_resampler(audio_rate, rf_rate),
             blocks.float_to_complex(),
             self.__bus)
     
     self.__signal_type = SignalType(
         kind='IQ',
         sample_rate=rf_rate)
     self.__usable_bandwidth = RangeT([(-rf_rate / 2, rf_rate / 2)])
Example #46
0
from six.moves import urllib

from twisted.web import http
from twisted.web.resource import Resource
from twisted.internet import endpoints
from twisted.python.filepath import FilePath
from twisted.python.util import sibpath
from twisted.web import template
from twisted.web.server import Site

from shinysdr.i.json import serialize
from shinysdr.i.pycompat import defaultstr
from shinysdr.i.roots import IEntryPoint

# TODO: Change this constant to something more generic, but save that for when we're changing the URL layout for other reasons anyway.
CAP_OBJECT_PATH_ELEMENT = defaultstr('radio')
AUDIO_STREAM_PATH_ELEMENT = 'audio-stream'
UNIQUE_PUBLIC_CAP = 'public'


static_resource_path = sibpath(__file__, '../webstatic')
template_path = sibpath(__file__, '../webparts')
template_filepath = FilePath(template_path)
deps_path = sibpath(__file__, '../../deps')


class IWebEntryPoint(IEntryPoint):
    def get_entry_point_resource(wcommon):
        """Returns a twisted.web.resource.IResource."""

Example #47
0
def _make_cap_url(cap):
    assert isinstance(cap, six.text_type)
    return defaultstr('/' + urllib.parse.quote(cap.encode('utf-8'), safe='') + '/')
Example #48
0
 def test_flowgraph_page(self):
     response, _data = yield http_get(the_reactor, self.url + defaultstr('flow-graph'))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'), ['image/png'])
Example #49
0
 def test_flowgraph_page(self):
     response, _data = yield http_get(the_reactor,
                                      self.url + defaultstr('flow-graph'))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'),
                      ['image/png'])
Example #50
0
def _make_cap_url(cap):
    assert isinstance(cap, six.text_type)
    return defaultstr('/' + urllib.parse.quote(cap.encode('utf-8'), safe='') +
                      '/')
Example #51
0
 def test_manifest(self):
     response, data = yield http_get(the_reactor, urljoin(self.url, defaultstr('/client/web-app-manifest.json')))
     self.assertEqual(response.code, http.OK)
     self.assertEqual(response.headers.getRawHeaders('Content-Type'), ['application/manifest+json'])
     manifest = json.loads(data)
     self.assertEqual(manifest['name'], 'test title')
Example #52
0
    def __init__(self,
                 mode,
                 input_rate,
                 output_rate,
                 demod_class=None,
                 freq=0.0,
                 quiet=False):
        gr.hier_block2.__init__(self,
                                type(self).__name__,
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(2, 2, gr.sizeof_float))

        if demod_class is None:
            mode_def = lookup_mode(mode)
            if mode_def is None:
                raise Exception(
                    '{}: No demodulator registered for mode {!r}, only {!r}'.
                    format(
                        type(self).__name__, mode,
                        [md.mode for md in get_modes()]))
            demod_class = mode_def.demod_class

        context = _DemodulatorAdapterContext(adapter=self, freq=freq)

        demod = self.__demodulator = IDemodulator(
            demod_class(mode=mode, input_rate=input_rate, context=context))
        self.connect(self, demod)

        output_type = demod.get_output_type()
        demod_output_rate = output_type.get_sample_rate()
        same_rate = demod_output_rate == output_rate
        stereo = output_type.get_kind() == 'STEREO'

        # connect outputs, resampling and adapting mono/stereo as needed
        # TODO: Make the logic for this in receiver.py reusable?
        if output_type.get_kind() == 'NONE':
            # TODO: produce correct sample rate of zeroes and maybe a warning
            dummy = blocks.vector_source_f([])
            self.connect(dummy, (self, 0))
            self.connect(dummy, (self, 1))
        else:
            if stereo:
                splitter = blocks.vector_to_streams(gr.sizeof_float, 2)
                self.connect(demod, splitter)
            if same_rate:
                if stereo:
                    self.connect((splitter, 0), (self, 0))
                    self.connect((splitter, 1), (self, 1))
                else:
                    self.connect(demod, (self, 0))
                    self.connect(demod, (self, 1))
            else:
                if not quiet:
                    gr.log.info(
                        defaultstr(
                            '{}: Native {} demodulated rate is {}; resampling to {}'
                            .format(
                                type(self).__name__, mode, demod_output_rate,
                                output_rate)))
                if stereo:
                    self.connect((splitter, 0),
                                 make_resampler(demod_output_rate,
                                                output_rate), (self, 0))
                    self.connect((splitter, 1),
                                 make_resampler(demod_output_rate,
                                                output_rate), (self, 1))
                else:
                    resampler = make_resampler(demod_output_rate, output_rate)
                    self.connect(demod, resampler, (self, 0))
                    self.connect(resampler, (self, 1))
Example #53
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.)
    
    if not _available:
        raise Exception('OsmoSDRDevice: gr-osmosdr Python bindings not found; cannot create device')
    
    osmo_device = defaultstr(osmo_device)
    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)