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. 3
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. 4
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
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