def _make_lin_frame(raw_bytes, frame_start, enhanced_ids):
    '''Generate a LINStreamFrame from raw bytes'''
    if len(raw_bytes) >= 2:
        lf = LINFrame(raw_bytes[0].data & 0x3F, [b.data for b in raw_bytes[1:-1]],\
                raw_bytes[-1].data, pid_parity=raw_bytes[0].data >> 6)
    else:
        lf = LINFrame(raw_bytes[0].data & 0x3F, pid_parity=raw_bytes[0].data >> 6)

    if lf.data is not None:
        if enhanced_ids is not None:
            if lf.id in enhanced_ids:
                lf.cs_type = LINChecksum.Enhanced
        else: # Determine checksum type automatically
            if not lf.checksum_is_valid():
                lf.cs_type = LINChecksum.Enhanced # Try using enhanced checksum
                if not lf.checksum_is_valid():
                    lf.cs_type = LINChecksum.Classic # Revert to classic checksum

    sf = LINStreamFrame((frame_start, raw_bytes[-1].end_time), lf)

    # Add annotated PID field
    pid_start = raw_bytes[0].subrecords[1].start_time
    pid_end = raw_bytes[0].subrecords[1].end_time
    id_end = (pid_end - pid_start) / 8 * 6 + pid_start

    sf.subrecords.append(stream.StreamSegment((pid_start, id_end), lf.id, kind='ID'))
    sf.subrecords[-1].annotate('addr', {'_bits':6})

    status = stream.StreamStatus.Ok if lf.pid_is_valid() else LINStreamStatus.PIDError
    sf.subrecords.append(stream.StreamSegment((id_end, pid_end), lf.pid >> 6, kind='PID parity', status=status))
    sf.subrecords[-1].annotate('check', {'_bits':2}, stream.AnnotationFormat.Hidden)


    if len(raw_bytes) >= 2:
        if len(raw_bytes) > 2: # Add data annotation
            for d in raw_bytes[1:-1]:
                d_info = d.subrecords[1]
                sf.subrecords.append(stream.StreamSegment((d_info.start_time, d_info.end_time), d.data, kind='data'))
                sf.subrecords[-1].annotate('data', {'_bits':8})

        # Add checksum annotation
        cs_info = raw_bytes[-1].subrecords[1]
        status = stream.StreamStatus.Ok if lf.checksum_is_valid() else LINStreamStatus.ChecksumError
        sf.subrecords.append(stream.StreamSegment((cs_info.start_time, cs_info.end_time),\
                            lf.checksum, kind='checksum', status=status))
        sf.subrecords[-1].annotate('check', {'_bits':8}, stream.AnnotationFormat.Hex)


    return sf
