class PollerCellsSpecimen(ExportedState): """Helper for TestPoller""" foo = None def __init__(self): self.subscribable = LooseCell(key='subscribable', value='', type=str) def state_def(self, callback): super(PollerCellsSpecimen, self).state_def(callback) # TODO make this possible to be decorator style callback(self.subscribable) # force worst-case def state_is_dynamic(self): return True @exported_value() def get_foo(self): return self.foo @setter def set_foo(self, value): self.foo = value def get_subscribable(self): return self.subscribable.get() def set_subscribable(self, value): self.subscribable.set(value)
class _OsmoSDRTuning(object): def __init__(self, profile, correction_ppm, osmo_block): self.__profile = profile self.__correction_ppm = correction_ppm self.__osmo_block = osmo_block self.__vfo_cell = LooseCell( key='freq', value=0.0, # TODO: Eventually we'd like to be able to make the freq range vary dynamically with the correction setting type=convert_osmosdr_range( osmo_block.get_freq_range(ch), strict=False, transform=self.from_hardware_freq, unit=units.Hz, add_zero=profile.e4000), writable=True, persists=True, post_hook=self.__set_freq) def __set_freq(self, freq): self.__osmo_block.set_center_freq(self.to_hardware_freq(freq)) def to_hardware_freq(self, effective_freq): if abs(effective_freq) < 1e-2 and self.__profile.e4000: # Quirk: Tuning to 3686.6-3730 MHz on the E4000 causes operation effectively at 0Hz. # Original report: <http://www.reddit.com/r/RTLSDR/comments/12d2wc/a_very_surprising_discovery/> return 3700e6 else: return effective_freq * (1 - 1e-6 * self.__correction_ppm) def from_hardware_freq(self, freq): freq = freq / (1 - 1e-6 * self.__correction_ppm) if 3686.6e6 <= freq <= 3730e6 and self.__profile.e4000: freq = 0.0 return freq def get_vfo_cell(self): return self.__vfo_cell def get_correction_ppm(self): return self.__correction_ppm def set_correction_ppm(self, value): self.__correction_ppm = float(value) # Not using the osmosdr feature because changing it at runtime produces glitches like the sample rate got changed; therefore we emulate it ourselves. TODO: I am informed that using set_freq_corr can correct sample-clock error, so we ought to at least use it on init. # self.osmosdr_source_block.set_freq_corr(value, 0) self.__set_freq(self.__vfo_cell.get()) def calc_usable_bandwidth(self, sample_rate): passband = sample_rate * (3 / 8) # 3/4 of + and - halves if self.__profile.dc_offset: epsilon = 1.0 # RangeT has only inclusive bounds, so we need a nonzero value. return RangeT([(-passband, -epsilon), (epsilon, passband)]) else: return RangeT([(-passband, passband)]) def set_block(self, value): self.__osmo_block = value if self.__osmo_block is not None: self.__set_freq(self.__vfo_cell.get())
class PollerCellsSpecimen(ExportedState): """Helper for TestPoller""" foo = None def __init__(self): self.subscribable = LooseCell(key='subscribable', value='', type=str) def state_def(self, callback): super(PollerCellsSpecimen, self).state_def(callback) # TODO make this possible to be decorator style callback(self.subscribable) # force worst-case def state_is_dynamic(self): return True @exported_value(changes='continuous', persists=False) def get_foo(self): return self.foo @setter def set_foo(self, value): self.foo = value def get_subscribable(self): return self.subscribable.get() def set_subscribable(self, value): self.subscribable.set(value)
class PollerCellsSpecimen(ExportedState): """Helper for TestPoller""" foo = None def __init__(self): self.subscribable = LooseCell(value='', type=str, writable=True) def state_def(self): for d in super(PollerCellsSpecimen, self).state_def(): yield d # TODO make this possible to be decorator style yield 'subscribable', self.subscribable # force worst-case def state_is_dynamic(self): return True @exported_value(changes='continuous', persists=False) def get_foo(self): return self.foo @setter def set_foo(self, value): self.foo = value def get_subscribable(self): return self.subscribable.get() def set_subscribable(self, value): self.subscribable.set(value)
def __init_center_cell(self): base_freq_cell = self.__rx_main.state()[_FREQ_CELL_KEY] mode_cell = self.__rx_main.state()['MD'] sidetone_cell = self.state()['CW'] iq_offset_cell = LooseCell(key='iq_offset', value=0.0, type=float) self.__iq_center_cell = ViewCell( base=base_freq_cell, get_transform=lambda x: x + iq_offset_cell.get(), set_transform=lambda x: x - iq_offset_cell.get(), key=_FREQ_CELL_KEY, type=base_freq_cell.type(), # runtime variable... writable=True, persists=base_freq_cell.metadata().persists) def changed_iq(_value=None): # TODO this is KX3-specific mode = mode_cell.get() if mode == 'CW': iq_offset = sidetone_cell.get() elif mode == 'CW-REV': iq_offset = -sidetone_cell.get() elif mode == 'AM' or mode == 'FM': iq_offset = 11000.0 else: # USB, LSB, other iq_offset = 0.0 iq_offset_cell.set(iq_offset) self.__iq_center_cell.changed_transform() # TODO bad practice mode_cell._subscribe_immediate(changed_iq) sidetone_cell._subscribe_immediate(changed_iq) changed_iq()
def setUp(self): self.lc = LooseCell(value=0, key='a', ctor=int) self.vc = ViewCell(base=self.lc, get_transform=lambda x: x + 1, set_transform=lambda x: x - 1, key='b', ctor=int)
def setUp(self): self.lc = LooseCell(value=0, type=RangeT([(-100, 100)])) self.delta = 1 self.vc = ViewCell(base=self.lc, get_transform=lambda x: x + self.delta, set_transform=lambda x: x - self.delta, type=int)
class _OsmoSDRTuning(object): def __init__(self, profile, correction_ppm, osmo_block): self.__profile = profile self.__correction_ppm = correction_ppm self.__osmo_block = osmo_block self.__vfo_cell = LooseCell( key='freq', value=0.0, # TODO: Eventually we'd like to be able to make the freq range vary dynamically with the correction setting type=convert_osmosdr_range( osmo_block.get_freq_range(ch), strict=False, transform=self.from_hardware_freq, unit=units.Hz, add_zero=profile.e4000), writable=True, persists=True, post_hook=self.__set_freq) def __set_freq(self, freq): self.__osmo_block.set_center_freq(self.to_hardware_freq(freq)) def to_hardware_freq(self, effective_freq): if abs(effective_freq) < 1e-2 and self.__profile.e4000: # Quirk: Tuning to 3686.6-3730 MHz on the E4000 causes operation effectively at 0Hz. # Original report: <http://www.reddit.com/r/RTLSDR/comments/12d2wc/a_very_surprising_discovery/> return 3700e6 else: return effective_freq * (1 - 1e-6 * self.__correction_ppm) def from_hardware_freq(self, freq): freq = freq / (1 - 1e-6 * self.__correction_ppm) if 3686.6e6 <= freq <= 3730e6 and self.__profile.e4000: freq = 0.0 return freq def get_vfo_cell(self): return self.__vfo_cell def get_correction_ppm(self): return self.__correction_ppm def set_correction_ppm(self, value): self.__correction_ppm = float(value) # Not using the osmosdr feature because changing it at runtime produces glitches like the sample rate got changed; therefore we emulate it ourselves. TODO: I am informed that using set_freq_corr can correct sample-clock error, so we ought to at least use it on init. # self.osmosdr_source_block.set_freq_corr(value, 0) self.__set_freq(self.__vfo_cell.get()) def calc_usable_bandwidth(self, sample_rate): passband = sample_rate * (3 / 8) # 3/4 of + and - halves if self.__profile.dc_offset: epsilon = 1.0 # TODO: Put width in the profile. return RangeT([(-passband, -epsilon), (epsilon, passband)]) else: return RangeT([(-passband, passband)]) def set_block(self, value): self.__osmo_block = value if self.__osmo_block is not None: self.__set_freq(self.__vfo_cell.get())
def __init__(self, lime_block): self.__lime_block = lime_block self.__vfo_cell = LooseCell(value=0.0, type=RangeT([(10e6, 3500e6)], strict=False, unit=units.Hz), writable=True, persists=True, post_hook=self.__set_freq)
def setUp(self): self.lc = LooseCell(value=0, type=RangeT([(-100, 100)]), writable=True) self.delta = 1 self.vc = ViewCell(base=self.lc, get_transform=lambda x: x + self.delta, set_transform=lambda x: x - self.delta, type=int, writable=True, interest_tracker=LoopbackInterestTracker())
def __init__(self, signal_type=None, enable_scope=False, freq_resolution=4096, time_length=2048, window_type=windows.WIN_BLACKMAN_HARRIS, frame_rate=30.0, input_center_freq=0.0, paused=False, context=None): assert isinstance(signal_type, SignalType) assert context is not None itemsize = signal_type.get_itemsize() gr.hier_block2.__init__( self, type(self).__name__, gr.io_signature(1, 1, itemsize), gr.io_signature(0, 0, 0), ) # constant parameters self.__power_offset = 40 # TODO autoset or controllable self.__itemsize = itemsize self.__context = context self.__enable_scope = enable_scope # settable parameters self.__signal_type = signal_type self.__freq_resolution = int(freq_resolution) self.__time_length = int(time_length) self.__window_type = _window_type_enum(window_type) self.__frame_rate = float(frame_rate) self.__input_center_freq = float(input_center_freq) self.__paused = bool(paused) # interest tracking # this is indirect because we ignore interest when paused self.__interested_cell = LooseCell(type=bool, value=False, writable=False, persists=False) self.__has_subscriptions = False self.__interest = InterestTracker(self.__cell_interest_callback) self.__fft_queue = gr.msg_queue() self.__scope_queue = gr.msg_queue() # stuff created by __do_connect self.__gate = None self.__fft_sink = None self.__scope_sink = None self.__frame_dec = None self.__frame_rate_to_decimation_conversion = 0.0 self.__do_connect()
def __init__(self, signal_type=None, enable_scope=False, freq_resolution=4096, time_length=2048, frame_rate=30.0, input_center_freq=0.0, paused=False, context=None): assert isinstance(signal_type, SignalType) assert context is not None itemsize = signal_type.get_itemsize() gr.hier_block2.__init__( self, type(self).__name__, gr.io_signature(1, 1, itemsize), gr.io_signature(0, 0, 0), ) # constant parameters self.__power_offset = 40 # TODO autoset or controllable self.__itemsize = itemsize self.__context = context self.__enable_scope = enable_scope # settable parameters self.__signal_type = signal_type self.__freq_resolution = int(freq_resolution) self.__time_length = int(time_length) self.__frame_rate = float(frame_rate) self.__input_center_freq = float(input_center_freq) self.__paused = bool(paused) self.__interested_cell = LooseCell(key='interested', type=bool, value=False, writable=False, persists=False) # blocks self.__gate = None self.__fft_sink = None self.__scope_sink = None self.__scope_chunker = None self.__before_fft = None self.__logpwrfft = None self.__overlapper = None self.__rebuild() self.__connect()
def __init_center_cell(self): base_freq_cell = self.__rx_main.state()[_FREQ_CELL_KEY] mode_cell = self.__rx_main.state()['MD'] sidetone_cell = self.state()['CW'] submode_cell = self.state()['DT'] iq_offset_cell = LooseCell(key='iq_offset', value=0.0, type=float) self.__iq_center_cell = ViewCell( base=base_freq_cell, get_transform=lambda x: x + iq_offset_cell.get(), set_transform=lambda x: x - iq_offset_cell.get(), key=_FREQ_CELL_KEY, type=base_freq_cell.type(), # runtime variable... writable=True, persists=base_freq_cell.metadata().persists) def changed_iq(_value=None): # TODO this is KX3-specific mode = mode_cell.get() if mode == 'CW': iq_offset = sidetone_cell.get() elif mode == 'CW-REV': iq_offset = -sidetone_cell.get() elif mode == 'AM' or mode == 'FM': iq_offset = 11000.0 elif mode == 'DATA' or mode == 'DATA-REV': submode = submode_cell.get() if submode == 0: # "DATA A", SSB with less processing iq_offset = 0.0 # ??? elif submode == 1: # "AFSK A", SSB with RTTY style filter iq_offset = 0.0 # ??? elif submode == 2: # "FSK D", RTTY iq_offset = 900.0 elif submode == 3: # "PSK D", PSK31 iq_offset = 1000.0 # I think so... else: iq_offset = 0 # fallback if mode == 'DATA-REV': iq_offset = -iq_offset else: # USB, LSB, other iq_offset = 0.0 iq_offset_cell.set(iq_offset) self.__iq_center_cell.changed_transform() # TODO bad practice mode_cell._subscribe_immediate(changed_iq) sidetone_cell._subscribe_immediate(changed_iq) submode_cell._subscribe_immediate(changed_iq) changed_iq()
def test_specify_all_metadata(self): # using LooseCell as an arbitrary concrete subclass cell = LooseCell( value=0, type=int, persists=False, # the non-default value label='mylabel', description='mydescription', sort_key='mysortkey') self.assertEqual(cell.metadata().value_type, to_value_type(int)) self.assertEqual(cell.metadata().persists, False) self.assertEqual(cell.metadata().naming, EnumRow( label='mylabel', description='mydescription', sort_key='mysortkey'))
def __init__(self, profile, correction_ppm, osmo_block): self.__profile = profile self.__correction_ppm = correction_ppm self.__osmo_block = osmo_block self.__vfo_cell = LooseCell( key='freq', value=0.0, # TODO: Eventually we'd like to be able to make the freq range vary dynamically with the correction setting type=convert_osmosdr_range(osmo_block.get_freq_range(ch), strict=False, transform=self.from_hardware_freq, add_zero=profile.e4000), writable=True, persists=True, post_hook=self.__set_freq)
def __init_center_cell(self): base_freq_cell = self.__rx_main.state()[_FREQ_CELL_KEY] mode_cell = self.__rx_main.state()['MD'] sidetone_cell = self.state()['CW'] submode_cell = self.state()['DT'] iq_offset_cell = LooseCell(value=0.0, type=float, writable=True) self.__iq_center_cell = ViewCell( base=base_freq_cell, get_transform=lambda x: x + iq_offset_cell.get(), set_transform=lambda x: x - iq_offset_cell.get(), type=base_freq_cell.type(), # runtime variable... writable=True, persists=base_freq_cell.metadata().persists) def changed_iq(_value=None): # TODO this is KX3-specific mode = mode_cell.get() if mode == 'CW': iq_offset = sidetone_cell.get() elif mode == 'CW-REV': iq_offset = -sidetone_cell.get() elif mode == 'AM' or mode == 'FM': iq_offset = 11000.0 elif mode == 'DATA' or mode == 'DATA-REV': submode = submode_cell.get() if submode == 0: # "DATA A", SSB with less processing iq_offset = 0.0 # ??? elif submode == 1: # "AFSK A", SSB with RTTY style filter iq_offset = 0.0 # ??? elif submode == 2: # "FSK D", RTTY iq_offset = 900.0 elif submode == 3: # "PSK D", PSK31 iq_offset = 1000.0 # I think so... else: iq_offset = 0 # fallback if mode == 'DATA-REV': iq_offset = -iq_offset else: # USB, LSB, other iq_offset = 0.0 iq_offset_cell.set(iq_offset) self.__iq_center_cell.changed_transform() # TODO bad practice mode_cell._subscribe_immediate(changed_iq) sidetone_cell._subscribe_immediate(changed_iq) submode_cell._subscribe_immediate(changed_iq) changed_iq()
class _LimeSDRTuning(object): def __init__(self, lime_block): self.__lime_block = lime_block self.__vfo_cell = LooseCell(value=0.0, type=RangeT([(10e6, 3500e6)], strict=False, unit=units.Hz), writable=True, persists=True, post_hook=self.__set_freq) def __set_freq(self, freq): self.__lime_block.set_rf_freq(freq) def get_vfo_cell(self): return self.__vfo_cell def calc_usable_bandwidth(self, total_bandwidth): # Assume right up against the edges of the filter are unusable. passband = total_bandwidth * (3 / 8) # 3/4 of + and - halves return RangeT([(-passband, passband)]) def set_block(self, value): self.__lime_block = value if self.__lime_block is not None: self.__set_freq(self.__vfo_cell.get())
class _LimeSDRTuning(object): def __init__(self, lime_block): self.__lime_block = lime_block self.__vfo_cell = LooseCell( value=0.0, type=RangeT([(10e6, 3500e6)], strict=False, unit=units.Hz), writable=True, persists=True, post_hook=self.__set_freq) def __set_freq(self, freq): self.__lime_block.set_rf_freq(freq) def get_vfo_cell(self): return self.__vfo_cell def calc_usable_bandwidth(self, total_bandwidth): # Assume right up against the edges of the filter are unusable. passband = total_bandwidth * (3 / 8) # 3/4 of + and - halves return RangeT([(-passband, passband)]) def set_block(self, value): self.__lime_block = value if self.__lime_block is not None: self.__set_freq(self.__vfo_cell.get())
def test_vfos(self): d = merge_devices([ Device(vfo_cell=_ConstantVFOCell(1)), Device(vfo_cell=LooseCell( value=0, type=RangeT([(10, 20)]), writable=True)) ]) self.assertTrue(d.get_vfo_cell().isWritable())
def AudioDevice( rx_device='', # may be used positionally, not recommented tx_device=None, name=None, sample_rate=44100, quadrature_as_stereo=False): rx_device = str(rx_device) if tx_device is not None: tx_device = str(tx_device) if name is None: full_name = u'Audio ' + rx_device if tx_device is not None: full_name += '/' + tx_device else: full_name = unicode(name) rx_driver = _AudioRXDriver(device_name=rx_device, sample_rate=sample_rate, quadrature_as_stereo=quadrature_as_stereo) if tx_device is not None: tx_driver = _AudioTXDriver(device_name=tx_device, sample_rate=sample_rate, quadrature_as_stereo=quadrature_as_stereo) else: tx_driver = nullExportedState return Device(name=full_name, vfo_cell=LooseCell(key='freq', value=0.0, ctor=Range([(0.0, 0.0)]), writable=True, persists=False), rx_driver=rx_driver, tx_driver=tx_driver)
def _ConstantVFOCell(value): value = float(value) return LooseCell(key='freq', value=value, ctor=Range([(value, value)]), writable=False, persists=False)
def _ConstantVFOCell(value): value = float(value) return LooseCell( value=value, type=RangeT([(value, value)]), writable=False, persists=False)
def __init__(self, adapter, freq): self.__adapter = adapter self.__freq_cell = LooseCell( value=freq, type=float, persists=False, writable=False)
def SimulatedDevice(name='Simulated RF', freq=0.0): return Device(name=name, vfo_cell=LooseCell(key='freq', value=freq, ctor=Range([(freq, freq)]), writable=True, persists=False), rx_driver=_SimulatedRXDriver(name))
def setUp(self): self.lc = LooseCell(value=0, type=RangeT([(-100, 100)])) self.delta = 1 self.vc = ViewCell( base=self.lc, get_transform=lambda x: x + self.delta, set_transform=lambda x: x - self.delta, type=int)
def _RetuningTestDevice(freq, has_dc_offset): return Device( rx_driver=_RetuningTestRXDriver(has_dc_offset), vfo_cell=LooseCell( value=freq, type=RangeT([(-1e9, 1e9)]), # TODO kludge magic numbers writable=True, persists=False))
def setUp(self): self.lc = LooseCell(value=0, key='a', type=int) self.vc = ViewCell( base=self.lc, get_transform=lambda x: x + 1, set_transform=lambda x: x - 1, key='b', type=int)
def __init__(self, signal_type=None, enable_scope=False, freq_resolution=4096, time_length=2048, window_type=windows.WIN_BLACKMAN_HARRIS, frame_rate=30.0, input_center_freq=0.0, paused=False, context=None): assert isinstance(signal_type, SignalType) assert context is not None itemsize = signal_type.get_itemsize() gr.hier_block2.__init__( self, type(self).__name__, gr.io_signature(1, 1, itemsize), gr.io_signature(0, 0, 0), ) # constant parameters self.__power_offset = 40 # TODO autoset or controllable self.__itemsize = itemsize self.__context = context self.__enable_scope = enable_scope # settable parameters self.__signal_type = signal_type self.__freq_resolution = int(freq_resolution) self.__time_length = int(time_length) self.__window_type = _window_type_enum(window_type) self.__frame_rate = float(frame_rate) self.__input_center_freq = float(input_center_freq) self.__paused = bool(paused) # interest tracking # this is indirect because we ignore interest when paused self.__interested_cell = LooseCell(type=bool, value=False, writable=False, persists=False) self.__has_subscriptions = False self.__interest = InterestTracker(self.__cell_interest_callback) self.__fft_cell = ElementSinkCell( info_getter=self._get_fft_info, type=BulkDataT(array_format='b', info_format='dff'), interest_tracker=self.__interest, label='Spectrum') self.__scope_cell = ElementSinkCell( info_getter=self._get_scope_info, type=BulkDataT(array_format='f', info_format='d'), interest_tracker=self.__interest, label='Scope') # stuff created by __do_connect self.__gate = None self.__frame_dec = None self.__frame_rate_to_decimation_conversion = 0.0 self.__do_connect()
class TestViewCell(unittest.TestCase): def setUp(self): self.lc = LooseCell(value=0, key='a', type=int) self.vc = ViewCell( base=self.lc, get_transform=lambda x: x + 1, set_transform=lambda x: x - 1, key='b', type=int) def test_get_set(self): self.assertEqual(0, self.lc.get()) self.assertEqual(1, self.vc.get()) self.vc.set(2) self.assertEqual(1, self.lc.get()) self.assertEqual(2, self.vc.get()) self.lc.set(3) self.assertEqual(3, self.lc.get()) self.assertEqual(4, self.vc.get()) def test_subscription(self): fired = [] def f(): fired.append(self.vc.get()) self.vc.subscribe(f) self.lc.set(1) self.assertEqual([2], fired)
def __init__(self): self.messages = [] # 12,345,678 Hz, all the time, every day. self.__absolute_frequency_cell = LooseCell(value=12345678, type=float, writable=False, persists=False) verifyObject(IDemodulatorContext, self) # Ensure we are a good fake.
def _install_cells(self, callback, send, encoding): callback( LooseCell( key=self.__name, type=self.__type, value=u'', writable=True, persists=True, post_hook=lambda value: send(unicode(value).encode(encoding))))
def _cells(self, send, encoding): # TODO: Autogenerate unique keys instead of requiring __name to be unique. yield self.__name, LooseCell( type=self.__type, value=u'', writable=True, persists=True, post_hook=lambda value: send(unicode(value).encode(encoding)), label=self.__name)
def setUp(self): self.lc = LooseCell(value=0, type=RangeT([(-100, 100)]), writable=True) self.delta = 1 self.vc = ViewCell( base=self.lc, get_transform=lambda x: x + self.delta, set_transform=lambda x: x - self.delta, type=int, writable=True, interest_tracker=LoopbackInterestTracker())
def SimulatedDevice(name='Simulated RF', freq=0.0, allow_tuning=False): return Device( name=name, vfo_cell=LooseCell( key='freq', value=freq, ctor=Range([(-1e9, 1e9)]) if allow_tuning else Range([(freq, freq)]), # TODO kludge magic numbers writable=True, persists=False), rx_driver=_SimulatedRXDriver(name))
def __init__(self, lime_block): self.__lime_block = lime_block self.__vfo_cell = LooseCell( value=0.0, type=RangeT([(10e6, 3500e6)], strict=False, unit=units.Hz), writable=True, persists=True, post_hook=self.__set_freq)
def make_cell(self, protocol, is_sub): key = self.__command_name def send(value): protocol.send_command( _format_command(key, self.__syntax.format(value), is_sub=is_sub)) if self.__has_sub == (is_sub is not None): return key, LooseCell(post_hook=send, **self.__cell_kwargs)
def _install_cells(self, callback, send, encoding): callback( LooseCell( # TODO: Autogenerate unique keys instead of requiring __name to be unique. key=self.__name, type=self.__type, value=u'', writable=True, persists=True, post_hook=lambda value: send(unicode(value).encode(encoding)), label=self.__name))
def _install_cell(self, name, is_level, writable, callback, caps): # this is a function for the sake of the closure variables if name == 'Frequency': cell_name = 'freq' # consistency with our naming scheme elsewhere, also IHasFrequency else: cell_name = name if is_level: # TODO: Use range info from hamlib if available if name == 'STRENGTH level': vtype = Range([(-54, 50)], strict=False) elif name == 'SWR level': vtype = Range([(1, 30)], strict=False) elif name == 'RFPOWER level': vtype = Range([(0, 100)], strict=False) else: vtype = Range([(-10, 10)], strict=False) elif name == 'Mode' or name == 'TX Mode': # kludge vtype = Enum({x: x for x in caps['Mode list'].strip().split(' ')}) elif name == 'VFO' or name == 'TX VFO': vtype = Enum({x: x for x in caps['VFO list'].strip().split(' ')}) else: vtype = self._info[name] def updater(strval): try: if vtype is bool: value = bool(int(strval)) else: value = vtype(strval) except ValueError: value = unicode(strval) cell.set_internal(value) def actually_write_value(value): if vtype is bool: self._ehs_set(name, str(int(value))) else: self._ehs_set(name, str(vtype(value))) cell = LooseCell( key=cell_name, value='placeholder', type=vtype, writable=writable, persists=False, post_hook=actually_write_value, label=name) # TODO: supply label values from _info table self._cell_updaters[name] = updater updater(self._ehs_get(name)) callback(cell)
def receive(self, message_wrapper): """Implements ITelemetryObject.""" self.__last_heard_time = message_wrapper.receive_time for k, v in message_wrapper.message.iteritems(): if _message_field_is_id.get(k, False) or k == u'time': continue if k not in self.__cells: self.__cells[k] = LooseCell(key=k, value=None, type=object, writable=False, persists=False) self.__cells[k].set_internal(v)
def __init__(self, profile, correction_ppm, source): self.__profile = profile self.__correction_ppm = correction_ppm self.__source = source self.__vfo_cell = LooseCell( key='freq', value=0.0, # TODO: Eventually we'd like to be able to make the freq range vary dynamically with the correction setting ctor=convert_osmosdr_range( source.get_freq_range(ch), strict=False, transform=self.from_hardware_freq, add_zero=profile.e4000), writable=True, persists=True, post_hook=self.__set_freq)
def __init__(self, profile, correction_ppm, osmo_block): self.__profile = profile self.__correction_ppm = correction_ppm self.__osmo_block = osmo_block self.__vfo_cell = LooseCell( value=0.0, # TODO: Eventually we'd like to be able to make the freq range vary dynamically with the correction setting type=convert_osmosdr_range( osmo_block.get_freq_range(ch), strict=False, transform=self.from_hardware_freq, unit=units.Hz, add_zero=profile.e4000), writable=True, persists=True, post_hook=self.__set_freq)
class TestLooseCell(unittest.TestCase): def setUp(self): self.lc = LooseCell(value=0, type=int) def test_get_set(self): self.assertEqual(0, self.lc.get()) self.lc.set(1) self.assertEqual(1, self.lc.get()) self.lc.set(2.1) self.assertEqual(2, self.lc.get()) def test_subscription(self): st = CellSubscriptionTester(self.lc) self.lc.set(1) st.expect_now(1) st.unsubscribe() self.lc.set(2) st.advance() # check for unwanted callbacks def test_repr(self): self.assertEqual(repr(self.lc), '<LooseCell PythonT(<type \'int\'>) 0>')
def __init__(self): self.subscribable = LooseCell(value='', type=str, writable=True)
class MonitorSink(gr.hier_block2, ExportedState): """Convenience wrapper around all the bits and pieces to display the signal spectrum to the client. The units of the FFT output are dB power/Hz (power spectral density) relative to unit amplitude (i.e. dBFS assuming the source clips at +/-1). Note this is different from the standard logpwrfft result of power _per bin_, which would be undesirably dependent on the sample rate and bin size. """ implements(IMonitor) def __init__(self, signal_type=None, enable_scope=False, freq_resolution=4096, time_length=2048, frame_rate=30.0, input_center_freq=0.0, paused=False, context=None): assert isinstance(signal_type, SignalType) assert context is not None itemsize = signal_type.get_itemsize() gr.hier_block2.__init__( self, type(self).__name__, gr.io_signature(1, 1, itemsize), gr.io_signature(0, 0, 0), ) # constant parameters self.__power_offset = 40 # TODO autoset or controllable self.__itemsize = itemsize self.__context = context self.__enable_scope = enable_scope # settable parameters self.__signal_type = signal_type self.__freq_resolution = int(freq_resolution) self.__time_length = int(time_length) self.__frame_rate = float(frame_rate) self.__input_center_freq = float(input_center_freq) self.__paused = bool(paused) self.__interested_cell = LooseCell(key='interested', type=bool, value=False, writable=False, persists=False) # blocks self.__gate = None self.__fft_sink = None self.__scope_sink = None self.__scope_chunker = None self.__before_fft = None self.__logpwrfft = None self.__overlapper = None self.__rebuild() self.__connect() def state_def(self, callback): super(MonitorSink, self).state_def(callback) # TODO make this possible to be decorator style callback(StreamCell(self, 'fft', type=BulkDataT(array_format='b', info_format='dff'), label='Spectrum')) callback(StreamCell(self, 'scope', type=BulkDataT(array_format='f', info_format='d'), label='Scope')) def __rebuild(self): if self.__signal_type.is_analytic(): input_length = self.__freq_resolution output_length = self.__freq_resolution self.__after_fft = None else: # use vector_to_streams to cut the output in half and discard the redundant part input_length = self.__freq_resolution * 2 output_length = self.__freq_resolution self.__after_fft = blocks.vector_to_streams(itemsize=output_length * gr.sizeof_float, nstreams=2) sample_rate = self.__signal_type.get_sample_rate() overlap_factor = int(math.ceil(_maximum_fft_rate * input_length / sample_rate)) # sanity limit -- OverlapGimmick is not free overlap_factor = min(16, overlap_factor) self.__gate = blocks.copy(gr.sizeof_gr_complex) self.__gate.set_enabled(not self.__paused) self.__fft_sink = MessageDistributorSink( itemsize=output_length * gr.sizeof_char, context=self.__context, migrate=self.__fft_sink, notify=self.__update_interested) self.__overlapper = _OverlapGimmick( size=input_length, factor=overlap_factor, itemsize=self.__itemsize) # Adjusts units so displayed level is independent of resolution and sample rate. Also throw in the packing offset compensation = to_dB(input_length / sample_rate) + self.__power_offset # TODO: Consider not using the logpwrfft block self.__logpwrfft = logpwrfft.logpwrfft_c( sample_rate=sample_rate * overlap_factor, fft_size=input_length, ref_scale=10.0 ** (-compensation / 20.0) * 2, # not actually using this as a reference scale value but avoiding needing to use a separate add operation to apply the unit change -- this expression is the inverse of what logpwrfft does internally frame_rate=self.__frame_rate, avg_alpha=1.0, average=False) # It would make slightly more sense to use unsigned chars, but blocks.float_to_uchar does not support vlen. self.__fft_converter = blocks.float_to_char(vlen=self.__freq_resolution, scale=1.0) self.__scope_sink = MessageDistributorSink( itemsize=self.__time_length * gr.sizeof_gr_complex, context=self.__context, migrate=self.__scope_sink, notify=self.__update_interested) self.__scope_chunker = blocks.stream_to_vector_decimator( item_size=gr.sizeof_gr_complex, sample_rate=sample_rate, vec_rate=self.__frame_rate, # TODO doesn't need to be coupled vec_len=self.__time_length) def __connect(self): self.__context.lock() try: self.disconnect_all() self.connect( self, self.__gate, self.__overlapper, self.__logpwrfft) if self.__after_fft is not None: self.connect(self.__logpwrfft, self.__after_fft) self.connect(self.__after_fft, self.__fft_converter, self.__fft_sink) self.connect((self.__after_fft, 1), blocks.null_sink(gr.sizeof_float * self.__freq_resolution)) else: self.connect(self.__logpwrfft, self.__fft_converter, self.__fft_sink) if self.__enable_scope: self.connect( self.__gate, self.__scope_chunker, self.__scope_sink) finally: self.__context.unlock() # non-exported def get_interested_cell(self): return self.__interested_cell def __update_interested(self): self.__interested_cell.set_internal(not self.__paused and ( self.__fft_sink.get_subscription_count() > 0 or self.__scope_sink.get_subscription_count() > 0)) @exported_value(type=SignalType, changes='explicit') def get_signal_type(self): return self.__signal_type # non-exported def set_signal_type(self, value): # TODO: don't rebuild if the rate did not change and the spectrum-sidedness of the type did not change assert self.__signal_type.compatible_items(value) self.__signal_type = value self.__rebuild() self.__connect() self.state_changed('signal_type') # non-exported def set_input_center_freq(self, value): self.__input_center_freq = float(value) @exported_value( type=RangeT([(2, 4096)], logarithmic=True, integer=True), changes='this_setter', label='Resolution', description='Frequency domain resolution; number of FFT bins.') def get_freq_resolution(self): return self.__freq_resolution @setter def set_freq_resolution(self, freq_resolution): self.__freq_resolution = freq_resolution self.__rebuild() self.__connect() @exported_value(type=RangeT([(1, 4096)], logarithmic=True, integer=True), changes='this_setter') def get_time_length(self): return self.__time_length @setter def set_time_length(self, value): self.__time_length = value self.__rebuild() self.__connect() @exported_value( type=RangeT([(1, _maximum_fft_rate)], logarithmic=True, integer=False), changes='this_setter', label='Rate', description='Number of FFT frames per second.') def get_frame_rate(self): return self.__frame_rate @setter def set_frame_rate(self, value): self.__logpwrfft.set_vec_rate(float(value)) self.__frame_rate = self.__logpwrfft.frame_rate() @exported_value(type=bool, changes='this_setter', label='Pause') def get_paused(self): return self.__paused @setter def set_paused(self, value): self.__paused = value self.__gate.set_enabled(not value) self.__update_interested() # exported via state_def def get_fft_info(self): return (self.__input_center_freq, self.__signal_type.get_sample_rate(), self.__power_offset) def get_fft_distributor(self): return self.__fft_sink # exported via state_def def get_scope_info(self): return (self.__signal_type.get_sample_rate(),) def get_scope_distributor(self): return self.__scope_sink
def setUp(self): self.lc = LooseCell( value=0, type=int, writable=True, interest_tracker=LoopbackInterestTracker())
class TestLooseCell(unittest.TestCase): def setUp(self): self.lc = LooseCell( value=0, type=int, writable=True, interest_tracker=LoopbackInterestTracker()) def test_get_set(self): self.assertEqual(0, self.lc.get()) self.lc.set(1) self.assertEqual(1, self.lc.get()) self.lc.set(2.1) self.assertEqual(2, self.lc.get()) def test_subscription(self): st = CellSubscriptionTester(self.lc) self.lc.set(1) st.expect_now(1) st.unsubscribe() self.lc.set(2) st.advance() # check for unwanted callbacks def test_repr(self): if six.PY2: self.assertEqual(repr(self.lc), '<LooseCell PythonT(<type \'int\'>) 0>') else: self.assertEqual(repr(self.lc), '<LooseCell PythonT(<class \'int\'>) 0>') def test_default_writability(self): self.assertFalse(LooseCell(value=0, type=int).isWritable()) def test_not_writable(self): self.lc = LooseCell(value=0, type=int, writable=False) self.assertRaises(Exception, lambda: self.lc.set(1)) self.assertEqual(self.lc.get(), 0)
def test_not_writable(self): self.lc = LooseCell(value=0, type=int, writable=False) self.assertRaises(Exception, lambda: self.lc.set(1)) self.assertEqual(self.lc.get(), 0)
class MonitorSink(gr.hier_block2, ExportedState): """Convenience wrapper around all the bits and pieces to display the signal spectrum to the client. The units of the FFT output are dB power/Hz (power spectral density) relative to unit amplitude (i.e. dBFS assuming the source clips at +/-1). Note this is different from the standard logpwrfft result of power _per bin_, which would be undesirably dependent on the sample rate and bin size. """ def __init__(self, signal_type=None, enable_scope=False, freq_resolution=4096, time_length=2048, frame_rate=30.0, input_center_freq=0.0, paused=False, context=None): assert isinstance(signal_type, SignalType) assert context is not None itemsize = signal_type.get_itemsize() gr.hier_block2.__init__( self, type(self).__name__, gr.io_signature(1, 1, itemsize), gr.io_signature(0, 0, 0), ) # constant parameters self.__power_offset = 40 # TODO autoset or controllable self.__itemsize = itemsize self.__context = context self.__enable_scope = enable_scope # settable parameters self.__signal_type = signal_type self.__freq_resolution = int(freq_resolution) self.__time_length = int(time_length) self.__frame_rate = float(frame_rate) self.__input_center_freq = float(input_center_freq) self.__paused = bool(paused) self.__interested_cell = LooseCell(type=bool, value=False, writable=False, persists=False) # stuff created by __do_connect self.__gate = None self.__fft_sink = None self.__scope_sink = None self.__frame_dec = None self.__frame_rate_to_decimation_conversion = 0.0 self.__do_connect() def state_def(self): for d in super(MonitorSink, self).state_def(): yield d # TODO make this possible to be decorator style yield 'fft', StreamCell(self, 'fft', type=BulkDataT(array_format='b', info_format='dff'), label='Spectrum') yield 'scope', StreamCell(self, 'scope', type=BulkDataT(array_format='f', info_format='d'), label='Scope') def __do_connect(self): itemsize = self.__itemsize if self.__signal_type.is_analytic(): input_length = self.__freq_resolution output_length = self.__freq_resolution self.__after_fft = None else: # use vector_to_streams to cut the output in half and discard the redundant part input_length = self.__freq_resolution * 2 output_length = self.__freq_resolution self.__after_fft = blocks.vector_to_streams(itemsize=output_length * gr.sizeof_float, nstreams=2) sample_rate = self.__signal_type.get_sample_rate() overlap_factor = int(math.ceil(_maximum_fft_rate * input_length / sample_rate)) # sanity limit -- OverlapGimmick is not free overlap_factor = min(16, overlap_factor) self.__frame_rate_to_decimation_conversion = sample_rate * overlap_factor / input_length self.__gate = blocks.copy(itemsize) self.__gate.set_enabled(not self.__paused) overlapper = _OverlappedStreamToVector( size=input_length, factor=overlap_factor, itemsize=itemsize) self.__frame_dec = blocks.keep_one_in_n( itemsize=itemsize * input_length, n=int(round(self.__frame_rate_to_decimation_conversion / self.__frame_rate))) # the actual FFT logic, which is similar to GR's logpwrfft_c window = windows.blackmanharris(input_length) window_power = sum(x * x for x in window) # TODO: use fft_vfc when applicable fft_block = (fft_vcc if itemsize == gr.sizeof_gr_complex else fft_vfc)( fft_size=input_length, forward=True, window=window) mag_squared = blocks.complex_to_mag_squared(input_length) logarithmizer = blocks.nlog10_ff( n=10, # the "deci" in "decibel" vlen=input_length, k=( -to_dB(window_power) + # compensate for window -to_dB(sample_rate) + # convert from power-per-sample to power-per-Hz self.__power_offset # offset for packing into bytes )) # It would make slightly more sense to use unsigned chars, but blocks.float_to_uchar does not support vlen. self.__fft_converter = blocks.float_to_char(vlen=self.__freq_resolution, scale=1.0) self.__fft_sink = MessageDistributorSink( itemsize=output_length * gr.sizeof_char, context=self.__context, migrate=self.__fft_sink, notify=self.__update_interested) self.__scope_sink = MessageDistributorSink( itemsize=self.__time_length * gr.sizeof_gr_complex, context=self.__context, migrate=self.__scope_sink, notify=self.__update_interested) scope_chunker = blocks.stream_to_vector_decimator( item_size=gr.sizeof_gr_complex, sample_rate=sample_rate, vec_rate=self.__frame_rate, # TODO doesn't need to be coupled vec_len=self.__time_length) # connect everything self.__context.lock() try: self.disconnect_all() self.connect( self, self.__gate, overlapper, self.__frame_dec, fft_block, mag_squared, logarithmizer) if self.__after_fft is not None: self.connect(logarithmizer, self.__after_fft) self.connect(self.__after_fft, self.__fft_converter, self.__fft_sink) self.connect((self.__after_fft, 1), blocks.null_sink(gr.sizeof_float * self.__freq_resolution)) else: self.connect(logarithmizer, self.__fft_converter, self.__fft_sink) if self.__enable_scope: self.connect( self.__gate, scope_chunker, self.__scope_sink) finally: self.__context.unlock() # non-exported def get_interested_cell(self): return self.__interested_cell def __update_interested(self): self.__interested_cell.set_internal(not self.__paused and ( self.__fft_sink.get_subscription_count() > 0 or self.__scope_sink.get_subscription_count() > 0)) @exported_value(type=SignalType, changes='explicit') def get_signal_type(self): return self.__signal_type # non-exported def set_signal_type(self, value): # TODO: don't rebuild if the rate did not change and the spectrum-sidedness of the type did not change assert self.__signal_type.compatible_items(value) self.__signal_type = value self.__do_connect() self.state_changed('signal_type') # non-exported def set_input_center_freq(self, value): self.__input_center_freq = float(value) @exported_value( type=RangeT([(2, 4096)], logarithmic=True, integer=True), changes='this_setter', label='Resolution', description='Frequency domain resolution; number of FFT bins.') def get_freq_resolution(self): return self.__freq_resolution @setter def set_freq_resolution(self, freq_resolution): self.__freq_resolution = freq_resolution self.__do_connect() @exported_value(type=RangeT([(1, 4096)], logarithmic=True, integer=True), changes='this_setter') def get_time_length(self): return self.__time_length @setter def set_time_length(self, value): self.__time_length = value self.__do_connect() @exported_value( type=RangeT([(1, _maximum_fft_rate)], unit=units.Hz, logarithmic=True, integer=False), changes='this_setter', label='Rate', description='Number of FFT frames per second.') def get_frame_rate(self): return self.__frame_rate @setter def set_frame_rate(self, value): n = int(round(self.__frame_rate_to_decimation_conversion / value)) self.__frame_dec.set_n(n) # derive effective value by calculating inverse self.__frame_rate = self.__frame_rate_to_decimation_conversion / n @exported_value(type=bool, changes='this_setter', label='Pause') def get_paused(self): return self.__paused @setter def set_paused(self, value): self.__paused = value self.__gate.set_enabled(not value) self.__update_interested() # exported via state_def def get_fft_info(self): return (self.__input_center_freq, self.__signal_type.get_sample_rate(), self.__power_offset) def get_fft_distributor(self): return self.__fft_sink # exported via state_def def get_scope_info(self): return (self.__signal_type.get_sample_rate(),) def get_scope_distributor(self): return self.__scope_sink
def setUp(self): self.lc = LooseCell(value=0, type=int)
class TestViewCell(unittest.TestCase): def setUp(self): self.lc = LooseCell(value=0, type=RangeT([(-100, 100)])) self.delta = 1 self.vc = ViewCell( base=self.lc, get_transform=lambda x: x + self.delta, set_transform=lambda x: x - self.delta, type=int) # TODO: Add tests for behavior when the transform is not perfectly one-to-one (such as due to floating-point error). def test_get_set(self): self.assertEqual(0, self.lc.get()) self.assertEqual(1, self.vc.get()) self.vc.set(2) self.assertEqual(1, self.lc.get()) self.assertEqual(2, self.vc.get()) self.lc.set(3) self.assertEqual(3, self.lc.get()) self.assertEqual(4, self.vc.get()) self.delta = 10 self.vc.changed_transform() self.assertEqual(3, self.lc.get()) self.assertEqual(13, self.vc.get()) def test_subscription(self): st = CellSubscriptionTester(self.vc) self.lc.set(1) st.expect_now(2) self.delta = 10 self.vc.changed_transform() self.assertEqual(1, self.lc.get()) st.expect_now(11) st.unsubscribe() self.lc.set(2) st.advance() def test_coerced_base_value(self): self.vc.set(999) # out of base cell's range, gets clamped self.assertEqual(100 + self.delta, self.vc.get())
def __init__(self): self.subscribable = LooseCell(key='subscribable', value='', type=str)