Esempio n. 1
0
    def test_usb_crc16(self):
        import ripyl.util.bitops as bitops
        self.test_name = 'USB CRC-16'
        self.trial_count = 1000
        for i in xrange(self.trial_count):
            self.update_progress(i+1)

            data_size = random.randint(1,30)
            data = [random.randint(0,255) for _ in xrange(data_size)]

            b_data = []
            for d in data:
                b_data.extend(reversed(bitops.split_bits(d, 8)))

            crc16 = bitops.join_bits(reversed(usb.usb_crc16(b_data)))
            tcrc16 = bitops.join_bits(reversed(usb.table_usb_crc16(data)))

            if crc16 != tcrc16:
                print('\nMismatch: {}, {}, {},  {}'.format(hex(crc16), hex(tcrc16), hex(ncrc16), data))

            self.assertEqual(crc16, tcrc16, 'CRC-16 mismatch')
    def test_usb_crc16(self):
        import ripyl.util.bitops as bitops
        self.test_name = 'USB CRC-16'
        self.trial_count = 1000
        for i in xrange(self.trial_count):
            self.update_progress(i + 1)

            data_size = random.randint(1, 30)
            data = [random.randint(0, 255) for _ in xrange(data_size)]

            b_data = []
            for d in data:
                b_data.extend(reversed(bitops.split_bits(d, 8)))

            crc16 = bitops.join_bits(reversed(usb.usb_crc16(b_data)))
            tcrc16 = bitops.join_bits(reversed(usb.table_usb_crc16(data)))

            if crc16 != tcrc16:
                print('\nMismatch: {}, {}, {},  {}'.format(
                    hex(crc16), hex(tcrc16), hex(ncrc16), data))

            self.assertEqual(crc16, tcrc16, 'CRC-16 mismatch')
def lin_pid(id):
    '''Generate a LIN PID from an ID

    id (int)
        The ID to generate parity for

    Returns the PID as an int.
    '''

    p0 = reduce(operator.xor, split_bits(id & _p0_mask, 6))
    p1 = reduce(operator.xor, split_bits(id & _p1_mask, 6)) ^ 0x01

    return join_bits([p1, p0] + split_bits(id, 6))
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
Esempio n. 6
0
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
Esempio n. 7
0
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
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
Esempio n. 10
0
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
Esempio n. 11
0
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
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
Esempio n. 13
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
Esempio n. 14
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
Esempio n. 15
0
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