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)
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): self.assertEqual(repr(self.lc), '<LooseCell PythonT(<type \'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)
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 _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())
class TestViewCell(unittest.TestCase): 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()) # TODO: Add tests for behavior when the transform is not perfectly one-to-one (such as due to floating-point error). # TODO: Test propagation of interest 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_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()
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 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)
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 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 _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 TestLooseCell(unittest.TestCase): def setUp(self): self.lc = LooseCell(value=0, key='a', 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
class TestLooseCell(unittest.TestCase): def setUp(self): self.lc = LooseCell(value=0, type=int, 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): self.assertEqual(repr(self.lc), '<LooseCell PythonT(<type \'int\'>) 0>')
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 __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 __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 _OsmoSDRTuning(object): 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 __set_freq(self, freq): self.__source.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())