2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
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.
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.
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/>.
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.
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
39 # - Cleanup the decoder implementation in general terms. Some details
40 # have become obsolete ("edges", "pads"), and/or are covered by other
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
56 import sigrokdecode as srd
57 from common.srdhelper import bitpack
58 from math import ceil, floor
61 OUTPUT_PYTHON format for stacked decoders:
63 General packet format:
66 This is the list of <ptype>s and their respective <pdata> values:
69 - 'IDLE': <pdata> is the pin level (always 0).
70 - 'BUSY': <pdata> is always True.
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).
78 Date bytes and frames:
79 - 'SYNC_PAD': <pdata> is True. Spans the high pad bit as well as the
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.
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.
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
105 ANN_CARRIER_BUSY, ANN_CARRIER_IDLE, \
106 ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA, ANN_SYNC_LOSS, \
108 ANN_FRAME_INIT, ANN_FRAME_BYTES, ANN_FRAME_WAIT, \
111 class SamplerateError(Exception):
114 class Decoder(srd.Decoder):
118 longname = 'Padded Jittering Data Link'
119 desc = 'PJDL, a single wire serial link layer for PJON.'
122 outputs = ['pjon_link']
123 tags = ['Embedded/industrial']
125 {'id': 'data' , 'name': 'DATA', 'desc': 'Single wire data'},
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},
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'),
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,)),
153 # Communication modes' data bit and pad bit duration (in us), and
154 # tolerances in percent and absolute (us).
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
180 self.sync_pads = None
181 self.data_bits = None
182 self.frame_bytes = None
183 self.short_bits = None
186 self.out_ann = self.register(srd.OUTPUT_ANN)
187 self.out_python = self.register(srd.OUTPUT_PYTHON)
189 def metadata(self, key, value):
190 if key == srd.SRD_CONF_SAMPLERATE:
191 self.samplerate = value
194 def putg(self, ss, es, data):
196 if not _with_ann_carrier and cls in (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE):
198 if not _with_ann_sync_loss and cls in (ANN_SYNC_LOSS,):
200 self.put(ss, es, self.out_ann, data)
202 def putpy(self, ss, es, ptype, pdata):
203 self.put(ss, es, self.out_python, [ptype, pdata])
205 def symbols_clear(self):
206 syms = self.symbols or []
210 def symbols_append(self, ss, es, symbol, data = None):
211 if self.symbols is None:
213 item = (ss, es, symbol, data)
214 self.symbols.append(item)
216 def symbols_get_last(self, count = None):
221 if len(self.symbols) < count:
223 items = self.symbols[-count:]
228 def symbols_update_last(self, ss, es, symbol, data = None):
231 item = list(self.symbols[-1])
236 if symbol is not None:
240 self.symbols[-1] = tuple(item)
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:
247 if len(self.symbols) < len(want_items):
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:
255 def symbols_collapse(self, count, symbol, data = None, squeeze = None):
256 if self.symbols is None:
258 if len(self.symbols) < count:
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]
266 item = (ss, es, symbol, data)
267 self.symbols.append(item)
269 def frame_flush(self):
270 syms = self.symbols_clear()
271 while syms and syms[0][2] == 'IDLE':
273 while syms and syms[-1][2] == 'IDLE':
280 if sym[2] == 'FRAME_INIT':
284 if sym[2] == 'SYNC_PAD':
285 if not text or text[-1] != 'SYNC':
289 if sym[2] == 'DATA_BYTE':
290 b = [bit[3] for bit in sym[3] if bit[2] == 'DATA_BIT']
292 text.append('{:02x}'.format(b))
295 if sym[2] == 'SHORT_BIT':
296 if not text or text[-1] != 'SHORT':
300 if sym[2] == 'WAIT_ACK':
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)
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)
322 self.putg(ss, es, [ANN_CARRIER_IDLE, ['IDLE']])
324 def carrier_set_idle(self, on, ss, es):
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.
338 self.carrier_is_idle = True
339 self.carrier_want_idle = False
342 if self.symbols_has_prev('IDLE'):
343 self.symbols_update_last(None, es, None)
345 self.carrier_is_idle = False
346 self.carrier_idle_ss = None
348 def carrier_set_busy(self, on, snum):
349 self.carrier_is_busy = on
351 self.carrier_is_idle = None
352 if not self.carrier_busy_ss:
353 self.carrier_busy_ss = snum
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
360 def carrier_check(self, level, snum):
362 # When HIGH is seen, immediately end IDLE and switch to BUSY.
364 self.carrier_set_idle(False, snum, snum)
365 self.carrier_set_busy(True, snum)
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)
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)
381 def span_prepare(self):
382 '''Prepare calculation of durations in terms of samples.'''
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).
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
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
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)
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)
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
422 self.lookahead_width = int(4 * self.data_width)
424 def span_snum_to_us(self, count):
425 return count / self.usec_width
427 def span_is_pad(self, span):
428 return span in range(*self.pad_bit_range)
430 def span_is_data(self, span):
431 if span in range(*self.data_bit_1_range):
433 if span in range(*self.data_bit_2_range):
435 if span in range(*self.data_bit_3_range):
437 if span in range(*self.data_bit_4_range):
441 def span_is_short(self, span):
442 return span in range(*self.short_data_range)
444 def wait_until(self, want):
445 '''Wait until a given location, but keep sensing carrier.'''
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).
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:
462 if not self.samplerate or self.samplerate < 1e6:
463 raise SamplerateError('Need a samplerate of at least 1MSa/s')
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.
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.
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.
482 # Help kick-start the IDLE condition detection after
483 # decoder state reset.
485 curr_level, = self.wait({PIN_DATA: 'l'})
486 self.carrier_check(curr_level, self.samplenum)
487 self.edges = [self.samplenum]
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]
499 bit_level = 1 - bit_level
501 self.edges = [self.samplenum]
503 self.edges.append(self.samplenum)
504 curr_snum = self.samplenum
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)
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)
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)
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:
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
541 if self.symbols_has_prev('DATA_BYTE'):
543 if self.symbols_has_prev('SHORT_BIT'):
545 if self.symbols_has_prev('WAIT_ACK'):
548 # Get (consume!) the LOW DATA bit after a PAD.
550 if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'):
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)
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)
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.)
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.
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])
628 self.edges = [self.samplenum]
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
642 self.edges = [self.samplenum]
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.
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)
669 curr_level, = self.wait_until(end_snum)
670 curr_snum = self.samplenum
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.
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
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)
693 # Turn a sequence of a SYNC-PAD and eight DATA bits into a
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)
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
709 sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE']
710 if self.symbols_has_prev(sync_resp_seq):