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