Move the MorseDistanceModulator class to its own file
authorAntonio Ospite <ao2@ao2.it>
Sun, 3 Jan 2016 17:38:34 +0000 (18:38 +0100)
committerAntonio Ospite <ao2@ao2.it>
Mon, 4 Jan 2016 09:48:26 +0000 (10:48 +0100)
src/savemysugar/CallDistanceTransceiver.py
src/savemysugar/MorseDistanceModulator.py [new file with mode: 0755]

index f04c533..1e378d2 100755 (executable)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-# This hack allows MorseTranslator to be imported also when
+# This hack allows importing local modules also when
 # __name__ == "__main__"
 try:
+    from .MorseDistanceModulator import MorseDistanceModulator
     from .MorseTranslator import MorseTranslator
 except SystemError:
+    from MorseDistanceModulator import MorseDistanceModulator
     from MorseTranslator import MorseTranslator
 
 import logging
 import time
 
 
-class SymbolTime(object):
-    """
-    In theory the symbol distance (the distance to discriminate symbols) can
-    be arbitrary, and it's only bounded below by the stability of the period
-    time, but in practice it can be necessary to wait a predefined minimum
-    amount of time between periods because of technological limits of the
-    transmitting devices, we call this time the "minimum inter-symbol
-    distance".
-    """
-
-    # pylint: disable=too-few-public-methods
-    def __init__(self, period_min, period_max, multiplier,
-                 min_inter_symbol_distance=0.0):
-        assert multiplier >= 0
-        if (period_min == period_max) and (min_inter_symbol_distance == 0):
-            raise ValueError("If (period_min == period_max) a non-zero",
-                             "inter-symbol distance MUST be specified")
-
-        symbol_distance = 2 * (period_max - period_min)
-        if symbol_distance == 0:
-            symbol_distance = min_inter_symbol_distance
-
-        # The time the transmitter has to wait to disambiguate between this
-        # symbol and a different one.
-        self.dist = min_inter_symbol_distance + symbol_distance * multiplier
-
-        # The minimum time which represents the symbol at the receiving end.
-        self.min = min_inter_symbol_distance + period_min + \
-            symbol_distance * multiplier
-
-        # The maximum time which represents the symbol at the receiving end
-        self.max = min_inter_symbol_distance + period_min + \
-            symbol_distance * (multiplier + 1)
-
-
-class MorseDistanceModulator(object):
-    def __init__(self, period_min, period_max, pulse_min, pulse_max,
-                 inter_symbol_distance):
-        self.set_parameters(period_min, period_max,
-                            pulse_min, pulse_max,
-                            inter_symbol_distance)
-
-    def set_parameters(self, period_min, period_max, pulse_min, pulse_max,
-                       inter_symbol_distance):
-        self.period_min = period_min
-        self.period_max = period_max
-        self.pulse_min = pulse_min
-        self.pulse_max = pulse_max
-        self.inter_symbol_distance = inter_symbol_distance
-
-        self.dot_time = SymbolTime(period_min,
-                                   period_max, 0,
-                                   inter_symbol_distance)
-        self.dash_time = SymbolTime(period_min,
-                                    period_max, 1,
-                                    inter_symbol_distance)
-        self.signalspace_time = SymbolTime(period_min,
-                                           period_max, 2,
-                                           inter_symbol_distance)
-        self.wordspace_time = SymbolTime(period_min,
-                                         period_max, 3,
-                                         inter_symbol_distance)
-        self.eom_time = SymbolTime(period_min,
-                                   period_max, 4,
-                                   inter_symbol_distance)
-
-    def symbol_to_distance(self, symbol):
-        if symbol == ".":
-            return self.dot_time.dist
-        elif symbol == "-":
-            return self.dash_time.dist
-        elif symbol == " ":
-            return self.signalspace_time.dist
-        elif symbol == "/" or symbol == " / ":
-            return self.wordspace_time.dist
-        elif symbol == "EOM":
-            return self.eom_time.dist
-
-        raise ValueError("Unexpected symbol %s" % symbol)
-
-    def is_same_period(self, distance):
-        return distance > self.pulse_min and distance <= self.pulse_max
-
-    def distance_to_symbol(self, distance):
-        if distance > self.dot_time.min and \
-           distance <= self.dot_time.max:
-            return "."
-
-        if distance > self.dash_time.min and \
-           distance <= self.dash_time.max:
-            return "-"
-
-        if distance > self.signalspace_time.min and \
-           distance <= self.signalspace_time.max:
-            return " "
-
-        if distance > self.wordspace_time.min and \
-           distance <= self.wordspace_time.max:
-            return "/"
-
-        if distance > self.eom_time.min:
-            return "EOM"
-
-        raise ValueError("Unexpected distance %.2f" % distance)
-
-    def modulate(self, morse):
-        signals = morse.split(' ')
-        distances = []
-        for i, signal in enumerate(signals):
-            for symbol in signal:
-                distances.append(self.symbol_to_distance(symbol))
-
-            # Transmit a signal separator only when strictly necessary.
-            #
-            # Avoid it in these cases:
-            #  - after the last symbol, because EOM is going to ne transmitted
-            #    anyway and that will mark the end of the last symbol.
-            #  - between words, because the word separator act as a symbol
-            #    separator too.
-            if i != len(signals) - 1 and signals[i + 1] != "/" and signal != "/":
-                distances.append(self.symbol_to_distance(" "))
-
-        distances.append(self.symbol_to_distance("EOM"))
-
-        # Since the Morse signals are encoded in the distance between calls, an
-        # extra call is needed in order for receiver actually get the EOM and
-        # see that the transmission has terminated.
-        distances.append(0)
-
-        return distances
-
-
 def log_symbol(distance, symbol, extra_info=""):
     logging.info("distance: %.2f Received \"%s\"%s", distance, symbol,
                  extra_info)
