]> sigrok.org Git - libsigrokdecode.git/blob - decoders/ook/pd.py
added ook
[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             mystring = 'Pulses(us)='
146             mystring += ','.join(str(int(int(x) * 1000000 / self.samplerate))
147                                  for x in self.pulse_lengths)
148             mystring += '\n'
149             self.put(self.samplenum - 10, self.samplenum, self.out_binary,
150                      [0, bytes([ord(c) for c in mystring])])
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         if state == '1':
156             dsamples = self.sample_high
157         else:
158             dsamples = self.sample_low
159         self.ss = start
160         self.es = start + samples
161         while samples > dsamples * 0.5:
162             if samples >= dsamples * 1.5: # More than one bit.
163                 self.es = self.ss + dsamples
164                 self.putx([5, [state]])
165                 self.decoded.append([self.ss, self.es, state])
166                 self.edge_count += 1
167             elif samples >= dsamples * 0.5 and samples < dsamples * 1.5: # Last bit.
168                 self.putx([5, [state]])
169                 self.decoded.append([self.ss, self.es, state])
170                 self.edge_count += 1
171             else:
172                 self.edge_count += 1
173             samples -= dsamples
174             self.ss += dsamples
175             self.es += dsamples
176
177             # Ensure 2nd row doesn't go past end of 1st row.
178             if self.es > self.samplenum:
179                 self.es = self.samplenum
180
181             if self.state == 'DECODE_TIMEOUT': # Five bits - reset.
182                 self.ss = self.decoded[0][0]
183                 self.es = self.decoded[len(self.decoded) - 1][1]
184                 self.dump_pulse_lengths()
185                 self.putp(self.decoded)
186                 self.decode_timeout()
187                 break
188
189     def lock_onto_preamble(self, samples, state): # Filters and recovers clock.
190         self.edge_count += 1
191         l2s = 5 # Max ratio of long to short pulses.
192
193         # Filter incoming pulses to remove random noise.
194         if self.state == 'DECODE_TIMEOUT':
195             self.preamble = []
196             self.edge_count == 0
197             self.word_first = self.samplenum
198             self.sample_first = self.samplenum - self.samplenumber_last
199             self.state = 'WAITING_FOR_PREAMBLE'
200             self.man_errors = 0
201
202         pre_detect = int(self.preamble_len) # Number of valid pulses to detect.
203         pre_samples = self.samplenum - self.samplenumber_last
204         if len(self.preamble) > 0:
205             if (pre_samples * l2s < self.preamble[-1][1] or
206                 self.preamble[-1][1] * l2s < pre_samples): # Garbage in.
207                 self.put(self.samplenum, self.samplenum,
208                          self.out_ann, [0, ['R']]) # Display resets.
209                 self.preamble = [] # Clear buffer.
210                 self.preamble.append([self.samplenumber_last,
211                                      pre_samples, state])
212                 self.edge_count == 0
213                 self.samplenumber_last = self.samplenum
214                 self.word_first = self.samplenum
215             else:
216                 self.preamble.append([self.samplenumber_last,
217                                      pre_samples, state])
218         else:
219             self.preamble.append([self.samplenumber_last,
220                                  pre_samples, state])
221
222         pre = self.preamble
223         if len(self.preamble) == pre_detect: # Have a valid series of pulses.
224             if self.preamble[0][2] == '1':
225                 self.sample_high = self.preamble[0][1] # Allows skewed pulses.
226                 self.sample_low = self.preamble[1][1]
227             else:
228                 self.sample_high = self.preamble[1][1]
229                 self.sample_low = self.preamble[0][1]
230
231             self.edge_count = 0
232
233             for i in range(len(self.preamble)):
234                 if i > 1:
235                     if (pre[i][1] > pre[i - 2][1] * 1.25 or
236                         pre[i][1] * 1.25 < pre[i - 2][1]): # Adjust ref width.
237                         if pre[i][2] == '1':
238                             self.sample_high = pre[i][1]
239                         else:
240                             self.sample_low = pre[i][1]
241
242                 # Display start of preamble.
243                 if self.decodeas == 'NRZ':
244                     self.decode_nrz(pre[i][0], pre[i][1], pre[i][2])
245                 if self.decodeas == 'Manchester':
246                     self.decode_manchester(pre[i][0], pre[i][1], pre[i][2])
247                 if self.decodeas == 'Diff Manchester':
248                     self.es = pre[i][0] + pre[i][1]
249                     self.decode_diff_manchester(pre[i][0], pre[i][1], pre[i][2])
250
251                 # Used to timeout signal.
252                 self.sample_first = int((self.sample_high + self.sample_low)/2)
253             self.insync = 1
254             self.state = 'DECODING'
255         self.lstate = state
256         self.lstate_1010 = state
257
258     def decode_diff_manchester(self, start, samples, state):
259         self.pulse_lengths.append(samples)
260
261         # Use different high and low widths to compensate skewed waveforms.
262         if state == '1':
263             dsamples = self.sample_high
264         else:
265             dsamples = self.sample_low
266
267         self.es = start + samples
268         p_length = round(samples / dsamples) # Find relative pulse length.
269
270         if self.edge_count == 0:
271             self.diff_man_trans = '1'  # Very first pulse must be a transition.
272             self.diff_man_len = 1      # Must also be a half pulse.
273             self.ss = start
274         elif self.edge_count % 2 == 1: # Time to make a decision.
275             if self.diffmanvar == '0': # Transition at self.ss is a zero.
276                 self.diff_man_trans = '0' if self.diff_man_trans == '1' else '1'
277             if self.diff_man_len == 1 and p_length == 1:
278                 self.putx([4, [self.diff_man_trans]])
279                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
280                 self.diff_man_trans = '1'
281             elif self.diff_man_len == 1 and p_length == 2:
282                 self.es -= int(samples / 2)
283                 self.putx([4, [self.diff_man_trans]])
284                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
285                 self.diff_man_trans = '0'
286                 self.edge_count += 1 # Add a virt edge to keep in sync with clk.
287             elif self.diff_man_len == 2 and p_length == 1:
288                 self.putx([4, [self.diff_man_trans]])
289                 self.decoded.append([self.ss, self.es, self.diff_man_trans])
290                 self.diff_man_trans = '1'
291             elif self.diff_man_len == 2 and p_length == 2: # Double illegal E E.
292                 self.es -= samples
293                 self.putx([4, ['E']])
294                 self.decoded.append([self.ss, self.es, 'E'])
295                 self.ss = self.es
296                 self.es += samples
297                 self.putx([4, ['E']])
298                 self.decoded.append([self.ss, self.es, 'E'])
299                 self.diff_man_trans = '1'
300             elif self.diff_man_len == 1 and p_length > 4:
301                 if self.state == 'DECODE_TIMEOUT':
302                     self.es = self.ss + 2 * self.sample_first
303                     self.putx([4, [self.diff_man_trans]]) # Write error.
304                     self.decoded.append([self.ss, self.es, self.diff_man_trans])
305                     self.ss = self.decoded[0][0]
306                     self.es = self.decoded[len(self.decoded) - 1][1]
307                     self.dump_pulse_lengths()
308                     if self.man_errors < self.max_errors:
309                         self.putp(self.decoded)
310                     else:
311                         error_message = 'Probably not Diff Manchester encoded'
312                         self.ss = self.word_first
313                         self.putx([1, [error_message]])
314                     self.decode_timeout()
315                 self.diff_man_trans = '1'
316             self.ss = self.es
317         self.diff_man_len = p_length # Save the previous length.
318         self.edge_count += 1
319
320     def decode_manchester_sim(self, start, samples, state,
321                               dsamples, half_time, lstate, ss, pream):
322         ook_bit = []
323         errors = 0
324         if self.edge_count == 0:
325             half_time += 1
326         if samples > 0.75 * dsamples and samples <= 1.5 * dsamples: # Long p.
327             half_time += 2
328             if half_time % 2 == 0: # Transition.
329                 es = start
330             else:
331                 es = start + int(samples / 2)
332             if ss == start:
333                 lstate = 'E'
334                 es = start + samples
335             if not (self.edge_count == 0 and pream == '1010'): # Skip first p.
336                 ook_bit = [ss, es, lstate]
337             lstate = state
338             ss = es
339         elif samples > 0.25 * dsamples and samples <= 0.75 * dsamples: # Short p.
340             half_time += 1
341             if (half_time % 2 == 0): # Transition.
342                 es = start + samples
343                 ook_bit = [ss, es, lstate]
344                 lstate = state
345                 ss = es
346             else: # 1st half.
347                 ss = start
348                 lstate = state
349         else: # Too long or too short - error.
350             errors = 1
351             if self.state != 'DECODE_TIMEOUT': # Error condition.
352                 lstate = 'E'
353                 es = ss + samples
354             else: # Assume final half bit buried in timeout pulse.
355                 es = ss + self.sample_first
356             ook_bit = [ss, es, lstate]
357             ss = es
358
359         return (half_time, lstate, ss, ook_bit, errors)
360
361     def decode_manchester(self, start, samples, state):
362         self.pulse_lengths.append(samples)
363
364         # Use different high and low widths to compensate skewed waveforms.
365         if state == '1':
366             dsamples = self.sample_high
367         else:
368             dsamples = self.sample_low
369
370         if self.preamble_val != '1010': # 1111 preamble is half clock T.
371             (self.half_time, self.lstate, self.ss_1111, ook_bit, errors) = (
372              self.decode_manchester_sim(start, samples, state, dsamples * 2,
373                                     self.half_time, self.lstate,
374                                     self.ss_1111, '1111'))
375             self.man_errors += errors
376             if ook_bit != []:
377                 self.decoded.append([ook_bit[0], ook_bit[1], ook_bit[2]])
378
379         if self.preamble_val != '1111': # 1010 preamble is clock T.
380             (self.half_time_1010, self.lstate_1010, self.ss_1010,
381              ook_bit, errors) = (
382               self.decode_manchester_sim(start, samples, state, dsamples,
383                                     self.half_time_1010, self.lstate_1010,
384                                     self.ss_1010, '1010'))
385             self.man_errors_1010 += errors
386             if ook_bit != []:
387                 self.decoded_1010.append([ook_bit[0], ook_bit[1], ook_bit[2]])
388
389         self.edge_count += 1
390
391         # Stream display and save ook_bit.
392         if ook_bit != []:
393             self.ss = ook_bit[0]
394             self.es = ook_bit[1]
395             if self.preamble_val == '1111':
396                 self.putx([2, [ook_bit[2]]])
397             if self.preamble_val == '1010':
398                 self.putx([3, [ook_bit[2]]])
399
400         if self.state == 'DECODE_TIMEOUT': # End of packet.
401             self.dump_pulse_lengths()
402
403             decoded = []
404             # If 1010 preamble has less errors use it.
405             if (self.preamble_val == '1010' or
406                 (self.man_errors_1010 < self.max_errors and
407                 self.man_errors_1010 < self.man_errors and
408                 len(self.decoded_1010) > 0)):
409                 decoded = self.decoded_1010
410                 man_errors = self.man_errors_1010
411                 d_row = 3
412             else:
413                 decoded = self.decoded
414                 man_errors = self.man_errors
415                 d_row = 2
416
417             if self.preamble_val == 'auto': # Display OOK packet.
418                 for i in range(len(decoded)):
419                     self.ss = decoded[i][0]
420                     self.es = decoded[i][1]
421                     self.putx([d_row, [decoded[i][2]]])
422
423             if (man_errors < self.max_errors and len(decoded) > 0):
424                 self.ss = decoded[0][0]
425                 self.es = decoded[len(decoded) - 1][1]
426                 self.putp(decoded)
427             else:
428                 error_message = 'Not Manchester encoded or wrong preamble'
429                 self.ss = self.word_first
430                 self.putx([1, [error_message]])
431
432             self.put(self.es, self.es, self.out_ann, [0, ['T']]) # Mark timeout.
433             self.decode_timeout()
434
435     def decode_timeout(self):
436         self.word_count = 0
437         self.samplenumber_last = None
438         self.edge_count = 0
439         self.man_errors = 0                     # Clear the bit error counters.
440         self.man_errors_1010 = 0
441         self.state = 'IDLE'
442         self.wait({0: 'e'})                     # Get rid of long pulse.
443         self.samplenumber_last = self.samplenum
444         self.word_first = self.samplenum
445         self.insync = 0                         # Preamble in sync flag
446         self.preamble = []                      # Preamble buffer
447         self.half_time = -1                     # Half time for man 1111
448         self.half_time_1010 = 0                 # Half time for man 1010
449         self.decoded = []                       # Decoded bits
450         self.decoded_1010 = []                  # Decoded bits for man 1010
451         self.pulse_lengths = []
452
453     def decode(self):
454         while True:
455             if self.edge_count == 0: # Waiting for a signal.
456                 pin = self.wait({0: 'e'})
457                 self.state = 'DECODING'
458             else:
459                 pin = self.wait([{0: 'e'}, {'skip': 5 * self.sample_first}])
460                 if self.matched[1] and not self.matched[0]: # No edges for 5 p's.
461                     self.state = 'DECODE_TIMEOUT'
462
463             if not self.samplenumber_last: # Set counters to start of signal.
464                 self.samplenumber_last = self.samplenum
465                 self.word_first = self.samplenum
466                 continue
467             samples = self.samplenum - self.samplenumber_last
468             if not self.sample_first: # Get number of samples for first pulse.
469                 self.sample_first = samples
470
471             pinstate = pin[0]
472             if self.state == 'DECODE_TIMEOUT': # No edge so flip the state.
473                 pinstate = int(not pinstate)
474             if self.invert == 'yes': # Invert signal.
475                 pinstate = int(not pinstate)
476             if pinstate:
477                 state = '0'
478             else:
479                 state = '1'
480
481             # No preamble filtering or checking and no skew correction.
482             if self.preamble_len == '0':
483                 self.sample_high = self.sample_first
484                 self.sample_low = self.sample_first
485                 self.insync = 0
486
487             if self.insync == 0:
488                 self.lock_onto_preamble(samples, state)
489             else:
490                 if self.decodeas == 'NRZ':
491                     self.decode_nrz(self.samplenumber_last, samples, state)
492                 if self.decodeas == 'Manchester':
493                     self.decode_manchester(self.samplenumber_last,
494                                            samples, state)
495                 if self.decodeas == 'Diff Manchester':
496                     self.decode_diff_manchester(self.samplenumber_last,
497                                                 samples, state)
498
499             self.samplenumber_last = self.samplenum