]> sigrok.org Git - libsigrokdecode.git/commitdiff
ir_nec: add support for extended NEC protocol (16bit address)
authorGerhard Sittig <redacted>
Sat, 25 Jul 2020 15:56:54 +0000 (17:56 +0200)
committerGerhard Sittig <redacted>
Sat, 25 Jul 2020 21:21:23 +0000 (23:21 +0200)
Add support for the extended NEC protocol, where the address field spans
16 bits and the complement is not sent. Commands still span 8 bits and
are sent in normal and in inverted form. The user needs to select the
extended protocol (off by default for compatibility), the input data
does not allow automatic detection of the protocol variant. It's also
not appropriate to assume the extended format when the address field
happens to fail the validity check.

The unfortunate mix of data value checks and annotation emission in
combination with "global" state makes this implementation more redundant
than I would like. A later cleanup is desirable.

This resolves bug #1583.

decoders/ir_nec/__init__.py
decoders/ir_nec/pd.py

index c361c3dc6ccd9fd7da3ec1e14e36abf5edf6e96b..c3ab29365b7a0def98c8bdfa36f80d52d6ede800 100644 (file)
@@ -19,6 +19,7 @@
 
 '''
 NEC is a pulse-distance based infrared remote control protocol.
 
 '''
 NEC is a pulse-distance based infrared remote control protocol.
+See https://www.sbprojects.net/knowledge/ir/nec.php for a description.
 '''
 
 from .pd import Decoder
 '''
 
 from .pd import Decoder
index af22f93a7d98adab56b884947c19f79a63200f56..c05e4744dc6a76c06f3007c2387040bcfa71a096 100644 (file)
@@ -49,6 +49,8 @@ class Decoder(srd.Decoder):
         {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
             'values': ('active-low', 'active-high')},
         {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
         {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
             'values': ('active-low', 'active-high')},
         {'id': 'cd_freq', 'desc': 'Carrier Frequency', 'default': 0},
+        {'id': 'extended', 'desc': 'Extended NEC Protocol',
+            'default': 'no', 'values': ('yes', 'no')},
     )
     annotations = (
         ('bit', 'Bit'),
     )
     annotations = (
         ('bit', 'Bit'),
@@ -78,16 +80,17 @@ class Decoder(srd.Decoder):
     def putb(self, data):
         self.put(self.ss_bit, self.samplenum, self.out_ann, data)
 
     def putb(self, data):
         self.put(self.ss_bit, self.samplenum, self.out_ann, data)
 
-    def putd(self, data):
+    def putd(self, data, bit_count):
         name = self.state.title()
         d = {'ADDRESS': Ann.ADDR, 'ADDRESS#': Ann.ADDR_INV,
              'COMMAND': Ann.CMD, 'COMMAND#': Ann.CMD_INV}
         s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
              'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
         name = self.state.title()
         d = {'ADDRESS': Ann.ADDR, 'ADDRESS#': Ann.ADDR_INV,
              'COMMAND': Ann.CMD, 'COMMAND#': Ann.CMD_INV}
         s = {'ADDRESS': ['ADDR', 'A'], 'ADDRESS#': ['ADDR#', 'A#'],
              'COMMAND': ['CMD', 'C'], 'COMMAND#': ['CMD#', 'C#']}
+        fmt = '{{}}: 0x{{:0{}X}}'.format(bit_count // 4)
         self.putx([d[self.state], [
         self.putx([d[self.state], [
-            '{}: 0x{:02X}'.format(name, data),
-            '{}: 0x{:02X}'.format(s[self.state][0], data),
-            '{}: 0x{:02X}'.format(s[self.state][1], data),
+            fmt.format(name, data),
+            fmt.format(s[self.state][0], data),
+            fmt.format(s[self.state][1], data),
             s[self.state][1],
         ]])
 
             s[self.state][1],
         ]])
 
@@ -155,24 +158,32 @@ class Decoder(srd.Decoder):
             self.data.append(ret)
         self.ss_bit = self.samplenum
 
             self.data.append(ret)
         self.ss_bit = self.samplenum
 
-    def data_ok(self, check):
+    def data_ok(self, check, want_len):
         name = self.state.title()
         normal, inverted = bitpack(self.data[:8]), bitpack(self.data[8:])
         valid = (normal ^ inverted) == 0xff
         show = inverted if self.state.endswith('#') else normal
         name = self.state.title()
         normal, inverted = bitpack(self.data[:8]), bitpack(self.data[8:])
         valid = (normal ^ inverted) == 0xff
         show = inverted if self.state.endswith('#') else normal
-        if len(self.data) == 8:
+        is_ext_addr = self.is_extended and self.state == 'ADDRESS'
+        if is_ext_addr:
+            normal = bitpack(self.data)
+            show = normal
+            valid = True
+        if len(self.data) == want_len:
             if self.state == 'ADDRESS':
                 self.addr = normal
             if self.state == 'COMMAND':
                 self.cmd = normal
             if self.state == 'ADDRESS':
                 self.addr = normal
             if self.state == 'COMMAND':
                 self.cmd = normal
-            self.putd(show)
+            self.putd(show, want_len)
             self.ss_start = self.samplenum
             self.ss_start = self.samplenum
+            if is_ext_addr:
+                self.data = []
+                self.ss_bit = self.ss_start = self.samplenum
             return True
         if check and not valid:
             warn_show = bitpack(self.data)
             self.putx([Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]])
         else:
             return True
         if check and not valid:
             warn_show = bitpack(self.data)
             self.putx([Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]])
         else:
