#!/usr/bin/env python3 # # MorseTranslator - translate to and from Morse code # # Copyright (C) 2015 Antonio Ospite # # 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 . import re class MorseTranslator(object): """International Morse Code translator. The specification of the International Morse Code is in ITU-R M.1677-1 (10/2009), Annex 1. The terminology used here may differ from the one used in some other places, so here is some nomenclature: symbol: one of . (dot), - (dash), ' ' (signal separator), '/' (word separator) character: a letter of the alphabet, a number, a punctuation mark, or a ' ' (text word separator) signal: a sequence of . and - symbols which encode a character, or a '/' (Morse word separator) word: a sequence of characters not containing a ' ', or a sequence of signals not containing a '/' text: a sequence of characters morse: a sequence of signals separated by ' ' NOTE: signals are separated by a ' ' (signal separator), characters are not separated one from the other. This class defines a subset of the signals in Section 1 of the aforementioned specification, plus a word space, and it does not make assumptions about their actual transmission. """ def __init__(self): self.signals_table = {} self.characters_table = {} # XXX the current code only handles single characters, # so prosigns are not added to the tables below # Letters self.signals_table['a'] = ".-" self.signals_table['b'] = "-..." self.signals_table['c'] = "-.-." self.signals_table['d'] = "-.." self.signals_table['e'] = "." self.signals_table['f'] = "..-." self.signals_table['g'] = "--." self.signals_table['h'] = "...." self.signals_table['i'] = ".." self.signals_table['j'] = ".---" self.signals_table['k'] = "-.-" self.signals_table['l'] = ".-.." self.signals_table['m'] = "--" self.signals_table['n'] = "-." self.signals_table['o'] = "---" self.signals_table['p'] = ".--." self.signals_table['q'] = "--.-" self.signals_table['r'] = ".-." self.signals_table['s'] = "..." self.signals_table['t'] = "-" self.signals_table['u'] = "..-" self.signals_table['v'] = "...-" self.signals_table['w'] = ".--" self.signals_table['x'] = "-..-" self.signals_table['y'] = "-.--" self.signals_table['z'] = "--.." # Figures self.signals_table['1'] = ".----" self.signals_table['2'] = "..---" self.signals_table['3'] = "...--" self.signals_table['4'] = "....-" self.signals_table['5'] = "....." self.signals_table['6'] = "-...." self.signals_table['7'] = "--..." self.signals_table['8'] = "---.." self.signals_table['9'] = "----." self.signals_table['0'] = "-----" # Punctuation marks and miscellaneous signs self.signals_table['.'] = ".-.-.-" self.signals_table[','] = "--..--" self.signals_table[':'] = "---..." self.signals_table['?'] = "..--.." self.signals_table['\''] = ".----." self.signals_table['-'] = "-....-" self.signals_table['/'] = "-..-." self.signals_table['('] = "-.--." self.signals_table[')'] = "-.--.-" self.signals_table['"'] = ".-..-." self.signals_table['='] = "-...-" self.signals_table['+'] = ".-.-." self.signals_table['x'] = "-..-" self.signals_table['@'] = ".--.-." # Represent the word space as a signal with only one "/" symbol self.signals_table[' '] = "/" for key, value in self.signals_table.items(): self.characters_table[value] = key def stats(self): signal_length_sum = 0 for signal in self.signals_table.values(): signal_length_sum += len(signal) average_signal_length = signal_length_sum / len(self.signals_table) character_length_sum = 0 for character in self.characters_table.values(): character_length_sum += len(character) average_char_length = character_length_sum / len(self.characters_table) return average_signal_length, average_char_length def sanitize_text(self, text): sanitized = text.lower() sanitized = re.sub(r"[^a-z0-9.,:?\'-/()\"=\+@ ]", "", sanitized) sanitized = re.sub(r"\s+", " ", sanitized) return sanitized def char_to_signal(self, character): char = character.lower() if char in self.signals_table: return self.signals_table[char] else: return "" def text_to_morse(self, text, sanitize=True): if sanitize: text = self.sanitize_text(text) signal = [self.char_to_signal(c) for c in text] return str(" ").join(signal) def sanitize_morse(self, morse): sanitized = re.sub("_", "-", morse) sanitized = re.sub(r"[^\-\.\/]", " ", sanitized) sanitized = re.sub(r"\s+", " ", sanitized) sanitized = re.sub(r"( ?/ ?)+", " / ", sanitized) return sanitized def signal_to_character(self, signal): if signal in self.characters_table: return self.characters_table[signal] else: return '*' def morse_to_text(self, morse, sanitize=True): if sanitize: morse = self.sanitize_morse(morse) signals = morse.split() characters = [self.signal_to_character(signal) for signal in signals] return str('').join(characters) def test(): translator = MorseTranslator() avg_signal_length, avg_character_length = translator.stats() print("Average signal length:", avg_signal_length) print("Average character length:", avg_character_length) text = "Hello, I am just some text." print(text) morse = translator.text_to_morse(text) print(morse) text = translator.morse_to_text(morse) print(text) print("\n\nTesting sanitizing functions") print() dirty_text = ' < >Hello::## this is dirty^^%% text! ' print(dirty_text) print(translator.sanitize_text(dirty_text)) print(translator.text_to_morse(translator.sanitize_text(dirty_text))) print() dirty_morse = ' 009 .... . ._.. .-.. --- /34// / // // - .... .. ...' + \ ' / .. ... / -.. .. .-. - -.-- / - . -..- _ ' print(dirty_morse) print(translator.sanitize_morse(dirty_morse)) print(translator.morse_to_text(translator.sanitize_morse(dirty_morse))) print("\n\nTesting conversion on unsanitized strings") print(dirty_text) print(translator.text_to_morse(dirty_text)) print(translator.morse_to_text(translator.text_to_morse(dirty_text))) print(dirty_morse) print(translator.morse_to_text(dirty_morse)) if __name__ == "__main__": test()