+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
+
+