diff --git a/src/savemysugar/MorseDistanceModulator.py b/src/savemysugar/MorseDistanceModulator.py
new file mode 100755 (executable)
index 0000000..417c5f9
--- /dev/null
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+#
+# MorseDistanceModulator - represent Morse symbols using Distance Modulation
+#
+# Copyright (C) 2015  Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+class SymbolTime(object):
+    """
+    In theory the symbol distance (the distance to discriminate symbols) can
+    be arbitrary, and it's only bounded below by the stability of the period
+    time, but in practice it can be necessary to wait a predefined minimum
+    amount of time between periods because of technological limits of the
+    transmitting devices, we call this time the "minimum inter-symbol
+    distance".
+    """
+
+    # pylint: disable=too-few-public-methods
+    def __init__(self, period_min, period_max, multiplier,
+                 min_inter_symbol_distance=0.0):
+        assert multiplier >= 0
+        if (period_min == period_max) and (min_inter_symbol_distance == 0):
+            raise ValueError("If (period_min == period_max) a non-zero",
+                             "inter-symbol distance MUST be specified")
+
+        symbol_distance = 2 * (period_max - period_min)
+        if symbol_distance == 0:
+            symbol_distance = min_inter_symbol_distance
+
+        # The time the transmitter has to wait to disambiguate between this
+        # symbol and a different one.
+        self.dist = min_inter_symbol_distance + symbol_distance * multiplier
+
+        # The minimum time which represents the symbol at the receiving end.
+        self.min = min_inter_symbol_distance + period_min + \
+            symbol_distance * multiplier
+
+        # The maximum time which represents the symbol at the receiving end
+        self.max = min_inter_symbol_distance + period_min + \
+            symbol_distance * (multiplier + 1)
+
+
+class MorseDistanceModulator(object):
+    def __init__(self, period_min, period_max, pulse_min, pulse_max,
+                 inter_symbol_distance):
+        self.set_parameters(period_min, period_max,
+                            pulse_min, pulse_max,
+                            inter_symbol_distance)
+
+    def set_parameters(self, period_min, period_max, pulse_min, pulse_max,
+                       inter_symbol_distance):
+        self.period_min = period_min
+        self.period_max = period_max
+        self.pulse_min = pulse_min
+        self.pulse_max = pulse_max
+        self.inter_symbol_distance = inter_symbol_distance
+
+        self.dot_time = SymbolTime(period_min,
+                                   period_max, 0,
+                                   inter_symbol_distance)
+        self.dash_time = SymbolTime(period_min,
+                                    period_max, 1,
+                                    inter_symbol_distance)
+        self.signalspace_time = SymbolTime(period_min,
+                                           period_max, 2,
+                                           inter_symbol_distance)
+        self.wordspace_time = SymbolTime(period_min,
+                                         period_max, 3,
+                                         inter_symbol_distance)
+        self.eom_time = SymbolTime(period_min,
+                                   period_max, 4,
+                                   inter_symbol_distance)
+
+    def symbol_to_distance(self, symbol):
+        if symbol == ".":
+            return self.dot_time.dist
+        elif symbol == "-":
+            return self.dash_time.dist
+        elif symbol == " ":
+            return self.signalspace_time.dist
+        elif symbol == "/" or symbol == " / ":
+            return self.wordspace_time.dist
+        elif symbol == "EOM":
+            return self.eom_time.dist
+
+        raise ValueError("Unexpected symbol %s" % symbol)
+
+    def is_same_period(self, distance):
+        return distance > self.pulse_min and distance <= self.pulse_max
+
+    def distance_to_symbol(self, distance):
+        if distance > self.dot_time.min and \
+           distance <= self.dot_time.max:
+            return "."
+
+        if distance > self.dash_time.min and \
+           distance <= self.dash_time.max:
+            return "-"
+
+        if distance > self.signalspace_time.min and \
+           distance <= self.signalspace_time.max:
+            return " "
+
+        if distance > self.wordspace_time.min and \
+           distance <= self.wordspace_time.max:
+            return "/"
+
+        if distance > self.eom_time.min:
+            return "EOM"
+
+        raise ValueError("Unexpected distance %.2f" % distance)
+
+    def modulate(self, morse):
+        signals = morse.split(' ')
+        distances = []
+        for i, signal in enumerate(signals):
+            for symbol in signal:
+                distances.append(self.symbol_to_distance(symbol))
+
+            # Transmit a signal separator only when strictly necessary.
+            #
+            # Avoid it in these cases:
+            #  - after the last symbol, because EOM is going to ne transmitted
+            #    anyway and that will mark the end of the last symbol.
+            #  - between words, because the word separator act as a symbol
+            #    separator too.
+            if i != len(signals) - 1 and signals[i + 1] != "/" and signal != "/":
+                distances.append(self.symbol_to_distance(" "))
+
+        distances.append(self.symbol_to_distance("EOM"))
+
+        # Since the Morse signals are encoded in the distance between calls, an
+        # extra call is needed in order for receiver actually get the EOM and
+        # see that the transmission has terminated.
+        distances.append(0)
+
+        return distances
+
+
+def test_modulate():
+    period_min = 7
+    period_max = 15
+    pulse_min = 4.9
+    pulse_max = 5.2
+    min_inter_symbol_distance = pulse_max
+    morse = "... -- ..."
+    modulator = MorseDistanceModulator(period_min, period_max,
+                                       pulse_min, pulse_max,
+                                       min_inter_symbol_distance)
+
+    print(modulator.modulate(morse))
+
+if __name__ == "__main__":
+    test_modulate()