forked from Ivasoft/DSView
Add 'IR IRMP' decoder from libsigrokdecode codebase
This commit is contained in:
25
libsigrokdecode4DSL/decoders/ir_irmp/__init__.py
Normal file
25
libsigrokdecode4DSL/decoders/ir_irmp/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
##
|
||||
## This file is part of the libsigrokdecode project.
|
||||
##
|
||||
## Copyright (C) 2019 Rene Staffen
|
||||
##
|
||||
## 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, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
IRMP is a multi protocol infrared remote protocol decoder. See
|
||||
https://www.mikrocontroller.net/articles/IRMP for details.
|
||||
'''
|
||||
|
||||
from .pd import Decoder
|
||||
137
libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py
Normal file
137
libsigrokdecode4DSL/decoders/ir_irmp/irmp_library.py
Normal file
@@ -0,0 +1,137 @@
|
||||
##
|
||||
## This file is part of the libsigrokdecode project.
|
||||
##
|
||||
## Copyright (C) 2019 Rene Staffen
|
||||
##
|
||||
## 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, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
'''
|
||||
Python binding for the IRMP library.
|
||||
'''
|
||||
|
||||
import ctypes
|
||||
import platform
|
||||
|
||||
class IrmpLibrary:
|
||||
'''
|
||||
Library instance for an infrared protocol detector.
|
||||
'''
|
||||
|
||||
__usable_instance = None
|
||||
|
||||
class ResultData(ctypes.Structure):
|
||||
_fields_ = [
|
||||
( 'protocol', ctypes.c_uint32, ),
|
||||
( 'protocol_name', ctypes.c_char_p, ),
|
||||
( 'address', ctypes.c_uint32, ),
|
||||
( 'command', ctypes.c_uint32, ),
|
||||
( 'flags', ctypes.c_uint32, ),
|
||||
( 'start_sample', ctypes.c_uint32, ),
|
||||
( 'end_sample', ctypes.c_uint32, ),
|
||||
]
|
||||
|
||||
FLAG_REPETITION = 1 << 0
|
||||
FLAG_RELEASE = 1 << 1
|
||||
|
||||
def _library_filename(self):
|
||||
'''
|
||||
Determine the library filename depending on the platform.
|
||||
'''
|
||||
|
||||
if platform.uname()[0] == 'Linux':
|
||||
return 'libirmp.so'
|
||||
if platform.uname()[0] == 'Darwin':
|
||||
return 'libirmp.dylib'
|
||||
return 'irmp.dll'
|
||||
|
||||
def _library_setup_api(self):
|
||||
'''
|
||||
Lookup the C library's API routines. Declare their prototypes.
|
||||
'''
|
||||
|
||||
if not self._lib:
|
||||
return False
|
||||
|
||||
self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
|
||||
self._lib.irmp_get_sample_rate.argtypes = []
|
||||
|
||||
self._lib.irmp_reset_state.restype = None
|
||||
self._lib.irmp_reset_state.argtypes = []
|
||||
|
||||
self._lib.irmp_add_one_sample.restype = ctypes.c_int
|
||||
self._lib.irmp_add_one_sample.argtypes = [ ctypes.c_int, ]
|
||||
|
||||
if False:
|
||||
self._lib.irmp_detect_buffer.restype = self.ResultData
|
||||
self._lib.irmp_detect_buffer.argtypes = [ ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t, ]
|
||||
|
||||
self._lib.irmp_get_result_data.restype = ctypes.c_int
|
||||
self._lib.irmp_get_result_data.argtypes = [ ctypes.POINTER(self.ResultData), ]
|
||||
|
||||
self._lib.irmp_get_protocol_name.restype = ctypes.c_char_p
|
||||
self._lib.irmp_get_protocol_name.argtypes = [ ctypes.c_uint32, ]
|
||||
|
||||
# Create a result buffer that's local to the library instance.
|
||||
self._data = self.ResultData()
|
||||
|
||||
return True
|
||||
|
||||
def __init__(self):
|
||||
'''
|
||||
Create a library instance.
|
||||
'''
|
||||
|
||||
# Only create a working instance for the first invocation.
|
||||
# Degrade all other instances, make them fail "late" during
|
||||
# execution, so that users will see the errors.
|
||||
self._lib = None
|
||||
self._data = None
|
||||
if IrmpLibrary.__usable_instance is None:
|
||||
filename = self._library_filename()
|
||||
self._lib = ctypes.cdll.LoadLibrary(filename)
|
||||
self._library_setup_api()
|
||||
IrmpLibrary.__usable_instance = self
|
||||
|
||||
def get_sample_rate(self):
|
||||
if not self._lib:
|
||||
return None
|
||||
return self._lib.irmp_get_sample_rate()
|
||||
|
||||
def reset_state(self):
|
||||
if not self._lib:
|
||||
return None
|
||||
self._lib.irmp_reset_state()
|
||||
|
||||
def add_one_sample(self, level):
|
||||
if not self._lib:
|
||||
raise Exception("IRMP library limited to a single instance.")
|
||||
if not self._lib.irmp_add_one_sample(int(level)):
|
||||
return False
|
||||
self._lib.irmp_get_result_data(ctypes.byref(self._data))
|
||||
return True
|
||||
|
||||
def get_result_data(self):
|
||||
if not self._data:
|
||||
return None
|
||||
return {
|
||||
'proto_nr': self._data.protocol,
|
||||
'proto_name': self._data.protocol_name.decode('UTF-8', 'ignore'),
|
||||
'address': self._data.address,
|
||||
'command': self._data.command,
|
||||
'repeat': bool(self._data.flags & self.FLAG_REPETITION),
|
||||
'release': bool(self._data.flags & self.FLAG_RELEASE),
|
||||
'start': self._data.start_sample,
|
||||
'end': self._data.end_sample,
|
||||
}
|
||||
137
libsigrokdecode4DSL/decoders/ir_irmp/pd.py
Normal file
137
libsigrokdecode4DSL/decoders/ir_irmp/pd.py
Normal file
@@ -0,0 +1,137 @@
|
||||
##
|
||||
## This file is part of the libsigrokdecode project.
|
||||
##
|
||||
## Copyright (C) 2014 Gump Yang <gump.yang@gmail.com>
|
||||
## Copyright (C) 2019 Rene Staffen
|
||||
##
|
||||
## 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, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
from . import irmp_library
|
||||
import sigrokdecode as srd
|
||||
|
||||
class SamplerateError(Exception):
|
||||
pass
|
||||
|
||||
class LibraryError(Exception):
|
||||
pass
|
||||
|
||||
class Decoder(srd.Decoder):
|
||||
api_version = 3
|
||||
id = 'ir_irmp'
|
||||
name = 'IR IRMP'
|
||||
longname = 'IR IRMP'
|
||||
desc = 'IRMP infrared remote control multi protocol.'
|
||||
license = 'gplv2+'
|
||||
inputs = ['logic']
|
||||
outputs = []
|
||||
tags = ['IR']
|
||||
channels = (
|
||||
{'id': 'ir', 'name': 'IR', 'desc': 'Data line'},
|
||||
)
|
||||
options = (
|
||||
{'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
|
||||
'values': ('active-low', 'active-high')},
|
||||
)
|
||||
annotations = (
|
||||
('packet', 'Packet'),
|
||||
)
|
||||
annotation_rows = (
|
||||
('packets', 'IR Packets', (0,)),
|
||||
)
|
||||
|
||||
def putframe(self, data):
|
||||
'''Emit annotation for an IR frame.'''
|
||||
|
||||
# Cache result data fields in local variables. Get the ss/es
|
||||
# timestamps, scaled to sample numbers.
|
||||
nr = data['proto_nr']
|
||||
name = data['proto_name']
|
||||
addr = data['address']
|
||||
cmd = data['command']
|
||||
repeat = data['repeat']
|
||||
release = data['release']
|
||||
ss = data['start'] * self.rate_factor
|
||||
es = data['end'] * self.rate_factor
|
||||
|
||||
# Prepare display texts for several zoom levels.
|
||||
# Implementor's note: Keep list lengths for flags aligned during
|
||||
# maintenance. Make sure there are as many flags text variants
|
||||
# as are referenced by annotation text variants. Differing list
|
||||
# lengths or dynamic refs will severely complicate the logic.
|
||||
rep_txts = ['repeat', 'rep', 'r']
|
||||
rel_txts = ['release', 'rel', 'R']
|
||||
flag_txts = [None,] * len(rep_txts)
|
||||
for zoom in range(len(flag_txts)):
|
||||
flag_txts[zoom] = []
|
||||
if repeat:
|
||||
flag_txts[zoom].append(rep_txts[zoom])
|
||||
if release:
|
||||
flag_txts[zoom].append(rel_txts[zoom])
|
||||
flag_txts = [' '.join(t) or '-' for t in flag_txts]
|
||||
flg = flag_txts # Short name for .format() references.
|
||||
txts = [
|
||||
'Protocol: {name} ({nr}), Address 0x{addr:04x}, Command: 0x{cmd:04x}, Flags: {flg[0]}'.format(**locals()),
|
||||
'P: {name} ({nr}), Addr: 0x{addr:x}, Cmd: 0x{cmd:x}, Flg: {flg[1]}'.format(**locals()),
|
||||
'P: {nr} A: 0x{addr:x} C: 0x{cmd:x} F: {flg[1]}'.format(**locals()),
|
||||
'C:{cmd:x} A:{addr:x} {flg[2]}'.format(**locals()),
|
||||
'C:{cmd:x}'.format(**locals()),
|
||||
]
|
||||
|
||||
# Emit the annotation from details which were constructed above.
|
||||
self.put(ss, es, self.out_ann, [0, txts])
|
||||
|
||||
def __init__(self):
|
||||
self.irmp = None
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.want_reset = True
|
||||
|
||||
def start(self):
|
||||
self.out_ann = self.register(srd.OUTPUT_ANN)
|
||||
|
||||
def metadata(self, key, value):
|
||||
if key == srd.SRD_CONF_SAMPLERATE:
|
||||
self.samplerate = value
|
||||
|
||||
def decode(self):
|
||||
if not self.irmp:
|
||||
try:
|
||||
self.irmp = irmp_library.IrmpLibrary()
|
||||
except Exception as e:
|
||||
txt = e.args[0]
|
||||
raise LibraryError(txt)
|
||||
if self.irmp:
|
||||
self.lib_rate = self.irmp.get_sample_rate()
|
||||
if not self.irmp or not self.lib_rate:
|
||||
raise LibraryError('Cannot access IRMP library. One instance limit exceeded?')
|
||||
if not self.samplerate:
|
||||
raise SamplerateError('Cannot decode without samplerate.')
|
||||
if self.samplerate % self.lib_rate:
|
||||
raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(self.lib_rate))
|
||||
self.rate_factor = int(self.samplerate / self.lib_rate)
|
||||
if self.want_reset:
|
||||
self.irmp.reset_state()
|
||||
self.want_reset = False
|
||||
|
||||
self.active = 0 if self.options['polarity'] == 'active-low' else 1
|
||||
ir, = self.wait()
|
||||
while True:
|
||||
if self.active == 1:
|
||||
ir = 1 - ir
|
||||
if self.irmp.add_one_sample(ir):
|
||||
data = self.irmp.get_result_data()
|
||||
self.putframe(data)
|
||||
ir, = self.wait([{'skip': self.rate_factor}])
|
||||
Reference in New Issue
Block a user