## ## This file is part of the libsigrokdecode project. ## ## Copyright (C) 2017 Christoph Rackwitz ## ## 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 2 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 sigrokdecode as srd def decode_ditdah(s): return tuple({'-': 3, '.': 1}[c] for c in s) def encode_ditdah(tpl): return ''.join({1: '.', 3: '-'}[c] for c in tpl) # https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf # Recommendation ITU-R M.1677-1 # (10/2009) # International Morse code alphabet = { # 1.1.1 Letters '.-': 'a', '-...': 'b', '-.-.': 'c', '-..': 'd', '.': 'e', '..-..': 'é', # "accented" '..-.': 'f', '--.': 'g', '....': 'h', '..': 'i', '.---': 'j', '-.-': 'k', '.-..': 'l', '--': 'm', '-.': 'n', '---': 'o', '.--.': 'p', '--.-': 'q', '.-.': 'r', '...': 's', '-': 't', '..-': 'u', '...-': 'v', '.--': 'w', '-..-': 'x', '-.--': 'y', '--..': 'z', # 1.1.2 Figures '.----': '1', '..---': '2', '...--': '3', '....-': '4', '.....': '5', '-....': '6', '--...': '7', '---..': '8', '----.': '9', '-----': '0', # 1.1.3 Punctuation marks and miscellaneous signs '.-.-.-': '.', # Full stop (period) '--..--': ',', # Comma '---...': ':', # Colon or division sign '..--..': '?', # Question mark (note of interrogation or request for repetition of a transmission not understood) '.----.': '’', # Apostrophe '-....-': '-', # Hyphen or dash or subtraction sign '-..-.': '/', # Fraction bar or division sign '-.--.': '(', # Left-hand bracket (parenthesis) '-.--.-': ')', # Right-hand bracket (parenthesis) '.-..-.': '“ ”', # Inverted commas (quotation marks) (before and after the words) '-...-': '=', # Double hyphen '...-.': 'UNDERSTOOD', # Understood '........': 'ERROR', # Error (eight dots) '.-.-.': '+', # Cross or addition sign '.-...': 'WAIT', # Wait '...-.-': 'EOW', # End of work '-.-.-': 'START', # Starting signal (to precede every transmission) '.--.-.': '@', # Commercial at #'-.-': 'ITT', # K: Invitation to transmit # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted. #'-..-': '×', # Multiplication sign } alphabet = {decode_ditdah(k): v for k, v in alphabet.items()} # 2 Spacing and length of the signals (right side is just for printing). symbols = { # level, time units # 2.1 A dash is equal to three dots. (1, 1): '*', (1, 3): '===', # 2.2 The space between the signals forming the same letter is equal to one dot. (0, 1): '_', # 2.3 The space between two letters is equal to three dots. (0, 3): '__', # 2.4 The space between two words is equal to seven dots. (0, 7): '___', } class Decoder(srd.Decoder): api_version = 3 id = 'morse' name = 'Morse' longname = 'Morse code' desc = 'Demodulated morse code protocol.' license = 'gplv2+' inputs = ['logic'] outputs = [] tags = ['Encoding'] channels = ( {'id': 'data', 'name': 'Data', 'desc': 'Data line'}, ) options = ( {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1}, ) annotations = ( ('time', 'Time'), ('units', 'Units'), ('symbol', 'Symbol'), ('letter', 'Letter'), ('word', 'Word'), ) annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations)) def __init__(self): self.reset() def reset(self): self.samplerate = None def metadata(self, key, value): if key == srd.SRD_CONF_SAMPLERATE: self.samplerate = value def start(self): self.out_ann = self.register(srd.OUTPUT_ANN) self.out_binary = self.register(srd.OUTPUT_BINARY) def decode_symbols(self): # Annotate symbols, emit symbols, handle timeout via token. timeunit = self.options['timeunit'] self.wait({0: 'r'}) prevtime = self.samplenum # Time of an actual edge. while True: (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}]) pval = 1 - val curtime = self.samplenum dt = (curtime - prevtime) / self.samplerate units = dt / timeunit iunits = int(max(1, round(units))) error = abs(units - iunits) symbol = (pval, iunits) if self.matched[1]: yield None # Flush word. continue self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]]) if symbol in symbols: self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]]) yield (prevtime, curtime, symbol) else: self.put(prevtime, curtime, self.out_ann, [1, ['!! {:.1f}*{:.3g} !!'.format(units, timeunit)]]) prevtime = curtime thisunit = dt / iunits timeunit += (thisunit - timeunit) * 0.2 * max(0, 1 - 2*error) # Adapt. def decode_morse(self): # Group symbols into letters. sequence = () s0 = s1 = None for item in self.decode_symbols(): do_yield = False if item is not None: # Level + width. (t0, t1, symbol) = item (sval, sunits) = symbol if sval == 1: if s0 is None: s0 = t0 s1 = t1 sequence += (sunits,) else: # Generate "flush" for end of letter, end of word. if sunits >= 3: do_yield = True else: do_yield = True if do_yield: if sequence: yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence))) sequence = () s0 = s1 = None if item is None: yield None # Pass through flush of 5+ space. def decode(self): # Strictly speaking there is no point in running this decoder # when the sample rate is unknown or zero. But the previous # implementation already fell back to a rate of 1 in that case. # We stick with this approach, to not introduce new constraints # for existing use scenarios. if not self.samplerate: self.samplerate = 1.0 # Annotate letters, group into words. s0 = s1 = None word = '' for item in self.decode_morse(): do_yield = False if item is not None: # Append letter. (t0, t1, letter) = item self.put(t0, t1, self.out_ann, [3, [letter]]) if s0 is None: s0 = t0 s1 = t1 word += letter else: do_yield = True if do_yield: # Flush of word. if word: self.put(s0, s1, self.out_ann, [4, [word]]) word = '' s0 = s1 = None