]> sigrok.org Git - libsigrokdecode.git/blame - decoders/morse/pd.py
all decoders: introduce a reset() method
[libsigrokdecode.git] / decoders / morse / pd.py
CommitLineData
500d5ff4
CR
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2017 Christoph Rackwitz <christoph.rackwitz@rwth-aachen.de>
5##
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.
10##
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.
15##
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/>.
18##
19
20import sigrokdecode as srd
21
22def decode_ditdah(s):
23 return tuple({'-': 3, '.': 1}[c] for c in s)
24
25def encode_ditdah(tpl):
26 return ''.join({1: '.', 3: '-'}[c] for c in tpl)
27
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
30# (10/2009)
31# International Morse code
32alphabet = {
33 # 1.1.1 Letters
34 '.-': 'a',
35 '-...': 'b',
36 '-.-.': 'c',
37 '-..': 'd',
38 '.': 'e',
39 '..-..': 'é', # "accented"
40 '..-.': 'f',
41 '--.': 'g',
42 '....': 'h',
43 '..': 'i',
44 '.---': 'j',
45 '-.-': 'k',
46 '.-..': 'l',
47 '--': 'm',
48 '-.': 'n',
49 '---': 'o',
50 '.--.': 'p',
51 '--.-': 'q',
52 '.-.': 'r',
53 '...': 's',
54 '-': 't',
55 '..-': 'u',
56 '...-': 'v',
57 '.--': 'w',
58 '-..-': 'x',
59 '-.--': 'y',
60 '--..': 'z',
61
62 # 1.1.2 Figures
63 '.----': '1',
64 '..---': '2',
65 '...--': '3',
66 '....-': '4',
67 '.....': '5',
68 '-....': '6',
69 '--...': '7',
70 '---..': '8',
71 '----.': '9',
72 '-----': '0',
73
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
93
94 #'-.-': 'ITT', # K: Invitation to transmit
95
96 # 3.2.1 For the multiplication sign, the signal corresponding to the letter X shall be transmitted.
97 #'-..-': '×', # Multiplication sign
98}
99
100alphabet = {decode_ditdah(k): v for k, v in alphabet.items()}
101
102# 2 Spacing and length of the signals (right side is just for printing).
103symbols = { # level, time units
104 # 2.1 A dash is equal to three dots.
105 (1, 1): '*',
106 (1, 3): '===',
107 # 2.2 The space between the signals forming the same letter is equal to one dot.
108 (0, 1): '_',
109 # 2.3 The space between two letters is equal to three dots.
110 (0, 3): '__',
111 # 2.4 The space between two words is equal to seven dots.
112 (0, 7): '___',
113}
114
115class Decoder(srd.Decoder):
116 api_version = 3
117 id = 'morse'
118 name = 'Morse'
119 longname = 'Morse code'
120 desc = 'Demodulated morse code protocol.'
121 license = 'gplv2+'
122 inputs = ['logic']
123 outputs = ['morse']
124 channels = (
125 {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
126 )
127 options = (
128 {'id': 'timeunit', 'desc': 'Time unit (guess)', 'default': 0.1},
129 )
130 annotations = (
131 ('time', 'Time'),
132 ('units', 'Units'),
133 ('symbol', 'Symbol'),
134 ('letter', 'Letter'),
135 ('word', 'Word'),
136 )
137 annotation_rows = tuple((u, v, (i,)) for i, (u, v) in enumerate(annotations))
138
139 def __init__(self):
10aeb8ea
GS
140 self.reset()
141
142 def reset(self):
500d5ff4
CR
143 self.samplerate = None
144
145 def metadata(self, key, value):
146 if key == srd.SRD_CONF_SAMPLERATE:
147 self.samplerate = value
148
149 def start(self):
150 self.out_ann = self.register(srd.OUTPUT_ANN)
151 self.out_binary = self.register(srd.OUTPUT_BINARY)
152
153 def decode_symbols(self):
154 # Annotate symbols, emit symbols, handle timeout via token.
155
156 timeunit = self.options['timeunit']
157 if self.samplerate is None:
158 self.samplerate = 1.0
159
160 self.wait({0: 'r'})
161 prevtime = self.samplenum # Time of an actual edge.
162
163 while True:
164 (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
165
166 pval = 1 - val
167 curtime = self.samplenum
168 dt = (curtime - prevtime) / self.samplerate
169 units = dt / timeunit
170 iunits = round(units)
171 error = abs(units - iunits)
172
173 symbol = (pval, iunits)
174
175 if self.matched[1]:
176 yield None # Flush word.
177 continue
178
179 self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
180 self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
181
182 if symbol in symbols:
183 yield (prevtime, curtime, symbol)
184
185 prevtime = curtime
186
187 thisunit = dt / iunits
188 timeunit += (thisunit - timeunit) * 0.02 * iunits # Adapt.
189
190 def decode_morse(self):
191 # Group symbols into letters.
192 sequence = ()
193 s0 = s1 = None
194
195 for item in self.decode_symbols():
196 do_yield = False
197 if item is not None: # Level + width.
198 (t0, t1, symbol) = item
199 (sval, sunits) = symbol
200 if sval == 1:
201 if s0 is None:
202 s0 = t0
203 s1 = t1
204 sequence += (sunits,)
205 else:
206 # Generate "flush" for end of letter, end of word.
207 if sunits >= 3:
208 do_yield = True
209 else:
210 do_yield = True
211 if do_yield:
212 if sequence:
213 yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence)))
214 sequence = ()
215 s0 = s1 = None
216 if item is None:
217 yield None # Pass through flush of 5+ space.
218
219 def decode(self):
220 # Annotate letters, group into words.
221 s0 = s1 = None
222 word = ''
223 for item in self.decode_morse():
224 do_yield = False
225
226 if item is not None: # Append letter.
227 (t0, t1, letter) = item
228 self.put(t0, t1, self.out_ann, [3, [letter]])
229 if s0 is None:
230 s0 = t0
231 s1 = t1
232 word += letter
233 else:
234 do_yield = True
235
236 if do_yield: # Flush of word.
237 if word:
238 self.put(s0, s1, self.out_ann, [4, [word]])
239 word = ''
240 s0 = s1 = None