]> sigrok.org Git - libsigrokdecode.git/blame - decoders/morse/pd.py
spiflash: Add Winbond W25Q80DV metadata.
[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']
500d5ff4
CR
157
158 self.wait({0: 'r'})
159 prevtime = self.samplenum # Time of an actual edge.
160
161 while True:
162 (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
163
164 pval = 1 - val
165 curtime = self.samplenum
166 dt = (curtime - prevtime) / self.samplerate
167 units = dt / timeunit
2d0ef8bd 168 iunits = int(max(1, round(units)))
500d5ff4
CR
169 error = abs(units - iunits)
170
171 symbol = (pval, iunits)
172
173 if self.matched[1]:
174 yield None # Flush word.
175 continue
176
177 self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
500d5ff4
CR
178
179 if symbol in symbols:
2d0ef8bd 180 self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
500d5ff4 181 yield (prevtime, curtime, symbol)
2d0ef8bd
CR
182 else:
183 self.put(prevtime, curtime, self.out_ann, [1, ['!! {:.1f}*{:.3g} !!'.format(units, timeunit)]])
500d5ff4
CR
184
185 prevtime = curtime
186
187 thisunit = dt / iunits
2d0ef8bd 188 timeunit += (thisunit - timeunit) * 0.2 * max(0, 1 - 2*error) # Adapt.
500d5ff4
CR
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):
15a8a055
GS
220
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
228
500d5ff4
CR
229 # Annotate letters, group into words.
230 s0 = s1 = None
231 word = ''
232 for item in self.decode_morse():
233 do_yield = False
234
235 if item is not None: # Append letter.
236 (t0, t1, letter) = item
237 self.put(t0, t1, self.out_ann, [3, [letter]])
238 if s0 is None:
239 s0 = t0
240 s1 = t1
241 word += letter
242 else:
243 do_yield = True
244
245 if do_yield: # Flush of word.
246 if word:
247 self.put(s0, s1, self.out_ann, [4, [word]])
248 word = ''
249 s0 = s1 = None