summaryrefslogtreecommitdiff
path: root/decoders/em4100/pd.py
diff options
context:
space:
mode:
authorBenjamin Larsson <benjamin@southpole.se>2015-12-15 01:00:01 +0100
committerUwe Hermann <uwe@hermann-uwe.de>2015-12-18 18:23:00 +0100
commit8f26590799306bf7e13241c345adbf1fde811ec7 (patch)
treec079bef473e33766a1ddd435d5c0ae0f8374cfa1 /decoders/em4100/pd.py
parent033e7d4db76f2e2a289d5567e9532f26201cfa74 (diff)
downloadlibsigrokdecode-8f26590799306bf7e13241c345adbf1fde811ec7.tar.gz
libsigrokdecode-8f26590799306bf7e13241c345adbf1fde811ec7.zip
Add RFID EM4100 protocol decoding
Diffstat (limited to 'decoders/em4100/pd.py')
-rw-r--r--decoders/em4100/pd.py242
1 files changed, 242 insertions, 0 deletions
diff --git a/decoders/em4100/pd.py b/decoders/em4100/pd.py
new file mode 100644
index 0000000..a870874
--- /dev/null
+++ b/decoders/em4100/pd.py
@@ -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