2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
6 ## This program is free software; you can redistribute it and/or modify
7 ## it under the terms of the GNU General Public License as published by
8 ## the Free Software Foundation; either version 2 of the License, or
9 ## (at your option) any later version.
11 ## This program is distributed in the hope that it will be useful,
12 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ## GNU General Public License for more details.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
23 return tuple({'-': 3, '.': 1}[c] for c in s)
25 def encode_ditdah(tpl):
26 return ''.join({1: '.', 3: '-'}[c] for c in tpl)
28 # https://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.1677-1-200910-I!!PDF-E.pdf
29 # Recommendation ITU-R M.1677-1
31 # International Morse code
39 '..-..': 'é', # "accented"
74 # 1.1.3 Punctuation marks and miscellaneous signs
75 '.-.-.-': '.', # Full stop (period)
76 '--..--': ',', # Comma
77 '---...': ':', # Colon or division sign
78 '..--..': '?', # Question mark (note of interrogation or request for repetition of a transmission not understood)
79 '.----.': '’', # Apostrophe
80 '-....-': '-', # Hyphen or dash or subtraction sign
81 '-..-.': '/', # Fraction bar or division sign
82 '-.--.': '(', # Left-hand bracket (parenthesis)
83 '-.--.-': ')', # Right-hand bracket (parenthesis)
84 '.-..-.': '“ ”', # Inverted commas (quotation marks) (before and after the words)
85 '-...-': '=', # Double hyphen
86 '...-.': 'UNDERSTOOD', # Understood
87 '........': 'ERROR', # Error (eight dots)
88 '.-.-.': '+', # Cross or addition sign
89 '.-...': 'WAIT', # Wait
90 '...-.-': 'EOW', # End of work
91 '-.-.-': 'START', # Starting signal (to precede every transmission)
92 '.--.-.': '@', # Commercial at
94 #'-.-': 'ITT', # K: Invitation to transmit
96 # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted.
97 #'-..-': '×', # Multiplication sign
100 alphabet = {decode_ditdah(k): v for k, v in alphabet.items()}
102 # 2 Spacing and length of the signals (right side is just for printing).
103 symbols = { # level, time units
104 # 2.1 A dash is equal to three dots.
107 # 2.2 The space between the signals forming the same letter is equal to one dot.
109 # 2.3 The space between two letters is equal to three dots.
111 # 2.4 The space between two words is equal to seven dots.
115 class Decoder(srd.Decoder):
119 longname = 'Morse code'
120 desc = 'Demodulated morse code protocol.'
125 {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
128 {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1},
133 ('symbol', 'Symbol'),
134 ('letter', 'Letter'),
137 annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
143 self.samplerate = None
145 def metadata(self, key, value):
146 if key == srd.SRD_CONF_SAMPLERATE:
147 self.samplerate = value
150 self.out_ann = self.register(srd.OUTPUT_ANN)
151 self.out_binary = self.register(srd.OUTPUT_BINARY)
153 def decode_symbols(self):
154 # Annotate symbols, emit symbols, handle timeout via token.
156 timeunit = self.options['timeunit']
159 prevtime = self.samplenum # Time of an actual edge.
162 (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
165 curtime = self.samplenum
166 dt = (curtime - prevtime) / self.samplerate
167 units = dt / timeunit
168 iunits = int(max(1, round(units)))
169 error = abs(units - iunits)
171 symbol = (pval, iunits)
174 yield None # Flush word.
177 self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
179 if symbol in symbols:
180 self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
181 yield (prevtime, curtime, symbol)
183 self.put(prevtime, curtime, self.out_ann, [1, ['!! {:.1f}*{:.3g} !!'.format(units, timeunit)]])
187 thisunit = dt / iunits
188 timeunit += (thisunit - timeunit) * 0.2 * max(0, 1 - 2*error) # Adapt.
190 def decode_morse(self):
191 # Group symbols into letters.
195 for item in self.decode_symbols():
197 if item is not None: # Level + width.
198 (t0, t1, symbol) = item
199 (sval, sunits) = symbol
204 sequence += (sunits,)
206 # Generate "flush" for end of letter, end of word.
213 yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence)))
217 yield None # Pass through flush of 5+ space.
221 # Strictly speaking there is no point in running this decoder
222 # when the sample rate is unknown or zero. But the previous
223 # implementation already fell back to a rate of 1 in that case.
224 # We stick with this approach, to not introduce new constraints
225 # for existing use scenarios.
226 if not self.samplerate:
227 self.samplerate = 1.0
229 # Annotate letters, group into words.
232 for item in self.decode_morse():
235 if item is not None: # Append letter.
236 (t0, t1, letter) = item
237 self.put(t0, t1, self.out_ann, [3, [letter]])
245 if do_yield: # Flush of word.
247 self.put(s0, s1, self.out_ann, [4, [word]])