]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ook/pd.py
decoders: Add/update tags for each PD.
[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']
d6d8a8a4 57 tags = ['Encoding']
538d386b
S
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.
ca4a1d1d
UH
146 s = 'Pulses(us)='
147 s += ','.join(str(int(int(x) * 1000000 / self.samplerate))
148 for x in self.pulse_lengths)
149 s += '\n'
538d386b 150 self.put(self.samplenum - 10, self.samplenum, self.out_binary,
ca4a1d1d 151 [0, bytes([ord(c) for c in s])])
538d386b
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.
ca4a1d1d
UH
156 dsamples = self.sample_high if state == '1' else self.sample_low
157 self.ss, self.es = start, start + samples
538d386b
S
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 = []
2dfe0986 193 self.edge_count = 0
538d386b
S
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])
2dfe0986 209 self.edge_count = 0
538d386b
S
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.
ca4a1d1d 259 dsamples = self.sample_high if state == '1' else self.sample_low
538d386b
S
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.
ca4a1d1d 359 dsamples = self.sample_high if state == '1' else self.sample_low
538d386b
S
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 != []:
ca4a1d1d 384 self.ss, self.es = ook_bit[0], ook_bit[1]
538d386b
S
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)):
ca4a1d1d 409 self.ss, self.es = decoded[i][0], decoded[i][1]
538d386b
S
410 self.putx([d_row, [decoded[i][2]]])
411
412 if (man_errors < self.max_errors and len(decoded) > 0):
ca4a1d1d 413 self.ss, self.es = decoded[0][0], decoded[len(decoded) - 1][1]
538d386b
S
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)
ca4a1d1d 464 state = '0' if pinstate else '1'
538d386b
S
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