]> sigrok.org Git - libsigrokdecode.git/blob - decoders/pjdl/pd.py
5a61a2f86a7570f184f2bc5a2683d61c006f323f
[libsigrokdecode.git] / decoders / pjdl / pd.py
1 ##
2 ## This file is part of the libsigrokdecode project.
3 ##
4 ## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
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 # See the https://www.pjon.org/ PJON project page and especially the
21 # https://www.pjon.org/PJDL-specification-v4.1.php PJDL v4.1 spec for
22 # the "Padded Jittering Data Link" single wire serial data link layer.
23
24 # TODO
25 # - Improve (fix, and extend) carrier sense support. Detection of the
26 #   idle/busy connection state is incomplete and fragile. Getting 'IDLE'
27 #   operational in the PJDL decoder would greatly help the PJON decoder
28 #   to flush ACK details before the start of new frames.
29 # - Check the correctness of timing assumptions. This implementation has
30 #   support for tolerances, which the spec does not discuss. Though real
31 #   world traffic was found to not decode at all with strict spec values
32 #   and without tolerances, while communication peers were able to talk
33 #   to each other. This needs more attention.
34 # - Check robustness when input data contains glitches. The spec does
35 #   not discuss how to handle these. Data bit sampling happens to work
36 #   because their value is taken at the center of the bit time. But
37 #   pad bits suffer badly from glitches, which breaks frame inspection
38 #   as well.
39 # - Cleanup the decoder implementation in general terms. Some details
40 #   have become obsolete ("edges", "pads"), and/or are covered by other
41 #   code paths.
42 # - Implement more data link decoders which can feed their output into
43 #   the PJON protocol decoder. Candidates are: PJDLR, PJDLS, TSDL.
44 # - Determine whether or not the data link layer should interpret any
45 #   frame content. From my perspective it should not, and needs not in
46 #   the strict sense. Possible gains would be getting the (expected!)
47 #   packet length, or whether a synchronous response is requested. But
48 #   this would duplicate knowledge which should remain internal to the
49 #   PJON decoder. And this link layer decoder neither shall assume that
50 #   the input data would be correct, or complete. Instead the decoder
51 #   shall remain usable on captures which demonstrate faults, and be
52 #   helpful in pointing them out. The design goal is to extract the
53 #   maximum of information possible, and pass it on as transparently
54 #   as possible.
55
56 import sigrokdecode as srd
57 from common.srdhelper import bitpack
58 from math import ceil, floor
59
60 '''
61 OUTPUT_PYTHON format for stacked decoders:
62
63 General packet format:
64 [<ptype>, <pdata>]
65
66 This is the list of <ptype>s and their respective <pdata> values:
67
68 Carrier sense:
69 - 'IDLE': <pdata> is the pin level (always 0).
70 - 'BUSY': <pdata> is always True.
71
72 Raw bit slots:
73 - 'PAD_BIT': <pdata> is the pin level (always 1).
74 - 'DATA_BIT': <pdata> is the pin level (0, or 1).
75 - 'SHORT_BIT': <pdata> is the pin level (always 1).
76 - 'SYNC_LOSS': <pdata> is an arbitrary text (internal use only).
77
78 Date bytes and frames:
79 - 'SYNC_PAD': <pdata> is True. Spans the high pad bit as well as the
80   low data bit.
81 - 'DATA_BYTE': <pdata> is the byte value (0..255).
82 - 'FRAME_INIT': <pdata> is True. Spans three sync pads.
83 - 'FRAME_DATA': <pdata> is the sequence of bytes in the frame. Non-data
84   phases in the frame get represented by strings instead of numbers
85   ('INIT', 'SYNC', 'SHORT', 'WAIT'). Frames can be incomplete, depending
86   on the decoder's input data.
87 - 'SYNC_RESP_WAIT': <pdata> is always True.
88
89 Notice that this link layer decoder is not aware of frame content. Will
90 neither check packet length, nor variable width fields, nor verify the
91 presence of requested synchronous responses. Cannot tell the sequence of
92 frame bytes then ACK bytes (without wait phase) from just frame bytes.
93 An upper layer protocol decoder will interpret content, the link layer
94 decoder remains as transparent as possible, and will neither assume
95 correct nor complete input data.
96 '''
97
98 # Carrier sense, and synchronization loss implementation is currently
99 # incomplete, and results in too many too short annotations, some of
100 # them spurious and confusing. TODO Improve the implementation.
101 _with_ann_carrier = False
102 _with_ann_sync_loss = False
103
104 PIN_DATA, = range(1)
105 ANN_CARRIER_BUSY, ANN_CARRIER_IDLE, \
106 ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA, ANN_SYNC_LOSS, \
107 ANN_DATA_BYTE, \
108 ANN_FRAME_INIT, ANN_FRAME_BYTES, ANN_FRAME_WAIT, \
109     = range(11)
110
111 class SamplerateError(Exception):
112     pass
113
114 class Decoder(srd.Decoder):
115     api_version = 3
116     id = 'pjdl'
117     name = 'PJDL'
118     longname = 'Padded Jittering Data Link'
119     desc = 'PJDL, a single wire serial link layer for PJON.'
120     license = 'gplv2+'
121     inputs = ['logic']
122     outputs = ['pjon_link']
123     tags = ['Embedded']
124     channels = (
125         {'id': 'data' , 'name': 'DATA', 'desc': 'Single wire data'},
126     )
127     options = (
128         {'id': 'mode', 'desc': 'Communication mode',
129             'default': 1, 'values': (1, 2, 3, 4)},
130         {'id': 'idle_add_us', 'desc': 'Added idle time (us)', 'default': 4},
131     )
132     annotations = (
133         ('cs_busy', 'Carrier busy'),
134         ('cs_idle', 'Carrier idle'),
135         ('bit_pad', 'Pad bit'),
136         ('bit_low', 'Low bit'),
137         ('bit_data', 'Data bit'),
138         ('bit_short', 'Short data'),
139         ('sync_loss', 'Sync loss'),
140         ('byte', 'Data byte'),
141         ('frame_init', 'Frame init'),
142         ('frame_bytes', 'Frame bytes'),
143         ('frame_wait', 'Frame wait'),
144     )
145     annotation_rows = (
146         ('carriers', 'Carriers', (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE,)),
147         ('bits', 'Bits', (ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA,)),
148         ('bytes', 'Bytes', (ANN_FRAME_INIT, ANN_DATA_BYTE, ANN_FRAME_WAIT,)),
149         ('frames', 'Frames', (ANN_FRAME_BYTES,)),
150         ('warns', 'Warnings', (ANN_SYNC_LOSS,)),
151     )
152
153     # Communication modes' data bit and pad bit duration (in us), and
154     # tolerances in percent and absolute (us).
155     mode_times = {
156         1: (44, 116),
157         2: (40, 92),
158         3: (28, 88),
159         4: (26, 60),
160     }
161     time_tol_perc = 10
162     time_tol_abs = 1.5
163
164     def __init__(self):
165         self.reset()
166
167     def reset(self):
168         self.reset_state()
169
170     def reset_state(self):
171         self.carrier_want_idle = True
172         self.carrier_is_busy = False
173         self.carrier_is_idle = False
174         self.carrier_idle_ss = None
175         self.carrier_busy_ss = None
176         self.syncpad_fall_ss = None
177
178         self.edges = None
179         self.symbols = None
180         self.sync_pads = None
181         self.data_bits = None
182         self.frame_bytes = None
183         self.short_bits = None
184
185     def start(self):
186         self.out_ann = self.register(srd.OUTPUT_ANN)
187         self.out_python = self.register(srd.OUTPUT_PYTHON)
188
189     def metadata(self, key, value):
190         if key == srd.SRD_CONF_SAMPLERATE:
191             self.samplerate = value
192             self.span_prepare()
193
194     def putg(self, ss, es, data):
195         cls = data[0]
196         if not _with_ann_carrier and cls in (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE):
197             return
198         if not _with_ann_sync_loss and cls in (ANN_SYNC_LOSS,):
199             return
200         self.put(ss, es, self.out_ann, data)
201
202     def putpy(self, ss, es, ptype, pdata):
203         self.put(ss, es, self.out_python, [ptype, pdata])
204
205     def symbols_clear(self):
206         syms = self.symbols or []
207         self.symbols = []
208         return syms
209
210     def symbols_append(self, ss, es, symbol, data = None):
211         if self.symbols is None:
212             self.symbols = []
213         item = (ss, es, symbol, data)
214         self.symbols.append(item)
215
216     def symbols_get_last(self, count = None):
217         if not self.symbols:
218             return None
219         if count is None:
220             count = 1
221         if len(self.symbols) < count:
222             return None
223         items = self.symbols[-count:]
224         if count == 1:
225             items = items[0]
226         return items
227
228     def symbols_update_last(self, ss, es, symbol, data = None):
229         if not self.symbols:
230             return None
231         item = list(self.symbols[-1])
232         if ss is not None:
233             item[0] = ss
234         if es is not None:
235             item[1] = es
236         if symbol is not None:
237             item[2] = symbol
238         if data is not None:
239             item[3] = data
240         self.symbols[-1] = tuple(item)
241
242     def symbols_has_prev(self, want_items):
243         if not isinstance(want_items, (list, tuple,)):
244             want_items = [want_items]
245         if self.symbols is None:
246             return False
247         if len(self.symbols) < len(want_items):
248             return False
249         sym_off = len(self.symbols) - len(want_items)
250         for idx, want_item in enumerate(want_items):
251             if self.symbols[sym_off + idx][2] != want_item:
252                 return False
253         return True
254
255     def symbols_collapse(self, count, symbol, data = None, squeeze = None):
256         if self.symbols is None:
257             return None
258         if len(self.symbols) < count:
259             return None
260         self.symbols, last_data = self.symbols[:-count], self.symbols[-count:]
261         while squeeze and self.symbols and self.symbols[-1][2] == squeeze:
262             last_data.insert(0, self.symbols.pop())
263         ss, es = last_data[0][0], last_data[-1][1]
264         if data is None:
265             data = last_data
266         item = (ss, es, symbol, data)
267         self.symbols.append(item)
268
269     def frame_flush(self):
270         syms = self.symbols_clear()
271         while syms and syms[0][2] == 'IDLE':
272             syms.pop(0)
273         while syms and syms[-1][2] == 'IDLE':
274             syms.pop(-1)
275         if not syms:
276             return
277         text = []
278         data = []
279         for sym in syms:
280             if sym[2] == 'FRAME_INIT':
281                 text.append('INIT')
282                 data.append('INIT')
283                 continue
284             if sym[2] == 'SYNC_PAD':
285                 if not text or text[-1] != 'SYNC':
286                     text.append('SYNC')
287                     data.append('SYNC')
288                 continue
289             if sym[2] == 'DATA_BYTE':
290                 b = [bit[3] for bit in sym[3] if bit[2] == 'DATA_BIT']
291                 b = bitpack(b)
292                 text.append('{:02x}'.format(b))
293                 data.append(b)
294                 continue
295             if sym[2] == 'SHORT_BIT':
296                 if not text or text[-1] != 'SHORT':
297                     text.append('SHORT')
298                     data.append('SHORT')
299                 continue
300             if sym[2] == 'WAIT_ACK':
301                 text.append('WAIT')
302                 data.append('WAIT')
303                 continue
304         text = ' '.join(text)
305         ss, es = syms[0][0], syms[-1][1]
306         self.putg(ss, es, [ANN_FRAME_BYTES, [text]])
307         self.putpy(ss, es, 'FRAME_DATA', data)
308
309     def carrier_flush(self):
310         # Force annotations if BUSY started, or if IDLE tracking started
311         # and kept running for long enough. This will be called before
312         # internal state reset, so we won't manipulate internal variables,
313         # and can afford to emit annotations which haven't met their
314         # proper end condition yet.
315         if self.carrier_busy_ss:
316             ss, es = self.carrier_busy_ss, self.samplenum
317             self.putg(ss, es, [ANN_CARRIER_BUSY, ['BUSY']])
318         if self.carrier_idle_ss:
319             ss, es = self.carrier_idle_ss, self.samplenum
320             ss += int(self.idle_width)
321             if ss < es:
322                 self.putg(ss, es, [ANN_CARRIER_IDLE, ['IDLE']])
323
324     def carrier_set_idle(self, on, ss, es):
325         if on:
326             # IDLE starts here, or continues.
327             if not self.carrier_idle_ss:
328                 self.carrier_idle_ss = int(ss)
329             if not self.symbols_has_prev('IDLE'):
330                 self.symbols_append(ss, ss, 'IDLE')
331             self.symbols_update_last(None, es, None)
332             # HACK We have seen an IDLE condition. This implementation
333             # loses details which are used to track IDLE, but it's more
334             # important to start accumulation of a new frame here.
335             self.frame_flush()
336             self.reset_state()
337             # end of HACK
338             self.carrier_is_idle = True
339             self.carrier_want_idle = False
340             return
341         # IDLE ends here.
342         if self.symbols_has_prev('IDLE'):
343             self.symbols_update_last(None, es, None)
344         self.carrier_flush()
345         self.carrier_is_idle = False
346         self.carrier_idle_ss = None
347
348     def carrier_set_busy(self, on, snum):
349         self.carrier_is_busy = on
350         if on:
351             self.carrier_is_idle = None
352             if not self.carrier_busy_ss:
353                 self.carrier_busy_ss = snum
354             return
355         if self.carrier_busy_ss:
356             self.putg(self.carrier_busy_ss, snum, [ANN_CARRIER_BUSY, ['BUSY']])
357         self.carrier_busy_ss = None
358         self.carrier_is_busy = False
359
360     def carrier_check(self, level, snum):
361
362         # When HIGH is seen, immediately end IDLE and switch to BUSY.
363         if level:
364             self.carrier_set_idle(False, snum, snum)
365             self.carrier_set_busy(True, snum)
366             return
367
368         # LOW is seen. Start tracking an IDLE period if not done yet.
369         if not self.carrier_idle_ss:
370             self.carrier_idle_ss = int(snum)
371
372         # End BUSY when LOW persisted for an exact data byte's length.
373         # Start IDLE when LOW persisted for a data byte's length plus
374         # the user specified additional period.
375         span = snum - self.carrier_idle_ss
376         if span >= self.byte_width:
377             self.carrier_set_busy(False, snum)
378         if span >= self.idle_width:
379             self.carrier_set_idle(True, self.carrier_idle_ss + self.idle_width, snum)
380
381     def span_prepare(self):
382         '''Prepare calculation of durations in terms of samples.'''
383
384         # Determine samples per microsecond, and sample counts for
385         # several bit types, and sample count for a data byte's
386         # length, including optional extra time. Determine ranges
387         # for bit widths (tolerance margin).
388
389         # Get times in microseconds.
390         self.data_width, self.pad_width = self.mode_times[self.options['mode']]
391         self.byte_width = self.pad_width + 9 * self.data_width
392         self.add_idle_width = self.options['idle_add_us']
393         self.idle_width = self.byte_width + self.add_idle_width
394
395         # Derive ranges (add tolerance) and scale to sample counts.
396         self.usec_width = self.samplerate / 1e6
397         self.hold_high_width = 9 * self.time_tol_abs * self.usec_width
398
399         def _get_range(width):
400             reladd = self.time_tol_perc / 100
401             absadd = self.time_tol_abs
402             lower = min(width * (1 - reladd), width - absadd)
403             upper = max(width * (1 + reladd), width + absadd)
404             lower = floor(lower * self.usec_width)
405             upper = ceil(upper * self.usec_width)
406             return (lower, upper + 1)
407
408         self.data_bit_1_range = _get_range(self.data_width * 1)
409         self.data_bit_2_range = _get_range(self.data_width * 2)
410         self.data_bit_3_range = _get_range(self.data_width * 3)
411         self.data_bit_4_range = _get_range(self.data_width * 4)
412         self.short_data_range = _get_range(self.data_width / 4)
413         self.pad_bit_range = _get_range(self.pad_width)
414
415         self.data_width *= self.usec_width
416         self.pad_width *= self.usec_width
417         self.byte_width *= self.usec_width
418         self.idle_width *= self.usec_width
419
420         self.lookahead_width = int(4 * self.data_width)
421
422     def span_snum_to_us(self, count):
423         return count / self.usec_width
424
425     def span_is_pad(self, span):
426         return span in range(*self.pad_bit_range)
427
428     def span_is_data(self, span):
429         if span in range(*self.data_bit_1_range):
430             return 1
431         if span in range(*self.data_bit_2_range):
432             return 2
433         if span in range(*self.data_bit_3_range):
434             return 3
435         if span in range(*self.data_bit_4_range):
436             return 4
437         return False
438
439     def span_is_short(self, span):
440         return span in range(*self.short_data_range)
441
442     def wait_until(self, want):
443         '''Wait until a given location, but keep sensing carrier.'''
444
445         # Implementor's note: Avoids skip values below 1. This version
446         # "may overshoot" by one sample. Which should be acceptable for
447         # this specific use case (can put the sample point of a bit time
448         # out of the center by some 4% under worst case conditions).
449
450         want = int(want)
451         while True:
452             diff = max(want - self.samplenum, 1)
453             pins = self.wait([{PIN_DATA: 'e'}, {'skip': diff}])
454             self.carrier_check(pins[PIN_DATA], self.samplenum)
455             if self.samplenum >= want:
456                 return pins
457         # UNREACH
458
459     def decode(self):
460         if not self.samplerate or self.samplerate < 1e6:
461             raise SamplerateError('Need a samplerate of at least 1MSa/s')
462
463         # As a special case the first low period in the input capture is
464         # saught regardless of whether we can see its falling edge. This
465         # approach is also used to recover after synchronization was lost.
466         #
467         # The important condition here in the main loop is: Get the next
468         # edge's position, but time out after a maximum period of four
469         # data bits. This allows for the detection of SYNC pulses, also
470         # responds "soon enough" to DATA bits where edges can be few
471         # within a data byte. Also avoids excessive waits for unexpected
472         # communication errors.
473         #
474         # DATA bits within a byte are taken at fixed intervals relative
475         # to the SYNC-PAD's falling edge. It's essential to check the
476         # carrier at every edge, also during DATA bit sampling. Simple
477         # skips to the desired sample point could break that feature.
478         while True:
479
480             # Help kick-start the IDLE condition detection after
481             # decoder state reset.
482             if not self.edges:
483                 curr_level, = self.wait({PIN_DATA: 'l'})
484                 self.carrier_check(curr_level, self.samplenum)
485                 self.edges = [self.samplenum]
486                 continue
487
488             # Advance to the next edge, or over a medium span without an
489             # edge. Prepare to classify the distance to derive bit types
490             # from these details.
491             last_snum = self.samplenum
492             curr_level, = self.wait([{PIN_DATA: 'e'}, {'skip': self.lookahead_width}])
493             self.carrier_check(curr_level, self.samplenum)
494             bit_level = curr_level
495             edge_seen = self.matched[0]
496             if edge_seen:
497                 bit_level = 1 - bit_level
498             if not self.edges:
499                 self.edges = [self.samplenum]
500                 continue
501             self.edges.append(self.samplenum)
502             curr_snum = self.samplenum
503
504             # Check bit width (can also be multiple data bits).
505             span = self.edges[-1] - self.edges[-2]
506             is_pad = bit_level and self.span_is_pad(span)
507             is_data = self.span_is_data(span)
508             is_short = bit_level and self.span_is_short(span)
509
510             if is_pad:
511                 ss, es = last_snum, curr_snum
512                 texts = ['PAD', '{:d}'.format(bit_level)]
513                 self.putg(ss, es, [ANN_PAD_BIT, texts])
514                 self.symbols_append(ss, es, 'PAD_BIT', bit_level)
515                 ss, es = self.symbols_get_last()[:2]
516                 self.putpy(ss, es, 'PAD_BIT', bit_level)
517                 continue
518
519             if is_short:
520                 ss, es = last_snum, curr_snum
521                 texts = ['SHORT', '{:d}'.format(bit_level)]
522                 self.putg(ss, es, [ANN_SHORT_DATA, texts])
523                 self.symbols_append(ss, es, 'SHORT_BIT', bit_level)
524                 ss, es = self.symbols_get_last()[:2]
525                 self.putpy(ss, es, 'SHORT_BIT', bit_level)
526                 continue
527
528             # Force IDLE period check when the decoder seeks to sync
529             # to the input data stream.
530             if not bit_level and not self.symbols and self.carrier_want_idle:
531                 continue
532
533             # Accept arbitrary length LOW phases after DATA bytes(!) or
534             # SHORT pulses, but not within a DATA byte or SYNC-PAD etc.
535             # This covers the late start of the next SYNC-PAD (byte of
536             # a frame, or ACK byte after a frame, or the start of the
537             # next frame).
538             if not bit_level:
539                 if self.symbols_has_prev('DATA_BYTE'):
540                     continue
541                 if self.symbols_has_prev('SHORT_BIT'):
542                     continue
543                 if self.symbols_has_prev('WAIT_ACK'):
544                     continue
545
546             # Get (consume!) the LOW DATA bit after a PAD.
547             took_low = False
548             if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'):
549                 took_low = True
550                 is_data -= 1
551                 next_snum = int(last_snum + self.data_width)
552                 ss, es = last_snum, next_snum
553                 texts = ['ZERO', '{:d}'.format(bit_level)]
554                 self.putg(ss, es, [ANN_LOW_BIT, texts])
555                 self.symbols_append(ss, es, 'ZERO_BIT', bit_level)
556                 ss, es = self.symbols_get_last()[:2]
557                 self.putpy(ss, es, 'DATA_BIT', bit_level)
558                 self.data_fall_time = last_snum
559                 last_snum = next_snum
560             # Turn the combination of PAD and LOW DATA into SYNC-PAD.
561             # Start data bit accumulation after a SYNC-PAD was seen.
562             sync_pad_seq = ['PAD_BIT', 'ZERO_BIT']
563             if self.symbols_has_prev(sync_pad_seq):
564                 self.symbols_collapse(len(sync_pad_seq), 'SYNC_PAD')
565                 ss, es = self.symbols_get_last()[:2]
566                 self.putpy(ss, es, 'SYNC_PAD', True)
567                 self.data_bits = []
568             # Turn three subsequent SYNC-PAD into FRAME-INIT. Start the
569             # accumulation of frame bytes when FRAME-INIT was seen.
570             frame_init_seq = 3 * ['SYNC_PAD']
571             if self.symbols_has_prev(frame_init_seq):
572                 self.symbols_collapse(len(frame_init_seq), 'FRAME_INIT')
573                 # Force a flush of the previous frame after we have
574                 # reliably detected the start of another one. This is a
575                 # workaround for this decoder's inability to detect the
576                 # end of a frame after an ACK was seen or byte counts
577                 # have been reached. We cannot assume perfect input,
578                 # thus we leave all interpretation of frame content to
579                 # upper layers. Do keep the recently queued FRAME_INIT
580                 # symbol across the flush operation.
581                 if len(self.symbols) > 1:
582                     keep = self.symbols.pop(-1)
583                     self.frame_flush()
584                     self.symbols.clear()
585                     self.symbols.append(keep)
586                 ss, es = self.symbols_get_last()[:2]
587                 texts = ['FRAME INIT', 'INIT', 'I']
588                 self.putg(ss, es, [ANN_FRAME_INIT, texts])
589                 self.putpy(ss, es, 'FRAME_INIT', True)
590                 self.frame_bytes = []
591             # Collapse SYNC-PAD after SHORT+ into a WAIT-ACK. Include
592             # all leading SHORT bits in the WAIT as well.
593             wait_ack_seq = ['SHORT_BIT', 'SYNC_PAD']
594             if self.symbols_has_prev(wait_ack_seq):
595                 self.symbols_collapse(len(wait_ack_seq), 'WAIT_ACK',
596                     squeeze = 'SHORT_BIT')
597                 ss, es = self.symbols_get_last()[:2]
598                 texts = ['WAIT for sync response', 'WAIT response', 'WAIT', 'W']
599                 self.putg(ss, es, [ANN_FRAME_WAIT, texts])
600                 self.putpy(ss, es, 'SYNC_RESP_WAIT', True)
601             if took_low and not is_data:
602                 # Start at the very next edge if we just consumed a LOW
603                 # after a PAD bit, and the DATA bit count is exhausted.
604                 # This improves robustness, deals with inaccurate edge
605                 # positions. (Motivated by real world captures, the spec
606                 # would not discuss bit time tolerances.)
607                 continue
608
609             # When we get here, the only remaining (the only supported)
610             # activity is the collection of a data byte's DATA bits.
611             # These are not taken by the main loop's "edge search, with
612             # a timeout" approach, which is "too tolerant". Instead all
613             # DATA bits get sampled at a fixed interval and relative to
614             # the SYNC-PAD's falling edge. We expect to have seen the
615             # data byte' SYNC-PAD before. If we haven't, the decoder is
616             # not yet synchronized to the input data.
617             if not is_data:
618                 fast_cont = edge_seen and curr_level
619                 ss, es = last_snum, curr_snum
620                 texts = ['failed pulse length check', 'pulse length', 'length']
621                 self.putg(ss, es, [ANN_SYNC_LOSS, texts])
622                 self.frame_flush()
623                 self.carrier_flush()
624                 self.reset_state()
625                 if fast_cont:
626                     self.edges = [self.samplenum]
627                 continue
628             if not self.symbols_has_prev('SYNC_PAD'):
629                 # Fast reponse to the specific combination of: no-sync,
630                 # edge seen, and current high level. In this case we
631                 # can reset internal state, but also can continue the
632                 # interpretation right after the most recently seen
633                 # rising edge, which could start the next PAD time.
634                 # Otherwise continue slow interpretation after reset.
635                 fast_cont = edge_seen and curr_level
636                 self.frame_flush()
637                 self.carrier_flush()
638                 self.reset_state()
639                 if fast_cont:
640                     self.edges = [self.samplenum]
641                 continue
642
643             # The main loop's "edge search with period timeout" approach
644             # can have provided up to three more DATA bits after the LOW
645             # bit of the SYNC-PAD. Consume them immediately in that case,
646             # otherwise .wait() for their sample point. Stick with float
647             # values for bit sample points and bit time boundaries for
648             # improved accuracy, only round late to integers when needed.
649             bit_field = []
650             bit_ss = self.data_fall_time + self.data_width
651             for bit_idx in range(8):
652                 bit_es = bit_ss + self.data_width
653                 bit_snum = (bit_es + bit_ss) / 2
654                 if bit_snum > self.samplenum:
655                     bit_level, = self.wait_until(bit_snum)
656                 ss, es = ceil(bit_ss), floor(bit_es)
657                 texts = ['{:d}'.format(bit_level)]
658                 self.putg(ss, es, [ANN_DATA_BIT, texts])
659                 self.symbols_append(ss, es, 'DATA_BIT', bit_level)
660                 ss, es = self.symbols_get_last()[:2]
661                 self.putpy(ss, es, 'DATA_BIT', bit_level)
662                 bit_field.append(bit_level)
663                 if self.data_bits is not None:
664                     self.data_bits.append(bit_level)
665                 bit_ss = bit_es
666             end_snum = bit_es
667             curr_level, = self.wait_until(end_snum)
668             curr_snum = self.samplenum
669
670             # We are at the exact _calculated_ boundary of the last DATA
671             # bit time. Improve robustness for those situations where
672             # the transmitter's and the sender's timings differ within a
673             # margin, and the transmitter may hold the last DATA bit's
674             # HIGH level for a little longer.
675             if curr_level:
676                 hold = self.hold_high_width
677                 curr_level, = self.wait([{PIN_DATA: 'l'}, {'skip': int(hold)}])
678                 self.carrier_check(curr_level, self.samplenum)
679                 curr_snum = self.samplenum
680
681             # Get the byte value from the bits (when available).
682             # TODO Has the local 'bit_field' become obsolete, or should
683             # self.data_bits go away?
684             data_byte = bitpack(bit_field)
685             if self.data_bits is not None:
686                 data_byte = bitpack(self.data_bits)
687                 self.data_bits.clear()
688                 if self.frame_bytes is not None:
689                     self.frame_bytes.append(data_byte)
690
691             # Turn a sequence of a SYNC-PAD and eight DATA bits into a
692             # DATA-BYTE symbol.
693             byte_seq = ['SYNC_PAD'] + 8 * ['DATA_BIT']
694             if self.symbols_has_prev(byte_seq):
695                 self.symbols_collapse(len(byte_seq), 'DATA_BYTE')
696                 ss, es = self.symbols_get_last()[:2]
697                 texts = ['{:02x}'.format(data_byte)]
698                 self.putg(ss, es, [ANN_DATA_BYTE, texts])
699                 self.putpy(ss, es, 'DATA_BYTE', data_byte)
700
701             # Optionally terminate the accumulation of a frame when a
702             # WAIT-ACK period was followed by a DATA-BYTE? This could
703             # flush the current packet before the next FRAME-INIT or
704             # IDLE are seen, and increases usability for short input
705             # data (aggressive trimming). It won't help when WAIT is
706             # not seen, though.
707             sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE']
708             if self.symbols_has_prev(sync_resp_seq):
709                 self.frame_flush()