def can_decode(can, polarity=CANConfig.IdleHigh, bit_rate=None, bit_timing=None, coerce_rates=None, logic_levels=None,\
                stream_type=stream.StreamType.Samples, decode_info=None):
    '''Decode a CAN data stream

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.

    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    is consumed to determine the most likely logic levels in the signal.

    can (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a CAN data signal.
        This can be one of CAN-High, CAN-Low, or the differential voltage between
        them.

    polarity (CANConfig)
        Set the polarity (idle state high or low). This will be low when the can
        parameter is from CAN-Low, high when CAN-High, and dependent on probe orientation
        when using a differential input.

    bit_rate (number or None)
        The bit rate of the stream. If None, the first 50 edges will be analyzed to
        automatically determine the most likely bit rate for the stream. On average
        50 edges will occur after 11 bytes have been captured.

    bit_timing (CANTiming or None)
        An optional CANTiming object that specifies the time quanta for each bit phase.
        If None, a default timing object is used with prop. delay = 1q and p1 & p2 = 4q.

    coerce_rates (sequence of number or None)
        An optional list of standard bit rates to coerce the automatically detected
        bit rate to.
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the can parameter represents either Samples
        or Edges

    decode_info (dict or None)
        An optional dictionary object that is used to monitor the results of
        automatic parameter analysis and retrieve bit timing.
        
    Yields a series of CANStreamFrame objects. Each frame contains subrecords marking the location
      of sub-elements within the frame. CRC and Ack errors are recorded as an error status in their
      respective subrecords.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
      
    Raises AutoRateError if auto-rate detection is active and the bit rate cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            can_it, logic_levels = check_logic_levels(can)
        else:
            can_it = can
        
        edges = find_edges(can_it, logic_levels, hysteresis=0.4)
    else: # The stream is already a list of edges
        edges = can
        
    
    if bit_rate is None:
        # Find the bit rate
        
        # Tee off an independent iterator to determine bit rate
        edges_it, sre_it = itertools.tee(edges)
        
        min_edges = 50
        symbol_rate_edges = list(itertools.islice(sre_it, min_edges))
        
        # We need to ensure that we can pull out enough edges from the iterator slice
        if len(symbol_rate_edges) < min_edges:
            raise AutoRateError('Unable to compute automatic bit rate. Insufficient edges.')
        
        raw_symbol_rate = find_symbol_rate(iter(symbol_rate_edges), spectra=2)

        # Delete the tee'd iterators so that the internal buffer will not grow
        # as the edges_it is advanced later on
        del symbol_rate_edges
        del sre_it
        
        if coerce_rates:
            # find the standard rate closest to the raw rate
            bit_rate = _coerce_symbol_rate(raw_symbol_rate, coerce_rates)
        else:
            bit_rate = raw_symbol_rate

        if bit_rate == 0:
            raise AutoRateError('Unable to compute automatic bit rate. Got 0.')

        #print('### decoded bit rate:', bit_rate)
            
    else:
        edges_it = edges

    # Invert edge polarity if idle-low
    if polarity == CANConfig.IdleLow:
        edges_it = ((t, 1 - e) for t, e in edges_it)

    bit_period = 1.0 / float(bit_rate)
    es = EdgeSequence(edges_it, bit_period)

    if bit_timing is None:
        # Use default timing: prop delay = 1q, p1 & p2 = 4q
        bit_timing = CANTiming(1, 4)

    bit_timing.set_quantum_period(bit_period)
    #print('### resync jump:', bit_timing.resync_jump)

    sample_points = []
    be = _BitExtractor(es, bit_timing, sample_points)


    if decode_info is not None:
        decode_info['bit_rate'] = bit_rate
        if stream_type == stream.StreamType.Samples:
            decode_info['logic_levels'] = logic_levels

        decode_info['sample_points'] = sample_points


    # initialize to point where state is high --> idle time before first SOF
    while es.cur_state() == 0 and not es.at_end():
        es.advance_to_edge()

    while not es.at_end():
        # look for SOF falling edge
        #es.advance_to_edge()

        be.advance_to_falling()
        # We are now at a potential SOF
        
        # We could have an anamolous edge at the end of the edge list
        # Check if edge sequence is complete after our advance
        if es.at_end():
            break


        start_time = es.cur_time
        start_sample = len(sample_points)
        es.advance(bit_timing.sample_point_delay) # Move to sample point of SOF bit

        unstuffed_bits = be.get_bits(std_header_bits)

        stuffing_error = True if len(unstuffed_bits) < std_header_bits and not es.at_end() else False

        # If a data or remote frame ends with an error frame we will get a stuffing error.
        # If the error happens in the EOF field, the frame is still recoverable.
        # If a stuffing error occures in the last-but-one bit of the EOF it is regarded as an overload frame.

        found_data_rmt_frame = False

        field_info = []

        if len(unstuffed_bits) >= std_header_bits:
            found_data_rmt_frame = True
            header_bits = std_header_bits
            # Extract fields from unstuffed bits
            id_bits = unstuffed_bits[1:12]; field_info.append(('id', (1, 11)))
            rtr = unstuffed_bits[12]; field_info.append(('rtr', (12, 12)))
            ide = unstuffed_bits[13]; field_info.append(('ide', (13, 13)))

            field_ix = 14

            if ide == 1: # Extended format frame
                unstuffed_bits += be.get_bits(ext_header_bits - std_header_bits)
                if len(unstuffed_bits) >= ext_header_bits:
                    header_bits = ext_header_bits
                    srr = rtr
                    id_ext_bits = unstuffed_bits[field_ix:field_ix + 18]
                    field_info.append(('id_ext', (field_ix, field_ix+17)))
                    field_ix += 18
                    rtr = unstuffed_bits[field_ix]; field_info.append(('rtr', (field_ix, field_ix)))
                    field_info[1] = ('srr', (12, 12))
                    field_ix += 1
                    r1 = unstuffed_bits[field_ix]; field_info.append(('r1', (field_ix, field_ix)))
                    field_ix += 1
                else:
                    # ERROR: short extended frame
                    # Not enough bits to have partially decoded frame. Just abort
                    #print('### short extended frame:', len(unstuffed_bits), ext_header_bits)
                    continue

            r0 = unstuffed_bits[field_ix]; field_info.append(('r0', (field_ix, field_ix)))
            field_ix += 1


            dlc_bits = unstuffed_bits[field_ix:field_ix + 4]; field_info.append(('dlc', (field_ix, field_ix+3)))
            dlc = min(join_bits(dlc_bits), 8) # Limit to max of 8 data bytes
            field_ix += 4
            data = []

            if rtr == 0: # Data frame
                remaining_stuffed_bits = 8 * dlc + 15
            else: # Remote frame
                remaining_stuffed_bits = 15

            min_frame_bits = header_bits + remaining_stuffed_bits

            unstuffed_bits += be.get_bits(remaining_stuffed_bits)

            short_frame = False
            if rtr == 0: # Data frame
                # Verify we have enough raw bits
                if len(unstuffed_bits) < min_frame_bits:
                    # ERROR: short frame
                    short_frame = True
                else: # Get data bytes
                    for _ in xrange(dlc):
                        data.append(join_bits(unstuffed_bits[field_ix:field_ix + 8]))
                        field_info.append(('data', (field_ix, field_ix+7)))
                        field_ix += 8
            else: # Remote frame
                # Verify we have enough raw bits
                if len(unstuffed_bits) < min_frame_bits:
                    # ERROR: short frame
                    #print('### short remote frame', len(unstuffed_bits), min_frame_bits, unstuffed_bits, stuffed_bits)
                    short_frame = True

            form_error = False
            check_bits = []
            ack = 1
            if not short_frame:
                # Get checksum
                check_bits = unstuffed_bits[field_ix:field_ix + 15]; field_info.append(('crc', (field_ix, field_ix+14)))
                field_ix += 15

                # The remaining fields (crc delim, ack, ack delim) are not stuffed
                end_bits = be.get_bits(3, unstuff=False)
                if len(end_bits) == 3:
                    ack = True if end_bits[1] == 0 else False
                    if end_bits[0] != 1 or end_bits[2] != 1:
                        form_error = True
                else:
                    ack = False
                    form_error = True

                    # The last frame of the stream requires special treatment
                    # To position ourselves after the ack delim.
                    if es.cur_state() == 1:
                        es.advance((2 - len(end_bits)) * bit_period)

                field_info.append(('ack', (field_ix+1, field_ix+1)))


            if ide == 0:
                cf = CANStandardFrame(join_bits(id_bits), data, join_bits(dlc_bits), join_bits(check_bits), ack)
            else:
                cf = CANExtendedFrame(join_bits(id_bits + id_ext_bits), data, \
                    join_bits(dlc_bits), join_bits(check_bits), ack)

                cf.srr = srr
                cf.r1 = r1

            cf.rtr = rtr
            cf.ide = ide
            cf.r0 = r0 


            # Determine the end of the frame
            if es.cur_state() == 1:
                if es.cur_time > es.next_states[0]:
                    # Special case for last frame in stream
                    end_time = es.cur_time + 5 * bit_period + bit_timing.post_sample_delay
                elif es.next_states[0] > es.cur_time + 5 * bit_period:
                    end_time = es.cur_time + 5 * bit_period + bit_timing.post_sample_delay
                else:
                    end_time = es.next_states[0]
                    stuffing_error = True
                    es.advance_to_edge()
                    be.dom_start_time = es.cur_time

            else: # Aready in dominant state
                end_time = be.dom_start_time
                stuffing_error = True

            status = CANStreamStatus.FormError if form_error else stream.StreamStatus.Ok

            sf = CANStreamFrame((start_time, end_time), cf, field_info, be.stuffed_bits)

            if short_frame:
                sf.annotate('frame_bad', {}, stream.AnnotationFormat.Hidden)
                sf.status = CANStreamStatus.ShortFrameError

            # Add subrecords for each field in the frame
            adj_info = _adjust_fields_for_stuffing(field_info, be.stuffed_bits)

            field_sizes = [e - s + 1 for _, (s, e) in field_info]

            data_ix = 0
            for (field, bit_bounds), field_size in zip(adj_info, field_sizes):
                bounds = (sample_points[start_sample + bit_bounds[0]][0], sample_points[start_sample + bit_bounds[1] + 1][0])


                if field in _can_field_formats:
                    style, text_format = _can_field_formats[field]
                else:
                    style = 'ctrl'
                    text_format = stream.AnnotationFormat.Hex

                value = getattr(cf, field)
                if field == 'data':
                    value = value[data_ix]
                    data_ix += 1

                status = stream.StreamStatus.Ok
                if field == 'crc' and not cf.crc_is_valid():
                    status = CANStreamStatus.CRCError
                if field == 'ack' and not cf.ack:
                    status = CANStreamStatus.AckError


                sf.subrecords.append(stream.StreamSegment(bounds, value, kind=field, status=status))
                sf.subrecords[-1].annotate(style, {'_bits':field_size}, text_format)

            yield sf

        # Check if the EOF was complete

        if stuffing_error:
            # This could be an error or overload frame

            # Keep fetching dominant bits until they become recessive. Then look for 8 recessive delimiter bits.
            while es.cur_state() == 0 and not es.at_end():
                es.advance(bit_period)

            if es.cur_time < es.next_states[0]:
                end_time = es.next_states[0]
            else: # Special case for end of stream
                end_time = es.cur_time + 7 * bit_period

            if end_time - es.cur_time > 6.5 * bit_period: # Valid error or overload frame
                es.advance(7 * bit_period)

                if found_data_rmt_frame:
                    # There was an error frame following a data or remote frame
                    cf = CANErrorFrame()
                else:
                    cf = CANOverloadFrame()
                end_time = es.cur_time + bit_timing.post_sample_delay
                sf = CANStreamFrame((be.dom_start_time, end_time), cf)
                sf.annotate('frame', {'name':''}, stream.AnnotationFormat.String)
                yield sf
def rc5_decode(ir_stream, carrier_freq=36.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \
     stream_type=stream.StreamType.Samples):
    '''Decode RC5 infrared protocol

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.
    
    ir_stream (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream of IR pulses. The type of stream is identified
        by the stream_type parameter. When this is a sample stream, an initial block
        of data is consumed to determine the most likely logic levels in the signal.
        This signal can be either modulated or demodulated.

    carrier_freq (float)
        The carrier frequency for modulation.

    polarity (infrared.IRConfig)
        Set the polarity (idle state high or low).
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the ir_stream parameter represents either Samples
        or Edges.
        
    Yields a series of RC5StreamMessage objects.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            samp_it, logic_levels = decode.check_logic_levels(ir_stream)
        else:
            samp_it = ir_stream

        edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4)
    else:  # the stream is already a list of edges
        edges = ir_stream

    # Demodulate signal (also passes an unmodulated signal without changes)
    edges = ir.demodulate(edges, carrier_freq, polarity)

    pulse_width = 889.0e-6  # 889us pulse width
    es = decode.EdgeSequence(edges, pulse_width)

    while not es.at_end():
        # Look for the rising edge of a pulse
        if es.cur_state() == 0:
            es.advance_to_edge()  # Now we're 1
        msg_start_time = es.cur_time - pulse_width

        # Move ahead half a pulse and check that we are still 1
        es.advance(pulse_width / 2.0)
        if es.cur_state() != 1:
            continue

        # Advance through edge sequence recording state until we see 3 0's or 3 1's in a row
        # This indicates a break in the Manchester encoding.

        coded_bits = [0, 1]
        bit_starts = [0.0, 0.0]
        same_count = 1
        prev_state = 1
        while True:
            es.advance()
            coded_bits.append(es.cur_state())
            bit_starts.append(es.cur_time - pulse_width / 2.0)
            if es.cur_state() == prev_state:
                same_count += 1
            else:
                same_count = 1

            if same_count > 2:
                break

            prev_state = es.cur_state()

        msg_end_time = es.cur_time - pulse_width

        if len(coded_bits) >= 14 * 2:
            # Decode Manchester
            # The second bit of each pair is the same as the decoded bit
            msg_bits = coded_bits[1:28:2]
            mb_starts = bit_starts[::2]

            #print('$$$ coded_bits:', coded_bits)
            #print('$$$ msg_bits:', msg_bits)

            toggle = msg_bits[2]
            addr = join_bits(msg_bits[3:8])
            cmd = join_bits([0 if msg_bits[1] else 1] + msg_bits[8:14])
            msg = RC5Message(cmd, addr, toggle)
            sm = RC5StreamMessage((msg_start_time, msg_end_time), msg)
            sm.annotate('frame', {'name': 'frame'},
                        stream.AnnotationFormat.Hidden)

            sm.subrecords.append(
                stream.StreamSegment((mb_starts[2], mb_starts[3]),
                                     toggle,
                                     kind='toggle'))
            sm.subrecords[-1].annotate('data', {'_bits': 1},
                                       stream.AnnotationFormat.Int)
            sm.subrecords.append(
                stream.StreamSegment((mb_starts[3], mb_starts[8]),
                                     addr,
                                     kind='addr'))
            sm.subrecords[-1].annotate('addr', {'_bits': 5})
            sm.subrecords.append(
                stream.StreamSegment((mb_starts[8], mb_starts[14]),
                                     cmd,
                                     kind='cmd'))
            sm.subrecords[-1].annotate('data', {'_bits': 6})
            if cmd > 63:
                # Extended format; 7-th bit is just after first start bit
                sm.subrecords[-1].fields['_bits'] = 7
                sm.subrecords.append(
                    stream.StreamSegment((mb_starts[1], mb_starts[2]),
                                         msg_bits[1],
                                         kind='cmd bit-7'))
                sm.subrecords[-1].annotate('data1', {'_bits': 1},
                                           stream.AnnotationFormat.Int)

            yield sm
