]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
all decoders: introduce a reset() method
[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']
6a15597a 35 channels = (
5e6fa9cc 36 {'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
bee57ee8
UH
37 )
38 options = (
39 {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
40 'values': ('active-low', 'active-high')},
41 )
42 annotations = (
43 ('bit', 'Bit'),
44 ('agc-pulse', 'AGC pulse'),
45 ('longpause', 'Long pause'),
46 ('shortpause', 'Short pause'),
47 ('stop-bit', 'Stop bit'),
48 ('leader-code', 'Leader code'),
49 ('addr', 'Address'),
50 ('addr-inv', 'Address#'),
51 ('cmd', 'Command'),
52 ('cmd-inv', 'Command#'),
53 ('repeat-code', 'Repeat code'),
54 ('remote', 'Remote'),
55 ('warnings', 'Warnings'),
56 )
5e6fa9cc 57 annotation_rows = (
70835fd4
UH
58 ('bits', 'Bits', (0, 1, 2, 3, 4)),
59 ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
12fecc8f
UH
60 ('remote', 'Remote', (11,)),
61 ('warnings', 'Warnings', (12,)),
5e6fa9cc
GY
62 )
63
5e6fa9cc
GY
64 def putx(self, data):
65 self.put(self.ss_start, self.samplenum, self.out_ann, data)
66
67 def putb(self, data):
68 self.put(self.ss_bit, self.samplenum, self.out_ann, data)
69
70835fd4
UH
70 def putd(self, data):
71 name = self.state.title()
72 d = {'ADDRESS': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
73 s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
74 'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
75 self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
76 '%s: 0x%02X' % (s[self.state][0], data),
77 '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
78
79 def putstop(self, ss):
80 self.put(ss, ss + self.stop, self.out_ann,
81 [4, ['Stop bit', 'Stop', 'St', 'S']])
82
83 def putpause(self, p):
84 self.put(self.ss_start, self.ss_other_edge, self.out_ann,
85 [1, ['AGC pulse', 'AGC', 'A']])
86 idx = 2 if p == 'Long' else 3
87 self.put(self.ss_other_edge, self.samplenum, self.out_ann,
88 [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
89
12fecc8f
UH
90 def putremote(self):
91 dev = address.get(self.addr, 'Unknown device')
92 buttons = command.get(self.addr, None)
93 if buttons is None:
94 btn = ['Unknown', 'Unk']
95 else:
96 btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
97 self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
98 [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
99 '%s' % btn[1]]])
100
92b7b49f 101 def __init__(self):
10aeb8ea
GS
102 self.reset()
103
104 def reset(self):
5e6fa9cc 105 self.state = 'IDLE'
12fecc8f 106 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
5844bb0f 107 self.data = self.count = self.active = None
12fecc8f 108 self.addr = self.cmd = None
5e6fa9cc
GY
109
110 def start(self):
5e6fa9cc 111 self.out_ann = self.register(srd.OUTPUT_ANN)
73fc79e0 112 self.active = 0 if self.options['polarity'] == 'active-low' else 1
5844bb0f 113
5e6fa9cc
GY
114 def metadata(self, key, value):
115 if key == srd.SRD_CONF_SAMPLERATE:
116 self.samplerate = value
82ea183f 117 self.tolerance = 0.05 # +/-5%
73fc79e0
UH
118 self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
119 self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
120 self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
121 self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
70835fd4 122 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
5e6fa9cc 123
82ea183f
GM
124 def compare_with_tolerance(self, measured, base):
125 return (measured >= base * (1 - self.tolerance)
126 and measured <= base * (1 + self.tolerance))
127
70835fd4 128 def handle_bit(self, tick):
5bb61a25 129 ret = None
82ea183f 130 if self.compare_with_tolerance(tick, self.dazero):
5e6fa9cc 131 ret = 0
82ea183f 132 elif self.compare_with_tolerance(tick, self.daone):
5e6fa9cc 133 ret = 1
5bb61a25 134 if ret in (0, 1):
5e6fa9cc 135 self.putb([0, ['%d' % ret]])
5bb61a25 136 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 137 self.count = self.count + 1
5e6fa9cc 138 self.ss_bit = self.samplenum
5e6fa9cc 139
70835fd4 140 def data_ok(self):
73fc79e0 141 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 142 if self.count == 8:
12fecc8f
UH
143 if self.state == 'ADDRESS':
144 self.addr = self.data
145 if self.state == 'COMMAND':
146 self.cmd = self.data
70835fd4
UH
147 self.putd(self.data)
148 self.ss_start = self.samplenum
149 return True
5e6fa9cc 150 if ret == 0:
5bb61a25 151 self.putd(self.data >> 8)
5e6fa9cc 152 else:
12fecc8f 153 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
154 self.data = self.count = 0
155 self.ss_bit = self.ss_start = self.samplenum
70835fd4 156 return ret == 0
5e6fa9cc 157
5844bb0f 158 def decode(self):
21cda951
UH
159 if not self.samplerate:
160 raise SamplerateError('Cannot decode without samplerate.')
5844bb0f
UH
161 while True:
162 # Wait for any edge (rising or falling).
163 (self.ir,) = self.wait({0: 'e'})
00962e76 164
70835fd4 165 if self.ir != self.active:
5844bb0f 166 # Save the non-active edge, then wait for the next edge.
70835fd4 167 self.ss_other_edge = self.samplenum
5e6fa9cc
GY
168 continue
169
73fc79e0
UH
170 b = self.samplenum - self.ss_bit
171
172 # State machine.
173 if self.state == 'IDLE':
82ea183f 174 if self.compare_with_tolerance(b, self.lc):
70835fd4
UH
175 self.putpause('Long')
176 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
12fecc8f 177 self.ss_remote = self.ss_start
73fc79e0
UH
178 self.data = self.count = 0
179 self.state = 'ADDRESS'
82ea183f 180 elif self.compare_with_tolerance(b, self.rc):
70835fd4
UH
181 self.putpause('Short')
182 self.putstop(self.samplenum)
183 self.samplenum += self.stop
184 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
73fc79e0
UH
185 self.data = self.count = 0
186 self.ss_bit = self.ss_start = self.samplenum
187 elif self.state == 'ADDRESS':
70835fd4
UH
188 self.handle_bit(b)
189 if self.count == 8:
190 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
191 elif self.state == 'ADDRESS#':
192 self.handle_bit(b)
193 if self.count == 16:
194 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
73fc79e0 195 elif self.state == 'COMMAND':
70835fd4
UH
196 self.handle_bit(b)
197 if self.count == 8:
198 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
199 elif self.state == 'COMMAND#':
200 self.handle_bit(b)
201 if self.count == 16:
202 self.state = 'STOP' if self.data_ok() else 'IDLE'
203 elif self.state == 'STOP':
204 self.putstop(self.ss_bit)
12fecc8f 205 self.putremote()
70835fd4
UH
206 self.ss_bit = self.ss_start = self.samplenum
207 self.state = 'IDLE'