]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
PD tests moved into the sigrok-test repo.
[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
17## along with this program; if not, write to the Free Software
18## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19##
20
21import sigrokdecode as srd
12fecc8f 22from .lists import *
5e6fa9cc 23
21cda951
UH
24class SamplerateError(Exception):
25 pass
26
5e6fa9cc 27class Decoder(srd.Decoder):
12851357 28 api_version = 2
00962e76
UH
29 id = 'ir_nec'
30 name = 'IR NEC'
31 longname = 'IR NEC'
32 desc = 'NEC infrared remote control protocol.'
5e6fa9cc
GY
33 license = 'gplv2+'
34 inputs = ['logic']
00962e76 35 outputs = ['ir_nec']
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')},
42 )
43 annotations = (
44 ('bit', 'Bit'),
45 ('agc-pulse', 'AGC pulse'),
46 ('longpause', 'Long pause'),
47 ('shortpause', 'Short pause'),
48 ('stop-bit', 'Stop bit'),
49 ('leader-code', 'Leader code'),
50 ('addr', 'Address'),
51 ('addr-inv', 'Address#'),
52 ('cmd', 'Command'),
53 ('cmd-inv', 'Command#'),
54 ('repeat-code', 'Repeat code'),
55 ('remote', 'Remote'),
56 ('warnings', 'Warnings'),
57 )
5e6fa9cc 58 annotation_rows = (
70835fd4
UH
59 ('bits', 'Bits', (0, 1, 2, 3, 4)),
60 ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
12fecc8f
UH
61 ('remote', 'Remote', (11,)),
62 ('warnings', 'Warnings', (12,)),
5e6fa9cc
GY
63 )
64
5e6fa9cc
GY
65 def putx(self, data):
66 self.put(self.ss_start, self.samplenum, self.out_ann, data)
67
68 def putb(self, data):
69 self.put(self.ss_bit, self.samplenum, self.out_ann, data)
70
70835fd4
UH
71 def putd(self, 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]]])
79
80 def putstop(self, ss):
81 self.put(ss, ss + self.stop, self.out_ann,
82 [4, ['Stop bit', 'Stop', 'St', 'S']])
83
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']])
90
12fecc8f
UH
91 def putremote(self):
92 dev = address.get(self.addr, 'Unknown device')
93 buttons = command.get(self.addr, None)
94 if buttons is None:
95 btn = ['Unknown', 'Unk']
96 else:
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]),
100 '%s' % btn[1]]])
101
5e6fa9cc 102 def __init__(self, **kwargs):
5e6fa9cc 103 self.state = 'IDLE'
12fecc8f 104 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
73fc79e0 105 self.data = self.count = self.active = self.old_ir = None
12fecc8f 106 self.addr = self.cmd = None
5e6fa9cc
GY
107
108 def start(self):
5e6fa9cc 109 self.out_ann = self.register(srd.OUTPUT_ANN)
73fc79e0
UH
110 self.active = 0 if self.options['polarity'] == 'active-low' else 1
111 self.old_ir = 1 if self.active == 0 else 0
5e6fa9cc
GY
112
113 def metadata(self, key, value):
114 if key == srd.SRD_CONF_SAMPLERATE:
115 self.samplerate = value
73fc79e0
UH
116 self.margin = int(self.samplerate * 0.0001) - 1 # 0.1ms
117 self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
118 self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
119 self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
120 self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
70835fd4 121 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
5e6fa9cc 122
70835fd4 123 def handle_bit(self, tick):
5bb61a25 124 ret = None
00962e76 125 if tick in range(self.dazero - self.margin, self.dazero + self.margin):
5e6fa9cc 126 ret = 0
00962e76 127 elif tick in range(self.daone - self.margin, self.daone + self.margin):
5e6fa9cc 128 ret = 1
5bb61a25 129 if ret in (0, 1):
5e6fa9cc 130 self.putb([0, ['%d' % ret]])
5bb61a25 131 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 132 self.count = self.count + 1
5e6fa9cc 133 self.ss_bit = self.samplenum
5e6fa9cc 134
70835fd4 135 def data_ok(self):
73fc79e0 136 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 137 if self.count == 8:
12fecc8f
UH
138 if self.state == 'ADDRESS':
139 self.addr = self.data
140 if self.state == 'COMMAND':
141 self.cmd = self.data
70835fd4
UH
142 self.putd(self.data)
143 self.ss_start = self.samplenum
144 return True
5e6fa9cc 145 if ret == 0:
5bb61a25 146 self.putd(self.data >> 8)
5e6fa9cc 147 else:
12fecc8f 148 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
149 self.data = self.count = 0
150 self.ss_bit = self.ss_start = self.samplenum
70835fd4 151 return ret == 0
5e6fa9cc
GY
152
153 def decode(self, ss, es, data):
21cda951
UH
154 if not self.samplerate:
155 raise SamplerateError('Cannot decode without samplerate.')
5e6fa9cc
GY
156 for (self.samplenum, pins) in data:
157 self.ir = pins[0]
00962e76 158
70835fd4
UH
159 # Wait for an "interesting" edge, but also record the other ones.
160 if self.old_ir == self.ir:
161 continue
162 if self.ir != self.active:
163 self.ss_other_edge = self.samplenum
73fc79e0 164 self.old_ir = self.ir
5e6fa9cc
GY
165 continue
166
73fc79e0
UH
167 b = self.samplenum - self.ss_bit
168
169 # State machine.
170 if self.state == 'IDLE':
171 if b in range(self.lc - self.margin, self.lc + self.margin):
70835fd4
UH
172 self.putpause('Long')
173 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
12fecc8f 174 self.ss_remote = self.ss_start
73fc79e0
UH
175 self.data = self.count = 0
176 self.state = 'ADDRESS'
177 elif b in range(self.rc - self.margin, self.rc + self.margin):
70835fd4
UH
178 self.putpause('Short')
179 self.putstop(self.samplenum)
180 self.samplenum += self.stop
181 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
73fc79e0
UH
182 self.data = self.count = 0
183 self.ss_bit = self.ss_start = self.samplenum
184 elif self.state == 'ADDRESS':
70835fd4
UH
185 self.handle_bit(b)
186 if self.count == 8:
187 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
188 elif self.state == 'ADDRESS#':
189 self.handle_bit(b)
190 if self.count == 16:
191 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
73fc79e0 192 elif self.state == 'COMMAND':
70835fd4
UH
193 self.handle_bit(b)
194 if self.count == 8:
195 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
196 elif self.state == 'COMMAND#':
197 self.handle_bit(b)
198 if self.count == 16:
199 self.state = 'STOP' if self.data_ok() else 'IDLE'
200 elif self.state == 'STOP':
201 self.putstop(self.ss_bit)
12fecc8f 202 self.putremote()
70835fd4
UH
203 self.ss_bit = self.ss_start = self.samplenum
204 self.state = 'IDLE'
5e6fa9cc
GY
205
206 self.old_ir = self.ir