Beispiel #1
0
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)
Beispiel #2
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):
        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)
Beispiel #3
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())
Beispiel #4
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  # 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())
Beispiel #5
0
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 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())
Beispiel #7
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']
        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()
Beispiel #8
0
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())
Beispiel #9
0
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)
Beispiel #10
0
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)
Beispiel #11
0
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)
Beispiel #12
0
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())
Beispiel #13
0
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, 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
Beispiel #15
0
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>')
Beispiel #16
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)
Beispiel #17
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()
Beispiel #18
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(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()
Beispiel #19
0
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())