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.'
36 {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
39 {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
40 'values': ('active-low', 'active-high')},
41 {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
45 ('agc-pulse', 'AGC pulse'),
46 ('longpause', 'Long pause'),
47 ('shortpause', 'Short pause'),
48 ('stop-bit', 'Stop bit'),
49 ('leader-code', 'Leader code'),
51 ('addr-inv', 'Address#'),
53 ('cmd-inv', 'Command#'),
54 ('repeat-code', 'Repeat code'),
56 ('warnings', 'Warnings'),
59 ('bits', 'Bits', (0, 1, 2, 3, 4)),
60 ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
61 ('remote', 'Remote', (11,)),
62 ('warnings', 'Warnings', (12,)),
66 self.put(self.ss_start, self.samplenum, self.out_ann, data)
69 self.put(self.ss_bit, self.samplenum, self.out_ann, data)
72 name = self.state.title()
73 d = {'ADDRESS': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
74 s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
75 'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
76 self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
77 '%s: 0x%02X' % (s[self.state][0], data),
78 '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
80 def putstop(self, ss):
81 self.put(ss, ss + self.stop, self.out_ann,
82 [4, ['Stop bit', 'Stop', 'St', 'S']])
84 def putpause(self, p):
85 self.put(self.ss_start, self.ss_other_edge, self.out_ann,
86 [1, ['AGC pulse', 'AGC', 'A']])
87 idx = 2 if p == 'Long' else 3
88 self.put(self.ss_other_edge, self.samplenum, self.out_ann,
89 [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
92 dev = address.get(self.addr, 'Unknown device')
93 buttons = command.get(self.addr, None)
95 btn = ['Unknown', 'Unk']
97 btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
98 self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
99 [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
107 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
108 self.data = self.count = self.active = None
109 self.addr = self.cmd = None
112 self.out_ann = self.register(srd.OUTPUT_ANN)
113 self.active = 0 if self.options['polarity'] == 'active-low' else 1
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
169 # Detect changes in the presence of an active input signal.
170 # The decoder can either be fed an already filtered RX signal
171 # or optionally can detect the presence of a carrier. Periods
172 # of inactivity (signal changes slower than the carrier freq,
173 # if specified) pass on the most recently sampled level. This
174 # approach works for filtered and unfiltered input alike, and
175 # only slightly extends the active phase of input signals with
176 # carriers included by one period of the carrier frequency.
177 # IR based communication protocols can cope with this slight
178 # inaccuracy just fine by design. Enabling carrier detection
179 # on already filtered signals will keep the length of their
180 # active period, but will shift their signal changes by one
181 # carrier period before they get passed to decoding logic.
183 (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
186 if cur_ir == prev_ir:
191 (self.ir,) = self.wait({0: 'e'})
193 if self.ir != self.active:
194 # Save the non-active edge, then wait for the next edge.
195 self.ss_other_edge = self.samplenum
198 b = self.samplenum - self.ss_bit
201 if self.state == 'IDLE':
202 if self.compare_with_tolerance(b, self.lc):
203 self.putpause('Long')
204 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
205 self.ss_remote = self.ss_start
206 self.data = self.count = 0
207 self.state = 'ADDRESS'
208 elif self.compare_with_tolerance(b, self.rc):
209 self.putpause('Short')
210 self.putstop(self.samplenum)
211 self.samplenum += self.stop
212 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
213 self.data = self.count = 0
214 self.ss_bit = self.ss_start = self.samplenum
215 elif self.state == 'ADDRESS':
218 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
219 elif self.state == 'ADDRESS#':
222 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
223 elif self.state == 'COMMAND':
226 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
227 elif self.state == 'COMMAND#':
230 self.state = 'STOP' if self.data_ok() else 'IDLE'
231 elif self.state == 'STOP':
232 self.putstop(self.ss_bit)
234 self.ss_bit = self.ss_start = self.samplenum