]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
ir_nec: fix #1243, multiple capture frames.
[libsigrokdecode.git] / decoders / ir_nec / pd.py
CommitLineData
5e6fa9cc
GY
1##
2## This file is part of the libsigrokdecode project.
3##
4## Copyright (C) 2014 Gump Yang <gump.yang@gmail.com>
5##
6## This program is free software; you can redistribute it and/or modify
7## it under the terms of the GNU General Public License as published by
8## the Free Software Foundation; either version 2 of the License, or
9## (at your option) any later version.
10##
11## This program is distributed in the hope that it will be useful,
12## but WITHOUT ANY WARRANTY; without even the implied warranty of
13## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14## GNU General Public License for more details.
15##
16## You should have received a copy of the GNU General Public License
4539e9ca 17## along with this program; if not, see <http://www.gnu.org/licenses/>.
5e6fa9cc
GY
18##
19
20import sigrokdecode as srd
12fecc8f 21from .lists import *
5e6fa9cc 22
21cda951
UH
23class SamplerateError(Exception):
24 pass
25
5e6fa9cc 26class Decoder(srd.Decoder):
5844bb0f 27 api_version = 3
00962e76
UH
28 id = 'ir_nec'
29 name = 'IR NEC'
30 longname = 'IR NEC'
31 desc = 'NEC infrared remote control protocol.'
5e6fa9cc
GY
32 license = 'gplv2+'
33 inputs = ['logic']
6cbba91f 34 outputs = []
d6d8a8a4 35 tags = ['IR']
6a15597a 36 channels = (
5e6fa9cc 37 {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
bee57ee8
UH
38 )
39 options = (
40 {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
41 'values': ('active-low', 'active-high')},
fb1870b0 42 {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
bee57ee8
UH
43 )
44 annotations = (
45 ('bit', 'Bit'),
46 ('agc-pulse', 'AGC pulse'),
47 ('longpause', 'Long pause'),
48 ('shortpause', 'Short pause'),
49 ('stop-bit', 'Stop bit'),
50 ('leader-code', 'Leader code'),
51 ('addr', 'Address'),
52 ('addr-inv', 'Address#'),
53 ('cmd', 'Command'),
54 ('cmd-inv', 'Command#'),
55 ('repeat-code', 'Repeat code'),
56 ('remote', 'Remote'),
57 ('warnings', 'Warnings'),
58 )
5e6fa9cc 59 annotation_rows = (
70835fd4
UH
60 ('bits', 'Bits', (0, 1, 2, 3, 4)),
61 ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
12fecc8f
UH
62 ('remote', 'Remote', (11,)),
63 ('warnings', 'Warnings', (12,)),
5e6fa9cc
GY
64 )
65
5e6fa9cc
GY
66 def putx(self, data):
67 self.put(self.ss_start, self.samplenum, self.out_ann, data)
68
69 def putb(self, data):
70 self.put(self.ss_bit, self.samplenum, self.out_ann, data)
71
70835fd4
UH
72 def putd(self, data):
73 name = self.state.title()
74 d = {'ADDRESS': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
75 s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
76 'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
77 self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
78 '%s: 0x%02X' % (s[self.state][0], data),
79 '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
80
81 def putstop(self, ss):
82 self.put(ss, ss + self.stop, self.out_ann,
83 [4, ['Stop bit', 'Stop', 'St', 'S']])
84
85 def putpause(self, p):
86 self.put(self.ss_start, self.ss_other_edge, self.out_ann,
87 [1, ['AGC pulse', 'AGC', 'A']])
88 idx = 2 if p == 'Long' else 3
89 self.put(self.ss_other_edge, self.samplenum, self.out_ann,
90 [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
91
12fecc8f
UH
92 def putremote(self):
93 dev = address.get(self.addr, 'Unknown device')
94 buttons = command.get(self.addr, None)
95 if buttons is None:
96 btn = ['Unknown', 'Unk']
97 else:
98 btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
99 self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
100 [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
101 '%s' % btn[1]]])
102
92b7b49f 103 def __init__(self):
10aeb8ea
GS
104 self.reset()
105
106 def reset(self):
5e6fa9cc 107 self.state = 'IDLE'
12fecc8f 108 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
5844bb0f 109 self.data = self.count = self.active = None
12fecc8f 110 self.addr = self.cmd = None
5e6fa9cc
GY
111
112 def start(self):
5e6fa9cc 113 self.out_ann = self.register(srd.OUTPUT_ANN)
5844bb0f 114
5e6fa9cc
GY
115 def metadata(self, key, value):
116 if key == srd.SRD_CONF_SAMPLERATE:
117 self.samplerate = value
82ea183f 118 self.tolerance = 0.05 # +/-5%
73fc79e0
UH
119 self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
120 self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
121 self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
122 self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
70835fd4 123 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
5e6fa9cc 124
82ea183f
GM
125 def compare_with_tolerance(self, measured, base):
126 return (measured >= base * (1 - self.tolerance)
127 and measured <= base * (1 + self.tolerance))
128
70835fd4 129 def handle_bit(self, tick):
5bb61a25 130 ret = None
82ea183f 131 if self.compare_with_tolerance(tick, self.dazero):
5e6fa9cc 132 ret = 0
82ea183f 133 elif self.compare_with_tolerance(tick, self.daone):
5e6fa9cc 134 ret = 1
5bb61a25 135 if ret in (0, 1):
5e6fa9cc 136 self.putb([0, ['%d' % ret]])
5bb61a25 137 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 138 self.count = self.count + 1
5e6fa9cc 139 self.ss_bit = self.samplenum
5e6fa9cc 140
70835fd4 141 def data_ok(self):
73fc79e0 142 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 143 if self.count == 8:
12fecc8f
UH
144 if self.state == 'ADDRESS':
145 self.addr = self.data
146 if self.state == 'COMMAND':
147 self.cmd = self.data
70835fd4
UH
148 self.putd(self.data)
149 self.ss_start = self.samplenum
150 return True
5e6fa9cc 151 if ret == 0:
5bb61a25 152 self.putd(self.data >> 8)
5e6fa9cc 153 else:
12fecc8f 154 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
155 self.data = self.count = 0
156 self.ss_bit = self.ss_start = self.samplenum
70835fd4 157 return ret == 0
5e6fa9cc 158
5844bb0f 159 def decode(self):
21cda951
UH
160 if not self.samplerate:
161 raise SamplerateError('Cannot decode without samplerate.')
fb1870b0
GS
162
163 cd_count = None
164 if self.options['cd_freq']:
165 cd_count = int(self.samplerate / self.options['cd_freq']) + 1
1865f48d
PM
166 prev_ir = None
167
168 self.active = 0 if self.options['polarity'] == 'active-low' else 1
fb1870b0 169
5844bb0f 170 while True:
fb1870b0
GS
171 # Detect changes in the presence of an active input signal.
172 # The decoder can either be fed an already filtered RX signal
173 # or optionally can detect the presence of a carrier. Periods
174 # of inactivity (signal changes slower than the carrier freq,
175 # if specified) pass on the most recently sampled level. This
176 # approach works for filtered and unfiltered input alike, and
177 # only slightly extends the active phase of input signals with
178 # carriers included by one period of the carrier frequency.
179 # IR based communication protocols can cope with this slight
180 # inaccuracy just fine by design. Enabling carrier detection
181 # on already filtered signals will keep the length of their
182 # active period, but will shift their signal changes by one
183 # carrier period before they get passed to decoding logic.
184 if cd_count:
185 (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
186 if self.matched[0]:
187 cur_ir = self.active
188 if cur_ir == prev_ir:
189 continue
190 prev_ir = cur_ir
191 self.ir = cur_ir
192 else:
193 (self.ir,) = self.wait({0: 'e'})
00962e76 194
70835fd4 195 if self.ir != self.active:
5844bb0f 196 # Save the non-active edge, then wait for the next edge.
70835fd4 197 self.ss_other_edge = self.samplenum
5e6fa9cc
GY
198 continue
199
73fc79e0
UH
200 b = self.samplenum - self.ss_bit
201
202 # State machine.
203 if self.state == 'IDLE':
82ea183f 204 if self.compare_with_tolerance(b, self.lc):
70835fd4
UH
205 self.putpause('Long')
206 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
12fecc8f 207 self.ss_remote = self.ss_start
73fc79e0
UH
208 self.data = self.count = 0
209 self.state = 'ADDRESS'
82ea183f 210 elif self.compare_with_tolerance(b, self.rc):
70835fd4
UH
211 self.putpause('Short')
212 self.putstop(self.samplenum)
213 self.samplenum += self.stop
214 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
73fc79e0
UH
215 self.data = self.count = 0
216 self.ss_bit = self.ss_start = self.samplenum
217 elif self.state == 'ADDRESS':
70835fd4
UH
218 self.handle_bit(b)
219 if self.count == 8:
220 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
221 elif self.state == 'ADDRESS#':
222 self.handle_bit(b)
223 if self.count == 16:
224 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
73fc79e0 225 elif self.state == 'COMMAND':
70835fd4
UH
226 self.handle_bit(b)
227 if self.count == 8:
228 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
229 elif self.state == 'COMMAND#':
230 self.handle_bit(b)
231 if self.count == 16:
232 self.state = 'STOP' if self.data_ok() else 'IDLE'
233 elif self.state == 'STOP':
234 self.putstop(self.ss_bit)
12fecc8f 235 self.putremote()
70835fd4
UH
236 self.ss_bit = self.ss_start = self.samplenum
237 self.state = 'IDLE'