def rc5_decode(ir_stream, carrier_freq=36.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \ stream_type=stream.StreamType.Samples): '''Decode RC5 infrared protocol This is a generator function that can be used in a pipeline of waveform procesing operations. ir_stream (iterable of SampleChunk objects or (float, int) pairs) A sample stream or edge stream of IR pulses. The type of stream is identified by the stream_type parameter. When this is a sample stream, an initial block of data is consumed to determine the most likely logic levels in the signal. This signal can be either modulated or demodulated. carrier_freq (float) The carrier frequency for modulation. polarity (infrared.IRConfig) Set the polarity (idle state high or low). logic_levels ((float, float) or None) Optional pair that indicates (low, high) logic levels of the sample stream. When present, auto level detection is disabled. This has no effect on edge streams. stream_type (streaming.StreamType) A StreamType value indicating that the ir_stream parameter represents either Samples or Edges. Yields a series of RC5StreamMessage objects. Raises AutoLevelError if stream_type = Samples and the logic levels cannot be determined. ''' if stream_type == stream.StreamType.Samples: if logic_levels is None: samp_it, logic_levels = decode.check_logic_levels(ir_stream) else: samp_it = ir_stream edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4) else: # the stream is already a list of edges edges = ir_stream # Demodulate signal (also passes an unmodulated signal without changes) edges = ir.demodulate(edges, carrier_freq, polarity) pulse_width = 889.0e-6 # 889us pulse width es = decode.EdgeSequence(edges, pulse_width) while not es.at_end(): # Look for the rising edge of a pulse if es.cur_state() == 0: es.advance_to_edge() # Now we're 1 msg_start_time = es.cur_time - pulse_width # Move ahead half a pulse and check that we are still 1 es.advance(pulse_width / 2.0) if es.cur_state() != 1: continue # Advance through edge sequence recording state until we see 3 0's or 3 1's in a row # This indicates a break in the Manchester encoding. coded_bits = [0, 1] bit_starts = [0.0, 0.0] same_count = 1 prev_state = 1 while True: es.advance() coded_bits.append(es.cur_state()) bit_starts.append(es.cur_time - pulse_width / 2.0) if es.cur_state() == prev_state: same_count += 1 else: same_count = 1 if same_count > 2: break prev_state = es.cur_state() msg_end_time = es.cur_time - pulse_width if len(coded_bits) >= 14 * 2: # Decode Manchester # The second bit of each pair is the same as the decoded bit msg_bits = coded_bits[1:28:2] mb_starts = bit_starts[::2] #print('$$$ coded_bits:', coded_bits) #print('$$$ msg_bits:', msg_bits) toggle = msg_bits[2] addr = join_bits(msg_bits[3:8]) cmd = join_bits([0 if msg_bits[1] else 1] + msg_bits[8:14]) msg = RC5Message(cmd, addr, toggle) sm = RC5StreamMessage((msg_start_time, msg_end_time), msg) sm.annotate('frame', {'name': 'frame'}, stream.AnnotationFormat.Hidden) sm.subrecords.append( stream.StreamSegment((mb_starts[2], mb_starts[3]), toggle, kind='toggle')) sm.subrecords[-1].annotate('data', {'_bits': 1}, stream.AnnotationFormat.Int) sm.subrecords.append( stream.StreamSegment((mb_starts[3], mb_starts[8]), addr, kind='addr')) sm.subrecords[-1].annotate('addr', {'_bits': 5}) sm.subrecords.append( stream.StreamSegment((mb_starts[8], mb_starts[14]), cmd, kind='cmd')) sm.subrecords[-1].annotate('data', {'_bits': 6}) if cmd > 63: # Extended format; 7-th bit is just after first start bit sm.subrecords[-1].fields['_bits'] = 7 sm.subrecords.append( stream.StreamSegment((mb_starts[1], mb_starts[2]), msg_bits[1], kind='cmd bit-7')) sm.subrecords[-1].annotate('data1', {'_bits': 1}, stream.AnnotationFormat.Int) yield sm
def rc6_decode(ir_stream, carrier_freq=36.0e3, polarity=ir.IRConfig.IdleLow, logic_levels=None, \ stream_type=stream.StreamType.Samples): '''Decode RC6 infrared protocol This is a generator function that can be used in a pipeline of waveform procesing operations. ir_stream (iterable of SampleChunk objects or (float, int) pairs) A sample stream or edge stream of IR pulses. The type of stream is identified by the stream_type parameter. When this is a sample stream, an initial block of data is consumed to determine the most likely logic levels in the signal. This signal can be either modulated or demodulated. carrier_freq (float) The carrier frequency for modulation. polarity (infrared.IRConfig) Set the polarity (idle state high or low). logic_levels ((float, float) or None) Optional pair that indicates (low, high) logic levels of the sample stream. When present, auto level detection is disabled. This has no effect on edge streams. stream_type (streaming.StreamType) A StreamType value indicating that the ir_stream parameter represents either Samples or Edges. Yields a series of RC6StreamMessage objects. Raises AutoLevelError if stream_type = Samples and the logic levels cannot be determined. ''' if stream_type == stream.StreamType.Samples: if logic_levels is None: samp_it, logic_levels = decode.check_logic_levels(ir_stream) else: samp_it = ir_stream edges = decode.find_edges(samp_it, logic_levels, hysteresis=0.4) else: # the stream is already a list of edges edges = ir_stream # Demodulate signal (also passes an unmodulated signal without changes) edges = ir.demodulate(edges, carrier_freq, polarity) pulse_width = 444.0e-6 # 444us pulse width es = decode.EdgeSequence(edges, pulse_width) epsilon = 30.0e-6 # allow +/-30us variation for pulses and bit times time_is_nearly = functools.partial(ir.time_is_nearly, epsilon=epsilon) while not es.at_end(): # Look for the rising edge of a pulse if es.cur_state() == 0: es.advance_to_edge() # Now we're 1 msg_start_time = es.cur_time ts = es.advance_to_edge() if not time_is_nearly(ts, 6 * pulse_width): continue # This is not the leading AGC pulse of a message ts = es.advance_to_edge() if not time_is_nearly(ts, 2 * pulse_width): continue # This is not the leading AGC pulse of a message # Move ahead half a pulse and check that we are still 1 es.advance(pulse_width / 2.0) if es.cur_state() != 1: continue # Not a valid start bit es.advance() # End of start bit # Advance through edge sequence recording state until we see 4 0's or 4 1's in a row # This indicates a break in the Manchester encoding. coded_bits = [1, 0] # Start bit bit_starts = [0.0, 0.0] same_count = 1 prev_state = 1 while True: es.advance() coded_bits.append(es.cur_state()) bit_starts.append(es.cur_time - pulse_width / 2.0) if es.cur_state() == prev_state: same_count += 1 else: same_count = 1 if same_count > 3: break prev_state = es.cur_state() msg_end_time = es.cur_time - 2.5 * pulse_width #print('$$$ found bits:', len(coded_bits)) if len(coded_bits) >= 22 * 2: # Decode Manchester # The first bit of each pair is the same as the decoded bit msg_bits = coded_bits[0:44:2] mb_starts = bit_starts[::2] mode = join_bits(msg_bits[1:4]) toggle = msg_bits[4] if mode == 6: # RC6A message if msg_bits[6]: # 15-bit customer field msg_bits = coded_bits[0:76:2] customer = join_bits(msg_bits[7:22]) asb = 22 # addr start bit index else: # 7-bit customer field msg_bits = coded_bits[0:60:2] customer = join_bits(msg_bits[7:14]) asb = 14 else: # RC6 message customer = None asb = 6 #print('$$$ coded_bits:', coded_bits) #print('$$$ msg_bits:', msg_bits) addr = join_bits(msg_bits[asb:asb+8]) cmd = join_bits(msg_bits[asb+8:asb+16]) msg = RC6Message(cmd, addr, toggle, mode, customer) sm = RC6StreamMessage((msg_start_time, msg_end_time), msg) sm.annotate('frame', {'name':'frame'}, stream.AnnotationFormat.Hidden) sm.subrecords.append(stream.StreamSegment((mb_starts[1], mb_starts[4]), mode, kind='mode')) sm.subrecords[-1].annotate('addr', {'_bits':3}) sm.subrecords.append(stream.StreamSegment((mb_starts[4], mb_starts[6]), toggle, kind='toggle')) sm.subrecords[-1].annotate('data', {'_bits':1}, stream.AnnotationFormat.Int) if mode == 6: sm.subrecords.append(stream.StreamSegment((mb_starts[6], mb_starts[asb]), customer, kind='customer')) sm.subrecords[-1].annotate('data', {'_bits':7 + msg_bits[6]*8}) sm.subrecords.append(stream.StreamSegment((mb_starts[asb], mb_starts[asb+8]), addr, kind='addr')) sm.subrecords[-1].annotate('addr', {'_bits':8}) sm.subrecords.append(stream.StreamSegment((mb_starts[asb+8], mb_starts[asb+16]), cmd, kind='cmd')) sm.subrecords[-1].annotate('data', {'_bits':8}) yield sm
def 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 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