]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ook/pd.py
log: Use human-readable output type name everywhere.
[libsigrokdecode.git] / decoders / ook / pd.py
CommitLineData
538d386b
S
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
20import sigrokdecode as srd
21
22'''
23OUTPUT_PYTHON format:
24Samples: 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
27up with the pulses that were converted into the decoded bit particularly for
28Manchester 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.
31This 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
35pulse lengths according to the rules for the particular encoding. In some cases
36this is intentional (Oregon 1 preamble) and is part of the sync pattern. In
37other cases the signal could simply be broken.
38
39If there are more than self.max_errors (default 5) in decoding then the
40OUTPUT_PYTHON is not sent as the data is assumed to be worthless.
41There also needs to be a low for five times the preamble period at the end of
42each set of pulses to trigger a DECODE_TIMEOUT and get the OUTPUT_PYTHON sent.
43'''
44
45class SamplerateError(Exception):
46 pass
47
48class 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.
ca4a1d1d
UH
145 s = 'Pulses(us)='
146 s += ','.join(str(int(int(x) * 1000000 / self.samplerate))
147 for x in self.pulse_lengths)
148 s += '\n'
538d386b 149 self.put(self.samplenum - 10, self.samplenum, self.out_binary,
ca4a1d1d 150 [0, bytes([ord(c) for c in s])])
538d386b
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.
ca4a1d1d
UH
155 dsamples = self.sample_high if state == '1' else self.sample_low
156 self.ss, self.es = start, start + samples
538d386b
S
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.
ca4a1d1d 258 dsamples = self.sample_high if state == '1' else self.sample_low
538d386b
S
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.
ca4a1d1d 358 dsamples = self.sample_high if state == '1' else self.sample_low
538d386b
S
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 != []:
ca4a1d1d 383 self.ss, self.es = ook_bit[0], ook_bit[1]
538d386b
S
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)):
ca4a1d1d 408 self.ss, self.es = decoded[i][0], decoded[i][1]
538d386b
S
409 self.putx([d_row, [decoded[i][2]]])
410
411 if (man_errors < self.max_errors and len(decoded) > 0):
ca4a1d1d 412 self.ss, self.es = decoded[0][0], decoded[len(decoded) - 1][1]
538d386b
S
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)
ca4a1d1d 463 state = '0' if pinstate else '1'
538d386b
S
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