# 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)
--- /dev/null
+#!/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()