-            self.putd(show)
+            self.putd(show, want_len)
         self.data = []
         self.ss_bit = self.ss_start = self.samplenum
         return valid
         self.data = []
         self.ss_bit = self.ss_start = self.samplenum
         return valid
@@ -188,6 +199,8 @@ class Decoder(srd.Decoder):
         prev_ir = None
 
         active = 0 if self.options['polarity'] == 'active-low' else 1
         prev_ir = None
 
         active = 0 if self.options['polarity'] == 'active-low' else 1
+        self.is_extended = self.options['extended'] == 'yes'
+        want_addr_len = 16 if self.is_extended else 8
 
         while True:
             # Detect changes in the presence of an active input signal.
 
         while True:
             # Detect changes in the presence of an active input signal.
@@ -238,22 +251,22 @@ class Decoder(srd.Decoder):
                 self.ss_bit = self.ss_start = self.samplenum
             elif self.state == 'ADDRESS':
                 self.handle_bit(b)
                 self.ss_bit = self.ss_start = self.samplenum
             elif self.state == 'ADDRESS':
                 self.handle_bit(b)
-                if len(self.data) == 8:
-                    self.data_ok(False)
-                    self.state = 'ADDRESS#'
+                if len(self.data) == want_addr_len:
+                    self.data_ok(False, want_addr_len)
+                    self.state = 'COMMAND' if self.is_extended else 'ADDRESS#'
             elif self.state == 'ADDRESS#':
                 self.handle_bit(b)
                 if len(self.data) == 16:
             elif self.state == 'ADDRESS#':
                 self.handle_bit(b)
                 if len(self.data) == 16:
-                    self.state = 'COMMAND' if self.data_ok(True) else 'IDLE'
+                    self.state = 'COMMAND' if self.data_ok(True, 8) else 'IDLE'
             elif self.state == 'COMMAND':
                 self.handle_bit(b)
                 if len(self.data) == 8:
             elif self.state == 'COMMAND':
                 self.handle_bit(b)
                 if len(self.data) == 8:
-                    self.data_ok(False)
+                    self.data_ok(False, 8)
                     self.state = 'COMMAND#'
             elif self.state == 'COMMAND#':
                 self.handle_bit(b)
                 if len(self.data) == 16:
                     self.state = 'COMMAND#'
             elif self.state == 'COMMAND#':
                 self.handle_bit(b)
                 if len(self.data) == 16:
-                    self.state = 'STOP' if self.data_ok(True) else 'IDLE'
+                    self.state = 'STOP' if self.data_ok(True, 8) else 'IDLE'
             elif self.state == 'STOP':
                 self.putstop(self.ss_bit)
                 self.putremote()
             elif self.state == 'STOP':
                 self.putstop(self.ss_bit)
                 self.putremote()