]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
decoders: Add/update tags for each PD.
[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']
00962e76 34 outputs = ['ir_nec']
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)
73fc79e0 114 self.active = 0 if self.options['polarity'] == 'active-low' else 1
5844bb0f 115
5e6fa9cc
GY
116 def metadata(self, key, value):
117 if key == srd.SRD_CONF_SAMPLERATE:
118 self.samplerate = value
82ea183f 119 self.tolerance = 0.05 # +/-5%
73fc79e0
UH
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
70835fd4 124 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
5e6fa9cc 125
82ea183f
GM
126 def compare_with_tolerance(self, measured, base):
127 return (measured >= base * (1 - self.tolerance)
128 and measured <= base * (1 + self.tolerance))
129
70835fd4 130 def handle_bit(self, tick):
5bb61a25 131 ret = None
82ea183f 132 if self.compare_with_tolerance(tick, self.dazero):
5e6fa9cc 133 ret = 0
82ea183f 134 elif self.compare_with_tolerance(tick, self.daone):
5e6fa9cc 135 ret = 1
5bb61a25 136 if ret in (0, 1):
5e6fa9cc 137 self.putb([0, ['%d' % ret]])
5bb61a25 138 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 139 self.count = self.count + 1
5e6fa9cc 140 self.ss_bit = self.samplenum
5e6fa9cc 141
70835fd4 142 def data_ok(self):
73fc79e0 143 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 144 if self.count == 8:
12fecc8f
UH
145 if self.state == 'ADDRESS':
146 self.addr = self.data
147 if self.state == 'COMMAND':
148 self.cmd = self.data
70835fd4
UH
149 self.putd(self.data)
150 self.ss_start = self.samplenum
151 return True
5e6fa9cc 152 if ret == 0:
5bb61a25 153 self.putd(self.data >> 8)
5e6fa9cc 154 else:
12fecc8f 155 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
156 self.data = self.count = 0
157 self.ss_bit = self.ss_start = self.samplenum
70835fd4 158 return ret == 0
5e6fa9cc 159
5844bb0f 160 def decode(self):
21cda951
UH
161 if not self.samplerate:
162 raise SamplerateError('Cannot decode without samplerate.')
fb1870b0
GS
163
164 cd_count = None
165 if self.options['cd_freq']:
166 cd_count = int(self.samplerate / self.options['cd_freq']) + 1
167 prev_ir = None
168
5844bb0f 169 while True:
fb1870b0
GS
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.
183 if cd_count:
184 (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
185 if self.matched[0]:
186 cur_ir = self.active
187 if cur_ir == prev_ir:
188 continue
189 prev_ir = cur_ir
190 self.ir = cur_ir
191 else:
192 (self.ir,) = self.wait({0: 'e'})
00962e76 193
70835fd4 194 if self.ir != self.active:
5844bb0f 195 # Save the non-active edge, then wait for the next edge.
70835fd4 196 self.ss_other_edge = self.samplenum
5e6fa9cc
GY
197 continue
198
73fc79e0
UH
199 b = self.samplenum - self.ss_bit
200
201 # State machine.
202 if self.state == 'IDLE':
82ea183f 203 if self.compare_with_tolerance(b, self.lc):
70835fd4
UH
204 self.putpause('Long')
205 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
12fecc8f 206 self.ss_remote = self.ss_start
73fc79e0
UH
207 self.data = self.count = 0
208 self.state = 'ADDRESS'
82ea183f 209 elif self.compare_with_tolerance(b, self.rc):
70835fd4
UH
210 self.putpause('Short')
211 self.putstop(self.samplenum)
212 self.samplenum += self.stop
213 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
73fc79e0
UH
214 self.data = self.count = 0
215 self.ss_bit = self.ss_start = self.samplenum
216 elif self.state == 'ADDRESS':
70835fd4
UH
217 self.handle_bit(b)
218 if self.count == 8:
219 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
220 elif self.state == 'ADDRESS#':
221 self.handle_bit(b)
222 if self.count == 16:
223 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
73fc79e0 224 elif self.state == 'COMMAND':
70835fd4
UH
225 self.handle_bit(b)
226 if self.count == 8:
227 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
228 elif self.state == 'COMMAND#':
229 self.handle_bit(b)
230 if self.count == 16:
231 self.state = 'STOP' if self.data_ok() else 'IDLE'
232 elif self.state == 'STOP':
233 self.putstop(self.ss_bit)
12fecc8f 234 self.putremote()
70835fd4
UH
235 self.ss_bit = self.ss_start = self.samplenum
236 self.state = 'IDLE'