]> sigrok.org Git - libsigrokdecode.git/blob - decoders/ook/pd.py
b4971b621d3a330391c92e5388bfb35297794ffb
[libsigrokdecode.git] / decoders / ook / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2018 Steve R <steversig@virginmedia.com>
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 '''
23 OUTPUT_PYTHON format:
24 Samples:    The Samples array is sent when a DECODE_TIMEOUT occurs.
25 [<start>, <finish>, <state>]
26 <start> is the sample number of the start of the decoded bit. This may not line
27 up with the pulses that were converted into the decoded bit particularly for
28 Manchester encoding.
29 <finish> is the sample number of the end of the decoded bit.
30 <state> is a single character string which is the state of the decoded bit.
31 This can be
32 '0'   zero or low
33 '1'   one or high
34 'E'   Error or invalid. This can be caused by missing transitions or the wrong
35 pulse lengths according to the rules for the particular encoding. In some cases
36 this is intentional (Oregon 1 preamble) and is part of the sync pattern. In
37 other cases the signal could simply be broken.
38
39 If there are more than self.max_errors (default 5) in decoding then the
40 OUTPUT_PYTHON is not sent as the data is assumed to be worthless.
41 There also needs to be a low for five times the preamble period at the end of
42 each set of pulses to trigger a DECODE_TIMEOUT and get the OUTPUT_PYTHON sent.
43 '''
44
45 class SamplerateError(Exception):
46     pass
47
48 class Decoder(srd.Decoder):
49     api_version = 3
50     id = 'ook'
51     name = 'OOK'
52     longname = 'On-off keying'
53     desc = 'On-off keying protocol.'
54     license = 'gplv2+'
55     inputs = ['logic']
56     outputs = ['ook']
57     channels = (
58         {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
59     )
60     annotations = (
61         ('frame', 'Frame'),
62         ('info', 'Info'),
63         ('1111', '1111'),
64         ('1010', '1010'),
65         ('diffman', 'Diff Man'),
66         ('nrz', 'NRZ'),
67     )
68     annotation_rows = (
69         ('frame', 'Framing',(0,)),
70         ('info', 'Info', (1,)),
71         ('man1111', 'Man 1111', (2,)),
72         ('man1010', 'Man 1010', (3,)),
73         ('diffman', 'Diff Man', (4,)),
74         ('nrz', 'NRZ', (5,)),
75     )
76     binary = (
77         ('pulse-lengths', 'Pulse lengths'),
78     )
79     options = (
80         {'id': 'invert', 'desc': 'Invert data', 'default': 'no',
81          'values': ('no', 'yes')},
82         {'id': 'decodeas', 'desc': 'Decode type', 'default': 'Manchester',
83          'values': ('NRZ', 'Manchester', 'Diff Manchester')},
84         {'id': 'preamble', 'desc': 'Preamble', 'default': 'auto',
85          'values': ('auto', '1010', '1111')},
86         {'id': 'preamlen', 'desc': 'Filter length', 'default': '7',
87          'values': ('0', '3', '4', '5', '6', '7', '8', '9', '10')},
88         {'id': 'diffmanvar', 'desc': 'Transition at start', 'default': '1',
89          'values': ('1', '0')},
90     )
91
92     def __init__(self):
93         self.reset()
94
95     def reset(self):
96         self.samplerate = None
97         self.ss = self.es = -1
98         self.ss_1111 = self.ss_1010 = -1
99         self.samplenumber_last = None
100         self.sample_first = None
101         self.sample_high = 0
102         self.sample_low = 0
103         self.edge_count = 0
104         self.word_first = None
105         self.word_count = 0
106         self.state = 'IDLE'
107         self.lstate = None
108         self.lstate_1010 = None
109         self.insync = 0                 # Preamble in sync flag
110         self.man_errors = 0
111         self.man_errors_1010 = 0
112         self.preamble = []              # Preamble buffer
113         self.half_time = -1             # Half time for man 1111
114         self.half_time_1010 = 0         # Half time for man 1010
115         self.pulse_lengths = []         # Pulse lengths
116         self.decoded = []               # Decoded stream
117         self.decoded_1010 = []          # Decoded stream
118         self.diff_man_trans = '0'       # Transition
119         self.diff_man_len = 1           # Length of pulse in half clock periods
120         self.max_errors = 5             # Max number of errors to output OOK
121
122     def metadata(self, key, value):
123         if key == srd.SRD_CONF_SAMPLERATE:
124             self.samplerate = value
125
126     def start(self):
127         self.out_ann = self.register(srd.OUTPUT_ANN)
128         self.out_python = self.register(srd.OUTPUT_PYTHON)
129         self.out_binary = self.register(srd.OUTPUT_BINARY)
130         self.invert = self.options['invert']
131         self.decodeas = self.options['decodeas']
132         self.preamble_val = self.options['preamble']
133         self.preamble_len = self.options['preamlen']
134         self.diffmanvar = self.options['diffmanvar']
135
136     def putx(self, data):
137         self.put(self.ss, self.es, self.out_ann, data)
138
139     def putp(self, data):
140         self.put(self.ss, self.es, self.out_python, data)
141
142     def dump_pulse_lengths(self):
143         if self.samplerate:
144             self.pulse_lengths[-1] = self.sample_first # Fix final pulse length.
145             s = 'Pulses(us)='
146             s += ','.join(str(int(int(x) * 1000000 / self.samplerate))
147                           for x in self.pulse_lengths)
148             s += '\n'
149             self.put(self.samplenum - 10, self.samplenum, self.out_binary,
150                      [0, bytes([ord(c) for c in s])])
151
152     def decode_nrz(self, start, samples, state):
153         self.pulse_lengths.append(samples)
154         # Use different high and low widths to compensate skewed waveforms.
155         dsamples = self.sample_high if state == '1' else self.sample_low
156         self.ss, self.es = start, start + samples
157         while samples > dsamples * 0.5:
158             if samples >= dsamples * 1.5: # More than one bit.
159                 self.es = self.ss + dsamples
160                 self.putx([5, [state]])
161                 self.decoded.append([self.ss, self.es, state])
162                 self.edge_count += 1
163             elif samples >= dsamples * 0.5 and samples < dsamples * 1.5: # Last bit.
164                 self.putx([5, [state]])
165                 self.decoded.append([self.ss, self.es, state])
166                 self.edge_count += 1
167             else:
168                 self.edge_count += 1
169             samples -= dsamples
170             self.ss += dsamples
171             self.es += dsamples
172
173             # Ensure 2nd row doesn't go past end of 1st row.
174             if self.es > self.samplenum:
175                 self.es = self.samplenum
176
177             if self.state == 'DECODE_TIMEOUT': # Five bits - reset.
178                 self.ss = self.decoded[0][0]
179                 self.es = self.decoded[len(self.decoded) - 1][1]
180                 self.dump_pulse_lengths()
181                 self.putp(self.decoded)
182                 self.decode_timeout()
183                 break
184
185     def lock_onto_preamble(self, samples, state): # Filters and recovers clock.
186         self.edge_count += 1
187         l2s = 5 # Max ratio of long to short pulses.
188
189         # Filter incoming pulses to remove random noise.
190         if self.state == 'DECODE_TIMEOUT':
191             self.preamble = []
192             self.edge_count == 0
193             self.word_first = self.samplenum
194             self.sample_first = self.samplenum - self.samplenumber_last
195             self.state = 'WAITING_FOR_PREAMBLE'
196             self.man_errors = 0
197
198         pre_detect = int(self.preamble_len) # Number of valid pulses to detect.
199         pre_samples = self.samplenum - self.samplenumber_last
200         if len(self.preamble) > 0:
201             if (pre_samples * l2s < self.preamble[-1][1] or
202                 self.preamble[-1][1] * l2s < pre_samples): # Garbage in.
203                 self.put(self.samplenum, self.samplenum,
204                          self.out_ann, [0, ['R']]) # Display resets.
205                 self.preamble = [] # Clear buffer.
206                 self.preamble.append([self.samplenumber_last,
207                                      pre_samples, state])
208                 self.edge_count == 0
209                 self.samplenumber_last = self.samplenum
210                 self.word_first = self.samplenum
211             else:
212                 self.preamble.append([self.samplenumber_last,
213                                      pre_samples, state])
214         else:
215             self.preamble.append([self.samplenumber_last,
216                                  pre_samples, state])
217
218         pre = self.preamble
219         if len(self.preamble) == pre_detect: # Have a valid series of pulses.
220             if self.preamble[0][2] == '1':
221                 self.sample_high = self.preamble[0][1] # Allows skewed pulses.
222                 self.sample_low = self.preamble[1][1]
223             else:
224                 self.sample_high = self.preamble[1][1]
225                 self.sample_low = self.preamble[0][1]
226
227             self.edge_count = 0
228
229             for i in range(len(self.preamble)):
230                 if i > 1:
231                     if (pre[i][1] > pre[i - 2][1] * 1.25 or
232                         pre[i][1] * 1.25 < pre[i - 2][1]): # Adjust ref width.
233                         if pre[i][2] == '1':
234                             self.sample_high = pre[i][1]
235                         else:
236                             self.sample_low = pre[i][1]
237
238                 # Display start of preamble.
239                 if self.decodeas == 'NRZ':
240                     self.decode_nrz(pre[i][0], pre[i][1], pre[i][2])
241                 if self.decodeas == 'Manchester':
242                     self.decode_manchester(pre[i][0], pre[i][1], pre[i][2])
243                 if self.decodeas == 'Diff Manchester':
244                     self.es = pre[i][0] + pre[i][1]
245                     self.decode_diff_manchester(pre[i][0], pre[i][1], pre[i][2])
246
247                 # Used to timeout signal.
248                 self.sample_first = int((self.sample_high + self.sample_low)/2)
249             self.insync = 1
250             self.state = 'DECODING'
251         self.lstate = state
252         self.lstate_1010 = state
253
254     def decode_diff_manchester(self, start, samples, state):
255         self.pulse_lengths.append(samples)
256
257         # Use different high and low widths to compensate skewed waveforms.
258         dsamples = self.sample_high if state == '1' else self.sample_low
259
260         self.es = start + samples
261         p_length = round(samples / dsamples) # Find relative pulse length.
262
263         if self.edge_count == 0:
264             self.diff_man_trans = '1'  # Very first pulse must be a transition.
265             self.diff_man_len = 1      # Must also be a half pulse.
266             self.ss = start
267         elif self.edge_count % 2 == 1: # Time to make a decision.
268             if self.diffmanvar == '0': # Transition at self.ss is a zero.
269                 self.diff_man_trans = '0' if self.diff_man_trans == '1' else '1'
270             if self.diff_man_len == 1 and p_length == 1:
271                 self.putx([4, [self.diff_man_trans]])
272                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
273                 self.diff_man_trans = '1'
274             elif self.diff_man_len == 1 and p_length == 2:
275                 self.es -= int(samples / 2)
276                 self.putx([4, [self.diff_man_trans]])
277                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
278                 self.diff_man_trans = '0'
279                 self.edge_count += 1 # Add a virt edge to keep in sync with clk.
280             elif self.diff_man_len == 2 and p_length == 1:
281                 self.putx([4, [self.diff_man_trans]])
282                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
283                 self.diff_man_trans = '1'
284             elif self.diff_man_len == 2 and p_length == 2: # Double illegal E E.
285                 self.es -= samples
286                 self.putx([4, ['E']])
287                 self.decoded.append([self.ss, self.es, 'E'])
288                 self.ss = self.es
289                 self.es += samples
290                 self.putx([4, ['E']])
291                 self.decoded.append([self.ss, self.es, 'E'])
292                 self.diff_man_trans = '1'
293             elif self.diff_man_len == 1 and p_length > 4:
294                 if self.state == 'DECODE_TIMEOUT':
295                     self.es = self.ss + 2 * self.sample_first
296                     self.putx([4, [self.diff_man_trans]]) # Write error.
297                     self.decoded.append([self.ss, self.es, self.diff_man_trans])
298                     self.ss = self.decoded[0][0]
299                     self.es = self.decoded[len(self.decoded) - 1][1]
300                     self.dump_pulse_lengths()
301                     if self.man_errors < self.max_errors:
302                         self.putp(self.decoded)
303                     else:
304                         error_message = 'Probably not Diff Manchester encoded'
305                         self.ss = self.word_first
306                         self.putx([1, [error_message]])
307                     self.decode_timeout()
308                 self.diff_man_trans = '1'
309             self.ss = self.es
310         self.diff_man_len = p_length # Save the previous length.
311         self.edge_count += 1
312
313     def decode_manchester_sim(self, start, samples, state,
314                               dsamples, half_time, lstate, ss, pream):
315         ook_bit = []
316         errors = 0
317         if self.edge_count == 0:
318             half_time += 1
319         if samples > 0.75 * dsamples and samples <= 1.5 * dsamples: # Long p.
320             half_time += 2
321             if half_time % 2 == 0: # Transition.
322                 es = start
323             else:
324                 es = start + int(samples / 2)
325             if ss == start:
326                 lstate = 'E'
327                 es = start + samples
328             if not (self.edge_count == 0 and pream == '1010'): # Skip first p.
329                 ook_bit = [ss, es, lstate]
330             lstate = state
331             ss = es
332         elif samples > 0.25 * dsamples and samples <= 0.75 * dsamples: # Short p.
333             half_time += 1
334             if (half_time % 2 == 0): # Transition.
335                 es = start + samples
336                 ook_bit = [ss, es, lstate]
337                 lstate = state
338                 ss = es
339             else: # 1st half.
340                 ss = start
341                 lstate = state
342         else: # Too long or too short - error.
343             errors = 1
344             if self.state != 'DECODE_TIMEOUT': # Error condition.
345                 lstate = 'E'
346                 es = ss + samples
347             else: # Assume final half bit buried in timeout pulse.
348                 es = ss + self.sample_first
349             ook_bit = [ss, es, lstate]
350             ss = es
351
352         return (half_time, lstate, ss, ook_bit, errors)
353
354     def decode_manchester(self, start, samples, state):
355         self.pulse_lengths.append(samples)
356
357         # Use different high and low widths to compensate skewed waveforms.
358         dsamples = self.sample_high if state == '1' else self.sample_low
359
360         if self.preamble_val != '1010': # 1111 preamble is half clock T.
361             (self.half_time, self.lstate, self.ss_1111, ook_bit, errors) = (
362              self.decode_manchester_sim(start, samples, state, dsamples * 2,
363                                     self.half_time, self.lstate,
364                                     self.ss_1111, '1111'))
365             self.man_errors += errors
366             if ook_bit != []:
367                 self.decoded.append([ook_bit[0], ook_bit[1], ook_bit[2]])
368
369         if self.preamble_val != '1111': # 1010 preamble is clock T.
370             (self.half_time_1010, self.lstate_1010, self.ss_1010,
371              ook_bit, errors) = (
372               self.decode_manchester_sim(start, samples, state, dsamples,
373                                     self.half_time_1010, self.lstate_1010,
374                                     self.ss_1010, '1010'))
375             self.man_errors_1010 += errors
376             if ook_bit != []:
377                 self.decoded_1010.append([ook_bit[0], ook_bit[1], ook_bit[2]])
378
379         self.edge_count += 1
380
381         # Stream display and save ook_bit.
382         if ook_bit != []:
383             self.ss, self.es = ook_bit[0], ook_bit[1]
384             if self.preamble_val == '1111':
385                 self.putx([2, [ook_bit[2]]])
386             if self.preamble_val == '1010':
387                 self.putx([3, [ook_bit[2]]])
388
389         if self.state == 'DECODE_TIMEOUT': # End of packet.
390             self.dump_pulse_lengths()
391
392             decoded = []
393             # If 1010 preamble has less errors use it.
394             if (self.preamble_val == '1010' or
395                 (self.man_errors_1010 < self.max_errors and
396                 self.man_errors_1010 < self.man_errors and
397                 len(self.decoded_1010) > 0)):
398                 decoded = self.decoded_1010
399                 man_errors = self.man_errors_1010
400                 d_row = 3
401             else:
402                 decoded = self.decoded
403                 man_errors = self.man_errors
404                 d_row = 2
405
406             if self.preamble_val == 'auto': # Display OOK packet.
407                 for i in range(len(decoded)):
408                     self.ss, self.es = decoded[i][0], decoded[i][1]
409                     self.putx([d_row, [decoded[i][2]]])
410
411             if (man_errors < self.max_errors and len(decoded) > 0):
412                 self.ss, self.es = decoded[0][0], decoded[len(decoded) - 1][1]
413                 self.putp(decoded)
414             else:
415                 error_message = 'Not Manchester encoded or wrong preamble'
416                 self.ss = self.word_first
417                 self.putx([1, [error_message]])
418
419             self.put(self.es, self.es, self.out_ann, [0, ['T']]) # Mark timeout.
420             self.decode_timeout()
421
422     def decode_timeout(self):
423         self.word_count = 0
424         self.samplenumber_last = None
425         self.edge_count = 0
426         self.man_errors = 0                     # Clear the bit error counters.
427         self.man_errors_1010 = 0
428         self.state = 'IDLE'
429         self.wait({0: 'e'})                     # Get rid of long pulse.
430         self.samplenumber_last = self.samplenum
431         self.word_first = self.samplenum
432         self.insync = 0                         # Preamble in sync flag
433         self.preamble = []                      # Preamble buffer
434         self.half_time = -1                     # Half time for man 1111
435         self.half_time_1010 = 0                 # Half time for man 1010
436         self.decoded = []                       # Decoded bits
437         self.decoded_1010 = []                  # Decoded bits for man 1010
438         self.pulse_lengths = []
439
440     def decode(self):
441         while True:
442             if self.edge_count == 0: # Waiting for a signal.
443                 pin = self.wait({0: 'e'})
444                 self.state = 'DECODING'
445             else:
446                 pin = self.wait([{0: 'e'}, {'skip': 5 * self.sample_first}])
447                 if self.matched[1] and not self.matched[0]: # No edges for 5 p's.
448                     self.state = 'DECODE_TIMEOUT'
449
450             if not self.samplenumber_last: # Set counters to start of signal.
451                 self.samplenumber_last = self.samplenum
452                 self.word_first = self.samplenum
453                 continue
454             samples = self.samplenum - self.samplenumber_last
455             if not self.sample_first: # Get number of samples for first pulse.
456                 self.sample_first = samples
457
458             pinstate = pin[0]
459             if self.state == 'DECODE_TIMEOUT': # No edge so flip the state.
460                 pinstate = int(not pinstate)
461             if self.invert == 'yes': # Invert signal.
462                 pinstate = int(not pinstate)
463             state = '0' if pinstate else '1'
464
465             # No preamble filtering or checking and no skew correction.
466             if self.preamble_len == '0':
467                 self.sample_high = self.sample_first
468                 self.sample_low = self.sample_first
469                 self.insync = 0
470
471             if self.insync == 0:
472                 self.lock_onto_preamble(samples, state)
473             else:
474                 if self.decodeas == 'NRZ':
475                     self.decode_nrz(self.samplenumber_last, samples, state)
476                 if self.decodeas == 'Manchester':
477                     self.decode_manchester(self.samplenumber_last,
478                                            samples, state)
479                 if self.decodeas == 'Diff Manchester':
480                     self.decode_diff_manchester(self.samplenumber_last,
481                                                 samples, state)
482
483             self.samplenumber_last = self.samplenum