]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
ir_nec: Make timing margin relative
[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):
5e6fa9cc 102 self.state = 'IDLE'
12fecc8f 103 self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
5844bb0f 104 self.data = self.count = self.active = None
12fecc8f 105 self.addr = self.cmd = None
5e6fa9cc
GY
106
107 def start(self):
5e6fa9cc 108 self.out_ann = self.register(srd.OUTPUT_ANN)
73fc79e0 109 self.active = 0 if self.options['polarity'] == 'active-low' else 1
5844bb0f 110
5e6fa9cc
GY
111 def metadata(self, key, value):
112 if key == srd.SRD_CONF_SAMPLERATE:
113 self.samplerate = value
82ea183f 114 self.tolerance = 0.05 # +/-5%
73fc79e0
UH
115 self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
116 self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
117 self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
118 self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
70835fd4 119 self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
5e6fa9cc 120
82ea183f
GM
121 def compare_with_tolerance(self, measured, base):
122 return (measured >= base * (1 - self.tolerance)
123 and measured <= base * (1 + self.tolerance))
124
70835fd4 125 def handle_bit(self, tick):
5bb61a25 126 ret = None
82ea183f 127 if self.compare_with_tolerance(tick, self.dazero):
5e6fa9cc 128 ret = 0
82ea183f 129 elif self.compare_with_tolerance(tick, self.daone):
5e6fa9cc 130 ret = 1
5bb61a25 131 if ret in (0, 1):
5e6fa9cc 132 self.putb([0, ['%d' % ret]])
5bb61a25 133 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 134 self.count = self.count + 1
5e6fa9cc 135 self.ss_bit = self.samplenum
5e6fa9cc 136
70835fd4 137 def data_ok(self):
73fc79e0 138 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 139 if self.count == 8:
12fecc8f
UH
140 if self.state == 'ADDRESS':
141 self.addr = self.data
142 if self.state == 'COMMAND':
143 self.cmd = self.data
70835fd4
UH
144 self.putd(self.data)
145 self.ss_start = self.samplenum
146 return True
5e6fa9cc 147 if ret == 0:
5bb61a25 148 self.putd(self.data >> 8)
5e6fa9cc 149 else:
12fecc8f 150 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
151 self.data = self.count = 0
152 self.ss_bit = self.ss_start = self.samplenum
70835fd4 153 return ret == 0
5e6fa9cc 154
5844bb0f 155 def decode(self):
21cda951
UH
156 if not self.samplerate:
157 raise SamplerateError('Cannot decode without samplerate.')
5844bb0f
UH
158 while True:
159 # Wait for any edge (rising or falling).
160 (self.ir,) = self.wait({0: 'e'})
00962e76 161
70835fd4 162 if self.ir != self.active:
5844bb0f 163 # Save the non-active edge, then wait for the next edge.
70835fd4 164 self.ss_other_edge = self.samplenum
5e6fa9cc
GY
165 continue
166
73fc79e0
UH
167 b = self.samplenum - self.ss_bit
168
169 # State machine.
170 if self.state == 'IDLE':
82ea183f 171 if self.compare_with_tolerance(b, self.lc):
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'
82ea183f 177 elif self.compare_with_tolerance(b, self.rc):
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'