]> sigrok.org Git - libsigrokdecode.git/blame - decoders/ir_nec/pd.py
license: remove FSF postal address from boiler plate license text
[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
UH
110
111 # Set the initial (assumed) value of the pin as per user-config.
112 self.initial_pins = [1 if self.active == 0 else 0]
5e6fa9cc
GY
113
114 def metadata(self, key, value):
115 if key == srd.SRD_CONF_SAMPLERATE:
116 self.samplerate = value
73fc79e0
UH
117 self.margin = int(self.samplerate * 0.0001) - 1 # 0.1ms
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
70835fd4 124 def handle_bit(self, tick):
5bb61a25 125 ret = None
00962e76 126 if tick in range(self.dazero - self.margin, self.dazero + self.margin):
5e6fa9cc 127 ret = 0
00962e76 128 elif tick in range(self.daone - self.margin, self.daone + self.margin):
5e6fa9cc 129 ret = 1
5bb61a25 130 if ret in (0, 1):
5e6fa9cc 131 self.putb([0, ['%d' % ret]])
5bb61a25 132 self.data |= (ret << self.count) # LSB-first
5e6fa9cc 133 self.count = self.count + 1
5e6fa9cc 134 self.ss_bit = self.samplenum
5e6fa9cc 135
70835fd4 136 def data_ok(self):
73fc79e0 137 ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
70835fd4 138 if self.count == 8:
12fecc8f
UH
139 if self.state == 'ADDRESS':
140 self.addr = self.data
141 if self.state == 'COMMAND':
142 self.cmd = self.data
70835fd4
UH
143 self.putd(self.data)
144 self.ss_start = self.samplenum
145 return True
5e6fa9cc 146 if ret == 0:
5bb61a25 147 self.putd(self.data >> 8)
5e6fa9cc 148 else:
12fecc8f 149 self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
5e6fa9cc
GY
150 self.data = self.count = 0
151 self.ss_bit = self.ss_start = self.samplenum
70835fd4 152 return ret == 0
5e6fa9cc 153
5844bb0f 154 def decode(self):
21cda951
UH
155 if not self.samplerate:
156 raise SamplerateError('Cannot decode without samplerate.')
5844bb0f
UH
157 while True:
158 # Wait for any edge (rising or falling).
159 (self.ir,) = self.wait({0: 'e'})
00962e76 160
70835fd4 161 if self.ir != self.active:
5844bb0f 162 # Save the non-active edge, then wait for the next edge.
70835fd4 163 self.ss_other_edge = self.samplenum
5e6fa9cc
GY
164 continue
165
73fc79e0
UH
166 b = self.samplenum - self.ss_bit
167
168 # State machine.
169 if self.state == 'IDLE':
170 if b in range(self.lc - self.margin, self.lc + self.margin):
70835fd4
UH
171 self.putpause('Long')
172 self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
12fecc8f 173 self.ss_remote = self.ss_start
73fc79e0
UH
174 self.data = self.count = 0
175 self.state = 'ADDRESS'
176 elif b in range(self.rc - self.margin, self.rc + self.margin):
70835fd4
UH
177 self.putpause('Short')
178 self.putstop(self.samplenum)
179 self.samplenum += self.stop
180 self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
73fc79e0
UH
181 self.data = self.count = 0
182 self.ss_bit = self.ss_start = self.samplenum
183 elif self.state == 'ADDRESS':
70835fd4
UH
184 self.handle_bit(b)
185 if self.count == 8:
186 self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
187 elif self.state == 'ADDRESS#':
188 self.handle_bit(b)
189 if self.count == 16:
190 self.state = 'COMMAND' if self.data_ok() else 'IDLE'
73fc79e0 191 elif self.state == 'COMMAND':
70835fd4
UH
192 self.handle_bit(b)
193 if self.count == 8:
194 self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
195 elif self.state == 'COMMAND#':
196 self.handle_bit(b)
197 if self.count == 16:
198 self.state = 'STOP' if self.data_ok() else 'IDLE'
199 elif self.state == 'STOP':
200 self.putstop(self.ss_bit)
12fecc8f 201 self.putremote()
70835fd4
UH
202 self.ss_bit = self.ss_start = self.samplenum
203 self.state = 'IDLE'