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)
114 self.active = 0 if self.options['polarity'] == 'active-low' else 1
116 def metadata(self, key, value):
117 if key == srd.SRD_CONF_SAMPLERATE:
118 self.samplerate = value
119 self.tolerance = 0.05 # +/-5%
120 self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
121 self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
122 self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
123 self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
124 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
126 def compare_with_tolerance(self, measured, base):
127 return (measured >= base * (1 - self.tolerance)
128 and measured <= base * (1 + self.tolerance))
130 def handle_bit(self, tick):
132 if self.compare_with_tolerance(tick, self.dazero):
134 elif self.compare_with_tolerance(tick, self.daone):
137 self.putb([0, ['%d' % ret]])
138 self.data |= (ret << self.count) # LSB-first
139 self.count = self.count + 1
140 self.ss_bit = self.samplenum
143 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
145 if self.state == 'ADDRESS':
146 self.addr = self.data
147 if self.state == 'COMMAND':
150 self.ss_start = self.samplenum
153 self.putd(self.data >> 8)
155 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
156 self.data = self.count = 0
157 self.ss_bit = self.ss_start = self.samplenum
161 if not self.samplerate:
162 raise SamplerateError('Cannot decode without samplerate.')
165 if self.options['cd_freq']:
166 cd_count = int(self.samplerate / self.options['cd_freq']) + 1
170 # Detect changes in the presence of an active input signal.
171 # The decoder can either be fed an already filtered RX signal
172 # or optionally can detect the presence of a carrier. Periods
173 # of inactivity (signal changes slower than the carrier freq,
174 # if specified) pass on the most recently sampled level. This
175 # approach works for filtered and unfiltered input alike, and
176 # only slightly extends the active phase of input signals with
177 # carriers included by one period of the carrier frequency.
178 # IR based communication protocols can cope with this slight
179 # inaccuracy just fine by design. Enabling carrier detection
180 # on already filtered signals will keep the length of their
181 # active period, but will shift their signal changes by one
182 # carrier period before they get passed to decoding logic.
184 (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
187 if cur_ir == prev_ir:
192 (self.ir,) = self.wait({0: 'e'})
194 if self.ir != self.active:
195 # Save the non-active edge, then wait for the next edge.
196 self.ss_other_edge = self.samplenum
199 b = self.samplenum - self.ss_bit
202 if self.state == 'IDLE':
203 if self.compare_with_tolerance(b, self.lc):
204 self.putpause('Long')
205 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
206 self.ss_remote = self.ss_start
207 self.data = self.count = 0
208 self.state = 'ADDRESS'
209 elif self.compare_with_tolerance(b, self.rc):
210 self.putpause('Short')
211 self.putstop(self.samplenum)
212 self.samplenum += self.stop
213 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
214 self.data = self.count = 0
215 self.ss_bit = self.ss_start = self.samplenum
216 elif self.state == 'ADDRESS':
219 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
220 elif self.state == 'ADDRESS#':
223 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
224 elif self.state == 'COMMAND':
227 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
228 elif self.state == 'COMMAND#':
231 self.state = 'STOP' if self.data_ok() else 'IDLE'
232 elif self.state == 'STOP':
233 self.putstop(self.ss_bit)
235 self.ss_bit = self.ss_start = self.samplenum