def rc6_decode(ir_stream, carrier_freq=36.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \
     stream_type=stream.StreamType.Samples):

    '''Decode RC6 infrared protocol

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.
    
    ir_stream (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream of IR pulses. The type of stream is identified
        by the stream_type parameter. When this is a sample stream, an initial block
        of data is consumed to determine the most likely logic levels in the signal.
        This signal can be either modulated or demodulated.

    carrier_freq (float)
        The carrier frequency for modulation.

    polarity (infrared.IRConfig)
        Set the polarity (idle state high or low).
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the ir_stream parameter represents either Samples
        or Edges.
        
    Yields a series of RC6StreamMessage objects.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            samp_it, logic_levels = decode.check_logic_levels(ir_stream)
        else:
            samp_it = ir_stream
        
        edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4)
    else: # the stream is already a list of edges
        edges = ir_stream

    # Demodulate signal (also passes an unmodulated signal without changes)
    edges = ir.demodulate(edges, carrier_freq, polarity)

    pulse_width = 444.0e-6 # 444us pulse width
    es = decode.EdgeSequence(edges, pulse_width)

    epsilon = 30.0e-6 # allow +/-30us variation for pulses and bit times
    time_is_nearly = functools.partial(ir.time_is_nearly, epsilon=epsilon)


    while not es.at_end():
        # Look for the rising edge of a pulse
        if es.cur_state() == 0:
            es.advance_to_edge() # Now we're 1
        msg_start_time = es.cur_time

        ts = es.advance_to_edge()
        if not time_is_nearly(ts, 6 * pulse_width):
            continue # This is not the leading AGC pulse of a message

        ts = es.advance_to_edge()
        if not time_is_nearly(ts, 2 * pulse_width):
            continue # This is not the leading AGC pulse of a message

        # Move ahead half a pulse and check that we are still 1
        es.advance(pulse_width / 2.0)
        if es.cur_state() != 1:
            continue # Not a valid start bit

        es.advance() # End of start bit

        # Advance through edge sequence recording state until we see 4 0's or 4 1's in a row
        # This indicates a break in the Manchester encoding.

        coded_bits = [1, 0] # Start bit
        bit_starts = [0.0, 0.0]
        same_count = 1
        prev_state = 1
        while True:
            es.advance()
            coded_bits.append(es.cur_state())
            bit_starts.append(es.cur_time - pulse_width / 2.0)
            if es.cur_state() == prev_state:
                same_count += 1
            else:
                same_count = 1

            if same_count > 3:
                break

            prev_state = es.cur_state()

        msg_end_time = es.cur_time - 2.5 * pulse_width

        #print('$$$ found bits:', len(coded_bits))

        if len(coded_bits) >= 22 * 2:
            # Decode Manchester
            # The first bit of each pair is the same as the decoded bit
            msg_bits = coded_bits[0:44:2]
            mb_starts = bit_starts[::2]

            mode = join_bits(msg_bits[1:4])
            toggle = msg_bits[4]

            if mode == 6: # RC6A message
                if msg_bits[6]: # 15-bit customer field
                    msg_bits = coded_bits[0:76:2]
                    customer = join_bits(msg_bits[7:22])
                    asb = 22 # addr start bit index
                else: # 7-bit customer field
                    msg_bits = coded_bits[0:60:2]
                    customer = join_bits(msg_bits[7:14])
                    asb = 14
            else: # RC6 message
                customer = None
                asb = 6

            #print('$$$ coded_bits:', coded_bits)
            #print('$$$       msg_bits:', msg_bits)


            addr = join_bits(msg_bits[asb:asb+8])
            cmd = join_bits(msg_bits[asb+8:asb+16])
            msg = RC6Message(cmd, addr, toggle, mode, customer)
            sm = RC6StreamMessage((msg_start_time, msg_end_time), msg)
            sm.annotate('frame', {'name':'frame'}, stream.AnnotationFormat.Hidden)

            sm.subrecords.append(stream.StreamSegment((mb_starts[1], mb_starts[4]), mode, kind='mode'))
            sm.subrecords[-1].annotate('addr', {'_bits':3})
            sm.subrecords.append(stream.StreamSegment((mb_starts[4], mb_starts[6]), toggle, kind='toggle'))
            sm.subrecords[-1].annotate('data', {'_bits':1}, stream.AnnotationFormat.Int)
           
            if mode == 6:
                sm.subrecords.append(stream.StreamSegment((mb_starts[6], mb_starts[asb]), customer, kind='customer'))
                sm.subrecords[-1].annotate('data', {'_bits':7 + msg_bits[6]*8})

            sm.subrecords.append(stream.StreamSegment((mb_starts[asb], mb_starts[asb+8]), addr, kind='addr'))
            sm.subrecords[-1].annotate('addr', {'_bits':8})
            sm.subrecords.append(stream.StreamSegment((mb_starts[asb+8], mb_starts[asb+16]), cmd, kind='cmd'))
            sm.subrecords[-1].annotate('data', {'_bits':8})


            yield sm
def ps2_decode(clk, data, logic_levels=None, stream_type=stream.StreamType.Samples):
    '''Decode a PS/2 data stream
    
    This is a generator function that can be used in a pipeline of waveform
    processing operations.
    
    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    on the clk stream is consumed to determine the most likely logic levels in the signal.
    
    clk (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a PS/2 clk signal
    
    data (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a PS/2 data signal.
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.

    stream_type (streaming.StreamType)
        A StreamType value indicating that the clk and data parameters represent either Samples
        or Edges

    Yields a series of PS2StreamFrame objects.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            s_clk_it, logic_levels = check_logic_levels(clk)
        else:
            s_clk_it = clk
        
        hyst = 0.4
        clk_it = find_edges(s_clk_it, logic_levels, hysteresis=hyst)
        data_it = find_edges(data, logic_levels, hysteresis=hyst)

    else: # the streams are already lists of edges
        clk_it = clk
        data_it = data

    edge_sets = {
        'clk': clk_it,
        'data': data_it
    }
    
    es = MultiEdgeSequence(edge_sets, 0.0)

    # begin to decode

    direction = PS2Dir.DeviceToHost
    find_frame_start = True
    bits_remaining = 10
    bits = []
    byte_complete = False


    # Minimum clock is 10kHz
    min_period = 1.0 / 10.0e3 * 1.05 # add 5%


    while not es.at_end('clk'):
        ts, cname = es.advance_to_edge(channel_name='clk')

        if es.at_end('clk'):
            continue

        clk_val = es.cur_state('clk')
        d_val = es.cur_state('data')

        if ts > 100.0e-6 and not find_frame_start:
            # Some sort of framing error happened. Start over to resynchronize
            find_frame_start = True
            bits = []
            ne = stream.StreamEvent(es.cur_time(), kind='PS/2 resynch', \
                status=PS2StreamStatus.FramingError)
            yield ne
            

        if find_frame_start == True and (d_val == 0):
            bits_remaining = 10
            start_time = es.cur_time()
            timing_error = False
            if clk_val == 0: # falling edge
                direction = PS2Dir.DeviceToHost
                find_frame_start = False
            elif clk_val == 1 and ts > 100.0e-6:
                direction = PS2Dir.HostToDevice
                find_frame_start = False
                get_ack = False
            continue


        if not find_frame_start:
            # Confirm that the clock rate is fast enough
            # We should always be advancing by no more than 1/2 the minimum clock period
            if ts > (min_period / 2):
                timing_error = True

            if direction == PS2Dir.DeviceToHost:
                if clk_val == 0: # this is a falling edge, capture data
                    if bits_remaining:
                        bits.append(d_val)
                        bits_remaining -= 1

                    if not bits_remaining: # completed a byte, verify and build frame object
                        end_time = es.cur_time()
                        bit_period = (end_time - start_time) / 10
                        start_time -= bit_period / 2
                        end_time += bit_period / 2

                        data_time = start_time + bit_period
                        parity_time = end_time - (bit_period * 2)
                        stop_time = end_time - bit_period
                        stop_end_time = end_time

                        byte_complete = True

            else: # HostToDevice
                if clk_val == 1 and get_ack == False: # this is a rising edge, capture data
                    if bits_remaining:
                        bits.append(d_val)
                        bits_remaining -= 1

                    if not bits_remaining: # ready to look for ack
                        get_ack = True

                elif clk_val == 0 and get_ack == True:
                    bits.append(d_val)

                    end_time = es.cur_time()
                    bit_period = (end_time - start_time) / 10.5
                    start_time -= bit_period / 2
                    end_time += bit_period / 2

                    data_time = start_time + bit_period
                    parity_time = end_time - (bit_period * 2.5)
                    stop_time = end_time - (bit_period * 1.5)
                    ack_time = end_time - (bit_period * 0.75)
                    stop_end_time = ack_time

                    byte_complete = True
                    if d_val == 0:
                        es.advance_to_edge('data') # move ahead to rising edge of ack pulse

            if byte_complete:

                data = join_bits(reversed(bits[0:8]))
                p = 1
                for b in bits[0:8]:
                    p ^= b

                parity_error = True if p != bits[8] else False
                
                framing_error = True if bits[9] != 1 else False # missing stop bit


                status = PS2StreamStatus.FramingError if framing_error else \
                    PS2StreamStatus.TimingError if timing_error else stream.StreamStatus.Ok

                nf = PS2StreamFrame((start_time, end_time), PS2Frame(data, direction), status=status)
                nf.annotate('frame', {}, stream.AnnotationFormat.Hidden)

                nf.subrecords.append(stream.StreamSegment((start_time, data_time), kind='start bit'))
                nf.subrecords[-1].annotate('misc', {'_bits':1}, stream.AnnotationFormat.Invisible)
                nf.subrecords.append(stream.StreamSegment((data_time, parity_time), data, kind='data bits'))
                nf.subrecords[-1].annotate('data', {'_bits':bits}, stream.AnnotationFormat.General)

                status = PS2StreamStatus.ParityError if parity_error else stream.StreamStatus.Ok
                nf.subrecords.append(stream.StreamSegment((parity_time, stop_time), kind='parity', status=status))
                nf.subrecords[-1].annotate('check', {'_bits':1}, stream.AnnotationFormat.Hidden)
                nf.subrecords.append(stream.StreamSegment((stop_time, stop_end_time), kind='stop bit'))
                nf.subrecords[-1].annotate('misc', {'_bits':1}, stream.AnnotationFormat.Invisible)

                if direction == PS2Dir.HostToDevice:
                    ack_error = True if bits[10] != 0 else False
                    status = PS2StreamStatus.AckError if ack_error else stream.StreamStatus.Ok
                    nf.subrecords.append(stream.StreamSegment((ack_time, end_time), kind='ack bit', status=status))
                    nf.subrecords[-1].annotate('ack', {'_bits':1}, stream.AnnotationFormat.Hidden)

                bits = []
                find_frame_start = True
                byte_complete = False

                yield nf
示例#6
0
文件: lm73.py 项目: tsk/ripyl
def lm73_decode(i2c_stream, addresses=LM73Addresses):
    '''Decode an LM73 data stream
    
    i2c_stream (sequence of StreamRecord or I2CTransfer)
        An iterable representing either a stream of I2C StreamRecord objects or
        I2CTransfer objects produced by i2c_decode() or reconstruct_i2c_transfers() respectively.
    
    addresses (set of ints)
        A collection identifying the valid LM73 addresses to decode. All others are ignored.
        
    Yields a series of LM73Transfer objects and any unrelated I2CTransfer objects.
    '''
    cur_reg = LM73Register.Temperature

    # check type of stream
    stream_it, check_it = itertools.tee(i2c_stream)
    try:
        rec0 = next(check_it)
    except StopIteration:
        # Stream is empty
        rec0 = None

    if rec0 is not None:
        if not isinstance(rec0, i2c.I2CTransfer):
            # Convert the stream to a set of I2C transfers
            stream_it = i2c.reconstruct_i2c_transfers(stream_it)

    del check_it

    for tfer in stream_it:
        if tfer.address not in addresses:
            yield tfer
            continue

        if tfer.r_wn == i2c.I2C.Write:
            if len(tfer.data) == 0:  # Error condition
                # This should only happen if the data portion of a write is missing
                tfer.status = LM73StreamStatus.MissingDataError
                yield tfer
                continue

            elif len(tfer.data) == 1:  # Set pointer op
                cur_reg = tfer.data[0]
                lm_tfer = LM73Transfer(tfer.address, LM73Operation.SetPointer,
                                       cur_reg, None)
                lm_tfer.annotate('frame', {}, stream.AnnotationFormat.Hidden)
                lm_tfer.subrecords = tfer.subrecords
                lm_tfer.subrecords[
                    0].data_format = stream.AnnotationFormat.Small
                address = lm_tfer.subrecords[0].data >> 1
                lm_tfer.subrecords[0].fields['value'] = 'set ptr\n{}'.format(
                    hex(address))
                lm_tfer.subrecords[2].annotate('ctrl', None,
                                               stream.AnnotationFormat.Enum)
                lm_tfer.subrecords[2].fields['value'] = LM73Register(
                    lm_tfer.subrecords[2].data)

            else:  # Write data
                cur_reg = tfer.data[0]
                lm_tfer = LM73Transfer(tfer.address, LM73Operation.WriteData, \
                    cur_reg, tfer.data[1:])
                lm_tfer.annotate('frame', {}, stream.AnnotationFormat.Hidden)
                lm_tfer.subrecords = tfer.subrecords

        else:  # Read data
            lm_tfer = LM73Transfer(tfer.address, LM73Operation.ReadData,
                                   cur_reg, tfer.data)
            lm_tfer.annotate('frame', {}, stream.AnnotationFormat.Hidden)
            lm_tfer.subrecords = tfer.subrecords
            lm_tfer.subrecords[0].data_format = stream.AnnotationFormat.Small
            address = lm_tfer.subrecords[0].data >> 1
            lm_tfer.subrecords[0].fields['value'] = 'read\n{}'.format(
                hex(address))

            for sr in lm_tfer.subrecords[2::2]:
                sr.data_format = stream.AnnotationFormat.Hex

            if cur_reg == LM73Register.Temperature and len(tfer.data) == 2:
                temp_data = lm_tfer.subrecords[2:]
                temp_sr = stream.StreamSegment(
                    (temp_data[0].start_time, temp_data[-1].end_time),
                    kind='LM73 Temperature')
                value = 'Temp. = {} C\n0x{:04X}'.format(
                    lm_tfer.temperature, (tfer.data[0] << 8) + tfer.data[1])
                temp_sr.annotate('data', {'value': value},
                                 stream.AnnotationFormat.Small)
                temp_sr.subrecords = temp_data
                for sr in temp_sr.subrecords:
                    sr.data_format = stream.AnnotationFormat.Hidden

                lm_tfer.subrecords = lm_tfer.subrecords[0:2] + [temp_sr]

        lm_tfer.i2c_tfer = tfer
        yield lm_tfer
def _build_j1850_record(bytes, ifr_bytes, byte_starts, ifr_byte_starts,
                        ifr_with_crc, bounds):
    '''Create a J1850 frame from raw bytes'''
    header_len = 1 if bytes[0] & 0x10 else 3
    priority = bytes[0] >> 5
    msg_type = bytes[0] & 0x0F

    data = bytes[header_len:-1]
    if len(data) == 0: data = None

    ifr_data = None
    ifr_crc = None
    if len(ifr_bytes) > 0:
        ifr_data = ifr_bytes[:-1] if ifr_with_crc else ifr_bytes
        ifr_crc = ifr_bytes[-1] if ifr_with_crc else None

    if header_len == 3:
        nf = J1850Frame(priority, msg_type, data, bytes[1], bytes[2], crc=bytes[-1], \
                        ifr_data=ifr_data, ifr_crc=ifr_crc)
    else:
        nf = J1850Frame(priority,
                        msg_type,
                        data,
                        crc=bytes[-1],
                        ifr_data=ifr_data,
                        ifr_crc=ifr_crc)

    sf = J1850StreamFrame(bounds, nf)

    # Add annotations

    bounds = (byte_starts[0], byte_starts[1])
    sf.subrecords.append(stream.StreamSegment(bounds, bytes[0], kind='header'))
    sf.subrecords[-1].annotate('ctrl', {'_bits': 8})

    if header_len == 3:
        # Add target and source
        bounds = (byte_starts[1], byte_starts[2])
        sf.subrecords.append(
            stream.StreamSegment(bounds, bytes[1], kind='target'))
        sf.subrecords[-1].annotate('addr', {'_bits': 8})

        bounds = (byte_starts[2], byte_starts[3])
        sf.subrecords.append(
            stream.StreamSegment(bounds, bytes[2], kind='source'))
        sf.subrecords[-1].annotate('addr', {'_bits': 8})

    # Data
    for i, d in enumerate(bytes[header_len:-1]):
        bounds = (byte_starts[i + header_len], byte_starts[i + 1 + header_len])
        sf.subrecords.append(stream.StreamSegment(bounds, d, kind='data'))
        sf.subrecords[-1].annotate('data', {'_bits': 8})

    # CRC
    bounds = (byte_starts[-2], byte_starts[-1])
    status = J1850StreamStatus.CRCError if not nf.crc_is_valid(
    ) else stream.StreamStatus.Ok
    sf.subrecords.append(
        stream.StreamSegment(bounds, bytes[-1], kind='CRC', status=status))
    sf.subrecords[-1].annotate('check', {'_bits': 8})

    if len(ifr_bytes) > 0:  # IFR
        last_ifr = len(ifr_bytes) if not ifr_with_crc else len(ifr_bytes) - 1
        for i, d in enumerate(ifr_bytes[:last_ifr]):
            bounds = (ifr_byte_starts[i], ifr_byte_starts[i + 1])
            sf.subrecords.append(
                stream.StreamSegment(bounds, d, kind='IFR data'))
            sf.subrecords[-1].annotate('data', {'_bits': 8})

        if ifr_with_crc:
            bounds = (ifr_byte_starts[-2], ifr_byte_starts[-1])
            status = J1850StreamStatus.CRCError if not nf.ifr_crc_is_valid(
            ) else stream.StreamStatus.Ok
            sf.subrecords.append(
                stream.StreamSegment(bounds,
                                     ifr_bytes[-1],
                                     kind='CRC',
                                     status=status))
            sf.subrecords[-1].annotate('check', {'_bits': 8})

    return sf
def j1850_pwm_decode(pwm,
                     logic_levels=None,
                     stream_type=stream.StreamType.Samples):
    '''Decode a J1850 PWM data stream

    This decodes the Pulse Width Modulated version of J1850 (Ford).

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.

    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    is consumed to determine the most likely logic levels in the signal.

    pwm (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a PWM Bus+ signal or the differential
        Bus+ - Bus-.

    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the can parameter represents either Samples
        or Edges

    Yields a series of J1850StreamFrame objects. Each frame contains subrecords marking the location
      of sub-elements within the frame. CRC errors are recorded as an error status in their
      respective subrecords.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            pwm_it, logic_levels = decode.check_logic_levels(pwm)
        else:
            pwm_it = pwm

        edges = decode.find_edges(pwm_it, logic_levels, hysteresis=0.4)
    else:  # The stream is already a list of edges
        edges = pwm

    es = decode.EdgeSequence(edges, 0.0)

    while not es.at_end():
        # Look for start of frame
        es.advance_to_edge()

        if es.cur_state() != 1: continue

        frame_start = es.cur_time

        if es.next_states[0] > es.cur_time:
            pulse_width = es.next_states[0] - es.cur_time
        else:
            pulse_width = 500.0e-6

        if 27.0e-6 <= pulse_width <= 34.0e-6:  # This is a valid SOF pulse (Tp7)
            sof_start = es.cur_time
            es.advance_to_edge()  # Move to end of SOF pulse
            if es.next_states[0] > es.cur_time:
                pulse_width = es.next_states[0] - es.cur_time
            else:
                pulse_width = 500.0e-6

            if 42.0e-6 <= es.cur_time - sof_start + pulse_width <= 54.0e-6:
                # Valid SOF (Tp4)
                es.advance_to_edge()  # Move to first bit

        else:  # Look for break condition
            if 34.0e-6 < pulse_width <= 43.0e-6 and es.cur_state() == 1:
                brk = J1850Break()
                sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                          brk,
                                          kind='break')
                sf.annotate('frame', {'value': 'Break'},
                            stream.AnnotationFormat.String)
                yield sf

            continue

        def collect_bits(es):
            '''Find frame and IFR bits'''
            frame_bits = []
            bit_starts = []

            if es.next_states[0] > es.cur_time:
                pulse_width = es.next_states[0] - es.cur_time
            else:
                pulse_width = 500.0e-6

            while pulse_width <= 18.0e-6:
                if 4.0e-6 <= pulse_width <= 18.0e-6:
                    if pulse_width <= 10.0e-6:
                        b = 1
                    else:
                        b = 0

                    frame_bits.append(b)
                    bit_starts.append(es.cur_time)

                    bit_pulse = pulse_width
                    # Move to end of pulse
                    es.advance_to_edge()
                    if es.next_states[0] > es.cur_time:
                        pulse_width = es.next_states[0] - es.cur_time
                    else:
                        pulse_width = 500.0e-6

                    tp3 = bit_pulse + pulse_width
                    if 21.0e-6 <= tp3 <= 27e-6:  # Valid bit
                        es.advance_to_edge()  # Move to start of next bit
                        if es.next_states[0] > es.cur_time:
                            pulse_width = es.next_states[0] - es.cur_time
                        else:
                            pulse_width = 500.0e-6

                    else:  # No more bits
                        break

                else:  # Invalid pulse < 4us
                    break

            bit_starts.append(es.cur_time)
            return (frame_bits, bit_starts, pulse_width)

        # Get the frame bits
        frame_bits, bit_starts, pulse_width = collect_bits(es)
        if 34.0e-6 < pulse_width <= 43.0e-6 and es.cur_state() == 1:
            brk = J1850Break()
            sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                      brk,
                                      kind='break')
            sf.annotate('frame', {'value': 'Break'},
                        stream.AnnotationFormat.String)
            yield sf
            continue

        # Validate collected bits
        if len(frame_bits) % 8 != 0 or len(frame_bits) < 2 * 8:
            continue

        # Convert frame bits to bytes
        bytes = []
        for i in xrange(len(frame_bits) // 8):
            bytes.append(join_bits(frame_bits[i * 8:i * 8 + 8]))

        byte_starts = bit_starts[::8]

        header_len = 1 if bytes[0] & 0x10 else 3
        if header_len == 3 and len(bytes) < 4: continue

        priority = bytes[0] >> 5
        msg_type = bytes[0] & 0x0F
        data = bytes[header_len:-1]
        if len(data) == 0: data = None

        # Look for IFR
        if es.next_states[0] > es.cur_time:
            pulse_width = es.next_states[0] - es.cur_time
        else:
            pulse_width = 500.0e-6

        ifr_bytes = []
        ifr_byte_starts = []
        ifr_with_crc = False
        if 42.0e-6 <= pulse_width + (es.cur_time - bit_starts[-2]
                                     ) <= 63.0e-6 and (msg_type & 0x08) == 0:
            # IFR is present
            es.advance_to_edge()  # Start of first IFR bit

            ifr_with_crc = True if msg_type == J1850MT.FunctionRead else False

            # Get the IFR bits
            ifr_bits, ifr_bit_starts, pulse_width = collect_bits(es)
            if 34.0e-6 < pulse_width <= 43.0e-6 and es.cur_state() == 1:
                brk = J1850Break()
                sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                          brk,
                                          kind='break')
                sf.annotate('frame', {'value': 'Break'},
                            stream.AnnotationFormat.String)
                yield sf
                continue

            # Validate IFR bits
            if len(ifr_bits) % 8 == 0 and len(ifr_bits) >= 8:
                # Convert IFR bits to bytes
                for i in xrange(len(ifr_bits) // 8):
                    ifr_bytes.append(join_bits(ifr_bits[i * 8:i * 8 + 8]))

                ifr_byte_starts = ifr_bit_starts[::8]

        sf = _build_j1850_record(bytes, ifr_bytes, byte_starts, ifr_byte_starts, ifr_with_crc, \
                                (frame_start, es.cur_time + 4.0e-6))
        yield sf
def j1850_vpw_decode(vpw,
                     norm_bit=VPWNormBitStyle.SAE,
                     logic_levels=None,
                     stream_type=stream.StreamType.Samples):
    '''Decode a J1850 VPW data stream

    This decodes the Variable Pulse Width version of J1850 (GM & Chrysler).

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.

    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    is consumed to determine the most likely logic levels in the signal.

    vpw (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a VPW data signal.

    norm_bit (VPWNormBitStyle)
        How to interpret the normalization bit for In-Frame Response. Either standard SAE
        style or the GM specific variant. This determines whether the IFR is expected to
        have a CRC independently from the message type.

    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the can parameter represents either Samples
        or Edges

    Yields a series of J1850StreamFrame objects. Each frame contains subrecords marking the location
      of sub-elements within the frame. CRC errors are recorded as an error status in their
      respective subrecords.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            vpw_it, logic_levels = decode.check_logic_levels(vpw)
        else:
            vpw_it = vpw

        edges = decode.find_edges(vpw_it, logic_levels, hysteresis=0.4)
    else:  # The stream is already a list of edges
        edges = vpw

    es = decode.EdgeSequence(edges, 0.0)

    while not es.at_end():
        # Look for start of frame
        es.advance_to_edge()

        if es.cur_state() != 1: continue

        frame_start = es.cur_time

        if es.next_states[0] > es.cur_time:
            pulse_width = es.next_states[0] - es.cur_time
        else:
            pulse_width = 500.0e-6

        if 163.0e-6 < pulse_width <= 239.0e-6:  # This is a valid SOF
            es.advance_to_edge()  # Move to first bit
        else:
            if pulse_width > 280.0e-6 and es.cur_state() == 1:
                brk = J1850Break()
                sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                          brk,
                                          kind='break')
                sf.annotate('frame', {'value': 'Break'},
                            stream.AnnotationFormat.String)
                yield sf

            continue

        def collect_bits(es):
            '''Find frame and IFR bits'''
            frame_bits = []
            bit_starts = []
            is_passive = 1

            if es.next_states[0] > es.cur_time:
                pulse_width = es.next_states[0] - es.cur_time
            else:
                pulse_width = 500.0e-6

            while pulse_width <= 239.0e-6:

                if 34.0e-6 < pulse_width <= 163.0e-6:
                    if pulse_width > 96.0e-6:  # 128us pulse
                        b = 1 if is_passive else 0
                    else:  # 64us pulse
                        b = 0 if is_passive else 1

                    frame_bits.append(b)
                    bit_starts.append(es.cur_time)
                    is_passive = 1 - is_passive

                elif pulse_width > 163.0e-6:  # EOD
                    break
                else:  # Invalid pulse < 34us
                    break

                es.advance_to_edge()
                if es.next_states[0] > es.cur_time:
                    pulse_width = es.next_states[0] - es.cur_time
                else:
                    pulse_width = 500.0e-6

            bit_starts.append(es.cur_time)

            return (frame_bits, bit_starts, pulse_width)

        # Get the frame bits
        frame_bits, bit_starts, pulse_width = collect_bits(es)
        if pulse_width > 280.0e-6 and es.cur_state() == 1:
            brk = J1850Break()
            sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                      brk,
                                      kind='break')
            sf.annotate('frame', {'value': 'Break'},
                        stream.AnnotationFormat.String)
            yield sf
            continue

        # Validate collected bits
        if len(frame_bits) % 8 != 0 or len(frame_bits) < 2 * 8:
            continue

        # Convert frame bits to bytes
        bytes = []
        for i in xrange(len(frame_bits) // 8):
            bytes.append(join_bits(frame_bits[i * 8:i * 8 + 8]))

        byte_starts = bit_starts[::8]

        header_len = 1 if bytes[0] & 0x10 else 3
        if header_len == 3 and len(bytes) < 4: continue

        priority = bytes[0] >> 5
        msg_type = bytes[0] & 0x0F
        data = bytes[header_len:-1]
        if len(data) == 0: data = None

        # Look for IFR
        if es.next_states[0] > es.cur_time:
            pulse_width = es.next_states[0] - es.cur_time
        else:
            pulse_width = 500.0e-6

        ifr_bytes = []
        ifr_byte_starts = []
        ifr_with_crc = False
        if 200.0e-6 < pulse_width <= 280.0e-6 and (msg_type & 0x08) == 0:
            # IFR is present
            # Check normalization bit width to determine if IFR CRC is present
            es.advance_to_edge()  # Start of norm bit
            if es.next_states[0] > es.cur_time:
                pulse_width = es.next_states[0] - es.cur_time
            else:
                pulse_width = 500.0e-6

            if norm_bit == VPWNormBitStyle.SAE:
                ifr_with_crc = True if pulse_width > 96.0e-6 else False
            else:  # GM
                ifr_with_crc = True if pulse_width <= 96.0e-6 else False

            es.advance_to_edge()  # Move to first bit

            # Get the IFR bits
            ifr_bits, ifr_bit_starts, pulse_width = collect_bits(es)
            if pulse_width > 280.0e-6 and es.cur_state() == 1:
                brk = J1850Break()
                sf = stream.StreamSegment((es.cur_time, es.next_states[0]),
                                          brk,
                                          kind='break')
                sf.annotate('frame', {'value': 'Break'},
                            stream.AnnotationFormat.String)
                yield sf
                continue

            # Validate IFR bits
            if len(ifr_bits) % 8 == 0 and len(ifr_bits) >= 8:
                # Convert IFR bits to bytes
                for i in xrange(len(ifr_bits) // 8):
                    ifr_bytes.append(join_bits(ifr_bits[i * 8:i * 8 + 8]))

                ifr_byte_starts = ifr_bit_starts[::8]

        sf = _build_j1850_record(bytes, ifr_bytes, byte_starts,
                                 ifr_byte_starts, ifr_with_crc,
                                 (frame_start, es.cur_time + 64.0e-6))
        yield sf
示例#10
0
def i2c_decode(scl,
               sda,
               logic_levels=None,
               stream_type=stream.StreamType.Samples):
    '''Decode an I2C data stream
    
    This is a generator function that can be used in a pipeline of waveform
    processing operations.

    The scl, and sda parameters are edge or sample streams.
    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    on the scl stream is consumed to determine the most likely logic levels in the signal.

    scl (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing the I2C serial clock

    sda (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing the I2C serial data

    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.

    stream_type (streaming.StreamType)
        Indicates the type of stream used for scl and sda.
        
        When StreamType.Samples, the iterators represent a sequence of samples.
        Each sample is a 2-tuple representing the time of the sample and the sample's
        value. When this type is used, the scl stream is analyzed to determine the
        logic levels of the two streams.
        
        When StreamType.Edges, the iterators represent a series of edges.
        scl and sda are iterables of 2-tuples representing each edge transition.
        The 2-tuples *must* be in the absolute time form (time, logic level).

    
    Yields a series of StreamRecord-based objects. These will be one of three event types
      or two data types. The three events are represented by StreamEvent object with these
      obj.kind attribute values:

        * 'I2C start'   The start of an I2C transfer
        * 'I2C restart' A start condition during a transfer
        * 'I2C stop'    The end of a transfer
        
      The two data types are represented by the objects I2CAddress and I2CByte. The former
      is a 7-bit or 10-bit address from the start of a transfer or restart. The latter contains
      the data read or written during the transfer. I2CByte has an attribute ack_bit that
      records the value of the ACK for that byte. I2CAddress has a r_wn attribute that indicates
      if the transfer is a read or write. The subrecords attribute contains the I2CByte object
      or objects that composed the address.
    
    Raises AutoLevelError when the stream_type is Samples and the logic levels cannot
      be determined automatically.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            s_scl_it, logic_levels = check_logic_levels(scl)
        else:
            s_scl_it = scl

        hyst = 0.4
        scl_it = find_edges(s_scl_it, logic_levels, hysteresis=hyst)
        sda_it = find_edges(sda, logic_levels, hysteresis=hyst)

    else:  # the streams are already lists of edges
        scl_it = scl
        sda_it = sda

    edge_sets = {'scl': scl_it, 'sda': sda_it}

    es = MultiEdgeSequence(edge_sets, 0.0)

    S_IDLE = 0
    S_ADDR = 1
    S_ADDR_10B = 2
    S_DATA = 3

    state = S_IDLE

    start_time = None
    end_time = None
    bits = []
    prev_10b_addr = None

    while not es.at_end():

        ts, cname = es.advance_to_edge()

        if state == S_IDLE:
            bits = []
            if cname == 'sda' and not es.at_end('sda') \
            and es.cur_state('sda') == 0 and es.cur_state('scl') == 1:
                # start condition met
                se = stream.StreamEvent(es.cur_time(),
                                        data=None,
                                        kind='I2C start')
                yield se
                state = S_ADDR

        else:
            # check for stop and restart
            if cname == 'sda' and not es.at_end('sda'):
                if es.cur_state('sda') == 1 and es.cur_state('scl') == 1:
                    # stop condition met
                    se = stream.StreamEvent(es.cur_time(),
                                            data=None,
                                            kind='I2C stop')
                    yield se
                    state = S_IDLE
                    continue

                if es.cur_state('sda') == 0 and es.cur_state('scl') == 1:
                    # restart condition met
                    se = stream.StreamEvent(es.cur_time(),
                                            data=None,
                                            kind='I2C restart')
                    yield se
                    bits = []
                    state = S_ADDR
                    continue

            if cname == 'scl' and not es.at_end('scl') and es.cur_state(
                    'scl') == 1:
                # rising edge of SCL

                # accumulate the bit
                if len(bits) == 0:
                    start_time = es.cur_time()

                bits.append(es.cur_state('sda'))
                end_time = es.cur_time()

                if len(bits) == 9:
                    word = join_bits(bits[0:8])
                    ack_bit = bits[8]

                    clock_period = (end_time - start_time) / 9.0
                    f_bound = (start_time - 0.4 * clock_period,
                               end_time + 0.4 * clock_period)
                    d_start = start_time - 0.25 * clock_period
                    d_bound = (d_start, d_start + 8.5 * clock_period)
                    a_bound = (end_time - 0.25 * clock_period,
                               end_time + 0.25 * clock_period)
                    a_status = stream.StreamStatus.Ok if ack_bit == 0 else stream.StreamStatus.Error

                    if state == S_ADDR:
                        addr = word >> 1
                        r_wn = word & 0x01
                        if addr > 0x77:  # first 2 bits of 10-bit address
                            if r_wn:  # 10-bit addressed read
                                # We will not receive the second byte of the address
                                # The 10-bit address being read should be the last one
                                # written to.
                                if prev_10b_addr is not None:
                                    addr_10b = prev_10b_addr

                                    # Check that the upper bits match
                                    ub = addr & 0x03
                                    prev_ub = (prev_10b_addr >> 8) & 0x03
                                    if ub != prev_ub:  # This shouldn't happen
                                        addr_10b = 0xFFF  # invalid address

                                else:  # This shouldn't happen
                                    addr_10b = 0xFFF  # invalid address

                                na = I2CAddress(f_bound, addr_10b, r_wn)
                                if addr_10b < 0xFFF:
                                    addr_text = '{:02X} {}'.format(
                                        addr_10b, 'r' if word & 0x01 else 'w')
                                else:  # Missing second address byte
                                    addr_text = '{:1X}?? {}'.format(
                                        addr & 0x03,
                                        'r' if word & 0x01 else 'w')
                                na.annotate('frame', {'value': addr_text},
                                            stream.AnnotationFormat.String)
                                na.subrecords.append(
                                    I2CByte(d_bound, word, ack_bit))
                                na.subrecords[-1].annotate(
                                    'addr', {'_bits': 8},
                                    stream.AnnotationFormat.Hidden)
                                na.subrecords.append(
                                    stream.StreamSegment(a_bound,
                                                         ack_bit,
                                                         kind='ack',
                                                         status=a_status))
                                na.subrecords[-1].annotate(
                                    'ack', {'_bits': 1},
                                    stream.AnnotationFormat.Hidden)

                                yield na
                                bits = []

                                state = S_DATA

                            else:  # 10-bit addressed write: first byte
                                first_addr = I2CByte(d_bound, word, ack_bit)
                                first_ack = stream.StreamSegment(
                                    a_bound,
                                    ack_bit,
                                    kind='ack',
                                    status=a_status)
                                bits = []
                                state = S_ADDR_10B

                        else:  # 7-bit address
                            r_wn = word & 0x01
                            na = I2CAddress(f_bound, addr, r_wn)
                            na.annotate('frame', {},
                                        stream.AnnotationFormat.Hidden)
                            na.subrecords.append(
                                I2CByte(d_bound, word, ack_bit))
                            addr_text = '{:02X} {}'.format(
                                word >> 1, 'r' if word & 0x01 else 'w')
                            na.subrecords[-1].annotate(
                                'addr', {
                                    'value': addr_text,
                                    '_bits': 8
                                }, stream.AnnotationFormat.Hex)
                            na.subrecords.append(
                                stream.StreamSegment(a_bound,
                                                     ack_bit,
                                                     kind='ack',
                                                     status=a_status))
                            na.subrecords[-1].annotate(
                                'ack', {'_bits': 1},
                                stream.AnnotationFormat.Hidden)

                            yield na
                            bits = []

                            state = S_DATA

                    elif state == S_ADDR_10B:  # 10-bit address
                        addr = (((first_addr.data >> 1) & 0x03) << 8) | word
                        r_wn = first_addr.data & 0x01
                        na = I2CAddress(
                            (first_addr.start_time - 0.4 * clock_period,
                             f_bound[1]), addr, r_wn)
                        addr_10b = ((
                            (first_addr.data * 256) >> 1) + word) & 0x3FF
                        addr_text = '{:02X} {}'.format(
                            addr_10b, 'r' if first_addr.data & 0x01 else 'w')
                        na.annotate('frame', {'value': addr_text},
                                    stream.AnnotationFormat.String)
                        na.subrecords.append(first_addr)
                        na.subrecords[-1].annotate(
                            'addr', {'_bits': 8},
                            stream.AnnotationFormat.Hidden)
                        na.subrecords.append(first_ack)
                        na.subrecords[-1].annotate(
                            'ack', {'_bits': 1},
                            stream.AnnotationFormat.Hidden)

                        na.subrecords.append(I2CByte(d_bound, word, ack_bit))
                        na.subrecords[-1].annotate(
                            'addr', {'_bits': 8},
                            stream.AnnotationFormat.Hidden)
                        na.subrecords.append(
                            stream.StreamSegment(a_bound,
                                                 ack_bit,
                                                 kind='ack',
                                                 status=a_status))
                        na.subrecords[-1].annotate(
                            'ack', {'_bits': 1},
                            stream.AnnotationFormat.Hidden)

                        prev_10b_addr = addr_10b
                        yield na
                        bits = []

                        state = S_DATA

                    else:  # S_DATA
                        nb = I2CByte(f_bound, word, ack_bit)
                        nb.annotate('frame', {},
                                    stream.AnnotationFormat.Hidden)
                        nb.subrecords.append(
                            stream.StreamSegment(d_bound, word, kind='data'))
                        nb.subrecords[-1].annotate('data', {'_bits': 8})
                        nb.subrecords.append(
                            stream.StreamSegment(a_bound,
                                                 ack_bit,
                                                 kind='ack',
                                                 status=a_status))
                        nb.subrecords[-1].annotate(
                            'ack', {'_bits': 1},
                            stream.AnnotationFormat.Hidden)

                        yield nb
                        bits = []
def sirc_decode(ir_stream, carrier_freq=40.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \
     stream_type=stream.StreamType.Samples):

    '''Decode Sony SIRC infrared protocol

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.
    
    ir_stream (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream of IR pulses. The type of stream is identified
        by the stream_type parameter. When this is a sample stream, an initial block
        of data is consumed to determine the most likely logic levels in the signal.
        This signal can be either modulated or demodulated.

    carrier_freq (float)
        The carrier frequency for modulation.

    polarity (infrared.IRConfig)
        Set the polarity (idle state high or low).
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the ir_stream parameter represents either Samples
        or Edges.
        
    Yields a series of SIRCStreamMessage objects.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            samp_it, logic_levels = decode.check_logic_levels(ir_stream)
        else:
            samp_it = ir_stream
        
        edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4)
    else: # the stream is already a list of edges
        edges = ir_stream

    # Demodulate signal (also passes an unmodulated signal without changes)
    edges = ir.demodulate(edges, carrier_freq, polarity)

    mod_period = (1.0 / carrier_freq) # Not really important. Just need a default value to pass to EdgeSequence
    es = decode.EdgeSequence(edges, mod_period)

    epsilon = 30.0e-6 # allow +/-30us variation for pulses and bit times
    time_is_nearly = functools.partial(ir.time_is_nearly, epsilon=epsilon)

    one_t = 600.0e-6 # 600us 1T time

    while not es.at_end():
        # Look for the falling edge of a start pulse
        if es.cur_state() == 0:
            es.advance_to_edge() # Now we're 1

        ts = es.advance_to_edge() # Now we're 0. Could be end of start pulse

        # Measure the time we skipped forward by
        if not time_is_nearly(ts, 4 * one_t):
            continue # Not a start pulse


        msg_start_time = es.cur_time - ts
        msg_bits = []
        bit_starts = []
        prev_edge = 0.0

        # Accumulate bits until idle for too long
        while True:
            prev_edge = es.cur_time
            ts = es.advance_to_edge()
            if not time_is_nearly(ts, one_t):
                break  # Not the beginning of a bit

            bit_starts.append(es.cur_time - ts)

            ts = es.advance_to_edge()
            if time_is_nearly(ts, one_t): # 0-bit
                msg_bits.append(0)

            elif time_is_nearly(ts, 2 * one_t): #1-bit
                msg_bits.append(1)

            else:
                break

        bit_starts.append(prev_edge) # End of last bit
        #print('### last bit:', es.cur_time)

        if len(msg_bits) in (12, 15, 20):
            cmd = join_bits(reversed(msg_bits[0:7]))
            cmd_range = (bit_starts[0], bit_starts[7])

            if len(msg_bits) == 12 or len(msg_bits) == 20:
                device = join_bits(reversed(msg_bits[7:12]))
                device_range = (bit_starts[7], bit_starts[12])
            else: # 15-bit command
                device = join_bits(reversed(msg_bits[7:15]))
                device_range = (bit_starts[7], bit_starts[15])

            extended = None
            if len(msg_bits) == 20: # 20-bit extended format
                extended = join_bits(reversed(msg_bits[12:20]))
                extended_range = (bit_starts[12], bit_starts[20])
                
            msg = SIRCMessage(cmd, device, extended)
            sm = SIRCStreamMessage((msg_start_time, prev_edge + 0.5*one_t), msg)
            sm.annotate('frame', {}, stream.AnnotationFormat.Hidden)

            cmd_ss = stream.StreamSegment((cmd_range[0], cmd_range[1]), cmd, kind='command')
            sm.subrecords.append(cmd_ss)
            sm.subrecords[-1].annotate('data', {'_bits':7})

            dev_ss = stream.StreamSegment((device_range[0], device_range[1]), device, kind='device')
            sm.subrecords.append(dev_ss)
            sm.subrecords[-1].annotate('addr')
            if len(msg_bits) == 15:
                sm.subrecords[-1].fields['_bits'] = 8
            else:
                sm.subrecords[-1].fields['_bits'] = 5

            if extended is not None:
                ext_ss = stream.StreamSegment((extended_range[0], extended_range[1]), extended, kind='extended')
                sm.subrecords.append(ext_ss)
                sm.subrecords[-1].annotate('data', {'_bits':8})


            yield sm
示例#12
0
def _ethernet_generic_decode(mstates, tag_ethertypes):
    '''Decode Manchester states into ethernet frames'''
    while True:
        try:
            cur_edge = next(mstates)
        except StopIteration:
            break

        if cur_edge[1] == ManchesterStates.High:
            # Possible link test pulse
            ltp_start = cur_edge[0]

            while True:
                try:
                    cur_edge = next(mstates)
                except StopIteration:
                    break

                if cur_edge[1] != ManchesterStates.High:
                    if 90.0e-9 < cur_edge[
                            0] - ltp_start < 110.0e-9:  # Pulse should be nominally 100ns wide
                        # Found a LTP
                        ltp = stream.StreamSegment((ltp_start, cur_edge[0]),
                                                   kind='LTP')
                        ltp.annotate('misc', {})
                        yield ltp
                    break

            continue

        elif cur_edge[1] not in (0, 1):
            continue

        frame_start = cur_edge[0]
        #print('## frame start:', frame_start)

        # Get preamble bits
        get_preamble = True
        prev_bit = cur_edge[1]
        preamble_count = 7 * 8 + 6 + 1
        # Get alternating 1's and 0's until we see a break in the pattern
        # that indicates we've reached the SFD.
        while preamble_count > 0:
            try:
                cur_edge = next(mstates)
            except StopIteration:
                break

            if cur_edge[1] != 1 - prev_bit:
                break

            prev_bit = cur_edge[1]
            preamble_count -= 1

        # Verify we have the SFD
        if not (prev_bit == 1 and cur_edge[1] == 1):
            # Restart search for a frame
            continue

        # Move to first bit of frame header
        try:
            cur_edge = next(mstates)
        except StopIteration:
            break
        header_start = cur_edge[0]

        frame_bits = []
        bit_start_times = []

        # Get all frame bits
        while cur_edge[1] in (0, 1):
            frame_bits.append(cur_edge[1])
            bit_start_times.append(cur_edge[0])
            try:
                cur_edge = next(mstates)
            except StopIteration:
                break

        crc_end_time = cur_edge[0]

        # Find end of frame
        while True:
            try:
                cur_edge = next(mstates)
            except StopIteration:
                break

            if cur_edge[1] == ManchesterStates.Idle:
                break

        end_time = cur_edge[0]

        #print('## got frame bits:', len(frame_bits))

        # Verify we have a multiple of 8 bits
        if len(frame_bits) % 8 != 0:
            continue

        # Verify we have the minimum of 64 bytes for a frame
        if len(frame_bits) < 64 * 8:
            continue

        # Convert bits to bytes
        frame_bytes = []
        for i in xrange(0, len(frame_bits), 8):
            frame_bytes.append(join_bits(reversed(frame_bits[i:i + 8])))

        byte_start_times = [t for t in bit_start_times[::8]]

        #print('## got bytes:', ['{:02x}'.format(b) for b in frame_bytes])

        # Create frame object
        if tag_ethertypes is None:
            tag_ethertypes = [0x8100, 0x88a8, 0x9100]

        tags = []

        lt_start = 12
        length_type = frame_bytes[lt_start] * 256 + frame_bytes[lt_start + 1]
        while length_type in tag_ethertypes:  # This is a tag
            tpid = length_type
            tci = frame_bytes[lt_start + 2] * 256 + frame_bytes[lt_start + 3]
            tags.append(EthernetTag(tpid, tci))

            lt_start += 4
            length_type = frame_bytes[lt_start] * 256 + frame_bytes[lt_start +
                                                                    1]

        if len(tags) == 0:  # No tags
            tags = None

        data_bytes = frame_bytes[lt_start + 2:-4]
        crc = 0
        for b in frame_bytes[-4:]:
            crc <<= 8
            crc += b
        ef = EthernetFrame(frame_bytes[0:6],
                           frame_bytes[6:12],
                           tags=tags,
                           length_type=length_type,
                           data=data_bytes,
                           crc=crc)

        status = EthernetStreamStatus.CRCError if not ef.crc_is_valid(
        ) else stream.StreamStatus.Ok
        sf = EthernetStreamFrame((frame_start, end_time), ef)

        # Annotate fields

        bounds = (byte_start_times[0], byte_start_times[6])
        sf.subrecords.append(
            stream.StreamSegment(bounds, str(ef.dest), kind='dest'))
        sf.subrecords[-1].annotate('addr', {'_bits': 48},
                                   stream.AnnotationFormat.Small)

        bounds = (byte_start_times[6], byte_start_times[12])
        sf.subrecords.append(
            stream.StreamSegment(bounds, str(ef.source), kind='source'))
        sf.subrecords[-1].annotate('addr', {'_bits': 48},
                                   stream.AnnotationFormat.Small)

        # Tags
        if tags is not None:
            for i, t in enumerate(tags):
                bounds = (byte_start_times[12 + 4 * i],
                          byte_start_times[12 + 4 * i + 4])
                sf.subrecords.append(
                    stream.StreamSegment(bounds, 'tag', kind='tag'))
                sf.subrecords[-1].annotate('ctrl', {},
                                           stream.AnnotationFormat.String)

        # Ethertype / length
        bounds = (byte_start_times[lt_start], byte_start_times[lt_start + 2])
        length_type = ef.length_type

        if length_type >= 0x600:
            kind = 'ethertype'
            if length_type in ethertypes:
                value = ethertypes[length_type]
            else:
                value = 'Unknown: {:04X}'.format(length_type)
            text_format = stream.AnnotationFormat.Small
        else:
            kind = 'length'
            value = length_type
            text_format = stream.AnnotationFormat.Int

        sf.subrecords.append(stream.StreamSegment(bounds, value, kind=kind))
        sf.subrecords[-1].annotate('ctrl', {'_bits': 16}, text_format)

        # Data
        bounds = (byte_start_times[lt_start + 2], byte_start_times[-4])
        sf.subrecords.append(
            stream.StreamSegment(bounds,
                                 'Payload, {} bytes'.format(len(data_bytes)),
                                 kind='data'))
        sf.subrecords[-1].annotate('data', {}, stream.AnnotationFormat.String)

        # CRC
        bounds = (byte_start_times[-4], crc_end_time)
        status = EthernetStreamStatus.CRCError if not ef.crc_is_valid(
        ) else stream.StreamStatus.Ok
        #print('## CRC bytes:', [hex(b) for b in frame_bytes[-4:]])
        sf.subrecords.append(
            stream.StreamSegment(bounds,
                                 frame_bytes[-4:],
                                 kind='CRC',
                                 status=status))
        sf.subrecords[-1].annotate('check', {}, stream.AnnotationFormat.Hex)

        yield sf
示例#13
0
def uart_decode(stream_data, bits=8, parity=None, stop_bits=1.0, lsb_first=True, polarity=UARTConfig.IdleHigh, \
    baud_rate=None, use_std_baud=True, logic_levels=None, stream_type=stream.StreamType.Samples, param_info=None):
    '''Decode a UART data stream

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.

    Sample streams are a sequence of SampleChunk Objects. Edge streams are a sequence
    of 2-tuples of (time, int) pairs. The type of stream is identified by the stream_type
    parameter. Sample streams will be analyzed to find edge transitions representing
    0 and 1 logic states of the waveforms. With sample streams, an initial block of data
    is consumed to determine the most likely logic levels in the signal.

    stream_data (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream representing a serial data signal.

    bits (int)
        The number of bits in each word. Typically 5, 7, 8, or 9.
    
    parity (string or None)
        The type of parity to use. One of None, 'even', or 'odd'
    
    stop_bits (number)
        The number of stop bits. Typically 1, 1.5, or 2
    
    lsb_first (bool)
        Flag indicating whether the Least Significant Bit is transmitted first.
    
    inverted (bool)
        Flag indicating if the signal levels have been inverted from their logical
        meaning. Use this when the input stream derives from an inverting driver such
        as those used for RS-232.
    polarity (UARTConfig)
        Set the polarity (idle state high or low).
    
    baud_rate (int)
        The baud rate of the stream. If None, the first 50 edges will be analyzed to
        automatically determine the most likely baud rate for the stream. On average
        50 edges will occur after 11 frames have been captured.
    
    use_std_baud (bool)
        Flag that forces coercion of automatically detected baud rate to the set of
        standard rates
        
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the stream parameter represents either Samples
        or Edges
        
    param_info (dict or None)
        An optional dictionary object that is used to monitor the results of
        automatic baud detection.

        
    Yields a series of UARTFrame objects. Each frame contains subrecords marking the location
      of sub-elements within the frame (start, data, parity, stop). Parity errors are recorded
      as an error status in the parity subrecord. BRK conditions are reported as a data value
      0x00 with a framing error in the status code.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
      
    Raises AutoBaudError if auto-baud is active and the baud rate cannot
      be determined.
      
    Raises ValueError if the parity argument is invalid.
    '''

    bits = int(bits)

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            samp_it, logic_levels = check_logic_levels(stream_data)
        else:
            samp_it = stream_data

        edges = find_edges(samp_it, logic_levels, hysteresis=0.4)
    else:  # the stream is already a list of edges
        edges = stream_data

    raw_symbol_rate = 0

    if baud_rate is None:
        # Find the baud rate

        # tee off an independent iterator to determine baud rate
        edges_it, sre_it = itertools.tee(edges)

        # Experiments on random data indicate that find_symbol_rate() will almost
        # always converge to a close estimate of baud rate within the first 35 edges.
        # It seems to be a guarantee after 50 edges (pathological cases not withstanding).
        min_edges = 50
        symbol_rate_edges = itertools.islice(sre_it, min_edges)

        # We need to ensure that we can pull out enough edges from the iterator slice
        # Just consume them all for a count
        sre_list = list(symbol_rate_edges)
        if len(sre_list) < min_edges:
            raise AutoBaudError(
                'Unable to compute automatic baud rate. Insufficient edges.')

        raw_symbol_rate = find_symbol_rate(iter(sre_list), spectra=2)

        if raw_symbol_rate == 0:
            # Some special data sequences may lack a second harmonic which
            # ruins the HPS used in find_symbol_rate().

            # In this case we bypass the HPS and just take the symbol rate using the dominant span
            raw_symbol_rate = find_symbol_rate(iter(sre_list), spectra=1)

        # delete the tee'd iterators so that the internal buffer will not grow
        # as the edges_it is advanced later on
        del symbol_rate_edges
        del sre_it

        std_bauds = (110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, \
                     56000, 57600, 115200, 128000, 153600, 230400, 256000, 460800, 921600)

        if use_std_baud:
            # find the standard baud closest to the raw rate
            baud_rate = min(std_bauds, key=lambda x: abs(x - raw_symbol_rate))
        else:
            baud_rate = raw_symbol_rate

        #print('@@@@@@@@@@ baud rate:', baud_rate, raw_symbol_rate)

        if baud_rate == 0:
            raise AutoBaudError(
                'Unable to compute automatic baud rate. Got 0.')

    else:
        edges_it = edges

    # Invert edge polarity if idle-low
    if polarity == UARTConfig.IdleLow:
        edges_it = ((t, 1 - e) for t, e in edges_it)

    if param_info is not None:
        param_info['baud_rate'] = baud_rate
        param_info['raw_symbol_rate'] = raw_symbol_rate
        if stream_type == stream.StreamType.Samples:
            param_info['logic_levels'] = logic_levels

    bit_period = 1.0 / float(baud_rate)
    es = EdgeSequence(edges_it, bit_period)

    # Now we start the actual decode process

    mark = 1
    space = 0

    # initialize to point where state is 'mark' --> idle time before first start bit
    while es.cur_state() == space and not es.at_end():
        es.advance_to_edge()

    #print('start bit', es.cur_time)

    while not es.at_end():
        # look for start bit falling edge
        es.advance_to_edge()
        #print('### advance:', es.cur_time)

        # We could have an anamolous edge at the end of the edge list
        # Check if edge sequence is complete after our advance
        if es.at_end():
            break

        # We should be at the start of the start bit (space)
        # If not then the previous character was likely a BRK condition and
        # we just now returned to idle (mark).
        if es.cur_state() != space:
            continue

        start_time = es.cur_time
        data_time = es.cur_time + bit_period
        es.advance(bit_period * 1.5)  # move to middle of first data bit

        byte = 0
        cur_bit = 0

        p = 0
        if parity is not None:
            if parity.lower() == 'even':
                p = 0
            elif parity.lower() == 'odd':
                p = 1
            else:
                raise ValueError('Invalid parity argument')

        while cur_bit < bits:
            bit_val = es.cur_state()

            p ^= bit_val
            if lsb_first:
                byte = byte >> 1 | (bit_val << (bits - 1))
            else:
                byte = byte << 1 | bit_val

            cur_bit += 1
            es.advance()
            #print(es.cur_time)

        data_end_time = es.cur_time - bit_period * 0.5
        parity_error = False
        if parity is not None:
            parity_time = data_end_time
            parity_val = es.cur_state()
            #print('PB:', p, parity_val)
            # check the parity
            if parity_val != p:
                parity_error = True
            es.advance()

        # We are currently 1/2 bit past the last data or parity bit
        stop_time = es.cur_time - bit_period * 0.5

        # Verify the stop bit(s)
        if stop_bits > 1.0:
            # Move to within 1/2 bit of the end of the stop bits
            es.advance(bit_period * (stop_bits - 1.0))

        framing_error = False
        if es.cur_state() != mark:  # Not at idle -> break condition
            framing_error = True

        end_time = es.cur_time + bit_period * 0.5

        # construct frame objects
        status = UARTStreamStatus.FramingError if framing_error else stream.StreamStatus.Ok
        nf = UARTFrame((start_time, end_time), byte, status=status)
        nf.annotate('frame', {}, stream.AnnotationFormat.Hidden)

        nf.subrecords.append(
            stream.StreamSegment((start_time, data_time), kind='start bit'))
        nf.subrecords[-1].annotate('misc', {'_bits': 1},
                                   stream.AnnotationFormat.Invisible)
        nf.subrecords.append(
            stream.StreamSegment((data_time, data_end_time),
                                 byte,
                                 kind='data bits'))
        nf.subrecords[-1].annotate('data', {'_bits': bits},
                                   stream.AnnotationFormat.General)
        if parity is not None:
            status = UARTStreamStatus.ParityError if parity_error else stream.StreamStatus.Ok
            nf.subrecords.append(
                stream.StreamSegment((parity_time, stop_time),
                                     kind='parity',
                                     status=status))
            nf.subrecords[-1].annotate('check', {'_bits': 1},
                                       stream.AnnotationFormat.General)

        nf.subrecords.append(
            stream.StreamSegment((stop_time, end_time), kind='stop bit'))
        nf.subrecords[-1].annotate('misc', {'_bits': stop_bits},
                                   stream.AnnotationFormat.Invisible)

        yield nf
def nec_decode(ir_stream, carrier_freq=38.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \
     stream_type=stream.StreamType.Samples):
    '''Decode NEC infrared protocol

    This is a generator function that can be used in a pipeline of waveform
    procesing operations.
    
    ir_stream (iterable of SampleChunk objects or (float, int) pairs)
        A sample stream or edge stream of IR pulses. The type of stream is identified
        by the stream_type parameter. When this is a sample stream, an initial block
        of data is consumed to determine the most likely logic levels in the signal.
        This signal can be either modulated or demodulated.

    carrier_freq (float)
        The carrier frequency for modulation.

    polarity (infrared.IRConfig)
        Set the polarity (idle state high or low).
    
    logic_levels ((float, float) or None)
        Optional pair that indicates (low, high) logic levels of the sample
        stream. When present, auto level detection is disabled. This has no effect on
        edge streams.
    
    stream_type (streaming.StreamType)
        A StreamType value indicating that the ir_stream parameter represents either Samples
        or Edges.
        
    Yields a series of NECStreamMessage objects.
      
    Raises AutoLevelError if stream_type = Samples and the logic levels cannot
      be determined.
    '''

    if stream_type == stream.StreamType.Samples:
        if logic_levels is None:
            samp_it, logic_levels = decode.check_logic_levels(ir_stream)
        else:
            samp_it = ir_stream

        edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4)
    else:  # the stream is already a list of edges
        edges = ir_stream

    # Demodulate signal (also passes an unmodulated signal without changes)
    edges = ir.demodulate(edges, carrier_freq, polarity)

    mod_period = (
        1.0 / carrier_freq
    )  # Not really important. Just need a default value to pass to EdgeSequence
    es = decode.EdgeSequence(edges, mod_period)

    epsilon = 30.0e-6  # Allow +/-30us variation for pulses and bit times
    time_is_nearly = functools.partial(ir.time_is_nearly, epsilon=epsilon)
    time_is_at_least = functools.partial(ir.time_is_at_least, epsilon=epsilon)

    while not es.at_end():
        # Look for the falling edge of an AGC burst
        if es.cur_state() == 0:
            es.advance_to_edge()  # Now we're 1

        es.advance_to_edge()  # Now we're 0. Could be end of AGC
        ts = es.advance_to_edge()  # Start of next pulse

        # Measure the time we skipped forward by
        if time_is_nearly(ts, 2.25e-3) or time_is_nearly(ts, 4.5e-3):
            # Previous pulse was AGC (gap between 2.25ms and 4.5ms)
            msg_start_time = es.cur_time - ts - 9.0e-3

            if time_is_at_least(ts, 4.5e-3):  # command message

                msg_bits = []
                bit_starts = []

                while len(msg_bits) < 32:
                    bit_start_time = es.cur_time

                    ts = es.advance_to_edge()
                    if time_is_nearly(ts, 560.0e-6):  # 560us bit pulse time
                        # Measure next time gap to determine if bit is 1 or 0
                        ts = es.advance_to_edge()
                        bit_period = es.cur_time - bit_start_time
                        if time_is_nearly(bit_period, 2.25e-3):  # 1-bit
                            msg_bits.append(1)
                            bit_starts.append(bit_start_time)

                        if time_is_nearly(bit_period, 1.12e-3):  # 0-bit
                            msg_bits.append(0)
                            bit_starts.append(bit_start_time)
                    else:
                        break

                if len(msg_bits) == 32:
                    bit_starts.append(es.cur_time)  # End of last byte

                    # Check for the stop bit
                    ts = es.advance_to_edge()
                    if time_is_nearly(ts, 560.0e-6):  # 560us stop pulse time
                        # Valid command message

                        msg_bytes = [
                            join_bits(reversed(msg_bits[i:i + 8]))
                            for i in xrange(0, 32, 8)
                        ]
                        m_bounds = [(bit_starts[i], bit_starts[i + 8])
                                    for i in xrange(0, 32, 8)]

                        addr_low = msg_bytes[0]
                        addr_high = msg_bytes[1]
                        cmd = msg_bytes[2]
                        cmd_inv = msg_bytes[3]

                        nec_msg = NECMessage(cmd, addr_low, addr_high, cmd_inv)
                        sm = NECStreamMessage((msg_start_time, es.cur_time),
                                              nec_msg)
                        sm.annotate('frame', {},
                                    stream.AnnotationFormat.Hidden)

                        sm.subrecords.append(
                            stream.StreamSegment(
                                (m_bounds[0][0], m_bounds[0][1]),
                                addr_low,
                                kind='addr-low'))
                        sm.subrecords[-1].annotate('addr', {'_bits': 8})
                        sm.subrecords.append(
                            stream.StreamSegment(
                                (m_bounds[1][0], m_bounds[1][1]),
                                addr_high,
                                kind='addr-high'))
                        sm.subrecords[-1].annotate('addr', {'_bits': 8})
                        sm.subrecords.append(
                            stream.StreamSegment(
                                (m_bounds[2][0], m_bounds[2][1]),
                                cmd,
                                kind='cmd'))
                        sm.subrecords[-1].annotate('data', {'_bits': 8})

                        status = stream.StreamStatus.Ok if cmd == (
                            ~cmd_inv) & 0xFF else stream.StreamStatus.Error
                        sm.subrecords.append(
                            stream.StreamSegment(
                                (m_bounds[3][0], m_bounds[3][1]),
                                cmd_inv,
                                kind='cmd-inv',
                                status=status))
                        sm.subrecords[-1].annotate('check', {'_bits': 8})

                        yield sm

            else:  # repeat message
                # Check for the stop bit
                ts = es.advance_to_edge()
                if time_is_nearly(ts, 560.0e-6):  # 560us stop pulse time
                    # Valid repeat message
                    sm = NECStreamMessage((msg_start_time, es.cur_time),
                                          NECRepeat())
                    sm.annotate('frame', {'name': ''},
                                stream.AnnotationFormat.String)
                    yield sm