]> sigrok.org Git - libsigrokdecode.git/blob - decoders/morse/pd.py
ssi32: rename reset() helper method
[libsigrokdecode.git] / decoders / morse / pd.py
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
20 import sigrokdecode as srd
21
22 def decode_ditdah(s):
23     return tuple({'-': 3, '.': 1}[c] for c in s)
24
25 def 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
32 alphabet = {
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
100 alphabet = {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).
103 symbols = { # 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
115 class 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):
140         self.samplerate = None
141
142     def metadata(self, key, value):
143         if key == srd.SRD_CONF_SAMPLERATE:
144             self.samplerate = value
145
146     def start(self):
147         self.out_ann = self.register(srd.OUTPUT_ANN)
148         self.out_binary = self.register(srd.OUTPUT_BINARY)
149
150     def decode_symbols(self):
151         # Annotate symbols, emit symbols, handle timeout via token.
152
153         timeunit = self.options['timeunit']
154         if self.samplerate is None:
155             self.samplerate = 1.0
156
157         self.wait({0: 'r'})
158         prevtime = self.samplenum # Time of an actual edge.
159
160         while True:
161             (val,) = self.wait([{0: 'e'}, {'skip': int(5 * self.samplerate * timeunit)}])
162
163             pval = 1 - val
164             curtime = self.samplenum
165             dt = (curtime - prevtime) / self.samplerate
166             units = dt / timeunit
167             iunits = round(units)
168             error = abs(units - iunits)
169
170             symbol = (pval, iunits)
171
172             if self.matched[1]:
173                 yield None # Flush word.
174                 continue
175
176             self.put(prevtime, curtime, self.out_ann, [0, ['{:.3g}'.format(dt)]])
177             self.put(prevtime, curtime, self.out_ann, [1, ['{:.1f}*{:.3g}'.format(units, timeunit)]])
178
179             if symbol in symbols:
180                 yield (prevtime, curtime, symbol)
181
182             prevtime = curtime
183
184             thisunit = dt / iunits
185             timeunit += (thisunit - timeunit) * 0.02 * iunits # Adapt.
186
187     def decode_morse(self):
188         # Group symbols into letters.
189         sequence = ()
190         s0 = s1 = None
191
192         for item in self.decode_symbols():
193             do_yield = False
194             if item is not None: # Level + width.
195                 (t0, t1, symbol) = item
196                 (sval, sunits) = symbol
197                 if sval == 1:
198                     if s0 is None:
199                         s0 = t0
200                     s1 = t1
201                     sequence += (sunits,)
202                 else:
203                     # Generate "flush" for end of letter, end of word.
204                     if sunits >= 3:
205                         do_yield = True
206             else:
207                 do_yield = True
208             if do_yield:
209                 if sequence:
210                     yield (s0, s1, alphabet.get(sequence, encode_ditdah(sequence)))
211                     sequence = ()
212                     s0 = s1 = None
213             if item is None:
214                 yield None # Pass through flush of 5+ space.
215
216     def decode(self):
217         # Annotate letters, group into words.
218         s0 = s1 = None
219         word = ''
220         for item in self.decode_morse():
221             do_yield = False
222
223             if item is not None: # Append letter.
224                 (t0, t1, letter) = item
225                 self.put(t0, t1, self.out_ann, [3, [letter]])
226                 if s0 is None:
227                     s0 = t0
228                 s1 = t1
229                 word += letter
230             else:
231                 do_yield = True
232
233             if do_yield: # Flush of word.
234                 if word:
235                     self.put(s0, s1, self.out_ann, [4, [word]])
236                     word = ''
237                     s0 = s1 = None