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)
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)
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)
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)
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)
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)
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, ))
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)
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)
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)
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)
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) )
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')
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 []
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 []
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)
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)
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())
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))
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())
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)
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)
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'])
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')
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)
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))
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')
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)
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'])
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)
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)
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)
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)
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() ])
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) )
def __init__(self, source, device_type, lna_path, name, tuning, sample_rate): gr.hier_block2.__init__( self, defaultstr('RX ' + name), gr.io_signature(0, 0, 0), gr.io_signature(1, 1, gr.sizeof_gr_complex * 1), ) self.__source = source self.__name = name self.__tuning = tuning self.connect(self.__source, self) # State of the source that there are no getters for, so we must keep our own copy of self.__track_gain = 50. source.set_gain(int(self.__track_gain), ch) self.__track_bandwidth = max(sample_rate / 2, 1.5e6) source.set_analog_filter(True, self.__track_bandwidth, ch) self.__lna_path_type = EnumT({ LNANONE: 'None', LNAH: 'LNAH', LNAL: 'LNAL', LNAW: 'LNAW', }) if device_type == LimeSDRMini: self.__lna_path_type = EnumT({ LNAH: 'LNAH', LNAW: 'LNAW', }) self.__track_lna_path = lna_path self.__signal_type = SignalType( kind='IQ', sample_rate=sample_rate) self.__usable_bandwidth = tuning.calc_usable_bandwidth(sample_rate)
def __init__(self, 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)
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()])
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)
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)
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) )
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)
def __init__(self, info_format, array_format): # TODO: Document the format parameters self.__info_format = info_format self.__array_format = defaultstr(array_format)
def to_json(self): unpacker = array.array(defaultstr('b')) unpacker.fromstring(self.data) return [self.info, unpacker.tolist()]
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)])
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."""
def _make_cap_url(cap): assert isinstance(cap, six.text_type) return defaultstr('/' + urllib.parse.quote(cap.encode('utf-8'), safe='') + '/')
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'])
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'])
def _make_cap_url(cap): assert isinstance(cap, six.text_type) return defaultstr('/' + urllib.parse.quote(cap.encode('utf-8'), safe='') + '/')
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')
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))
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)