]> sigrok.org Git - libsigrokdecode.git/blob - decoders/pjdl/pd.py
pjdl: prepare for "stretched" timings
[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         mode_times = self.mode_times[self.options['mode']]
391         mode_times = [t * 1.0 for t in mode_times]
392         self.data_width, self.pad_width = mode_times
393         self.byte_width = self.pad_width + 9 * self.data_width
394         self.add_idle_width = self.options['idle_add_us']
395         self.idle_width = self.byte_width + self.add_idle_width
396
397         # Derive ranges (add tolerance) and scale to sample counts.
398         self.usec_width = self.samplerate / 1e6
399         self.hold_high_width = 9 * self.time_tol_abs * self.usec_width
400
401         def _get_range(width):
402             reladd = self.time_tol_perc / 100
403             absadd = self.time_tol_abs
404             lower = min(width * (1 - reladd), width - absadd)
405             upper = max(width * (1 + reladd), width + absadd)
406             lower = floor(lower * self.usec_width)
407             upper = ceil(upper * self.usec_width)
408             return (lower, upper + 1)
409
410         self.data_bit_1_range = _get_range(self.data_width * 1)
411         self.data_bit_2_range = _get_range(self.data_width * 2)
412         self.data_bit_3_range = _get_range(self.data_width * 3)
413         self.data_bit_4_range = _get_range(self.data_width * 4)
414         self.short_data_range = _get_range(self.data_width / 4)
415         self.pad_bit_range = _get_range(self.pad_width)
416
417         self.data_width *= self.usec_width
418         self.pad_width *= self.usec_width
419         self.byte_width *= self.usec_width
420         self.idle_width *= self.usec_width
421
422         self.lookahead_width = int(4 * self.data_width)
423
424     def span_snum_to_us(self, count):
425         return count / self.usec_width
426
427     def span_is_pad(self, span):
428         return span in range(*self.pad_bit_range)
429
430     def span_is_data(self, span):
431         if span in range(*self.data_bit_1_range):
432             return 1
433         if span in range(*self.data_bit_2_range):
434             return 2
435         if span in range(*self.data_bit_3_range):
436             return 3
437         if span in range(*self.data_bit_4_range):
438             return 4
439         return False
440
441     def span_is_short(self, span):
442         return span in range(*self.short_data_range)
443
444     def wait_until(self, want):
445         '''Wait until a given location, but keep sensing carrier.'''
446
447         # Implementor's note: Avoids skip values below 1. This version
448         # "may overshoot" by one sample. Which should be acceptable for
449         # this specific use case (can put the sample point of a bit time
450         # out of the center by some 4% under worst case conditions).
451
452         want = int(want)
453         while True:
454             diff = max(want - self.samplenum, 1)
455             pins = self.wait([{PIN_DATA: 'e'}, {'skip': diff}])
456             self.carrier_check(pins[PIN_DATA], self.samplenum)
457             if self.samplenum >= want:
458                 return pins
459         # UNREACH
460
461     def decode(self):
462         if not self.samplerate or self.samplerate < 1e6:
463             raise SamplerateError('Need a samplerate of at least 1MSa/s')
464
465         # As a special case the first low period in the input capture is
466         # saught regardless of whether we can see its falling edge. This
467         # approach is also used to recover after synchronization was lost.
468         #
469         # The important condition here in the main loop is: Get the next
470         # edge's position, but time out after a maximum period of four
471         # data bits. This allows for the detection of SYNC pulses, also
472         # responds "soon enough" to DATA bits where edges can be few
473         # within a data byte. Also avoids excessive waits for unexpected
474         # communication errors.
475         #
476         # DATA bits within a byte are taken at fixed intervals relative
477         # to the SYNC-PAD's falling edge. It's essential to check the
478         # carrier at every edge, also during DATA bit sampling. Simple
479         # skips to the desired sample point could break that feature.
480         while True:
481
482             # Help kick-start the IDLE condition detection after
483             # decoder state reset.
484             if not self.edges:
485                 curr_level, = self.wait({PIN_DATA: 'l'})
486                 self.carrier_check(curr_level, self.samplenum)
487                 self.edges = [self.samplenum]
488                 continue
489
490             # Advance to the next edge, or over a medium span without an
491             # edge. Prepare to classify the distance to derive bit types
492             # from these details.
493             last_snum = self.samplenum
494             curr_level, = self.wait([{PIN_DATA: 'e'}, {'skip': self.lookahead_width}])
495             self.carrier_check(curr_level, self.samplenum)
496             bit_level = curr_level
497             edge_seen = self.matched[0]
498             if edge_seen:
499                 bit_level = 1 - bit_level
500             if not self.edges:
501                 self.edges = [self.samplenum]
502                 continue
503             self.edges.append(self.samplenum)
504             curr_snum = self.samplenum
505
506             # Check bit width (can also be multiple data bits).
507             span = self.edges[-1] - self.edges[-2]
508             is_pad = bit_level and self.span_is_pad(span)
509             is_data = self.span_is_data(span)
510             is_short = bit_level and self.span_is_short(span)
511
512             if is_pad:
513                 ss, es = last_snum, curr_snum
514                 texts = ['PAD', '{:d}'.format(bit_level)]
515                 self.putg(ss, es, [ANN_PAD_BIT, texts])
516                 self.symbols_append(ss, es, 'PAD_BIT', bit_level)
517                 ss, es = self.symbols_get_last()[:2]
518                 self.putpy(ss, es, 'PAD_BIT', bit_level)
519                 continue
520
521             if is_short:
522                 ss, es = last_snum, curr_snum
523                 texts = ['SHORT', '{:d}'.format(bit_level)]
524                 self.putg(ss, es, [ANN_SHORT_DATA, texts])
525                 self.symbols_append(ss, es, 'SHORT_BIT', bit_level)
526                 ss, es = self.symbols_get_last()[:2]
527                 self.putpy(ss, es, 'SHORT_BIT', bit_level)
528                 continue
529
530             # Force IDLE period check when the decoder seeks to sync
531             # to the input data stream.
532             if not bit_level and not self.symbols and self.carrier_want_idle:
533                 continue
534
535             # Accept arbitrary length LOW phases after DATA bytes(!) or
536             # SHORT pulses, but not within a DATA byte or SYNC-PAD etc.
537             # This covers the late start of the next SYNC-PAD (byte of
538             # a frame, or ACK byte after a frame, or the start of the
539             # next frame).
540             if not bit_level:
541                 if self.symbols_has_prev('DATA_BYTE'):
542                     continue
543                 if self.symbols_has_prev('SHORT_BIT'):
544                     continue
545                 if self.symbols_has_prev('WAIT_ACK'):
546                     continue
547
548             # Get (consume!) the LOW DATA bit after a PAD.
549             took_low = False
550             if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'):
551                 took_low = True
552                 is_data -= 1
553                 next_snum = int(last_snum + self.data_width)
554                 ss, es = last_snum, next_snum
555                 texts = ['ZERO', '{:d}'.format(bit_level)]
556                 self.putg(ss, es, [ANN_LOW_BIT, texts])
557                 self.symbols_append(ss, es, 'ZERO_BIT', bit_level)
558                 ss, es = self.symbols_get_last()[:2]
559                 self.putpy(ss, es, 'DATA_BIT', bit_level)
560                 self.data_fall_time = last_snum
561                 last_snum = next_snum
562             # Turn the combination of PAD and LOW DATA into SYNC-PAD.
563             # Start data bit accumulation after a SYNC-PAD was seen.
564             sync_pad_seq = ['PAD_BIT', 'ZERO_BIT']
565             if self.symbols_has_prev(sync_pad_seq):
566                 self.symbols_collapse(len(sync_pad_seq), 'SYNC_PAD')
567                 ss, es = self.symbols_get_last()[:2]
568                 self.putpy(ss, es, 'SYNC_PAD', True)
569                 self.data_bits = []
570             # Turn three subsequent SYNC-PAD into FRAME-INIT. Start the
571             # accumulation of frame bytes when FRAME-INIT was seen.
572             frame_init_seq = 3 * ['SYNC_PAD']
573             if self.symbols_has_prev(frame_init_seq):
574                 self.symbols_collapse(len(frame_init_seq), 'FRAME_INIT')
575                 # Force a flush of the previous frame after we have
576                 # reliably detected the start of another one. This is a
577                 # workaround for this decoder's inability to detect the
578                 # end of a frame after an ACK was seen or byte counts
579                 # have been reached. We cannot assume perfect input,
580                 # thus we leave all interpretation of frame content to
581                 # upper layers. Do keep the recently queued FRAME_INIT
582                 # symbol across the flush operation.
583                 if len(self.symbols) > 1:
584                     keep = self.symbols.pop(-1)
585                     self.frame_flush()
586                     self.symbols.clear()
587                     self.symbols.append(keep)
588                 ss, es = self.symbols_get_last()[:2]
589                 texts = ['FRAME INIT', 'INIT', 'I']
590                 self.putg(ss, es, [ANN_FRAME_INIT, texts])
591                 self.putpy(ss, es, 'FRAME_INIT', True)
592                 self.frame_bytes = []
593             # Collapse SYNC-PAD after SHORT+ into a WAIT-ACK. Include
594             # all leading SHORT bits in the WAIT as well.
595             wait_ack_seq = ['SHORT_BIT', 'SYNC_PAD']
596             if self.symbols_has_prev(wait_ack_seq):
597                 self.symbols_collapse(len(wait_ack_seq), 'WAIT_ACK',
598                     squeeze = 'SHORT_BIT')
599                 ss, es = self.symbols_get_last()[:2]
600                 texts = ['WAIT for sync response', 'WAIT response', 'WAIT', 'W']
601                 self.putg(ss, es, [ANN_FRAME_WAIT, texts])
602                 self.putpy(ss, es, 'SYNC_RESP_WAIT', True)
603             if took_low and not is_data:
604                 # Start at the very next edge if we just consumed a LOW
605                 # after a PAD bit, and the DATA bit count is exhausted.
606                 # This improves robustness, deals with inaccurate edge
607                 # positions. (Motivated by real world captures, the spec
608                 # would not discuss bit time tolerances.)
609                 continue
610
611             # When we get here, the only remaining (the only supported)
612             # activity is the collection of a data byte's DATA bits.
613             # These are not taken by the main loop's "edge search, with
614             # a timeout" approach, which is "too tolerant". Instead all
615             # DATA bits get sampled at a fixed interval and relative to
616             # the SYNC-PAD's falling edge. We expect to have seen the
617             # data byte' SYNC-PAD before. If we haven't, the decoder is
618             # not yet synchronized to the input data.
619             if not is_data:
620                 fast_cont = edge_seen and curr_level
621                 ss, es = last_snum, curr_snum
622                 texts = ['failed pulse length check', 'pulse length', 'length']
623                 self.putg(ss, es, [ANN_SYNC_LOSS, texts])
624                 self.frame_flush()
625                 self.carrier_flush()
626                 self.reset_state()
627                 if fast_cont:
628                     self.edges = [self.samplenum]
629                 continue
630             if not self.symbols_has_prev('SYNC_PAD'):
631                 # Fast reponse to the specific combination of: no-sync,
632                 # edge seen, and current high level. In this case we
633                 # can reset internal state, but also can continue the
634                 # interpretation right after the most recently seen
635                 # rising edge, which could start the next PAD time.
636                 # Otherwise continue slow interpretation after reset.
637                 fast_cont = edge_seen and curr_level
638                 self.frame_flush()
639                 self.carrier_flush()
640                 self.reset_state()
641                 if fast_cont:
642                     self.edges = [self.samplenum]
643                 continue
644
645             # The main loop's "edge search with period timeout" approach
646             # can have provided up to three more DATA bits after the LOW
647             # bit of the SYNC-PAD. Consume them immediately in that case,
648             # otherwise .wait() for their sample point. Stick with float
649             # values for bit sample points and bit time boundaries for
650             # improved accuracy, only round late to integers when needed.
651             bit_field = []
652             bit_ss = self.data_fall_time + self.data_width
653             for bit_idx in range(8):
654                 bit_es = bit_ss + self.data_width
655                 bit_snum = (bit_es + bit_ss) / 2
656                 if bit_snum > self.samplenum:
657                     bit_level, = self.wait_until(bit_snum)
658                 ss, es = ceil(bit_ss), floor(bit_es)
659                 texts = ['{:d}'.format(bit_level)]
660                 self.putg(ss, es, [ANN_DATA_BIT, texts])
661                 self.symbols_append(ss, es, 'DATA_BIT', bit_level)
662                 ss, es = self.symbols_get_last()[:2]
663                 self.putpy(ss, es, 'DATA_BIT', bit_level)
664                 bit_field.append(bit_level)
665                 if self.data_bits is not None:
666                     self.data_bits.append(bit_level)
667                 bit_ss = bit_es
668             end_snum = bit_es
669             curr_level, = self.wait_until(end_snum)
670             curr_snum = self.samplenum
671
672             # We are at the exact _calculated_ boundary of the last DATA
673             # bit time. Improve robustness for those situations where
674             # the transmitter's and the sender's timings differ within a
675             # margin, and the transmitter may hold the last DATA bit's
676             # HIGH level for a little longer.
677             if curr_level:
678                 hold = self.hold_high_width
679                 curr_level, = self.wait([{PIN_DATA: 'l'}, {'skip': int(hold)}])
680                 self.carrier_check(curr_level, self.samplenum)
681                 curr_snum = self.samplenum
682
683             # Get the byte value from the bits (when available).
684             # TODO Has the local 'bit_field' become obsolete, or should
685             # self.data_bits go away?
686             data_byte = bitpack(bit_field)
687             if self.data_bits is not None:
688                 data_byte = bitpack(self.data_bits)
689                 self.data_bits.clear()
690                 if self.frame_bytes is not None:
691                     self.frame_bytes.append(data_byte)
692
693             # Turn a sequence of a SYNC-PAD and eight DATA bits into a
694             # DATA-BYTE symbol.
695             byte_seq = ['SYNC_PAD'] + 8 * ['DATA_BIT']
696             if self.symbols_has_prev(byte_seq):
697                 self.symbols_collapse(len(byte_seq), 'DATA_BYTE')
698                 ss, es = self.symbols_get_last()[:2]
699                 texts = ['{:02x}'.format(data_byte)]
700                 self.putg(ss, es, [ANN_DATA_BYTE, texts])
701                 self.putpy(ss, es, 'DATA_BYTE', data_byte)
702
703             # Optionally terminate the accumulation of a frame when a
704             # WAIT-ACK period was followed by a DATA-BYTE? This could
705             # flush the current packet before the next FRAME-INIT or
706             # IDLE are seen, and increases usability for short input
707             # data (aggressive trimming). It won't help when WAIT is
708             # not seen, though.
709             sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE']
710             if self.symbols_has_prev(sync_resp_seq):
711                 self.frame_flush()