2 ## This file is part of the libsigrokdecode project.
4 ## Copyright (C) 2014 Gump Yang <gump.yang@gmail.com>
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.
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.
16 ## You should have received a copy of the GNU General Public License
17 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
20 import sigrokdecode as srd
23 class SamplerateError(Exception):
26 class Decoder(srd.Decoder):
31 desc = 'NEC infrared remote control protocol.'
37 {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
40 {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
41 'values': ('active-low', 'active-high')},
42 {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
46 ('agc-pulse', 'AGC pulse'),
47 ('longpause', 'Long pause'),
48 ('shortpause', 'Short pause'),
49 ('stop-bit', 'Stop bit'),
50 ('leader-code', 'Leader code'),
52 ('addr-inv', 'Address#'),
54 ('cmd-inv', 'Command#'),
55 ('repeat-code', 'Repeat code'),
57 ('warnings', 'Warnings'),
60 ('bits', 'Bits', (0, 1, 2, 3, 4)),
61 ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
62 ('remote', 'Remote', (11,)),
63 ('warnings', 'Warnings', (12,)),
67 self.put(self.ss_start, self.samplenum, self.out_ann, data)
70 self.put(self.ss_bit, self.samplenum, self.out_ann, 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]]])
81 def putstop(self, ss):
82 self.put(ss, ss + self.stop, self.out_ann,
83 [4, ['Stop bit', 'Stop', 'St', 'S']])
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']])
93 dev = address.get(self.addr, 'Unknown device')
94 buttons = command.get(self.addr, None)
96 btn = ['Unknown', 'Unk']
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]),
108 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
109 self.data = self.count = self.active = None
110 self.addr = self.cmd = None
113 self.out_ann = self.register(srd.OUTPUT_ANN)
115 def metadata(self, key, value):
116 if key == srd.SRD_CONF_SAMPLERATE:
117 self.samplerate = value
118 self.tolerance = 0.05 # +/-5%
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
123 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
125 def compare_with_tolerance(self, measured, base):
126 return (measured >= base * (1 - self.tolerance)
127 and measured <= base * (1 + self.tolerance))
129 def handle_bit(self, tick):
131 if self.compare_with_tolerance(tick, self.dazero):
133 elif self.compare_with_tolerance(tick, self.daone):
136 self.putb([0, ['%d' % ret]])
137 self.data |= (ret << self.count) # LSB-first
138 self.count = self.count + 1
139 self.ss_bit = self.samplenum
142 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
144 if self.state == 'ADDRESS':
145 self.addr = self.data
146 if self.state == 'COMMAND':
149 self.ss_start = self.samplenum
152 self.putd(self.data >> 8)
154 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
155 self.data = self.count = 0
156 self.ss_bit = self.ss_start = self.samplenum
160 if not self.samplerate:
161 raise SamplerateError('Cannot decode without samplerate.')
164 if self.options['cd_freq']:
165 cd_count = int(self.samplerate / self.options['cd_freq']) + 1
168 self.active = 0 if self.options['polarity'] == 'active-low' else 1
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.
185 (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
188 if cur_ir == prev_ir:
193 (self.ir,) = self.wait({0: 'e'})
195 if self.ir != self.active:
196 # Save the non-active edge, then wait for the next edge.
197 self.ss_other_edge = self.samplenum
200 b = self.samplenum - self.ss_bit
203 if self.state == 'IDLE':
204 if self.compare_with_tolerance(b, self.lc):
205 self.putpause('Long')
206 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
207 self.ss_remote = self.ss_start
208 self.data = self.count = 0
209 self.state = 'ADDRESS'
210 elif self.compare_with_tolerance(b, self.rc):
211 self.putpause('Short')
212 self.putstop(self.samplenum)
213 self.samplenum += self.stop
214 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
215 self.data = self.count = 0
216 self.ss_bit = self.ss_start = self.samplenum
217 elif self.state == 'ADDRESS':
220 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
221 elif self.state == 'ADDRESS#':
224 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
225 elif self.state == 'COMMAND':
228 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
229 elif self.state == 'COMMAND#':
232 self.state = 'STOP' if self.data_ok() else 'IDLE'
233 elif self.state == 'STOP':
234 self.putstop(self.ss_bit)
236 self.ss_bit = self.ss_start = self.samplenum