]> sigrok.org Git - libsigrokdecode.git/commitdiff
Add RFID EM4100 protocol decoding
authorBenjamin Larsson <redacted>
Tue, 15 Dec 2015 00:00:01 +0000 (01:00 +0100)
committerUwe Hermann <redacted>
Fri, 18 Dec 2015 17:23:00 +0000 (18:23 +0100)
decoders/em4100/__init__.py [new file with mode: 0644]
decoders/em4100/pd.py [new file with mode: 0644]

diff --git a/decoders/em4100/__init__.py b/decoders/em4100/__init__.py
new file mode 100644 (file)
index 0000000..cff361f
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+'''
+EM4100 is a biphase/manchester/PSK based 100-150kHz RFID protocol.
+'''
+
+from .pd import Decoder
diff --git a/decoders/em4100/pd.py b/decoders/em4100/pd.py
new file mode 100644 (file)
index 0000000..a870874
--- /dev/null
@@ -0,0 +1,242 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2015 Benjamin Larsson <benjamin@southpole.se>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either data 2 of the License, or
+## (at your option) any later data.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+##
+
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 2
+    id = 'em4100'
+    name = 'EM4100'
+    longname = 'RFID EM4100'
+    desc = 'EM4100 100-150kHz RFID protocol.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['em4100']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    options = (
+        {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-high',
+            'values': ('active-low', 'active-high')},
+        {'id': 'datarate' , 'desc': 'Data rate', 'default': '64',
+            'values': ('64', '32', '16')},
+#        {'id': 'coding', 'desc': 'Bit coding', 'default': 'biphase',
+#            'values': ('biphase', 'manchester', 'psk')},
+        {'id': 'coilfreq', 'desc': 'Coil frequency', 'default': '125000'},
+    )
+    annotations = (
+        ('headerbit', 'Header bit'),
+        ('databit', 'Version bit'),
+        ('rowparity', 'Row Parity'),
+        ('databit', 'Data bit'),
+        ('colparity', 'Column Parity'),
+        ('stopbit', 'Stop bit'),
+        ('rowparity_check', 'Row Parity Check'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (0,)),
+        ('fields', 'Fields', (1, 2, 4, 7)),
+        ('value', 'Value', (3, 5, 6, 8)),
+    )
+
+    def __init__(self, **kwargs):
+        self.samplerate = None
+        self.oldpin = None
+        self.last_samplenum = None
+        self.lastlast_samplenum = None
+        self.last_edge = 0
+        self.bit_width = 0
+        self.halfbit_limit = 0
+        self.oldpp = 0
+        self.oldpl = 0
+        self.oldsamplenum = 0
+        self.last_bit_pos = 0
+        self.first_start = 0
+        self.first_one = 0
+        self.state = 'HEADER'
+        self.data = 0
+        self.data_bits = 0
+        self.data_start = 0
+        self.data_parity = 0
+        self.payload_cnt = 0
+        self.data_col_parity = [0, 0, 0, 0, 0, 0]
+        self.col_parity = [0, 0, 0, 0, 0, 0]
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+        self.bit_width = (self.samplerate / (int(self.options['coilfreq']))) * int(self.options['datarate'])
+        self.halfbit_limit = self.bit_width/2 + self.bit_width/4
+        self.polarity = 0 if self.options['polarity'] == 'active-low' else 1
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def add_bit(self, bit, bit_start, bit_stop):
+        if self.state == 'HEADER':
+            if bit == 1:
+                if self.first_one > 0:
+                    self.first_one += 1
+                if self.first_one == 9:
+                    self.put(int(self.first_start), int(bit_stop), self.out_ann,
+                             [1, ['Header', 'Head', 'He', 'H']])
+                    self.first_one = 0
+                    self.state = 'PAYLOAD'
+                    return
+                if self.first_one == 0:
+                    self.first_one = 1
+                    self.first_start = bit_start
+
+            if bit == 0:
+                self.first_one = 0
+            return
+
+        if self.state == 'PAYLOAD':
+            self.payload_cnt += 1
+            if self.data_bits == 0:
+                self.data_start = bit_start
+                self.data = 0
+                self.data_parity = 0
+            self.data_bits += 1
+            if self.data_bits == 5:
+                self.put(int(self.data_start), int(bit_start), self.out_ann,
+                         [2, ['Data', 'Da', 'D']])
+                self.put(int(bit_start), int(bit_stop), self.out_ann,
+                         [4, ['Parity', 'Par', 'Pa', 'P']])
+                self.put(int(self.data_start), int(bit_start), self.out_ann,
+                         [3, [str("%X" % self.data)]])
+                if self.data_parity == bit:
+                    p_string = ['OK', 'O']
+                else:
+                    p_string = ['ERROR', 'ERR', 'ER', 'E']
+                self.put(int(bit_start), int(bit_stop), self.out_ann,
+                         [6, p_string])
+                self.data_bits = 0
+                if self.payload_cnt == 50:
+                    self.state = 'TRAILER'
+                    self.payload_cnt = 0
+
+            self.data_parity ^= bit
+            self.data_col_parity[self.data_bits] ^= bit
+            self.data = (self.data << 1) | bit
+            return
+
+        if self.state == 'TRAILER':
+            self.payload_cnt += 1
+            if self.data_bits == 0:
+                self.data_start = bit_start
+                self.data = 0
+                self.data_parity = 0
+            self.data_bits += 1
+            self.col_parity[self.data_bits] = bit
+
+            if self.data_bits == 5:
+                p_string = ['ERROR', 'ERR', 'ER', 'E']
+                if self.data_col_parity[1] == self.col_parity[1]:
+                    if self.data_col_parity[2] == self.col_parity[2]:
+                        if self.data_col_parity[3] == self.col_parity[3]:
+                            if self.data_col_parity[4] == self.col_parity[4]:
+                                p_string = ['OK', 'O']
+
+                self.put(int(self.data_start), int(bit_start), self.out_ann,
+                         [4, ['Column parity', 'Col par', 'CP', 'P']])
+                self.put(int(bit_start), int(bit_stop), self.out_ann,
+                         [4, ['Stop bit', 'St bi', 'SB', 'S']])
+                self.put(int(self.data_start), int(bit_start), self.out_ann,
+                         [6, p_string])
+
+                self.data_bits = 0
+                if self.payload_cnt == 5:
+                    self.state = 'HEADER'
+                    self.payload_cnt = 0
+                    self.data_col_parity = [0, 0, 0, 0, 0, 0]
+                    self.col_parity = [0, 0, 0, 0, 0, 0]
+
+    def putbit(self, bit, bit_start, bit_stop):
+        self.put(int(bit_start), int(bit_stop), self.out_ann,
+                 [0, [str(bit)]])
+        self.add_bit(bit, bit_start, bit_stop)
+
+    def manchester_decode(self, samplenum, pl, pp, pin):
+        bit_start = 0
+        bit_stop = 0
+        bit = self.oldpin ^ self.polarity
+        if pl > self.halfbit_limit:
+            samples = samplenum - self.oldsamplenum
+            t = samples / self.samplerate
+
+            if self.oldpl > self.halfbit_limit:
+                bit_start = int(self.oldsamplenum - self.oldpl/2)
+                bit_stop = int(samplenum - pl/2)
+                self.putbit(bit, bit_start, bit_stop)
+            if self.oldpl <= self.halfbit_limit:
+                bit_start = int(self.oldsamplenum - self.oldpl)
+                bit_stop = int(samplenum - pl/2)
+                self.putbit(bit, bit_start, bit_stop)
+            self.last_bit_pos = int(samplenum - pl/2)
+
+        if pl < self.halfbit_limit:
+            samples = samplenum - self.oldsamplenum
+            t = samples / self.samplerate
+
+            if self.oldpl > self.halfbit_limit:
+                bit_start = self.oldsamplenum - self.oldpl/2
+                bit_stop = int(samplenum)
+                self.putbit(bit, bit_start, bit_stop)
+                self.last_bit_pos = int(samplenum)
+            if self.oldpl <= self.halfbit_limit:
+                if self.last_bit_pos <= self.oldsamplenum - self.oldpl:
+                    bit_start = self.oldsamplenum - self.oldpl
+                    bit_stop = int(samplenum)
+                    self.putbit(bit, bit_start, bit_stop)
+                    self.last_bit_pos = int(samplenum)
+
+    def decode(self, ss, es, data):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        for (samplenum, (pin,)) in data:
+            # Ignore identical samples early on (for performance reasons).
+            if self.oldpin == pin:
+                continue
+
+            if self.oldpin is None:
+                self.oldpin = pin
+                self.last_samplenum = samplenum
+                self.lastlast_samplenum = samplenum
+                self.last_edge = samplenum
+                self.oldpl = 0
+                self.oldpp = 0
+                self.oldsamplenum = 0
+                self.last_bit_pos = 0
+                continue
+
+            if self.oldpin != pin:
+                pl = samplenum - self.oldsamplenum
+                pp = pin
+
+                self.manchester_decode(samplenum, pl, pp, pin)
+
+                self.oldpl = pl
+                self.oldpp = pp
+                self.oldsamplenum = samplenum
+                self.oldpin = pin