]> sigrok.org Git - libsigrokdecode.git/commitdiff
avr_isp: Add more parts master github/master
authoratoomnetmarc <redacted>
Mon, 4 Mar 2024 19:36:46 +0000 (20:36 +0100)
committerSoeren Apel <redacted>
Mon, 4 Mar 2024 20:46:14 +0000 (21:46 +0100)
83 files changed:
Makefile.am
README
configure.ac
decoder.c
decoders/adf435x/pd.py
decoders/avr_isp/parts.py
decoders/avr_isp/pd.py
decoders/caliper/__init__.py [new file with mode: 0644]
decoders/caliper/pd.py [new file with mode: 0644]
decoders/can/pd.py
decoders/common/srdhelper/mod.py
decoders/eeprom24xx/lists.py
decoders/eeprom24xx/pd.py
decoders/i2c/pd.py
decoders/i2cfilter/pd.py
decoders/ieee488/pd.py
decoders/ir_irmp/__init__.py [new file with mode: 0644]
decoders/ir_irmp/irmp_library.py [new file with mode: 0644]
decoders/ir_irmp/pd.py [new file with mode: 0644]
decoders/ir_nec/__init__.py
decoders/ir_nec/lists.py
decoders/ir_nec/pd.py
decoders/ir_sirc/__init__.py [new file with mode: 0644]
decoders/ir_sirc/lists.py [new file with mode: 0644]
decoders/ir_sirc/pd.py [new file with mode: 0644]
decoders/jtag/pd.py
decoders/lfast/__init__.py [new file with mode: 0644]
decoders/lfast/pd.py [new file with mode: 0644]
decoders/lpc/pd.py
decoders/nes_gamepad/pd.py
decoders/nrf905/pd.py
decoders/numbers_and_state/__init__.py [new file with mode: 0644]
decoders/numbers_and_state/pd.py [new file with mode: 0644]
decoders/parallel/__init__.py
decoders/parallel/pd.py
decoders/pca9571/pd.py
decoders/pjdl/__init__.py [new file with mode: 0644]
decoders/pjdl/pd.py [new file with mode: 0644]
decoders/pjon/__init__.py [new file with mode: 0644]
decoders/pjon/pd.py [new file with mode: 0644]
decoders/rc_encode/__init__.py
decoders/rc_encode/pd.py
decoders/rgb_led_spi/pd.py
decoders/rgb_led_ws281x/pd.py
decoders/sae_j1850_vpw/__init__.py [new file with mode: 0644]
decoders/sae_j1850_vpw/pd.py [new file with mode: 0644]
decoders/sbus_futaba/__init__.py [new file with mode: 0644]
decoders/sbus_futaba/pd.py [new file with mode: 0644]
decoders/sdq/__init__.py [new file with mode: 0644]
decoders/sdq/pd.py [new file with mode: 0644]
decoders/seven_segment/pd.py
decoders/sipi/__init__.py [new file with mode: 0644]
decoders/sipi/pd.py [new file with mode: 0644]
decoders/sle44xx/__init__.py [new file with mode: 0644]
decoders/sle44xx/pd.py [new file with mode: 0644]
decoders/spdif/pd.py
decoders/spiflash/lists.py
decoders/st25r39xx_spi/__init__.py [new file with mode: 0644]
decoders/st25r39xx_spi/lists.py [new file with mode: 0644]
decoders/st25r39xx_spi/pd.py [new file with mode: 0644]
decoders/st7735/pd.py
decoders/tca6408a/pd.py
decoders/timing/pd.py
decoders/uart/pd.py
decoders/xy2-100/__init__.py [new file with mode: 0644]
decoders/xy2-100/pd.py [new file with mode: 0644]
instance.c
irmp/README-sigrok.txt [new file with mode: 0644]
irmp/README.txt [new file with mode: 0644]
irmp/irmp-main-sharedlib.c [new file with mode: 0644]
irmp/irmp-main-sharedlib.h [new file with mode: 0644]
irmp/irmp.c [new file with mode: 0644]
irmp/irmp.h [new file with mode: 0644]
irmp/irmpconfig.h [new file with mode: 0644]
irmp/irmpprotocols.h [new file with mode: 0644]
irmp/irmpsystem.h [new file with mode: 0644]
libsigrokdecode-internal.h
libsigrokdecode.h
module_sigrokdecode.c
session.c
srd.c
type_decoder.c
util.c

index 97e5228517357d15a959015d11cb27ff9f98c1b0..32c6dd63f4cefa5703e2fdc43271f427bd7ee15b 100644 (file)
@@ -54,6 +54,21 @@ pkginclude_HEADERS = libsigrokdecode.h
 nodist_pkginclude_HEADERS = version.h
 noinst_HEADERS = libsigrokdecode-internal.h
 
+if WITH_IRMP
+lib_LTLIBRARIES += libirmp.la
+libirmp_la_SOURCES = \
+       irmp/irmp-main-sharedlib.c \
+       irmp/irmp-main-sharedlib.h \
+       irmp/irmp.h \
+       irmp/irmpconfig.h \
+       irmp/irmpsystem.h \
+       irmp/irmpprotocols.h
+noinst_HEADERS += irmp/irmp.c
+libirmp_la_CFLAGS = $(LIBIRMP_CFLAGS)
+libirmp_la_LIBADD = $(LIBIRMP_LIBS)
+libirmp_la_LDFLAGS = -no-undefined -version-info 0:0:0
+endif
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libsigrokdecode.pc
 
diff --git a/README b/README
index 784be277fc6f28e488873473cc09a563f87b6153..301f235fefaa5228e581ffc5cdca4537d6bf7d06 100644 (file)
--- a/README
+++ b/README
@@ -91,7 +91,7 @@ Mailing list
 IRC
 ---
 
-You can find the sigrok developers in the #sigrok IRC channel on Freenode.
+You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat.
 
 
 Website
index d59e0bea35ee64618169ce4feac351559ab5cfa2..dc04166bd9ce862d2a617e7cd315645224eca1fe 100644 (file)
@@ -40,9 +40,16 @@ AC_PROG_CC
 AC_PROG_INSTALL
 AC_PROG_LN_S
 
+AC_C_CONST
+
 # Required for per-target flags or subdir-objects with C sources.
 AM_PROG_CC_C_O
 
+# Support building Windows DLLs.
+AC_LIBTOOL_WIN32_DLL
+AM_PROG_CC_STDC
+AM_PROG_LIBTOOL
+
 # Set the standard the C library headers should conform to.
 AH_VERBATIM([_POSIX_C_SOURCE], [/* The targeted POSIX standard. */
 #ifndef _POSIX_C_SOURCE
@@ -93,7 +100,10 @@ SR_PKG_CHECK_SUMMARY([srd_pkglibs_summary])
 # first, since usually only that variant will add "-lpython3.8".
 # https://docs.python.org/3/whatsnew/3.8.html#debug-build-uses-the-same-abi-as-release-build
 SR_PKG_CHECK([python3], [SRD_PKGLIBS],
-       [python-3.8-embed], [python-3.8 >= 3.8], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5], [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2], [python3 >= 3.2])
+       [python-3.12-embed], [python-3.11-embed],
+       [python-3.10-embed], [python-3.9-embed], [python-3.8-embed], [python3-embed],
+       [python-3.8 >= 3.8], [python-3.7 >= 3.7], [python-3.6 >= 3.6], [python-3.5 >= 3.5],
+       [python-3.4 >= 3.4], [python-3.3 >= 3.3], [python-3.2 >= 3.2], [python3 >= 3.2])
 AS_IF([test "x$sr_have_python3" = xno],
        [AC_MSG_ERROR([Cannot find Python 3 development headers.])])
 
@@ -132,6 +142,17 @@ AC_SYS_LARGEFILE
 
 AC_C_BIGENDIAN
 
+#########################
+##  Optional features. ##
+#########################
+
+# Enable IRMP support by default. Accept user overrides.
+AC_ARG_ENABLE([irmp],
+       [AS_HELP_STRING([--enable-irmp], [enable IRMP shared object [default=yes]])],
+       [], [enable_irmp_so=yes])
+AM_CONDITIONAL([WITH_IRMP], [test "x$enable_irmp_so" = "xyes"])
+test -n "$enable_irmp_so" || enable_irmp_so=no
+
 ##############################
 ##  Finalize configuration  ##
 ##############################
@@ -141,6 +162,7 @@ AC_SUBST([SRD_PKGLIBS])
 # Retrieve the compile and link flags for all modules combined.
 # Also, bail out at this point if any module dependency is not met.
 PKG_CHECK_MODULES([LIBSIGROKDECODE], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
+PKG_CHECK_MODULES([LIBIRMP], [glib-2.0 >= 2.34 $SRD_PKGLIBS])
 PKG_CHECK_MODULES([TESTS], [$SRD_PKGLIBS_TESTS glib-2.0 $SRD_PKGLIBS])
 
 srd_glib_version=`$PKG_CONFIG --modversion glib-2.0 2>&AS_MESSAGE_LOG_FD`
@@ -175,4 +197,6 @@ Detected libraries (required):
 $srd_pkglibs_summary
 Detected libraries (optional):
 $srd_pkglibs_opt_summary
+Optional features:
+  - IRMP support library .......... $enable_irmp_so
 _EOF
index fc6ae07317c86929a8db634c9b9d2e4391dba081..dd3bd5ad40646b0dc81c5f24adefff076cabc2c9 100644 (file)
--- a/decoder.c
+++ b/decoder.c
@@ -136,6 +136,18 @@ static void annotation_row_free(void *data)
        g_free(row);
 }
 
+static void logic_output_channel_free(void *data)
+{
+       struct srd_decoder_logic_output_channel *logic_out_ch = data;
+
+       if (!logic_out_ch)
+               return;
+
+       g_free(logic_out_ch->desc);
+       g_free(logic_out_ch->id);
+       g_free(logic_out_ch);
+}
+
 static void decoder_option_free(void *data)
 {
        struct srd_decoder_option *opt = data;
@@ -187,7 +199,7 @@ static int get_channels(const struct srd_decoder *d, const char *attr,
        PyObject *py_channellist, *py_entry;
        struct srd_channel *pdch;
        GSList *pdchl;
-       ssize_t i;
+       ssize_t ch_idx;
        PyGILState_STATE gstate;
 
        gstate = PyGILState_Ensure();
@@ -210,8 +222,9 @@ static int get_channels(const struct srd_decoder *d, const char *attr,
                goto err_out;
        }
 
-       for (i = PyTuple_Size(py_channellist) - 1; i >= 0; i--) {
-               py_entry = PyTuple_GetItem(py_channellist, i);
+       ch_idx = PyTuple_Size(py_channellist);
+       while (ch_idx--) {
+               py_entry = PyTuple_GetItem(py_channellist, ch_idx);
                if (!py_entry)
                        goto except_out;
 
@@ -231,7 +244,7 @@ static int get_channels(const struct srd_decoder *d, const char *attr,
                if (py_dictitem_as_str(py_entry, "desc", &pdch->desc) != SRD_OK)
                        goto err_out;
 
-               pdch->order = offset + i;
+               pdch->order = offset + ch_idx;
        }
 
        Py_DECREF(py_channellist);
@@ -259,7 +272,7 @@ static int get_options(struct srd_decoder *d)
        GSList *options;
        struct srd_decoder_option *o;
        GVariant *gvar;
-       ssize_t opt, i;
+       ssize_t opt, val_idx;
        PyGILState_STATE gstate;
 
        gstate = PyGILState_Ensure();
@@ -339,8 +352,9 @@ static int get_options(struct srd_decoder *d)
                                goto err_out;
                        }
 
-                       for (i = PyTuple_Size(py_values) - 1; i >= 0; i--) {
-                               py_item = PyTuple_GetItem(py_values, i);
+                       val_idx = PyTuple_Size(py_values);
+                       while (val_idx--) {
+                               py_item = PyTuple_GetItem(py_values, val_idx);
                                if (!py_item)
                                        goto except_out;
 
@@ -379,14 +393,17 @@ err_out:
 }
 
 /* Convert annotation class attribute to GSList of char **. */
-static int get_annotations(struct srd_decoder *dec)
+static int get_annotations(struct srd_decoder *dec, size_t *ret_count)
 {
        PyObject *py_annlist, *py_ann;
        GSList *annotations;
        char **annpair;
-       ssize_t i;
+       ssize_t ann_idx;
        PyGILState_STATE gstate;
 
+       if (ret_count)
+               *ret_count = 0;
+
        gstate = PyGILState_Ensure();
 
        if (!PyObject_HasAttrString(dec->py_dec, "annotations")) {
@@ -401,20 +418,22 @@ static int get_annotations(struct srd_decoder *dec)
                goto except_out;
 
        if (!PyTuple_Check(py_annlist)) {
-               srd_err("Protocol decoder %s annotations should "
-                       "be a tuple.", dec->name);
+               srd_err("Protocol decoder %s annotations should be a tuple.",
+                       dec->name);
                goto err_out;
        }
 
-       for (i = PyTuple_Size(py_annlist) - 1; i >= 0; i--) {
-               py_ann = PyTuple_GetItem(py_annlist, i);
+       ann_idx = PyTuple_Size(py_annlist);
+       if (ret_count)
+               *ret_count = ann_idx;
+       while (ann_idx--) {
+               py_ann = PyTuple_GetItem(py_annlist, ann_idx);
                if (!py_ann)
                        goto except_out;
 
                if (!PyTuple_Check(py_ann) || PyTuple_Size(py_ann) != 2) {
-                       srd_err("Protocol decoder %s annotation %zd should "
-                               "be a tuple with two elements.",
-                               dec->name, i + 1);
+                       srd_err("Protocol decoder %s annotation %zd should be a tuple with two elements.",
+                               dec->name, ann_idx + 1);
                        goto err_out;
                }
                if (py_strseq_to_char(py_ann, &annpair) != SRD_OK)
@@ -440,43 +459,45 @@ err_out:
 }
 
 /* Convert annotation_rows to GSList of 'struct srd_decoder_annotation_row'. */
-static int get_annotation_rows(struct srd_decoder *dec)
+static int get_annotation_rows(struct srd_decoder *dec, size_t cls_count)
 {
+       const char *py_member_name = "annotation_rows";
+
        PyObject *py_ann_rows, *py_ann_row, *py_ann_classes, *py_item;
        GSList *annotation_rows;
        struct srd_decoder_annotation_row *ann_row;
-       ssize_t i, k;
+       ssize_t row_idx, item_idx;
        size_t class_idx;
        PyGILState_STATE gstate;
 
        gstate = PyGILState_Ensure();
 
-       if (!PyObject_HasAttrString(dec->py_dec, "annotation_rows")) {
+       if (!PyObject_HasAttrString(dec->py_dec, py_member_name)) {
                PyGILState_Release(gstate);
                return SRD_OK;
        }
 
        annotation_rows = NULL;
 
-       py_ann_rows = PyObject_GetAttrString(dec->py_dec, "annotation_rows");
+       py_ann_rows = PyObject_GetAttrString(dec->py_dec, py_member_name);
        if (!py_ann_rows)
                goto except_out;
 
        if (!PyTuple_Check(py_ann_rows)) {
-               srd_err("Protocol decoder %s annotation_rows "
-                       "must be a tuple.", dec->name);
+               srd_err("Protocol decoder %s %s must be a tuple.",
+                       dec->name, py_member_name);
                goto err_out;
        }
 
-       for (i = PyTuple_Size(py_ann_rows) - 1; i >= 0; i--) {
-               py_ann_row = PyTuple_GetItem(py_ann_rows, i);
+       row_idx = PyTuple_Size(py_ann_rows);
+       while (row_idx--) {
+               py_ann_row = PyTuple_GetItem(py_ann_rows, row_idx);
                if (!py_ann_row)
                        goto except_out;
 
                if (!PyTuple_Check(py_ann_row) || PyTuple_Size(py_ann_row) != 3) {
-                       srd_err("Protocol decoder %s annotation_rows "
-                               "must contain only tuples of 3 elements.",
-                               dec->name);
+                       srd_err("Protocol decoder %s %s must contain only tuples of 3 elements.",
+                               dec->name, py_member_name);
                        goto err_out;
                }
                ann_row = g_malloc0(sizeof(struct srd_decoder_annotation_row));
@@ -500,26 +521,30 @@ static int get_annotation_rows(struct srd_decoder *dec)
                        goto except_out;
 
                if (!PyTuple_Check(py_ann_classes)) {
-                       srd_err("Protocol decoder %s annotation_rows tuples "
-                               "must have a tuple of numbers as 3rd element.",
-                               dec->name);
+                       srd_err("Protocol decoder %s %s tuples must have a tuple of numbers as 3rd element.",
+                               dec->name, py_member_name);
                        goto err_out;
                }
 
-               for (k = PyTuple_Size(py_ann_classes) - 1; k >= 0; k--) {
-                       py_item = PyTuple_GetItem(py_ann_classes, k);
+               item_idx = PyTuple_Size(py_ann_classes);
+               while (item_idx--) {
+                       py_item = PyTuple_GetItem(py_ann_classes, item_idx);
                        if (!py_item)
                                goto except_out;
 
                        if (!PyLong_Check(py_item)) {
-                               srd_err("Protocol decoder %s annotation row "
-                                       "class tuple must only contain numbers.",
+                               srd_err("Protocol decoder %s annotation row class tuple must only contain numbers.",
                                        dec->name);
                                goto err_out;
                        }
                        class_idx = PyLong_AsSize_t(py_item);
                        if (PyErr_Occurred())
                                goto except_out;
+                       if (class_idx >= cls_count) {
+                               srd_err("Protocol decoder %s annotation row %zd references invalid class %zu.",
+                                       dec->name, row_idx, class_idx);
+                               goto err_out;
+                       }
 
                        ann_row->ann_classes = g_slist_prepend(ann_row->ann_classes,
                                        GSIZE_TO_POINTER(class_idx));
@@ -549,7 +574,7 @@ static int get_binary_classes(struct srd_decoder *dec)
        PyObject *py_bin_classes, *py_bin_class;
        GSList *bin_classes;
        char **bin;
-       ssize_t i;
+       ssize_t bin_idx;
        PyGILState_STATE gstate;
 
        gstate = PyGILState_Ensure();
@@ -571,8 +596,9 @@ static int get_binary_classes(struct srd_decoder *dec)
                goto err_out;
        }
 
-       for (i = PyTuple_Size(py_bin_classes) - 1; i >= 0; i--) {
-               py_bin_class = PyTuple_GetItem(py_bin_classes, i);
+       bin_idx = PyTuple_Size(py_bin_classes);
+       while (bin_idx--) {
+               py_bin_class = PyTuple_GetItem(py_bin_classes, bin_idx);
                if (!py_bin_class)
                        goto except_out;
 
@@ -606,6 +632,79 @@ err_out:
        return SRD_ERR_PYTHON;
 }
 
+/* Convert logic_output_channels to GSList of 'struct srd_decoder_logic_output_channel'. */
+static int get_logic_output_channels(struct srd_decoder *dec)
+{
+       PyObject *py_logic_out_chs, *py_logic_out_ch, *py_item;
+       GSList *logic_out_chs;
+       struct srd_decoder_logic_output_channel *logic_out_ch;
+       ssize_t i;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
+
+       if (!PyObject_HasAttrString(dec->py_dec, "logic_output_channels")) {
+               PyGILState_Release(gstate);
+               return SRD_OK;
+       }
+
+       logic_out_chs = NULL;
+
+       py_logic_out_chs = PyObject_GetAttrString(dec->py_dec, "logic_output_channels");
+       if (!py_logic_out_chs)
+               goto except_out;
+
+       if (!PyTuple_Check(py_logic_out_chs)) {
+               srd_err("Protocol decoder %s logic_output_channels "
+                       "must be a tuple.", dec->name);
+               goto err_out;
+       }
+
+       for (i = PyTuple_Size(py_logic_out_chs) - 1; i >= 0; i--) {
+               py_logic_out_ch = PyTuple_GetItem(py_logic_out_chs, i);
+               if (!py_logic_out_ch)
+                       goto except_out;
+
+               if (!PyTuple_Check(py_logic_out_ch) || PyTuple_Size(py_logic_out_ch) != 2) {
+                       srd_err("Protocol decoder %s logic_output_channels "
+                               "must contain only tuples of 2 elements.",
+                               dec->name);
+                       goto err_out;
+               }
+               logic_out_ch = g_malloc0(sizeof(*logic_out_ch));
+               /* Add to list right away so it doesn't get lost. */
+               logic_out_chs = g_slist_prepend(logic_out_chs, logic_out_ch);
+
+               py_item = PyTuple_GetItem(py_logic_out_ch, 0);
+               if (!py_item)
+                       goto except_out;
+               if (py_str_as_str(py_item, &logic_out_ch->id) != SRD_OK)
+                       goto err_out;
+
+               py_item = PyTuple_GetItem(py_logic_out_ch, 1);
+               if (!py_item)
+                       goto except_out;
+               if (py_str_as_str(py_item, &logic_out_ch->desc) != SRD_OK)
+                       goto err_out;
+       }
+       dec->logic_output_channels = logic_out_chs;
+       Py_DECREF(py_logic_out_chs);
+       PyGILState_Release(gstate);
+
+       return SRD_OK;
+
+except_out:
+       srd_exception_catch("Failed to get %s decoder logic output channels",
+                       dec->name);
+
+err_out:
+       g_slist_free_full(logic_out_chs, &logic_output_channel_free);
+       Py_XDECREF(py_logic_out_chs);
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
+}
+
 /* Check whether the Decoder class defines the named method. */
 static int check_method(PyObject *py_dec, const char *mod_name,
                const char *method_name)
@@ -732,6 +831,7 @@ SRD_API int srd_decoder_load(const char *module_name)
        int is_subclass;
        const char *fail_txt;
        PyGILState_STATE gstate;
+       size_t ann_cls_count;
 
        if (!srd_check_init())
                return SRD_ERR;
@@ -874,12 +974,12 @@ SRD_API int srd_decoder_load(const char *module_name)
                goto err_out;
        }
 
-       if (get_annotations(d) != SRD_OK) {
+       if (get_annotations(d, &ann_cls_count) != SRD_OK) {
                fail_txt = "cannot get annotations";
                goto err_out;
        }
 
-       if (get_annotation_rows(d) != SRD_OK) {
+       if (get_annotation_rows(d, ann_cls_count) != SRD_OK) {
                fail_txt = "cannot get annotation rows";
                goto err_out;
        }
@@ -944,6 +1044,11 @@ SRD_API int srd_decoder_load(const char *module_name)
                goto err_out;
        }
 
+       if (get_logic_output_channels(d) != SRD_OK) {
+               fail_txt = "cannot get logic output channels";
+               goto err_out;
+       }
+
        PyGILState_Release(gstate);
 
        /* Append it to the list of loaded decoders. */
index c60ed4e47469a3cfcc0b84d3d73be8c7048a72d9..ca61fbc02e8c41f9cf89047efdf17b390db5e428 100644 (file)
 ##
 
 import sigrokdecode as srd
+from common.srdhelper import bitpack_lsb
 
 def disabled_enabled(v):
     return ['Disabled', 'Enabled'][v]
 
 def output_power(v):
-    return '%+ddBm' % [-4, -1, 2, 5][v]
+    return '{:+d}dBm'.format([-4, -1, 2, 5][v])
 
+# Notes on the implementation:
+# - A register's description is an iterable of tuples which contain:
+#   The starting bit position, the bit count, the name of a field, and
+#   an optional parser which interprets the field's content. Parser are
+#   expected to yield a single text string when they exist. Other types
+#   of output are passed to Python's .format() routine as is.
+# - Bit fields' width in registers determines the range of indices in
+#   table/tuple lookups. Keep the implementation as robust as possible
+#   during future maintenance. Avoid Python runtime errors when adjusting
+#   the decoder.
 regs = {
-# reg:   name                        offset width parser
-    0: [
-        ('FRAC',                          3, 12, None),
-        ('INT',                          15, 16, lambda v: 'Not Allowed' if v < 32 else v)
-    ],
-    1: [
-        ('MOD',                           3, 12, None),
-        ('Phase',                        15, 12, None),
-        ('Prescalar',                    27,  1, lambda v: ['4/5', '8/9'][v]),
-        ('Phase Adjust',                 28,  1, lambda v: ['Off', 'On'][v]),
-    ],
-    2: [
-        ('Counter Reset',                 3,  1, disabled_enabled),
-        ('Charge Pump Three-State',       4,  1, disabled_enabled),
-        ('Power-Down',                    5,  1, disabled_enabled),
-        ('PD Polarity',                   6,  1, lambda v: ['Negative', 'Positive'][v]),
-        ('LDP',                           7,  1, lambda v: ['10ns', '6ns'][v]),
-        ('LDF',                           8,  1, lambda v: ['FRAC-N', 'INT-N'][v]),
-        ('Charge Pump Current Setting',   9,  4, lambda v: '%0.2fmA @ 5.1kΩ' %
-            [0.31, 0.63, 0.94, 1.25, 1.56, 1.88, 2.19, 2.50,
-            2.81, 3.13, 3.44, 3.75, 4.06, 4.38, 4.69, 5.00][v]),
-        ('Double Buffer',                13,  1, disabled_enabled),
-        ('R Counter',                    14, 10, None),
-        ('RDIV2',                        24,  1, disabled_enabled),
-        ('Reference Doubler',            25,  1, disabled_enabled),
-        ('MUXOUT',                       26,  3, lambda v:
-            ['Three-State Output', 'DVdd', 'DGND', 'R Counter Output', 'N Divider Output',
-            'Analog Lock Detect', 'Digital Lock Detect', 'Reserved'][v]),
-        ('Low Noise and Low Spur Modes', 29,  2, lambda v:
-            ['Low Noise Mode', 'Reserved', 'Reserved', 'Low Spur Mode'][v])
-    ],
-    3: [
-        ('Clock Divider',                 3, 12, None),
-        ('Clock Divider Mode',           15,  2, lambda v:
-            ['Clock Divider Off', 'Fast Lock Enable', 'Resync Enable', 'Reserved'][v]),
-        ('CSR Enable',                   18,  1, disabled_enabled),
-        ('Charge Cancellation',          21,  1, disabled_enabled),
-        ('ABP',                          22,  1, lambda v: ['6ns (FRAC-N)', '3ns (INT-N)'][v]),
-        ('Band Select Clock Mode',       23,  1, lambda v: ['Low', 'High'][v])
-    ],
-    4: [
-        ('Output Power',                  3,  2, output_power),
-        ('Output Enable',                 5,  1, disabled_enabled),
-        ('AUX Output Power',              6,  2, output_power),
-        ('AUX Output Select',             8,  1, lambda v: ['Divided Output', 'Fundamental'][v]),
-        ('AUX Output Enable',             9,  1, disabled_enabled),
-        ('MTLD',                         10,  1, disabled_enabled),
-        ('VCO Power-Down',               11,  1, lambda v:
-            'VCO Powered ' + ('Down' if v == 1 else 'Up')),
-        ('Band Select Clock Divider',    12,  8, None),
-        ('RF Divider Select',            20,  3, lambda v: '÷' + str(2**v)),
-        ('Feedback Select',              23,  1, lambda v: ['Divided', 'Fundamental'][v]),
-    ],
-    5: [
-        ('LD Pin Mode',                  22,  2, lambda v:
-            ['Low', 'Digital Lock Detect', 'Low', 'High'][v])
-    ]
+    # Register description fields:
+    # offset, width, name, parser.
+    0: (
+        ( 3, 12, 'FRAC'),
+        (15, 16, 'INT',
+            None, lambda v: 'Not Allowed' if v < 23 else None,
+        ),
+    ),
+    1: (
+        ( 3, 12, 'MOD'),
+        (15, 12, 'Phase'),
+        (27,  1, 'Prescalar', lambda v: ('4/5', '8/9',)[v]),
+        (28,  1, 'Phase Adjust', lambda v: ('Off', 'On',)[v]),
+    ),
+    2: (
+        ( 3,  1, 'Counter Reset', disabled_enabled),
+        ( 4,  1, 'Charge Pump Three-State', disabled_enabled),
+        ( 5,  1, 'Power-Down', disabled_enabled),
+        ( 6,  1, 'PD Polarity', lambda v: ('Negative', 'Positive',)[v]),
+        ( 7,  1, 'LDP', lambda v: ('10ns', '6ns',)[v]),
+        ( 8,  1, 'LDF', lambda v: ('FRAC-N', 'INT-N',)[v]),
+        ( 9,  4, 'Charge Pump Current Setting',
+            lambda v: '{curr:0.2f}mA @ 5.1kΩ'.format(curr = (
+                0.31, 0.63, 0.94, 1.25, 1.56, 1.88, 2.19, 2.50,
+                2.81, 3.13, 3.44, 3.75, 4.06, 4.38, 4.69, 5.00,
+            )[v])),
+        (13,  1, 'Double Buffer', disabled_enabled),
+        (14, 10, 'R Counter'),
+        (24,  1, 'RDIV2', disabled_enabled),
+        (25,  1, 'Reference Doubler', disabled_enabled),
+        (26,  3, 'MUXOUT',
+            lambda v: '{text}'.format(text = (
+                'Three-State Output', 'DVdd', 'DGND',
+                'R Counter Output', 'N Divider Output',
+                'Analog Lock Detect', 'Digital Lock Detect',
+                'Reserved',
+            )[v])),
+        (29,  2, 'Low Noise and Low Spur Modes',
+            lambda v: '{text}'.format(text = (
+                'Low Noise Mode', 'Reserved', 'Reserved', 'Low Spur Mode',
+            )[v])),
+    ),
+    3: (
+        ( 3, 12, 'Clock Divider'),
+        (15,  2, 'Clock Divider Mode',
+            lambda v: '{text}'.format(text = (
+                'Clock Divider Off', 'Fast Lock Enable',
+                'Resync Enable', 'Reserved',
+            )[v])),
+        (18,  1, 'CSR Enable', disabled_enabled),
+        (21,  1, 'Charge Cancellation', disabled_enabled),
+        (22,  1, 'ABP', lambda v: ('6ns (FRAC-N)', '3ns (INT-N)',)[v]),
+        (23,  1, 'Band Select Clock Mode', lambda v: ('Low', 'High',)[v]),
+    ),
+    4: (
+        ( 3,  2, 'Output Power', output_power),
+        ( 5,  1, 'Output Enable', disabled_enabled),
+        ( 6,  2, 'AUX Output Power', output_power),
+        ( 8,  1, 'AUX Output Select',
+            lambda v: ('Divided Output', 'Fundamental',)[v]),
+        ( 9,  1, 'AUX Output Enable', disabled_enabled),
+        (10,  1, 'MTLD', disabled_enabled),
+        (11,  1, 'VCO Power-Down',
+            lambda v: 'VCO Powered {ud}'.format(ud = 'Down' if v else 'Up')),
+        (12,  8, 'Band Select Clock Divider'),
+        (20,  3, 'RF Divider Select', lambda v: '÷{:d}'.format(2 ** v)),
+        (23,  1, 'Feedback Select', lambda v: ('Divided', 'Fundamental',)[v]),
+    ),
+    5: (
+        (22,  2, 'LD Pin Mode',
+            lambda v: '{text}'.format(text = (
+                'Low', 'Digital Lock Detect', 'Low', 'High',
+            )[v])),
+    ),
 }
 
-ANN_REG = 0
+( ANN_REG, ANN_WARN, ) = range(2)
 
 class Decoder(srd.Decoder):
     api_version = 3
@@ -100,9 +128,11 @@ class Decoder(srd.Decoder):
     annotations = (
         # Sent from the host to the chip.
         ('write', 'Register write'),
+        ('warning', "Warnings"),
     )
     annotation_rows = (
         ('writes', 'Register writes', (ANN_REG,)),
+        ('warnings', 'Warnings', (ANN_WARN,)),
     )
 
     def __init__(self):
@@ -114,31 +144,87 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
+    def putg(self, ss, es, cls, data):
+        self.put(ss, es, self.out_ann, [ cls, data, ])
+
     def decode_bits(self, offset, width):
-        return (sum([(1 << i) if self.bits[offset + i][0] else 0 for i in range(width)]),
-            (self.bits[offset + width - 1][1], self.bits[offset][2]))
+        '''Extract a bit field. Expects LSB input data.'''
+        bits = self.bits[offset:][:width]
+        ss, es = bits[-1][1], bits[0][2]
+        value = bitpack_lsb(bits, 0)
+        return ( value, ( ss, es, ))
+
+    def decode_field(self, name, offset, width, parser = None, checker = None):
+        '''Interpret a bit field. Emits an annotation.'''
+        # Get the register field's content and position.
+        val, ( ss, es, ) = self.decode_bits(offset, width)
+        # Have the field's content formatted, emit an annotation.
+        formatted = parser(val) if parser else '{}'.format(val)
+        if formatted is not None:
+            text = ['{name}: {val}'.format(name = name, val = formatted)]
+        else:
+            text = ['{name}'.format(name = name)]
+        if text:
+            self.putg(ss, es, ANN_REG, text)
+        # Have the field's content checked, emit an optional warning.
+        warn = checker(val) if checker else None
+        if warn:
+            text = ['{}'.format(warn)]
+            self.putg(ss, es, ANN_WARN, text)
 
-    def decode_field(self, name, offset, width, parser):
-        val, pos = self.decode_bits(offset, width)
-        self.put(pos[0], pos[1], self.out_ann, [ANN_REG,
-            ['%s: %s' % (name, parser(val) if parser else str(val))]])
-        return val
+    def decode_word(self, ss, es, bits):
+        '''Interpret a 32bit word after accumulation completes.'''
+        # SPI transfer content must be exactly one 32bit word.
+        count = len(self.bits)
+        if count != 32:
+            text = [
+                'Frame error: Bit count: want 32, got {}'.format(count),
+                'Frame error: Bit count',
+                'Frame error',
+            ]
+            self.putg(ss, es, ANN_WARN, text)
+            return
+        # Holding bits in LSB order during interpretation simplifies
+        # bit field extraction. And annotation emitting routines expect
+        # this reverse order of bits' timestamps.
+        self.bits.reverse()
+        # Determine which register was accessed.
+        reg_addr, ( reg_ss, reg_es, ) = self.decode_bits(0, 3)
+        text = [
+            'Register: {addr}'.format(addr = reg_addr),
+            'Reg: {addr}'.format(addr = reg_addr),
+            '[{addr}]'.format(addr = reg_addr),
+        ]
+        self.putg(reg_ss, reg_es, ANN_REG, text)
+        # Interpret the register's content (when parsers are available).
+        field_descs = regs.get(reg_addr, None)
+        if not field_descs:
+            return
+        for field_desc in field_descs:
+            parser = None
+            checker = None
+            if len(field_desc) == 3:
+                start, count, name, = field_desc
+            elif len(field_desc) == 4:
+                start, count, name, parser = field_desc
+            elif len(field_desc) == 5:
+                start, count, name, parser, checker = field_desc
+            else:
+                # Unsupported regs{} syntax, programmer's error.
+                return
+            self.decode_field(name, start, count, parser, checker)
 
     def decode(self, ss, es, data):
+        ptype, _, _ = data
+
+        if ptype == 'TRANSFER':
+            # Process accumulated bits after completion of a transfer.
+            self.decode_word(ss, es, self.bits)
+            self.bits.clear()
 
-        ptype, data1, data2 = data
-
-        if ptype == 'CS-CHANGE':
-            if data1 == 1:
-                if len(self.bits) == 32:
-                    reg_value, reg_pos = self.decode_bits(0, 3)
-                    self.put(reg_pos[0], reg_pos[1], self.out_ann, [ANN_REG,
-                        ['Register: %d' % reg_value, 'Reg: %d' % reg_value,
-                         '[%d]' % reg_value]])
-                    if reg_value < len(regs):
-                        field_descs = regs[reg_value]
-                        for field_desc in field_descs:
-                            field = self.decode_field(*field_desc)
-                self.bits = []
         if ptype == 'BITS':
-            self.bits = data1 + self.bits
+            _, mosi_bits, miso_bits = data
+            # Accumulate bits in MSB order as they are seen in SPI frames.
+            msb_bits = mosi_bits.copy()
+            msb_bits.reverse()
+            self.bits.extend(msb_bits)
index 0767789a0a594520b7fe3871f3e530c9ce66a00c..fee4d9b058cd7e4243f5a36942a6f71ecb9da09e 100644 (file)
 
 # Vendor code
 vendor_code = {
-    0x1e: 'Atmel',
+    0x1E: 'Atmel',
     0x00: 'Device locked',
 }
 
 # (Part family + flash size, part number)
 part = {
     (0x90, 0x01): 'AT90S1200',
+    (0x90, 0x05): 'ATtiny12',
+    (0x90, 0x06): 'ATtiny15',
+    (0x90, 0x07): 'ATtiny13',
     (0x91, 0x01): 'AT90S2313',
+    (0x91, 0x02): 'AT90S2323',
+    (0x91, 0x03): 'AT90S2343',
+    (0x91, 0x05): 'AT90S2333',
+    (0x91, 0x06): 'ATtiny22',
+    (0x91, 0x07): 'ATtiny28',
+    (0x91, 0x08): 'ATtiny25',
+    (0x91, 0x09): 'ATtiny26',
+    (0x91, 0x0A): 'ATtiny2313',
+    (0x91, 0x0B): 'ATtiny24',
+    (0x91, 0x0C): 'ATtiny261',
     (0x92, 0x01): 'AT90S4414',
-    (0x92, 0x05): 'ATmega48', # 4kB flash
+    (0x92, 0x03): 'AT90S4433',
+    (0x92, 0x05): 'ATmega48(A)',
+    (0x92, 0x06): 'ATtiny45',
+    (0x92, 0x08): 'ATtiny461',
+    (0x92, 0x09): 'ATtiny48',
+    (0x92, 0x0A): 'ATmega48PA',
+    (0x92, 0x0D): 'ATtiny4313',
+    (0x92, 0x10): 'ATmega48PB',
     (0x93, 0x01): 'AT90S8515',
-    (0x93, 0x0a): 'ATmega88', # 8kB flash
-    (0x94, 0x06): 'ATmega168', # 16kB flash
-    (0xff, 0xff): 'Device code erased, or target missing',
+    (0x93, 0x03): 'AT90S8535',
+    (0x93, 0x07): 'ATmega8',
+    (0x93, 0x0A): 'ATmega88(A)',
+    (0x93, 0x0B): 'ATtiny85',
+    (0x93, 0x0D): 'ATtiny861',
+    (0x93, 0x0F): 'ATmega88PA',
+    (0x93, 0x11): 'ATtiny88',
+    (0x93, 0x16): 'ATmega88PB',
+    (0x93, 0x89): 'ATmega8U2',
+    (0x94, 0x01): 'ATmega161',
+    (0x94, 0x02): 'ATmega163',
+    (0x94, 0x03): 'ATmega16',
+    (0x94, 0x04): 'ATmega162',
+    (0x94, 0x06): 'ATmega168(A)',
+    (0x94, 0x0A): 'ATmega164PA',
+    (0x94, 0x0B): 'ATmega168PA',
+    (0x94, 0x0F): 'ATmega164A',
+    (0x94, 0x12): 'ATtiny1634',
+    (0x94, 0x15): 'ATmega168PB',
+    (0x94, 0x88): 'ATmega16U4',
+    (0x94, 0x89): 'ATmega16U2',
+    (0x95, 0x01): 'ATmega32',
+    (0x95, 0x01): 'ATmega323',
+    (0x95, 0x0F): 'ATmega328P',
+    (0x95, 0x11): 'ATmega324PA',
+    (0x95, 0x14): 'ATmega328',
+    (0x95, 0x15): 'ATmega324A',
+    (0x95, 0x87): 'ATmega32U4',
+    (0x95, 0x8A): 'ATmega32U2',
+    (0x96, 0x08): 'ATmega640',
+    (0x96, 0x09): 'ATmega644(A)',
+    (0x96, 0x0A): 'ATmega644PA',
+    (0x97, 0x01): 'ATmega103',
+    (0x97, 0x03): 'ATmega1280',
+    (0x97, 0x04): 'ATmega1281',
+    (0x97, 0x05): 'ATmega1284P',
+    (0x97, 0x06): 'ATmega1284',
+    (0x98, 0x01): 'ATmega2560',
+    (0x98, 0x02): 'ATmega2561',
+    (0xFF, 0xFF): 'Device code erased, or target missing',
     (0x01, 0x02): 'Device locked',
-    # TODO: Lots more entries.
 }
index bb52bb683227b1bf9c1ad4b35d0f4a4ab9301a49..9e3c5df12a7e824c11037ca401237b4d156422e5 100644 (file)
 import sigrokdecode as srd
 from .parts import *
 
+class Ann:
+    PE, RSB0, RSB1, RSB2, CE, RFB, RHFB, REFB, \
+    RLB, REEM, RP, LPMP, WP, WARN, DEV, = range(15)
+
 VENDOR_CODE_ATMEL = 0x1e
 
 class Decoder(srd.Decoder):
@@ -41,14 +45,20 @@ class Decoder(srd.Decoder):
         ('rfb', 'Read fuse bits'),
         ('rhfb', 'Read high fuse bits'),
         ('refb', 'Read extended fuse bits'),
+        ('rlb', 'Read lock bits'),
+        ('reem', 'Read EEPROM memory'),
+        ('rp', 'Read program memory'),
+        ('lpmp' , 'Load program memory page'),
+        ('wp', 'Write program memory'),
         ('warning', 'Warning'),
         ('dev', 'Device'),
     )
     annotation_rows = (
-        ('bits', 'Bits', ()),
-        ('commands', 'Commands', tuple(range(7 + 1))),
-        ('warnings', 'Warnings', (8,)),
-        ('devs', 'Devices', (9,)),
+        ('commands', 'Commands', (Ann.PE, Ann.RSB0, Ann.RSB1, Ann.RSB2,
+            Ann.CE, Ann.RFB, Ann.RHFB, Ann.REFB,
+            Ann.RLB, Ann.REEM, Ann.RP, Ann.LPMP, Ann.WP,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+        ('devs', 'Devices', (Ann.DEV,)),
     )
 
     def __init__(self):
@@ -70,17 +80,17 @@ class Decoder(srd.Decoder):
     def handle_cmd_programming_enable(self, cmd, ret):
         # Programming enable.
         # Note: The chip doesn't send any ACK for 'Programming enable'.
-        self.putx([0, ['Programming enable']])
+        self.putx([Ann.PE, ['Programming enable']])
 
         # Sanity check on reply.
         if ret[1:4] != [0xac, 0x53, cmd[2]]:
-            self.putx([8, ['Warning: Unexpected bytes in reply!']])
+            self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
 
     def handle_cmd_read_signature_byte_0x00(self, cmd, ret):
         # Signature byte 0x00: vendor code.
         self.vendor_code = ret[3]
         v = vendor_code[self.vendor_code]
-        self.putx([1, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
+        self.putx([Ann.RSB0, ['Vendor code: 0x%02x (%s)' % (ret[3], v)]])
 
         # Store for later.
         self.xx = cmd[1] # Same as ret[2].
@@ -89,16 +99,16 @@ class Decoder(srd.Decoder):
 
         # Sanity check on reply.
         if ret[1] != 0x30 or ret[2] != cmd[1]:
-            self.putx([8, ['Warning: Unexpected bytes in reply!']])
+            self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
 
         # Sanity check for the vendor code.
         if self.vendor_code != VENDOR_CODE_ATMEL:
-            self.putx([8, ['Warning: Vendor code was not 0x1e (Atmel)!']])
+            self.putx([Ann.WARN, ['Warning: Vendor code was not 0x1e (Atmel)!']])
 
     def handle_cmd_read_signature_byte_0x01(self, cmd, ret):
         # Signature byte 0x01: part family and memory size.
         self.part_fam_flash_size = ret[3]
-        self.putx([2, ['Part family / memory size: 0x%02x' % ret[3]]])
+        self.putx([Ann.RSB1, ['Part family / memory size: 0x%02x' % ret[3]]])
 
         # Store for later.
         self.mm = cmd[3]
@@ -106,20 +116,23 @@ class Decoder(srd.Decoder):
 
         # Sanity check on reply.
         if ret[1] != 0x30 or ret[2] != cmd[1] or ret[0] != self.yy:
-            self.putx([8, ['Warning: Unexpected bytes in reply!']])
+            self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
 
     def handle_cmd_read_signature_byte_0x02(self, cmd, ret):
         # Signature byte 0x02: part number.
         self.part_number = ret[3]
-        self.putx([3, ['Part number: 0x%02x' % ret[3]]])
+        self.putx([Ann.RSB2, ['Part number: 0x%02x' % ret[3]]])
 
-        p = part[(self.part_fam_flash_size, self.part_number)]
-        data = [9, ['Device: Atmel %s' % p]]
-        self.put(self.ss_device, self.es_cmd, self.out_ann, data)
+        # Part name if known
+        key = (self.part_fam_flash_size, self.part_number)
+        if key in part:
+            p = part[key]
+            data = [Ann.DEV, ['Device: Atmel %s' % p]]
+            self.put(self.ss_device, self.es_cmd, self.out_ann, data)
 
         # Sanity check on reply.
         if ret[1] != 0x30 or ret[2] != self.xx or ret[0] != self.mm:
-            self.putx([8, ['Warning: Unexpected bytes in reply!']])
+            self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
 
         self.xx, self.yy, self.zz, self.mm = 0, 0, 0, 0
 
@@ -127,36 +140,78 @@ class Decoder(srd.Decoder):
         # Chip erase (erases both flash an EEPROM).
         # Upon successful chip erase, the lock bits will also be erased.
         # The only way to end a Chip Erase cycle is to release RESET#.
-        self.putx([4, ['Chip erase']])
+        self.putx([Ann.CE, ['Chip erase']])
 
         # TODO: Check/handle RESET#.
 
         # Sanity check on reply.
         bit = (ret[2] & (1 << 7)) >> 7
         if ret[1] != 0xac or bit != 1 or ret[3] != cmd[2]:
-            self.putx([8, ['Warning: Unexpected bytes in reply!']])
+            self.putx([Ann.WARN, ['Warning: Unexpected bytes in reply!']])
 
     def handle_cmd_read_fuse_bits(self, cmd, ret):
         # Read fuse bits.
-        self.putx([5, ['Read fuse bits: 0x%02x' % ret[3]]])
+        self.putx([Ann.RFB, ['Read fuse bits: 0x%02x' % ret[3]]])
 
         # TODO: Decode fuse bits.
         # TODO: Sanity check on reply.
 
     def handle_cmd_read_fuse_high_bits(self, cmd, ret):
         # Read fuse high bits.
-        self.putx([6, ['Read fuse high bits: 0x%02x' % ret[3]]])
+        self.putx([Ann.RHFB, ['Read fuse high bits: 0x%02x' % ret[3]]])
 
         # TODO: Decode fuse bits.
         # TODO: Sanity check on reply.
 
     def handle_cmd_read_extended_fuse_bits(self, cmd, ret):
         # Read extended fuse bits.
-        self.putx([7, ['Read extended fuse bits: 0x%02x' % ret[3]]])
+        self.putx([Ann.REFB, ['Read extended fuse bits: 0x%02x' % ret[3]]])
 
         # TODO: Decode fuse bits.
         # TODO: Sanity check on reply.
 
+    def handle_cmd_read_lock_bits(self, cmd, ret):
+        # Read lock bits
+        self.putx([Ann.RLB, ['Read lock bits: 0x%02x' % ret[3]]])
+
+    def handle_cmd_read_eeprom_memory(self, cmd, ret):
+        # Read EEPROM Memory
+        _addr = ((cmd[1] & 1) << 8) + cmd[2]
+        self.putx([Ann.REEM, ['Read EEPROM Memory: [0x%03x]: 0x%02x' % (_addr, ret[3])]])
+
+    def handle_cmd_read_program_memory(self, cmd, ret):
+        # Read Program Memory
+        _HL = 'Low'
+        _H = 'L'
+        if cmd[0] & 0x08:
+            _HL = 'High'
+            _H = 'H'
+        _addr = ((cmd[1] & 0x0f) << 8) + cmd[2]
+        self.putx([Ann.RP, [
+            'Read program memory %s: [0x%03x]: 0x%02x' % (_HL, _addr, ret[3]),
+            '[%03x%s]:%02x' % (_addr, _H, ret[3]),
+            '%02x' % ret[3]
+        ]])
+
+    def handle_cmd_load_program_memory_page(self, cmd, ret):
+        # Load Program Memory Page
+        _HL = 'Low'
+        _H = 'L'
+        if cmd[0] & 0x08:
+            _HL = 'High'
+            _H = 'H'
+        _addr = cmd[2] & 0x1F
+        self.putx([Ann.LPMP, [
+            'Load program memory page %s: [0x%03x]: 0x%02x' % (_HL, _addr, cmd[3]),
+            '[%03x%s]=%02x' % (_addr, _H, cmd[3]),
+            '%02x' % cmd[3]
+        ]])
+
+    def handle_cmd_write_program_memory_page(self, cmd, ret):
+        # Write Program Memory Page
+        _addr = ((cmd[1] & 0x0F) << 3) + (cmd[2] << 5)
+        self.putx([Ann.WP, ['Write program memory page: 0x%02x' % _addr]])
+
     def handle_command(self, cmd, ret):
         if cmd[:2] == [0xac, 0x53]:
             self.handle_cmd_programming_enable(cmd, ret)
@@ -174,10 +229,20 @@ class Decoder(srd.Decoder):
             self.handle_cmd_read_signature_byte_0x01(cmd, ret)
         elif cmd[0] == 0x30 and cmd[2] == 0x02:
             self.handle_cmd_read_signature_byte_0x02(cmd, ret)
+        elif cmd[:2] == [0x58, 0x00]:
+            self.handle_cmd_read_lock_bits(cmd,ret)
+        elif cmd[0] == 0xa0 and (cmd[1] & (3 << 6)) == (0 << 6):
+            self.handle_cmd_read_eeprom_memory(cmd, ret)
+        elif (cmd[0] == 0x20 or cmd[0] == 0x28) and ((cmd[1] & 0xf0) == 0x00):
+            self.handle_cmd_read_program_memory(cmd, ret)
+        elif (cmd[0] == 0x40 or cmd[0] == 0x48) and ((cmd[1] & 0xf0) == 0x00):
+            self.handle_cmd_load_program_memory_page(cmd, ret)
+        elif (cmd[0] == 0x4C and ((cmd[1] & 0xf0) == 0x00)):
+            self.handle_cmd_write_program_memory_page(cmd, ret)
         else:
             c = '%02x %02x %02x %02x' % tuple(cmd)
             r = '%02x %02x %02x %02x' % tuple(ret)
-            self.putx([0, ['Unknown command: %s (reply: %s)!' % (c, r)]])
+            self.putx([Ann.WARN, ['Unknown command: %s (reply: %s)!' % (c, r)]])
 
     def decode(self, ss, es, data):
         ptype, mosi, miso = data
diff --git a/decoders/caliper/__init__.py b/decoders/caliper/__init__.py
new file mode 100644 (file)
index 0000000..44dab08
--- /dev/null
@@ -0,0 +1,36 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
+##
+## 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/>.
+##
+
+'''
+This decoder interprets the digital output of cheap generic calipers
+(usually made in China), and shows the measured value in millimeters
+or inches.
+
+Notice that these devices often communicate on voltage levels below
+3.3V and may require additional circuitry to capture the signal.
+
+This decoder does not work for calipers using the Digimatic protocol
+(eg. Mitutoyo and similar brands).
+
+For more information see:
+http://www.shumatech.com/support/chinese_scales.htm
+https://www.instructables.com/id/Reading-Digital-Callipers-with-an-Arduino-USB/
+'''
+
+from .pd import Decoder
diff --git a/decoders/caliper/pd.py b/decoders/caliper/pd.py
new file mode 100644 (file)
index 0000000..20a2a55
--- /dev/null
@@ -0,0 +1,146 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tomas Mudrunka <harvie@github>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is
+## furnished to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+## SOFTWARE.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+
+# Millimeters per inch.
+mm_per_inch = 25.4
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'caliper'
+    name = 'Caliper'
+    longname = 'Digital calipers'
+    desc = 'Protocol of cheap generic digital calipers.'
+    license = 'mit'
+    inputs = ['logic']
+    outputs = []
+    channels = (
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock line'},
+        {'id': 'data', 'name': 'DATA', 'desc': 'Serial data line'},
+    )
+    options = (
+        {'id': 'timeout_ms', 'desc': 'Packet timeout in ms, 0 to disable',
+            'default': 10},
+        {'id': 'unit', 'desc': 'Convert units', 'default': 'keep',
+            'values': ('keep', 'mm', 'inch')},
+        {'id': 'changes', 'desc': 'Changes only', 'default': 'no',
+            'values': ('no', 'yes')},
+    )
+    tags = ['Analog/digital', 'Sensor']
+    annotations = (
+        ('measurement', 'Measurement'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('measurements', 'Measurements', (0,)),
+        ('warnings', 'Warnings', (1,)),
+    )
+
+    def metadata(self, key, value):
+       if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.ss, self.es = 0, 0
+        self.number_bits = []
+        self.flags_bits = []
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putg(self, ss, es, cls, data):
+        self.put(ss, es, self.out_ann, [cls, data])
+
+    def decode(self):
+        last_sent = None
+        timeout_ms = self.options['timeout_ms']
+        want_unit = self.options['unit']
+        show_all = self.options['changes'] == 'no'
+        wait_cond = [{0: 'r'}]
+        if timeout_ms:
+            snum_per_ms = self.samplerate / 1000
+            timeout_snum = timeout_ms * snum_per_ms
+            wait_cond.append({'skip': round(timeout_snum)})
+        while True:
+            # Sample data at the rising clock edge. Optionally timeout
+            # after inactivity for a user specified period. Present the
+            # number of unprocessed bits to the user for diagnostics.
+            clk, data = self.wait(wait_cond)
+            if timeout_ms and not self.matched[0]:
+                if self.number_bits or self.flags_bits:
+                    count = len(self.number_bits) + len(self.flags_bits)
+                    self.putg(self.ss, self.samplenum, 1, [
+                        'timeout with {} bits in buffer'.format(count),
+                        'timeout ({} bits)'.format(count),
+                        'timeout',
+                    ])
+                self.reset()
+                continue
+
+            # Store position of first bit and last activity.
+            # Shift in measured number and flag bits.
+            if not self.ss:
+                self.ss = self.samplenum
+            self.es = self.samplenum
+            if len(self.number_bits) < 16:
+                self.number_bits.append(data)
+                continue
+            if len(self.flags_bits) < 8:
+                self.flags_bits.append(data)
+                if len(self.flags_bits) < 8:
+                    continue
+
+            # Get raw values from received data bits. Run the number
+            # conversion, controlled by flags and/or user specs.
+            negative = bool(self.flags_bits[4])
+            is_inch = bool(self.flags_bits[7])
+            number = bitpack(self.number_bits)
+            if negative:
+                number = -number
+            if is_inch:
+                number /= 2000
+                if want_unit == 'mm':
+                    number *= mm_per_inch
+                    is_inch = False
+            else:
+                number /= 100
+                if want_unit == 'inch':
+                    number = round(number / mm_per_inch, 4)
+                    is_inch = True
+            unit = 'in' if is_inch else 'mm'
+
+            # Construct and emit an annotation.
+            if show_all or (number, unit) != last_sent:
+                self.putg(self.ss, self.es, 0, [
+                    '{number}{unit}'.format(**locals()),
+                    '{number}'.format(**locals()),
+                ])
+                last_sent = (number, unit)
+
+            # Reset internal state for the start of the next packet.
+            self.reset()
index 3dbadc090313cc424feaf13051cf724e82c29108..fcd13e6aefba1bdcc3215d108b335cf452750a48 100644 (file)
@@ -18,6 +18,7 @@
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
+from common.srdhelper import bitpack_msb
 import sigrokdecode as srd
 
 class SamplerateError(Exception):
@@ -34,7 +35,7 @@ class Decoder(srd.Decoder):
     desc = 'Field bus protocol for distributed realtime control.'
     license = 'gplv2+'
     inputs = ['logic']
-    outputs = []
+    outputs = ['can']
     tags = ['Automotive']
     channels = (
         {'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
@@ -79,6 +80,7 @@ class Decoder(srd.Decoder):
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
 
     def set_bit_rate(self, bitrate):
         self.bit_width = float(self.samplerate) / float(bitrate)
@@ -117,6 +119,9 @@ class Decoder(srd.Decoder):
     def putb(self, data):
         self.putg(self.ss_block, self.samplenum, data)
 
+    def putpy(self, data):
+        self.put(self.ss_packet, self.es_packet, self.out_python, data)
+
     def reset_variables(self):
         self.state = 'IDLE'
         self.sof = self.frame_type = self.dlc = None
@@ -128,6 +133,8 @@ class Decoder(srd.Decoder):
         self.ss_bit12 = None
         self.ss_bit32 = None
         self.ss_databytebits = []
+        self.frame_bytes = []
+        self.rtr_type = None
         self.fd = False
         self.rtr = None
 
@@ -198,7 +205,7 @@ class Decoder(srd.Decoder):
 
             x = self.last_databit + 1
             crc_bits = self.bits[x:x + self.crc_len + 1]
-            self.crc = int(''.join(str(d) for d in crc_bits), 2)
+            self.crc = bitpack_msb(crc_bits)
             self.putb([11, ['%s sequence: 0x%04x' % (crc_type, self.crc),
                             '%s: 0x%04x' % (crc_type, self.crc), '%s' % crc_type]])
             if not self.is_valid_crc(crc_bits):
@@ -235,6 +242,10 @@ class Decoder(srd.Decoder):
             self.putb([2, ['End of frame', 'EOF', 'E']])
             if self.rawbits[-7:] != [1, 1, 1, 1, 1, 1, 1]:
                 self.putb([16, ['End of frame (EOF) must be 7 recessive bits']])
+            self.es_packet = self.samplenum
+            py_data = tuple([self.frame_type, self.fullid, self.rtr_type,
+                self.dlc, self.frame_bytes])
+            self.putpy(py_data)
             self.reset_variables()
             return True
 
@@ -266,6 +277,7 @@ class Decoder(srd.Decoder):
                 rtr = 'remote' if self.bits[12] == 1 else 'data'
                 self.put12([8, ['Remote transmission request: %s frame' % rtr,
                                 'RTR: %s frame' % rtr, 'RTR']])
+                self.rtr_type = rtr
                 self.dlc_start = 15
 
         if bitnum == 15 and self.fd:
@@ -283,7 +295,7 @@ class Decoder(srd.Decoder):
 
         # Bits 15-18: Data length code (DLC), in number of bytes (0-8).
         elif bitnum == self.dlc_start + 3:
-            self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
+            self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
             self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
@@ -300,7 +312,8 @@ class Decoder(srd.Decoder):
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
             for i in range(dlc2len(self.dlc)):
                 x = self.dlc_start + 4 + (8 * i)
-                b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+                b = bitpack_msb(self.bits[x:x + 8])
+                self.frame_bytes.append(b)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
                 self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
@@ -323,12 +336,12 @@ class Decoder(srd.Decoder):
 
         # Bits 14-31: Extended identifier (EID[17..0])
         elif bitnum == 31:
-            self.eid = int(''.join(str(d) for d in self.bits[14:]), 2)
+            self.eid = bitpack_msb(self.bits[14:])
             s = '%d (0x%x)' % (self.eid, self.eid)
             self.putb([4, ['Extended Identifier: %s' % s,
                            'Extended ID: %s' % s, 'Extended ID', 'EID']])
 
-            self.fullid = self.id << 18 | self.eid
+            self.fullid = self.ident << 18 | self.eid
             s = '%d (0x%x)' % (self.fullid, self.fullid)
             self.putb([5, ['Full Identifier: %s' % s, 'Full ID: %s' % s,
                            'Full ID', 'FID']])
@@ -347,9 +360,10 @@ class Decoder(srd.Decoder):
             self.rtr = can_rx
 
             if not self.fd:
-               rtr = 'remote' if can_rx == 1 else 'data'
-               self.putx([8, ['Remote transmission request: %s frame' % rtr,
+                rtr = 'remote' if can_rx == 1 else 'data'
+                self.putx([8, ['Remote transmission request: %s frame' % rtr,
                               'RTR: %s frame' % rtr, 'RTR']])
+                self.rtr_type = rtr
 
         # Bit 33: RB1 (reserved bit)
         elif bitnum == 33:
@@ -383,7 +397,7 @@ class Decoder(srd.Decoder):
 
         # Bits 35-38: Data length code (DLC), in number of bytes (0-8).
         elif bitnum == self.dlc_start + 3:
-            self.dlc = int(''.join(str(d) for d in self.bits[self.dlc_start:self.dlc_start + 4]), 2)
+            self.dlc = bitpack_msb(self.bits[self.dlc_start:self.dlc_start + 4])
             self.putb([10, ['Data length code: %d' % self.dlc,
                             'DLC: %d' % self.dlc, 'DLC']])
             self.last_databit = self.dlc_start + 3 + (dlc2len(self.dlc) * 8)
@@ -398,7 +412,8 @@ class Decoder(srd.Decoder):
             self.ss_databytebits.append(self.samplenum) # Last databyte bit.
             for i in range(dlc2len(self.dlc)):
                 x = self.dlc_start + 4 + (8 * i)
-                b = int(''.join(str(d) for d in self.bits[x:x + 8]), 2)
+                b = bitpack_msb(self.bits[x:x + 8])
+                self.frame_bytes.append(b)
                 ss = self.ss_databytebits[i * 8]
                 es = self.ss_databytebits[((i + 1) * 8) - 1]
                 self.putg(ss, es, [0, ['Data byte %d: 0x%02x' % (i, b),
@@ -433,6 +448,7 @@ class Decoder(srd.Decoder):
 
         # Bit 0: Start of frame (SOF) bit
         if bitnum == 0:
+            self.ss_packet = self.samplenum
             self.putx([1, ['Start of frame', 'SOF', 'S']])
             if can_rx != 0:
                 self.putx([16, ['Start of frame (SOF) must be a dominant bit']])
@@ -444,10 +460,13 @@ class Decoder(srd.Decoder):
         # Bits 1-11: Identifier (ID[10..0])
         # The bits ID[10..4] must NOT be all recessive.
         elif bitnum == 11:
-            self.id = int(''.join(str(d) for d in self.bits[1:]), 2)
-            s = '%d (0x%x)' % (self.id, self.id),
+            # BEWARE! Don't clobber the decoder's .id field which is
+            # part of its boiler plate!
+            self.ident = bitpack_msb(self.bits[1:])
+            self.fullid = self.ident
+            s = '%d (0x%x)' % (self.ident, self.ident),
             self.putb([3, ['Identifier: %s' % s, 'ID: %s' % s, 'ID']])
-            if (self.id & 0x7f0) == 0x7f0:
+            if (self.ident & 0x7f0) == 0x7f0:
                 self.putb([16, ['Identifier bits 10..4 must not be all recessive']])
 
         # RTR or SRR bit, depending on frame type (gets handled later).
index 6c45af98b7ae9232b8d115caafa6478801436d20..b56cce6df3dbb2385136f462ac7af45fc11e706c 100644 (file)
@@ -31,6 +31,20 @@ def bin2int(s: str):
 def bitpack(bits):
     return sum([b << i for i, b in enumerate(bits)])
 
+def bitpack_lsb(bits, idx=None):
+    '''Conversion from LSB first bit sequence to integer.'''
+    if idx is not None:
+        bits = [b[idx] for b in bits]
+    return bitpack(bits)
+
+def bitpack_msb(bits, idx=None):
+    '''Conversion from MSB first bit sequence to integer.'''
+    bits = bits[:]
+    if idx is not None:
+        bits = [b[idx] for b in bits]
+    bits.reverse()
+    return bitpack(bits)
+
 def bitunpack(num, minbits=0):
     res = []
     while num or minbits > 0:
index c6ee63d519e672822a438a9823e7f6a47ad2b792..a66cb19aa59ef71a0a4d9ea4c868171f681eca32 100644 (file)
@@ -189,6 +189,16 @@ chips = {
         'addr_pins': 3, # Called E0, E1, E2 on this chip.
         'max_speed': 400,
     },
+    'st_m24c32': {
+        'vendor': 'ST',
+        'model': 'M24C32',
+        'size': 4 * 1024,
+        'page_size': 32,
+        'page_wraparound': True,
+        'addr_bytes': 2,
+        'addr_pins': 3, # Called E0, E1, E2 on this chip.
+        'max_speed': 1000,
+    },
 
     # Xicor
     'xicor_x24c02': {
index 549ee2df9b7a4b19694771ed3f5dc4c822c0d30f..7491f581c572e15fec3864f6e2f92b98bcb9310e 100644 (file)
@@ -17,6 +17,7 @@
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
+import copy
 import sigrokdecode as srd
 from .lists import *
 
@@ -416,16 +417,25 @@ class Decoder(srd.Decoder):
             self.reset_variables()
 
     def decode(self, ss, es, data):
-        self.cmd, self.databyte = data
+        cmd, _ = data
 
         # Collect the 'BITS' packet, then return. The next packet is
         # guaranteed to belong to these bits we just stored.
-        if self.cmd == 'BITS':
-            self.bits = self.databyte
+        if cmd == 'BITS':
+            _, databits = data
+            self.bits = copy.deepcopy(databits)
             return
 
-        # Store the start/end samples of this I²C packet.
+        # Store the start/end samples of this I²C packet. Deep copy
+        # caller's data, assuming that implementation details of the
+        # above complex methods can access the data after returning
+        # from the .decode() invocation, with the data having become
+        # invalid by that time of access. This conservative approach
+        # can get weakened after close inspection of those methods.
         self.ss, self.es = ss, es
+        _, databyte = data
+        databyte = copy.deepcopy(databyte)
+        self.cmd, self.databyte = cmd, databyte
 
         # State machine.
         s = 'handle_%s' % self.state.lower().replace(' ', '_')
index 82976627bdcc92ce972a181d426bc98fb596a034..2259b4538654211128dc71e1bc7fd2e4228c5607 100644 (file)
@@ -21,6 +21,7 @@
 # TODO: Implement support for inverting SDA/SCL levels (0->1 and 1->0).
 # TODO: Implement support for detecting various bus errors.
 
+from common.srdhelper import bitpack_msb
 import sigrokdecode as srd
 
 '''
@@ -45,20 +46,24 @@ Packet:
 command. Slave addresses do not include bit 0 (the READ/WRITE indication bit).
 For example, a slave address field could be 0x51 (instead of 0xa2).
 For 'START', 'START REPEAT', 'STOP', 'ACK', and 'NACK' <pdata> is None.
+For 'BITS' <pdata> is a sequence of tuples of bit values and their start and
+stop positions, in LSB first order (although the I2C protocol is MSB first).
 '''
 
-# CMD: [annotation-type-index, long annotation, short annotation]
+# Meaning of table items:
+# command -> [annotation class, annotation text in order of decreasing length]
 proto = {
-    'START':           [0, 'Start',         'S'],
-    'START REPEAT':    [1, 'Start repeat',  'Sr'],
-    'STOP':            [2, 'Stop',          'P'],
-    'ACK':             [3, 'ACK',           'A'],
-    'NACK':            [4, 'NACK',          'N'],
-    'BIT':             [5, 'Bit',           'B'],
-    'ADDRESS READ':    [6, 'Address read',  'AR'],
-    'ADDRESS WRITE':   [7, 'Address write', 'AW'],
-    'DATA READ':       [8, 'Data read',     'DR'],
-    'DATA WRITE':      [9, 'Data write',    'DW'],
+    'START':         [0, 'Start', 'S'],
+    'START REPEAT':  [1, 'Start repeat', 'Sr'],
+    'STOP':          [2, 'Stop', 'P'],
+    'ACK':           [3, 'ACK', 'A'],
+    'NACK':          [4, 'NACK', 'N'],
+    'BIT':           [5, '{b:1d}'],
+    'ADDRESS READ':  [6, 'Address read: {b:02X}', 'AR: {b:02X}', '{b:02X}'],
+    'ADDRESS WRITE': [7, 'Address write: {b:02X}', 'AW: {b:02X}', '{b:02X}'],
+    'DATA READ':     [8, 'Data read: {b:02X}', 'DR: {b:02X}', '{b:02X}'],
+    'DATA WRITE':    [9, 'Data write: {b:02X}', 'DW: {b:02X}', '{b:02X}'],
+    'WARN':          [10, '{text}'],
 }
 
 class Decoder(srd.Decoder):
@@ -109,15 +114,15 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.ss = self.es = self.ss_byte = -1
-        self.bitcount = 0
-        self.databyte = 0
-        self.wr = -1
-        self.is_repeat_start = 0
-        self.state = 'FIND START'
+        self.is_write = None
+        self.rem_addr_bytes = None
+        self.slave_addr_7 = None
+        self.slave_addr_10 = None
+        self.is_repeat_start = False
         self.pdu_start = None
         self.pdu_bits = 0
-        self.bits = []
+        self.data_bits = []
+        self.bitwidth = 0
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -130,136 +135,210 @@ class Decoder(srd.Decoder):
         self.out_bitrate = self.register(srd.OUTPUT_META,
                 meta=(int, 'Bitrate', 'Bitrate from Start bit to Stop bit'))
 
-    def putx(self, data):
-        self.put(self.ss, self.es, self.out_ann, data)
-
-    def putp(self, data):
-        self.put(self.ss, self.es, self.out_python, data)
-
-    def putb(self, data):
-        self.put(self.ss, self.es, self.out_binary, data)
-
-    def handle_start(self, pins):
-        self.ss, self.es = self.samplenum, self.samplenum
-        self.pdu_start = self.samplenum
-        self.pdu_bits = 0
-        cmd = 'START REPEAT' if (self.is_repeat_start == 1) else 'START'
-        self.putp([cmd, None])
-        self.putx([proto[cmd][0], proto[cmd][1:]])
-        self.state = 'FIND ADDRESS'
-        self.bitcount = self.databyte = 0
-        self.is_repeat_start = 1
-        self.wr = -1
-        self.bits = []
+    def putg(self, ss, es, cls, text):
+        self.put(ss, es, self.out_ann, [cls, text])
+
+    def putp(self, ss, es, data):
+        self.put(ss, es, self.out_python, data)
+
+    def putb(self, ss, es, data):
+        self.put(ss, es, self.out_binary, data)
+
+    def _wants_start(self):
+        # Check whether START is required (to sync to the input stream).
+        return self.pdu_start is None
+
+    def _collects_address(self):
+        # Check whether the transfer still is in the address phase (is
+        # still collecting address and r/w details, or has not started
+        # collecting it).
+        return self.rem_addr_bytes is None or self.rem_addr_bytes != 0
+
+    def _collects_byte(self):
+        # Check whether bits of a byte are being collected. Outside of
+        # the data byte, the bit is the ACK/NAK slot.
+        return self.data_bits is None or len(self.data_bits) < 8
+
+    def handle_start(self, ss, es):
+        if self.is_repeat_start:
+            cmd = 'START REPEAT'
+        else:
+            cmd = 'START'
+            self.pdu_start = ss
+            self.pdu_bits = 0
+        self.putp(ss, es, [cmd, None])
+        cls, texts = proto[cmd][0], proto[cmd][1:]
+        self.putg(ss, es, cls, texts)
+        self.is_repeat_start = True
+        self.is_write = None
+        self.slave_addr_7 = None
+        self.slave_addr_10 = None
+        self.rem_addr_bytes = None
+        self.data_bits.clear()
+        self.bitwidth = 0
 
     # Gather 8 bits of data plus the ACK/NACK bit.
-    def handle_address_or_data(self, pins):
-        scl, sda = pins
+    def handle_address_or_data(self, ss, es, value):
         self.pdu_bits += 1
 
-        # Address and data are transmitted MSB-first.
-        self.databyte <<= 1
-        self.databyte |= sda
-
-        # Remember the start of the first data/address bit.
-        if self.bitcount == 0:
-            self.ss_byte = self.samplenum
-
-        # Store individual bits and their start/end samplenumbers.
-        # In the list, index 0 represents the LSB (I²C transmits MSB-first).
-        self.bits.insert(0, [sda, self.samplenum, self.samplenum])
-        if self.bitcount > 0:
-            self.bits[1][2] = self.samplenum
-        if self.bitcount == 7:
-            self.bitwidth = self.bits[1][2] - self.bits[2][2]
-            self.bits[0][2] += self.bitwidth
-
-        # Return if we haven't collected all 8 + 1 bits, yet.
-        if self.bitcount < 7:
-            self.bitcount += 1
+        # Accumulate a byte's bits, including its start position.
+        # Accumulate individual bits and their start/end sample numbers
+        # as we see them. Get the start sample number at the time when
+        # the bit value gets sampled. Assume the start of the next bit
+        # as the end sample number of the previous bit. Guess the last
+        # bit's end sample number from the second last bit's width.
+        # Keep the bits in receive order (MSB first) during accumulation.
+        # (gsi: Strictly speaking falling SCL would be the end of the
+        # bit value's validity. That'd break compatibility though.)
+        if self.data_bits:
+            self.data_bits[-1][2] = ss
+        self.data_bits.append([value, ss, es])
+        if len(self.data_bits) < 8:
             return
-
-        d = self.databyte
-        if self.state == 'FIND ADDRESS':
-            # The READ/WRITE bit is only in address bytes, not data bytes.
-            self.wr = 0 if (self.databyte & 1) else 1
-            if self.options['address_format'] == 'shifted':
-                d = d >> 1
-
+        self.bitwidth = self.data_bits[-2][2] - self.data_bits[-3][2]
+        self.data_bits[-1][2] = self.data_bits[-1][1] + self.bitwidth
+
+        # Get the byte value. Address and data are transmitted MSB-first.
+        d = bitpack_msb(self.data_bits, 0)
+        ss_byte, es_byte = self.data_bits[0][1], self.data_bits[-1][2]
+
+        # Process the address bytes at the start of a transfer. The
+        # first byte will carry the R/W bit, and all of the 7bit address
+        # or part of a 10bit address. Bit pattern 0b11110xxx signals
+        # that another byte follows which carries the remaining bits of
+        # a 10bit slave address.
+        is_address = self._collects_address()
+        if is_address:
+            addr_byte = d
+            if self.rem_addr_bytes is None:
+                if (addr_byte & 0xf8) == 0xf0:
+                    self.rem_addr_bytes = 2
+                    self.slave_addr_7 = None
+                    self.slave_addr_10 = addr_byte & 0x06
+                    self.slave_addr_10 <<= 7
+                else:
+                    self.rem_addr_bytes = 1
+                    self.slave_addr_7 = addr_byte >> 1
+                    self.slave_addr_10 = None
+            has_rw_bit = self.is_write is None
+            if self.is_write is None:
+                read_bit = bool(addr_byte & 1)
+                if self.options['address_format'] == 'shifted':
+                    d >>= 1
+                self.is_write = False if read_bit else True
+            elif self.slave_addr_10 is not None:
+                self.slave_addr_10 |= addr_byte
+            else:
+                cls, texts = proto['WARN'][0], proto['WARN'][1:]
+                msg = 'Unhandled address byte'
+                texts = [t.format(text = msg) for t in texts]
+                self.putg(ss_byte, es_byte, cls, texts)
+        is_write = self.is_write
+        is_seven = self.slave_addr_7 is not None
+
+        # Determine annotation classes depending on whether the byte is
+        # an address or payload data, and whether it's written or read.
         bin_class = -1
-        if self.state == 'FIND ADDRESS' and self.wr == 1:
+        if is_address and is_write:
             cmd = 'ADDRESS WRITE'
             bin_class = 1
-        elif self.state == 'FIND ADDRESS' and self.wr == 0:
+        elif is_address and not is_write:
             cmd = 'ADDRESS READ'
             bin_class = 0
-        elif self.state == 'FIND DATA' and self.wr == 1:
+        elif not is_address and is_write:
             cmd = 'DATA WRITE'
             bin_class = 3
-        elif self.state == 'FIND DATA' and self.wr == 0:
+        elif not is_address and not is_write:
             cmd = 'DATA READ'
             bin_class = 2
 
-        self.ss, self.es = self.ss_byte, self.samplenum + self.bitwidth
-
-        self.putp(['BITS', self.bits])
-        self.putp([cmd, d])
-
-        self.putb([bin_class, bytes([d])])
-
-        for bit in self.bits:
-            self.put(bit[1], bit[2], self.out_ann, [5, ['%d' % bit[0]]])
-
-        if cmd.startswith('ADDRESS'):
-            self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
-            w = ['Write', 'Wr', 'W'] if self.wr else ['Read', 'Rd', 'R']
-            self.putx([proto[cmd][0], w])
-            self.ss, self.es = self.ss_byte, self.samplenum
-
-        self.putx([proto[cmd][0], ['%s: %02X' % (proto[cmd][1], d),
-                   '%s: %02X' % (proto[cmd][2], d), '%02X' % d]])
-
-        # Done with this packet.
-        self.bitcount = self.databyte = 0
-        self.bits = []
-        self.state = 'FIND ACK'
-
-    def get_ack(self, pins):
-        scl, sda = pins
-        self.ss, self.es = self.samplenum, self.samplenum + self.bitwidth
-        cmd = 'NACK' if (sda == 1) else 'ACK'
-        self.putp([cmd, None])
-        self.putx([proto[cmd][0], proto[cmd][1:]])
-        # There could be multiple data bytes in a row, so either find
-        # another data byte or a STOP condition next.
-        self.state = 'FIND DATA'
-
-    def handle_stop(self, pins):
+        # Reverse the list of bits to LSB first order before emitting
+        # annotations and passing bits to upper layers. This may be
+        # unexpected because the protocol is MSB first, but it keeps
+        # backwards compatibility.
+        lsb_bits = self.data_bits[:]
+        lsb_bits.reverse()
+        self.putp(ss_byte, es_byte, ['BITS', lsb_bits])
+        self.putp(ss_byte, es_byte, [cmd, d])
+
+        self.putb(ss_byte, es_byte, [bin_class, bytes([d])])
+
+        for bit_value, ss_bit, es_bit in lsb_bits:
+            cls, texts = proto['BIT'][0], proto['BIT'][1:]
+            texts = [t.format(b = bit_value) for t in texts]
+            self.putg(ss_bit, es_bit, cls, texts)
+
+        if is_address and has_rw_bit:
+            # Assign the last bit's location to the R/W annotation.
+            # Adjust the address value's location to the left.
+            ss_bit, es_bit = self.data_bits[-1][1], self.data_bits[-1][2]
+            es_byte = self.data_bits[-2][2]
+            cls = proto[cmd][0]
+            w = ['Write', 'Wr', 'W'] if self.is_write else ['Read', 'Rd', 'R']
+            self.putg(ss_bit, es_bit, cls, w)
+
+        cls, texts = proto[cmd][0], proto[cmd][1:]
+        texts = [t.format(b = d) for t in texts]
+        self.putg(ss_byte, es_byte, cls, texts)
+
+    def get_ack(self, ss, es, value):
+        ss_bit, es_bit = ss, es
+        cmd = 'ACK' if value == 0 else 'NACK'
+        self.putp(ss_bit, es_bit, [cmd, None])
+        cls, texts = proto[cmd][0], proto[cmd][1:]
+        self.putg(ss_bit, es_bit, cls, texts)
+        # Slave addresses can span one or two bytes, before data bytes
+        # follow. There can be an arbitrary number of data bytes. Stick
+        # with getting more address bytes if applicable, or enter or
+        # remain in the data phase of the transfer otherwise.
+        if self.rem_addr_bytes:
+            self.rem_addr_bytes -= 1
+        self.data_bits.clear()
+
+    def handle_stop(self, ss, es):
         # Meta bitrate
-        if self.samplerate:
-            elapsed = 1 / float(self.samplerate) * (self.samplenum - self.pdu_start + 1)
+        if self.samplerate and self.pdu_start:
+            elapsed = es - self.pdu_start + 1
+            elapsed /= self.samplerate
             bitrate = int(1 / elapsed * self.pdu_bits)
-            self.put(self.ss_byte, self.samplenum, self.out_bitrate, bitrate)
+            ss_meta, es_meta = self.pdu_start, es
+            self.put(ss_meta, es_meta, self.out_bitrate, bitrate)
+            self.pdu_start = None
+            self.pdu_bits = 0
 
         cmd = 'STOP'
-        self.ss, self.es = self.samplenum, self.samplenum
-        self.putp([cmd, None])
-        self.putx([proto[cmd][0], proto[cmd][1:]])
-        self.state = 'FIND START'
-        self.is_repeat_start = 0
-        self.wr = -1
-        self.bits = []
+        self.putp(ss, es, [cmd, None])
+        cls, texts = proto[cmd][0], proto[cmd][1:]
+        self.putg(ss, es, cls, texts)
+        self.is_repeat_start = False
+        self.is_write = None
+        self.data_bits.clear()
 
     def decode(self):
+        # Check for several bus conditions. Determine sample numbers
+        # here and pass ss, es, and bit values to handling routines.
         while True:
             # State machine.
-            if self.state == 'FIND START':
+            # BEWARE! This implementation expects to see valid traffic,
+            # is rather picky in which phase which symbols get handled.
+            # This attempts to support severely undersampled captures,
+            # which a previous implementation happened to read instead
+            # of rejecting the inadequate input data.
+            # NOTE that handling bits at the start of their validity,
+            # and assuming that they remain valid until the next bit
+            # starts, is also done for backwards compatibility.
+            if self._wants_start():
                 # Wait for a START condition (S): SCL = high, SDA = falling.
-                self.handle_start(self.wait({0: 'h', 1: 'f'}))
-            elif self.state == 'FIND ADDRESS':
+                pins = self.wait({0: 'h', 1: 'f'})
+                ss, es = self.samplenum, self.samplenum
+                self.handle_start(ss, es)
+            elif self._collects_address() and self._collects_byte():
                 # Wait for a data bit: SCL = rising.
-                self.handle_address_or_data(self.wait({0: 'r'}))
-            elif self.state == 'FIND DATA':
+                pins = self.wait({0: 'r'})
+                _, sda = pins
+                ss, es = self.samplenum, self.samplenum + self.bitwidth
+                self.handle_address_or_data(ss, es, sda)
+            elif self._collects_byte():
                 # Wait for any of the following conditions (or combinations):
                 #  a) Data sampling of receiver: SCL = rising, and/or
                 #  b) START condition (S): SCL = high, SDA = falling, and/or
@@ -268,11 +347,18 @@ class Decoder(srd.Decoder):
 
                 # Check which of the condition(s) matched and handle them.
                 if self.matched[0]:
-                    self.handle_address_or_data(pins)
+                    _, sda = pins
+                    ss, es = self.samplenum, self.samplenum + self.bitwidth
+                    self.handle_address_or_data(ss, es, sda)
                 elif self.matched[1]:
-                    self.handle_start(pins)
+                    ss, es = self.samplenum, self.samplenum
+                    self.handle_start(ss, es)
                 elif self.matched[2]:
-                    self.handle_stop(pins)
-            elif self.state == 'FIND ACK':
+                    ss, es = self.samplenum, self.samplenum
+                    self.handle_stop(ss, es)
+            else:
                 # Wait for a data/ack bit: SCL = rising.
-                self.get_ack(self.wait({0: 'r'}))
+                pins = self.wait({0: 'r'})
+                _, sda = pins
+                ss, es = self.samplenum, self.samplenum + self.bitwidth
+                self.get_ack(ss, es, sda)
index a54baab247b88a474db2c08a3649d097ba2c5e52..877c4676a0fe895679dab96c00cac862dff4fdd1 100644 (file)
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
-# TODO: Support for filtering out multiple slave/direction pairs?
+# TODO
+# - Accept other slave address forms than decimal numbers?
+# - Support for filtering out multiple slave/direction pairs?
+# - Support 10bit slave addresses?
 
+import copy
 import sigrokdecode as srd
 
 class Decoder(srd.Decoder):
@@ -43,51 +47,60 @@ class Decoder(srd.Decoder):
         self.reset()
 
     def reset(self):
-        self.curslave = -1
-        self.curdirection = None
-        self.packets = [] # Local cache of I²C packets
+        self.seen_packets = []
+        self.do_forward = None
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON, proto_id='i2c')
         if self.options['address'] not in range(0, 127 + 1):
             raise Exception('Invalid slave (must be 0..127).')
+        self.want_addrs = []
+        if self.options['address']:
+            self.want_addrs.append(self.options['address'])
+        self.want_dir = {
+            'read': 'READ', 'write': 'WRITE',
+        }.get(self.options['direction'], None)
 
-    # Grab I²C packets into a local cache, until an I²C STOP condition
-    # packet comes along. At some point before that STOP condition, there
-    # will have been an ADDRESS READ or ADDRESS WRITE which contains the
-    # I²C address of the slave that the master wants to talk to.
-    # If that slave shall be filtered, output the cache (all packets from
-    # START to STOP) as proto 'i2c', otherwise drop it.
-    def decode(self, ss, es, data):
+    def _need_to_forward(self, slave_addr, direction):
+        if self.want_addrs and slave_addr not in self.want_addrs:
+            return False
+        if self.want_dir and direction != self.want_dir:
+            return False
+        return True
 
-        cmd, databyte = data
+    # Accumulate observed I2C packets until a STOP or REPEATED START
+    # condition is seen. These are conditions where transfers end or
+    # where direction potentially changes. Forward all previously
+    # accumulated traffic if it passes the slave address and direction
+    # filter. This assumes that the slave address as well as the read
+    # or write direction was part of the observed traffic. There should
+    # be no surprise when incomplete traffic does not match the filter
+    # condition.
+    def decode(self, ss, es, data):
 
-        # Add the I²C packet to our local cache.
-        self.packets.append([ss, es, data])
+        # Unconditionally accumulate every lower layer packet we see.
+        # Keep deep copies for later, only reference caller's values
+        # as long as this .decode() invocation executes.
+        self.seen_packets.append([ss, es, copy.deepcopy(data)])
+        cmd, _ = data
 
+        # Check the slave address and transfer direction early when
+        # we see them. Keep accumulating packets while it's already
+        # known here whether to forward them. This simplifies other
+        # code paths. Including future handling of 10bit addresses.
         if cmd in ('ADDRESS READ', 'ADDRESS WRITE'):
-            self.curslave = databyte
-            self.curdirection = cmd[8:].lower()
-        elif cmd in ('STOP', 'START REPEAT'):
-            # If this chunk was not for the correct slave, drop it.
-            if self.options['address'] == 0:
-                pass
-            elif self.curslave != self.options['address']:
-                self.packets = []
-                return
-
-            # If this chunk was not in the right direction, drop it.
-            if self.options['direction'] == 'both':
-                pass
-            elif self.options['direction'] != self.curdirection:
-                self.packets = []
-                return
-
-            # TODO: START->STOP chunks with both read and write (Repeat START)
-            # Otherwise, send out the whole chunk of I²C packets.
-            for p in self.packets:
-                self.put(p[0], p[1], self.out_python, p[2])
+            direction = cmd[len('ADDRESS '):]
+            _, slave_addr = data
+            self.do_forward = self._need_to_forward(slave_addr, direction)
+            return
 
-            self.packets = []
-        else:
-            pass # Do nothing, only add the I²C packet to our cache.
+        # Forward previously accumulated packets as we see their
+        # completion, and when they pass the filter condition. Prepare
+        # to handle the next transfer (the next read/write part of it).
+        if cmd in ('STOP', 'START REPEAT'):
+            if self.do_forward:
+                for ss, es, data in self.seen_packets:
+                    self.put(ss, es, self.out_python, data)
+            self.seen_packets.clear()
+            self.do_forward = None
+            return
index 4531cb306bef6de710ebc17a4b15ab2a64135e4e..b0948a685b40bdf08999a98179c57be3264208b8 100644 (file)
@@ -69,6 +69,8 @@ GPIB level byte fields (commands, addresses, pieces of data):
    when addressing channels within the device.
  - 'DATA_BYTE': <addr> is the talker address (when available), <pdata>
    is the raw data byte (transport layer, ATN inactive).
+ - 'PPOLL': <addr> is not applicable, <pdata> is a list of bit indices
+   (DIO1 to DIO8 order) which responded to the PP request.
 
 Extracted payload information (peers and their communicated data):
  - 'TALK_LISTEN': <addr> is the current talker, <pdata> is the list of
@@ -239,11 +241,12 @@ PIN_DATA = PIN_DIO1
     ANN_RAW_BIT, ANN_RAW_BYTE,
     ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,
     ANN_EOI,
+    ANN_PP,
     ANN_TEXT,
     # TODO Want to provide one annotation class per talker address (0-30)?
     ANN_IEC_PERIPH,
     ANN_WARN,
-) = range(11)
+) = range(12)
 
 (
     BIN_RAW,
@@ -284,10 +287,12 @@ class Decoder(srd.Decoder):
         {'id': 'clk', 'name': 'CLK', 'desc': 'Serial clock'},
     )
     options = (
-        {'id': 'iec_periph', 'desc': 'Decode Commodore IEC bus peripherals details',
+        {'id': 'iec_periph', 'desc': 'Decode Commodore IEC peripherals',
             'default': 'no', 'values': ('no', 'yes')},
         {'id': 'delim', 'desc': 'Payload data delimiter',
             'default': 'eol', 'values': ('none', 'eol')},
+        {'id': 'atn_parity', 'desc': 'ATN commands use parity',
+            'default': 'no', 'values': ('no', 'yes')},
     )
     annotations = (
         ('bit', 'IEC bit'),
@@ -298,6 +303,7 @@ class Decoder(srd.Decoder):
         ('saddr', 'Secondary address'),
         ('data', 'Data byte'),
         ('eoi', 'EOI'),
+        ('pp', 'Parallel poll'),
         ('text', 'Talker text'),
         ('periph', 'IEC bus peripherals'),
         ('warning', 'Warning'),
@@ -307,6 +313,7 @@ class Decoder(srd.Decoder):
         ('raws', 'Raw bytes', (ANN_RAW_BYTE,)),
         ('gpib', 'Commands/data', (ANN_CMD, ANN_LADDR, ANN_TADDR, ANN_SADDR, ANN_DATA,)),
         ('eois', 'EOI', (ANN_EOI,)),
+        ('polls', 'Polls', (ANN_PP,)),
         ('texts', 'Talker texts', (ANN_TEXT,)),
         ('periphs', 'IEC peripherals', (ANN_IEC_PERIPH,)),
         ('warnings', 'Warnings', (ANN_WARN,)),
@@ -333,6 +340,7 @@ class Decoder(srd.Decoder):
         self.es_eoi = None
         self.ss_text = None
         self.es_text = None
+        self.ss_pp = None
         self.last_talker = None
         self.last_listener = []
         self.last_iec_addr = None
@@ -401,6 +409,63 @@ class Decoder(srd.Decoder):
         if had_eol and not is_eol:
             self.flush_bytes_text_accu()
 
+    def check_pp(self, dio = None):
+        # The combination of ATN and EOI means PP (parallel poll). Track
+        # this condition's start and end, and keep grabing the DIO lines'
+        # state as long as the condition is seen, since DAV is not used
+        # in the PP communication.
+        capture_in_pp = self.curr_atn and self.curr_eoi
+        decoder_in_pp = self.ss_pp is not None
+        if capture_in_pp and not decoder_in_pp:
+            # Phase starts. Track its ss. Start collecting DIO state.
+            self.ss_pp = self.samplenum
+            self.dio_pp = []
+            return 'enter'
+        if not capture_in_pp and decoder_in_pp:
+            # Phase ends. Void its ss. Process collected DIO state.
+            ss, es = self.ss_pp, self.samplenum
+            dio = self.dio_pp or []
+            self.ss_pp, self.dio_pp = None, None
+            if ss == es:
+                # False positive, caused by low oversampling.
+                return 'leave'
+            # Emit its annotation. Translate bit indices 0..7 for the
+            # DIO1..DIO8 signals to display text. Pass bit indices in
+            # the Python output for upper layers.
+            #
+            # TODO The presentation of this information may need more
+            # adjustment. The bit positions need not translate to known
+            # device addresses. Bits need not even belong to a single
+            # device. Participants and their location in the DIO pattern
+            # is configurable. Leave the interpretation to upper layers.
+            bits = [i for i, b in enumerate(dio) if b]
+            bits_text = ' '.join(['{}'.format(i + 1) for i in bits])
+            dios = ['DIO{}'.format(i + 1) for i in bits]
+            dios_text = ' '.join(dios or ['-'])
+            text = [
+                'PPOLL {}'.format(dios_text),
+                'PP {}'.format(bits_text),
+                'PP',
+            ]
+            self.emit_data_ann(ss, es, ANN_PP, text)
+            self.putpy(ss, es, 'PPOLL', None, bits)
+            # Cease collecting DIO state.
+            return 'leave'
+        if decoder_in_pp:
+            # Keep collecting DIO state for each individual sample in
+            # the PP phase. Logically OR all DIO values that were seen.
+            # This increases robustness for low oversampling captures,
+            # where DIO may no longer be asserted when ATN/EOI deassert,
+            # and DIO was not asserted yet when ATN/EOI start asserting.
+            if dio is None:
+                dio = []
+            if len(dio) > len(self.dio_pp):
+                self.dio_pp.extend([ 0, ] * (len(dio) - len(self.dio_pp)))
+            for i, b in enumerate(dio):
+                self.dio_pp[i] |= b
+            return 'keep'
+        return 'idle'
+
     def handle_ifc_change(self, ifc):
         # Track IFC line for parallel input.
         # Assertion of IFC de-selects all talkers and listeners.
@@ -486,6 +551,13 @@ class Decoder(srd.Decoder):
             upd_iec = False,
             py_type = None
             py_peers = False
+            if self.options['atn_parity'] == 'yes':
+                par = 1 if b & 0x80 else 0
+                b &= ~0x80
+                ones = bin(b).count('1') + par
+                if ones % 2:
+                    warn_texts = ['Command parity error', 'parity', 'PAR']
+                    self.emit_warn_ann(self.ss_raw, self.es_raw, warn_texts)
             is_cmd, is_unl, is_unt = _is_command(b)
             laddr = _is_listen_addr(b)
             taddr = _is_talk_addr(b)
@@ -683,6 +755,11 @@ class Decoder(srd.Decoder):
         # low signal levels, i.e. won't include the initial falling edge.
         # Scan for ATN/EOI edges as well (including the trick which works
         # around initial pin state).
+        #
+        # Use efficient edge based wait conditions for most activities,
+        # though some phases may require individual inspection of each
+        # sample (think parallel poll in combination with slow sampling).
+        #
         # Map low-active physical transport lines to positive logic here,
         # to simplify logical inspection/decoding of communicated data,
         # and to avoid redundancy and inconsistency in later code paths.
@@ -699,6 +776,14 @@ class Decoder(srd.Decoder):
         if has_ifc:
             idx_ifc = len(waitcond)
             waitcond.append({PIN_IFC: 'l'})
+        idx_pp_check = None
+        def add_data_cond(conds):
+            idx = len(conds)
+            conds.append({'skip': 1})
+            return idx
+        def del_data_cond(conds, idx):
+            conds.pop(idx)
+            return None
         while True:
             pins = self.wait(waitcond)
             pins = self.invert_pins(pins)
@@ -707,18 +792,34 @@ class Decoder(srd.Decoder):
             # captures, many edges fall onto the same sample number. So
             # we process active edges of flags early (before processing
             # data bits), and inactive edges late (after data got processed).
+            want_pp_check = False
             if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 1:
                 self.handle_ifc_change(pins[PIN_IFC])
             if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 1:
                 self.handle_eoi_change(pins[PIN_EOI])
+                want_pp_check = True
             if self.matched[idx_atn] and pins[PIN_ATN] == 1:
                 self.handle_atn_change(pins[PIN_ATN])
+                want_pp_check = True
+            if want_pp_check and not idx_pp_check:
+                pp = self.check_pp()
+                if pp in ('enter',):
+                    idx_pp_check = add_data_cond(waitcond)
             if self.matched[idx_dav]:
                 self.handle_dav_change(pins[PIN_DAV], pins[PIN_DIO1:PIN_DIO8 + 1])
+            if idx_pp_check:
+                pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+            want_pp_check = False
             if self.matched[idx_atn] and pins[PIN_ATN] == 0:
                 self.handle_atn_change(pins[PIN_ATN])
+                want_pp_check = True
             if idx_eoi is not None and self.matched[idx_eoi] and pins[PIN_EOI] == 0:
                 self.handle_eoi_change(pins[PIN_EOI])
+                want_pp_check = True
+            if idx_pp_check is not None and want_pp_check:
+                pp = self.check_pp(pins[PIN_DIO1:PIN_DIO8 + 1])
+                if pp in ('leave',) and idx_pp_check is not None:
+                    idx_pp_check = del_data_cond(waitcond, idx_pp_check)
             if idx_ifc is not None and self.matched[idx_ifc] and pins[PIN_IFC] == 0:
                 self.handle_ifc_change(pins[PIN_IFC])
 
diff --git a/decoders/ir_irmp/__init__.py b/decoders/ir_irmp/__init__.py
new file mode 100644 (file)
index 0000000..b6bbff6
--- /dev/null
@@ -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
diff --git a/decoders/ir_irmp/irmp_library.py b/decoders/ir_irmp/irmp_library.py
new file mode 100644 (file)
index 0000000..a1bc258
--- /dev/null
@@ -0,0 +1,168 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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.
+        '''
+
+        self._lib.irmp_get_sample_rate.restype = ctypes.c_uint32
+        self._lib.irmp_get_sample_rate.argtypes = []
+
+        self._lib.irmp_instance_alloc.restype = ctypes.c_void_p
+        self._lib.irmp_instance_alloc.argtypes = []
+
+        self._lib.irmp_instance_free.restype = None
+        self._lib.irmp_instance_free.argtypes = [ ctypes.c_void_p, ]
+
+        self._lib.irmp_instance_id.restype = ctypes.c_size_t
+        self._lib.irmp_instance_id.argtypes = [ ctypes.c_void_p, ]
+
+        self._lib.irmp_instance_lock.restype = ctypes.c_int
+        self._lib.irmp_instance_lock.argtypes = [ ctypes.c_void_p, ctypes.c_int, ]
+
+        self._lib.irmp_instance_unlock.restype = None
+        self._lib.irmp_instance_unlock.argtypes = [ ctypes.c_void_p, ]
+
+        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()
+        self._inst = None
+
+        return True
+
+    def __init__(self):
+        '''
+        Create a library instance.
+        '''
+
+        filename = self._library_filename()
+        self._lib = ctypes.cdll.LoadLibrary(filename)
+        self._library_setup_api()
+
+    def __del__(self):
+        '''
+        Release a disposed library instance.
+        '''
+
+        if self._inst:
+            self._lib.irmp_instance_free(self._inst)
+        self._inst = None
+
+    def __enter__(self):
+        '''
+        Enter a context (lock management).
+        '''
+
+        if self._inst is None:
+            self._inst = self._lib.irmp_instance_alloc()
+        self._lib.irmp_instance_lock(self._inst, 1)
+        return self
+
+    def __exit__(self, extype, exvalue, trace):
+        '''
+        Leave a context (lock management).
+        '''
+
+        self._lib.irmp_instance_unlock(self._inst)
+        return False
+
+    def client_id(self):
+        return self._lib.irmp_instance_id(self._inst)
+
+    def get_sample_rate(self):
+        return self._lib.irmp_get_sample_rate()
+
+    def reset_state(self):
+        self._lib.irmp_reset_state()
+
+    def add_one_sample(self, level):
+        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,
+        }
diff --git a/decoders/ir_irmp/pd.py b/decoders/ir_irmp/pd.py
new file mode 100644 (file)
index 0000000..b8df819
--- /dev/null
@@ -0,0 +1,139 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2014 Gump Yang <gump.yang@gmail.com>
+## Copyright (C) 2019 Rene Staffen
+## Copyright (C) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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):
+        pass
+
+    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 not self.irmp:
+            raise LibraryError('Cannot access IRMP library.')
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        lib_rate = self.irmp.get_sample_rate()
+        if not lib_rate:
+            raise LibraryError('Cannot determine IRMP library\'s samplerate.')
+        if self.samplerate % lib_rate:
+            raise SamplerateError('Capture samplerate must be multiple of library samplerate ({})'.format(lib_rate))
+
+        self.rate_factor = int(self.samplerate / lib_rate)
+        active = 0 if self.options['polarity'] == 'active-low' else 1
+
+        ir, = self.wait()
+        with self.irmp:
+            self.irmp.reset_state()
+            while True:
+                if 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}])
index c361c3dc6ccd9fd7da3ec1e14e36abf5edf6e96b..c3ab29365b7a0def98c8bdfa36f80d52d6ede800 100644 (file)
@@ -19,6 +19,7 @@
 
 '''
 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
index 7d47a46dca65b2d7bc9bbbbe19575c0a1af192d0..dbb9288d2b864778e74a45ea911318e9da4dd638 100644 (file)
@@ -19,7 +19,9 @@
 
 # Addresses/devices. Items that are not listed are reserved/unknown.
 address = {
+    0x00: 'Joy-it SBC-IRC01',
     0x40: 'Matsui TV',
+    0xEA41: 'Unknown LED Panel',
 }
 
 digits = {
@@ -47,4 +49,45 @@ command = {
         31: ['Program down', 'P-'],
         68: ['AV', 'AV'],
     }.items())),
+
+    # This is most likely a generic remote control. The PCB
+    # has space for 16 buttons total, of which not all are
+    # connected. The PCB is marked "JSY", "XSK-5462", and
+    # "2014-6-12 JW". It consists of only a single IC, marked
+    # "BJEC107BNE" or similar. The following buttons are
+    # marked for the remote control of a LED panel this was
+    # found in.
+    0xEA41: {
+        0x10: ['Warmer', 'T+'],
+        0x11: ['Colder', 'T-'],
+        0x12: ['Brighter', '+'],
+        0x13: ['Darker', '-'],
+        0x14: ['Off', 'O'],
+        0x15: ['On', 'I'],
+        0x41: ['Min Brightness', 'Min'],
+        0x48: ['Max Brightness', 'Max'],
+    },
+    0x00: {
+        0x45: ['Volume down', 'Vol-'],
+        0x46: ['Play/Pause', 'P/P'],
+        0x47: ['Volume up', 'Vol+'],
+        0x44: ['Setup', 'Set'],
+        0x40: ['Up', 'U'],
+        0x43: ['Stop / Mode', 'S/M'],
+        0x07: ['Left', 'L'],
+        0x15: ['Enter', 'E'],
+        0x09: ['Right', 'R'],
+        0x16: ['0 / 10+', '0'],
+        0x19: ['Down', 'D'],
+        0x0D: ['Back', 'B'],
+        0x0C: ['1', '1'],
+        0x18: ['2', '2'],
+        0x5E: ['3', '3'],
+        0x08: ['4', '4'],
+        0x1C: ['5', '5'],
+        0x5A: ['6', '6'],
+        0x42: ['7', '7'],
+        0x52: ['8', '8'],
+        0x4A: ['9', '9'],
+    }
 }
index 3ee3716b14bc3e572c521c02c0eb78916d5dc02f..dffe23eedbe5d48687c03d1230bd7ce5d715370d 100644 (file)
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
-import sigrokdecode as srd
+from common.srdhelper import bitpack
 from .lists import *
+import sigrokdecode as srd
+
+# Concentrate all timing constraints of the IR protocol here in a single
+# location at the top of the source, to raise awareness and to simplify
+# review and adjustment. The tolerance is an arbitrary choice, available
+# literature does not mention any. The inter-frame timeout is not a part
+# of the protocol, but an implementation detail of this sigrok decoder.
+_TIME_TOL  =  8     # tolerance, in percent
+_TIME_IDLE = 20.0   # inter-frame timeout, in ms
+_TIME_LC   = 13.5   # leader code, in ms
+_TIME_RC   = 11.25  # repeat code, in ms
+_TIME_ONE  =  2.25  # one data bit, in ms
+_TIME_ZERO =  1.125 # zero data bit, in ms
+_TIME_STOP =  0.562 # stop bit, in ms
 
 class SamplerateError(Exception):
     pass
 
+class Pin:
+    IR, = range(1)
+
+class Ann:
+    BIT, AGC, LONG_PAUSE, SHORT_PAUSE, STOP_BIT, \
+    LEADER_CODE, ADDR, ADDR_INV, CMD, CMD_INV, REPEAT_CODE, \
+    REMOTE, WARN = range(13)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'ir_nec'
@@ -38,8 +60,10 @@ class Decoder(srd.Decoder):
     )
     options = (
         {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
-            'values': ('active-low', 'active-high')},
+            'values': ('auto', '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'),
@@ -57,10 +81,10 @@ class Decoder(srd.Decoder):
         ('warning', 'Warning'),
     )
     annotation_rows = (
-        ('bits', 'Bits', (0, 1, 2, 3, 4)),
-        ('fields', 'Fields', (5, 6, 7, 8, 9, 10)),
-        ('remote-vals', 'Remote', (11,)),
-        ('warnings', 'Warnings', (12,)),
+        ('bits', 'Bits', (Ann.BIT, Ann.AGC, Ann.LONG_PAUSE, Ann.SHORT_PAUSE, Ann.STOP_BIT)),
+        ('fields', 'Fields', (Ann.LEADER_CODE, Ann.ADDR, Ann.ADDR_INV, Ann.CMD, Ann.CMD_INV, Ann.REPEAT_CODE)),
+        ('remote-vals', 'Remote', (Ann.REMOTE,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
     )
 
     def putx(self, data):
@@ -69,36 +93,44 @@ class Decoder(srd.Decoder):
     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': 6, 'ADDRESS#': 7, 'COMMAND': 8, 'COMMAND#': 9}
+        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#']}
-        self.putx([d[self.state], ['%s: 0x%02X' % (name, data),
-                  '%s: 0x%02X' % (s[self.state][0], data),
-                  '%s: 0x%02X' % (s[self.state][1], data), s[self.state][1]]])
+        fmt = '{{}}: 0x{{:0{}X}}'.format(bit_count // 4)
+        self.putx([d[self.state], [
+            fmt.format(name, data),
+            fmt.format(s[self.state][0], data),
+            fmt.format(s[self.state][1], data),
+            s[self.state][1],
+        ]])
 
     def putstop(self, ss):
         self.put(ss, ss + self.stop, self.out_ann,
-                 [4, ['Stop bit', 'Stop', 'St', 'S']])
+                 [Ann.STOP_BIT, ['Stop bit', 'Stop', 'St', 'S']])
 
     def putpause(self, p):
         self.put(self.ss_start, self.ss_other_edge, self.out_ann,
-                 [1, ['AGC pulse', 'AGC', 'A']])
-        idx = 2 if p == 'Long' else 3
-        self.put(self.ss_other_edge, self.samplenum, self.out_ann,
-                 [idx, [p + ' pause', '%s-pause' % p[0], '%sP' % p[0], 'P']])
+                 [Ann.AGC, ['AGC pulse', 'AGC', 'A']])
+        idx = Ann.LONG_PAUSE if p == 'Long' else Ann.SHORT_PAUSE
+        self.put(self.ss_other_edge, self.samplenum, self.out_ann, [idx, [
+            '{} pause'.format(p),
+            '{}-pause'.format(p[0]),
+            '{}P'.format(p[0]),
+            'P',
+        ]])
 
     def putremote(self):
         dev = address.get(self.addr, 'Unknown device')
-        buttons = command.get(self.addr, None)
-        if buttons is None:
-            btn = ['Unknown', 'Unk']
-        else:
-            btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
-        self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann,
-                 [11, ['%s: %s' % (dev, btn[0]), '%s: %s' % (dev, btn[1]),
-                 '%s' % btn[1]]])
+        buttons = command.get(self.addr, {})
+        btn = buttons.get(self.cmd, ['Unknown', 'Unk'])
+        self.put(self.ss_remote, self.ss_bit + self.stop, self.out_ann, [Ann.REMOTE, [
+            '{}: {}'.format(dev, btn[0]),
+            '{}: {}'.format(dev, btn[1]),
+            '{}'.format(btn[1]),
+        ]])
 
     def __init__(self):
         self.reset()
@@ -106,7 +138,7 @@ class Decoder(srd.Decoder):
     def reset(self):
         self.state = 'IDLE'
         self.ss_bit = self.ss_start = self.ss_other_edge = self.ss_remote = 0
-        self.data = self.count = self.active = None
+        self.data = []
         self.addr = self.cmd = None
 
     def start(self):
@@ -115,12 +147,15 @@ class Decoder(srd.Decoder):
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
-        self.tolerance = 0.05 # +/-5%
-        self.lc = int(self.samplerate * 0.0135) - 1 # 13.5ms
-        self.rc = int(self.samplerate * 0.01125) - 1 # 11.25ms
-        self.dazero = int(self.samplerate * 0.001125) - 1 # 1.125ms
-        self.daone = int(self.samplerate * 0.00225) - 1 # 2.25ms
-        self.stop = int(self.samplerate * 0.000652) - 1 # 0.652ms
+
+    def calc_rate(self):
+        self.tolerance = _TIME_TOL / 100
+        self.lc = int(self.samplerate * _TIME_LC / 1000) - 1
+        self.rc = int(self.samplerate * _TIME_RC / 1000) - 1
+        self.dazero = int(self.samplerate * _TIME_ZERO / 1000) - 1
+        self.daone = int(self.samplerate * _TIME_ONE / 1000) - 1
+        self.stop = int(self.samplerate * _TIME_STOP / 1000) - 1
+        self.idle_to = int(self.samplerate * _TIME_IDLE / 1000) - 1
 
     def compare_with_tolerance(self, measured, base):
         return (measured >= base * (1 - self.tolerance)
@@ -133,39 +168,57 @@ class Decoder(srd.Decoder):
         elif self.compare_with_tolerance(tick, self.daone):
             ret = 1
         if ret in (0, 1):
-            self.putb([0, ['%d' % ret]])
-            self.data |= (ret << self.count) # LSB-first
-            self.count = self.count + 1
+            self.putb([Ann.BIT, ['{:d}'.format(ret)]])
+            self.data.append(ret)
         self.ss_bit = self.samplenum
 
-    def data_ok(self):
-        ret, name = (self.data >> 8) & (self.data & 0xff), self.state.title()
-        if self.count == 8:
+    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
+        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 = self.data
+                self.addr = normal
             if self.state == 'COMMAND':
-                self.cmd = self.data
-            self.putd(self.data)
+                self.cmd = normal
+            self.putd(show, want_len)
             self.ss_start = self.samplenum
+            if is_ext_addr:
+                self.data = []
+                self.ss_bit = self.ss_start = self.samplenum
             return True
-        if ret == 0:
-            self.putd(self.data >> 8)
-        else:
-            self.putx([12, ['%s error: 0x%04X' % (name, self.data)]])
-        self.data = self.count = 0
+        self.putd(show, want_len)
+        if check and not valid:
+            warn_show = bitpack(self.data)
+            self.putx([Ann.WARN, ['{} error: 0x{:04X}'.format(name, warn_show)]])
+        self.data = []
         self.ss_bit = self.ss_start = self.samplenum
-        return ret == 0
+        return valid
 
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
+        self.calc_rate()
 
         cd_count = None
         if self.options['cd_freq']:
             cd_count = int(self.samplerate / self.options['cd_freq']) + 1
         prev_ir = None
 
-        self.active = 0 if self.options['polarity'] == 'active-low' else 1
+        if self.options['polarity'] == 'auto':
+            # Take sample 0 as reference.
+            curr_level, = self.wait({'skip': 0})
+            active = 1 - curr_level
+        else:
+            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.
@@ -182,54 +235,64 @@ class Decoder(srd.Decoder):
             # active period, but will shift their signal changes by one
             # carrier period before they get passed to decoding logic.
             if cd_count:
-                (cur_ir,) = self.wait([{0: 'e'}, {'skip': cd_count}])
+                (cur_ir,) = self.wait([{Pin.IR: 'e'}, {'skip': cd_count}])
                 if self.matched[0]:
-                    cur_ir = self.active
+                    cur_ir = active
                 if cur_ir == prev_ir:
                     continue
                 prev_ir = cur_ir
                 self.ir = cur_ir
             else:
-                (self.ir,) = self.wait({0: 'e'})
+                (self.ir,) = self.wait({Pin.IR: 'e'})
 
-            if self.ir != self.active:
-                # Save the non-active edge, then wait for the next edge.
+            if self.ir != active:
+                # Save the location of the non-active edge (recessive),
+                # then wait for the next edge. Immediately process the
+                # end of the STOP bit which completes an IR frame.
                 self.ss_other_edge = self.samplenum
-                continue
+                if self.state != 'STOP':
+                    continue
 
-            b = self.samplenum - self.ss_bit
+            # Reset internal state for long periods of idle level.
+            width = self.samplenum - self.ss_bit
+            if width >= self.idle_to and self.state != 'STOP':
+                self.reset()
 
             # State machine.
             if self.state == 'IDLE':
-                if self.compare_with_tolerance(b, self.lc):
+                if self.compare_with_tolerance(width, self.lc):
                     self.putpause('Long')
-                    self.putx([5, ['Leader code', 'Leader', 'LC', 'L']])
+                    self.putx([Ann.LEADER_CODE, ['Leader code', 'Leader', 'LC', 'L']])
                     self.ss_remote = self.ss_start
-                    self.data = self.count = 0
+                    self.data = []
                     self.state = 'ADDRESS'
-                elif self.compare_with_tolerance(b, self.rc):
+                elif self.compare_with_tolerance(width, self.rc):
                     self.putpause('Short')
                     self.putstop(self.samplenum)
                     self.samplenum += self.stop
-                    self.putx([10, ['Repeat code', 'Repeat', 'RC', 'R']])
-                    self.data = self.count = 0
+                    self.putx([Ann.REPEAT_CODE, ['Repeat code', 'Repeat', 'RC', 'R']])
+                    self.data = []
                 self.ss_bit = self.ss_start = self.samplenum
             elif self.state == 'ADDRESS':
-                self.handle_bit(b)
-                if self.count == 8:
-                    self.state = 'ADDRESS#' if self.data_ok() else 'IDLE'
+                self.handle_bit(width)
+                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 self.count == 16:
-                    self.state = 'COMMAND' if self.data_ok() else 'IDLE'
+                self.handle_bit(width)
+                if len(self.data) == 16:
+                    self.data_ok(True, 8)
+                    self.state = 'COMMAND'
             elif self.state == 'COMMAND':
-                self.handle_bit(b)
-                if self.count == 8:
-                    self.state = 'COMMAND#' if self.data_ok() else 'IDLE'
+                self.handle_bit(width)
+                if len(self.data) == 8:
+                    self.data_ok(False, 8)
+                    self.state = 'COMMAND#'
             elif self.state == 'COMMAND#':
-                self.handle_bit(b)
-                if self.count == 16:
-                    self.state = 'STOP' if self.data_ok() else 'IDLE'
+                self.handle_bit(width)
+                if len(self.data) == 16:
+                    self.data_ok(True, 8)
+                    self.state = 'STOP'
             elif self.state == 'STOP':
                 self.putstop(self.ss_bit)
                 self.putremote()
diff --git a/decoders/ir_sirc/__init__.py b/decoders/ir_sirc/__init__.py
new file mode 100644 (file)
index 0000000..4061ed7
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## 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/>.
+##
+
+'''
+Decoder for the Sony IR remote control protocol (SIRC).
+
+https://www.sbprojects.net/knowledge/ir/sirc.php
+'''
+
+from .pd import Decoder
diff --git a/decoders/ir_sirc/lists.py b/decoders/ir_sirc/lists.py
new file mode 100644 (file)
index 0000000..5c6d8fa
--- /dev/null
@@ -0,0 +1,201 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## 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/>.
+##
+
+NUMBERS = {
+    0x00: '1',
+    0x01: '2',
+    0x02: '3',
+    0x03: '4',
+    0x04: '5',
+    0x05: '6',
+    0x06: '7',
+    0x07: '8',
+    0x08: '9',
+    0x09: '0/10',
+}
+
+ADDRESSES = {
+    # TV
+    (0x01, None): (['TV: ', 'TV:'], {
+        0x15: 'Power',
+        0x25: 'Input',
+
+        0x33: 'Right',
+        0x34: 'Left',
+        0x3A: 'Display',
+
+        0x60: 'Home',
+        0x65: 'Enter',
+
+        0x74: 'Up',
+        0x75: 'Down',
+
+    }),
+
+    # Video
+    (0x0B, None): (['Video: ', 'V:'], {
+        0x18: 'Stop',
+        0x19: 'Pause',
+        0x1A: 'Play',
+        0x1B: 'Rewind',
+        0x1C: 'Fast Forward',
+
+        0x42: 'Up',
+        0x43: 'Down',
+        0x4D: 'Home',
+
+        0x51: 'Enter',
+        0x5A: 'Display',
+
+        0x61: 'Right',
+        0x62: 'Left',
+    }),
+
+    # BR Input select
+    (0x10, 0x28): (['BlueRay: ', 'BR:'], {
+        0x16: 'BlueRay',
+    }),
+
+    # Amp, Game, Sat, Tuner, USB
+    (0x10, 0x08): (['Playback: ', 'PB:'], {
+        0x2A: 'Shuffle',
+        0x2C: 'Repeat',
+        0x2E: 'Folder Down',
+        0x2F: 'Folder Up',
+
+        0x30: 'Previous',
+        0x31: 'Next',
+        0x32: 'Play',
+        0x33: 'Rewind',
+        0x34: 'Fast Forward',
+        0x38: 'Stop',
+        0x39: 'Pause',
+
+        0x73: 'Options',
+        0x7D: 'Return',
+    }),
+
+    # CD
+    (0x11, None): (['CD: ', 'CD:'], {
+        0x28: 'Display',
+
+        0x30: 'Previous',
+        0x31: 'Next',
+        0x32: 'Play',
+        0x33: 'Rewind',
+        0x34: 'Fast Forward',
+        0x38: 'Stop',
+        0x39: 'Pause',
+    }),
+
+    # BD
+    (0x1A, 0xE2): (['BlueRay: ', 'BD:'], {
+        0x18: 'Stop',
+        0x19: 'Pause',
+        0x1A: 'Play',
+        0x1B: 'Rewind',
+        0x1C: 'Fast Forward',
+
+        0x29: 'Menu',
+        0x2C: 'Top Menu',
+
+        0x39: 'Up',
+        0x3A: 'Down',
+        0x3B: 'Left',
+        0x3C: 'Right',
+        0x3D: 'Enter',
+        0x3F: 'Options',
+
+        0x41: 'Display',
+        0x42: 'Home',
+        0x43: 'Return',
+
+        0x56: 'Next',
+        0x57: 'Previous',
+    }),
+
+    # DVD
+    (0x1A, 0x49): (['DVD: ', 'DVD:'], {
+        0x0B: 'Enter',
+        0x0E: 'Return',
+        0x17: 'Options',
+
+        0x1A: 'Top Menu',
+        0x1B: 'Menu',
+
+        0x30: 'Previous',
+        0x31: 'Next',
+        0x32: 'Play',
+        0x33: 'Rewind',
+        0x34: 'Fast Forward',
+        0x38: 'Stop',
+        0x39: 'Pause',
+
+        0x54: 'Display',
+
+        0x7B: 'Left',
+        0x7C: 'Right',
+        0x79: 'Up',
+        0x7A: 'Down',
+    }),
+
+    # Amp, Game, Sat, Tuner, USB modes
+    (0x30, None): (['Keypad: ', 'KP:'], {
+        0x0C: 'Enter',
+
+        0x12: 'Volume Up',
+        0x13: 'Volume Down',
+        0x14: 'Mute',
+        0x15: 'Power',
+
+        0x21: 'Tuner',
+        0x22: 'Video',
+        0x25: 'CD',
+
+        0x4D: 'Home',
+        0x4B: 'Display',
+
+        0x60: 'Sleep',
+        0x6A: 'TV',
+
+        0x53: 'Home',
+
+        0x7C: 'Game',
+        0x7D: 'DVD',
+    }),
+
+    # Amp, Game, Sat, Tuner, USB modes
+    (0xB0, None): (['Arrows: ', 'Ar:'], {
+        0x7A: 'Left',
+        0x7B: 'Right',
+        0x78: 'Up',
+        0x79: 'Down',
+        0x77: 'Amp Menu',
+    }),
+
+    # TV mode
+    (0x97, None): (['TV Extra', 'TV:'], {
+        0x23: 'Return',
+        0x36: 'Options',
+
+    }),
+}
+
+for (address, extended), (name, commands) in ADDRESSES.items():
+    commands.update(NUMBERS)
diff --git a/decoders/ir_sirc/pd.py b/decoders/ir_sirc/pd.py
new file mode 100644 (file)
index 0000000..14ba63f
--- /dev/null
@@ -0,0 +1,215 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Tom Flanagan <knio@zkpq.ca>
+##
+## 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 common.srdhelper import bitpack_lsb
+from .lists import ADDRESSES
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class SIRCError(Exception):
+    pass
+
+class SIRCErrorSilent(SIRCError):
+    pass
+
+class Ann:
+    BIT, AGC, PAUSE, START, CMD, ADDR, EXT, REMOTE, WARN = range(9)
+
+AGC_USEC = 2400
+ONE_USEC = 1200
+ZERO_USEC = 600
+PAUSE_USEC = 600
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'ir_sirc'
+    name = 'IR SIRC'
+    longname = 'Sony IR (SIRC)'
+    desc = 'Sony infrared remote control protocol (SIRC).'
+    license = 'gplv2+'
+    tags = ['IR']
+    inputs = ['logic']
+    outputs = []
+    channels = (
+        {'id': 'ir', 'name': 'IR', 'desc': 'IR data line'},
+    )
+    options = (
+        {'id': 'polarity', 'desc': 'Polarity', 'default': 'active-low',
+            'values': ('active-low', 'active-high')},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('agc', 'AGC'),
+        ('pause', 'Pause'),
+        ('start', 'Start'),
+        ('command', 'Command'),
+        ('address', 'Address'),
+        ('extended', 'Extended'),
+        ('remote', 'Remote'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (Ann.BIT, Ann.AGC, Ann.PAUSE)),
+        ('fields', 'Fields', (Ann.START, Ann.CMD, Ann.ADDR, Ann.EXT)),
+        ('remotes', 'Remotes', (Ann.REMOTE,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.active = self.options['polarity'] == 'active-high'
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+            self.snum_per_us = self.samplerate / 1e6
+
+    def putg(self, ss, es, cls, texts):
+        self.put(ss, es, self.out_ann, [cls, texts])
+
+    def tolerance(self, ss, es, expected):
+        microseconds = (es - ss) / self.snum_per_us
+        tolerance = expected * 0.30
+        return (expected - tolerance) < microseconds < (expected + tolerance)
+
+    def wait_wrap(self, conds, timeout):
+        if timeout is not None:
+            to = int(timeout * self.snum_per_us)
+            conds.append({'skip': to})
+        ss = self.samplenum
+        pins = self.wait(conds)
+        es = self.samplenum
+        return pins, ss, es, self.matched
+
+    def read_pulse(self, high, time):
+        e = 'f' if high else 'r'
+        max_time = int(time * 1.30)
+        (ir,), ss, es, (edge, timeout) = self.wait_wrap([{0: e}], max_time)
+        if timeout or not self.tolerance(ss, es, time):
+            raise SIRCError('Timeout')
+        return ir, ss, es, (edge, timeout)
+
+    def read_bit(self):
+        e = 'f' if self.active else 'r'
+        _, high_ss, high_es, (edge, timeout) = self.wait_wrap([{0: e}], 2000)
+        if timeout:
+            raise SIRCError('Bit High Timeout')
+        if self.tolerance(high_ss, high_es, ONE_USEC):
+            bit = 1
+        elif self.tolerance(high_ss, high_es, ZERO_USEC):
+            bit = 0
+        else:
+            raise SIRCError('Bit Low Timeout')
+        try:
+            _, low_ss, low_es, _ = self.read_pulse(not self.active, PAUSE_USEC)
+            good = True
+        except SIRCError:
+            low_es = high_es + int(PAUSE_USEC * self.snum_per_us)
+            good = False
+        self.putg(high_ss, low_es, Ann.BIT, ['{}'.format(bit)])
+        return bit, high_ss, low_es, good
+
+    def read_signal(self):
+        # Start code
+        try:
+            _, agc_ss, agc_es, _ = self.read_pulse(self.active, AGC_USEC)
+            _, pause_ss, pause_es, _ = self.read_pulse(not self.active, PAUSE_USEC)
+        except SIRCError:
+            raise SIRCErrorSilent('not an SIRC message')
+        self.putg(agc_ss, agc_es, Ann.AGC, ['AGC', 'A'])
+        self.putg(pause_ss, pause_es, Ann.PAUSE, ['Pause', 'P'])
+        self.putg(agc_ss, pause_es, Ann.START, ['Start', 'S'])
+
+        # Read bits
+        bits = []
+        while True:
+            bit, ss, es, good = self.read_bit()
+            bits.append((bit, ss, es))
+            if len(bits) > 20:
+                raise SIRCError('too many bits')
+            if not good:
+                if len(bits) == 12:
+                    command = bits[0:7]
+                    address = bits[7:12]
+                    extended = []
+                elif len(bits) == 15:
+                    command = bits[0:7]
+                    address = bits[7:15]
+                    extended = []
+                elif len(bits) == 20:
+                    command = bits[0:7]
+                    address = bits[7:12]
+                    extended = bits[12:20]
+                else:
+                    raise SIRCError('incorrect bits count {}'.format(len(bits)))
+                break
+
+        command_num = bitpack_lsb(command, 0)
+        address_num = bitpack_lsb(address, 0)
+        command_str = '0x{:02X}'.format(command_num)
+        address_str = '0x{:02X}'.format(address_num)
+        self.putg(command[0][1], command[-1][2], Ann.CMD, [
+            'Command: {}'.format(command_str),
+            'C:{}'.format(command_str),
+        ])
+        self.putg(address[0][1], address[-1][2], Ann.ADDR, [
+            'Address: {}'.format(address_str),
+            'A:{}'.format(address_str),
+        ])
+        extended_num = None
+        if extended:
+            extended_num = bitpack_lsb(extended, 0)
+            extended_str = '0x{:02X}'.format(extended_num)
+            self.putg(extended[0][1], extended[-1][2], Ann.EXT, [
+                'Extended: {}'.format(extended_str),
+                'E:{}'.format(extended_str),
+            ])
+        return address_num, command_num, extended_num, bits[0][1], bits[-1][2]
+
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        unknown = (['Unknown Device: ', 'UNK: '], {})
+        while True:
+            e = 'h' if self.active else 'l'
+            _, _, frame_ss, _ = self.wait_wrap([{0: e}], None)
+            try:
+                addr, cmd, ext, payload_ss, payload_es = self.read_signal()
+                names, cmds = ADDRESSES.get((addr, ext), unknown)
+                text = cmds.get(cmd, 'Unknown')
+                self.putg(frame_ss, payload_es, Ann.REMOTE, [
+                    n + text for n in names
+                ])
+            except SIRCErrorSilent as e:
+                pass
+            except SIRCError as e:
+                self.putg(frame_ss, self.samplenum, Ann.WARN, [
+                    'Error: {}'.format(e),
+                    'Error',
+                    'E',
+                ])
index 00602bb1a07e2452417d4154d73572e3fab1e212..5fa63ab4a18e676690b6792b833c57c310a99b4f 100644 (file)
@@ -183,18 +183,18 @@ class Decoder(srd.Decoder):
                 self.ss_bitstring = self.samplenum
                 self.first_bit = False
             else:
-                self.putx([16, [str(self.bits_tdi[0])]])
-                self.putx([17, [str(self.bits_tdo[0])]])
+                self.putx([16, [str(self.bits_tdi[-1])]])
+                self.putx([17, [str(self.bits_tdo[-1])]])
                 # Use self.samplenum as ES of the previous bit.
-                self.bits_samplenums_tdi[0][1] = self.samplenum
-                self.bits_samplenums_tdo[0][1] = self.samplenum
+                self.bits_samplenums_tdi[-1][1] = self.samplenum
+                self.bits_samplenums_tdo[-1][1] = self.samplenum
 
-            self.bits_tdi.insert(0, tdi)
-            self.bits_tdo.insert(0, tdo)
+            self.bits_tdi.append(tdi)
+            self.bits_tdo.append(tdo)
 
             # Use self.samplenum as SS of the current bit.
-            self.bits_samplenums_tdi.insert(0, [self.samplenum, -1])
-            self.bits_samplenums_tdo.insert(0, [self.samplenum, -1])
+            self.bits_samplenums_tdi.append([self.samplenum, -1])
+            self.bits_samplenums_tdo.append([self.samplenum, -1])
 
         # Output all TDI/TDO bits if we just switched to UPDATE-*.
         if self.state.value.startswith('UPDATE-'):
@@ -202,6 +202,8 @@ class Decoder(srd.Decoder):
             self.es_bitstring = self.samplenum
 
             t = self.state.value[-2:] + ' TDI'
+            self.bits_tdi.reverse()
+            self.bits_samplenums_tdi.reverse()
             b = ''.join(map(str, self.bits_tdi[1:]))
             h = ' (0x%x' % int('0b0' + b, 2) + ')'
             s = t + ': ' + b + h + ', ' + str(len(self.bits_tdi[1:])) + ' bits'
@@ -211,6 +213,8 @@ class Decoder(srd.Decoder):
             self.bits_samplenums_tdi = []
 
             t = self.state.value[-2:] + ' TDO'
+            self.bits_tdo.reverse()
+            self.bits_samplenums_tdo.reverse()
             b = ''.join(map(str, self.bits_tdo[1:]))
             h = ' (0x%x' % int('0b0' + b, 2) + ')'
             s = t + ': ' + b + h + ', ' + str(len(self.bits_tdo[1:])) + ' bits'
diff --git a/decoders/lfast/__init__.py b/decoders/lfast/__init__.py
new file mode 100644 (file)
index 0000000..681f4f9
--- /dev/null
@@ -0,0 +1,35 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## 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/>.
+##
+
+'''
+LFAST is a physical communication interface used mainly by the NXP Zipwire
+interface. It's a framed asynchronous serial interface using differential
+TX/RX pairs, capable of data rates of up to 320 MBit/s.
+
+This interface is also provided by Infineon as HSCT.
+
+As with most differential signals, it's sufficient to measure TXP or RXP, no
+need for a differential probe. The REFCLK used by the hardware isn't needed by
+this protocol decoder either.
+
+For details see https://www.nxp.com/docs/en/application-note/AN5134.pdf and
+https://hitex.co.uk/fileadmin/uk-files/downloads/ShieldBuddy/tc27xD_um_v2.2.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/lfast/pd.py b/decoders/lfast/pd.py
new file mode 100644 (file)
index 0000000..7476e59
--- /dev/null
@@ -0,0 +1,335 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## 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/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+import decimal
+
+'''
+OUTPUT_PYTHON format:
+
+[ss, es, data] where data is a data byte of the LFAST payload. All bytes of
+the payload are sent at once, each with its start and end sample.
+'''
+
+# See tc27xD_um_v2.2.pdf, Table 20-10
+payload_sizes = {
+    0b000: '8 bit',
+    0b001: '32 bit / 4 byte',
+    0b010: '64 bit / 8 byte',
+    0b011: '96 bit / 12 byte',
+    0b100: '128 bit / 16 byte',
+    0b101: '256 bit / 32 byte',
+    0b110: '512 bit / 64 byte',
+    0b111: '288 bit / 36 byte'
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-10
+payload_byte_sizes = {
+    0b000: 1,
+    0b001: 4,
+    0b010: 8,
+    0b011: 12,
+    0b100: 16,
+    0b101: 32,
+    0b110: 64,
+    0b111: 36
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-11
+channel_types = {
+    0b0000: 'Interface Control / PING',
+    0b0001: 'Unsolicited Status (32 bit)',
+    0b0010: 'Slave Interface Control / Read',
+    0b0011: 'CTS Transfer',
+    0b0100: 'Data Channel A',
+    0b0101: 'Data Channel B',
+    0b0110: 'Data Channel C',
+    0b0111: 'Data Channel D',
+    0b1000: 'Data Channel E',
+    0b1001: 'Data Channel F',
+    0b1010: 'Data Channel G',
+    0b1011: 'Data Channel H',
+    0b1100: 'Reserved',
+    0b1101: 'Reserved',
+    0b1110: 'Reserved',
+    0b1111: 'Reserved',
+}
+
+# See tc27xD_um_v2.2.pdf, Table 20-12
+control_payloads = {
+    0x00: 'PING',
+    0x01: 'Reserved',
+    0x02: 'Slave interface clock multiplier start',
+    0x04: 'Slave interface clock multiplier stop',
+    0x08: 'Use 5 MBaud for M->S',
+    0x10: 'Use 320 MBaud for M->S',
+    0x20: 'Use 5 MBaud for S->M',
+    0x40: 'Use 20 MBaud for S->M (needs 20 MHz SysClk)',
+    0x80: 'Use 320 MBaud for S->M',
+    0x31: 'Enable slave interface transmitter',
+    0x32: 'Disable slave interface transmitter',
+    0x34: 'Enable clock test mode',
+    0x38: 'Disable clock test mode and payload loopback',
+    0xFF: 'Enable payload loopback',
+}
+
+
+ann_bit, ann_sync, ann_header_pl_size, ann_header_ch_type, ann_header_cts, \
+    ann_payload, ann_control_data, ann_sleepbit, ann_warning = range(9)
+state_sync, state_header, state_payload, state_sleepbit = range(4)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'lfast'
+    name = 'LFAST'
+    longname = 'NXP LFAST interface'
+    desc = 'Differential high-speed P2P interface'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['lfast']
+    tags = ['Embedded/industrial']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'TXP or RXP'},
+    )
+    annotations = (
+        ('bit', 'Bits'),
+        ('sync', 'Sync Pattern'),
+        ('header_pl_size', 'Payload Size'),
+        ('header_ch_type', 'Logical Channel Type'),
+        ('header_cts', 'Clear To Send'),
+        ('payload', 'Payload'),
+        ('ctrl_data', 'Control Data'),
+        ('sleep', 'Sleep Bit'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (ann_bit,)),
+        ('fields', 'Fields', (ann_sync, ann_header_pl_size, ann_header_ch_type,
+            ann_header_cts, ann_payload, ann_control_data, ann_sleepbit,)),
+        ('warnings', 'Warnings', (ann_warning,)),
+    )
+
+    def __init__(self):
+        decimal.getcontext().rounding = decimal.ROUND_HALF_UP
+        self.bit_len = 0xFFFFFFFF
+        self.reset()
+
+    def reset(self):
+        self.prev_bit_len = self.bit_len
+        self.ss = self.es = 0
+        self.ss_payload = self.es_payload = 0
+        self.ss_byte = 0
+        self.bits = []
+        self.payload = []
+        self.payload_size = 0  # Expected number of bytes, as read from header
+        self.bit_len = 0       # Length of one bit time, in samples
+        self.timeout = 0       # Desired timeout for next edge, in samples
+        self.ch_type_id = 0    # ID of channel type
+        self.state = state_sync
+
+    def metadata(self, key, value):
+        pass
+
+    def start(self):
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def put_ann(self, ss, es, ann_class, value):
+        self.put(ss, es, self.out_ann, [ann_class, value])
+
+    def put_payload(self):
+        self.put(self.ss_payload, self.es_payload, self.out_python, self.payload)
+
+    def handle_sync(self):
+        if len(self.bits) == 1:
+            self.ss_sync = self.ss_bit
+
+        if len(self.bits) == 16:
+            value = bitpack(self.bits)
+            if value == 0xA84B:
+                self.put_ann(self.ss_sync, self.es_bit, ann_sync, ['Sync OK'])
+            else:
+                self.put_ann(self.ss_sync, self.es_bit, ann_warning, ['Wrong Sync Value: {:02X}'.format(value)])
+                self.reset()
+
+            # Only continue if we didn't just reset
+            if self.ss > 0:
+                self.bits = []
+                self.state = state_header
+                self.timeout = int(9.4 * self.bit_len)
+
+    def handle_header(self):
+        if len(self.bits) == 1:
+            self.ss_header = self.ss_bit
+
+        if len(self.bits) == 8:
+            # See tc27xD_um_v2.2.pdf, Figure 20-47, for the header structure
+            bit_len = (self.es_bit - self.ss_header) / 8
+            value = bitpack(self.bits)
+
+            ss = self.ss_header
+            es = ss + 3 * bit_len
+            size_id = (value & 0xE0) >> 5
+            size = payload_sizes.get(size_id)
+            self.payload_size = payload_byte_sizes.get(size_id)
+            self.put_ann(int(ss), int(es), ann_header_pl_size, [size])
+
+            ss = es
+            es = ss + 4 * bit_len
+            self.ch_type_id = (value & 0x1E) >> 1
+            ch_type = channel_types.get(self.ch_type_id)
+            self.put_ann(int(ss), int(es), ann_header_ch_type, [ch_type])
+
+            ss = es
+            es = ss + bit_len
+            cts = value & 0x01
+            self.put_ann(int(ss), int(es), ann_header_cts, ['{}'.format(cts)])
+
+            self.bits = []
+            self.state = state_payload
+            self.timeout = int(9.4 * self.bit_len)
+
+    def handle_payload(self):
+        self.timeout = int((self.payload_size - len(self.payload)) * 8 * self.bit_len)
+
+        if len(self.bits) == 1:
+            self.ss_byte = self.ss_bit
+            if self.ss_payload == 0:
+                self.ss_payload = self.ss_bit
+
+        if len(self.bits) == 8:
+            value = bitpack(self.bits)
+            value_hex = '{:02X}'.format(value)
+
+            # Control transfers have no SIPI payload, show them as control transfers
+            # Check the channel_types list for the meaning of the magic values
+            if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011):
+                self.put_ann(self.ss_byte, self.es_bit, ann_payload, [value_hex])
+            else:
+                # Control transfers are 8-bit transfers, so only evaluate the first byte
+                if len(self.payload) == 0:
+                    ctrl_data = control_payloads.get(value, value_hex)
+                    self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [ctrl_data])
+                else:
+                    self.put_ann(self.ss_byte, self.es_bit, ann_control_data, [value_hex])
+
+            self.bits = []
+            self.es_payload = self.es_bit
+            self.payload.append((self.ss_byte, self.es_payload, value))
+
+            if (len(self.payload) == self.payload_size):
+                self.timeout = int(1.4 * self.bit_len)
+                self.state = state_sleepbit
+
+    def handle_sleepbit(self):
+        if len(self.bits) == 0:
+            self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N'])
+        elif len(self.bits) > 1:
+            self.put_ann(self.ss_bit, self.es_bit, ann_warning, ['Expected only the sleep bit, got {} bits instead'.format(len(self.bits))])
+        else:
+            if self.bits[0] == 1:
+                self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['LVDS sleep mode request', 'Sleep', 'Y'])
+            else:
+                self.put_ann(self.ss_bit, self.es_bit, ann_sleepbit, ['No LVDS sleep mode request', 'No sleep', 'N'])
+
+        # We only send the payload out if this is an actual data transfer;
+        # check the channel_types list for the meaning of the magic values
+        if (self.ch_type_id >= 0b0100) and (self.ch_type_id <= 0b1011):
+            if len(self.payload) > 0:
+                self.put_payload()
+
+    def decode(self):
+        while True:
+            if self.timeout == 0:
+                rising_edge, = self.wait({0: 'e'})
+            else:
+                rising_edge, = self.wait([{0: 'e'}, {'skip': self.timeout}])
+
+            # If this is the first edge, we only update ss
+            if self.ss == 0:
+                self.ss = self.samplenum
+                # Let's set the timeout for the sync pattern as well
+                self.timeout = int(16.2 * self.prev_bit_len)
+                continue
+
+            self.es = self.samplenum
+
+            # Check for the sleep bit if this is a timeout condition
+            if (len(self.matched) == 2) and self.matched[1]:
+                rising_edge = ~rising_edge
+                if self.state == state_sync:
+                    self.reset()
+                    continue
+                elif self.state == state_sleepbit:
+                    self.ss_bit += self.bit_len
+                    self.es_bit = self.ss_bit + self.bit_len
+                    self.handle_sleepbit()
+                    self.reset()
+                    continue
+
+            # Shouldn't happen but we check just in case
+            if int(self.es - self.ss) == 0:
+                continue
+
+            # We use the first bit to deduce the bit length
+            if self.bit_len == 0:
+                self.bit_len = self.es - self.ss
+
+            # Determine number of bits covered by this edge
+            bit_count = (self.es - self.ss) / self.bit_len
+            bit_count = int(decimal.Decimal(bit_count).to_integral_value())
+
+            if bit_count == 0:
+                self.put_ann(self.ss, self.es, ann_warning, ['Bit time too short'])
+                self.reset()
+                continue
+
+            bit_value = '0' if rising_edge else '1'
+
+            divided_len = (self.es - self.ss) / bit_count
+            for i in range(bit_count):
+                self.ss_bit = int(self.ss + i * divided_len)
+                self.es_bit = int(self.ss_bit + divided_len)
+                self.put_ann(self.ss_bit, self.es_bit, ann_bit, [bit_value])
+
+                # Place the new bit at the front of the bit list
+                self.bits.insert(0, (0 if rising_edge else 1))
+
+                if self.state == state_sync:
+                    self.handle_sync()
+                elif self.state == state_header:
+                    self.handle_header()
+                elif self.state == state_payload:
+                    self.handle_payload()
+                elif self.state == state_sleepbit:
+                    self.handle_sleepbit()
+                    self.reset()
+
+                if self.ss == 0:
+                    break  # Because reset() was called, invalidating everything
+
+            # Only update ss if we didn't just perform a reset
+            if self.ss > 0:
+                self.ss = self.samplenum
+
+            # If we got here when a timeout occurred, we have processed all null
+            # bits that we could and should reset now to find the next packet
+            if (len(self.matched) == 2) and self.matched[1]:
+                self.reset()
index cf1707fa670b07265486204d3d885906f6a0fbb8..2a88e30473841140682d14fb513a3044158661bf 100644 (file)
@@ -315,13 +315,9 @@ class Decoder(srd.Decoder):
         self.state = 'IDLE'
 
     def decode(self):
+        conditions = [{i: 'e'} for i in range(6)]
         while True:
-            # TODO: Come up with more appropriate self.wait() conditions.
-            pins = self.wait()
-
-            # If none of the pins changed, there's nothing to do.
-            if self.oldpins == pins:
-                continue
+            pins = self.wait(conditions)
 
             # Store current pin values for the next round.
             self.oldpins = pins
index b276e5db7bf173ec31b7e7ccad929ba8a5ecf8c3..a393abfac6b103fcc9d411b8b5afeb1dbf3aefbc 100644 (file)
@@ -51,24 +51,22 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.variant = None
-        self.ss_block = None
-        self.es_block = None
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.variant = self.options['variant']
 
-    def putx(self, data):
-        self.put(self.ss_block, self.es_block, self.out_ann, data)
+    def putg(self, ss, es, cls, text):
+        self.put(ss, es, self.out_ann, [cls, [text]])
 
-    def handle_data(self, value):
-        if value == 0xFF:
-          self.putx([1, ['No button is pressed']])
-          return
+    def handle_data(self, ss, es, value):
+        if value == 0xff:
+            self.putg(ss, es, 1, 'No button is pressed')
+            return
 
         if value == 0x00:
-          self.putx([2, ['Gamepad is not connected']])
-          return
+            self.putg(ss, es, 2, 'Gamepad is not connected')
+            return
 
         buttons = [
             'A',
@@ -78,28 +76,17 @@ class Decoder(srd.Decoder):
             'North',
             'South',
             'West',
-            'East'
+            'East',
         ]
 
-        bits = format(value, '08b')
-        button_str = ''
-
-        for b in enumerate(bits):
-            button_index = b[0]
-            button_is_pressed = b[1] == '0'
-
-            if button_is_pressed:
-                if button_str != '':
-                    button_str += ' + '
-                button_str += buttons[button_index]
-
-        self.putx([0, ['%s' % button_str]])
+        bits = '{:08b}'.format(value)
+        text = [buttons[i] for i, b in enumerate(bits) if b == '0']
+        text = ' + '.join(text)
+        self.putg(ss, es, 0, text)
 
     def decode(self, ss, es, data):
-        ptype, mosi, miso = data
-        self.ss_block, self.es_block = ss, es
-
-        if ptype != 'DATA':
-          return
-
-        self.handle_data(miso)
+        ptype, _, _ = data
+        if ptype == 'DATA':
+            _, _, miso = data
+            self.handle_data(ss, es, miso)
+            return
index 3192c476df13a0840babfa1ba94dcebe4cd9c05b..12949fea618c1d8dc5d0b42f43b0e5e90cb9b88e 100644 (file)
@@ -85,7 +85,7 @@ class Decoder(srd.Decoder):
     desc = '433/868/933MHz transceiver chip.'
     license = 'mit'
     inputs = ['spi']
-    outputs = ['nrf905']
+    outputs = []
     tags = ['IC', 'Wireless/RF']
     annotations = (
         ('cmd', 'Command sent to the device'),
diff --git a/decoders/numbers_and_state/__init__.py b/decoders/numbers_and_state/__init__.py
new file mode 100644 (file)
index 0000000..4fe42a3
--- /dev/null
@@ -0,0 +1,41 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Comlab AG
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+'''
+This protocol decoder takes a set of logic input signals, and interprets
+their bit pattern according to user specifications as different kinds of
+numbers, or an enumeration of e.g. machine states.
+
+Supported formats are: signed and unsigned integers, fixed point numbers,
+IEEE754 floating point numbers, and number to text mapping controlled by
+external data files. (Support for half precision floats depends on the
+Python runtime, and may not universally be available.)
+
+User provided text mapping files can either use the JSON format:
+  {"one": 1, "two": 2, "four": 4}
+or the Python programming language:
+  enumtext = { 1: "one", 2: "two", 3: "three", }
+
+In addition to all enum values on one row (sequential presentation of
+the data), a limited number of enum values also are shown in tabular
+presentation, which can help visualize state machines or task switches.
+'''
+
+from .pd import Decoder
diff --git a/decoders/numbers_and_state/pd.py b/decoders/numbers_and_state/pd.py
new file mode 100644 (file)
index 0000000..b8ac87e
--- /dev/null
@@ -0,0 +1,377 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Comlab AG
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+# This implementation started as a "vector slicer", then turned into the
+# "numbers and states" decoder, because users always had the freedom to
+# connect any logic signal to either of the decoder inputs. That's when
+# slicing vectors took second seat, and just was not needed any longer
+# in the strict sense.
+#
+# TODO
+# - Find an appropriate number of input channels, and maximum enum slots.
+# - Re-check correctness of signed integers. Signed fixed point is based
+#   on integers and transparently benefits from fixes and improvements.
+# - Local formatting in individual decoders becomes obsolete when common
+#   support for user selected formatting gets introduced.
+# - There is overlap with the 'parallel' decoder. Ideally the numbers
+#   decoder could stack on top of parallel, but parallel currently is
+#   severely limited in its number of input channels, and dramatically
+#   widening the parallel decoder may be undesirable.
+
+from common.srdhelper import bitpack
+import json
+import sigrokdecode as srd
+import struct
+
+'''
+OUTPUT_PYTHON format:
+
+Packet:
+[<ptype>, <pdata>]
+
+This is a list of <ptype>s and their respective <pdata> values:
+ - 'RAW': The data is a tuple of bit count and bit pattern (a number,
+   assuming unsigned integer presentation of the input data bit pattern).
+ - 'NUMBER': The data is the conversion result of the bit pattern.
+ - 'ENUM': The data is a tuple of the raw number and its mapped text.
+'''
+
+# TODO Better raise the number of channels to 32. This allows access to
+# IEEE754 single precision numbers, and shall cover most busses, _and_
+# remains within most logic analyzers' capabilities, and keeps the UI
+# dialog somewhat managable. What's a good default for the number of
+# enum slots (which translate to annotation rows)? Notice that 2 to the
+# power of the channel count is way out of the question. :)
+_max_channels = 16
+_max_enum_slots = 32
+
+class ChannelError(Exception):
+    pass
+
+class Pin:
+    CLK, BIT_0 = range(2)
+    BIT_N = BIT_0 + _max_channels
+
+class Ann:
+    RAW, NUM = range(2)
+    ENUM_0 = NUM + 1
+    ENUM_OVR = ENUM_0 + _max_enum_slots
+    ENUMS = range(ENUM_0, ENUM_OVR)
+    WARN = ENUM_OVR + 1
+
+    @staticmethod
+    def enum_indices():
+        return [i for i in range(Ann.ENUMS)]
+
+    @staticmethod
+    def get_enum_idx(code):
+        if code in range(_max_enum_slots):
+            return Ann.ENUM_0 + code
+        return Ann.ENUM_OVR
+
+def _channel_decl(count):
+    return tuple([
+        {'id': 'bit{}'.format(i), 'name': 'Bit{}'.format(i), 'desc': 'Bit position {}'.format(i)}
+        for i in range(count)
+    ])
+
+def _enum_cls_decl(count):
+    return tuple([
+        ('enum{}'.format(i), 'Enumeration slot {}'.format(i))
+        for i in range(count)
+    ] + [('enumovr', 'Enumeration overflow')])
+
+def _enum_rows_decl(count):
+    return tuple([
+        ('enums{}'.format(i), 'Enumeration slots {}'.format(i), (Ann.ENUM_0 + i,))
+        for i in range(count)
+    ] + [('enumsovr', 'Enumeration overflows', (Ann.ENUM_OVR,))])
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'numbers_and_state'
+    name = 'Numbers and State'
+    longname = 'Interpret bit patters as numbers or state enums'
+    desc = 'Interpret bit patterns as different kinds of numbers (integer, float, enum).'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['numbers_and_state']
+    tags = ['Encoding', 'Util']
+    optional_channels = (
+        {'id': 'clk', 'name': 'Clock', 'desc': 'Clock'},
+    ) + _channel_decl(_max_channels)
+    options = (
+        {'id': 'clkedge', 'desc': 'Clock edge', 'default': 'rising',
+            'values': ('rising', 'falling', 'either')},
+        {'id': 'count', 'desc': 'Total bits count', 'default': 0},
+        {'id': 'interp', 'desc': 'Interpretation', 'default': 'unsigned',
+            'values': ('unsigned', 'signed', 'fixpoint', 'fixsigned', 'ieee754', 'enum')},
+        {'id': 'fracbits', 'desc': 'Fraction bits count', 'default': 0},
+        {'id': 'mapping', 'desc': 'Enum to text map file',
+            'default': 'enumtext.json'},
+        {'id': 'format', 'desc': 'Number format', 'default': '-',
+            'values': ('-', 'bin', 'oct', 'dec', 'hex')},
+    )
+    annotations = (
+        ('raw', 'Raw pattern'),
+        ('number', 'Number'),
+    ) + _enum_cls_decl(_max_enum_slots) + (
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('raws', 'Raw bits', (Ann.RAW,)),
+        ('numbers', 'Numbers', (Ann.NUM,)),
+    ) + _enum_rows_decl(_max_enum_slots) + (
+        ('warnings', 'Warnings', (Ann.WARN,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        pass
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+    def putg(self, ss, es, cls, data):
+        self.put(ss, es, self.out_ann, [cls, data])
+
+    def putpy(self, ss, es, ptype, pdata):
+        self.put(ss, es, self.out_python, (ptype, pdata))
+
+    def grab_pattern(self, pins):
+        '''Get a bit pattern from potentially incomplete probes' values.'''
+
+        # Pad and trim the input data, to achieve the user specified
+        # total number of bits. Map all unassigned signals to 0 (low).
+        # Return raw number (unsigned integer interpreation).
+        bits = pins + (None,) * self.bitcount
+        bits = bits[:self.bitcount]
+        bits = [b if b in (0, 1) else 0 for b in bits]
+        pattern = bitpack(bits)
+        return pattern
+
+    def handle_pattern(self, ss, es, pattern):
+        fmt = '{{:0{}b}}'.format(self.bitcount)
+        txt = fmt.format(pattern)
+        self.putg(ss, es, Ann.RAW, [txt])
+        self.putpy(ss, es, 'RAW', (self.bitcount, pattern))
+
+        try:
+            value = self.interpreter(ss, es, pattern)
+        except:
+            value = None
+        if value is None:
+            return
+        self.putpy(ss, es, 'NUMBER', value)
+        try:
+            formatted = self.formatter(ss, es, value)
+        except:
+            formatted = None
+        if formatted:
+            self.putg(ss, es, Ann.NUM, formatted)
+            if self.interpreter == self.interp_enum:
+                cls = Ann.get_enum_idx(pattern)
+                self.putg(ss, es, cls, formatted)
+                self.putpy(ss, es, 'ENUM', (value, formatted))
+
+    def interp_unsigned(self, ss, es, pattern):
+        value = pattern
+        return value
+
+    def interp_signed(self, ss, es, pattern):
+        if not 'signmask' in self.interp_state:
+            self.interp_state.update({
+                'signmask': 1 << (self.bitcount - 1),
+                'signfull': 1 << self.bitcount,
+            })
+        is_neg = pattern & self.interp_state['signmask']
+        if is_neg:
+            value = -(self.interp_state['signfull'] - pattern)
+        else:
+            value = pattern
+        return value
+
+    def interp_fixpoint(self, ss, es, pattern):
+        if not 'fixdiv' in self.interp_state:
+            self.interp_state.update({
+                'fixsign': self.options['interp'] == 'fixsigned',
+                'fixdiv': 2 ** self.options['fracbits'],
+            })
+        if self.interp_state['fixsign']:
+            value = self.interp_signed(ss, es, pattern)
+        else:
+            value = self.interp_unsigned(ss, es, pattern)
+        value /= self.interp_state['fixdiv']
+        return value
+
+    def interp_ieee754(self, ss, es, pattern):
+        if not 'ieee_has_16bit' in self.interp_state:
+            self.interp_state.update({
+                'ieee_fmt_int_16': '=H',
+                'ieee_fmt_flt_16': '=e',
+                'ieee_fmt_int_32': '=L',
+                'ieee_fmt_flt_32': '=f',
+                'ieee_fmt_int_64': '=Q',
+                'ieee_fmt_flt_64': '=d',
+            })
+            try:
+                fmt = self.interp_state.update['ieee_fmt_flt_16']
+                has_16bit_support = 8 * struct.calcsize(fmt) == 16
+            except:
+                has_16bit_support = False
+            self.interp_state['ieee_has_16bit'] = has_16bit_support
+        if self.bitcount == 16:
+            if not self.interp_state['ieee_has_16bit']:
+                return None
+            buff = struct.pack(self.interp_state['ieee_fmt_int_16'], pattern)
+            value, = struct.unpack(self.interp_state['ieee_fmt_flt_16'], buff)
+            return value
+        if self.bitcount == 32:
+            buff = struct.pack(self.interp_state['ieee_fmt_int_32'], pattern)
+            value, = struct.unpack(self.interp_state['ieee_fmt_flt_32'], buff)
+            return value
+        if self.bitcount == 64:
+            buff = struct.pack(self.interp_state['ieee_fmt_int_64'], pattern)
+            value, = struct.unpack(self.interp_state['ieee_fmt_flt_64'], buff)
+            return value
+        return None
+
+    def interp_enum(self, ss, es, pattern):
+        if not 'enum_map' in self.interp_state:
+            self.interp_state.update({
+                'enum_fn': self.options['mapping'],
+                'enum_map': {},
+                'enum_have_map': False,
+            })
+            try:
+                fn = self.interp_state['enum_fn']
+                # TODO Optionally try in several locations? Next to the
+                # decoder implementation? Where else? Expect users to
+                # enter absolute paths?
+                with open(fn, 'r') as f:
+                    maptext = f.read()
+                maptable = {}
+                if fn.endswith('.js') or fn.endswith('.json'):
+                    # JSON requires string literals on the LHS, so the
+                    # table is written "in reverse order".
+                    js_table = json.loads(maptext)
+                    for k, v in js_table.items():
+                        maptable[v] = k
+                elif fn.endswith('.py'):
+                    # Expect a specific identifier at the Python module
+                    # level, and assume that it's a dictionary.
+                    py_table = {}
+                    exec(maptext, py_table)
+                    maptable.update(py_table['enumtext'])
+                self.interp_state['enum_map'].update(maptable)
+                self.interp_state['enum_have_map'] = True
+            except:
+                # Silently ignore failure. This happens while the user
+                # is typing the filename, and is non-fatal. If the file
+                # exists and is not readable or not valid or of unknown
+                # format, the worst thing that can happen is that the
+                # decoder implementation keeps using "anonymous" phrases
+                # until a mapping has become available. No harm is done.
+                # This decoder cannot tell intermediate from final file
+                # read attempts, so we cannot raise severity here.
+                pass
+        value = self.interp_state['enum_map'].get(pattern, None)
+        if value is None:
+            value = pattern
+        return value
+
+    def format_native(self, ss, es, value):
+        return ['{}'.format(value),]
+
+    def format_bin(self, ss, es, value):
+        if not self.format_string:
+            self.format_string = '{{:0{}b}}'.format(self.bitcount)
+        return [self.format_string.format(value)]
+
+    def format_oct(self, ss, es, value):
+        if not self.format_string:
+            self.format_string = '{{:0{}o}}'.format((self.bitcount + 3 - 1) // 3)
+        return [self.format_string.format(value)]
+
+    def format_dec(self, ss, es, value):
+        if not self.format_string:
+            self.format_string = '{:d}'
+        return [self.format_string.format(value)]
+
+    def format_hex(self, ss, es, value):
+        if not self.format_string:
+            self.format_string = '{{:0{}x}}'.format((self.bitcount + 4 - 1) // 4)
+        return [self.format_string.format(value)]
+
+    def decode(self):
+        channels = [ch for ch in range(_max_channels) if self.has_channel(ch)]
+        have_clk = Pin.CLK in channels
+        if have_clk:
+            channels.remove(Pin.CLK)
+        if not channels:
+            raise ChannelError("Need at least one bit channel.")
+        if have_clk:
+            clkedge = {
+                'rising': 'r',
+                'falling': 'f',
+                'either': 'e',
+            }.get(self.options['clkedge'])
+            wait_cond = {Pin.CLK: clkedge}
+        else:
+            wait_cond = [{ch: 'e'} for ch in channels]
+
+        bitcount = self.options['count']
+        if not bitcount:
+            bitcount = channels[-1] - Pin.BIT_0 + 1
+        self.bitcount = bitcount
+
+        self.interpreter = {
+            'unsigned': self.interp_unsigned,
+            'signed': self.interp_signed,
+            'fixpoint': self.interp_fixpoint,
+            'fixsigned': self.interp_fixpoint,
+            'ieee754': self.interp_ieee754,
+            'enum': self.interp_enum,
+        }.get(self.options['interp'])
+        self.interp_state = {}
+        self.formatter = {
+            '-': self.format_native,
+            'bin': self.format_bin,
+            'oct': self.format_oct,
+            'dec': self.format_dec,
+            'hex': self.format_hex,
+        }.get(self.options['format'])
+        self.format_string = None
+
+        pins = self.wait()
+        ss = self.samplenum
+        prev_pattern = self.grab_pattern(pins[Pin.BIT_0:])
+        while True:
+            pins = self.wait(wait_cond)
+            es = self.samplenum
+            pattern = self.grab_pattern(pins[Pin.BIT_0:])
+            if pattern == prev_pattern:
+                continue
+            self.handle_pattern(ss, es, prev_pattern)
+            ss = es
+            prev_pattern = pattern
index 100523ec1464be87babf055d48be212372e38ca8..e7ed36cb2c7d452331f05fdd20c20ff2068818b6 100644 (file)
 
 '''
 This protocol decoder can decode synchronous parallel buses with various
-number of data bits/channels and one (optional) clock line.
+data bits/channels counts, an (optional) clock line, and an (optional)
+select/enable/reset line.
 
-If no clock line is supplied, the decoder works slightly differently in
-that it interprets every transition on any of the supplied data channels
-like there had been a clock transition.
+Data bits are taken from the decoder's lowest connected input pins. The
+input signal's data lines count need not span the full amount of the
+decoder's maximum supported data lines count. Not connected data lines
+are assumed to be low.
 
-It is required to use the lowest data channels, and use consecutive ones.
-For example, for a 4-bit sync parallel bus, channels D0/D1/D2/D3 (and CLK)
-should be used. Using combinations like D7/D12/D3/D15 is not supported.
-For an 8-bit bus you should use D0-D7, for a 16-bit bus use D0-D15 and so on.
+Example use cases are: Connect D3/D2/D1/D0 (and CLK) to a 4-bit bus.
+Connect D7 and D6 to inspect the two most significant bits of an 8-bit
+bus (and have 8-bit values shown instead of just 2-bit values).
+
+When provided, the specified clock edge determines when data lines get
+sampled. Without a clock spec, each transition on any of the data lines
+will be shown, which can become busy/noisy depending on the input data.
+
+Another signal optionally can control the period of time within which
+the data lines' bit pattern gets interpreted. Typical use cases would be
+reset, or select, or enable signals that are related to the bus' data
+communication. This optional signal can also improve synchronization to
+wider payload data which spans several bus cycles (multiplexing).
 '''
 
 from .pd import Decoder
index 8f23aa836e9f985ca456287a62d9d29f3365cef9..1e3120858a7a4f750cd1ee3810a803c4439b7435 100644 (file)
@@ -54,18 +54,22 @@ Packet:
    word <worditemcount> is 7, and so on.
 '''
 
-def channel_list(num_channels):
-    l = [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}]
-    for i in range(num_channels):
-        d = {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data line %d' % i}
-        l.append(d)
-    return tuple(l)
+NUM_CHANNELS = 16
+
+class Pin:
+    CLOCK = 0
+    DATA_0 = CLOCK + 1
+    DATA_N = DATA_0 + NUM_CHANNELS
+    # BEWARE! DATA_N points _beyond_ the data partition (Python range(3)
+    # semantics, useful to have to simplify other code locations).
+    RESET = DATA_N
+
+class Ann:
+    ITEM, WORD, WARN = range(3)
 
 class ChannelError(Exception):
     pass
 
-NUM_CHANNELS = 8
-
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'parallel'
@@ -76,10 +80,19 @@ class Decoder(srd.Decoder):
     inputs = ['logic']
     outputs = ['parallel']
     tags = ['Util']
-    optional_channels = channel_list(NUM_CHANNELS)
+    optional_channels = tuple(
+        [{'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'}] +
+        [
+            {'id': 'd%d' % i, 'name': 'D%d' % i, 'desc': 'Data line %d' % i}
+            for i in range(NUM_CHANNELS)
+        ] +
+        [{'id': 'rst', 'name': 'RST', 'desc': 'RESET line'}]
+    )
     options = (
         {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
-            'default': 'rising', 'values': ('rising', 'falling')},
+            'default': 'rising', 'values': ('rising', 'falling', 'either')},
+        {'id': 'reset_polarity', 'desc': 'Reset line polarity',
+            'default': 'low-active', 'values': ('low-active', 'high-active')},
         {'id': 'wordsize', 'desc': 'Data wordsize (# bus cycles)',
             'default': 0},
         {'id': 'endianness', 'desc': 'Data endianness',
@@ -88,125 +101,185 @@ class Decoder(srd.Decoder):
     annotations = (
         ('item', 'Item'),
         ('word', 'Word'),
+        ('warning', 'Warning'),
     )
     annotation_rows = (
-        ('items', 'Items', (0,)),
-        ('words', 'Words', (1,)),
+        ('items', 'Items', (Ann.ITEM,)),
+        ('words', 'Words', (Ann.WORD,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+    )
+    binary = (
+        ('binary', 'Binary'),
     )
 
     def __init__(self):
         self.reset()
 
     def reset(self):
-        self.items = []
-        self.saved_item = None
-        self.ss_item = self.es_item = None
-        self.saved_word = None
-        self.ss_word = self.es_word = None
-        self.first = True
+        self.pend_item = None
+        self.word_items = []
 
     def start(self):
         self.out_python = self.register(srd.OUTPUT_PYTHON)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
-    def putpb(self, data):
-        self.put(self.ss_item, self.es_item, self.out_python, data)
-
-    def putb(self, data):
-        self.put(self.ss_item, self.es_item, self.out_ann, data)
-
-    def putpw(self, data):
-        self.put(self.ss_word, self.es_word, self.out_python, data)
-
-    def putw(self, data):
-        self.put(self.ss_word, self.es_word, self.out_ann, data)
-
-    def handle_bits(self, item, used_pins):
-
-        # If a word was previously accumulated, then emit its annotation
-        # now after its end samplenumber became available.
-        if self.saved_word is not None:
-            if self.options['wordsize'] > 0:
-                self.es_word = self.samplenum
-                self.putw([1, [self.fmt_word.format(self.saved_word)]])
-                self.putpw(['WORD', self.saved_word])
-            self.saved_word = None
-
-        # Defer annotations for individual items until the next sample
-        # is taken, and the previous sample's end samplenumber has
-        # become available.
-        if self.first:
-            # Save the start sample and item for later (no output yet).
-            self.ss_item = self.samplenum
-            self.first = False
-            self.saved_item = item
-        else:
-            # Output the saved item (from the last CLK edge to the current).
-            self.es_item = self.samplenum
-            self.putpb(['ITEM', self.saved_item])
-            self.putb([0, [self.fmt_item.format(self.saved_item)]])
-            self.ss_item = self.samplenum
-            self.saved_item = item
-
-        # Get as many items as the configured wordsize specifies.
-        if not self.items:
-            self.ss_word = self.samplenum
-        self.items.append(item)
-        ws = self.options['wordsize']
-        if len(self.items) < ws:
+    def putg(self, ss, es, ann, txts):
+        self.put(ss, es, self.out_ann, [ann, txts])
+
+    def putpy(self, ss, es, ann, data):
+        self.put(ss, es, self.out_python, [ann, data])
+
+    def putbin(self, ss, es, ann_class, data):
+        self.put(ss, es, self.out_binary, [ann_class, data])
+
+    def flush_word(self, bus_width):
+        if not self.word_items:
             return
+        word_size = self.options['wordsize']
+
+        items = self.word_items
+        ss, es = items[0][0], items[-1][1]
+        items = [i[2] for i in items]
+        if self.options['endianness'] == 'big':
+            items.reverse()
+        word = sum([d << (i * bus_width) for i, d in enumerate(items)])
+
+        txts = [self.fmt_word.format(word)]
+        self.putg(ss, es, Ann.WORD, txts)
+        self.putpy(ss, es, 'WORD', (word, bus_width, word_size))
+
+        if len(items) != word_size:
+            txts = ['incomplete word size', 'word size', 'ws']
+            self.putg(ss, es, Ann.WARN, txts)
+
+        self.word_items.clear()
+
+    def queue_word(self, now, item, bus_width):
+        wordsize = self.options['wordsize']
+        if not wordsize:
+            return
+
+        # Terminate a previously seen item of a word first. Emit the
+        # word's annotation when the last item's end was seen.
+        if self.word_items:
+            ss, _, data = self.word_items[-1]
+            es = now
+            self.word_items[-1] = (ss, es, data)
+            if len(self.word_items) == wordsize:
+                self.flush_word(bus_width)
+
+        # Start tracking the currently seen item (yet unknown end time).
+        if item is not None:
+            pend = (now, None, item)
+            self.word_items.append(pend)
+
+    def handle_bits(self, now, item, bus_width):
 
-        # Collect words and prepare annotation details, but defer emission
-        # until the end samplenumber becomes available.
-        endian = self.options['endianness']
-        if endian == 'big':
-            self.items.reverse()
-        word = sum([self.items[i] << (i * used_pins) for i in range(ws)])
-        self.saved_word = word
-        self.items = []
+        # Optionally flush a previously started item.
+        if self.pend_item:
+            ss, _, data = self.pend_item
+            self.pend_item = None
+            es = now
+            txts = [self.fmt_item.format(data)]
+            self.putg(ss, es, Ann.ITEM, txts)
+            self.putpy(ss, es, 'ITEM', (data, bus_width))
+            self.putbin(ss, es, 0, data.to_bytes(1, byteorder='big'))
+
+        # Optionally queue the currently seen item.
+        if item is not None:
+            self.pend_item = (now, None, item)
+
+        # Pass the current item to the word accumulation logic.
+        self.queue_word(now, item, bus_width)
 
     def decode(self):
         # Determine which (optional) channels have input data. Insist in
         # a non-empty input data set. Cope with sparse connection maps.
         # Store enough state to later "compress" sampled input data.
-        max_possible = len(self.optional_channels)
-        idx_channels = [
+        data_indices = [
             idx if self.has_channel(idx) else None
-            for idx in range(max_possible)
+            for idx in range(Pin.DATA_0, Pin.DATA_N)
         ]
-        has_channels = [idx for idx in idx_channels if idx is not None]
-        if not has_channels:
-            raise ChannelError('At least one channel has to be supplied.')
-        max_connected = max(has_channels)
-
-        # Determine .wait() conditions, depending on the presence of a
-        # clock signal. Either inspect samples on the configured edge of
-        # the clock, or inspect samples upon ANY edge of ANY of the pins
-        # which provide input data.
-        if self.has_channel(0):
-            edge = self.options['clock_edge'][0]
-            conds = {0: edge}
-        else:
-            conds = [{idx: 'e'} for idx in has_channels]
+        has_data = [idx for idx in data_indices if idx is not None]
+        if not has_data:
+            raise ChannelError('Need at least one data channel.')
+        max_connected = max(has_data)
 
         # Pre-determine which input data to strip off, the width of
         # individual items and multiplexed words, as well as format
         # strings here. This simplifies call sites which run in tight
         # loops later.
-        idx_strip = max_connected + 1
-        num_item_bits = idx_strip - 1
+        upper_data_bound = max_connected + 1
+        num_item_bits = upper_data_bound - Pin.DATA_0
         num_word_items = self.options['wordsize']
         num_word_bits = num_item_bits * num_word_items
-        num_digits = (num_item_bits + 3) // 4
+        num_digits = (num_item_bits + 4 - 1) // 4
         self.fmt_item = "{{:0{}x}}".format(num_digits)
-        num_digits = (num_word_bits + 3) // 4
+        num_digits = (num_word_bits + 4 - 1) // 4
         self.fmt_word = "{{:0{}x}}".format(num_digits)
 
+        # Determine .wait() conditions, depending on the presence of a
+        # clock signal. Either inspect samples on the configured edge of
+        # the clock, or inspect samples upon ANY edge of ANY of the pins
+        # which provide input data.
+        conds = []
+        cond_idx_clock = None
+        cond_idx_data_0 = None
+        cond_idx_data_N = None
+        cond_idx_reset = None
+        has_clock = self.has_channel(Pin.CLOCK)
+        if has_clock:
+            cond_idx_clock = len(conds)
+            edge = {
+                'rising': 'r',
+                'falling': 'f',
+                'either': 'e',
+            }.get(self.options['clock_edge'])
+            conds.append({Pin.CLOCK: edge})
+        else:
+            cond_idx_data_0 = len(conds)
+            conds.extend([{idx: 'e'} for idx in has_data])
+            cond_idx_data_N = len(conds)
+        has_reset = self.has_channel(Pin.RESET)
+        if has_reset:
+            cond_idx_reset = len(conds)
+            conds.append({Pin.RESET: 'e'})
+            reset_active = {
+                'low-active': 0,
+                'high-active': 1,
+            }.get(self.options['reset_polarity'])
+
         # Keep processing the input stream. Assume "always zero" for
         # not-connected input lines. Pass data bits (all inputs except
-        # clock) to the handle_bits() method.
+        # clock and reset) to the handle_bits() method. Handle reset
+        # edges first and data changes then, within the same iteration.
+        # This results in robust operation for low-oversampled input.
+        in_reset = False
         while True:
-            pins = self.wait(conds)
-            bits = [0 if idx is None else pins[idx] for idx in idx_channels]
-            item = bitpack(bits[1:idx_strip])
-            self.handle_bits(item, num_item_bits)
+            try:
+                pins = self.wait(conds)
+            except EOFError as e:
+                break
+            clock_edge = cond_idx_clock is not None and self.matched[cond_idx_clock]
+            data_edge = cond_idx_data_0 is not None and [idx for idx in range(cond_idx_data_0, cond_idx_data_N) if self.matched[idx]]
+            reset_edge = cond_idx_reset is not None and self.matched[cond_idx_reset]
+
+            if reset_edge:
+                in_reset = pins[Pin.RESET] == reset_active
+                if in_reset:
+                    self.handle_bits(self.samplenum, None, num_item_bits)
+                    self.flush_word(num_item_bits)
+            if in_reset:
+                continue
+
+            if clock_edge or data_edge:
+                data_bits = [0 if idx is None else pins[idx] for idx in data_indices]
+                data_bits = data_bits[:num_item_bits]
+                item = bitpack(data_bits)
+                self.handle_bits(self.samplenum, item, num_item_bits)
+
+        self.handle_bits(self.samplenum, None, num_item_bits)
+        # TODO Determine whether a WARN annotation needs to get emitted.
+        # The decoder has not seen the end of the last accumulated item.
+        # Instead it just ran out of input data.
index e27f215f0144f327680725dc7ebf5012f5638a05..ea8715d6d2ca15c1fecfd5d7b61ddc18a9eae215 100644 (file)
@@ -23,6 +23,12 @@ NUM_OUTPUT_CHANNELS = 8
 
 # TODO: Other I²C functions: general call / reset address, device ID address.
 
+def logic_channels(num_channels):
+    l = []
+    for i in range(num_channels):
+        l.append(tuple(['p%d' % i, 'P%d' % i]))
+    return tuple(l)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'pca9571'
@@ -38,6 +44,7 @@ class Decoder(srd.Decoder):
         ('value', 'Register value'),
         ('warning', 'Warning'),
     )
+    logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS)
     annotation_rows = (
         ('regs', 'Registers', (0, 1)),
         ('warnings', 'Warnings', (2,)),
@@ -49,13 +56,24 @@ class Decoder(srd.Decoder):
     def reset(self):
         self.state = 'IDLE'
         self.last_write = 0xFF # Chip port default state is high.
+        self.last_write_es = 0
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_logic = self.register(srd.OUTPUT_LOGIC)
+
+    def flush(self):
+        self.put_logic_states()
 
     def putx(self, data):
         self.put(self.ss, self.es, self.out_ann, data)
 
+    def put_logic_states(self):
+        if (self.es > self.last_write_es):
+            data = bytes([self.last_write])
+            self.put(self.last_write_es, self.es, self.out_logic, [0, data])
+            self.last_write_es = self.es
+
     def handle_io(self, b):
         if self.state == 'READ DATA':
             operation = ['Outputs read', 'R']
@@ -64,7 +82,9 @@ class Decoder(srd.Decoder):
                                '(%02X) are different' % self.last_write]])
         else:
             operation = ['Outputs set', 'W']
+            self.put_logic_states()
             self.last_write = b
+
         self.putx([1, [operation[0] + ': %02X' % b,
                        operation[1] + ': %02X' % b]])
 
diff --git a/decoders/pjdl/__init__.py b/decoders/pjdl/__init__.py
new file mode 100644 (file)
index 0000000..c3cc855
--- /dev/null
@@ -0,0 +1,27 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+'''
+This protocol decoder interprets the PJDL data link of the PJON protocol.
+Bytes and frames get extracted from single wire serial communication
+(which often is referred to as "software bitbang" because that's what
+the Arduino reference implementation happens to do).
+'''
+
+from .pd import Decoder
diff --git a/decoders/pjdl/pd.py b/decoders/pjdl/pd.py
new file mode 100644 (file)
index 0000000..d5cc39f
--- /dev/null
@@ -0,0 +1,723 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+# See the https://www.pjon.org/ PJON project page and especially the
+# https://www.pjon.org/PJDL-specification-v4.1.php PJDL v4.1 spec for
+# the "Padded Jittering Data Link" single wire serial data link layer.
+
+# TODO
+# - Improve (fix, and extend) carrier sense support. Detection of the
+#   idle/busy connection state is incomplete and fragile. Getting 'IDLE'
+#   operational in the PJDL decoder would greatly help the PJON decoder
+#   to flush ACK details before the start of new frames.
+# - Check the correctness of timing assumptions. This implementation has
+#   support for tolerances, which the spec does not discuss. Though real
+#   world traffic was found to not decode at all with strict spec values
+#   and without tolerances, while communication peers were able to talk
+#   to each other. This needs more attention.
+# - Check robustness when input data contains glitches. The spec does
+#   not discuss how to handle these. Data bit sampling happens to work
+#   because their value is taken at the center of the bit time. But
+#   pad bits suffer badly from glitches, which breaks frame inspection
+#   as well.
+# - Cleanup the decoder implementation in general terms. Some details
+#   have become obsolete ("edges", "pads"), and/or are covered by other
+#   code paths.
+# - Implement more data link decoders which can feed their output into
+#   the PJON protocol decoder. Candidates are: PJDLR, PJDLS, TSDL.
+# - Determine whether or not the data link layer should interpret any
+#   frame content. From my perspective it should not, and needs not in
+#   the strict sense. Possible gains would be getting the (expected!)
+#   packet length, or whether a synchronous response is requested. But
+#   this would duplicate knowledge which should remain internal to the
+#   PJON decoder. And this link layer decoder neither shall assume that
+#   the input data would be correct, or complete. Instead the decoder
+#   shall remain usable on captures which demonstrate faults, and be
+#   helpful in pointing them out. The design goal is to extract the
+#   maximum of information possible, and pass it on as transparently
+#   as possible.
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack
+from math import ceil, floor
+
+'''
+OUTPUT_PYTHON format for stacked decoders:
+
+General packet format:
+[<ptype>, <pdata>]
+
+This is the list of <ptype>s and their respective <pdata> values:
+
+Carrier sense:
+- 'IDLE': <pdata> is the pin level (always 0).
+- 'BUSY': <pdata> is always True.
+
+Raw bit slots:
+- 'PAD_BIT': <pdata> is the pin level (always 1).
+- 'DATA_BIT': <pdata> is the pin level (0, or 1).
+- 'SHORT_BIT': <pdata> is the pin level (always 1).
+- 'SYNC_LOSS': <pdata> is an arbitrary text (internal use only).
+
+Date bytes and frames:
+- 'SYNC_PAD': <pdata> is True. Spans the high pad bit as well as the
+  low data bit.
+- 'DATA_BYTE': <pdata> is the byte value (0..255).
+- 'FRAME_INIT': <pdata> is True. Spans three sync pads.
+- 'FRAME_DATA': <pdata> is the sequence of bytes in the frame. Non-data
+  phases in the frame get represented by strings instead of numbers
+  ('INIT', 'SYNC', 'SHORT', 'WAIT'). Frames can be incomplete, depending
+  on the decoder's input data.
+- 'SYNC_RESP_WAIT': <pdata> is always True.
+
+Notice that this link layer decoder is not aware of frame content. Will
+neither check packet length, nor variable width fields, nor verify the
+presence of requested synchronous responses. Cannot tell the sequence of
+frame bytes then ACK bytes (without wait phase) from just frame bytes.
+An upper layer protocol decoder will interpret content, the link layer
+decoder remains as transparent as possible, and will neither assume
+correct nor complete input data.
+'''
+
+# Carrier sense, and synchronization loss implementation is currently
+# incomplete, and results in too many too short annotations, some of
+# them spurious and confusing. TODO Improve the implementation.
+_with_ann_carrier = False
+_with_ann_sync_loss = False
+
+PIN_DATA, = range(1)
+ANN_CARRIER_BUSY, ANN_CARRIER_IDLE, \
+ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA, ANN_SYNC_LOSS, \
+ANN_DATA_BYTE, \
+ANN_FRAME_INIT, ANN_FRAME_BYTES, ANN_FRAME_WAIT, \
+    = range(11)
+
+class SamplerateError(Exception):
+    pass
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'pjdl'
+    name = 'PJDL'
+    longname = 'Padded Jittering Data Link'
+    desc = 'PJDL, a single wire serial link layer for PJON.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = ['pjon_link']
+    tags = ['Embedded/industrial']
+    channels = (
+        {'id': 'data' , 'name': 'DATA', 'desc': 'Single wire data'},
+    )
+    options = (
+        {'id': 'mode', 'desc': 'Communication mode',
+            'default': 1, 'values': (1, 2, 3, 4)},
+        {'id': 'idle_add_us', 'desc': 'Added idle time (us)', 'default': 4},
+    )
+    annotations = (
+        ('cs_busy', 'Carrier busy'),
+        ('cs_idle', 'Carrier idle'),
+        ('bit_pad', 'Pad bit'),
+        ('bit_low', 'Low bit'),
+        ('bit_data', 'Data bit'),
+        ('bit_short', 'Short data'),
+        ('sync_loss', 'Sync loss'),
+        ('byte', 'Data byte'),
+        ('frame_init', 'Frame init'),
+        ('frame_bytes', 'Frame bytes'),
+        ('frame_wait', 'Frame wait'),
+    )
+    annotation_rows = (
+        ('carriers', 'Carriers', (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE,)),
+        ('bits', 'Bits', (ANN_PAD_BIT, ANN_LOW_BIT, ANN_DATA_BIT, ANN_SHORT_DATA,)),
+        ('bytes', 'Bytes', (ANN_FRAME_INIT, ANN_DATA_BYTE, ANN_FRAME_WAIT,)),
+        ('frames', 'Frames', (ANN_FRAME_BYTES,)),
+        ('warns', 'Warnings', (ANN_SYNC_LOSS,)),
+    )
+
+    # Communication modes' data bit and pad bit duration (in us), and
+    # tolerances in percent and absolute (us).
+    mode_times = {
+        1: (44, 116),
+        2: (40, 92),
+        3: (28, 88),
+        4: (26, 60),
+    }
+    time_tol_perc = 10
+    time_tol_abs = 1.5
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.reset_state()
+
+    def reset_state(self):
+        self.carrier_want_idle = True
+        self.carrier_is_busy = False
+        self.carrier_is_idle = False
+        self.carrier_idle_ss = None
+        self.carrier_busy_ss = None
+        self.syncpad_fall_ss = None
+
+        self.edges = None
+        self.symbols = None
+        self.sync_pads = None
+        self.data_bits = None
+        self.frame_bytes = None
+        self.short_bits = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_python = self.register(srd.OUTPUT_PYTHON)
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+            self.span_prepare()
+
+    def putg(self, ss, es, data):
+        cls = data[0]
+        if not _with_ann_carrier and cls in (ANN_CARRIER_BUSY, ANN_CARRIER_IDLE):
+            return
+        if not _with_ann_sync_loss and cls in (ANN_SYNC_LOSS,):
+            return
+        self.put(ss, es, self.out_ann, data)
+
+    def putpy(self, ss, es, ptype, pdata):
+        self.put(ss, es, self.out_python, [ptype, pdata])
+
+    def symbols_clear(self):
+        syms = self.symbols or []
+        self.symbols = []
+        return syms
+
+    def symbols_append(self, ss, es, symbol, data = None):
+        if self.symbols is None:
+            self.symbols = []
+        item = (ss, es, symbol, data)
+        self.symbols.append(item)
+
+    def symbols_get_last(self, count = None):
+        if not self.symbols:
+            return None
+        if count is None:
+            count = 1
+        if len(self.symbols) < count:
+            return None
+        items = self.symbols[-count:]
+        if count == 1:
+            items = items[0]
+        return items
+
+    def symbols_update_last(self, ss, es, symbol, data = None):
+        if not self.symbols:
+            return None
+        item = list(self.symbols[-1])
+        if ss is not None:
+            item[0] = ss
+        if es is not None:
+            item[1] = es
+        if symbol is not None:
+            item[2] = symbol
+        if data is not None:
+            item[3] = data
+        self.symbols[-1] = tuple(item)
+
+    def symbols_has_prev(self, want_items):
+        if not isinstance(want_items, (list, tuple,)):
+            want_items = [want_items]
+        if self.symbols is None:
+            return False
+        if len(self.symbols) < len(want_items):
+            return False
+        sym_off = len(self.symbols) - len(want_items)
+        for idx, want_item in enumerate(want_items):
+            if self.symbols[sym_off + idx][2] != want_item:
+                return False
+        return True
+
+    def symbols_collapse(self, count, symbol, data = None, squeeze = None):
+        if self.symbols is None:
+            return None
+        if len(self.symbols) < count:
+            return None
+        self.symbols, last_data = self.symbols[:-count], self.symbols[-count:]
+        while squeeze and self.symbols and self.symbols[-1][2] == squeeze:
+            last_data.insert(0, self.symbols.pop())
+        ss, es = last_data[0][0], last_data[-1][1]
+        if data is None:
+            data = last_data
+        item = (ss, es, symbol, data)
+        self.symbols.append(item)
+
+    def frame_flush(self):
+        syms = self.symbols_clear()
+        while syms and syms[0][2] == 'IDLE':
+            syms.pop(0)
+        while syms and syms[-1][2] == 'IDLE':
+            syms.pop(-1)
+        if not syms:
+            return
+        text = []
+        data = []
+        for sym in syms:
+            if sym[2] == 'FRAME_INIT':
+                text.append('INIT')
+                data.append('INIT')
+                continue
+            if sym[2] == 'SYNC_PAD':
+                if not text or text[-1] != 'SYNC':
+                    text.append('SYNC')
+                    data.append('SYNC')
+                continue
+            if sym[2] == 'DATA_BYTE':
+                b = [bit[3] for bit in sym[3] if bit[2] == 'DATA_BIT']
+                b = bitpack(b)
+                text.append('{:02x}'.format(b))
+                data.append(b)
+                continue
+            if sym[2] == 'SHORT_BIT':
+                if not text or text[-1] != 'SHORT':
+                    text.append('SHORT')
+                    data.append('SHORT')
+                continue
+            if sym[2] == 'WAIT_ACK':
+                text.append('WAIT')
+                data.append('WAIT')
+                continue
+        text = ' '.join(text)
+        ss, es = syms[0][0], syms[-1][1]
+        self.putg(ss, es, [ANN_FRAME_BYTES, [text]])
+        self.putpy(ss, es, 'FRAME_DATA', data)
+
+    def carrier_flush(self):
+        # Force annotations if BUSY started, or if IDLE tracking started
+        # and kept running for long enough. This will be called before
+        # internal state reset, so we won't manipulate internal variables,
+        # and can afford to emit annotations which haven't met their
+        # proper end condition yet.
+        if self.carrier_busy_ss:
+            ss, es = self.carrier_busy_ss, self.samplenum
+            self.putg(ss, es, [ANN_CARRIER_BUSY, ['BUSY']])
+        if self.carrier_idle_ss:
+            ss, es = self.carrier_idle_ss, self.samplenum
+            ss += int(self.idle_width)
+            if ss < es:
+                self.putg(ss, es, [ANN_CARRIER_IDLE, ['IDLE']])
+
+    def carrier_set_idle(self, on, ss, es):
+        if on:
+            # IDLE starts here, or continues.
+            if not self.carrier_idle_ss:
+                self.carrier_idle_ss = int(ss)
+            if not self.symbols_has_prev('IDLE'):
+                self.symbols_append(ss, ss, 'IDLE')
+            self.symbols_update_last(None, es, None)
+            # HACK We have seen an IDLE condition. This implementation
+            # loses details which are used to track IDLE, but it's more
+            # important to start accumulation of a new frame here.
+            self.frame_flush()
+            self.reset_state()
+            # end of HACK
+            self.carrier_is_idle = True
+            self.carrier_want_idle = False
+            return
+        # IDLE ends here.
+        if self.symbols_has_prev('IDLE'):
+            self.symbols_update_last(None, es, None)
+        self.carrier_flush()
+        self.carrier_is_idle = False
+        self.carrier_idle_ss = None
+
+    def carrier_set_busy(self, on, snum):
+        self.carrier_is_busy = on
+        if on:
+            self.carrier_is_idle = None
+            if not self.carrier_busy_ss:
+                self.carrier_busy_ss = snum
+            return
+        if self.carrier_busy_ss:
+            self.putg(self.carrier_busy_ss, snum, [ANN_CARRIER_BUSY, ['BUSY']])
+        self.carrier_busy_ss = None
+        self.carrier_is_busy = False
+
+    def carrier_check(self, level, snum):
+
+        # When HIGH is seen, immediately end IDLE and switch to BUSY.
+        if level:
+            self.carrier_set_idle(False, snum, snum)
+            self.carrier_set_busy(True, snum)
+            return
+
+        # LOW is seen. Start tracking an IDLE period if not done yet.
+        if not self.carrier_idle_ss:
+            self.carrier_idle_ss = int(snum)
+
+        # End BUSY when LOW persisted for an exact data byte's length.
+        # Start IDLE when LOW persisted for a data byte's length plus
+        # the user specified additional period.
+        span = snum - self.carrier_idle_ss
+        if span >= self.byte_width:
+            self.carrier_set_busy(False, snum)
+        if span >= self.idle_width:
+            self.carrier_set_idle(True, self.carrier_idle_ss + self.idle_width, snum)
+
+    def span_prepare(self):
+        '''Prepare calculation of durations in terms of samples.'''
+
+        # Determine samples per microsecond, and sample counts for
+        # several bit types, and sample count for a data byte's
+        # length, including optional extra time. Determine ranges
+        # for bit widths (tolerance margin).
+
+        # Get times in microseconds.
+        mode_times = self.mode_times[self.options['mode']]
+        mode_times = [t * 1.0 for t in mode_times]
+        self.data_width, self.pad_width = mode_times
+        self.byte_width = self.pad_width + 9 * self.data_width
+        self.add_idle_width = self.options['idle_add_us']
+        self.idle_width = self.byte_width + self.add_idle_width
+
+        # Derive ranges (add tolerance) and scale to sample counts.
+        self.usec_width = self.samplerate / 1e6
+        self.hold_high_width = 9 * self.time_tol_abs * self.usec_width
+
+        def _get_range(width):
+            reladd = self.time_tol_perc / 100
+            absadd = self.time_tol_abs
+            lower = min(width * (1 - reladd), width - absadd)
+            upper = max(width * (1 + reladd), width + absadd)
+            lower = floor(lower * self.usec_width)
+            upper = ceil(upper * self.usec_width)
+            return (lower, upper + 1)
+
+        self.data_bit_1_range = _get_range(self.data_width * 1)
+        self.data_bit_2_range = _get_range(self.data_width * 2)
+        self.data_bit_3_range = _get_range(self.data_width * 3)
+        self.data_bit_4_range = _get_range(self.data_width * 4)
+        self.short_data_range = _get_range(self.data_width / 4)
+        self.pad_bit_range = _get_range(self.pad_width)
+
+        self.data_width *= self.usec_width
+        self.pad_width *= self.usec_width
+        self.byte_width *= self.usec_width
+        self.idle_width *= self.usec_width
+
+        self.lookahead_width = int(4 * self.data_width)
+
+    def span_snum_to_us(self, count):
+        return count / self.usec_width
+
+    def span_is_pad(self, span):
+        return span in range(*self.pad_bit_range)
+
+    def span_is_data(self, span):
+        if span in range(*self.data_bit_1_range):
+            return 1
+        if span in range(*self.data_bit_2_range):
+            return 2
+        if span in range(*self.data_bit_3_range):
+            return 3
+        if span in range(*self.data_bit_4_range):
+            return 4
+        return False
+
+    def span_is_short(self, span):
+        return span in range(*self.short_data_range)
+
+    def wait_until(self, want):
+        '''Wait until a given location, but keep sensing carrier.'''
+
+        # Implementor's note: Avoids skip values below 1. This version
+        # "may overshoot" by one sample. Which should be acceptable for
+        # this specific use case (can put the sample point of a bit time
+        # out of the center by some 4% under worst case conditions).
+
+        want = int(want)
+        while True:
+            diff = max(want - self.samplenum, 1)
+            pins = self.wait([{PIN_DATA: 'e'}, {'skip': diff}])
+            self.carrier_check(pins[PIN_DATA], self.samplenum)
+            if self.samplenum >= want:
+                return pins
+        # UNREACH
+
+    def decode(self):
+        if not self.samplerate or self.samplerate < 1e6:
+            raise SamplerateError('Need a samplerate of at least 1MSa/s')
+
+        # As a special case the first low period in the input capture is
+        # saught regardless of whether we can see its falling edge. This
+        # approach is also used to recover after synchronization was lost.
+        #
+        # The important condition here in the main loop is: Get the next
+        # edge's position, but time out after a maximum period of four
+        # data bits. This allows for the detection of SYNC pulses, also
+        # responds "soon enough" to DATA bits where edges can be few
+        # within a data byte. Also avoids excessive waits for unexpected
+        # communication errors.
+        #
+        # DATA bits within a byte are taken at fixed intervals relative
+        # to the SYNC-PAD's falling edge. It's essential to check the
+        # carrier at every edge, also during DATA bit sampling. Simple
+        # skips to the desired sample point could break that feature.
+        while True:
+
+            # Help kick-start the IDLE condition detection after
+            # decoder state reset.
+            if not self.edges:
+                curr_level, = self.wait({PIN_DATA: 'l'})
+                self.carrier_check(curr_level, self.samplenum)
+                self.edges = [self.samplenum]
+                continue
+
+            # Advance to the next edge, or over a medium span without an
+            # edge. Prepare to classify the distance to derive bit types
+            # from these details.
+            last_snum = self.samplenum
+            curr_level, = self.wait([{PIN_DATA: 'e'}, {'skip': self.lookahead_width}])
+            self.carrier_check(curr_level, self.samplenum)
+            bit_level = curr_level
+            edge_seen = self.matched[0]
+            if edge_seen:
+                bit_level = 1 - bit_level
+            if not self.edges:
+                self.edges = [self.samplenum]
+                continue
+            self.edges.append(self.samplenum)
+            curr_snum = self.samplenum
+
+            # Check bit width (can also be multiple data bits).
+            span = self.edges[-1] - self.edges[-2]
+            is_pad = bit_level and self.span_is_pad(span)
+            is_data = self.span_is_data(span)
+            is_short = bit_level and self.span_is_short(span)
+
+            if is_pad:
+                # BEWARE! Use ss value of last edge (genuinely seen, or
+                # inserted after a DATA byte) for PAD bit annotations.
+                ss, es = self.edges[-2], curr_snum
+                texts = ['PAD', '{:d}'.format(bit_level)]
+                self.putg(ss, es, [ANN_PAD_BIT, texts])
+                self.symbols_append(ss, es, 'PAD_BIT', bit_level)
+                ss, es = self.symbols_get_last()[:2]
+                self.putpy(ss, es, 'PAD_BIT', bit_level)
+                continue
+
+            if is_short:
+                ss, es = last_snum, curr_snum
+                texts = ['SHORT', '{:d}'.format(bit_level)]
+                self.putg(ss, es, [ANN_SHORT_DATA, texts])
+                self.symbols_append(ss, es, 'SHORT_BIT', bit_level)
+                ss, es = self.symbols_get_last()[:2]
+                self.putpy(ss, es, 'SHORT_BIT', bit_level)
+                continue
+
+            # Force IDLE period check when the decoder seeks to sync
+            # to the input data stream.
+            if not bit_level and not self.symbols and self.carrier_want_idle:
+                continue
+
+            # Accept arbitrary length LOW phases after DATA bytes(!) or
+            # SHORT pulses, but not within a DATA byte or SYNC-PAD etc.
+            # This covers the late start of the next SYNC-PAD (byte of
+            # a frame, or ACK byte after a frame, or the start of the
+            # next frame).
+            if not bit_level:
+                if self.symbols_has_prev('DATA_BYTE'):
+                    continue
+                if self.symbols_has_prev('SHORT_BIT'):
+                    continue
+                if self.symbols_has_prev('WAIT_ACK'):
+                    continue
+
+            # Get (consume!) the LOW DATA bit after a PAD.
+            took_low = False
+            if is_data and not bit_level and self.symbols_has_prev('PAD_BIT'):
+                took_low = True
+                is_data -= 1
+                next_snum = int(last_snum + self.data_width)
+                ss, es = last_snum, next_snum
+                texts = ['ZERO', '{:d}'.format(bit_level)]
+                self.putg(ss, es, [ANN_LOW_BIT, texts])
+                self.symbols_append(ss, es, 'ZERO_BIT', bit_level)
+                ss, es = self.symbols_get_last()[:2]
+                self.putpy(ss, es, 'DATA_BIT', bit_level)
+                self.data_fall_time = last_snum
+                last_snum = next_snum
+            # Turn the combination of PAD and LOW DATA into SYNC-PAD.
+            # Start data bit accumulation after a SYNC-PAD was seen.
+            sync_pad_seq = ['PAD_BIT', 'ZERO_BIT']
+            if self.symbols_has_prev(sync_pad_seq):
+                self.symbols_collapse(len(sync_pad_seq), 'SYNC_PAD')
+                ss, es = self.symbols_get_last()[:2]
+                self.putpy(ss, es, 'SYNC_PAD', True)
+                self.data_bits = []
+            # Turn three subsequent SYNC-PAD into FRAME-INIT. Start the
+            # accumulation of frame bytes when FRAME-INIT was seen.
+            frame_init_seq = 3 * ['SYNC_PAD']
+            if self.symbols_has_prev(frame_init_seq):
+                self.symbols_collapse(len(frame_init_seq), 'FRAME_INIT')
+                # Force a flush of the previous frame after we have
+                # reliably detected the start of another one. This is a
+                # workaround for this decoder's inability to detect the
+                # end of a frame after an ACK was seen or byte counts
+                # have been reached. We cannot assume perfect input,
+                # thus we leave all interpretation of frame content to
+                # upper layers. Do keep the recently queued FRAME_INIT
+                # symbol across the flush operation.
+                if len(self.symbols) > 1:
+                    keep = self.symbols.pop(-1)
+                    self.frame_flush()
+                    self.symbols.clear()
+                    self.symbols.append(keep)
+                ss, es = self.symbols_get_last()[:2]
+                texts = ['FRAME INIT', 'INIT', 'I']
+                self.putg(ss, es, [ANN_FRAME_INIT, texts])
+                self.putpy(ss, es, 'FRAME_INIT', True)
+                self.frame_bytes = []
+            # Collapse SYNC-PAD after SHORT+ into a WAIT-ACK. Include
+            # all leading SHORT bits in the WAIT as well.
+            wait_ack_seq = ['SHORT_BIT', 'SYNC_PAD']
+            if self.symbols_has_prev(wait_ack_seq):
+                self.symbols_collapse(len(wait_ack_seq), 'WAIT_ACK',
+                    squeeze = 'SHORT_BIT')
+                ss, es = self.symbols_get_last()[:2]
+                texts = ['WAIT for sync response', 'WAIT response', 'WAIT', 'W']
+                self.putg(ss, es, [ANN_FRAME_WAIT, texts])
+                self.putpy(ss, es, 'SYNC_RESP_WAIT', True)
+            if took_low and not is_data:
+                # Start at the very next edge if we just consumed a LOW
+                # after a PAD bit, and the DATA bit count is exhausted.
+                # This improves robustness, deals with inaccurate edge
+                # positions. (Motivated by real world captures, the spec
+                # would not discuss bit time tolerances.)
+                continue
+
+            # When we get here, the only remaining (the only supported)
+            # activity is the collection of a data byte's DATA bits.
+            # These are not taken by the main loop's "edge search, with
+            # a timeout" approach, which is "too tolerant". Instead all
+            # DATA bits get sampled at a fixed interval and relative to
+            # the SYNC-PAD's falling edge. We expect to have seen the
+            # data byte' SYNC-PAD before. If we haven't, the decoder is
+            # not yet synchronized to the input data.
+            if not is_data:
+                fast_cont = edge_seen and curr_level
+                ss, es = last_snum, curr_snum
+                texts = ['failed pulse length check', 'pulse length', 'length']
+                self.putg(ss, es, [ANN_SYNC_LOSS, texts])
+                self.frame_flush()
+                self.carrier_flush()
+                self.reset_state()
+                if fast_cont:
+                    self.edges = [self.samplenum]
+                continue
+            if not self.symbols_has_prev('SYNC_PAD'):
+                # Fast reponse to the specific combination of: no-sync,
+                # edge seen, and current high level. In this case we
+                # can reset internal state, but also can continue the
+                # interpretation right after the most recently seen
+                # rising edge, which could start the next PAD time.
+                # Otherwise continue slow interpretation after reset.
+                fast_cont = edge_seen and curr_level
+                self.frame_flush()
+                self.carrier_flush()
+                self.reset_state()
+                if fast_cont:
+                    self.edges = [self.samplenum]
+                continue
+
+            # The main loop's "edge search with period timeout" approach
+            # can have provided up to three more DATA bits after the LOW
+            # bit of the SYNC-PAD. Consume them immediately in that case,
+            # otherwise .wait() for their sample point. Stick with float
+            # values for bit sample points and bit time boundaries for
+            # improved accuracy, only round late to integers when needed.
+            bit_field = []
+            bit_ss = self.data_fall_time + self.data_width
+            for bit_idx in range(8):
+                bit_es = bit_ss + self.data_width
+                bit_snum = (bit_es + bit_ss) / 2
+                if bit_snum > self.samplenum:
+                    bit_level, = self.wait_until(bit_snum)
+                ss, es = ceil(bit_ss), floor(bit_es)
+                texts = ['{:d}'.format(bit_level)]
+                self.putg(ss, es, [ANN_DATA_BIT, texts])
+                self.symbols_append(ss, es, 'DATA_BIT', bit_level)
+                ss, es = self.symbols_get_last()[:2]
+                self.putpy(ss, es, 'DATA_BIT', bit_level)
+                bit_field.append(bit_level)
+                if self.data_bits is not None:
+                    self.data_bits.append(bit_level)
+                bit_ss = bit_es
+            end_snum = bit_es
+            curr_level, = self.wait_until(end_snum)
+            curr_snum = self.samplenum
+
+            # We are at the exact _calculated_ boundary of the last DATA
+            # bit time. Improve robustness for those situations where
+            # the transmitter's and the sender's timings differ within a
+            # margin, and the transmitter may hold the last DATA bit's
+            # HIGH level for a little longer.
+            #
+            # When no falling edge is seen within the maximum tolerance
+            # for the last DATA bit, then this could be the combination
+            # of a HIGH DATA bit and a PAD bit without a LOW in between.
+            # Fake an edge in that case, to re-use existing code paths.
+            # Make sure to keep referencing times to the last SYNC pad's
+            # falling edge. This is the last reliable condition we have.
+            if curr_level:
+                hold = self.hold_high_width
+                curr_level, = self.wait([{PIN_DATA: 'l'}, {'skip': int(hold)}])
+                self.carrier_check(curr_level, self.samplenum)
+                if self.matched[1]:
+                    self.edges.append(curr_snum)
+                    curr_level = 1 - curr_level
+                curr_snum = self.samplenum
+
+            # Get the byte value from the bits (when available).
+            # TODO Has the local 'bit_field' become obsolete, or should
+            # self.data_bits go away?
+            data_byte = bitpack(bit_field)
+            if self.data_bits is not None:
+                data_byte = bitpack(self.data_bits)
+                self.data_bits.clear()
+                if self.frame_bytes is not None:
+                    self.frame_bytes.append(data_byte)
+
+            # Turn a sequence of a SYNC-PAD and eight DATA bits into a
+            # DATA-BYTE symbol.
+            byte_seq = ['SYNC_PAD'] + 8 * ['DATA_BIT']
+            if self.symbols_has_prev(byte_seq):
+                self.symbols_collapse(len(byte_seq), 'DATA_BYTE')
+                ss, es = self.symbols_get_last()[:2]
+                texts = ['{:02x}'.format(data_byte)]
+                self.putg(ss, es, [ANN_DATA_BYTE, texts])
+                self.putpy(ss, es, 'DATA_BYTE', data_byte)
+
+            # Optionally terminate the accumulation of a frame when a
+            # WAIT-ACK period was followed by a DATA-BYTE? This could
+            # flush the current packet before the next FRAME-INIT or
+            # IDLE are seen, and increases usability for short input
+            # data (aggressive trimming). It won't help when WAIT is
+            # not seen, though.
+            sync_resp_seq = ['WAIT_ACK'] + ['DATA_BYTE']
+            if self.symbols_has_prev(sync_resp_seq):
+                self.frame_flush()
diff --git a/decoders/pjon/__init__.py b/decoders/pjon/__init__.py
new file mode 100644 (file)
index 0000000..579fb59
--- /dev/null
@@ -0,0 +1,25 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+'''
+This protocol decoder interprets the PJON protocol on top of the PJDL
+link layer (and potentially other link layers).
+'''
+
+from .pd import Decoder
diff --git a/decoders/pjon/pd.py b/decoders/pjon/pd.py
new file mode 100644 (file)
index 0000000..b23cfb8
--- /dev/null
@@ -0,0 +1,603 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+# See the https://www.pjon.org/ PJON project page and especially the
+# https://www.pjon.org/PJON-protocol-specification-v3.2.php protocol
+# specification, which can use different link layers.
+
+# TODO
+# - Check for the correct order of optional fields (the spec is not as
+#   explicit on these details as I'd expect).
+# - Check decoder's robustness, completeness, and correctness when more
+#   captures become available. Currently there are only few, which only
+#   cover minimal communication, and none of the protocol's flexibility.
+#   The decoder was essentially written based on the available docs, and
+#   then took some arbitrary choices and liberties to cope with real life
+#   data from an example setup. Strictly speaking this decoder violates
+#   the spec, and errs towards the usability side.
+
+import sigrokdecode as srd
+import struct
+
+ANN_RX_INFO, ANN_HDR_CFG, ANN_PKT_LEN, ANN_META_CRC, ANN_TX_INFO, \
+ANN_SVC_ID, ANN_PKT_ID, ANN_ANON_DATA, ANN_PAYLOAD, ANN_END_CRC, \
+ANN_SYN_RSP, \
+ANN_RELATION, \
+ANN_WARN, \
+    = range(13)
+
+def calc_crc8(data):
+    crc = 0
+    for b in data:
+        crc ^= b
+        for i in range(8):
+            odd = crc % 2
+            crc >>= 1
+            if odd:
+                crc ^= 0x97
+    return crc
+
+def calc_crc32(data):
+    crc = 0xffffffff
+    for b in data:
+        crc ^= b
+        for i in range(8):
+            odd = crc % 2
+            crc >>= 1
+            if odd:
+                crc ^= 0xedb88320
+    crc ^= 0xffffffff
+    return crc
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'pjon'
+    name = 'PJON'
+    longname = 'PJON'
+    desc = 'The PJON protocol.'
+    license = 'gplv2+'
+    inputs = ['pjon_link']
+    outputs = []
+    tags = ['Embedded/industrial']
+    annotations = (
+        ('rx_info', 'Receiver ID'),
+        ('hdr_cfg', 'Header config'),
+        ('pkt_len', 'Packet length'),
+        ('meta_crc', 'Meta CRC'),
+        ('tx_info', 'Sender ID'),
+        ('port', 'Service ID'),
+        ('pkt_id', 'Packet ID'),
+        ('anon', 'Anonymous data'),
+        ('payload', 'Payload'),
+        ('end_crc', 'End CRC'),
+        ('syn_rsp', 'Sync response'),
+        ('relation', 'Relation'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('fields', 'Fields', (
+            ANN_RX_INFO, ANN_HDR_CFG, ANN_PKT_LEN, ANN_META_CRC, ANN_TX_INFO,
+            ANN_SVC_ID, ANN_ANON_DATA, ANN_PAYLOAD, ANN_END_CRC, ANN_SYN_RSP,
+        )),
+        ('relations', 'Relations', (ANN_RELATION,)),
+        ('warnings', 'Warnings', (ANN_WARN,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.reset_frame()
+
+    def reset_frame(self):
+        self.frame_ss = None
+        self.frame_es = None
+        self.frame_rx_id = None
+        self.frame_tx_id = None
+        self.frame_payload_text = None
+        self.frame_bytes = None
+        self.frame_has_ack = None
+        self.ack_bytes = None
+        self.ann_ss = None
+        self.ann_es = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def putg(self, ss, es, ann, data):
+        self.put(ss, es, self.out_ann, [ann, data])
+
+    def frame_flush(self):
+        if not self.frame_bytes:
+            return
+        if not self.frame_ss or not self.frame_es:
+            return
+
+        # Emit "communication relation" details.
+        # TODO Include the service ID (port number) as well?
+        text = []
+        if self.frame_rx_id is not None:
+            text.append("RX {}".format(self.frame_rx_id[-1]))
+        if self.frame_tx_id is not None:
+            text.append("TX {}".format(self.frame_tx_id[-1]))
+        if self.frame_payload_text is not None:
+            text.append("DATA {}".format(self.frame_payload_text))
+        if self.frame_has_ack is not None:
+            text.append("ACK {:02x}".format(self.frame_has_ack))
+        if text:
+            text = " - ".join(text)
+            self.putg(self.frame_ss, self.frame_es, ANN_RELATION, [text])
+
+    def handle_field_get_desc(self, idx = None):
+        '''Lookup description of a PJON frame field.'''
+        if not self.field_desc:
+            return None
+        if idx is None:
+            idx = self.field_desc_idx
+        if idx >= 0 and idx >= len(self.field_desc):
+            return None
+        if idx < 0 and abs(idx) > len(self.field_desc):
+            return None
+        desc = self.field_desc[idx]
+        return desc
+
+    def handle_field_add_desc(self, fmt, hdl, cls = None):
+        '''Register description for a PJON frame field.'''
+        item = {
+            'format': fmt,
+            'width': struct.calcsize(fmt),
+            'handler': hdl,
+            'anncls': cls,
+        }
+        self.field_desc.append(item)
+
+    def handle_field_seed_desc(self):
+        '''Seed list of PJON frame fields' descriptions.'''
+
+        # At the start of a PJON frame, the layout of only two fields
+        # is known. Subsequent fields (their presence, and width) depend
+        # on the content of the header config field.
+
+        self.field_desc = []
+        self.handle_field_add_desc('<B', self.handle_field_rx_id, ANN_RX_INFO)
+        self.handle_field_add_desc('<B', self.handle_field_config, ANN_HDR_CFG)
+
+        self.field_desc_idx = 0
+        self.field_desc_got = 0
+
+        self.frame_ss = None
+        self.frame_es = None
+        self.frame_rx_id = None
+        self.frame_is_broadcast = None
+        self.frame_tx_id = None
+        self.frame_payload = None
+        self.frame_payload_text = None
+        self.frame_has_ack = None
+
+    def handle_field_rx_id(self, b):
+        '''Process receiver ID field of a PJON frame.'''
+
+        b = b[0]
+
+        # Provide text presentation, caller emits frame field annotation.
+        if b == 255: # "not assigned"
+            id_txt = 'NA'
+        elif b == 0: # "broadcast"
+            id_txt = 'BC'
+        else: # unicast
+            id_txt = '{:d}'.format(b)
+        texts = [
+            'RX_ID {}'.format(id_txt),
+            '{}'.format(id_txt),
+        ]
+
+        # Track RX info for communication relation emission.
+        self.frame_rx_id = (b, id_txt)
+        self.frame_is_broadcast = b == 0
+
+        return texts
+
+    def handle_field_config(self, b):
+        '''Process header config field of a PJON frame.'''
+
+        # Caller provides a list of values. We want a single scalar.
+        b = b[0]
+
+        # Get the config flags.
+        self.cfg_shared = b & (1 << 0)
+        self.cfg_tx_info = b & (1 << 1)
+        self.cfg_sync_ack = b & (1 << 2)
+        self.cfg_async_ack = b & (1 << 3)
+        self.cfg_port = b & (1 << 4)
+        self.cfg_crc32 = b & (1 << 5)
+        self.cfg_len16 = b & (1 << 6)
+        self.cfg_pkt_id = b & (1 << 7)
+
+        # Get a textual presentation of the flags.
+        text = []
+        text.append('pkt_id' if self.cfg_pkt_id else '-') # packet number
+        text.append('len16' if self.cfg_len16 else '-') # 16bit length not 8bit
+        text.append('crc32' if self.cfg_crc32 else '-') # 32bit CRC not 8bit
+        text.append('svc_id' if self.cfg_port else '-') # port aka service ID
+        text.append('ack_mode' if self.cfg_async_ack else '-') # async response
+        text.append('ack' if self.cfg_sync_ack else '-') # synchronous response
+        text.append('tx_info' if self.cfg_tx_info else '-') # sender address
+        text.append('bus_id' if self.cfg_shared else '-') # "shared" vs "local"
+        text = ' '.join(text)
+        bits = '{:08b}'.format(b)
+        texts = [
+            'CFG {:s}'.format(text),
+            'CFG {}'.format(bits),
+            bits
+        ]
+
+        # TODO Come up with the most appropriate phrases for this logic.
+        # Are separate instruction groups with repeated conditions more
+        # readable than one common block which registers fields _and_
+        # updates the overhead size? Or is the latter preferrable due to
+        # easier maintenance and less potential for inconsistency?
+
+        # Get the size of variable width fields, to calculate the size
+        # of the packet overhead (the part that is not the payload data).
+        # This lets us derive the payload length when we later receive
+        # the frame's total length.
+        u8_fmt = '>B'
+        u16_fmt = '>H'
+        u32_fmt = '>L'
+        len_fmt = u16_fmt if self.cfg_len16 else u8_fmt
+        bus_fmt = '>4B'
+        crc_fmt = u32_fmt if self.cfg_crc32 else u8_fmt
+        self.cfg_overhead = 0
+        self.cfg_overhead += struct.calcsize(u8_fmt) # receiver ID
+        self.cfg_overhead += struct.calcsize(u8_fmt) # header config
+        self.cfg_overhead += struct.calcsize(len_fmt) # packet length
+        self.cfg_overhead += struct.calcsize(u8_fmt) # initial CRC, always CRC8
+        # TODO Check for completeness and correctness.
+        if self.cfg_shared:
+            self.cfg_overhead += struct.calcsize(u32_fmt) # receiver bus
+        if self.cfg_tx_info:
+            if self.cfg_shared:
+                self.cfg_overhead += struct.calcsize(u32_fmt) # sender bus
+            self.cfg_overhead += struct.calcsize(u8_fmt) # sender ID
+        if self.cfg_port:
+            self.cfg_overhead += struct.calcsize(u16_fmt) # service ID
+        if self.cfg_pkt_id:
+            self.cfg_overhead += struct.calcsize(u16_fmt) # packet ID
+        self.cfg_overhead += struct.calcsize(crc_fmt) # end CRC
+
+        # Register more frame fields as we learn about their presence and
+        # format. Up to this point only receiver ID and header config were
+        # registered since their layout is fixed.
+        #
+        # Packet length and meta CRC are always present but can be of
+        # variable width. Optional fields follow the meta CRC and preceed
+        # the payload bytes. Notice that payload length isn't known here
+        # either, though its position is known already. The packet length
+        # is yet to get received. Subtracting the packet overhead from it
+        # (which depends on the header configuration) will provide that
+        # information.
+        #
+        # TODO Check for completeness and correctness.
+        # TODO Optionally fold overhead size arith and field registration
+        # into one block of instructions, to reduce the redundancy in the
+        # condition checks, and raise awareness for incomplete sequences
+        # during maintenance.
+        self.handle_field_add_desc(len_fmt, self.handle_field_pkt_len, ANN_PKT_LEN)
+        self.handle_field_add_desc(u8_fmt, self.handle_field_meta_crc, ANN_META_CRC)
+        if self.cfg_shared:
+            self.handle_field_add_desc(bus_fmt, self.handle_field_rx_bus, ANN_ANON_DATA)
+        if self.cfg_tx_info:
+            if self.cfg_shared:
+                self.handle_field_add_desc(bus_fmt, self.handle_field_tx_bus, ANN_ANON_DATA)
+            self.handle_field_add_desc(u8_fmt, self.handle_field_tx_id, ANN_ANON_DATA)
+        if self.cfg_port:
+            self.handle_field_add_desc(u16_fmt, ['PORT {:d}', '{:d}'], ANN_ANON_DATA)
+        if self.cfg_pkt_id:
+            self.handle_field_add_desc(u16_fmt, ['PKT {:04x}', '{:04x}'], ANN_ANON_DATA)
+        pl_fmt = '>{:d}B'.format(0)
+        self.handle_field_add_desc(pl_fmt, self.handle_field_payload, ANN_PAYLOAD)
+        self.handle_field_add_desc(crc_fmt, self.handle_field_end_crc, ANN_END_CRC)
+
+        # Emit warning annotations for invalid flag combinations.
+        warn_texts = []
+        wants_ack = self.cfg_sync_ack or self.cfg_async_ack
+        if wants_ack and not self.cfg_tx_info:
+            warn_texts.append('ACK request without TX info')
+        if wants_ack and self.frame_is_broadcast:
+            warn_texts.append('ACK request for broadcast')
+        if self.cfg_sync_ack and self.cfg_async_ack:
+            warn_texts.append('sync and async ACK request')
+        if self.cfg_len16 and not self.cfg_crc32:
+            warn_texts.append('extended length needs CRC32')
+        if warn_texts:
+            warn_texts = ', '.join(warn_texts)
+            self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+
+        # Have the caller emit the annotation for configuration data.
+        return texts
+
+    def handle_field_pkt_len(self, b):
+        '''Process packet length field of a PJON frame.'''
+
+        # Caller provides a list of values. We want a single scalar.
+        b = b[0]
+
+        # The wire communicates the total packet length. Some of it is
+        # overhead (non-payload data), while its volume is variable in
+        # size (depends on the header configuration).
+        #
+        # Derive the payload size from previously observed flags. Update
+        # the previously registered field description (the second last
+        # item in the list, before the end CRC).
+
+        pkt_len = b
+        pl_len = b - self.cfg_overhead
+        warn_texts = []
+        if pkt_len not in range(self.cfg_overhead, 65536):
+            warn_texts.append('suspicious packet length')
+        if pkt_len > 15 and not self.cfg_crc32:
+            warn_texts.append('length above 15 needs CRC32')
+        if pl_len < 1:
+            warn_texts.append('suspicious payload length')
+            pl_len = 0
+        if warn_texts:
+            warn_texts = ', '.join(warn_texts)
+            self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+        pl_fmt = '>{:d}B'.format(pl_len)
+
+        desc = self.handle_field_get_desc(-2)
+        desc['format'] = pl_fmt
+        desc['width'] = struct.calcsize(pl_fmt)
+
+        # Have the caller emit the annotation for the packet length.
+        # Provide information of different detail level for zooming.
+        texts = [
+            'LENGTH {:d} (PAYLOAD {:d})'.format(pkt_len, pl_len),
+            'LEN {:d} (PL {:d})'.format(pkt_len, pl_len),
+            '{:d} ({:d})'.format(pkt_len, pl_len),
+            '{:d}'.format(pkt_len),
+        ]
+        return texts
+
+    def handle_field_common_crc(self, have, is_meta):
+        '''Process a CRC field of a PJON frame.'''
+
+        # CRC algorithm and width are configurable, and can differ
+        # across meta and end checksums in a frame's fields.
+        caption = 'META' if is_meta else 'END'
+        crc_len = 8 if is_meta else 32 if self.cfg_crc32 else 8
+        crc_bytes = crc_len // 8
+        crc_fmt = '{:08x}' if crc_len == 32 else '{:02x}'
+        have_text = crc_fmt.format(have)
+
+        # Check received against expected checksum. Emit warnings.
+        warn_texts = []
+        data = self.frame_bytes[:-crc_bytes]
+        want = calc_crc32(data) if crc_len == 32 else calc_crc8(data)
+        if want != have:
+            want_text = crc_fmt.format(want)
+            warn_texts.append('CRC mismatch - want {} have {}'.format(want_text, have_text))
+        if warn_texts:
+            warn_texts = ', '.join(warn_texts)
+            self.putg(self.ann_ss, self.ann_es, ANN_WARN, [warn_texts])
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            '{}_CRC {}'.format(caption, have_text),
+            'CRC {}'.format(have_text),
+            have_text,
+        ]
+        return texts
+
+    def handle_field_meta_crc(self, b):
+        '''Process initial CRC (meta) field of a PJON frame.'''
+        # Caller provides a list of values. We want a single scalar.
+        b = b[0]
+        return self.handle_field_common_crc(b, True)
+
+    def handle_field_end_crc(self, b):
+        '''Process end CRC (total frame) field of a PJON frame.'''
+        # Caller provides a list of values. We want a single scalar.
+        b = b[0]
+        return self.handle_field_common_crc(b, False)
+
+    def handle_field_common_bus(self, b):
+        '''Common handling of bus ID details. Used for RX and TX.'''
+        bus_id = b[:4]
+        bus_num = struct.unpack('>L', bytearray(bus_id))
+        bus_txt = '.'.join(['{:d}'.format(b) for b in bus_id])
+        return bus_num, bus_txt
+
+    def handle_field_rx_bus(self, b):
+        '''Process receiver bus ID field of a PJON frame.'''
+
+        # When we get here, there always should be an RX ID already.
+        bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+        rx_txt = "{} {}".format(bus_txt, self.frame_rx_id[-1])
+        self.frame_rx_id = (bus_num, self.frame_rx_id[0], rx_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'RX_BUS {}'.format(bus_txt),
+            bus_txt,
+        ]
+        return texts
+
+    def handle_field_tx_bus(self, b):
+        '''Process transmitter bus ID field of a PJON frame.'''
+
+        # The TX ID field is optional, as is the use of bus ID fields.
+        # In the TX info case the TX bus ID is seen before the TX ID.
+        bus_num, bus_txt = self.handle_field_common_bus(b[:4])
+        self.frame_tx_id = (bus_num, None, bus_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'TX_BUS {}'.format(bus_txt),
+            bus_txt,
+        ]
+        return texts
+
+    def handle_field_tx_id(self, b):
+        '''Process transmitter ID field of a PJON frame.'''
+
+        b = b[0]
+
+        id_txt = "{:d}".format(b)
+        if self.frame_tx_id is None:
+            self.frame_tx_id = (b, id_txt)
+        else:
+            tx_txt = "{} {}".format(self.frame_tx_id[-1], id_txt)
+            self.frame_tx_id = (self.frame_tx_id[0], b, tx_txt)
+
+        # Provide text representation for frame field, caller emits
+        # the annotation.
+        texts = [
+            'TX_ID {}'.format(id_txt),
+            id_txt,
+        ]
+        return texts
+
+    def handle_field_payload(self, b):
+        '''Process payload data field of a PJON frame.'''
+
+        text = ' '.join(['{:02x}'.format(v) for v in b])
+        self.frame_payload = b[:]
+        self.frame_payload_text = text
+
+        texts = [
+            'PAYLOAD {}'.format(text),
+            text,
+        ]
+        return texts
+
+    def handle_field_sync_resp(self, b):
+        '''Process synchronous response for a PJON frame.'''
+
+        self.frame_has_ack = b
+
+        texts = [
+            'ACK {:02x}'.format(b),
+            '{:02x}'.format(b),
+        ]
+        return texts
+
+    def decode(self, ss, es, data):
+        ptype, pdata = data
+
+        # Start frame bytes accumulation when FRAME_INIT is seen. Flush
+        # previously accumulated frame bytes when a new frame starts.
+        if ptype == 'FRAME_INIT':
+            self.frame_flush()
+            self.reset_frame()
+            self.frame_bytes = []
+            self.handle_field_seed_desc()
+            self.frame_ss = ss
+            self.frame_es = es
+            return
+
+        # Use IDLE as another (earlier) trigger to flush frames. Also
+        # trigger flushes on FRAME-DATA which mean that the link layer
+        # inspection has seen the end of a protocol frame.
+        #
+        # TODO Improve usability? Emit warnings for PJON frames where
+        # FRAME_DATA was seen but FRAME_INIT wasn't? So that users can
+        # become aware of broken frames.
+        if ptype in ('IDLE', 'FRAME_DATA'):
+            self.frame_flush()
+            self.reset_frame()
+            return
+
+        # Switch from data bytes to response bytes when WAIT is seen.
+        if ptype == 'SYNC_RESP_WAIT':
+            self.ack_bytes = []
+            self.ann_ss, self.ann_es = None, None
+            return
+
+        # Accumulate data bytes as they arrive. Put them in the bucket
+        # which corresponds to its most recently seen leader.
+        if ptype == 'DATA_BYTE':
+            b = pdata
+            self.frame_es = es
+
+            # Are we collecting response bytes (ACK)?
+            if self.ack_bytes is not None:
+                if not self.ann_ss:
+                    self.ann_ss = ss
+                self.ack_bytes.append(b)
+                self.ann_es = es
+                text = self.handle_field_sync_resp(b)
+                if text:
+                    self.putg(self.ann_ss, self.ann_es, ANN_SYN_RSP, text)
+                self.ann_ss, self.ann_es = None, None
+                return
+
+            # Are we collecting frame content?
+            if self.frame_bytes is not None:
+                if not self.ann_ss:
+                    self.ann_ss = ss
+                self.frame_bytes.append(b)
+                self.ann_es = es
+
+                # Has the field value become available yet?
+                desc = self.handle_field_get_desc()
+                if not desc:
+                    return
+                width = desc.get('width', None)
+                if not width:
+                    return
+                self.field_desc_got += 1
+                if self.field_desc_got != width:
+                    return
+
+                # Grab most recent received field as a byte array. Get
+                # the values that it contains.
+                fmt = desc.get('format', '>B')
+                raw = bytearray(self.frame_bytes[-width:])
+                values = struct.unpack(fmt, raw)
+
+                # Process the value, and get its presentation. Can be
+                # mere formatting, or serious execution of logic.
+                hdl = desc.get('handler', '{!r}')
+                if isinstance(hdl, str):
+                    text = [hdl.format(*values)]
+                elif isinstance(hdl, (list, tuple)):
+                    text = [f.format(*values) for f in hdl]
+                elif hdl:
+                    text = hdl(values)
+                cls = desc.get('anncls', ANN_ANON_DATA)
+
+                # Emit annotation unless the handler routine already did.
+                if cls is not None and text:
+                    self.putg(self.ann_ss, self.ann_es, cls, text)
+                self.ann_ss, self.ann_es = None, None
+
+                # Advance scan position for to-get-received field.
+                self.field_desc_idx += 1
+                self.field_desc_got = 0
+                return
+
+            # Unknown phase, not collecting. Not synced yet to the input?
+            return
+
+        # Unknown or unhandled kind of link layer output.
+        return
index db78dc1e5ea11192a6d3b7c3b655d18cad58864d..7cc7ab59e5721c6b73e6bedb89ac9123f8b9293d 100644 (file)
@@ -26,11 +26,15 @@ pressed and address settings into a series of pulses which is then
 transmitted over whatever frequency and modulation that the designer
 chooses. These devices operate at a number of frequencies including 433MHz.
 
-This PD should also decode the HX2262 and SC5262 which are equivalents.
+This PD should also decode the HX2262 and SC5262 which are equivalents, as
+well as the 2272 variants of these ICs. Support for the EV1527, RT1527, FP1527
+and HS1527 is also present.
 
-The decoder also contains some additional decoding for a Maplin L95AR
-remote control and will turn the received signal into which button was
-pressed and what the address code DIP switches are set to.
+The decoder can additionaly decoding the Maplin L95AR remote control and will
+turn the received signal into which button was pressed and what the address
+code DIP switches are set to.
+Please contact the sigrok team if you want decoding for further remote
+controls to be added.
 '''
 
 from .pd import Decoder
index 0d7cc8c55d6a55047c14fbbe821324872496e5f2..31727bdb1aac4abd6d985e2b5a4ff50cbc1c641e 100644 (file)
@@ -21,43 +21,63 @@ import sigrokdecode as srd
 
 bitvals = ('0', '1', 'f', 'U')
 
-def decode_bit(edges):
-    # Datasheet says long pulse is 3 times short pulse.
-    lmin = 2 # long min multiplier
-    lmax = 5 # long max multiplier
-    eqmin = 0.5 # equal min multiplier
-    eqmax = 1.5 # equal max multiplier
-    if ( # 0 -___-___
-        (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
-        (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
-        (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
-        return '0'
-    elif ( # 1 ---_---_
-        (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
-        (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
-        (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
-        return '1'
-    elif ( # float ---_-___
-        (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
-        (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
-        (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
-        return 'f'
-    else:
-        return 'U'
-
-def pinlabels(bit_count):
-    if bit_count <= 6:
-        return 'A%i' % (bit_count - 1)
-    else:
-        return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+def decode_bit(edges, pulses_per_bit):
+    if pulses_per_bit == 2:
+        # Datasheet says long pulse is 3 times short pulse.
+        lmin = 1.5 # long min multiplier
+        lmax = 5 # long max multiplier
+        if (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax): # 0 -___
+            return '0'
+        elif (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax): # 1 ---_
+             return '1'
+        # No float type for this line encoding
+        else:
+            return 'U'
+
+    if pulses_per_bit == 4:
+        # Datasheet says long pulse is 3 times short pulse.
+        lmin = 2 # long min multiplier
+        lmax = 5 # long max multiplier
+        eqmin = 0.5 # equal min multiplier
+        eqmax = 1.5 # equal max multiplier
+        if ( # 0 -___-___
+            (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+            (edges[2] >= edges[0] * eqmin and edges[2] <= edges[0] * eqmax) and
+            (edges[3] >= edges[0] * lmin and edges[3] <= edges[0] * lmax)):
+            return '0'
+        elif ( # 1 ---_---_
+            (edges[0] >= edges[1] * lmin and edges[0] <= edges[1] * lmax) and
+            (edges[0] >= edges[2] * eqmin and edges[0] <= edges[2] * eqmax) and
+            (edges[0] >= edges[3] * lmin and edges[0] <= edges[3] * lmax)):
+             return '1'
+        elif ( # float ---_-___
+             (edges[1] >= edges[0] * lmin and edges[1] <= edges[0] * lmax) and
+            (edges[2] >= edges[0] * lmin and edges[2] <= edges[0]* lmax) and
+            (edges[3] >= edges[0] * eqmin and edges[3] <= edges[0] * eqmax)):
+            return 'f'
+        else:
+            return 'U'
+
+def pinlabels(bit_count, packet_bit_count):
+    if packet_bit_count == 12:
+        if bit_count <= 6:
+            return 'A%i' % (bit_count - 1)
+        else:
+            return 'A%i/D%i' % (bit_count - 1, 12 - bit_count)
+
+    if packet_bit_count == 24:
+        if bit_count <= 20:
+            return 'A%i' % (bit_count - 1)
+        else:
+            return 'D%i' % (bit_count - 21)
 
 def decode_model(model, bits):
     if model == 'maplin_l95ar':
-        address = 'Addr' # Address pins A0 to A5
+        address = 'Addr' # Address bits A0 to A5
         for i in range(0, 6):
             address += ' %i:' % (i + 1) + ('on' if bits[i][0] == '0' else 'off')
         button = 'Button'
-        # Button pins A6/D5 to A11/D0
+        # Button bits A6/D5 to A11/D0
         if bits[6][0] == '0' and bits[11][0] == '0':
             button += ' A ON/OFF'
         elif bits[7][0] == '0' and bits[11][0] == '0':
@@ -68,15 +88,36 @@ def decode_model(model, bits):
             button += ' D ON/OFF'
         else:
             button += ' Unknown'
-        return ['%s' % address, bits[0][1], bits[5][2], \
-                '%s' % button, bits[6][1], bits[11][2]]
+        return [address, bits[0][1], bits[5][2], \
+                button, bits[6][1], bits[11][2]]
+
+    if model == 'xx1527':
+        addr = 0
+        addr_valid = 1
+        for i in range(0, 20):
+            if bits[i][0] != 'U':
+                addr += int(bits[i][0]) * 2 ** i
+            else:
+                addr_valid = 0
+
+        if addr_valid == 1:
+            address = 'Address 0x%X %X %X' % (addr & 0xFF, (addr >> 8) & 0xFF, addr >> 16)
+        else:
+            address = 'Invalid address as not all bits are 0 or 1'
+
+        output  = ' K0 = ' + bits[20][0] + ','
+        output += ' K1 = ' + bits[21][0] + ','
+        output += ' K2 = ' + bits[22][0] + ','
+        output += ' K3 = ' + bits[23][0]
+        return [address, bits[0][1], bits[19][2], \
+                output, bits[20][1], bits[23][2]]
 
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'rc_encode'
     name = 'RC encode'
     longname = 'Remote control encoder'
-    desc = 'PT2262/HX2262/SC5262 remote control encoder protocol.'
+    desc = 'PT22x2/HX22x2/SC52x2 and xx1527 remote control encoder protocol.'
     license = 'gplv2+'
     inputs = ['logic']
     outputs = []
@@ -100,8 +141,8 @@ class Decoder(srd.Decoder):
         ('code-words', 'Code words', (6, 7)),
     )
     options = (
-        {'id': 'remote', 'desc': 'Remote', 'default': 'none', 
-            'values': ('none', 'maplin_l95ar')},
+        {'id': 'linecoding', 'desc': 'Encoding', 'default': 'SC52x2/HX22x2', 'values': ('SC52x2/HX22x2', 'xx1527')},
+        {'id': 'remote', 'desc': 'Remote', 'default': 'none', 'values': ('none', 'maplin_l95ar')},
     )
 
     def __init__(self):
@@ -120,6 +161,13 @@ class Decoder(srd.Decoder):
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
         self.model = self.options['remote']
+        if self.options['linecoding'] == 'xx1527':
+            self.pulses_per_bit = 2
+            self.packet_bits = 24
+            self.model = 'xx1527'
+        else:
+            self.pulses_per_bit = 4 # Each bit is repeated
+            self.packet_bits = 12
 
     def putx(self, data):
         self.put(self.ss, self.es, self.out_ann, data)
@@ -134,20 +182,20 @@ class Decoder(srd.Decoder):
                 self.ss = self.samplenum
                 continue
 
-            if self.bit_count < 12: # Decode A0 to A11.
+            if self.bit_count < self.packet_bits: # Decode A0 to A11 / A23.
                 self.bit_count += 1
-                for i in range(0, 4): # Get four pulses for each bit.
+                for i in range(0, self.pulses_per_bit):
                     if i > 0:
-                        pin = self.wait({0: 'e'}) # Get next 3 edges.
+                        pin = self.wait({0: 'e'}) # Get next edges if we need more.
                     samples = self.samplenum - self.samplenumber_last
                     self.pulses.append(samples) # Save the pulse width.
                     self.samplenumber_last = self.samplenum
                 self.es = self.samplenum
-                self.bits.append([decode_bit(self.pulses), self.ss,
+                self.bits.append([decode_bit(self.pulses, self.pulses_per_bit), self.ss,
                                   self.es]) # Save states and times.
-                idx = bitvals.index(decode_bit(self.pulses))
-                self.putx([idx, [decode_bit(self.pulses)]]) # Write decoded bit.
-                self.putx([5, [pinlabels(self.bit_count)]]) # Write pin labels.
+                idx = bitvals.index(decode_bit(self.pulses, self.pulses_per_bit))
+                self.putx([idx, [decode_bit(self.pulses, self.pulses_per_bit)]]) # Write decoded bit.
+                self.putx([5, [pinlabels(self.bit_count, self.packet_bits)]]) # Write pin labels.
                 self.pulses = []
                 self.ss = self.samplenum
             else:
index 82877b393c06627273afa992e6cbb50ccfacd484..899a64a6ba621892958ce93f9d3e6417e92923d6 100644 (file)
@@ -19,6 +19,8 @@
 
 import sigrokdecode as srd
 
+( ANN_RGB, ) = range(1)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'rgb_led_spi'
@@ -37,34 +39,34 @@ class Decoder(srd.Decoder):
         self.reset()
 
     def reset(self):
-        self.ss_cmd, self.es_cmd = 0, 0
+        self.ss_cmd = None
         self.mosi_bytes = []
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
 
-    def putx(self, data):
-        self.put(self.ss_cmd, self.es_cmd, self.out_ann, data)
+    def putg(self, ss, es, cls, text):
+        self.put(ss, es, self.out_ann, [cls, text])
 
     def decode(self, ss, es, data):
-        ptype, mosi, miso = data
+        ptype = data[0]
 
-        # Only care about data packets.
+        # Grab the payload of three DATA packets. These hold the
+        # RGB values (in this very order).
         if ptype != 'DATA':
             return
-        self.ss, self.es = ss, es
-
-        if len(self.mosi_bytes) == 0:
+        _, mosi, _ = data
+        if not self.mosi_bytes:
             self.ss_cmd = ss
         self.mosi_bytes.append(mosi)
-
-        # RGB value == 3 bytes
-        if len(self.mosi_bytes) != 3:
+        if len(self.mosi_bytes) < 3:
             return
 
-        red, green, blue = self.mosi_bytes
+        # Emit annotations. Invalidate accumulated details as soon as
+        # they were processed, to prepare the next iteration.
+        ss_cmd, es_cmd = self.ss_cmd, es
+        self.ss_cmd = None
+        red, green, blue = self.mosi_bytes[:3]
+        self.mosi_bytes.clear()
         rgb_value = int(red) << 16 | int(green) << 8 | int(blue)
-
-        self.es_cmd = es
-        self.putx([0, ['#%.6x' % rgb_value]])
-        self.mosi_bytes = []
+        self.putg(ss_cmd, es_cmd, ANN_RGB, ['#{:06x}'.format(rgb_value)])
index bf181b6614143b308729ee0c043ffbf1f5b6a699..099a2ce855a2681c35915821e390191e50ecde38 100644 (file)
 ## along with this program; if not, see <http://www.gnu.org/licenses/>.
 ##
 
+# Implementor's notes on the wire format:
+# - World Semi vendor, (Adafruit copy of the) datasheet
+#   https://cdn-shop.adafruit.com/datasheets/WS2812.pdf
+# - reset pulse is 50us (or more) of low pin level
+# - 24bits per WS281x item, 3x 8bits, MSB first, GRB sequence,
+#   cascaded WS281x items, all "excess bits" are passed through
+# - bit time starts with high period, continues with low period,
+#   high to low periods' ratio determines bit value, datasheet
+#   mentions 0.35us/0.8us for value 0, 0.7us/0.6us for value 1
+#   (huge 150ns tolerances, un-even 0/1 value length, hmm)
+# - experience suggests the timing "is variable", rough estimation
+#   often is good enough, microcontroller firmware got away with
+#   four quanta per bit time, or even with three quanta (30%/60%),
+#   Adafruit learn article suggests 1.2us total and 0.4/0.8 or
+#   0.8/0.4 high/low parts, four quanta are easier to handle when
+#   the bit stream is sent via SPI to avoid MCU bit banging and its
+#   inaccurate timing (when interrupts are used in the firmware)
+# - RGBW datasheet (Adafruit copy) for SK6812
+#   https://cdn-shop.adafruit.com/product-files/2757/p2757_SK6812RGBW_REV01.pdf
+#   also 1.2us total, shared across 0.3/0.9 for 0, 0.6/0.6 for 1,
+#   80us reset pulse, R8/G8/B8/W8 format per 32bits
+# - WS2815, RGB LED, uses GRB wire format, 280us RESET pulse width
+# - more vendors and models available and in popular use,
+#   suggests "one third" or "two thirds" ratio would be most robust,
+#   sample "a little before" the bit half? reset pulse width may need
+#   to become an option? matrices and/or fast refresh environments
+#   may want to experiment with back to back pixel streams
+
 import sigrokdecode as srd
-from functools import reduce
+from common.srdhelper import bitpack_msb
 
 class SamplerateError(Exception):
     pass
 
+class DecoderError(Exception):
+    pass
+
+(
+    ANN_BIT, ANN_RESET, ANN_RGB,
+    ANN_COMP_R, ANN_COMP_G, ANN_COMP_B, ANN_COMP_W,
+) = range(7)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'rgb_led_ws281x'
@@ -40,10 +76,22 @@ class Decoder(srd.Decoder):
         ('bit', 'Bit'),
         ('reset', 'RESET'),
         ('rgb', 'RGB'),
+        ('r', 'R'),
+        ('g', 'G'),
+        ('b', 'B'),
+        ('w', 'W'),
     )
     annotation_rows = (
-        ('bits', 'Bits', (0, 1)),
-        ('rgb-vals', 'RGB values', (2,)),
+        ('bits', 'Bits', (ANN_BIT, ANN_RESET,)),
+        ('rgb-comps', 'RGB components', (ANN_COMP_R, ANN_COMP_G, ANN_COMP_B, ANN_COMP_W,)),
+        ('rgb-vals', 'RGB values', (ANN_RGB,)),
+    )
+    options = (
+        {'id': 'wireorder', 'desc': 'colour components order (wire)',
+         'default': 'GRB',
+         'values': ('BGR', 'BRG', 'GBR', 'GRB', 'RBG', 'RGB', 'RWBG', 'RGBW')},
+        {'id': 'textorder', 'desc': 'components output order (text)',
+         'default': 'RGB[W]', 'values': ('wire', 'RGB[W]', 'RGB', 'RGBW', 'RGWB')},
     )
 
     def __init__(self):
@@ -51,12 +99,7 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.oldpin = None
-        self.ss_packet = None
-        self.ss = None
-        self.es = None
         self.bits = []
-        self.inreset = False
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
@@ -65,71 +108,160 @@ class Decoder(srd.Decoder):
         if key == srd.SRD_CONF_SAMPLERATE:
             self.samplerate = value
 
-    def handle_bits(self, samplenum):
-        if len(self.bits) == 24:
-            grb = reduce(lambda a, b: (a << 1) | b, self.bits)
-            rgb = (grb & 0xff0000) >> 8 | (grb & 0x00ff00) << 8 | (grb & 0x0000ff)
-            self.put(self.ss_packet, samplenum, self.out_ann,
-                     [2, ['#%06x' % rgb]])
-            self.bits = []
-            self.ss_packet = None
+    def putg(self, ss, es, cls, text):
+        self.put(ss, es, self.out_ann, [cls, text])
+
+    def handle_bits(self):
+        if len(self.bits) < self.need_bits:
+            return
+        ss_packet, es_packet = self.bits[0][1], self.bits[-1][2]
+        r, g, b, w = 0, 0, 0, None
+        comps = []
+        for i, c in enumerate(self.wireformat):
+            first_idx, after_idx = 8 * i, 8 * i + 8
+            comp_bits = self.bits[first_idx:after_idx]
+            comp_ss, comp_es = comp_bits[0][1], comp_bits[-1][2]
+            comp_value = bitpack_msb(comp_bits, 0)
+            comp_text = '{:02x}'.format(comp_value)
+            comp_ann = {
+                    'r': ANN_COMP_R, 'g': ANN_COMP_G,
+                    'b': ANN_COMP_B, 'w': ANN_COMP_W,
+            }.get(c.lower(), None)
+            comp_item = (comp_ss, comp_es, comp_ann, comp_value, comp_text)
+            comps.append(comp_item)
+            if c.lower() == 'r':
+                r = comp_value
+            elif c.lower() == 'g':
+                g = comp_value
+            elif c.lower() == 'b':
+                b = comp_value
+            elif c.lower() == 'w':
+                w = comp_value
+        wt = '' if w is None else '{:02x}'.format(w)
+        if self.textformat == 'wire':
+            rgb_text = '#' + ''.join([c[-1] for c in comps])
+        else:
+            rgb_text = self.textformat.format(r = r, g = g, b = b, w = w, wt = wt)
+        for ss_comp, es_comp, cls_comp, value_comp, text_comp in comps:
+            self.putg(ss_comp, es_comp, cls_comp, [text_comp])
+        if rgb_text:
+            self.putg(ss_packet, es_packet, ANN_RGB, [rgb_text])
+        self.bits.clear()
+
+    def handle_bit(self, ss, es, value, ann_late = False):
+        if not ann_late:
+            text = ['{:d}'.format(value)]
+            self.putg(ss, es, ANN_BIT, text)
+        item = (value, ss, es)
+        self.bits.append(item)
+        self.handle_bits()
+        if ann_late:
+            text = ['{:d}'.format(value)]
+            self.putg(ss, es, ANN_BIT, text)
 
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
 
-        while True:
-            # TODO: Come up with more appropriate self.wait() conditions.
-            (pin,) = self.wait()
-
-            if self.oldpin is None:
-                self.oldpin = pin
-                continue
-
-            # Check RESET condition (manufacturer recommends 50 usec minimal,
-            # but real minimum is ~10 usec).
-            if not self.inreset and not pin and self.es is not None and \
-                    self.ss is not None and \
-                    (self.samplenum - self.es) / self.samplerate > 50e-6:
-
-                # Decode last bit value.
-                tH = (self.es - self.ss) / self.samplerate
-                bit_ = True if tH >= 625e-9 else False
-
-                self.bits.append(bit_)
-                self.handle_bits(self.es)
-
-                self.put(self.ss, self.es, self.out_ann, [0, ['%d' % bit_]])
-                self.put(self.es, self.samplenum, self.out_ann,
-                         [1, ['RESET', 'RST', 'R']])
-
-                self.inreset = True
-                self.bits = []
-                self.ss_packet = None
-                self.ss = None
-
-            if not self.oldpin and pin:
-                # Rising edge.
-                if self.ss and self.es:
-                    period = self.samplenum - self.ss
-                    duty = self.es - self.ss
-                    # Ideal duty for T0H: 33%, T1H: 66%.
-                    bit_ = (duty / period) > 0.5
+        # Preprocess options here, to simplify logic which executes
+        # much later in loops while settings have the same values.
+        wireorder = self.options['wireorder'].lower()
+        self.wireformat = [c for c in wireorder if c in 'rgbw']
+        self.need_bits = len(self.wireformat) * 8
+        textorder = self.options['textorder'].lower()
+        if textorder == 'wire':
+            self.textformat = 'wire'
+        elif textorder == 'rgb[w]':
+            self.textformat = '#{r:02x}{g:02x}{b:02x}{wt:s}'
+        else:
+            self.textformat = {
+                # "Obvious" permutations of R/G/B.
+                'bgr': '#{b:02x}{g:02x}{r:02x}',
+                'brg': '#{b:02x}{r:02x}{g:02x}',
+                'gbr': '#{g:02x}{b:02x}{r:02x}',
+                'grb': '#{g:02x}{r:02x}{b:02x}',
+                'rbg': '#{r:02x}{b:02x}{g:02x}',
+                'rgb': '#{r:02x}{g:02x}{b:02x}',
+                # RGB plus White. Only one of them useful?
+                'rgbw': '#{r:02x}{g:02x}{b:02x}{w:02x}',
+                # Weird RGBW permutation for compatibility to test case.
+                # Neither used RGBW nor the 'wire' order. Obsolete now?
+                'rgwb': '#{r:02x}{g:02x}{w:02x}{b:02x}',
+            }.get(textorder, None)
+            if self.textformat is None:
+                raise DecoderError('Unsupported text output format.')
 
-                    self.put(self.ss, self.samplenum, self.out_ann,
-                             [0, ['%d' % bit_]])
+        # Either check for edges which communicate bit values, or for
+        # long periods of idle level which represent a reset pulse.
+        # Track the left-most, right-most, and inner edge positions of
+        # a bit. The positive period's width determines the bit's value.
+        # Initially synchronize to the input stream by searching for a
+        # low period, which preceeds a data bit or starts a reset pulse.
+        # Don't annotate the very first reset pulse, but process it. We
+        # may not see the right-most edge of a data bit when reset is
+        # adjacent to that bit time.
+        cond_bit_starts = {0: 'r'}
+        cond_inbit_edge = {0: 'f'}
+        samples_625ns = int(self.samplerate * 625e-9)
+        samples_50us = round(self.samplerate * 50e-6)
+        cond_reset_pulse = {'skip': samples_50us + 1}
+        conds = [cond_bit_starts, cond_inbit_edge, cond_reset_pulse]
+        ss_bit, inv_bit, es_bit = None, None, None
+        pin, = self.wait({0: 'l'})
+        inv_bit = self.samplenum
+        check_reset = False
+        while True:
+            pin, = self.wait(conds)
 
-                    self.bits.append(bit_)
-                    self.handle_bits(self.samplenum)
+            # Check RESET condition. Manufacturers may disagree on the
+            # minimal pulse width. 50us are recommended in datasheets,
+            # experiments suggest the limit is around 10us.
+            # When the RESET pulse is adjacent to the low phase of the
+            # last bit time, we have no appropriate condition for the
+            # bit time's end location. That's why this BIT's annotation
+            # is shorter (only spans the high phase), and the RESET
+            # annotation immediately follows (spans from the falling edge
+            # to the end of the minimum RESET pulse width).
+            if check_reset and self.matched[2]:
+                es_bit = inv_bit
+                ss_rst, es_rst = inv_bit, self.samplenum
 
-                if self.ss_packet is None:
-                    self.ss_packet = self.samplenum
+                if ss_bit and inv_bit and es_bit:
+                    # Decode last bit value. Use the last processed bit's
+                    # width for comparison when available. Fallback to an
+                    # arbitrary threshold otherwise (which can result in
+                    # false detection of value 1 for those captures where
+                    # high and low pulses are of similar width).
+                    duty = inv_bit - ss_bit
+                    thres = samples_625ns
+                    if self.bits:
+                        period = self.bits[-1][2] - self.bits[-1][1]
+                        thres = period * 0.5
+                    bit_value = 1 if duty >= thres else 0
+                    self.handle_bit(ss_bit, inv_bit, bit_value, True)
 
-                self.ss = self.samplenum
+                if ss_rst and es_rst:
+                    text = ['RESET', 'RST', 'R']
+                    self.putg(ss_rst, es_rst, ANN_RESET, text)
+                check_reset = False
 
-            elif self.oldpin and not pin:
-                # Falling edge.
-                self.inreset = False
-                self.es = self.samplenum
+                self.bits.clear()
+                ss_bit, inv_bit, es_bit = None, None, None
 
-            self.oldpin = pin
+            # Rising edge starts a bit time. Falling edge ends its high
+            # period. Get the previous bit's duty cycle and thus its
+            # bit value when the next bit starts.
+            if self.matched[0]: # and pin:
+                check_reset = False
+                if ss_bit and inv_bit:
+                    # Got a previous bit? Handle it.
+                    es_bit = self.samplenum
+                    period = es_bit - ss_bit
+                    duty = inv_bit - ss_bit
+                    # Ideal duty for T0H: 33%, T1H: 66%.
+                    bit_value = 1 if (duty / period) > 0.5 else 0
+                    self.handle_bit(ss_bit, es_bit, bit_value)
+                ss_bit, inv_bit, es_bit = self.samplenum, None, None
+            if self.matched[1]: # and not pin:
+                check_reset = True
+                inv_bit = self.samplenum
diff --git a/decoders/sae_j1850_vpw/__init__.py b/decoders/sae_j1850_vpw/__init__.py
new file mode 100644 (file)
index 0000000..6894bfd
--- /dev/null
@@ -0,0 +1,24 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Anthony Symons <antus@pcmhacking.net>
+##
+## 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/>.
+##
+
+'''
+SAE J1850 Variable Pulse Width decoder. Decode GM VPW 1X and 4X Vehicle Bus.
+'''
+
+from .pd import Decoder
diff --git a/decoders/sae_j1850_vpw/pd.py b/decoders/sae_j1850_vpw/pd.py
new file mode 100644 (file)
index 0000000..3655f96
--- /dev/null
@@ -0,0 +1,296 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2016 Anthony Symons <antus@pcmhacking.net>
+## Copyright (C) 2023 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack_msb
+
+# VPW Timings. From the SAE J1850 1995 rev section 23.406 documentation.
+# Ideal, minimum and maximum tolerances.
+VPW_SOF = 200
+VPW_SOFL = 164
+VPW_SOFH = 245  # 240 by the spec, 245 so a 60us 4x sample will pass
+VPW_LONG = 128
+VPW_LONGL = 97
+VPW_LONGH = 170 # 164 by the spec but 170 for low sample rate tolerance.
+VPW_SHORT = 64
+VPW_SHORTL = 24 # 35 by the spec, 24 to allow down to 6us as measured in practice for 4x @ 1mhz sampling
+VPW_SHORTH = 97
+VPW_IFS = 240
+
+class SamplerateError(Exception):
+    pass
+
+(
+    ANN_SOF, ANN_BIT, ANN_IFS, ANN_BYTE,
+    ANN_PRIO, ANN_DEST, ANN_SRC, ANN_MODE, ANN_DATA, ANN_CSUM,
+    ANN_M1_PID,
+    ANN_WARN,
+) = range(12)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sae_j1850_vpw'
+    name = 'SAE J1850 VPW'
+    longname = 'SAE J1850 VPW.'
+    desc = 'SAE J1850 Variable Pulse Width 1x and 4x.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Automotive']
+    channels = (
+        {'id': 'data', 'name': 'Data', 'desc': 'Data line'},
+    )
+    annotations = (
+        ('sof', 'SOF'),
+        ('bit', 'Bit'),
+        ('ifs', 'EOF/IFS'),
+        ('byte', 'Byte'),
+        ('prio', 'Priority'),
+        ('dest', 'Destination'),
+        ('src', 'Source'),
+        ('mode', 'Mode'),
+        ('data', 'Data'),
+        ('csum', 'Checksum'),
+        ('m1_pid', 'Pid'),
+        ('warn', 'Warning'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (ANN_SOF, ANN_BIT, ANN_IFS,)),
+        ('bytes', 'Bytes', (ANN_BYTE,)),
+        ('fields', 'Fields', (ANN_PRIO, ANN_DEST, ANN_SRC, ANN_MODE, ANN_DATA, ANN_CSUM,)),
+        ('values', 'Values', (ANN_M1_PID,)),
+        ('warns', 'Warnings', (ANN_WARN,)),
+    )
+    # TODO Add support for options? Polarity. Glitch length.
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.active = 0 # Signal polarity. Needs to become an option?
+        self.bits = []
+        self.fields = {}
+
+    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 putg(self, ss, es, cls, texts):
+        self.put(ss, es, self.out_ann, [cls, texts])
+
+    def invalidate_frame_details(self):
+        self.bits.clear()
+        self.fields.clear()
+
+    def handle_databytes(self, fields, data):
+        # TODO Deep inspection of header fields and data values, including
+        # checksum verification results.
+        mode = fields.get('mode', None)
+        if mode is None:
+            return
+        if mode == 1:
+            # An earlier implementation commented that for mode 1 the
+            # first data byte would be the PID. But example captures
+            # have no data bytes in packets for that mode. This position
+            # is taken by the checksum. Is this correct?
+            pid = data[0] if data else fields.get('csum', None)
+            if pid is None:
+                text = ['PID missing']
+                self.putg(ss, es, ANN_WARN, text)
+            else:
+                byte_text = '{:02x}'.format(pid)
+                self.putg(ss, es, ANN_M1_PID, [byte_text])
+
+    def handle_byte(self, ss, es, b):
+        # Annotate all raw byte values. Inspect and process the first
+        # bytes in a frame already. Cease inspection and only accumulate
+        # all other bytes after the mode. The checksum's position and
+        # thus the data bytes' span will only be known when EOF or IFS
+        # were seen. Implementor's note: This method just identifies
+        # header fields. Processing is left to the .handle_databytes()
+        # method. Until then validity will have been checked, too (CS).
+        byte_text = '{:02x}'.format(b)
+        self.putg(ss, es, ANN_BYTE, [byte_text])
+
+        if not 'prio' in self.fields:
+            self.fields.update({'prio': b})
+            self.putg(ss, es, ANN_PRIO, [byte_text])
+            return
+        if not 'dest' in self.fields:
+            self.fields.update({'dest': b})
+            self.putg(ss, es, ANN_DEST, [byte_text])
+            return
+        if not 'src' in self.fields:
+            self.fields.update({'src': b})
+            self.putg(ss, es, ANN_SRC, [byte_text])
+            return
+        if not 'mode' in self.fields:
+            self.fields.update({'mode': b})
+            self.putg(ss, es, ANN_MODE, [byte_text])
+            return
+        if not 'data' in self.fields:
+            self.fields.update({'data': [], 'csum': None})
+        self.fields['data'].append((b, ss, es))
+
+    def handle_sof(self, ss, es, speed):
+        text = ['{speed:d}x SOF', 'S{speed:d}', 'S']
+        text = [f.format(speed = speed) for f in text]
+        self.putg(ss, es, ANN_SOF, text)
+        self.invalidate_frame_details()
+        self.fields.update({'speed': speed})
+
+    def handle_bit(self, ss, es, b):
+        self.bits.append((b, ss, es))
+        self.putg(ss, es, ANN_BIT, ['{:d}'.format(b)])
+        if len(self.bits) < 8:
+            return
+        ss, es = self.bits[0][1], self.bits[-1][2]
+        b = bitpack_msb(self.bits, 0)
+        self.bits.clear()
+        self.handle_byte(ss, es, b)
+
+    def handle_eof(self, ss, es, is_ifs = False):
+        # EOF or IFS were seen. Post process the data bytes sequence.
+        # Separate the checksum from the data bytes. Emit annotations.
+        # Pass data bytes and header fields to deeper inspection.
+        data = self.fields.get('data', {})
+        if not data:
+            text = ['Short data phase', 'Data']
+            self.putg(ss, es, ANN_WARN, text)
+        csum = None
+        if len(data) >= 1:
+            csum, ss_csum, es_csum = data.pop()
+            self.fields.update({'csum': csum})
+            # TODO Verify checksum's correctness?
+        if data:
+            ss_data, es_data = data[0][1], data[-1][2]
+            text = ' '.join(['{:02x}'.format(b[0]) for b in data])
+            self.putg(ss_data, es_data, ANN_DATA, [text])
+        if csum is not None:
+            text = '{:02x}'.format(csum)
+            self.putg(ss_csum, es_csum, ANN_CSUM, [text])
+        text = ['IFS', 'I'] if is_ifs else ['EOF', 'E']
+        self.putg(ss, es, ANN_IFS, text)
+        self.handle_databytes(self.fields, data);
+        self.invalidate_frame_details()
+
+    def handle_unknown(self, ss, es):
+        text = ['Unknown condition', 'Unknown', 'UNK']
+        self.putg(ss, es, ANN_WARN, text)
+        self.invalidate_frame_details()
+
+    def usecs_to_samples(self, us):
+        us *= 1e-6
+        us *= self.samplerate
+        return int(us)
+
+    def samples_to_usecs(self, n):
+        n /= self.samplerate
+        n *= 1000.0 * 1000.0
+        return int(n)
+
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+
+        # Get the distance between edges. Classify the distance
+        # to derive symbols and data bit values. Prepare waiting
+        # for an interframe gap as well, while this part of the
+        # condition is optional (switches in and out at runtime).
+        conds_edge = {0: 'e'}
+        conds_edge_only = [conds_edge]
+        conds_edge_idle = [conds_edge, {'skip': 0}]
+        conds = conds_edge_only
+        self.wait(conds)
+        es = self.samplenum
+        spd = None
+        while True:
+            ss = es
+            pin, = self.wait(conds)
+            es = self.samplenum
+            count = es - ss
+            t = self.samples_to_usecs(count)
+
+            # Synchronization to the next frame. Wait for SOF.
+            # Silently keep synchronizing until SOF was seen.
+            if spd is None:
+                if not self.matched[0]:
+                    continue
+                if pin != self.active:
+                    continue
+
+                # Detect the frame's speed from the SOF length. Adjust
+                # the expected BIT lengths to the SOF derived speed.
+                # Arrange for the additional supervision of EOF/IFS.
+                if t in range(VPW_SOFL // 1, VPW_SOFH // 1):
+                    spd = 1
+                elif t in range(VPW_SOFL // 4, VPW_SOFH // 4):
+                    spd = 4
+                else:
+                    continue
+                short_lower, short_upper = VPW_SHORTL // spd, VPW_SHORTH // spd
+                long_lower, long_upper = VPW_LONGL // spd, VPW_LONGH // spd
+                samples = self.usecs_to_samples(VPW_IFS // spd)
+                conds_edge_idle[-1]['skip'] = samples
+                conds = conds_edge_idle
+
+                # Emit the SOF annotation. Start collecting DATA.
+                self.handle_sof(ss, es, spd)
+                continue
+
+            # Inside the DATA phase. Get data bits. Handle EOF/IFS.
+            if len(conds) > 1 and self.matched[1]:
+                # TODO The current implementation gets here after a
+                # pre-determined minimum wait time. Does not differ
+                # between EOF and IFS. An earlier implementation had
+                # this developer note: EOF=239-280 IFS=281+
+                self.handle_eof(ss, es)
+                # Enter the IDLE phase. Wait for the next SOF.
+                spd = None
+                conds = conds_edge_only
+                continue
+            if t in range(short_lower, short_upper):
+                value = 1 if pin == self.active else 0
+                self.handle_bit(ss, es, value)
+                continue
+            if t in range(long_lower, long_upper):
+                value = 0 if pin == self.active else 1
+                self.handle_bit(ss, es, value)
+                continue
+
+            # Implementation detail: An earlier implementation used to
+            # ignore everything that was not handled above. This would
+            # be motivated by the noisy environment the protocol is
+            # typically used in. This more recent implementation accepts
+            # short glitches, but by design falls back to synchronization
+            # to the input stream for other unhandled conditions. This
+            # wants to improve usability of the decoder, by presenting
+            # potential issues to the user. The threshold (microseconds
+            # between edges that are not valid symbols that are handled
+            # above) is an arbitrary choice.
+            if t <= 2:
+                continue
+            self.handle_unknown(ss, es)
+            spd = None
+            conds = conds_edge_only
diff --git a/decoders/sbus_futaba/__init__.py b/decoders/sbus_futaba/__init__.py
new file mode 100644 (file)
index 0000000..9404f4f
--- /dev/null
@@ -0,0 +1,35 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+'''
+SBUS by Futaba, a hobby remote control protocol on top of UART.
+Sometimes referred to as "Serial BUS" or S-BUS.
+
+UART communication typically runs at 100kbps with 8e2 frame format and
+inverted signals (high voltage level is logic low).
+
+SBUS messages take 3ms to transfer, and typically repeat in intervals
+of 7ms or 14ms. An SBUS message consists of 25 UART bytes, and carries
+16 proportional channels with 11 bits each, and 2 digital channels
+(boolean, 1 bit), and flags which represent current communication state.
+Proportional channel values typically are in the 192..1792 range, but
+individual implementations may differ.
+'''
+
+from .pd import Decoder
diff --git a/decoders/sbus_futaba/pd.py b/decoders/sbus_futaba/pd.py
new file mode 100644 (file)
index 0000000..75c2cfb
--- /dev/null
@@ -0,0 +1,273 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2022 Gerhard Sittig <gerhard.sittig@gmx.net>
+##
+## 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/>.
+##
+
+"""
+OUTPUT_PYTHON format:
+
+Packet:
+(<ptype>, <pdata>)
+
+This is the list of <ptype> codes and their respective <pdata> values:
+ - 'HEADER': The data is the header byte's value.
+ - 'PROPORTIONAL': The data is a tuple of the channel number (1-based)
+   and the channel's value.
+ - 'DIGITAL': The data is a tuple of the channel number (1-based)
+   and the channel's value.
+ - 'FLAG': The data is a tuple of the flag's name, and the flag's value.
+ - 'FOOTER': The data is the footer byte's value.
+"""
+
+import sigrokdecode as srd
+from common.srdhelper import bitpack_lsb
+
+class Ann:
+    HEADER, PROPORTIONAL, DIGITAL, FRAME_LOST, FAILSAFE, FOOTER, \
+    WARN = range(7)
+    FLAG_LSB = FRAME_LOST
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sbus_futaba'
+    name = 'SBUS (Futaba)'
+    longname = 'Futaba SBUS (Serial bus)'
+    desc = 'Serial bus for hobby remote control by Futaba'
+    license = 'gplv2+'
+    inputs = ['uart']
+    outputs = ['sbus_futaba']
+    tags = ['Remote Control']
+    options = (
+        {'id': 'prop_val_min', 'desc': 'Proportional value lower boundary', 'default': 0},
+        {'id': 'prop_val_max', 'desc': 'Proportional value upper boundary', 'default': 2047},
+    )
+    annotations = (
+        ('header', 'Header'),
+        ('proportional', 'Proportional'),
+        ('digital', 'Digital'),
+        ('framelost', 'Frame Lost'),
+        ('failsafe', 'Failsafe'),
+        ('footer', 'Footer'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('framing', 'Framing', (Ann.HEADER, Ann.FOOTER,
+            Ann.FRAME_LOST, Ann.FAILSAFE)),
+        ('channels', 'Channels', (Ann.PROPORTIONAL, Ann.DIGITAL)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+    )
+
+    def __init__(self):
+        self.bits_accum = []
+        self.sent_fields = None
+        self.msg_complete = None
+        self.failed = None
+        self.reset()
+
+    def reset(self):
+        self.bits_accum.clear()
+        self.sent_fields = 0
+        self.msg_complete = False
+        self.failed = None
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_py = self.register(srd.OUTPUT_PYTHON)
+
+    def putg(self, ss, es, data):
+        # Put a graphical annotation.
+        self.put(ss, es, self.out_ann, data)
+
+    def putpy(self, ss, es, data):
+        # Pass Python to upper layers.
+        self.put(ss, es, self.out_py, data)
+
+    def get_ss_es_bits(self, bitcount):
+        # Get start/end times, and bit values of given length.
+        # Gets all remaining data when 'bitcount' is None.
+        if bitcount is None:
+            bitcount = len(self.bits_accum)
+        if len(self.bits_accum) < bitcount:
+            return None, None, None
+        bits = self.bits_accum[:bitcount]
+        self.bits_accum = self.bits_accum[bitcount:]
+        ss, es = bits[0][1], bits[-1][2]
+        bits = [b[0] for b in bits]
+        return ss, es, bits
+
+    def flush_accum_bits(self):
+        # Valid data was queued. See if we got full SBUS fields so far.
+        # Annotate them early, cease inspection of failed messages. The
+        # implementation is phrased to reduce the potential for clipboard
+        # errors: 'upto' is the next supported field count, 'want' is one
+        # field's bit count. Grab as many as we find in an invocation.
+        upto = 0
+        if self.failed:
+            return
+        # Annotate the header byte. Not seeing the expected bit pattern
+        # emits a warning annotation, but by design won't fail the SBUS
+        # message. It's considered more useful to present the channels'
+        # values instead. The warning still raises awareness.
+        upto += 1
+        want = 8
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            text = ['0x{:02x}'.format(value)]
+            self.putg(ss, es, [Ann.HEADER, text])
+            if value != 0x0f:
+                text = ['Unexpected header', 'Header']
+                self.putg(ss, es, [Ann.WARN, text])
+            self.putpy(ss, es, ['HEADER', value])
+            self.sent_fields += 1
+        # Annotate the proportional channels' data. Check for user
+        # provided value range violations. Channel numbers are in
+        # the 1..18 range (1-based).
+        upto += 16
+        want = 11
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            text = ['{:d}'.format(value)]
+            self.putg(ss, es, [Ann.PROPORTIONAL, text])
+            if value < self.options['prop_val_min']:
+                text = ['Low proportional value', 'Low value', 'Low']
+                self.putg(ss, es, [Ann.WARN, text])
+            if value > self.options['prop_val_max']:
+                text = ['High proportional value', 'High value', 'High']
+                self.putg(ss, es, [Ann.WARN, text])
+            idx = self.sent_fields - (upto - 16)
+            ch_nr = 1 + idx
+            self.putpy(ss, es, ['PROPORTIONAL', (ch_nr, value)])
+            self.sent_fields += 1
+        # Annotate the digital channels' data.
+        upto += 2
+        want = 1
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            text = ['{:d}'.format(value)]
+            self.putg(ss, es, [Ann.DIGITAL, text])
+            idx = self.sent_fields - (upto - 2)
+            ch_nr = 17 + idx
+            self.putpy(ss, es, ['DIGITAL', (ch_nr, value)])
+            self.sent_fields += 1
+        # Annotate the flags' state. Index starts from LSB.
+        flag_names = ['framelost', 'failsafe', 'msb']
+        upto += 2
+        want = 1
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            text = ['{:d}'.format(value)]
+            idx = self.sent_fields - (upto - 2)
+            cls = Ann.FLAG_LSB + idx
+            self.putg(ss, es, [cls, text])
+            flg_name = flag_names[idx]
+            self.putpy(ss, es, ['FLAG', (flg_name, value)])
+            self.sent_fields += 1
+        # Warn when flags' padding (bits [7:4]) is unexpexted.
+        upto += 1
+        want = 4
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            if value != 0x0:
+                text = ['Unexpected MSB flags', 'Flags']
+                self.putg(ss, es, [Ann.WARN, text])
+            flg_name = flag_names[-1]
+            self.putpy(ss, es, ['FLAG', (flg_name, value)])
+            self.sent_fields += 1
+        # Annotate the footer byte. Warn when unexpected.
+        upto += 1
+        want = 8
+        while self.sent_fields < upto:
+            if len(self.bits_accum) < want:
+                return
+            ss, es, bits = self.get_ss_es_bits(want)
+            value = bitpack_lsb(bits)
+            text = ['0x{:02x}'.format(value)]
+            self.putg(ss, es, [Ann.FOOTER, text])
+            if value != 0x00:
+                text = ['Unexpected footer', 'Footer']
+                self.putg(ss, es, [Ann.WARN, text])
+            self.putpy(ss, es, ['FOOTER', value])
+            self.sent_fields += 1
+        # Check for the completion of an SBUS message. Warn when more
+        # UART data is seen after the message. Defer the warning until
+        # more bits were collected, flush at next IDLE or BREAK, which
+        # spans all unprocessed data, and improves perception.
+        if self.sent_fields >= upto:
+            self.msg_complete = True
+        if self.msg_complete and self.bits_accum:
+            self.failed = ['Excess data bits', 'Excess']
+
+    def handle_bits(self, ss, es, bits):
+        # UART data bits were seen. Store them, validity is yet unknown.
+        self.bits_accum.extend(bits)
+
+    def handle_frame(self, ss, es, value, valid):
+        # A UART frame became complete. Get its validity. Process its bits.
+        if not valid:
+            self.failed = ['Invalid data', 'Invalid']
+        self.flush_accum_bits()
+
+    def handle_idle(self, ss, es):
+        # An IDLE period was seen in the UART level. Flush, reset state.
+        if self.bits_accum and not self.failed:
+            self.failed = ['Unprocessed data bits', 'Unprocessed']
+        if self.bits_accum and self.failed:
+            ss, es, _ = self.get_ss_es_bits(None)
+            self.putg(ss, es, [Ann.WARN, self.failed])
+        self.reset()
+
+    def handle_break(self, ss, es):
+        # A BREAK period was seen in the UART level. Warn, reset state.
+        break_ss, break_es = ss, es
+        if not self.failed:
+            self.failed = ['BREAK condition', 'Break']
+        # Re-use logic for "annotated bits warning".
+        self.handle_idle(None, None)
+        # Unconditionally annotate BREAK as warning.
+        text = ['BREAK condition', 'Break']
+        self.putg(ss, es, [Ann.WARN, text])
+        self.reset()
+
+    def decode(self, ss, es, data):
+        # Implementor's note: Expects DATA bits to arrive before FRAME
+        # validity. Either of IDLE or BREAK terminates an SBUS message.
+        ptype, rxtx, pdata = data
+        if ptype == 'DATA':
+            _, bits = pdata
+            self.handle_bits(ss, es, bits)
+        elif ptype == 'FRAME':
+            value, valid = pdata
+            self.handle_frame(ss, es, value, valid)
+        elif ptype == 'IDLE':
+            self.handle_idle(ss, es)
+        elif ptype == 'BREAK':
+            self.handle_break(ss, es)
diff --git a/decoders/sdq/__init__.py b/decoders/sdq/__init__.py
new file mode 100644 (file)
index 0000000..3fc1043
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Philip Ã…kesson <philip.akesson@gmail.com>
+##
+## 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/>.
+##
+
+'''
+The SDQ protocol was developed by Texas Instruments, and is used in
+devices like battery pack authentication. Apple uses SDQ in MagSafe
+and Lightning connectors, as well as some batteries.
+
+See https://www.ti.com/lit/ds/symlink/bq26100.pdf for details.
+'''
+
+from .pd import Decoder
diff --git a/decoders/sdq/pd.py b/decoders/sdq/pd.py
new file mode 100644 (file)
index 0000000..66df420
--- /dev/null
@@ -0,0 +1,131 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Philip Ã…kesson <philip.akesson@gmail.com>
+##
+## 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 common.srdhelper import bitpack
+import sigrokdecode as srd
+
+class SamplerateError(Exception):
+    pass
+
+class Pin:
+    SDQ, = range(1)
+
+class Ann:
+    BIT, BYTE, BREAK, = range(3)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sdq'
+    name = 'SDQ'
+    longname = 'Texas Instruments SDQ'
+    desc = 'Texas Instruments SDQ. The SDQ protocol is also used by Apple.'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Embedded/industrial']
+    channels = (
+        {'id': 'sdq', 'name': 'SDQ', 'desc': 'Single wire SDQ data line.'},
+    )
+    options = (
+        {'id': 'bitrate', 'desc': 'Bit rate', 'default': 98425},
+    )
+    annotations = (
+        ('bit', 'Bit'),
+        ('byte', 'Byte'),
+        ('break', 'Break'),
+    )
+    annotation_rows = (
+        ('bits', 'Bits', (Ann.BIT,)),
+        ('bytes', 'Bytes', (Ann.BYTE,)),
+        ('breaks', 'Breaks', (Ann.BREAK,)),
+    )
+
+    def puts(self, data):
+        self.put(self.startsample, self.samplenum, self.out_ann, data)
+
+    def putetu(self, data):
+        self.put(self.startsample, self.startsample + int(self.bit_width), self.out_ann, data)
+
+    def putbetu(self, data):
+        self.put(self.bytepos, self.startsample + int(self.bit_width), self.out_ann, data)
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.startsample = 0
+        self.bits = []
+        self.bytepos = 0
+
+    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 handle_bit(self, bit):
+        self.bits.append(bit)
+        self.putetu([Ann.BIT, [
+            'Bit: {:d}'.format(bit),
+            '{:d}'.format(bit),
+        ]])
+
+        if len(self.bits) == 8:
+            byte = bitpack(self.bits)
+            self.putbetu([Ann.BYTE, [
+                'Byte: 0x{:02x}'.format(byte),
+                '0x{:02x}'.format(byte),
+            ]])
+            self.bits = []
+            self.bytepos = 0
+
+    def handle_break(self):
+        self.puts([Ann.BREAK, ['Break', 'BR']])
+        self.bits = []
+        self.startsample = self.samplenum
+        self.bytepos = 0
+
+    def decode(self):
+        if not self.samplerate:
+            raise SamplerateError('Cannot decode without samplerate.')
+        self.bit_width = float(self.samplerate) / float(self.options['bitrate'])
+        self.half_bit_width = self.bit_width / 2.0
+        # BREAK if the line is low for longer than this.
+        break_threshold = self.bit_width * 1.2
+
+        # Wait until the line is high before inspecting input data.
+        sdq, = self.wait({Pin.SDQ: 'h'})
+        while True:
+            # Get the length of a low pulse (falling to rising edge).
+            sdq, = self.wait({Pin.SDQ: 'f'})
+            self.startsample = self.samplenum
+            if self.bytepos == 0:
+                self.bytepos = self.samplenum
+            sdq, = self.wait({Pin.SDQ: 'r'})
+
+            # Check for 0 or 1 data bits, or the BREAK symbol.
+            delta = self.samplenum - self.startsample
+            if delta > break_threshold:
+                self.handle_break()
+            elif delta > self.half_bit_width:
+                self.handle_bit(0)
+            else:
+                self.handle_bit(1)
index 87714bbcd81483df06817f9aa374c0b31d2bd293..2172269cd6a3288ad4e66cd622b99b9527639776 100644 (file)
@@ -22,8 +22,27 @@ import sigrokdecode as srd
 class ChannelError(Exception):
     pass
 
+# This table is sorted by ASCII code numbers, with the exception
+# of letters having their upper/lower case ignored.
+#
+# Traditional LED segment names and layout:
+#
+#      A
+#    F   B
+#      G
+#    E   C
+#      D
+#
+#    A  B  C  D  E  F  G
 digits = {
     (0, 0, 0, 0, 0, 0, 0): ' ',
+    (0, 1, 0, 0, 0, 1, 0): '"',
+    (1, 1, 0, 1, 1, 1, 1): "&",
+    (0, 0, 0, 0, 0, 1, 0): "'",
+    (0, 1, 0, 0, 0, 0, 0): "'",
+    (0, 0, 1, 1, 0, 0, 0): ',',
+    (0, 0, 0, 0, 0, 0, 1): '-',
+    (0, 0, 0, 0, 1, 0, 0): '.',
     (1, 1, 1, 1, 1, 1, 0): '0',
     (0, 1, 1, 0, 0, 0, 0): '1',
     (1, 1, 0, 1, 1, 0, 1): '2',
@@ -31,15 +50,53 @@ digits = {
     (0, 1, 1, 0, 0, 1, 1): '4',
     (1, 0, 1, 1, 0, 1, 1): '5',
     (1, 0, 1, 1, 1, 1, 1): '6',
+    (1, 1, 1, 0, 0, 1, 0): '7',
     (1, 1, 1, 0, 0, 0, 0): '7',
     (1, 1, 1, 1, 1, 1, 1): '8',
     (1, 1, 1, 1, 0, 1, 1): '9',
+    (1, 0, 0, 0, 0, 0, 1): '=',
+    (0, 0, 0, 1, 0, 0, 1): '=',
+    (1, 1, 0, 0, 1, 0, 1): '?',
     (1, 1, 1, 0, 1, 1, 1): 'A',
-    (0, 0, 1, 1, 1, 1, 1): 'B',
+    (1, 1, 1, 1, 1, 0, 1): 'a',
+    (0, 0, 1, 1, 1, 1, 1): 'b',
     (1, 0, 0, 1, 1, 1, 0): 'C',
-    (0, 1, 1, 1, 1, 0, 1): 'D',
+    (0, 0, 0, 1, 1, 0, 1): 'c',
+    (0, 1, 1, 1, 1, 0, 1): 'd',
     (1, 0, 0, 1, 1, 1, 1): 'E',
     (1, 0, 0, 0, 1, 1, 1): 'F',
+    (1, 0, 1, 1, 1, 1, 0): 'G',
+    (0, 1, 1, 0, 1, 1, 1): 'H',
+    (0, 0, 1, 0, 1, 1, 1): 'h',
+    (0, 0, 0, 0, 1, 1, 0): 'I',
+    (1, 0, 0, 0, 1, 0, 0): 'i',
+    (0, 0, 1, 0, 0, 0, 0): 'i',
+    (0, 1, 1, 1, 1, 0, 0): 'J',
+    (0, 1, 1, 1, 0, 0, 0): 'J',
+    (1, 0, 1, 1, 0, 0, 0): 'j',
+    (1, 0, 1, 0, 1, 1, 1): 'K',
+    (0, 0 ,0, 1, 1, 1, 0): 'L',
+    (1, 0, 1, 0, 1, 0, 0): 'M',
+    (1, 0, 1, 0, 1, 0, 1): 'M',
+    (1, 1, 1, 0, 1, 1, 0): 'N',
+    (0, 0, 1, 0, 1, 0, 1): 'n',
+    (0, 0, 1, 1, 1, 0, 1): 'o',
+    (1, 1, 0, 0, 1, 1, 1): 'p',
+    (1, 1, 1, 0, 0, 1, 1): 'q',
+    (1, 1, 0, 0, 1, 1, 0): 'R',
+    (0, 0, 0, 0, 1, 0, 1): 'r',
+    (0, 0, 0, 1, 1, 1, 1): 't',
+    (0, 0, 1, 1, 1, 0, 0): 'u',
+    (0, 1, 0, 1, 0, 1, 0): 'V',
+    (0, 1, 0, 0, 1, 1, 1): 'V',
+    (0, 1, 1, 1, 1, 1, 0): 'V',
+    (0, 1, 0, 0, 0, 1, 1): 'v',
+    (0, 1, 0, 1, 0, 1, 1): 'W',
+    (0, 0, 1, 0, 1, 0, 0): 'x',
+    (0, 1, 1, 1, 0, 1, 1): 'y',
+    (1, 1, 0, 1, 1, 0, 0): 'Z',
+    (1, 1, 0, 0, 0, 1, 0): '^',
+    (0, 0, 0, 1, 0, 0, 0): '_',
 }
 
 class Decoder(srd.Decoder):
@@ -67,6 +124,8 @@ class Decoder(srd.Decoder):
     options = (
         {'id': 'polarity', 'desc': 'Expected polarity',
             'default': 'common-cathode', 'values': ('common-cathode', 'common-anode')},
+        {'id': 'show_unknown', 'desc': 'Display Unknown characters as #',
+            'default': 'no', 'values': ('yes', 'no')},
     )
     annotations = (
         ('decoded-digit', 'Decoded digit'),
@@ -92,43 +151,36 @@ class Decoder(srd.Decoder):
 
     def decode(self):
         oldpins = self.wait()
-
-        # Check if at least the 7 signals are present.
-        if False in [p in (0, 1) for p in oldpins[:7]]:
-            raise ChannelError('7 or 8 pins have to be present.')
-
         lastpos = self.samplenum
 
+        # Check mandatory and optional decoder input signals.
+        if False in [p in (0, 1) for p in oldpins[:7]]:
+            raise ChannelError('Need at least segments A-G.')
         self.have_dp = self.has_channel(7)
+        seg_count = 8 if self.have_dp else 7
 
-        conditions = [{0: 'e'}, {1: 'e'}, {2: 'e'}, {3: 'e'}, {4: 'e'}, {5: 'e'}, {6: 'e'}]
-
-        if self.have_dp:
-            conditions.append({7: 'e'})
-
+        conditions = [{i: 'e'} for i in range(seg_count)]
         while True:
             # Wait for any change.
             pins = self.wait(conditions)
 
+            # Invert all data lines if a common anode display is used.
             if self.options['polarity'] == 'common-anode':
-                # Invert all data lines if a common anode display is used.
-                if self.have_dp:
-                    oldpins = tuple((1 - state for state in oldpins))
-                else:
-                    oldpins = tuple((1 - state for state in oldpins[:7]))
+                oldpins = tuple((1 - state for state in oldpins[:seg_count]))
 
             # Convert to character string.
             digit = self.pins_to_hex(oldpins[:7])
+            if digit is None and self.options['show_unknown'] == 'yes':
+                digit = '#'
 
+            # Emit annotation when conversion succeeded.
+            # Optionally present the decimal point when active.
             if digit is not None:
-                dp = oldpins[7]
-
-                # Check if decimal point is present and active.
-                if self.have_dp and dp == 1:
-                    digit += '.'
-
+                if self.have_dp:
+                    dp = oldpins[7]
+                    if dp == 1:
+                        digit += '.'
                 self.putb(lastpos, self.samplenum, [0, [digit]])
 
-            lastpos = self.samplenum
-
             oldpins = pins
+            lastpos = self.samplenum
diff --git a/decoders/sipi/__init__.py b/decoders/sipi/__init__.py
new file mode 100644 (file)
index 0000000..d62e3c1
--- /dev/null
@@ -0,0 +1,30 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## 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/>.
+##
+
+'''
+The Serial Inter-Processor Interface (SIPI) is a higher-level protocol that runs
+over the LFAST physical interface. Together, they form the NXP Zipwire interface.
+
+The SIPI interface is also provided by Infineon as HSST, using HSCT for transport.
+
+For details see https://www.nxp.com/docs/en/application-note/AN5134.pdf and
+https://hitex.co.uk/fileadmin/uk-files/downloads/ShieldBuddy/tc27xD_um_v2.2.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/sipi/pd.py b/decoders/sipi/pd.py
new file mode 100644 (file)
index 0000000..5bd58fb
--- /dev/null
@@ -0,0 +1,181 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2020 Soeren Apel <soeren@apelpie.net>
+##
+## 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/>.
+##
+
+import sigrokdecode as srd
+from binascii import crc_hqx
+
+# See tc27xD_um_v2.2.pdf, Table 20-2
+# (name, addr byte count, data byte count)
+command_codes = {
+    0b00000: ('Read byte', 4, 0),
+    0b00001: ('Read 2 byte', 4, 0),
+    0b00010: ('Read 4 byte', 4, 0),
+    # Reserved
+    0b00100: ('Write byte with ACK', 4, 4),
+    0b00101: ('Write 2 byte with ACK', 4, 4),
+    0b00110: ('Write 4 byte with ACK', 4, 4),
+    # Reserved
+    0b01000: ('ACK', 0, 0),
+    0b01001: ('NACK (Target Error)', 0, 0),
+    0b01010: ('Read Answer with ACK', 4, 4),
+    # Reserved
+    0b01100: ('Trigger with ACK', 0, 0),
+    # Reserved
+    # Reserved
+    # Reserved
+    # Reserved
+    # Reserved
+    0b10010: ('Read 4-byte JTAG ID', 0, 0),
+    # Reserved
+    # Reserved
+    # Reserved
+    # Reserved
+    0b10111: ('Stream 32 byte with ACK', 0, 32)
+    # Rest is reserved
+}
+
+
+ann_header_tag, ann_header_cmd, ann_header_ch, ann_address, ann_data, \
+    ann_crc, ann_warning = range(7)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sipi'
+    name = 'SIPI (Zipwire)'
+    longname = 'NXP SIPI interface'
+    desc = 'Serial Inter-Processor Interface (SIPI) aka Zipwire, aka HSSL'
+    license = 'gplv2+'
+    inputs = ['lfast']
+    outputs = []
+    tags = ['Embedded/industrial']
+    annotations = (
+        ('header_tag', 'Transaction Tag'),
+        ('header_cmd', 'Command Code'),
+        ('header_ch', 'Channel'),
+        ('address', 'Address'),
+        ('data', 'Data'),
+        ('crc', 'CRC'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('fields', 'Fields', (ann_header_tag, ann_header_cmd,
+            ann_header_ch, ann_address, ann_data, ann_crc,)),
+        ('warnings', 'Warnings', (ann_warning,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.byte_len = 0
+        self.frame_len = 0
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+    def put_ann(self, ss, es, ann_class, value):
+        self.put(int(ss), int(es), self.out_ann, [ann_class, value])
+
+    def put_header(self, ss_header, es_header, value):
+        ss = ss_header
+        es = ss + 3 * self.bit_len
+        tag = (value & 0xE000) >> 13
+        self.put_ann(ss, es, ann_header_tag, ['{:02X}'.format(tag)])
+
+        ss = es
+        es = ss + 5 * self.bit_len
+        cmd_id = (value & 0x1F00) >> 8
+        cmd_name, self.addr_len, self.data_len = \
+            command_codes.get(cmd_id, ('Reserved ({:02X})'.format(cmd_id), 0, 0))
+        self.frame_len = 2 + 2 + self.addr_len + self.data_len  # +Header +CRC
+        self.put_ann(ss, es, ann_header_cmd, [cmd_name])
+
+        # Bits 4..7 are reserved and should be 0, warn if they're not
+        ss = es
+        es = ss + 4 * self.bit_len
+        reserved_bits = (value & 0x00F0) >> 4
+        if reserved_bits > 0:
+            self.put_ann(ss, es, ann_warning, ['Reserved bits #4..7 should be 0'])
+
+        ss = es
+        es = ss + 3 * self.bit_len
+        ch = (value & 0x000E) >> 1  # See tc27xD_um_v2.2.pdf, Table 20-1
+        self.put_ann(ss, es, ann_header_ch, [str(ch)])
+
+        # Bit 0 is reserved and should be 0, warn if it's not
+        if (value & 0x0001) == 0x0001:
+            ss = es
+            es = ss + self.bit_len
+            self.put_ann(ss, es, ann_warning, ['Reserved bit #0 should be 0'])
+
+    def put_payload(self, data):
+        byte_idx = 0
+        if self.addr_len > 0:
+            for value_tuple in data[:self.addr_len]:
+                ss, es, value = value_tuple
+                self.put_ann(ss, es, ann_address, ['{:02X}'.format(value)])
+            byte_idx = self.addr_len
+
+        if self.data_len > 0:
+            for value_tuple in data[byte_idx:]:
+                ss, es, value = value_tuple
+                self.put_ann(ss, es, ann_data, ['{:02X}'.format(value)])
+
+    def put_crc(self, ss, es, crc_value, crc_payload_data):
+        crc_payload = []
+        for value_tuple in crc_payload_data:
+            crc_payload.append(value_tuple[2])
+
+        calculated_crc = crc_hqx(bytes(crc_payload), 0xFFFF)
+
+        if calculated_crc == crc_value:
+            self.put_ann(ss, es, ann_crc, ['CRC OK'])
+        else:
+            self.put_ann(ss, es, ann_crc, ['Have {:02X} but calculated {:02X}'.format(crc_value, calculated_crc)])
+            self.put_ann(ss, es, ann_warning, ['CRC mismatch'])
+
+    def decode(self, ss, es, data):
+        if len(data) == 1:
+            self.put_ann(ss, es, ann_warning, ['Header too short'])
+            return
+
+        # ss and es are now unused, we use them as local variables instead
+
+        self.bit_len = (data[0][1] - data[0][0]) / 8.0
+
+        byte_idx = 0
+
+        ss = data[byte_idx][0]
+        es = data[byte_idx + 1][1]
+        self.put_header(ss, es, (data[byte_idx][2] << 8) + data[byte_idx + 1][2])
+        byte_idx += 2
+
+        payload_len = self.frame_len - 2 - 2  # -Header -CRC
+        if payload_len > 0:
+            self.put_payload(data[byte_idx:-2])
+            byte_idx += payload_len
+
+        ss = data[byte_idx][0]
+        es = data[byte_idx + 1][1]
+        if byte_idx == len(data) - 2:
+            # CRC is calculated over header + payload bytes
+            self.put_crc(ss, es, (data[byte_idx][2] << 8) + data[byte_idx + 1][2], data[0:-2])
+        else:
+            self.put_ann(ss, es, ann_warning, ['CRC incomplete or missing'])
diff --git a/decoders/sle44xx/__init__.py b/decoders/sle44xx/__init__.py
new file mode 100644 (file)
index 0000000..0eb0285
--- /dev/null
@@ -0,0 +1,26 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
+##
+## 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/>.
+##
+
+'''
+SLE 4418/28/32/42 memory cards implement a 2-wire protocol (CLK and I/O)
+for data communication, along with the RST signal which resets the card's
+internal state, and can terminate currently executing long memory reads.
+'''
+
+from .pd import Decoder
diff --git a/decoders/sle44xx/pd.py b/decoders/sle44xx/pd.py
new file mode 100644 (file)
index 0000000..9f33207
--- /dev/null
@@ -0,0 +1,541 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Federico Cerutti <federico@ceres-c.it>
+##
+## 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 common.srdhelper import bitpack_lsb
+import sigrokdecode as srd
+
+class Pin:
+    RST, CLK, IO, = range(3)
+
+class Ann:
+    RESET_SYM, INTR_SYM, START_SYM, STOP_SYM, BIT_SYM, \
+    ATR_BYTE, CMD_BYTE, OUT_BYTE, PROC_BYTE, \
+    ATR_DATA, CMD_DATA, OUT_DATA, PROC_DATA, \
+    = range(13)
+
+class Bin:
+    BYTES, = range(1)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'sle44xx'
+    name = 'SLE 44xx'
+    longname = 'SLE44xx memory card'
+    desc = 'SLE 4418/28/32/42 memory card serial protocol'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+    tags = ['Memory']
+    channels = (
+        {'id': 'rst', 'name': 'RST', 'desc': 'Reset line'},
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock line'},
+        {'id': 'io', 'name': 'I/O', 'desc': 'I/O data line'},
+    )
+    annotations = (
+        ('reset_sym', 'Reset Symbol'),
+        ('intr_sym', 'Interrupt Symbol'),
+        ('start_sym', 'Start Symbol'),
+        ('stop_sym', 'Stop Symbol'),
+        ('bit_sym', 'Bit Symbol'),
+        ('atr_byte', 'ATR Byte'),
+        ('cmd_byte', 'Command Byte'),
+        ('out_byte', 'Outgoing Byte'),
+        ('proc_byte', 'Processing Byte'),
+        ('atr_data', 'ATR data'),
+        ('cmd_data', 'Command data'),
+        ('out_data', 'Outgoing data'),
+        ('proc_data', 'Processing data'),
+    )
+    annotation_rows = (
+        ('symbols', 'Symbols', (Ann.RESET_SYM, Ann.INTR_SYM,
+            Ann.START_SYM, Ann.STOP_SYM, Ann.BIT_SYM,)),
+        ('fields', 'Fields', (Ann.ATR_BYTE,
+            Ann.CMD_BYTE, Ann.OUT_BYTE, Ann.PROC_BYTE,)),
+        ('operations', 'Operations', (Ann.ATR_DATA,
+            Ann.CMD_DATA, Ann.OUT_DATA, Ann.PROC_DATA,)),
+    )
+    binary = (
+        ('bytes', 'Bytes'),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.samplerate = None
+        self.max_addr = 256
+        self.bits = []
+        self.atr_bytes = []
+        self.cmd_bytes = []
+        self.cmd_proc = None
+        self.out_len = None
+        self.out_bytes = []
+        self.proc_state = None
+        self.state = None
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_binary = self.register(srd.OUTPUT_BINARY)
+
+    def putx(self, ss, es, cls, data):
+        self.put(ss, es, self.out_ann, [cls, data,])
+
+    def putb(self, ss, es, cls , data):
+        self.put(ss, es, self.out_binary, [cls, data,])
+
+    def snums_to_usecs(self, snum_count):
+        if not self.samplerate:
+            return None
+        snums_per_usec = self.samplerate / 1e6
+        usecs = snum_count / snums_per_usec
+        return usecs
+
+    def lookup_proto_ann_txt(self, key, variables):
+        ann = {
+            'RESET_SYM': [Ann.RESET_SYM, 'Reset', 'R',],
+            'INTR_SYM': [Ann.INTR_SYM, 'Interrupt', 'Intr', 'I',],
+            'START_SYM': [Ann.START_SYM, 'Start', 'ST', 'S',],
+            'STOP_SYM': [Ann.STOP_SYM, 'Stop', 'SP', 'P',],
+            'BIT_SYM': [Ann.BIT_SYM, '{bit}',],
+            'ATR_BYTE': [Ann.ATR_BYTE,
+                'Answer To Reset: {data:02x}',
+                'ATR: {data:02x}',
+                '{data:02x}',
+            ],
+            'CMD_BYTE': [Ann.CMD_BYTE,
+                'Command: {data:02x}',
+                'Cmd: {data:02x}',
+                '{data:02x}',
+            ],
+            'OUT_BYTE': [Ann.OUT_BYTE,
+                'Outgoing data: {data:02x}',
+                'Data: {data:02x}',
+                '{data:02x}',
+            ],
+            'PROC_BYTE': [Ann.PROC_BYTE,
+                'Internal processing: {data:02x}',
+                'Proc: {data:02x}',
+                '{data:02x}',
+            ],
+            'ATR_DATA': [Ann.ATR_DATA,
+                'Answer To Reset: {data}',
+                'ATR: {data}',
+                '{data}',
+            ],
+            'CMD_DATA': [Ann.CMD_DATA,
+                'Command: {data}',
+                'Cmd: {data}',
+                '{data}',
+            ],
+            'OUT_DATA': [Ann.OUT_DATA,
+                'Outgoing: {data}',
+                'Out: {data}',
+                '{data}',
+            ],
+            'PROC_DATA': [Ann.PROC_DATA,
+                'Processing: {data}',
+                'Proc: {data}',
+                '{data}',
+            ],
+        }.get(key, None)
+        if ann is None:
+            return None, []
+        cls, texts = ann[0], ann[1:]
+        texts = [t.format(**variables) for t in texts]
+        return cls, texts
+
+    def text_for_accu_bytes(self, accu):
+        if not accu:
+            return None, None, None, None
+        ss, es = accu[0][1], accu[-1][2]
+        data = [a[0] for a in accu]
+        text = " ".join(['{:02x}'.format(a) for a in data])
+        return ss, es, data, text
+
+    def flush_queued(self):
+        '''Flush previously accumulated operations details.'''
+
+        # Can be called when either the completion of an operation got
+        # detected (reliably), or when some kind of reset condition was
+        # met while a potential previously observed operation has not
+        # been postprocessed yet (best effort). Should not harm when the
+        # routine gets invoked while no data was collected yet, or was
+        # flushed already.
+        # BEWARE! Will void internal state. Should really only get called
+        # "between operations", NOT between fields of an operation.
+
+        if self.atr_bytes:
+            key = 'ATR_DATA'
+            ss, es, _, text = self.text_for_accu_bytes(self.atr_bytes)
+            cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+            self.putx(ss, es, cls, texts)
+
+        if self.cmd_bytes:
+            key = 'CMD_DATA'
+            ss, es, _, text = self.text_for_accu_bytes(self.cmd_bytes)
+            cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+            self.putx(ss, es, cls, texts)
+
+        if self.out_bytes:
+            key = 'OUT_DATA'
+            ss, es, _, text = self.text_for_accu_bytes(self.out_bytes)
+            cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+            self.putx(ss, es, cls, texts)
+
+        if self.proc_state:
+            key = 'PROC_DATA'
+            ss = self.proc_state['ss']
+            es = self.proc_state['es']
+            clk = self.proc_state['clk']
+            high = self.proc_state['io1']
+            text = '{clk} clocks, I/O {high}'.format(clk = clk, high = int(high))
+            usecs = self.snums_to_usecs(es - ss)
+            if usecs:
+                msecs = usecs / 1000
+                text = '{msecs:.2f} ms, {text}'.format(msecs = msecs, text = text)
+            cls, texts = self.lookup_proto_ann_txt(key, {'data': text})
+            self.putx(ss, es, cls, texts)
+
+        self.atr_bytes = None
+        self.cmd_bytes = None
+        self.cmd_proc = None
+        self.out_len = None
+        self.out_bytes = None
+        self.proc_state = None
+        self.state = None
+
+    def handle_reset(self, ss, es, has_clk):
+        self.flush_queued()
+        key = '{}_SYM'.format('RESET' if has_clk else 'INTR')
+        cls, texts = self.lookup_proto_ann_txt(key, {})
+        self.putx(ss, es, cls, texts)
+        self.bits = []
+        self.state = 'ATR' if has_clk else None
+
+    def handle_command(self, ss, is_start):
+        if is_start:
+            self.flush_queued()
+        key = '{}_SYM'.format('START' if is_start else 'STOP')
+        cls, texts = self.lookup_proto_ann_txt(key, {})
+        self.putx(ss, ss, cls, texts)
+        self.bits = []
+        self.state = 'CMD' if is_start else 'DATA'
+
+    def command_check(self, ctrl, addr, data):
+        '''Interpret CTRL/ADDR/DATA command entry.'''
+
+        # See the Siemens Datasheet section 2.3 Commands. The abbreviated
+        # text variants are my guesses, terse for readability at coarser
+        # zoom levels.
+        codes_table = {
+            0x30: {
+                'fmt': [
+                    'read main memory, addr {addr:02x}',
+                    'RD-M @{addr:02x}',
+                ],
+                'len': lambda ctrl, addr, data: self.max_addr - addr,
+            },
+            0x31: {
+                'fmt': [
+                    'read security memory',
+                    'RD-S',
+                ],
+                'len': 4,
+            },
+            0x33: {
+                'fmt': [
+                    'compare verification data, addr {addr:02x}, data {data:02x}',
+                    'CMP-V @{addr:02x} ={data:02x}',
+                ],
+                'proc': True,
+            },
+            0x34: {
+                'fmt': [
+                    'read protection memory, addr {addr:02x}',
+                    'RD-P @{addr:02x}',
+                ],
+                'len': 4,
+            },
+            0x38: {
+                'fmt': [
+                    'update main memory, addr {addr:02x}, data {data:02x}',
+                    'WR-M @{addr:02x} ={data:02x}',
+                ],
+                'proc': True,
+            },
+            0x39: {
+                'fmt': [
+                    'update security memory, addr {addr:02x}, data {data:02x}',
+                    'WR-S @{addr:02x} ={data:02x}',
+                ],
+                'proc': True,
+            },
+            0x3c: {
+                'fmt': [
+                    'write protection memory, addr {addr:02x}, data {data:02x}',
+                    'WR-P @{addr:02x} ={data:02x}',
+                ],
+                'proc': True,
+            },
+        }
+        code = codes_table.get(ctrl, {})
+        dflt_fmt = [
+            'unknown, ctrl {ctrl:02x}, addr {addr:02x}, data {data:02x}',
+            'UNK-{ctrl:02x} @{addr:02x}, ={data:02x}',
+        ]
+        fmt = code.get('fmt', dflt_fmt)
+        if not isinstance(fmt, (list, tuple,)):
+            fmt = [fmt,]
+        texts = [f.format(ctrl = ctrl, addr = addr, data = data) for f in fmt]
+        length = code.get('len', None)
+        if callable(length):
+            length = length(ctrl, addr, data)
+        is_proc = code.get('proc', False)
+        return texts, length, is_proc
+
+    def processing_start(self, ss, es, io_high):
+        self.proc_state = {
+            'ss': ss or es,
+            'es': es or ss,
+            'clk': 0,
+            'io1': bool(io_high),
+        }
+
+    def processing_update(self, es, clk_inc, io_high):
+        if es is not None and es > self.proc_state['es']:
+            self.proc_state['es'] = es
+        self.proc_state['clk'] += clk_inc
+        if io_high:
+            self.proc_state['io1'] = True
+
+    def handle_data_byte(self, ss, es, data, bits):
+        '''Accumulate CMD or OUT data bytes.'''
+
+        if self.state == 'ATR':
+            if not self.atr_bytes:
+                self.atr_bytes = []
+            self.atr_bytes.append([data, ss, es, bits,])
+            if len(self.atr_bytes) == 4:
+                self.flush_queued()
+            return
+
+        if self.state == 'CMD':
+            if not self.cmd_bytes:
+                self.cmd_bytes = []
+            self.cmd_bytes.append([data, ss, es, bits,])
+            if len(self.cmd_bytes) == 3:
+                ctrl, addr, data = [c[0] for c in self.cmd_bytes]
+                texts, length, proc = self.command_check(ctrl, addr, data)
+                # Immediately emit the annotation to not lose the text,
+                # and to support zoom levels for this specific case.
+                ss, es = self.cmd_bytes[0][1], self.cmd_bytes[-1][2]
+                cls = Ann.CMD_DATA
+                self.putx(ss, es, cls, texts)
+                self.cmd_bytes = []
+                # Prepare to continue either at OUT or PROC after CMD.
+                self.out_len = length
+                self.cmd_proc = bool(proc)
+                self.state = None
+            return
+
+        if self.state == 'OUT':
+            if not self.out_bytes:
+                self.out_bytes = []
+            self.out_bytes.append([data, ss, es, bits,])
+            if self.out_len is not None and len(self.out_bytes) == self.out_len:
+                self.flush_queued()
+            return
+
+    def handle_data_bit(self, ss, es, bit):
+        '''Gather 8 bits of data (or track processing progress).'''
+
+        # Switch late from DATA to either OUT or PROC. We can tell the
+        # type and potentially fixed length at the end of CMD already,
+        # but a START/STOP condition may void this information. So we
+        # do the switch at the first data bit after CMD.
+        # In the OUT case data bytes get accumulated, until either the
+        # expected byte count is reached, or another CMD starts. In the
+        # PROC case a high I/O level terminates execution.
+        if self.state == 'DATA':
+            if self.out_len:
+                self.state = 'OUT'
+            elif self.cmd_proc:
+                self.state = 'PROC'
+                self.processing_start(ss or es, es or ss, bit == 1)
+            else:
+                # Implementor's note: Handle unknown situations like
+                # outgoing data bytes, for the user's convenience. This
+                # will show OUT bytes even if it's just processing CLK
+                # cycles with constant or irrelevant I/O bit patterns.
+                self.state = 'OUT'
+        if self.state == 'PROC':
+            high = bit == 1
+            if ss is not None:
+                self.processing_update(ss, 0, high)
+            if es is not None:
+                self.processing_update(es, 1, high)
+            if high:
+                self.flush_queued()
+            return
+
+        # This routine gets called two times per bit value. Track the
+        # bit's value and ss timestamp when the bit period starts. And
+        # update the es timestamp at the end of the bit's validity.
+        if ss is not None:
+            self.bits.append([bit, ss, es or ss])
+            return
+        if es is None:
+            # Unexpected invocation. Could be a glitch or invalid input
+            # data, or an interaction with RESET/START/STOP conditions.
+            self.bits = []
+            return
+        if not self.bits:
+            return
+        if bit is not None:
+            self.bits[-1][0] = bit
+            # TODO Check for consistent bit level at ss and es when
+            # the information was available? Is bit data sampled at
+            # different clock edges depending whether data is sent
+            # or received?
+        self.bits[-1][2] = es
+        # Emit the bit's annotation. See if a byte was received.
+        bit, ss, es = self.bits[-1]
+        cls, texts = self.lookup_proto_ann_txt('BIT_SYM', {'bit': bit})
+        self.putx(ss, es, cls, texts)
+        if len(self.bits) < 8:
+            return
+
+        # Get the data byte value, and the byte's ss/es. Emit the byte's
+        # annotation and binary output. Pass the byte to upper layers.
+        # TODO Vary annotation classes with the byte's position within
+        # a field? To tell CTRL/ADDR/DATA of a CMD entry apart?
+        bits = self.bits
+        self.bits = []
+        data = bitpack_lsb(bits, 0)
+        ss = bits[0][1]
+        es = bits[-1][2]
+
+        key = '{}_BYTE'.format(self.state)
+        cls, texts = self.lookup_proto_ann_txt(key, {'data': data})
+        if cls:
+            self.putx(ss, es, cls, texts)
+        self.putb(ss, es, Bin.BYTES, bytes([data]))
+
+        self.handle_data_byte(ss, es, data, bits)
+
+    def decode(self):
+        '''Decoder's main data interpretation loop.'''
+
+        # Signal conditions tracked by the protocol decoder:
+        # - Rising and falling RST edges, which span the width of a
+        #   high-active RESET pulse. RST has highest priority, no
+        #   other activity can take place in this period.
+        # - Rising and falling CLK edges when RST is active. The
+        #   CLK pulse when RST is asserted will reset the card's
+        #   address counter. RST alone can terminate memory reads.
+        # - Rising and falling CLK edges when RST is inactive. This
+        #   determines the period where BIT values are valid.
+        # - I/O edges during high CLK. These are START and STOP
+        #   conditions that tell COMMAND and DATA phases apart.
+        # - Rise of I/O during internal processing. This expression
+        #   is an unconditional part of the .wait() condition set. It
+        #   is assumed that skipping this match in many cases is more
+        #   efficient than the permanent re-construction of the .wait()
+        #   condition list in every loop iteration, and preferrable to
+        #   the maintainance cost of duplicating RST and CLK handling
+        #   when checking I/O during internal processing.
+        (
+            COND_RESET_START, COND_RESET_STOP,
+            COND_RSTCLK_START, COND_RSTCLK_STOP,
+            COND_DATA_START, COND_DATA_STOP,
+            COND_CMD_START, COND_CMD_STOP,
+            COND_PROC_IOH,
+        ) = range(9)
+        conditions = [
+            {Pin.RST: 'r'},
+            {Pin.RST: 'f'},
+            {Pin.RST: 'h', Pin.CLK: 'r'},
+            {Pin.RST: 'h', Pin.CLK: 'f'},
+            {Pin.RST: 'l', Pin.CLK: 'r'},
+            {Pin.RST: 'l', Pin.CLK: 'f'},
+            {Pin.CLK: 'h', Pin.IO: 'f'},
+            {Pin.CLK: 'h', Pin.IO: 'r'},
+            {Pin.RST: 'l', Pin.IO: 'r'},
+        ]
+
+        ss_reset = es_reset = ss_clk = es_clk = None
+        while True:
+
+            is_outgoing = self.state == 'OUT'
+            is_processing = self.state == 'PROC'
+            pins = self.wait(conditions)
+            io = pins[Pin.IO]
+
+            # Handle RESET conditions, including an optional CLK pulse
+            # while RST is asserted.
+            if self.matched[COND_RESET_START]:
+                self.flush_queued()
+                ss_reset = self.samplenum
+                es_reset = ss_clk = es_clk = None
+                continue
+            if self.matched[COND_RESET_STOP]:
+                es_reset = self.samplenum
+                self.handle_reset(ss_reset or 0, es_reset, ss_clk and es_clk)
+                ss_reset = es_reset = ss_clk = es_clk = None
+                continue
+            if self.matched[COND_RSTCLK_START]:
+                ss_clk = self.samplenum
+                es_clk = None
+                continue
+            if self.matched[COND_RSTCLK_STOP]:
+                es_clk = self.samplenum
+                continue
+
+            # Handle data bits' validity boundaries. Also covers the
+            # periodic check for high I/O level and update of details
+            # during internal processing.
+            if self.matched[COND_DATA_START]:
+                self.handle_data_bit(self.samplenum, None, io)
+                continue
+            if self.matched[COND_DATA_STOP]:
+                self.handle_data_bit(None, self.samplenum, None)
+                continue
+
+            # Additional check for idle I/O during internal processing,
+            # independent of CLK edges this time. This assures that the
+            # decoder ends processing intervals as soon as possible, at
+            # the most precise timestamp.
+            if is_processing and self.matched[COND_PROC_IOH]:
+                self.handle_data_bit(self.samplenum, self.samplenum, io)
+                continue
+
+            # The START/STOP conditions are only applicable outside of
+            # "outgoing data" or "internal processing" periods. This is
+            # what the data sheet specifies.
+            if not is_outgoing and not is_processing:
+                if self.matched[COND_CMD_START]:
+                    self.handle_command(self.samplenum, True)
+                    continue
+                if self.matched[COND_CMD_STOP]:
+                    self.handle_command(self.samplenum, False)
+                    continue
index 126a0274b5087a45214dccf504741c74191cca6b..1b7f6e92a7cf196f3e1e5edc4953d8c32d997f4c 100644 (file)
@@ -77,6 +77,14 @@ class Decoder(srd.Decoder):
         self.seen_preamble = False
         self.last_preamble = 0
 
+        self.bitrate_message_start = 0
+        self.bitrate_message_end = 0
+        self.frame_counter = 0
+        self.frame_start = 0
+        self.frame_length = 0
+
+        self.sampleratetmp = 1
+
         self.first_one = True
         self.subframe = []
 
@@ -88,8 +96,6 @@ class Decoder(srd.Decoder):
             self.samplerate = value
 
     def get_pulse_type(self):
-        if self.range1 == 0 or self.range2 == 0:
-            return -1
         if self.pulse_width >= self.range2:
             return 2
         elif self.pulse_width >= self.range1:
@@ -101,32 +107,54 @@ class Decoder(srd.Decoder):
         if self.pulse_width != 0:
             self.clocks.append(self.pulse_width)
             self.state = 'GET SECOND PULSE WIDTH'
+            self.puty([2, ['Found width 1: %d' % self.pulse_width, 'W1: %d' % self.pulse_width]])
+            self.ss_edge = self.samplenum
 
     def find_second_pulse_width(self):
         if self.pulse_width > (self.clocks[0] * 1.3) or \
-                self.pulse_width < (self.clocks[0] * 0.7):
+                self.pulse_width <= (self.clocks[0] * 0.75):
+            self.puty([2, ['Found width 2: %d' % self.pulse_width, 'W2: %d' % self.pulse_width]])
             self.clocks.append(self.pulse_width)
             self.state = 'GET THIRD PULSE WIDTH'
+        else:
+            self.puty([2, ['Search width 2: %d' % self.pulse_width, 'SW2: %d' % self.pulse_width]])
+        self.ss_edge = self.samplenum
 
     def find_third_pulse_width(self):
         if not ((self.pulse_width > (self.clocks[0] * 1.3) or \
-                self.pulse_width < (self.clocks[0] * 0.7)) \
+                self.pulse_width <= (self.clocks[0] * 0.75)) \
                 and (self.pulse_width > (self.clocks[1] * 1.3) or \
-                self.pulse_width < (self.clocks[1] * 0.7))):
+                self.pulse_width <= (self.clocks[1] * 0.75))):
+            self.puty([2, ['Search width 3: %d' % self.pulse_width, 'SW3: %d' % self.pulse_width]])
+            self.ss_edge = self.samplenum
             return
+        else:
+            self.puty([2, ['Found width 3: %d' % self.pulse_width, 'W3: %d' % self.pulse_width]])
+            self.ss_edge = self.samplenum
+            # The message of the calculated bitrate should start at this sample
+            # (right after the synchronisation).
+            self.bitrate_message_start = self.samplenum
 
         self.clocks.append(self.pulse_width)
         self.clocks.sort()
         self.range1 = (self.clocks[0] + self.clocks[1]) / 2
         self.range2 = (self.clocks[1] + self.clocks[2]) / 2
-        spdif_bitrate = int(self.samplerate / (self.clocks[2] / 1.5))
+        # Give some feedback during synchronisation and inform if sample rate
+        # is too low.
+        if self.clocks[0] <= 3:
+            self.putx(0, self.samplenum, [0, ['Short pulses detected. Increase sample rate!']])
+            raise SamplerateError('Short pulses detected')
+        else:
+            self.putx(0, self.samplenum, [0, ['Synchronisation']])
         self.ss_edge = 0
 
-        self.puty([0, ['Signal Bitrate: %d Mbit/s (=> %d kHz)' % \
-                  (spdif_bitrate, (spdif_bitrate/ (2 * 32)))]])
-
-        clock_period_nsec = 1000000000 / spdif_bitrate
+        # Mostly, the synchronisation ends with a long pulse because they
+        # appear rarely. A skip of the next pulse will then prevent a 'M'
+        # frame to be labeled an unknown preamble for the first decoded frame.
+        (data,) = self.wait({0: 'e'})
 
+        self.pulse_width = self.samplenum - self.samplenum_prev_edge
+        self.samplenum_prev_edge = self.samplenum
         self.last_preamble = self.samplenum
 
         # We are done recovering the clock, now let's decode the data stream.
@@ -140,24 +168,39 @@ class Decoder(srd.Decoder):
             if pulse == 2:
                 self.preamble.append(self.get_pulse_type())
                 self.state = 'DECODE PREAMBLE'
-                self.ss_edge = self.samplenum - self.pulse_width - 1
+                self.ss_edge = self.samplenum - self.pulse_width
+                # Use the first ten frames to calculate bit rates
+                if self.frame_counter == 0:
+                    # This is the first preamble to be decoded. Measurement of
+                    # bit rates starts here.
+                    self.frame_start = self.samplenum
+                    # The bit rate message should end here.
+                    self.bitrate_message_end = self.ss_edge
+                elif self.frame_counter == 10:
+                    self.frame_length = self.samplenum - self.frame_start
+                    # Use section between end of synchronisation and start of
+                    # first preamble to show measured bit rates.
+                    if self.samplerate:
+                        self.putx(self.bitrate_message_start, self.bitrate_message_end,\
+                            [0, ['Audio samplingrate: %6.2f kHz; Bit rate: %6.3f MBit/s' %\
+                            ((self.samplerate / 200 / self.frame_length), (self.samplerate / 200 * 64 / 1000 / self.frame_length))]])
+                    else:
+                        self.putx(self.bitrate_message_start, self.bitrate_message_end, [0, ['No sample rate given']])
+                self.frame_counter += 1
             return
 
         # We've seen a preamble.
         if pulse == 1 and self.first_one:
             self.first_one = False
-            self.subframe.append([pulse, self.samplenum - \
-                self.pulse_width - 1, self.samplenum])
+            self.subframe.append([pulse, self.samplenum - self.pulse_width, self.samplenum])
         elif pulse == 1 and not self.first_one:
             self.subframe[-1][2] = self.samplenum
             self.putx(self.subframe[-1][1], self.samplenum, [2, ['1']])
             self.bitcount += 1
             self.first_one = True
         else:
-            self.subframe.append([pulse, self.samplenum - \
-                self.pulse_width - 1, self.samplenum])
-            self.putx(self.samplenum - self.pulse_width - 1,
-                      self.samplenum, [2, ['0']])
+            self.subframe.append([pulse, self.samplenum - self.pulse_width, self.samplenum])
+            self.putx(self.samplenum - self.pulse_width, self.samplenum, [2, ['0']])
             self.bitcount += 1
 
         if self.bitcount == 28:
@@ -222,16 +265,22 @@ class Decoder(srd.Decoder):
         self.last_preamble = self.samplenum
 
     def decode(self):
+        # Set samplerate to 0 if it is not given. Decoding is still possible.
         if not self.samplerate:
-            raise SamplerateError('Cannot decode without samplerate.')
+            self.samplerate = 0
 
-        # Throw away first detected edge as it might be mangled data.
+        # Throw away first two edges as it might be mangled data.
         self.wait({0: 'e'})
+        self.wait({0: 'e'})
+        self.ss_edge = 0
+        self.puty([2, ['Skip']])
+        self.ss_edge = self.samplenum
+        self.samplenum_prev_edge = self.samplenum
 
         while True:
             # Wait for any edge (rising or falling).
             (data,) = self.wait({0: 'e'})
-            self.pulse_width = self.samplenum - self.samplenum_prev_edge - 1
+            self.pulse_width = self.samplenum - self.samplenum_prev_edge
             self.samplenum_prev_edge = self.samplenum
 
             if self.state == 'GET FIRST PULSE WIDTH':
index 80ca27d7abf1111b31071a386a06a6c719ac1b5d..e31daf75088f35bf735d403a44fbe8e8a3c635db 100644 (file)
@@ -60,6 +60,7 @@ device_name = {
         0x15: 'FM25Q32',
     },
     'macronix': {
+        0x13: 'MX25L8006',
         0x14: 'MX25L1605D',
         0x15: 'MX25L3205D',
         0x16: 'MX25L6405D',
@@ -151,6 +152,17 @@ chips = {
         'sector_size': 4 * 1024,
         'block_size': 64 * 1024,
     },
+    'macronix_mx25l8006': {
+        'vendor': 'Macronix',
+        'model': 'MX25L8006',
+        'res_id': 0x13,
+        'rems_id': 0xc213,
+        'rems2_id': 0xc213,
+        'rdid_id': 0xc22013,
+        'page_size': 256,
+        'sector_size': 4 * 1024,
+        'block_size': 64 * 1024,
+    },
     # Winbond
     'winbond_w25q80dv': {
         'vendor': 'Winbond',
diff --git a/decoders/st25r39xx_spi/__init__.py b/decoders/st25r39xx_spi/__init__.py
new file mode 100644 (file)
index 0000000..3803789
--- /dev/null
@@ -0,0 +1,31 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## 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/>.
+##
+
+'''
+This decoder stacks on top of the SPI PD and decodes the st25r39xx High performance NFC universal device and EMVCo reader protocol (SPI mode).
+
+It has been successfully tested with the st25r3916, other chips of this family may or may not be fully supported but not been verified.
+
+Please note that the SPI interface uses clock polarity 0 and clock phase 1, which is not the default setting.
+
+Details:
+https://www.st.com/resource/en/datasheet/st25r3916.pdf
+'''
+
+from .pd import Decoder
diff --git a/decoders/st25r39xx_spi/lists.py b/decoders/st25r39xx_spi/lists.py
new file mode 100644 (file)
index 0000000..1429765
--- /dev/null
@@ -0,0 +1,231 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2020 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## 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/>.
+##
+## v0.1 - 17 September 2019 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 1 (January 2019)
+## v0.2 - 28 April 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 2 (December 2019) https://www.st.com/resource/en/datasheet/st25r3916.pdf
+## v0.3 - 17 June 2020 B.VERNOUX using ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020) https://www.st.com/resource/en/datasheet/st25r3916.pdf
+
+## ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020) Â§4.4 Direct commands
+dir_cmd = {
+#   addr: 'name'
+# Set Default
+    0xC0: 'SET_DEFAULT',
+    0xC1: 'SET_DEFAULT',
+# Stop All Activities
+    0xC2: 'STOP',
+    0xC3: 'STOP',
+# Transmit With CRC
+    0xC4: 'TXCRC',
+# Transmit Without CRC
+    0xC5: 'TXNOCRC',
+# Transmit REQA
+    0xC6: 'TXREQA',
+# Transmit WUPA
+    0xC7: 'TXWUPA',
+# NFC Initial Field ON
+    0xC8: 'NFCINITFON',
+# NFC Response Field ON
+    0xC9: 'NFCRESFON',
+# Go to Sense (Idle)
+    0xCD: 'GOIDLE',
+# Go to Sleep (Halt)
+    0xCE: 'GOHALT',
+# Mask Receive Data / Stops receivers and RX decoders
+    0xD0: 'STOPRX',
+# Unmask Receive Data / Starts receivers and RX decoders
+    0xD1: 'STARRX',
+# Change AM Modulation state
+    0xD2: 'SETAMSTATE',
+# Measure Amplitude
+    0xD3: 'MAMP',
+# Reset RX Gain
+    0xD5: 'RSTRXGAIN',
+# Adjust Regulators
+    0xD6: 'ADJREG',
+# Calibrate Driver Timing
+    0xD8: 'CALDRVTIM',
+# Measure Phase
+    0xD9: 'MPHASE',
+# Clear RSSI
+    0xDA: 'CLRRSSI',
+# Clear FIFO
+    0xDB: 'CLRFIFO',
+# Enter Transparent Mode
+    0xDC: 'TRMODE',
+# Calibrate Capacitive Sensor
+    0xDD: 'CALCAPA',
+# Measure Capacitance
+    0xDE: 'MCAPA',
+# Measure Power Supply
+    0xDF: 'MPOWER',
+# Start General Purpose Timer
+    0xE0: 'STARGPTIM',
+# Start Wake-up Timer
+    0xE1: 'STARWTIM',
+# Start Mask-receive Timer
+    0xE2: 'STARMSKTIM',
+# Start No-response Timer
+    0xE3: 'STARNRESPTIM',
+# Start PPON2 Timer
+    0xE4: 'STARPPON2TIM',
+# Stop No-response Timer
+    0xE8: 'STOPNRESTIM',
+# RFU / Not Used
+    0xFA: 'RFU',
+# Register Space-B Access
+    0xFB: 'REGSPACEB',
+# Register Test access
+    0xFC: 'TESTACCESS'
+# Other codes => RFU / Not Used
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) Â§4.5 Registers Table 17. List of registers - Space A
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) Â§4.3.3 Serial peripheral interface (SPI) Table 11. SPI operation modes
+regsSpaceA = {
+#   addr: 'name'
+# Â§4.5 Registers Table 17. List of registers - Space A
+# IO configuration
+    0x00: 'IOCFG1',
+    0x01: 'IOCFG2',
+# Operation control and mode definition
+    0x02: 'OPCTRL',
+    0x03: 'MODEDEF',
+    0x04: 'BITRATE',
+# Protocol configuration
+    0x05: 'TYPEA',
+    0x06: 'TYPEB',
+    0x07: 'TYPEBF',
+    0x08: 'NFCIP1',
+    0x09: 'STREAM',
+    0x0A: 'AUX',
+# Receiver configuration
+    0x0B: 'RXCFG1',
+    0x0C: 'RXCFG2',
+    0x0D: 'RXCFG3',
+    0x0E: 'RXCFG4',
+# Timer definition
+    0x0F: 'MSKRXTIM',
+    0x10: 'NRESPTIM1',
+    0x11: 'NRESPTIM2',
+    0x12: 'TIMEMV',
+    0x13: 'GPTIM1',
+    0x14: 'GPTIM2',
+    0x15: 'PPON2',
+# Interrupt and associated reporting
+    0x16: 'MSKMAINIRQ',
+    0x17: 'MSKTIMNFCIRQ',
+    0x18: 'MSKERRWAKEIRQ',
+    0x19: 'TARGIRQ',
+    0x1A: 'MAINIRQ',
+    0x1B: 'TIMNFCIRQ',
+    0x1C: 'ERRWAKEIRQ',
+    0x1D: 'TARGIRQ',
+    0x1E: 'FIFOSTAT1',
+    0x1F: 'FIFOSTAT2',
+    0x20: 'COLLDISP',
+    0x21: 'TARGDISP',
+# Definition of number of transmitted bytes
+    0x22: 'NBTXB1',
+    0x23: 'NBTXB2',
+    0x24: 'BITRATEDET',
+# A/D converter output
+    0x25: 'ADCONVOUT',
+# Antenna calibration
+    0x26: 'ANTTUNECTRL1',
+    0x27: 'ANTTUNECTRL2',
+# Antenna driver and modulation
+    0x28: 'TXDRV',
+    0x29: 'TARGMOD',
+# External field detector threshold
+    0x2A: 'EXTFIELDON',
+    0x2B: 'EXTFIELDOFF',
+# Regulator
+    0x2C: 'REGVDDCTRL',
+# Receiver state display
+    0x2D: 'RSSIDISP',
+    0x2E: 'GAINSTATE',
+# Capacitive sensor
+    0x2F: 'CAPACTRL',
+    0x30: 'CAPADISP',
+# Auxiliary display
+    0x31: 'AUXDISP',
+# Wake-up
+    0x32: 'WAKETIMCTRL',
+    0x33: 'AMPCFG',
+    0x34: 'AMPREF',
+    0x35: 'AMPAAVGDISP',
+    0x36: 'AMPDISP',
+    0x37: 'PHASECFG',
+    0x38: 'PHASEREF',
+    0x39: 'PHASEAAVGDISP',
+    0x3A: 'PHASEDISP',
+    0x3B: 'CAPACFG',
+    0x3C: 'CAPAREF',
+    0x3D: 'CAPAAAVGDISP',
+    0x3E: 'CAPADISP',
+# IC identity
+    0x3F: 'ICIDENT',
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) Â§4.3.3 Serial peripheral interface (SPI) Table 11. SPI operation modes
+    0xA0: 'PT_memLoadA',
+    0xA8: 'PT_memLoadF',
+    0xAC: 'PT_memLoadTSN',
+    0xBF: 'PT_memRead'
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) Â§4.5 Registers Table 18. List of registers - Space B
+regsSpaceB = {
+#   addr: 'name'
+# Â§4.5 Registers Table 18. List of registers - Space B
+# Protocol configuration
+    0x05: 'EMDSUPPRCONF',
+    0x06: 'SUBCSTARTIM',
+# Receiver configuration
+    0x0B: 'P2PRXCONF',
+    0x0C: 'CORRCONF1',
+    0x0D: 'CORRCONF2',
+# Timer definition
+    0x0F: 'SQUELSHTIM',
+    0x15: 'NFCGUARDTIM',
+# Antenna driver and modulation
+    0x28: 'AUXMODSET',
+    0x29: 'TXDRVTIM',
+# External field detector threshold
+    0x2A: 'RESAMMODE',
+    0x2B: 'TXDRVTIMDISP',
+# Regulator
+    0x2C: 'REGDISP',
+# Protection
+    0x30: 'OSHOOTCONF1',
+    0x31: 'OSHOOTCONF2',
+    0x32: 'USHOOTCONF1',
+    0x33: 'USHOOTCONF2'
+}
+
+## ST25R3916 Datasheet DS12484 Rev 2 (December 2019) Â§4.4.17 Test access
+regsTest = {
+#   addr: 'name'
+# Â§4.4.17 Test access (Typo in datasheet it is not register 0x00 but 0x01)
+    0x01: 'ANTSTOBS'
+}
+
+## Optional TODO add important status bit fields / ANN_STATUS
+## Interrupt and associated reporting => Registers Space A from Address (hex) 0x16 to 0x21
+## Â§4.5.58 RSSI display register
+## Â§4.5.59 Gain reduction state register
+## ...
+
diff --git a/decoders/st25r39xx_spi/pd.py b/decoders/st25r39xx_spi/pd.py
new file mode 100644 (file)
index 0000000..a6f55b9
--- /dev/null
@@ -0,0 +1,360 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019-2021 Benjamin Vernoux <bvernoux@gmail.com>
+##
+## 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/>.
+##
+## v0.1 - 17 September 2019 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 1 (January 2019)
+## v0.2 - 28 April 2020 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 2 (December 2019)
+## v0.3 - 17 June 2020 B.VERNOUX
+### Use ST25R3916 Datasheet DS12484 Rev 3 (04 June 2020)
+## v0.4 - 10 Aug 2021 B.VERNOUX
+### Fix FIFOR/FIFOW issues with Pulseview (with "Tabular Output View")
+### because of FIFO Read/FIFO Write commands, was not returning the
+### annotations short name FIFOR/FIFOW
+
+import sigrokdecode as srd
+from collections import namedtuple
+from common.srdhelper import SrdIntEnum
+from .lists import *
+
+Ann = SrdIntEnum.from_str('Ann', 'BURST_READ BURST_WRITE \
+    BURST_READB BURST_WRITEB BURST_READT BURST_WRITET \
+    DIRECTCMD FIFO_WRITE FIFO_READ STATUS WARN')
+
+Pos = namedtuple('Pos', ['ss', 'es'])
+Data = namedtuple('Data', ['mosi', 'miso'])
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'st25r39xx_spi'
+    name = 'ST25R39xx (SPI mode)'
+    longname = 'STMicroelectronics ST25R39xx'
+    desc = 'High performance NFC universal device and EMVCo reader protocol.'
+    license = 'gplv2+'
+    inputs = ['spi']
+    outputs = []
+    tags = ['IC', 'Wireless/RF']
+    annotations = (
+        ('Read', 'Burst register read'),
+        ('Write', 'Burst register write'),
+        ('ReadB', 'Burst register SpaceB read'),
+        ('WriteB', 'Burst register SpaceB write'),
+        ('ReadT', 'Burst register Test read'),
+        ('WriteT', 'Burst register Test write'),
+        ('Cmd', 'Direct command'),
+        ('FIFOW', 'FIFO write'),
+        ('FIFOR', 'FIFO read'),
+        ('status_reg', 'Status register'),
+        ('warning', 'Warning'),
+    )
+    annotation_rows = (
+        ('regs', 'Regs', (Ann.prefixes('BURST_'))),
+        ('cmds', 'Commands', (Ann.DIRECTCMD,)),
+        ('data', 'Data', (Ann.prefixes('FIFO_'))),
+        ('status', 'Status register', (Ann.STATUS,)),
+        ('warnings', 'Warnings', (Ann.WARN,)),
+    )
+
+    def __init__(self):
+        self.reset()
+
+    def reset(self):
+        self.next()
+        self.requirements_met = True
+        self.cs_was_released = False
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def warn(self, pos, msg):
+        '''Put a warning message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [Ann.WARN, [msg]])
+
+    def putp(self, pos, ann, msg):
+        '''Put an annotation message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [ann, [msg]])
+
+    def putp2(self, pos, ann, msg1, msg2):
+        '''Put an annotation message 'msg' at 'pos'.'''
+        self.put(pos.ss, pos.es, self.out_ann, [ann, [msg1, msg2]])
+
+    def next(self):
+        '''Resets the decoder after a complete command was decoded.'''
+        # 'True' for the first byte after CS# went low.
+        self.first = True
+
+        # The current command, and the minimum and maximum number
+        # of data bytes to follow.
+        self.cmd = None
+        self.min = 0
+        self.max = 0
+
+        # Used to collect the bytes after the command byte
+        # (and the start/end sample number).
+        self.mb = []
+        self.ss_mb = -1
+        self.es_mb = -1
+
+    def mosi_bytes(self):
+        '''Returns the collected MOSI bytes of a multi byte command.'''
+        return [b.mosi for b in self.mb]
+
+    def miso_bytes(self):
+        '''Returns the collected MISO bytes of a multi byte command.'''
+        return [b.miso for b in self.mb]
+
+    def decode_command(self, pos, b):
+        '''Decodes the command byte 'b' at position 'pos' and prepares
+        the decoding of the following data bytes.'''
+        c = self.parse_command(b)
+        if c is None:
+            self.warn(pos, 'Unknown command')
+            return
+
+        self.cmd, self.dat, self.min, self.max = c
+
+        if self.cmd == 'Cmd':
+            self.putp(pos, Ann.DIRECTCMD, self.format_command())
+        else:
+            # Don't output anything now, the command is merged with
+            # the data bytes following it.
+            self.ss_mb = pos.ss
+
+    def format_command(self):
+        '''Returns the label for the current command.'''
+        if self.cmd in ('Write', 'Read', 'WriteB', 'ReadB', 'WriteT', 'ReadT', 'FIFOW', 'FIFOR'):
+            return self.cmd
+        if self.cmd == 'Cmd':
+            reg = dir_cmd.get(self.dat, 'Unknown direct command')
+            return '{} {}'.format(self.cmd, reg)
+        else:
+            return 'TODO Cmd {}'.format(self.cmd)
+
+    def parse_command(self, b):
+        '''Parses the command byte.
+        Returns a tuple consisting of:
+        - the name of the command
+        - additional data needed to dissect the following bytes
+        - minimum number of following bytes
+        - maximum number of following bytes (None for infinite)
+        '''
+        addr = b & 0x3F
+        # previous command was 'Space B'
+        if self.cmd == 'Space B':
+            if (b & 0xC0) == 0x00:
+                return ('WriteB', addr, 1, 99999)
+            if (b & 0xC0) == 0x40:
+                return ('ReadB', addr, 1, 99999)
+            else:
+                self.warn(pos, 'Unknown address/command combination')
+        # previous command was 'TestAccess'
+        elif self.cmd == 'TestAccess':
+            if (b & 0xC0) == 0x00:
+                return ('WriteT', addr, 1, 99999)
+            if (b & 0xC0) == 0x40:
+                return ('ReadT', addr, 1, 99999)
+            else:
+                self.warn(pos, 'Unknown address/command combination')
+        else:
+            # Space A regs or other operation modes (except Space B)
+            # Register Write   0b00xxxxxx 0x00 to 0x3F => 'Write'
+            # Register Read    0b01xxxxxx 0x40 to 0x7F => 'Read'
+            if (b <= 0x7F):
+                if (b & 0xC0) == 0x00:
+                    return ('Write', addr, 1, 99999)
+                if (b & 0xC0) == 0x40:
+                    return ('Read', addr, 1, 99999)
+                else:
+                    self.warn(pos, 'Unknown address/command combination')
+            else:
+                # FIFO Load                 0b10000000 0x80 => 'FIFO Write'
+                # PT_memory loadA-config    0b10100000 0xA0 => 'Write'
+                # PT_memory loadF-config    0b10101000 0xA8 => 'Write'
+                # PT_memory loadTSN data    0b10101100 0xAC => 'Write'
+                # PT_memory Read            0b10111111 0xBF => 'Read'
+                # FIFO Read                 0b10011111 0x9F => 'FIFO Read'
+                # Direct Command            0b11xxx1xx 0xC0 to 0xE8 => 'Cmd'
+                # Register Space-B Access   0b11111011 0xFB => 'Space B'
+                # Register Test Access      0b11111100 0xFC => 'TestAccess'
+                if b == 0x80:
+                    return ('FIFOW', b, 1, 99999)
+                if b == 0xA0:
+                    return ('Write', b, 1, 99999)
+                if b == 0xA8:
+                    return ('Write', b, 1, 99999)
+                if b == 0xAC:
+                    return ('Write', b, 1, 99999)
+                if b == 0xBF:
+                    return ('Read', b, 1, 99999)
+                if b == 0x9F:
+                    return ('FIFOR', b, 1, 99999)
+                if (b >= 0x0C and b <= 0xE8) :
+                    return ('Cmd', b, 0, 0)
+                if b == 0xFB:
+                    return ('Space B', b, 0, 0)
+                if b == 0xFC:
+                    return ('TestAccess', b, 0, 0)
+                else:
+                    self.warn(pos, 'Unknown address/command combination')
+
+    def decode_reg(self, pos, ann, regid, data):
+        '''Decodes a register.
+        pos   -- start and end sample numbers of the register
+        ann   -- the annotation number that is used to output the register.
+        regid -- may be either an integer used as a key for the 'regs'
+                 dictionary, or a string directly containing a register name.'
+        data  -- the register content.
+        '''
+        if type(regid) == int:
+            if (ann == Ann.FIFO_READ) or (ann == Ann.FIFO_WRITE):
+                name = ''
+            elif (ann == Ann.BURST_READB) or (ann == Ann.BURST_WRITEB):
+                # Get the name of the register.
+                if regid not in regsSpaceB:
+                    self.warn(pos, 'Unknown register SpaceB')
+                    return
+                name = '{} ({:02X})'.format(regsSpaceB[regid], regid)
+            elif (ann == Ann.BURST_READT) or (ann == Ann.BURST_WRITET):
+                # Get the name of the register.
+                if regid not in regsTest:
+                    self.warn(pos, 'Unknown register Test')
+                    return
+                name = '{} ({:02X})'.format(regsTest[regid], regid)
+            else:
+                # Get the name of the register.
+                if regid not in regsSpaceA:
+                    self.warn(pos, 'Unknown register SpaceA')
+                    return
+                name = '{} ({:02X})'.format(regsSpaceA[regid], regid)
+        else:
+            name = regid
+
+        if regid == 'STATUS' and ann == Ann.STATUS:
+            label = 'Status'
+            self.decode_status_reg(pos, ann, data, label)
+        else:
+            label = '{}: {}'.format(self.format_command(), name)
+        self.decode_mb_data(pos, ann, data, label)
+
+    def decode_status_reg(self, pos, ann, data, label):
+        '''Decodes the data bytes 'data' of a status register at position
+        'pos'. The decoded data is prefixed with 'label'.'''
+
+    def decode_mb_data(self, pos, ann, data, label):
+        '''Decodes the data bytes 'data' of a multibyte command at position
+        'pos'. The decoded data is prefixed with 'label'.'''
+
+        def escape(b):
+            return '{:02X}'.format(b)
+
+        data = ' '.join([escape(b) for b in data])
+        if (ann == Ann.FIFO_WRITE) or (ann == Ann.FIFO_READ):
+            text = '{}{}'.format(label, data)
+        else:
+            text = '{} = {}'.format(label, data)
+        self.putp(pos, ann, text)
+
+    def finish_command(self, pos):
+        '''Decodes the remaining data bytes at position 'pos'.'''
+        if self.cmd == 'Write':
+            self.decode_reg(pos, Ann.BURST_WRITE, self.dat, self.mosi_bytes())
+        elif self.cmd == 'Read':
+            self.decode_reg(pos, Ann.BURST_READ, self.dat, self.miso_bytes())
+        elif self.cmd == 'WriteB':
+            self.decode_reg(pos, Ann.BURST_WRITEB, self.dat, self.mosi_bytes())
+        elif self.cmd == 'ReadB':
+            self.decode_reg(pos, Ann.BURST_READB, self.dat, self.miso_bytes())
+        elif self.cmd == 'WriteT':
+            self.decode_reg(pos, Ann.BURST_WRITET, self.dat, self.mosi_bytes())
+        elif self.cmd == 'ReadT':
+            self.decode_reg(pos, Ann.BURST_READT, self.dat, self.miso_bytes())
+        elif self.cmd == 'FIFOW':
+            self.decode_reg(pos, Ann.FIFO_WRITE, self.dat, self.mosi_bytes())
+        elif self.cmd == 'FIFOR':
+            self.decode_reg(pos, Ann.FIFO_READ, self.dat, self.miso_bytes())
+        elif self.cmd == 'Cmd':
+            self.decode_reg(pos, Ann.DIRECTCMD, self.dat, self.mosi_bytes())
+        else:
+            self.warn(pos, 'Unhandled command {}'.format(self.cmd))
+
+    def decode(self, ss, es, data):
+        if not self.requirements_met:
+            return
+
+        ptype, data1, data2 = data
+
+        if ptype == 'CS-CHANGE':
+            if data1 is None:
+                if data2 is None:
+                    self.requirements_met = False
+                    raise ChannelError('CS# pin required.')
+                elif data2 == 1:
+                    self.cs_was_released = True
+
+            if data1 == 0 and data2 == 1:
+                # Rising edge, the complete command is transmitted, process
+                # the bytes that were sent after the command byte.
+                if self.cmd:
+                    # Check if we got the minimum number of data bytes
+                    # after the command byte.
+                    if len(self.mb) < self.min:
+                        self.warn((ss, ss), 'Missing data bytes')
+                    elif self.mb:
+                        self.finish_command(Pos(self.ss_mb, self.es_mb))
+
+                self.next()
+                self.cs_was_released = True
+
+        elif ptype == 'DATA' and self.cs_was_released:
+            mosi, miso = data1, data2
+            pos = Pos(ss, es)
+
+            if miso is None or mosi is None:
+                self.requirements_met = False
+                raise ChannelError('Both MISO and MOSI pins are required.')
+
+            if self.first:
+                # Register Space-B Access   0b11111011 0xFB => 'Space B'
+                if mosi == 0xFB:
+                    self.first = True
+                    # First MOSI byte 'Space B' command.
+                    self.decode_command(pos, mosi)
+                    # First MISO byte is always the status register.
+                    #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+                # Register TestAccess Access   0b11111100 0xFC => 'TestAccess'
+                elif mosi == 0xFC:
+                    self.first = True
+                    # First MOSI byte 'TestAccess' command.
+                    self.decode_command(pos, mosi)
+                    # First MISO byte is always the status register.
+                    #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+                else:
+                    self.first = False
+                    # First MOSI byte is always the command.
+                    self.decode_command(pos, mosi)
+                    # First MISO byte is always the status register.
+                    #self.decode_reg(pos, ANN_STATUS, 'STATUS', [miso])
+            else:
+                if not self.cmd or len(self.mb) >= self.max:
+                    self.warn(pos, 'Excess byte')
+                else:
+                    # Collect the bytes after the command byte.
+                    if self.ss_mb == -1:
+                        self.ss_mb = ss
+                    self.es_mb = es
+                    self.mb.append(Data(mosi, miso))
index 626d2eb16d2c4d3ca3ebe3ba6ecba989b9a399ba..d938a2c52b8840b070401a740d2eb81a42c00698 100644 (file)
@@ -108,7 +108,7 @@ class Decoder(srd.Decoder):
     def put_desc(self, ss, es, cmd, data):
         if cmd == -1:
             return
-        if META[cmd]:
+        if cmd in META:
             self.put(ss, es, self.out_ann, [Ann.DESC,
                 ['%s: %s' % (META[cmd]['name'].strip(), META[cmd]['desc'])]])
         else:
index 3a46fac0c4ed92ba7b276d3299cc963fc0ba8f0d..4ca1082a8bcfe9e2645d46ac502063332d898530 100644 (file)
 
 import sigrokdecode as srd
 
+NUM_OUTPUT_CHANNELS = 8
+
+def logic_channels(num_channels):
+    l = []
+    for i in range(num_channels):
+        l.append(tuple(['p%d' % i, 'P-port input/output %d' % i]))
+    return tuple(l)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'tca6408a'
@@ -36,6 +44,7 @@ class Decoder(srd.Decoder):
         ('value', 'Register value'),
         ('warning', 'Warning'),
     )
+    logic_output_channels = logic_channels(NUM_OUTPUT_CHANNELS)
     annotation_rows = (
         ('regs', 'Registers', (0, 1)),
         ('warnings', 'Warnings', (2,)),
@@ -48,17 +57,33 @@ class Decoder(srd.Decoder):
         self.state = 'IDLE'
         self.chip = -1
 
+        self.logic_output_es = 0
+        self.logic_value = 0
+
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
+        self.out_logic = self.register(srd.OUTPUT_LOGIC)
+
+    def flush(self):
+        self.put_logic_states()
 
     def putx(self, data):
         self.put(self.ss, self.es, self.out_ann, data)
 
+    def put_logic_states(self):
+        if (self.es > self.logic_output_es):
+            data = bytes([self.logic_value])
+            self.put(self.logic_output_es, self.es, self.out_logic, [0, data])
+            self.logic_output_es = self.es
+
     def handle_reg_0x00(self, b):
         self.putx([1, ['State of inputs: %02X' % b]])
+        # TODO
 
     def handle_reg_0x01(self, b):
-        self.putx([1, ['Outputs set: %02X' % b ]])
+        self.put_logic_states()
+        self.putx([1, ['Outputs set: %02X' % b]])
+        self.logic_value = b
 
     def handle_reg_0x02(self, b):
         self.putx([1, ['Polarity inverted: %02X' % b]])
index 3c70eca9ae3bfc65039bbd7c903720c14d5679e1..95c533211df78f85f4b5d9e91d7e5df6ddaccd27 100644 (file)
@@ -45,6 +45,56 @@ def normalize_time(t):
     else:
         return '%f' % t
 
+def terse_times(t, fmt):
+    # Strictly speaking these variants are not used in the current
+    # implementation, but can reduce diffs during future maintenance.
+    if fmt == 'full':
+        return [normalize_time(t)]
+    # End of "forward compatibility".
+
+    if fmt == 'samples':
+        # See below. No unit text, on purpose.
+        return ['{:d}'.format(t)]
+
+    # Use caller specified scale, or automatically find one.
+    scale, unit = None, None
+    if fmt == 'terse-auto':
+        if abs(t) >= 1e0:
+            scale, unit = 1e0, 's'
+        elif abs(t) >= 1e-3:
+            scale, unit = 1e3, 'ms'
+        elif abs(t) >= 1e-6:
+            scale, unit = 1e6, 'us'
+        elif abs(t) >= 1e-9:
+            scale, unit = 1e9, 'ns'
+        elif abs(t) >= 1e-12:
+            scale, unit = 1e12, 'ps'
+    # Beware! Uses unit-less text when the user picked the scale. For
+    # more consistent output with less clutter, thus faster navigation
+    # by humans. Can also un-hide text at higher distance zoom levels.
+    elif fmt == 'terse-s':
+        scale, unit = 1e0, ''
+    elif fmt == 'terse-ms':
+        scale, unit = 1e3, ''
+    elif fmt == 'terse-us':
+        scale, unit = 1e6, ''
+    elif fmt == 'terse-ns':
+        scale, unit = 1e9, ''
+    elif fmt == 'terse-ps':
+        scale, unit = 1e12, ''
+    if scale:
+        t *= scale
+        return ['{:.0f}{}'.format(t, unit), '{:.0f}'.format(t)]
+
+    # Unspecified format, and nothing auto-detected.
+    return ['{:f}'.format(t)]
+
+class Pin:
+    (DATA,) = range(1)
+
+class Ann:
+    (TIME, TERSE, AVG, DELTA,) = range(4)
+
 class Decoder(srd.Decoder):
     api_version = 3
     id = 'timing'
@@ -60,18 +110,25 @@ class Decoder(srd.Decoder):
     )
     annotations = (
         ('time', 'Time'),
+        ('terse', 'Terse'),
         ('average', 'Average'),
         ('delta', 'Delta'),
     )
     annotation_rows = (
-        ('times', 'Times', (0,)),
-        ('averages', 'Averages', (1,)),
-        ('deltas', 'Deltas', (2,)),
+        ('times', 'Times', (Ann.TIME, Ann.TERSE,)),
+        ('averages', 'Averages', (Ann.AVG,)),
+        ('deltas', 'Deltas', (Ann.DELTA,)),
     )
     options = (
         { 'id': 'avg_period', 'desc': 'Averaging period', 'default': 100 },
-        { 'id': 'edge', 'desc': 'Edges to check', 'default': 'any', 'values': ('any', 'rising', 'falling') },
-        { 'id': 'delta', 'desc': 'Show delta from last', 'default': 'no', 'values': ('yes', 'no') },
+        { 'id': 'edge', 'desc': 'Edges to check',
+          'default': 'any', 'values': ('any', 'rising', 'falling') },
+        { 'id': 'delta', 'desc': 'Show delta from last',
+          'default': 'no', 'values': ('yes', 'no') },
+        { 'id': 'format', 'desc': 'Format of \'time\' annotation',
+          'default': 'full', 'values': ('full', 'terse-auto',
+          'terse-s', 'terse-ms', 'terse-us', 'terse-ns', 'terse-ps',
+          'samples') },
     )
 
     def __init__(self):
@@ -79,11 +136,6 @@ class Decoder(srd.Decoder):
 
     def reset(self):
         self.samplerate = None
-        self.last_samplenum = None
-        self.last_n = deque()
-        self.chunks = 0
-        self.level_changed = False
-        self.last_t = None
 
     def metadata(self, key, value):
         if key == srd.SRD_CONF_SAMPLERATE:
@@ -91,38 +143,52 @@ class Decoder(srd.Decoder):
 
     def start(self):
         self.out_ann = self.register(srd.OUTPUT_ANN)
-        self.edge = self.options['edge']
 
     def decode(self):
         if not self.samplerate:
             raise SamplerateError('Cannot decode without samplerate.')
+        edge = self.options['edge']
+        avg_period = self.options['avg_period']
+        delta = self.options['delta'] == 'yes'
+        fmt = self.options['format']
+        ss = None
+        last_n = deque()
+        last_t = None
         while True:
-            if self.edge == 'rising':
-                pin = self.wait({0: 'r'})
-            elif self.edge == 'falling':
-                pin = self.wait({0: 'f'})
+            if edge == 'rising':
+                pin = self.wait({Pin.DATA: 'r'})
+            elif edge == 'falling':
+                pin = self.wait({Pin.DATA: 'f'})
             else:
-                pin = self.wait({0: 'e'})
+                pin = self.wait({Pin.DATA: 'e'})
 
-            if not self.last_samplenum:
-                self.last_samplenum = self.samplenum
+            if not ss:
+                ss = self.samplenum
                 continue
-            samples = self.samplenum - self.last_samplenum
-            t = samples / self.samplerate
-
-            if t > 0:
-                self.last_n.append(t)
-            if len(self.last_n) > self.options['avg_period']:
-                self.last_n.popleft()
-
-            self.put(self.last_samplenum, self.samplenum, self.out_ann,
-                     [0, [normalize_time(t)]])
-            if self.options['avg_period'] > 0:
-                self.put(self.last_samplenum, self.samplenum, self.out_ann,
-                         [1, [normalize_time(sum(self.last_n) / len(self.last_n))]])
-            if self.last_t and self.options['delta'] == 'yes':
-                self.put(self.last_samplenum, self.samplenum, self.out_ann,
-                         [2, [normalize_time(t - self.last_t)]])
-
-            self.last_t = t
-            self.last_samplenum = self.samplenum
+            es = self.samplenum
+            sa = es - ss
+            t = sa / self.samplerate
+
+            if fmt == 'full':
+                cls, txt = Ann.TIME, [normalize_time(t)]
+            elif fmt == 'samples':
+                cls, txt = Ann.TERSE, terse_times(sa, fmt)
+            else:
+                cls, txt = Ann.TERSE, terse_times(t, fmt)
+            if txt:
+                self.put(ss, es, self.out_ann, [cls, txt])
+
+            if avg_period > 0:
+                if t > 0:
+                    last_n.append(t)
+                if len(last_n) > avg_period:
+                    last_n.popleft()
+                average = sum(last_n) / len(last_n)
+                cls, txt = Ann.AVG, normalize_time(average)
+                self.put(ss, es, self.out_ann, [cls, [txt]])
+            if last_t and delta:
+                cls, txt = Ann.DELTA, normalize_time(t - last_t)
+                self.put(ss, es, self.out_ann, [cls, [txt]])
+
+            last_t = t
+            ss = es
index 0c501b0dc46d63c5321c60472b750def28525c9d..038a2f8e88abc41eb32478eb71b068a264604a7a 100644 (file)
@@ -114,7 +114,7 @@ class Decoder(srd.Decoder):
         {'id': 'parity', 'desc': 'Parity', 'default': 'none',
             'values': ('none', 'odd', 'even', 'zero', 'one', 'ignore')},
         {'id': 'stop_bits', 'desc': 'Stop bits', 'default': 1.0,
-            'values': (0.0, 0.5, 1.0, 1.5)},
+            'values': (0.0, 0.5, 1.0, 1.5, 2.0)},
         {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
             'values': ('lsb-first', 'msb-first')},
         {'id': 'format', 'desc': 'Data format', 'default': 'hex',
@@ -207,11 +207,12 @@ class Decoder(srd.Decoder):
         self.samplerate = None
         self.frame_start = [-1, -1]
         self.frame_valid = [None, None]
+        self.cur_frame_bit = [None, None]
         self.startbit = [-1, -1]
         self.cur_data_bit = [0, 0]
         self.datavalue = [0, 0]
         self.paritybit = [-1, -1]
-        self.stopbit1 = [-1, -1]
+        self.stopbits = [[], []]
         self.startsample = [-1, -1]
         self.state = ['WAIT FOR START BIT', 'WAIT FOR START BIT']
         self.databits = [[], []]
@@ -251,11 +252,13 @@ class Decoder(srd.Decoder):
         # Save the sample number where the start bit begins.
         self.frame_start[rxtx] = self.samplenum
         self.frame_valid[rxtx] = True
+        self.cur_frame_bit[rxtx] = 0
 
-        self.state[rxtx] = 'GET START BIT'
+        self.advance_state(rxtx, signal)
 
     def get_start_bit(self, rxtx, signal):
         self.startbit[rxtx] = signal
+        self.cur_frame_bit[rxtx] += 1
 
         # The startbit must be 0. If not, we report an error and wait
         # for the next start bit (assuming this one was spurious).
@@ -266,17 +269,21 @@ class Decoder(srd.Decoder):
             es = self.samplenum + ceil(self.bit_width / 2.0)
             self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
                 (self.datavalue[rxtx], self.frame_valid[rxtx])])
-            self.state[rxtx] = 'WAIT FOR START BIT'
+            self.advance_state(rxtx, signal, fatal = True, idle = es)
             return
 
+        # Reset internal state for the pending UART frame.
         self.cur_data_bit[rxtx] = 0
         self.datavalue[rxtx] = 0
+        self.paritybit[rxtx] = -1
+        self.stopbits[rxtx].clear()
         self.startsample[rxtx] = -1
+        self.databits[rxtx].clear()
 
         self.putp(['STARTBIT', rxtx, self.startbit[rxtx]])
         self.putg([Ann.RX_START + rxtx, ['Start bit', 'Start', 'S']])
 
-        self.state[rxtx] = 'GET DATA BITS'
+        self.advance_state(rxtx, signal)
 
     def handle_packet(self, rxtx):
         d = 'rx' if (rxtx == RX) else 'tx'
@@ -312,6 +319,7 @@ class Decoder(srd.Decoder):
         # Store individual data bits and their start/end samplenumbers.
         s, halfbit = self.samplenum, int(self.bit_width / 2)
         self.databits[rxtx].append([signal, s - halfbit, s + halfbit])
+        self.cur_frame_bit[rxtx] += 1
 
         # Return here, unless we already received all data bits.
         self.cur_data_bit[rxtx] += 1
@@ -339,11 +347,7 @@ class Decoder(srd.Decoder):
 
         self.databits[rxtx] = []
 
-        # Advance to either reception of the parity bit, or reception of
-        # the STOP bits if parity is not applicable.
-        self.state[rxtx] = 'GET PARITY BIT'
-        if self.options['parity'] == 'none':
-            self.state[rxtx] = 'GET STOP BITS'
+        self.advance_state(rxtx, signal)
 
     def format_value(self, v):
         # Format value 'v' according to configured options.
@@ -389,6 +393,7 @@ class Decoder(srd.Decoder):
 
     def get_parity_bit(self, rxtx, signal):
         self.paritybit[rxtx] = signal
+        self.cur_frame_bit[rxtx] += 1
 
         if parity_ok(self.options['parity'], self.paritybit[rxtx],
                      self.datavalue[rxtx], self.options['data_bits']):
@@ -400,34 +405,93 @@ class Decoder(srd.Decoder):
             self.putg([Ann.RX_PARITY_ERR + rxtx, ['Parity error', 'Parity err', 'PE']])
             self.frame_valid[rxtx] = False
 
-        self.state[rxtx] = 'GET STOP BITS'
+        self.advance_state(rxtx, signal)
 
-    # TODO: Currently only supports 1 stop bit.
     def get_stop_bits(self, rxtx, signal):
-        self.stopbit1[rxtx] = signal
+        self.stopbits[rxtx].append(signal)
+        self.cur_frame_bit[rxtx] += 1
 
         # Stop bits must be 1. If not, we report an error.
-        if self.stopbit1[rxtx] != 1:
-            self.putp(['INVALID STOPBIT', rxtx, self.stopbit1[rxtx]])
+        if signal != 1:
+            self.putp(['INVALID STOPBIT', rxtx, signal])
             self.putg([Ann.RX_WARN + rxtx, ['Frame error', 'Frame err', 'FE']])
             self.frame_valid[rxtx] = False
 
-        self.putp(['STOPBIT', rxtx, self.stopbit1[rxtx]])
+        self.putp(['STOPBIT', rxtx, signal])
         self.putg([Ann.RX_STOP + rxtx, ['Stop bit', 'Stop', 'T']])
 
+        # Postprocess the UART frame after all STOP bits were seen.
+        if len(self.stopbits[rxtx]) < self.options['stop_bits']:
+            return
+        self.advance_state(rxtx, signal)
+
+    def advance_state(self, rxtx, signal = None, fatal = False, idle = None):
+        # Advances the protocol decoder's internal state for all regular
+        # UART frame inspection. Deals with either edges, sample points,
+        # or other .wait() conditions. Also gracefully handles extreme
+        # undersampling. Each turn takes one .wait() call which in turn
+        # corresponds to at least one sample. That is why as many state
+        # transitions are done here as required within a single call.
+        frame_end = self.frame_start[rxtx] + self.frame_len_sample_count
+        if idle is not None:
+            # When requested by the caller, start another (potential)
+            # IDLE period after the caller specified position.
+            self.idle_start[rxtx] = idle
+        if fatal:
+            # When requested by the caller, don't advance to the next
+            # UART frame's field, but to the start of the next START bit
+            # instead.
+            self.state[rxtx] = 'WAIT FOR START BIT'
+            return
+        # Advance to the next UART frame's field that we expect. Cope
+        # with absence of optional fields. Force scan for next IDLE
+        # after the (optional) STOP bit field, so that callers need
+        # not deal with optional field presence. Also handles the cases
+        # where the decoder navigates to edges which are not strictly
+        # a field's sampling point.
+        if self.state[rxtx] == 'WAIT FOR START BIT':
+            self.state[rxtx] = 'GET START BIT'
+            return
+        if self.state[rxtx] == 'GET START BIT':
+            self.state[rxtx] = 'GET DATA BITS'
+            return
+        if self.state[rxtx] == 'GET DATA BITS':
+            self.state[rxtx] = 'GET PARITY BIT'
+            if self.options['parity'] != 'none':
+                return
+            # FALLTHROUGH
+        if self.state[rxtx] == 'GET PARITY BIT':
+            self.state[rxtx] = 'GET STOP BITS'
+            if self.options['stop_bits']:
+                return
+            # FALLTHROUGH
+        if self.state[rxtx] == 'GET STOP BITS':
+            # Postprocess the previously received UART frame. Advance
+            # the read position to after the frame's last bit time. So
+            # that the start of the next START bit won't fall into the
+            # end of the previously received UART frame. This improves
+            # robustness in the presence of glitchy input data.
+            ss = self.frame_start[rxtx]
+            es = self.samplenum + ceil(self.bit_width / 2.0)
+            self.handle_frame(rxtx, ss, es)
+            self.state[rxtx] = 'WAIT FOR START BIT'
+            self.idle_start[rxtx] = frame_end
+            return
+        # Unhandled state, actually a programming error. Emit diagnostics?
+        self.state[rxtx] = 'WAIT FOR START BIT'
+
+    def handle_frame(self, rxtx, ss, es):
         # Pass the complete UART frame to upper layers.
-        es = self.samplenum + ceil(self.bit_width / 2.0)
-        self.putpse(self.frame_start[rxtx], es, ['FRAME', rxtx,
+        self.putpse(ss, es, ['FRAME', rxtx,
             (self.datavalue[rxtx], self.frame_valid[rxtx])])
 
-        self.state[rxtx] = 'WAIT FOR START BIT'
-        self.idle_start[rxtx] = self.frame_start[rxtx] + self.frame_len_sample_count
+    def handle_idle(self, rxtx, ss, es):
+        self.putpse(ss, es, ['IDLE', rxtx, 0])
 
-    def handle_break(self, rxtx):
-        self.putpse(self.frame_start[rxtx], self.samplenum,
-                ['BREAK', rxtx, 0])
-        self.putgse(self.frame_start[rxtx], self.samplenum,
-                [Ann.RX_BREAK + rxtx, ['Break condition', 'Break', 'Brk', 'B']])
+    def handle_break(self, rxtx, ss, es):
+        self.putpse(ss, es, ['BREAK', rxtx, 0])
+        self.putgse(ss, es, [Ann.RX_BREAK + rxtx,
+                ['Break condition', 'Break', 'Brk', 'B']])
         self.state[rxtx] = 'WAIT FOR START BIT'
 
     def get_wait_cond(self, rxtx, inv):
@@ -437,17 +501,12 @@ class Decoder(srd.Decoder):
         state = self.state[rxtx]
         if state == 'WAIT FOR START BIT':
             return {rxtx: 'r' if inv else 'f'}
-        if state == 'GET START BIT':
-            bitnum = 0
-        elif state == 'GET DATA BITS':
-            bitnum = 1 + self.cur_data_bit[rxtx]
-        elif state == 'GET PARITY BIT':
-            bitnum = 1 + self.options['data_bits']
-        elif state == 'GET STOP BITS':
-            bitnum = 1 + self.options['data_bits']
-            bitnum += 0 if self.options['parity'] == 'none' else 1
-        want_num = ceil(self.get_sample_point(rxtx, bitnum))
-        return {'skip': want_num - self.samplenum}
+        if state in ('GET START BIT', 'GET DATA BITS',
+                'GET PARITY BIT', 'GET STOP BITS'):
+            bitnum = self.cur_frame_bit[rxtx]
+            # TODO: Currently does not support half STOP bits.
+            want_num = ceil(self.get_sample_point(rxtx, bitnum))
+            return {'skip': want_num - self.samplenum}
 
     def get_idle_cond(self, rxtx, inv):
         # Return a condition that corresponds to the (expected) end of
@@ -490,7 +549,8 @@ class Decoder(srd.Decoder):
             return
         diff = self.samplenum - self.break_start[rxtx]
         if diff >= self.break_min_sample_count:
-            self.handle_break(rxtx)
+            ss, es = self.frame_start[rxtx], self.samplenum
+            self.handle_break(rxtx, ss, es)
         self.break_start[rxtx] = None
 
     def inspect_idle(self, rxtx, signal, inv):
@@ -509,8 +569,8 @@ class Decoder(srd.Decoder):
         if diff < self.frame_len_sample_count:
             return
         ss, es = self.idle_start[rxtx], self.samplenum
-        self.putpse(ss, es, ['IDLE', rxtx, 0])
-        self.idle_start[rxtx] = self.samplenum
+        self.handle_idle(rxtx, ss, es)
+        self.idle_start[rxtx] = es
 
     def decode(self):
         if not self.samplerate:
diff --git a/decoders/xy2-100/__init__.py b/decoders/xy2-100/__init__.py
new file mode 100644 (file)
index 0000000..676e1af
--- /dev/null
@@ -0,0 +1,28 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Uli Huber
+##
+## 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/>.
+##
+
+'''
+XY2-100 is a serial bus for connecting galvo systems to controllers
+
+Details:
+
+http://www.newson.be/doc.php?id=XY2-100
+'''
+
+from .pd import Decoder
diff --git a/decoders/xy2-100/pd.py b/decoders/xy2-100/pd.py
new file mode 100644 (file)
index 0000000..47c4182
--- /dev/null
@@ -0,0 +1,242 @@
+##
+## This file is part of the libsigrokdecode project.
+##
+## Copyright (C) 2019 Uli Huber
+## Copyright (C) 2020 Soeren Apel
+##
+## 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/>.
+##
+
+import sigrokdecode as srd
+
+ann_bit, ann_stat_bit, ann_type, ann_command, ann_parameter, ann_parity, ann_pos, ann_status, ann_warning = range(9)
+frame_type_none, frame_type_command, frame_type_16bit_pos, frame_type_18bit_pos = range(4)
+
+class Decoder(srd.Decoder):
+    api_version = 3
+    id = 'xy2-100'
+    name = 'XY2-100'
+    longname = 'XY2-100(E) and XY-200(E) galvanometer protocol'
+    desc = 'Serial protocol for galvanometer positioning in laser systems'
+    license = 'gplv2+'
+    inputs = ['logic']
+    outputs = []
+
+    tags = ['Embedded/industrial']
+
+    channels = (
+        {'id': 'clk', 'name': 'CLK', 'desc': 'Clock'},
+        {'id': 'sync', 'name': 'SYNC', 'desc': 'Sync'},
+        {'id': 'data', 'name': 'DATA', 'desc': 'X, Y or Z axis data'},
+    )
+    optional_channels = (
+        {'id': 'status', 'name': 'STAT', 'desc': 'X, Y or Z axis status'},
+    )
+
+    annotations = (
+        ('bit', 'Data Bit'),
+        ('stat_bit', 'Status Bit'),
+        ('type', 'Frame Type'),
+        ('command', 'Command'),
+        ('parameter', 'Parameter'),
+        ('parity', 'Parity'),
+        ('position', 'Position'),
+        ('status', 'Status'),
+        ('warning', 'Human-readable warnings'),
+    )
+    annotation_rows = (
+        ('bits', 'Data Bits', (ann_bit,)),
+        ('stat_bits', 'Status Bits', (ann_stat_bit,)),
+        ('data', 'Data', (ann_type, ann_command, ann_parameter, ann_parity)),
+        ('positions', 'Positions', (ann_pos,)),
+        ('statuses', 'Statuses', (ann_status,)),
+        ('warnings', 'Warnings', (ann_warning,)),
+    )
+
+    def __init__(self):
+        self.samplerate = None
+        self.reset()
+
+    def reset(self):
+        self.bits = []
+        self.stat_bits = []
+        self.stat_skip_bit = True
+
+    def metadata(self, key, value):
+        if key == srd.SRD_CONF_SAMPLERATE:
+            self.samplerate = value
+
+    def start(self):
+        self.out_ann = self.register(srd.OUTPUT_ANN)
+
+    def put_ann(self, ss, es, ann_class, value):
+        self.put(ss, es, self.out_ann, [ann_class, value])
+
+    def process_bit(self, sync, bit_ss, bit_es, bit_value):
+        self.put_ann(bit_ss, bit_es, ann_bit, ['%d' % bit_value])
+        self.bits.append((bit_ss, bit_es, bit_value))
+
+        if sync == 0:
+            if len(self.bits) < 20:
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Not enough data bits'])
+                self.reset()
+                return
+
+            # Bit structure:
+            # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+            # T --------------- 18-bit pos ----------------- PARITY    or
+            # -TYPE-- ------------ 16-bit pos -------------- PARITY    or
+            # -TYPE-- -8-bit command -8-bit parameter value- PARITY
+
+            # Calculate parity, excluding the parity bit itself
+            parity = 0
+            for ss, es, value in self.bits[:-1]:
+                parity ^= value
+
+            par_ss, par_es, par_value = self.bits[19]
+            parity_even = 0
+            parity_odd = 0
+            if (par_value == parity):
+                parity_even = 1
+            else:
+                parity_odd = 1
+
+            type_1_value = self.bits[0][2]
+            type_3_value = (self.bits[0][2] << 2) | (self.bits[1][2] << 1) | self.bits[2][2]
+
+            # Determine frame type
+            type = frame_type_none
+            parity_status = ['X', 'Unknown']
+            type_ss = self.bits[0][0]
+            type_es = self.bits[2][1]
+
+            ### 18-bit position
+            if (type_1_value == 1) and (parity_odd == 1):
+                type = frame_type_18bit_pos
+                type_es = self.bits[0][1]
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+            ### 16-bit position
+            elif (type_3_value == 1):
+                type = frame_type_16bit_pos
+                if (parity_even == 1):
+                    parity_status = ['OK']
+                else:
+                    parity_status = ['NOK']
+                    self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Parity error', 'PE'])
+            ### Command
+            elif (type_3_value == 7) and (parity_even == 1):
+                type = frame_type_command
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Careful: 18-bit position frames with wrong parity and command frames with wrong parity cannot be identified'])
+            ### Other
+            else:
+                self.put_ann(self.bits[0][0], bit_es, ann_warning, ['Error', 'Unknown command or parity error'])
+                self.reset()
+                return
+
+            # Output command and parity annotations
+            if (type == frame_type_16bit_pos):
+                self.put_ann(type_ss, type_es, ann_type, ['16 bit Position Frame', '16 bit Pos', 'Pos', 'P'])
+            if (type == frame_type_18bit_pos):
+                self.put_ann(type_ss, type_es, ann_type, ['18 bit Position Frame', '18 bit Pos', 'Pos', 'P'])
+            if (type == frame_type_command):
+                self.put_ann(type_ss, type_es, ann_type, ['Command Frame', 'Command', 'C'])
+
+            self.put_ann(par_ss, par_es, ann_parity, parity_status)
+
+            # Output value
+            if (type == frame_type_16bit_pos) or (type == frame_type_18bit_pos):
+               pos = 0
+
+               if (type == frame_type_16bit_pos):
+                   count = 15
+                   for ss, es, value in self.bits[3:19]:
+                       pos |= value << count
+                       count -= 1
+                   pos = pos if pos < 32768 else pos - 65536
+               else:
+                   count = 17
+                   for ss, es, value in self.bits[3:19]:
+                       pos |= value << count
+                       count -= 1
+                   pos = pos if pos < 131072 else pos - 262144
+
+               self.put_ann(type_es, par_ss, ann_pos, ['%d' % pos])
+
+            if (type == frame_type_command):
+               count = 7
+               cmd = 0
+               cmd_es = 0
+               for ss, es, value in self.bits[3:11]:
+                   cmd |= value << count
+                   count -= 1
+                   cmd_es = es
+               self.put_ann(type_es, cmd_es, ann_command, ['Command 0x%X' % cmd, 'Cmd 0x%X' % cmd, '0x%X' % cmd])
+
+               count = 7
+               param = 0
+               for ss, es, value in self.bits[11:19]:
+                   param |= value << count
+                   count -= 1
+               self.put_ann(cmd_es, par_ss, ann_parameter, ['Parameter 0x%X / %d' % (param, param), '0x%X / %d' % (param, param),'0x%X' % param])
+
+            self.reset()
+
+    def process_stat_bit(self, sync, bit_ss, bit_es, bit_value):
+        if self.stat_skip_bit:
+            self.stat_skip_bit = False
+            return
+
+        self.put_ann(bit_ss, bit_es, ann_stat_bit, ['%d' % bit_value])
+        self.stat_bits.append((bit_ss, bit_es, bit_value))
+
+        if (sync == 0) and (len(self.stat_bits) == 19):
+            stat_ss = self.stat_bits[0][0]
+            stat_es = self.stat_bits[18][1]
+
+            status = 0
+            count = 18
+            for ss, es, value in self.stat_bits:
+                status |= value << count
+                count -= 1
+            self.put_ann(stat_ss, stat_es, ann_status, ['Status 0x%X' % status, '0x%X' % status])
+
+    def decode(self):
+        bit_ss = None
+        bit_es = None
+        bit_value = 0
+        stat_ss = None
+        stat_es = None
+        stat_value = 0
+        sync_value = 0
+        has_stat = self.has_channel(3)
+
+        while True:
+            # Wait for any edge on clk
+            clk, sync, data, stat = self.wait({0: 'e'})
+
+            if clk == 1:
+                stat_value = stat
+
+                bit_es = self.samplenum
+                if bit_ss:
+                    self.process_bit(sync_value, bit_ss, bit_es, bit_value)
+                bit_ss = self.samplenum
+            else:
+                bit_value = data
+                sync_value = sync
+
+                stat_es = self.samplenum
+                if stat_ss and has_stat:
+                    self.process_stat_bit(sync_value, stat_ss, stat_es, stat_value)
+                stat_ss = self.samplenum
index 36088744893b7a76f8b0c74e8da33d977c3d3a13..067e98da44ca9797c1a4c2be5ad28083b9759860 100644 (file)
@@ -426,6 +426,7 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess,
        di->got_new_samples = FALSE;
        di->handled_all_samples = FALSE;
        di->want_wait_terminate = FALSE;
+       di->communicate_eof = FALSE;
        di->decoder_state = SRD_OK;
 
        /*
@@ -500,6 +501,7 @@ static void srd_inst_reset_state(struct srd_decoder_inst *di)
        di->got_new_samples = FALSE;
        di->handled_all_samples = FALSE;
        di->want_wait_terminate = FALSE;
+       di->communicate_eof = FALSE;
        di->decoder_state = SRD_OK;
        /* Conditions and mutex got reset after joining the thread. */
 }
@@ -1058,6 +1060,17 @@ static gpointer di_thread(gpointer data)
        py_res = PyObject_CallMethod(di->py_inst, "decode", NULL);
        srd_dbg("%s: decode() terminated.", di->inst_id);
 
+       /*
+        * Termination with an EOFError exception is accepted to simplify
+        * the implementation of decoders and for backwards compatibility.
+        */
+       if (PyErr_Occurred() && PyErr_ExceptionMatches(PyExc_EOFError)) {
+               srd_dbg("%s: ignoring EOFError during decode() execution.",
+                       di->inst_id);
+               PyErr_Clear();
+               if (!py_res)
+                       py_res = Py_None;
+       }
        if (!py_res)
                di->decoder_state = SRD_ERR;
 
@@ -1234,12 +1247,111 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di,
                g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
        g_mutex_unlock(&di->data_mutex);
 
+       /* Flush all PDs in the stack that can be flushed */
+       srd_inst_flush(di);
+
        if (di->want_wait_terminate)
                return SRD_ERR_TERM_REQ;
 
        return SRD_OK;
 }
 
+
+/**
+ * Flush all data that is pending, bottom decoder first up to the top of the stack.
+ *
+ * @param di The decoder instance to call. Must not be NULL.
+ *
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @private
+ */
+SRD_PRIV int srd_inst_flush(struct srd_decoder_inst *di)
+{
+       PyGILState_STATE gstate;
+       PyObject *py_ret;
+       GSList *l;
+       int ret;
+
+       if (!di)
+               return SRD_ERR_ARG;
+
+       gstate = PyGILState_Ensure();
+       if (PyObject_HasAttrString(di->py_inst, "flush")) {
+               srd_dbg("Calling flush() of instance %s", di->inst_id);
+               py_ret = PyObject_CallMethod(di->py_inst, "flush", NULL);
+               Py_XDECREF(py_ret);
+       }
+       PyGILState_Release(gstate);
+
+       /* Pass the "flush" request to all stacked decoders. */
+       for (l = di->next_di; l; l = l->next) {
+               ret = srd_inst_flush(l->data);
+               if (ret != SRD_OK)
+                       return ret;
+       }
+
+       return di->decoder_state;
+}
+
+/**
+ * Communicate the end of the stream of sample data to a decoder instance.
+ *
+ * @param[in] di The decoder instance to call. Must not be NULL.
+ *
+ * @return SRD_OK upon success, a (negative) error code otherwise.
+ *
+ * @private
+ */
+SRD_PRIV int srd_inst_send_eof(struct srd_decoder_inst *di)
+{
+       GSList *l;
+       int ret;
+
+       if (!di)
+               return SRD_ERR_ARG;
+
+       /*
+        * Send EOF to the caller specified decoder instance. Only
+        * communicate EOF to currently executing decoders. Never
+        * started or previously finished is perfectly acceptable.
+        */
+       srd_dbg("End of sample data: instance %s.", di->inst_id);
+       if (!di->thread_handle) {
+               srd_dbg("No worker thread, nothing to do.");
+               return SRD_OK;
+       }
+
+       /* Signal the thread about the EOF condition. */
+       g_mutex_lock(&di->data_mutex);
+       di->inbuf = NULL;
+       di->inbuflen = 0;
+       di->got_new_samples = TRUE;
+       di->handled_all_samples = FALSE;
+       di->want_wait_terminate = TRUE;
+       di->communicate_eof = TRUE;
+       g_cond_signal(&di->got_new_samples_cond);
+       g_mutex_unlock(&di->data_mutex);
+
+       /* Only return from here when the condition was handled. */
+       g_mutex_lock(&di->data_mutex);
+       while (!di->handled_all_samples && !di->want_wait_terminate)
+               g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex);
+       g_mutex_unlock(&di->data_mutex);
+
+       /* Flush the decoder instance which handled EOF. */
+       srd_inst_flush(di);
+
+       /* Pass EOF to all stacked decoders. */
+       for (l = di->next_di; l; l = l->next) {
+               ret = srd_inst_send_eof(l->data);
+               if (ret != SRD_OK)
+                       return ret;
+       }
+
+       return SRD_OK;
+}
+
 /**
  * Terminate current decoder work, prepare for re-use on new input data.
  *
diff --git a/irmp/README-sigrok.txt b/irmp/README-sigrok.txt
new file mode 100644 (file)
index 0000000..03ff472
--- /dev/null
@@ -0,0 +1,3 @@
+This directory contains a subset of the IRMP project source code. Then
+extends it to allow sigrok protocol decoders to use the IRMP backend.
+See README.txt for information on the IRMP project itself.
diff --git a/irmp/README.txt b/irmp/README.txt
new file mode 100644 (file)
index 0000000..52d8c73
--- /dev/null
@@ -0,0 +1,13 @@
+IRMP - Infrared Multi Protocol Decoder
+--------------------------------------
+
+Version IRMP:  3.1.5 2019-08-27
+Version IRSND: 3.1.5 2019-08-28
+
+Documentation:
+   http://www.mikrocontroller.net/articles/IRMP
+   http://www.mikrocontroller.net/articles/IRMP_-_english
+
+   http://www.mikrocontroller.net/articles/IRSND
+   http://www.mikrocontroller.net/articles/IRSND_-_english
diff --git a/irmp/irmp-main-sharedlib.c b/irmp/irmp-main-sharedlib.c
new file mode 100644 (file)
index 0000000..4d02460
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * irmp-main-sharedlib.c
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de
+ * Copyright (c) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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.
+ */
+
+/*
+ * Declare the library's public API first. Prove it's consistent and
+ * complete as a standalone header file.
+ */
+#include "irmp-main-sharedlib.h"
+
+#include <errno.h>
+#include <glib.h>
+#include <Python.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Include the IRMP core logic. This approach is required because of
+ * static variables which hold internal state. The core logic started
+ * as an MCU project where resources are severely constrained.
+ *
+ * This libsigrokdecode incarnation of IRMP will always be used in the
+ * UNIX_OR_WINDOWS configuration. But libtool(1) breaks the upstream
+ * logic's platform detection. Check reliably available conditions here
+ * and provide expected symbols to the library, to reduce changes to the
+ * upstream project.
+ */
+#if defined _WIN32
+#  if !defined WIN32
+#    define WIN32
+#  endif
+#else
+#  if !defined unix
+#    define unix
+#  endif
+#endif
+#include "irmp.h"
+#include "irmp.c"
+
+/*
+ * The remaining source code implements the PC library, which accepts
+ * sample data from API callers, and provides detector results as they
+ * become available after seeing input data.
+ *
+ * TODO items, known constraints
+ * - Counters in the IRMP core logic and the library wrapper are 32bit
+ *   only. In the strictest sense they only need to cover the span of
+ *   an IR frame. In the PC side library case they need to cover "a
+ *   detection phase", which happens to be under calling applications'
+ *   control. The library shall not mess with the core's internal state,
+ *   and may even not be able to reliably tell whether detection of a
+ *   frame started in the core. Fortunately the 32bit counters only roll
+ *   over after some 2.5 days at the highest available sample rate. So
+ *   this limitation is not a blocker.
+ * - The IRMP core keeps internal state in global variables. Which is
+ *   appropriate for MCU configurations. For the PC library use case
+ *   this constraint prevents concurrency, only a single data stream
+ *   can get processed at any time. This limitation can get addressed
+ *   later, making the flexible and featureful IRMP detection available
+ *   in the first place is considered highly desirable, and is a great
+ *   improvement in itself.
+ * - The detection of IR frames from buffered data is both limited and
+ *   complicated at the same time. The routine re-uses the caller's
+ *   buffer _and_ internal state across multiple calls. Thus windowed
+ *   operation over a larger set of input data is not available. The
+ *   API lacks a flag for failed detection, thus applications need to
+ *   guess from always returned payload data.
+ * - Is it worth adding a "detection in progress" query to the API? Is
+ *   the information available to the library wrapper, and reliable?
+ *   Shall applications be able to "poll" the started, and completed
+ *   state for streamed operation including periodic state resets which
+ *   won't interfere with pending detection? (It's assumed that this
+ *   is only required when feeding single values in individual calls is
+ *   found to be rather expensive.
+ * - Some of the result data reflects the core's internal presentation
+ *   while there is no declaration in the library's API. This violates
+ *   API layers, and needs to get addressed properly.
+ * - The IRMP core logic (strictly speaking the specific details of
+ *   preprocessor symbol arrangements in the current implementation)
+ *   appears to assume either to run on an MCU and capture IR signals
+ *   from hardware pins, falling back to AVR if no other platform got
+ *   detected. Or assumes to run on a (desktop) PC, and automatically
+ *   enables ANALYZE mode, which results in lots of stdio traffic that
+ *   is undesirable for application code which uses the shared library
+ *   for strict detection purposes but no further analysis or research.
+ *   It's a pity that turning off ANALYZE switches to MCU mode, and that
+ *   keeping ANALYZE enabled but silencing the output is rather messy
+ *   and touches the innards of the core logic (the irmp.c source file
+ *   and its dependency header files).
+ */
+
+#ifndef ARRAY_SIZE
+#  define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#endif
+
+static int irmp_lib_initialized;
+static size_t irmp_lib_client_id;
+static GMutex irmp_lib_mutex;
+
+struct irmp_instance {
+       size_t client_id;
+       GMutex *mutex;
+};
+
+static void irmp_lib_autoinit(void)
+{
+       if (irmp_lib_initialized)
+               return;
+
+       irmp_lib_client_id = 0;
+       g_mutex_init(&irmp_lib_mutex);
+
+       irmp_lib_initialized = 1;
+}
+
+static size_t irmp_next_client_id(void)
+{
+       size_t id;
+
+       do {
+               id = ++irmp_lib_client_id;
+       } while (!id);
+
+       return id;
+}
+
+IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void)
+{
+       struct irmp_instance *inst;
+
+       irmp_lib_autoinit();
+
+       inst = g_malloc0(sizeof(*inst));
+       if (!inst)
+               return NULL;
+
+       inst->client_id = irmp_next_client_id();
+       inst->mutex = &irmp_lib_mutex;
+
+       return inst;
+}
+
+IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state)
+{
+
+       irmp_lib_autoinit();
+
+       if (!state)
+               return;
+
+       g_free(state);
+}
+
+IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state)
+{
+
+       irmp_lib_autoinit();
+
+       return state ? state->client_id : 0;
+}
+
+IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait)
+{
+       int rc;
+       PyGILState_STATE pyst;
+
+       irmp_lib_autoinit();
+
+       if (!state || !state->mutex)
+               return -EINVAL;
+
+       pyst = PyGILState_Ensure();
+       Py_BEGIN_ALLOW_THREADS
+       if (wait) {
+               g_mutex_lock(state->mutex);
+               rc = 0;
+       } else {
+               rc = g_mutex_trylock(state->mutex);
+       }
+       Py_END_ALLOW_THREADS
+       PyGILState_Release(pyst);
+       if (rc != 0)
+               return rc;
+
+       return 0;
+}
+
+IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state)
+{
+
+       irmp_lib_autoinit();
+
+       if (!state || !state->mutex)
+               return;
+
+       g_mutex_unlock(state->mutex);
+}
+
+static uint32_t s_end_sample;
+
+IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void)
+{
+       return F_INTERRUPTS;
+}
+
+IRMP_DLLEXPORT void irmp_reset_state(void)
+{
+       size_t i;
+       IRMP_DATA data;
+
+       /*
+        * Provide the equivalent of 1s idle input signal level. Then
+        * drain any potentially accumulated result data. This clears
+        * the internal decoder state.
+        */
+       IRMP_PIN = 0xff;
+       i = F_INTERRUPTS;
+       while (i-- > 0) {
+               (void)irmp_ISR();
+       }
+       (void)irmp_get_data(&data);
+
+       time_counter = 0;
+       s_startBitSample = 0;
+       s_curSample = 0;
+       s_end_sample = 0;
+
+       /*
+        * TODO This is not the most appropriate location to control the
+        * core logic's verbosity. But out of the public set of library
+        * routines this call is closest to some initialization routine.
+        * The query for compile time parameter values is optional, the
+        * state reset is not. Multiple verbosity setup activities in
+        * the same program lifetime won't harm. This HACK is clearly
+        * preferrable over more fiddling with core logic innards, or
+        * the introduction of yet another DLL routine.
+        */
+       silent = 1;
+       verbose = 0;
+}
+
+IRMP_DLLEXPORT int irmp_add_one_sample(int sample)
+{
+       int ret;
+
+       IRMP_PIN = sample ? 0xff : 0x00;
+       ret = irmp_ISR() ? 1 : 0;
+       s_end_sample = s_curSample++;
+       return ret;
+}
+
+IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data)
+{
+       IRMP_DATA d;
+
+       if (!irmp_get_data(&d))
+               return 0;
+
+       data->address = d.address;
+       data->command = d.command;
+       data->protocol = d.protocol;
+       data->protocol_name = irmp_get_protocol_name(d.protocol);
+       data->flags = d.flags;
+       data->start_sample = s_startBitSample;
+       data->end_sample = s_end_sample;
+       return 1;
+}
+
+#if WITH_IRMP_DETECT_BUFFER
+IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buff, size_t len)
+{
+       struct irmp_result_data ret;
+
+       memset(&ret, 0, sizeof(ret));
+       while (s_curSample < len) {
+               if (irmp_add_one_sample(buff[s_curSample])) {
+                       irmp_get_result_data(&ret);
+                       return ret;
+               }
+       }
+       return ret;
+}
+#endif
+
+IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol)
+{
+       const char *name;
+
+       if (protocol >= ARRAY_SIZE(irmp_protocol_names))
+               return "unknown";
+       name = irmp_protocol_names[protocol];
+       if (!name || !*name)
+               return "unknown";
+       return name;
+}
+
+static __attribute__((constructor)) void init(void)
+{
+       irmp_lib_autoinit();
+}
diff --git a/irmp/irmp-main-sharedlib.h b/irmp/irmp-main-sharedlib.h
new file mode 100644 (file)
index 0000000..67e7997
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * irmp-main-sharedlib.h
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Copyright (c) 2009-2019 René Staffen - r.staffen(at)gmx.de
+ * Copyright (c) 2020-2021 Gerhard Sittig <gerhard.sittig@gmx.net>
+ *
+ * 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.
+ */
+
+#ifndef IRMP_SHAREDLIB_H
+#define IRMP_SHAREDLIB_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Export the public API routines. */
+#ifndef IRMP_DLLEXPORT
+#  if defined WIN32 && defined _MSC_VER
+#    define IRMP_DLLEXPORT __declspec(dllexport)
+#  else
+#    define IRMP_DLLEXPORT __attribute__((visibility("default")))
+#  endif
+#endif
+
+/* Part of the library API is optional. */
+#define WITH_IRMP_DETECT_BUFFER 0
+
+/**
+ * @brief State container for a decoder core instance. Opaque to clients.
+ */
+struct irmp_instance;
+
+/**
+ * @brief Allocate a decoder instance.
+ *
+ * @returns Reference to the allocated instance state.
+ */
+IRMP_DLLEXPORT struct irmp_instance *irmp_instance_alloc(void);
+
+/**
+ * @brief Release a decoder instance.
+ *
+ * @param[in] state Reference to the instance's state.
+ */
+IRMP_DLLEXPORT void irmp_instance_free(struct irmp_instance *state);
+
+/**
+ * @brief Get the client ID of an IRMP decoder core instance.
+ */
+IRMP_DLLEXPORT size_t irmp_instance_id(struct irmp_instance *state);
+
+/**
+ * @brief Acquire a decoder instance's lock.
+ *
+ * @param[in] state Reference to the instance's state.
+ * @param[in] wait Whether to block until the lock is acquired.
+ *
+ * @returns 0 upon success, non-zero upon failure
+ */
+IRMP_DLLEXPORT int irmp_instance_lock(struct irmp_instance *state, int wait);
+
+/**
+ * @brief Release a decoder instance's lock.
+ *
+ * @param[in] state Reference to the instance's state.
+ *
+ * @returns 0 upon success, non-zero upon failure
+ */
+IRMP_DLLEXPORT void irmp_instance_unlock(struct irmp_instance *state);
+
+/**
+ * @brief IR decoder result data at the library's public API.
+ */
+struct irmp_result_data {
+       uint32_t protocol;      /**!< protocol, e.g. NEC_PROTOCOL */
+       const char *protocol_name;      /**!< name of the protocol */
+       uint32_t address;       /**!< address */
+       uint32_t command;       /**!< command */
+       uint32_t flags;         /**!< flags currently only repetition (bit 0) */
+       uint32_t start_sample;  /**!< the sampleindex there the detected command started */
+       uint32_t end_sample;    /**!< the sampleindex there the detected command ended */
+};
+
+#define IRMP_DATA_FLAG_REPETITION      (1 << 0)
+#define IRMP_DATA_FLAG_RELEASE         (1 << 1)
+
+/**
+ * @brief Query the IRMP library's configured sample rate.
+ *
+ * The internally used sample rate is a compile time option. Any data
+ * that is provided at runtime needs to match this rate, or detection
+ * will fail.
+ */
+IRMP_DLLEXPORT uint32_t irmp_get_sample_rate(void);
+
+/**
+ * @brief Reset internal decoder state.
+ *
+ * This must be called before data processing starts.
+ */
+IRMP_DLLEXPORT void irmp_reset_state(void);
+
+/**
+ * @brief Feed an individual sample to the detector.
+ *
+ * See @ref irmp_get_result_data() for result retrieval when detection
+ * of an IR frame completes. Make sure @ref irmp_reset_state() was
+ * called before providing the first sample.
+ *
+ * @param[in] sample The pin value to feed to the detector.
+ *
+ * @returns Non-zero when an IR frame was detected.
+ */
+IRMP_DLLEXPORT int irmp_add_one_sample(int sample);
+
+#if WITH_IRMP_DETECT_BUFFER
+/**
+ * @brief Process the given buffer until an IR frame is found.
+ *
+ * Stops at the first detected IR frame, and returns its data. Subsequent
+ * calls resume processing at the previously stopped position. Make sure
+ * @ref irmp_reset_state() was called before the first detect call.
+ *
+ * @param[in] buf Pointer to the data buffer.
+ * @param[in] len Number of samples in the Buffer.
+ */
+IRMP_DLLEXPORT struct irmp_result_data irmp_detect_buffer(const uint8_t *buf, size_t len);
+#endif
+
+/**
+ * @brief Query result data after detection succeeded.
+ *
+ * @param[out] data The caller provided result buffer.
+ *
+ * @returns Non-zero if data was available, zero otherwise.
+ */
+IRMP_DLLEXPORT int irmp_get_result_data(struct irmp_result_data *data);
+
+/**
+ * @brief Resolve the protocol identifer to the protocol's name.
+ *
+ * @param[in] protocol The numerical identifier.
+ *
+ * @returns A pointer to the string literal, or #NULL in case of failure.
+ */
+IRMP_DLLEXPORT const char *irmp_get_protocol_name(uint32_t protocol);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/irmp/irmp.c b/irmp/irmp.c
new file mode 100644 (file)
index 0000000..42b04c8
--- /dev/null
@@ -0,0 +1,6151 @@
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmp.c - infrared multi-protocol decoder, supports several remote control protocols
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ *
+ * Supported AVR mikrocontrollers:
+ *
+ * ATtiny87,  ATtiny167
+ * ATtiny45,  ATtiny85
+ * ATtiny44,  ATtiny84
+ * ATmega8,   ATmega16,  ATmega32
+ * ATmega162
+ * ATmega164, ATmega324, ATmega644,  ATmega644P, ATmega1284, ATmega1284P
+ * ATmega88,  ATmega88P, ATmega168,  ATmega168P, ATmega328P
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#include "irmp.h"
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 || IRMP_SUPPORT_NOKIA_PROTOCOL == 1 || IRMP_SUPPORT_IR60_PROTOCOL == 1
+#  define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL  1
+#else
+#  define IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL  0
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 || IRMP_SUPPORT_RUWIDO_PROTOCOL == 1
+#  define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL   1
+#else
+#  define IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL   0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 ||                   \
+    IRMP_SUPPORT_RCII_PROTOCOL == 1 ||                  \
+    IRMP_SUPPORT_S100_PROTOCOL == 1 ||                  \
+    IRMP_SUPPORT_RC6_PROTOCOL == 1 ||                   \
+    IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1 ||    \
+    IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1 ||     \
+    IRMP_SUPPORT_IR60_PROTOCOL == 1 ||                  \
+    IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1 ||               \
+    IRMP_SUPPORT_MERLIN_PROTOCOL == 1 ||                \
+    IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+#  define IRMP_SUPPORT_MANCHESTER                   1
+#else
+#  define IRMP_SUPPORT_MANCHESTER                   0
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+#  define IRMP_SUPPORT_SERIAL                       1
+#else
+#  define IRMP_SUPPORT_SERIAL                       0
+#endif
+
+#define IRMP_KEY_REPETITION_LEN                 (uint_fast16_t)(F_INTERRUPTS * 150.0e-3 + 0.5)           // autodetect key repetition within 150 msec
+
+#define MIN_TOLERANCE_00                        1.0                           // -0%
+#define MAX_TOLERANCE_00                        1.0                           // +0%
+
+#define MIN_TOLERANCE_02                        0.98                          // -2%
+#define MAX_TOLERANCE_02                        1.02                          // +2%
+
+#define MIN_TOLERANCE_03                        0.97                          // -3%
+#define MAX_TOLERANCE_03                        1.03                          // +3%
+
+#define MIN_TOLERANCE_05                        0.95                          // -5%
+#define MAX_TOLERANCE_05                        1.05                          // +5%
+
+#define MIN_TOLERANCE_10                        0.9                           // -10%
+#define MAX_TOLERANCE_10                        1.1                           // +10%
+
+#define MIN_TOLERANCE_15                        0.85                          // -15%
+#define MAX_TOLERANCE_15                        1.15                          // +15%
+
+#define MIN_TOLERANCE_20                        0.8                           // -20%
+#define MAX_TOLERANCE_20                        1.2                           // +20%
+
+#define MIN_TOLERANCE_30                        0.7                           // -30%
+#define MAX_TOLERANCE_30                        1.3                           // +30%
+
+#define MIN_TOLERANCE_40                        0.6                           // -40%
+#define MAX_TOLERANCE_40                        1.4                           // +40%
+
+#define MIN_TOLERANCE_50                        0.5                           // -50%
+#define MAX_TOLERANCE_50                        1.5                           // +50%
+
+#define MIN_TOLERANCE_60                        0.4                           // -60%
+#define MAX_TOLERANCE_60                        1.6                           // +60%
+
+#define MIN_TOLERANCE_70                        0.3                           // -70%
+#define MAX_TOLERANCE_70                        1.7                           // +70%
+
+#define SIRCS_START_BIT_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_START_BIT_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_START_BIT_PAUSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#if IRMP_SUPPORT_NETBOX_PROTOCOL                // only 5% to avoid conflict with NETBOX:
+#  define SIRCS_START_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#else                                           // only 5% + 1 to avoid conflict with RC6:
+#  define SIRCS_START_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * SIRCS_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#endif
+#define SIRCS_1_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_1_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * SIRCS_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_0_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_0_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * SIRCS_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIRCS_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIRCS_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * SIRCS_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NEC_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_START_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_START_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * NEC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_REPEAT_START_BIT_PAUSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_REPEAT_START_BIT_PAUSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * NEC_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_PULSE_LEN_MIN                       ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_PULSE_LEN_MAX                       ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_1_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_1_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define NEC_0_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define NEC_0_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+// autodetect nec repetition frame within 50 msec:
+// NEC seems to send the first repetition frame after 40ms, further repetition frames after 100 ms
+#if 0
+#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX          (uint_fast16_t)(F_INTERRUPTS * NEC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+#else
+#define NEC_FRAME_REPEAT_PAUSE_LEN_MAX          (uint_fast16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5)
+#endif
+
+#define SAMSUNG_START_BIT_PULSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNG_START_BIT_PULSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNG_START_BIT_PAUSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNG_START_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNG_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNG_1_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_1_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNG_0_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNG_0_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNG_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define SAMSUNGAH_START_BIT_PULSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNGAH_START_BIT_PULSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNGAH_START_BIT_PAUSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SAMSUNGAH_START_BIT_PAUSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SAMSUNGAH_PULSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_PULSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNGAH_1_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_1_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define SAMSUNGAH_0_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define SAMSUNGAH_0_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * SAMSUNGAH_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define MATSUSHITA_START_BIT_PULSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MATSUSHITA_START_BIT_PULSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MATSUSHITA_START_BIT_PAUSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MATSUSHITA_START_BIT_PAUSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MATSUSHITA_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MATSUSHITA_1_PAUSE_LEN_MIN              ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_1_PAUSE_LEN_MAX              ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MATSUSHITA_0_PAUSE_LEN_MIN              ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MATSUSHITA_0_PAUSE_LEN_MAX              ((uint_fast8_t)(F_INTERRUPTS * MATSUSHITA_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+
+#define KASEIKYO_START_BIT_PULSE_LEN_MIN        ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_START_BIT_PULSE_LEN_MAX        ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_START_BIT_PAUSE_LEN_MIN        ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_START_BIT_PAUSE_LEN_MAX        ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define KASEIKYO_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define KASEIKYO_1_PAUSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_1_PAUSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define KASEIKYO_0_PAUSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define KASEIKYO_0_PAUSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KASEIKYO_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define MITSU_HEAVY_START_BIT_PULSE_LEN_MIN     ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MITSU_HEAVY_START_BIT_PULSE_LEN_MAX     ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN     ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX     ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MITSU_HEAVY_PULSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define MITSU_HEAVY_PULSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define MITSU_HEAVY_1_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MITSU_HEAVY_1_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define MITSU_HEAVY_0_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define MITSU_HEAVY_0_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * MITSU_HEAVY_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define VINCENT_START_BIT_PULSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define VINCENT_START_BIT_PULSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define VINCENT_START_BIT_PAUSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define VINCENT_START_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * VINCENT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define VINCENT_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * VINCENT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define VINCENT_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * VINCENT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define VINCENT_1_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * VINCENT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define VINCENT_1_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * VINCENT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define VINCENT_0_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * VINCENT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define VINCENT_0_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * VINCENT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define PANASONIC_START_BIT_PULSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PANASONIC_START_BIT_PULSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PANASONIC_START_BIT_PAUSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PANASONIC_START_BIT_PAUSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PANASONIC_PULSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define PANASONIC_PULSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define PANASONIC_1_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PANASONIC_1_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PANASONIC_0_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PANASONIC_0_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * PANASONIC_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define RECS80_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RECS80_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80_PULSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80_PULSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * RECS80_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RECS80_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RECS80_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1 // BOSE conflicts with RC5, so keep tolerance for RC5 minimal here:
+#define RC5_START_BIT_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RC5_START_BIT_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#else
+#define RC5_START_BIT_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC5_START_BIT_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#endif
+
+#define RC5_BIT_LEN_MIN                         ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC5_BIT_LEN_MAX                         ((uint_fast8_t)(F_INTERRUPTS * RC5_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RCII_START_BIT_PULSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT_PULSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCII_START_BIT_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCII_START_BIT2_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT2_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCII_START_BIT2_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * RCII_START_BIT2_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+
+#define RCII_BIT_LEN_MIN                        ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCII_BIT_LEN                            ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME))
+#define RCII_BIT_LEN_MAX                        ((uint_fast8_t)(F_INTERRUPTS * RCII_BIT_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1 // BOSE conflicts with S100, so keep tolerance for S100 minimal here:
+#define S100_START_BIT_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define S100_START_BIT_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#else
+#define S100_START_BIT_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define S100_START_BIT_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#endif
+
+#define S100_BIT_LEN_MIN                         ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define S100_BIT_LEN_MAX                         ((uint_fast8_t)(F_INTERRUPTS * S100_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define DENON_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * DENON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define DENON_1_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_1_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * DENON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+// RUWIDO (see t-home-mediareceiver-15kHz.txt) conflicts here with DENON
+#define DENON_0_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define DENON_0_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * DENON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define DENON_AUTO_REPETITION_PAUSE_LEN         ((uint_fast16_t)(F_INTERRUPTS * DENON_AUTO_REPETITION_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define THOMSON_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * THOMSON_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define THOMSON_1_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_1_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * THOMSON_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define THOMSON_0_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define THOMSON_0_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * THOMSON_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RC6_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_START_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_START_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RC6_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_TOGGLE_BIT_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_TOGGLE_BIT_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RC6_TOGGLE_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RC6_BIT_PULSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_BIT_PULSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_60 + 0.5) + 1)       // pulses: 300 - 800
+#define RC6_BIT_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RC6_BIT_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RC6_BIT_TIME * MAX_TOLERANCE_20 + 0.5) + 1)       // pauses: 300 - 600
+
+#define RECS80EXT_START_BIT_PULSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80EXT_START_BIT_PULSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80EXT_START_BIT_PAUSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_START_BIT_PAUSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80EXT_PULSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RECS80EXT_PULSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RECS80EXT_1_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_1_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RECS80EXT_0_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RECS80EXT_0_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * RECS80EXT_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NUBERT_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * NUBERT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_1_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_1_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_0_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_0_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NUBERT_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NUBERT_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * NUBERT_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define FAN_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_START_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_START_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * FAN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_1_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_1_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_1_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_1_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FAN_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_0_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_0_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define FAN_0_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FAN_0_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FAN_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define SPEAKER_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_1_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_1_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_0_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_0_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define SPEAKER_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define SPEAKER_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * SPEAKER_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT2_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX   ((PAUSE_LEN)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT3_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1) // value must be below IRMP_TIMEOUT
+#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_START_BIT4_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_PULSE_LEN_MIN              ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_PULSE_LEN_MAX              ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_1_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_1_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_0_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_0_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_R_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_R_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_R_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN  ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX  ((uint_fast8_t)(F_INTERRUPTS * BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define IR60_TIMEOUT_LEN                        ((uint_fast8_t)(F_INTERRUPTS * IR60_TIMEOUT_TIME * 0.5))
+#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN    ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX    ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_BIT_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define GRUNDIG_NOKIA_IR60_BIT_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN    ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) + 1)
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX    ((uint_fast8_t)(F_INTERRUPTS * GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN       ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX       ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define FDC_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)   // 5%: avoid conflict with NETBOX
+#define FDC_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#define FDC_START_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define FDC_START_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * FDC_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5))
+#define FDC_PULSE_LEN_MIN                       ((uint_fast8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define FDC_PULSE_LEN_MAX                       ((uint_fast8_t)(F_INTERRUPTS * FDC_PULSE_TIME * MAX_TOLERANCE_50 + 0.5) + 1)
+#define FDC_1_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define FDC_1_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FDC_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#if 0
+#define FDC_0_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)   // could be negative: 255
+#else
+#define FDC_0_PAUSE_LEN_MIN                     (1)                                                                         // simply use 1
+#endif
+#define FDC_0_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * FDC_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define RCCAR_START_BIT_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RCCAR_START_BIT_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RCCAR_START_BIT_PAUSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RCCAR_START_BIT_PAUSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * RCCAR_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RCCAR_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RCCAR_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * RCCAR_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RCCAR_1_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCCAR_1_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RCCAR_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define RCCAR_0_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define RCCAR_0_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * RCCAR_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define JVC_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_REPEAT_START_BIT_PAUSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MIN_TOLERANCE_40 + 0.5) - 1)  // HACK!
+#define JVC_REPEAT_START_BIT_PAUSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * (JVC_FRAME_REPEAT_PAUSE_TIME - IRMP_TIMEOUT_TIME) * MAX_TOLERANCE_70 + 0.5) - 1)  // HACK!
+#define JVC_PULSE_LEN_MIN                       ((uint_fast8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_PULSE_LEN_MAX                       ((uint_fast8_t)(F_INTERRUPTS * JVC_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_1_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_1_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * JVC_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define JVC_0_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define JVC_0_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * JVC_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+// autodetect JVC repetition frame within 50 msec:
+#define JVC_FRAME_REPEAT_PAUSE_LEN_MAX          (uint_fast16_t)(F_INTERRUPTS * JVC_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define NIKON_START_BIT_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_START_BIT_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * NIKON_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_START_BIT_PAUSE_LEN_MIN           ((uint_fast16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_START_BIT_PAUSE_LEN_MAX           ((uint_fast16_t)(F_INTERRUPTS * NIKON_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MIN    ((uint_fast8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_REPEAT_START_BIT_PAUSE_LEN_MAX    ((uint_fast8_t)(F_INTERRUPTS * NIKON_REPEAT_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * NIKON_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_1_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_1_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * NIKON_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_0_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define NIKON_0_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * NIKON_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define NIKON_FRAME_REPEAT_PAUSE_LEN_MAX        (uint_fast16_t)(F_INTERRUPTS * NIKON_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define KATHREIN_START_BIT_PULSE_LEN_MIN        ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_START_BIT_PULSE_LEN_MAX        ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_START_BIT_PAUSE_LEN_MIN        ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_START_BIT_PAUSE_LEN_MAX        ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_1_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_1_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_1_PAUSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_1_PAUSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_1_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_0_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_0_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_0_PAUSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_0_PAUSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_0_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * KATHREIN_SYNC_BIT_PAUSE_LEN_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define NETBOX_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define NETBOX_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define NETBOX_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define NETBOX_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * NETBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define NETBOX_PULSE_LEN                        ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME))
+#define NETBOX_PAUSE_LEN                        ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME))
+#define NETBOX_PULSE_REST_LEN                   ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PULSE_TIME / 4))
+#define NETBOX_PAUSE_REST_LEN                   ((uint_fast8_t)(F_INTERRUPTS * NETBOX_PAUSE_TIME / 4))
+
+#define LEGO_START_BIT_PULSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_START_BIT_PULSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_START_BIT_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_START_BIT_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * LEGO_START_BIT_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_PULSE_LEN_MIN                      ((uint_fast8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_PULSE_LEN_MAX                      ((uint_fast8_t)(F_INTERRUPTS * LEGO_PULSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_1_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_1_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * LEGO_1_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+#define LEGO_0_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MIN_TOLERANCE_40 + 0.5) - 1)
+#define LEGO_0_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * LEGO_0_PAUSE_TIME * MAX_TOLERANCE_40 + 0.5) + 1)
+
+#define IRMP16_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * IRMP16_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_PULSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * IRMP16_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_PULSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * IRMP16_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * IRMP16_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * IRMP16_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define IRMP16_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * IRMP16_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define IRMP16_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * IRMP16_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define GREE_START_BIT_PULSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_START_BIT_PULSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_START_BIT_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_START_BIT_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * GREE_START_BIT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_PULSE_LEN_MIN                      ((uint_fast8_t)(F_INTERRUPTS * GREE_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_PULSE_LEN_MAX                      ((uint_fast8_t)(F_INTERRUPTS * GREE_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_1_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * GREE_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_1_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * GREE_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define GREE_0_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * GREE_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define GREE_0_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * GREE_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define BOSE_START_BIT_PULSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_START_BIT_PULSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_START_BIT_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_START_BIT_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * BOSE_START_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_PULSE_LEN_MIN                       ((uint_fast8_t)(F_INTERRUPTS * BOSE_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_PULSE_LEN_MAX                       ((uint_fast8_t)(F_INTERRUPTS * BOSE_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_1_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * BOSE_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_1_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * BOSE_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_0_PAUSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * BOSE_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define BOSE_0_PAUSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * BOSE_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define BOSE_FRAME_REPEAT_PAUSE_LEN_MAX          (uint_fast16_t)(F_INTERRUPTS * 100.0e-3 * MAX_TOLERANCE_20 + 0.5)
+
+#define A1TVBOX_START_BIT_PULSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define A1TVBOX_START_BIT_PULSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define A1TVBOX_START_BIT_PAUSE_LEN_MIN         ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define A1TVBOX_START_BIT_PAUSE_LEN_MAX         ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define A1TVBOX_BIT_PULSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define A1TVBOX_BIT_PULSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define A1TVBOX_BIT_PAUSE_LEN_MIN               ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define A1TVBOX_BIT_PAUSE_LEN_MAX               ((uint_fast8_t)(F_INTERRUPTS * A1TVBOX_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define MERLIN_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MERLIN_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MERLIN_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define MERLIN_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * MERLIN_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define MERLIN_BIT_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define MERLIN_BIT_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define MERLIN_BIT_PAUSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define MERLIN_BIT_PAUSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * MERLIN_BIT_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+
+#define ORTEK_START_BIT_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_START_BIT_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_START_BIT_PAUSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_START_BIT_PAUSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * ORTEK_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_BIT_PULSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_BIT_PULSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ORTEK_BIT_PAUSE_LEN_MIN                 ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ORTEK_BIT_PAUSE_LEN_MAX                 ((uint_fast8_t)(F_INTERRUPTS * ORTEK_BIT_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+
+#define TELEFUNKEN_START_BIT_PULSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_START_BIT_PULSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define TELEFUNKEN_START_BIT_PAUSE_LEN_MIN      ((uint_fast8_t)(F_INTERRUPTS * (TELEFUNKEN_START_BIT_PAUSE_TIME) * MIN_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_START_BIT_PAUSE_LEN_MAX      ((uint_fast8_t)(F_INTERRUPTS * (TELEFUNKEN_START_BIT_PAUSE_TIME) * MAX_TOLERANCE_10 + 0.5) - 1)
+#define TELEFUNKEN_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define TELEFUNKEN_1_PAUSE_LEN_MIN              ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_1_PAUSE_LEN_MAX              ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+#define TELEFUNKEN_0_PAUSE_LEN_MIN              ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
+#define TELEFUNKEN_0_PAUSE_LEN_MAX              ((uint_fast8_t)(F_INTERRUPTS * TELEFUNKEN_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
+// autodetect TELEFUNKEN repetition frame within 50 msec:
+// #define TELEFUNKEN_FRAME_REPEAT_PAUSE_LEN_MAX   (uint_fast16_t)(F_INTERRUPTS * TELEFUNKEN_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define ROOMBA_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ROOMBA_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ROOMBA_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define ROOMBA_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define ROOMBA_1_PAUSE_LEN_EXACT                ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME + 0.5))
+#define ROOMBA_1_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_1_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_0_PAUSE_LEN                      ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME))
+#define ROOMBA_0_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_0_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define ROOMBA_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define ROOMBA_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * ROOMBA_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define RCMM32_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RCMM32_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_PULSE_LEN_MIN                ((uint_fast8_t)(F_INTERRUPTS * RCMM32_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_PULSE_LEN_MAX                ((uint_fast8_t)(F_INTERRUPTS * RCMM32_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_00_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_00_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_00_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_00_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_01_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_01_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_01_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_01_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_10_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_10_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_10_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_10_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define RCMM32_BIT_11_PAUSE_LEN_MIN             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_11_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define RCMM32_BIT_11_PAUSE_LEN_MAX             ((uint_fast8_t)(F_INTERRUPTS * RCMM32_11_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+
+#define PENTAX_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PENTAX_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PENTAX_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define PENTAX_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * PENTAX_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define PENTAX_1_PAUSE_LEN_EXACT                ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME + 0.5))
+#define PENTAX_PULSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_PULSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * PENTAX_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_0_PAUSE_LEN                      ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME))
+#define PENTAX_PULSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_PULSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * PENTAX_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define PENTAX_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define PENTAX_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * PENTAX_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define ACP24_START_BIT_PULSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PULSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_START_BIT_PULSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PULSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_START_BIT_PAUSE_LEN_MIN           ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_START_BIT_PAUSE_LEN_MAX           ((uint_fast8_t)(F_INTERRUPTS * ACP24_START_BIT_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_PULSE_LEN_MIN                     ((uint_fast8_t)(F_INTERRUPTS * ACP24_PULSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_PULSE_LEN_MAX                     ((uint_fast8_t)(F_INTERRUPTS * ACP24_PULSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_1_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * ACP24_1_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_1_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * ACP24_1_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+#define ACP24_0_PAUSE_LEN_MIN                   ((uint_fast8_t)(F_INTERRUPTS * ACP24_0_PAUSE_TIME * MIN_TOLERANCE_15 + 0.5) - 1)
+#define ACP24_0_PAUSE_LEN_MAX                   ((uint_fast8_t)(F_INTERRUPTS * ACP24_0_PAUSE_TIME * MAX_TOLERANCE_15 + 0.5) + 1)
+
+#define METZ_START_BIT_PULSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PULSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define METZ_START_BIT_PULSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PULSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define METZ_START_BIT_PAUSE_LEN_MIN            ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PAUSE_TIME * MIN_TOLERANCE_05 + 0.5) - 1)
+#define METZ_START_BIT_PAUSE_LEN_MAX            ((uint_fast8_t)(F_INTERRUPTS * METZ_START_BIT_PAUSE_TIME * MAX_TOLERANCE_05 + 0.5) + 1)
+#define METZ_PULSE_LEN_MIN                      ((uint_fast8_t)(F_INTERRUPTS * METZ_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_PULSE_LEN_MAX                      ((uint_fast8_t)(F_INTERRUPTS * METZ_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_1_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * METZ_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_1_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * METZ_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_0_PAUSE_LEN_MIN                    ((uint_fast8_t)(F_INTERRUPTS * METZ_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define METZ_0_PAUSE_LEN_MAX                    ((uint_fast8_t)(F_INTERRUPTS * METZ_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define METZ_FRAME_REPEAT_PAUSE_LEN_MAX         (uint_fast16_t)(F_INTERRUPTS * METZ_FRAME_REPEAT_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5)
+
+#define RADIO1_START_BIT_PULSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PULSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RADIO1_START_BIT_PULSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PULSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RADIO1_START_BIT_PAUSE_LEN_MIN          ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PAUSE_TIME * MIN_TOLERANCE_10 + 0.5) - 1)
+#define RADIO1_START_BIT_PAUSE_LEN_MAX          ((uint_fast8_t)(F_INTERRUPTS * RADIO1_START_BIT_PAUSE_TIME * MAX_TOLERANCE_10 + 0.5) + 1)
+#define RADIO1_1_PAUSE_LEN_EXACT                ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME + 0.5))
+#define RADIO1_1_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_1_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_1_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_1_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_1_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_0_PAUSE_LEN                      ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME))
+#define RADIO1_0_PULSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PULSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_0_PULSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PULSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+#define RADIO1_0_PAUSE_LEN_MIN                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME * MIN_TOLERANCE_20 + 0.5) - 1)
+#define RADIO1_0_PAUSE_LEN_MAX                  ((uint_fast8_t)(F_INTERRUPTS * RADIO1_0_PAUSE_TIME * MAX_TOLERANCE_20 + 0.5) + 1)
+
+#define AUTO_FRAME_REPETITION_LEN               (uint_fast16_t)(F_INTERRUPTS * AUTO_FRAME_REPETITION_TIME + 0.5)       // use uint_fast16_t!
+
+#ifdef ANALYZE
+#  define ANALYZE_PUTCHAR(a)                    { if (! silent)             { putchar (a);          } }
+#  define ANALYZE_ONLY_NORMAL_PUTCHAR(a)        { if (! silent && !verbose) { putchar (a);          } }
+#  define ANALYZE_PRINTF(...)                   { if (verbose)              { printf (__VA_ARGS__); } }
+#  define ANALYZE_ONLY_NORMAL_PRINTF(...)       { if (! silent && !verbose) { printf (__VA_ARGS__); } }
+#  define ANALYZE_NEWLINE()                     { if (verbose)              { putchar ('\n');       } }
+static int                                      silent;
+static int                                      time_counter;
+static int                                      verbose;
+
+#elif 0 /* not every PIC compiler knows variadic macros :-( */
+#  define ANALYZE_PUTCHAR(a)
+#  define ANALYZE_ONLY_NORMAL_PUTCHAR(a)
+#  define ANALYZE_PRINTF(...)
+#  define ANALYZE_ONLY_NORMAL_PRINTF(...)
+#  define ANALYZE_NEWLINE()
+
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+static void                                     (*irmp_callback_ptr) (uint_fast8_t);
+#endif // IRMP_USE_CALLBACK == 1
+
+#define PARITY_CHECK_OK                         1
+#define PARITY_CHECK_FAILED                     0
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Protocol names
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined(UNIX_OR_WINDOWS) || IRMP_PROTOCOL_NAMES == 1
+static const char proto_unknown[]       PROGMEM = "UNKNOWN";
+static const char proto_sircs[]         PROGMEM = "SIRCS";
+static const char proto_nec[]           PROGMEM = "NEC";
+static const char proto_samsung[]       PROGMEM = "SAMSUNG";
+static const char proto_matsushita[]    PROGMEM = "MATSUSH";
+static const char proto_kaseikyo[]      PROGMEM = "KASEIKYO";
+static const char proto_recs80[]        PROGMEM = "RECS80";
+static const char proto_rc5[]           PROGMEM = "RC5";
+static const char proto_denon[]         PROGMEM = "DENON";
+static const char proto_rc6[]           PROGMEM = "RC6";
+static const char proto_samsung32[]     PROGMEM = "SAMSG32";
+static const char proto_apple[]         PROGMEM = "APPLE";
+static const char proto_recs80ext[]     PROGMEM = "RECS80EX";
+static const char proto_nubert[]        PROGMEM = "NUBERT";
+static const char proto_bang_olufsen[]  PROGMEM = "BANG OLU";
+static const char proto_grundig[]       PROGMEM = "GRUNDIG";
+static const char proto_nokia[]         PROGMEM = "NOKIA";
+static const char proto_siemens[]       PROGMEM = "SIEMENS";
+static const char proto_fdc[]           PROGMEM = "FDC";
+static const char proto_rccar[]         PROGMEM = "RCCAR";
+static const char proto_jvc[]           PROGMEM = "JVC";
+static const char proto_rc6a[]          PROGMEM = "RC6A";
+static const char proto_nikon[]         PROGMEM = "NIKON";
+static const char proto_ruwido[]        PROGMEM = "RUWIDO";
+static const char proto_ir60[]          PROGMEM = "IR60";
+static const char proto_kathrein[]      PROGMEM = "KATHREIN";
+static const char proto_netbox[]        PROGMEM = "NETBOX";
+static const char proto_nec16[]         PROGMEM = "NEC16";
+static const char proto_nec42[]         PROGMEM = "NEC42";
+static const char proto_lego[]          PROGMEM = "LEGO";
+static const char proto_thomson[]       PROGMEM = "THOMSON";
+static const char proto_bose[]          PROGMEM = "BOSE";
+static const char proto_a1tvbox[]       PROGMEM = "A1TVBOX";
+static const char proto_ortek[]         PROGMEM = "ORTEK";
+static const char proto_telefunken[]    PROGMEM = "TELEFUNKEN";
+static const char proto_roomba[]        PROGMEM = "ROOMBA";
+static const char proto_rcmm32[]        PROGMEM = "RCMM32";
+static const char proto_rcmm24[]        PROGMEM = "RCMM24";
+static const char proto_rcmm12[]        PROGMEM = "RCMM12";
+static const char proto_speaker[]       PROGMEM = "SPEAKER";
+static const char proto_lgair[]         PROGMEM = "LGAIR";
+static const char proto_samsung48[]     PROGMEM = "SAMSG48";
+static const char proto_merlin[]        PROGMEM = "MERLIN";
+static const char proto_pentax[]        PROGMEM = "PENTAX";
+static const char proto_fan[]           PROGMEM = "FAN";
+static const char proto_s100[]          PROGMEM = "S100";
+static const char proto_acp24[]         PROGMEM = "ACP24";
+static const char proto_technics[]      PROGMEM = "TECHNICS";
+static const char proto_panasonic[]     PROGMEM = "PANASONIC";
+static const char proto_mitsu_heavy[]   PROGMEM = "MITSU_HEAVY";
+static const char proto_vincent[]       PROGMEM = "VINCENT";
+static const char proto_samsungah[]     PROGMEM = "SAMSUNGAH";
+static const char proto_irmp16[]        PROGMEM = "IRMP16";
+static const char proto_gree[]          PROGMEM = "GREE";
+static const char proto_rcii[]          PROGMEM = "RCII";
+static const char proto_metz[]          PROGMEM = "METZ";
+static const char proto_onkyo[]         PROGMEM = "ONKYO";
+
+static const char proto_radio1[]        PROGMEM = "RADIO1";
+
+const char * const
+irmp_protocol_names[IRMP_N_PROTOCOLS + 1] PROGMEM =
+{
+    proto_unknown,
+    proto_sircs,
+    proto_nec,
+    proto_samsung,
+    proto_matsushita,
+    proto_kaseikyo,
+    proto_recs80,
+    proto_rc5,
+    proto_denon,
+    proto_rc6,
+    proto_samsung32,
+    proto_apple,
+    proto_recs80ext,
+    proto_nubert,
+    proto_bang_olufsen,
+    proto_grundig,
+    proto_nokia,
+    proto_siemens,
+    proto_fdc,
+    proto_rccar,
+    proto_jvc,
+    proto_rc6a,
+    proto_nikon,
+    proto_ruwido,
+    proto_ir60,
+    proto_kathrein,
+    proto_netbox,
+    proto_nec16,
+    proto_nec42,
+    proto_lego,
+    proto_thomson,
+    proto_bose,
+    proto_a1tvbox,
+    proto_ortek,
+    proto_telefunken,
+    proto_roomba,
+    proto_rcmm32,
+    proto_rcmm24,
+    proto_rcmm12,
+    proto_speaker,
+    proto_lgair,
+    proto_samsung48,
+    proto_merlin,
+    proto_pentax,
+    proto_fan,
+    proto_s100,
+    proto_acp24,
+    proto_technics,
+    proto_panasonic,
+    proto_mitsu_heavy,
+    proto_vincent,
+    proto_samsungah,
+    proto_irmp16,
+    proto_gree,
+    proto_rcii,
+    proto_metz,
+    proto_onkyo,
+
+    proto_radio1
+};
+
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Logging
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if IRMP_LOGGING == 1                                                   // logging via UART
+
+#if defined(ARM_STM32F4XX)
+#  define  STM32_GPIO_CLOCK   RCC_AHB1Periph_GPIOA                      // UART2 on PA2
+#  define  STM32_UART_CLOCK   RCC_APB1Periph_USART2
+#  define  STM32_GPIO_PORT    GPIOA
+#  define  STM32_GPIO_PIN     GPIO_Pin_2
+#  define  STM32_GPIO_SOURCE  GPIO_PinSource2
+#  define  STM32_UART_AF      GPIO_AF_USART2
+#  define  STM32_UART_COM     USART2
+#  define  STM32_UART_BAUD    115200                                    // 115200 Baud
+#  include "stm32f4xx_usart.h"
+#elif defined(ARM_STM32F10X)
+#  define  STM32_UART_COM     USART3                                    // UART3 on PB10
+#elif defined(ARDUINO)                                                  // Arduino Serial implementation
+#  if defined(USB_SERIAL)
+#    include "usb_serial.h"
+#  else
+#    error USB_SERIAL not defined in ARDUINO Environment
+#  endif
+#elif defined(_CHIBIOS_HAL_)                                            // ChibiOS HAL
+#  if IRMP_EXT_LOGGING == 1
+#    error IRMP_EXT_LOGGING not implemented for ChibiOS HAL, use regular logging instead
+#  endif
+#else
+#  if IRMP_EXT_LOGGING == 1                                             // use external logging
+#    include "irmpextlog.h"
+#  else                                                                 // normal UART log (IRMP_EXT_LOGGING == 0)
+#    define BAUD                                    9600L
+#  ifndef UNIX_OR_WINDOWS
+#    include <util/setbaud.h>
+#  endif
+
+#ifdef UBRR0H
+
+#define UART0_UBRRH                             UBRR0H
+#define UART0_UBRRL                             UBRR0L
+#define UART0_UCSRA                             UCSR0A
+#define UART0_UCSRB                             UCSR0B
+#define UART0_UCSRC                             UCSR0C
+#define UART0_UDRE_BIT_VALUE                    (1<<UDRE0)
+#define UART0_UCSZ1_BIT_VALUE                   (1<<UCSZ01)
+#define UART0_UCSZ0_BIT_VALUE                   (1<<UCSZ00)
+#ifdef URSEL0
+#define UART0_URSEL_BIT_VALUE                   (1<<URSEL0)
+#else
+#define UART0_URSEL_BIT_VALUE                   (0)
+#endif
+#define UART0_TXEN_BIT_VALUE                    (1<<TXEN0)
+#define UART0_UDR                               UDR0
+#define UART0_U2X                               U2X0
+
+#else
+
+#define UART0_UBRRH                             UBRRH
+#define UART0_UBRRL                             UBRRL
+#define UART0_UCSRA                             UCSRA
+#define UART0_UCSRB                             UCSRB
+#define UART0_UCSRC                             UCSRC
+#define UART0_UDRE_BIT_VALUE                    (1<<UDRE)
+#define UART0_UCSZ1_BIT_VALUE                   (1<<UCSZ1)
+#define UART0_UCSZ0_BIT_VALUE                   (1<<UCSZ0)
+#ifdef URSEL
+#define UART0_URSEL_BIT_VALUE                   (1<<URSEL)
+#else
+#define UART0_URSEL_BIT_VALUE                   (0)
+#endif
+#define UART0_TXEN_BIT_VALUE                    (1<<TXEN)
+#define UART0_UDR                               UDR
+#define UART0_U2X                               U2X
+
+#endif //UBRR0H
+#endif //IRMP_EXT_LOGGING
+#endif //ARM_STM32F4XX
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Initialize  UART
+ *  @details  Initializes UART
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+void
+irmp_uart_init (void)
+{
+#ifndef UNIX_OR_WINDOWS
+#if defined(ARM_STM32F4XX)
+    GPIO_InitTypeDef GPIO_InitStructure;
+    USART_InitTypeDef USART_InitStructure;
+
+    // Clock enable vom TX Pin
+    RCC_AHB1PeriphClockCmd(STM32_GPIO_CLOCK, ENABLE);
+
+    // Clock enable der UART
+    RCC_APB1PeriphClockCmd(STM32_UART_CLOCK, ENABLE);
+
+    // UART Alternative-Funktion mit dem IO-Pin verbinden
+    GPIO_PinAFConfig(STM32_GPIO_PORT,STM32_GPIO_SOURCE,STM32_UART_AF);
+
+    // UART als Alternative-Funktion mit PushPull
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
+
+    // TX-Pin
+    GPIO_InitStructure.GPIO_Pin = STM32_GPIO_PIN;
+    GPIO_Init(STM32_GPIO_PORT, &GPIO_InitStructure);
+
+    // Oversampling
+    USART_OverSampling8Cmd(STM32_UART_COM, ENABLE);
+
+    // init baud rate, 8 data bits, 1 stop bit, no parity, no RTS+CTS
+    USART_InitStructure.USART_BaudRate = STM32_UART_BAUD;
+    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
+    USART_InitStructure.USART_StopBits = USART_StopBits_1;
+    USART_InitStructure.USART_Parity = USART_Parity_No;
+    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+    USART_InitStructure.USART_Mode = USART_Mode_Tx;
+    USART_Init(STM32_UART_COM, &USART_InitStructure);
+
+    // UART enable
+    USART_Cmd(STM32_UART_COM, ENABLE);
+
+#elif defined(ARM_STM32F10X)
+    GPIO_InitTypeDef GPIO_InitStructure;
+    USART_InitTypeDef USART_InitStructure;
+
+    // Clock enable vom TX Pin
+    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // UART3 an PB10
+
+    // Clock enable der UART
+    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
+
+    // UART als Alternative-Funktion mit PushPull
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+
+    // TX-Pin
+    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
+    GPIO_Init(GPIOB, &GPIO_InitStructure);
+
+    // Oversampling
+    USART_OverSampling8Cmd(STM32_UART_COM, ENABLE);
+
+    // init baud rate, 8 data bits, 1 stop bit, no parity, no RTS+CTS
+    USART_InitStructure.USART_BaudRate = 115200;
+    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
+    USART_InitStructure.USART_StopBits = USART_StopBits_1;
+    USART_InitStructure.USART_Parity = USART_Parity_No;
+    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
+    USART_InitStructure.USART_Mode = USART_Mode_Tx;
+    USART_Init(STM32_UART_COM, &USART_InitStructure);
+
+    // UART enable
+    USART_Cmd(STM32_UART_COM, ENABLE);
+
+#elif defined(ARDUINO)
+    // we use the Arduino Serial Imlementation
+    // you have to call Serial.begin(SER_BAUD); in Arduino setup() function
+
+#elif defined (__AVR_XMEGA__)
+
+    PMIC.CTRL |= PMIC_HILVLEN_bm;
+
+    USARTC1.BAUDCTRLB = 0;
+    USARTC1.BAUDCTRLA = F_CPU / 153600 - 1;
+    USARTC1.CTRLA = USART_RXCINTLVL_HI_gc;                                                          // high INT level (receive)
+    USARTC1.CTRLB = USART_TXEN_bm | USART_RXEN_bm;                                                  // activated RX and TX
+    USARTC1.CTRLC = USART_CHSIZE_8BIT_gc;                                                           // 8 Bit
+    PORTC.DIR |= (1<<7);                                                                            // TXD is output
+    PORTC.DIR &= ~(1<<6);
+
+#elif defined (_CHIBIOS_HAL_)
+    // we use the SD interface for logging, no need to init that here
+
+#else
+
+#if (IRMP_EXT_LOGGING == 0)                                                                         // use UART
+    UART0_UBRRH = UBRRH_VALUE;                                                                      // set baud rate
+    UART0_UBRRL = UBRRL_VALUE;
+
+#if USE_2X
+    UART0_UCSRA |= (1<<UART0_U2X);
+#else
+    UART0_UCSRA &= ~(1<<UART0_U2X);
+#endif
+
+    UART0_UCSRC = UART0_UCSZ1_BIT_VALUE | UART0_UCSZ0_BIT_VALUE | UART0_URSEL_BIT_VALUE;
+    UART0_UCSRB |= UART0_TXEN_BIT_VALUE;                                                            // enable UART TX
+#else                                                                                               // other log method
+    initextlog();
+#endif //IRMP_EXT_LOGGING
+#endif //ARM_STM32F4XX
+#endif // UNIX_OR_WINDOWS
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Send character
+ *  @details  Sends character
+ *  @param    ch character to be transmitted
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+void
+irmp_uart_putc (unsigned char ch)
+{
+#ifndef UNIX_OR_WINDOWS
+#if defined(ARM_STM32F4XX) || defined(ARM_STM32F10X)
+    // warten bis altes Byte gesendet wurde
+    while (USART_GetFlagStatus(STM32_UART_COM, USART_FLAG_TXE) == RESET)
+    {
+        ;
+    }
+
+    USART_SendData(STM32_UART_COM, ch);
+
+    if (ch == '\n')
+    {
+        while (USART_GetFlagStatus(STM32_UART_COM, USART_FLAG_TXE) == RESET);
+        USART_SendData(STM32_UART_COM, '\r');
+    }
+
+#elif defined(ARDUINO)
+    // we use the Arduino Serial Imlementation
+    usb_serial_putchar(ch);
+
+#elif defined(_CHIBIOS_HAL_)
+    // use the SD interface from HAL, log to IRMP_LOGGING_SD which is defined in irmpconfig.h
+    sdWriteI(&IRMP_LOGGING_SD,&ch,1);      // we are called from interrupt context, so use the ...I version of the function
+
+#else
+#if (IRMP_EXT_LOGGING == 0)
+
+#  if defined (__AVR_XMEGA__)
+    while (!(USARTC1.STATUS & USART_DREIF_bm))
+    {
+        ;
+    }
+
+    USARTC1.DATA = ch;
+
+#  else // AVR_MEGA
+    while (!(UART0_UCSRA & UART0_UDRE_BIT_VALUE))
+    {
+        ;
+    }
+
+    UART0_UDR = ch;
+
+#  endif // __AVR_XMEGA__
+
+#else
+
+    sendextlog(ch);                                                         // use external log
+
+#endif // IRMP_EXT_LOGGING
+#endif // ARM_STM32F4XX
+#else
+    fputc (ch, stderr);
+#endif // UNIX_OR_WINDOWS
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Log IR signal
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#define STARTCYCLES                       2                                 // min count of zeros before start of logging
+#define ENDBITS                        1000                                 // number of sequenced highbits to detect end
+#define DATALEN                         700                                 // log buffer size
+
+static void
+irmp_log (uint_fast8_t val)
+{
+    static uint8_t          buf[DATALEN];                                   // logging buffer
+    static uint_fast16_t    buf_idx;                                        // index
+    static uint_fast8_t     startcycles;                                    // current number of start-zeros
+    static uint_fast16_t    cnt;                                            // counts sequenced highbits - to detect end
+    static uint_fast8_t     last_val = 1;
+
+    if (! val && (startcycles < STARTCYCLES) && !buf_idx)                   // prevent that single random zeros init logging
+    {
+        startcycles++;
+    }
+    else
+    {
+        startcycles = 0;
+
+        if (! val || buf_idx != 0)                                          // start or continue logging on "0", "1" cannot init logging
+        {
+            if (last_val == val)
+            {
+                cnt++;
+
+                if (val && cnt > ENDBITS)                                   // if high received then look at log-stop condition
+                {                                                           // if stop condition is true, output on uart
+                    uint_fast8_t     i8;
+                    uint_fast16_t    i;
+                    uint_fast16_t    j;
+                    uint_fast8_t     v = '1';
+                    uint_fast16_t    d;
+
+                    for (i8 = 0; i8 < STARTCYCLES; i8++)
+                    {
+                        irmp_uart_putc ('0');                               // the ignored starting zeros
+                    }
+
+                    for (i = 0; i < buf_idx; i++)
+                    {
+                        d = buf[i];
+
+                        if (d == 0xff)
+                        {
+                            i++;
+                            d = buf[i];
+                            i++;
+                            d |= ((uint_fast16_t) buf[i] << 8);
+                        }
+
+                        for (j = 0; j < d; j++)
+                        {
+                            irmp_uart_putc (v);
+                        }
+
+                        v = (v == '1') ? '0' : '1';
+                    }
+
+                    for (i8 = 0; i8 < 20; i8++)
+                    {
+                        irmp_uart_putc ('1');
+                    }
+
+                    irmp_uart_putc ('\n');
+                    buf_idx = 0;
+                    last_val = 1;
+                    cnt = 0;
+                }
+            }
+            else if (buf_idx < DATALEN - 3)
+            {
+                if (cnt >= 0xff)
+                {
+                    buf[buf_idx++]  = 0xff;
+                    buf[buf_idx++]  = (cnt & 0xff);
+                    buf[buf_idx]    = (cnt >> 8);
+                }
+                else
+                {
+                    buf[buf_idx] = cnt;
+                }
+
+                buf_idx++;
+                cnt = 1;
+                last_val = val;
+            }
+        }
+    }
+}
+
+#else
+#define irmp_log(val)
+#endif //IRMP_LOGGING
+
+typedef struct
+{
+    uint_fast8_t    protocol;                                                // ir protocol
+    uint_fast8_t    pulse_1_len_min;                                         // minimum length of pulse with bit value 1
+    uint_fast8_t    pulse_1_len_max;                                         // maximum length of pulse with bit value 1
+    uint_fast8_t    pause_1_len_min;                                         // minimum length of pause with bit value 1
+    uint_fast8_t    pause_1_len_max;                                         // maximum length of pause with bit value 1
+    uint_fast8_t    pulse_0_len_min;                                         // minimum length of pulse with bit value 0
+    uint_fast8_t    pulse_0_len_max;                                         // maximum length of pulse with bit value 0
+    uint_fast8_t    pause_0_len_min;                                         // minimum length of pause with bit value 0
+    uint_fast8_t    pause_0_len_max;                                         // maximum length of pause with bit value 0
+    uint_fast8_t    address_offset;                                          // address offset
+    uint_fast8_t    address_end;                                             // end of address
+    uint_fast8_t    command_offset;                                          // command offset
+    uint_fast8_t    command_end;                                             // end of command
+    uint_fast8_t    complete_len;                                            // complete length of frame
+    uint_fast8_t    stop_bit;                                                // flag: frame has stop bit
+    uint_fast8_t    lsb_first;                                               // flag: LSB first
+    uint_fast8_t    flags;                                                   // some flags
+} IRMP_PARAMETER;
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER sircs_param =
+{
+    IRMP_SIRCS_PROTOCOL,                                                // protocol:        ir protocol
+    SIRCS_1_PULSE_LEN_MIN,                                              // pulse_1_len_min: minimum length of pulse with bit value 1
+    SIRCS_1_PULSE_LEN_MAX,                                              // pulse_1_len_max: maximum length of pulse with bit value 1
+    SIRCS_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    SIRCS_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    SIRCS_0_PULSE_LEN_MIN,                                              // pulse_0_len_min: minimum length of pulse with bit value 0
+    SIRCS_0_PULSE_LEN_MAX,                                              // pulse_0_len_max: maximum length of pulse with bit value 0
+    SIRCS_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    SIRCS_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    SIRCS_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    SIRCS_ADDRESS_OFFSET + SIRCS_ADDRESS_LEN,                           // address_end:     end of address
+    SIRCS_COMMAND_OFFSET,                                               // command_offset:  command offset
+    SIRCS_COMMAND_OFFSET + SIRCS_COMMAND_LEN,                           // command_end:     end of command
+    SIRCS_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    SIRCS_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    SIRCS_LSB,                                                          // lsb_first:       flag: LSB first
+    SIRCS_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nec_param =
+{
+    IRMP_NEC_PROTOCOL,                                                  // protocol:        ir protocol
+    NEC_PULSE_LEN_MIN,                                                  // pulse_1_len_min: minimum length of pulse with bit value 1
+    NEC_PULSE_LEN_MAX,                                                  // pulse_1_len_max: maximum length of pulse with bit value 1
+    NEC_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    NEC_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    NEC_PULSE_LEN_MIN,                                                  // pulse_0_len_min: minimum length of pulse with bit value 0
+    NEC_PULSE_LEN_MAX,                                                  // pulse_0_len_max: maximum length of pulse with bit value 0
+    NEC_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    NEC_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    NEC_ADDRESS_OFFSET,                                                 // address_offset:  address offset
+    NEC_ADDRESS_OFFSET + NEC_ADDRESS_LEN,                               // address_end:     end of address
+    NEC_COMMAND_OFFSET,                                                 // command_offset:  command offset
+    NEC_COMMAND_OFFSET + NEC_COMMAND_LEN,                               // command_end:     end of command
+    NEC_COMPLETE_DATA_LEN,                                              // complete_len:    complete length of frame
+    NEC_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    NEC_LSB,                                                            // lsb_first:       flag: LSB first
+    NEC_FLAGS                                                           // flags:           some flags
+};
+
+static const PROGMEM IRMP_PARAMETER nec_rep_param =
+{
+    IRMP_NEC_PROTOCOL,                                                  // protocol:        ir protocol
+    NEC_PULSE_LEN_MIN,                                                  // pulse_1_len_min: minimum length of pulse with bit value 1
+    NEC_PULSE_LEN_MAX,                                                  // pulse_1_len_max: maximum length of pulse with bit value 1
+    NEC_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    NEC_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    NEC_PULSE_LEN_MIN,                                                  // pulse_0_len_min: minimum length of pulse with bit value 0
+    NEC_PULSE_LEN_MAX,                                                  // pulse_0_len_max: maximum length of pulse with bit value 0
+    NEC_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    NEC_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    0,                                                                  // address_offset:  address offset
+    0,                                                                  // address_end:     end of address
+    0,                                                                  // command_offset:  command offset
+    0,                                                                  // command_end:     end of command
+    0,                                                                  // complete_len:    complete length of frame
+    NEC_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    NEC_LSB,                                                            // lsb_first:       flag: LSB first
+    NEC_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nec42_param =
+{
+    IRMP_NEC42_PROTOCOL,                                                // protocol:        ir protocol
+    NEC_PULSE_LEN_MIN,                                                  // pulse_1_len_min: minimum length of pulse with bit value 1
+    NEC_PULSE_LEN_MAX,                                                  // pulse_1_len_max: maximum length of pulse with bit value 1
+    NEC_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    NEC_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    NEC_PULSE_LEN_MIN,                                                  // pulse_0_len_min: minimum length of pulse with bit value 0
+    NEC_PULSE_LEN_MAX,                                                  // pulse_0_len_max: maximum length of pulse with bit value 0
+    NEC_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    NEC_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    NEC42_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    NEC42_ADDRESS_OFFSET + NEC42_ADDRESS_LEN,                           // address_end:     end of address
+    NEC42_COMMAND_OFFSET,                                               // command_offset:  command offset
+    NEC42_COMMAND_OFFSET + NEC42_COMMAND_LEN,                           // command_end:     end of command
+    NEC42_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    NEC_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    NEC_LSB,                                                            // lsb_first:       flag: LSB first
+    NEC_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER lgair_param =
+{
+    IRMP_LGAIR_PROTOCOL,                                                // protocol:        ir protocol
+    NEC_PULSE_LEN_MIN,                                                  // pulse_1_len_min: minimum length of pulse with bit value 1
+    NEC_PULSE_LEN_MAX,                                                  // pulse_1_len_max: maximum length of pulse with bit value 1
+    NEC_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    NEC_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    NEC_PULSE_LEN_MIN,                                                  // pulse_0_len_min: minimum length of pulse with bit value 0
+    NEC_PULSE_LEN_MAX,                                                  // pulse_0_len_max: maximum length of pulse with bit value 0
+    NEC_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    NEC_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    LGAIR_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    LGAIR_ADDRESS_OFFSET + LGAIR_ADDRESS_LEN,                           // address_end:     end of address
+    LGAIR_COMMAND_OFFSET,                                               // command_offset:  command offset
+    LGAIR_COMMAND_OFFSET + LGAIR_COMMAND_LEN,                           // command_end:     end of command
+    LGAIR_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    NEC_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    NEC_LSB,                                                            // lsb_first:       flag: LSB first
+    NEC_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER samsung_param =
+{
+    IRMP_SAMSUNG_PROTOCOL,                                              // protocol:        ir protocol
+    SAMSUNG_PULSE_LEN_MIN,                                              // pulse_1_len_min: minimum length of pulse with bit value 1
+    SAMSUNG_PULSE_LEN_MAX,                                              // pulse_1_len_max: maximum length of pulse with bit value 1
+    SAMSUNG_1_PAUSE_LEN_MIN,                                            // pause_1_len_min: minimum length of pause with bit value 1
+    SAMSUNG_1_PAUSE_LEN_MAX,                                            // pause_1_len_max: maximum length of pause with bit value 1
+    SAMSUNG_PULSE_LEN_MIN,                                              // pulse_0_len_min: minimum length of pulse with bit value 0
+    SAMSUNG_PULSE_LEN_MAX,                                              // pulse_0_len_max: maximum length of pulse with bit value 0
+    SAMSUNG_0_PAUSE_LEN_MIN,                                            // pause_0_len_min: minimum length of pause with bit value 0
+    SAMSUNG_0_PAUSE_LEN_MAX,                                            // pause_0_len_max: maximum length of pause with bit value 0
+    SAMSUNG_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    SAMSUNG_ADDRESS_OFFSET + SAMSUNG_ADDRESS_LEN,                       // address_end:     end of address
+    SAMSUNG_COMMAND_OFFSET,                                             // command_offset:  command offset
+    SAMSUNG_COMMAND_OFFSET + SAMSUNG_COMMAND_LEN,                       // command_end:     end of command
+    SAMSUNG_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame
+    SAMSUNG_STOP_BIT,                                                   // stop_bit:        flag: frame has stop bit
+    SAMSUNG_LSB,                                                        // lsb_first:       flag: LSB first
+    SAMSUNG_FLAGS                                                       // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER samsungah_param =
+{
+    IRMP_SAMSUNGAH_PROTOCOL,                                            // protocol:        ir protocol
+    SAMSUNGAH_PULSE_LEN_MIN,                                            // pulse_1_len_min: minimum length of pulse with bit value 1
+    SAMSUNGAH_PULSE_LEN_MAX,                                            // pulse_1_len_max: maximum length of pulse with bit value 1
+    SAMSUNGAH_1_PAUSE_LEN_MIN,                                          // pause_1_len_min: minimum length of pause with bit value 1
+    SAMSUNGAH_1_PAUSE_LEN_MAX,                                          // pause_1_len_max: maximum length of pause with bit value 1
+    SAMSUNGAH_PULSE_LEN_MIN,                                            // pulse_0_len_min: minimum length of pulse with bit value 0
+    SAMSUNGAH_PULSE_LEN_MAX,                                            // pulse_0_len_max: maximum length of pulse with bit value 0
+    SAMSUNGAH_0_PAUSE_LEN_MIN,                                          // pause_0_len_min: minimum length of pause with bit value 0
+    SAMSUNGAH_0_PAUSE_LEN_MAX,                                          // pause_0_len_max: maximum length of pause with bit value 0
+    SAMSUNGAH_ADDRESS_OFFSET,                                           // address_offset:  address offset
+    SAMSUNGAH_ADDRESS_OFFSET + SAMSUNGAH_ADDRESS_LEN,                   // address_end:     end of address
+    SAMSUNGAH_COMMAND_OFFSET,                                           // command_offset:  command offset
+    SAMSUNGAH_COMMAND_OFFSET + SAMSUNGAH_COMMAND_LEN,                   // command_end:     end of command
+    SAMSUNGAH_COMPLETE_DATA_LEN,                                        // complete_len:    complete length of frame
+    SAMSUNGAH_STOP_BIT,                                                 // stop_bit:        flag: frame has stop bit
+    SAMSUNGAH_LSB,                                                      // lsb_first:       flag: LSB first
+    SAMSUNGAH_FLAGS                                                     // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER telefunken_param =
+{
+    IRMP_TELEFUNKEN_PROTOCOL,                                           // protocol:        ir protocol
+    TELEFUNKEN_PULSE_LEN_MIN,                                           // pulse_1_len_min: minimum length of pulse with bit value 1
+    TELEFUNKEN_PULSE_LEN_MAX,                                           // pulse_1_len_max: maximum length of pulse with bit value 1
+    TELEFUNKEN_1_PAUSE_LEN_MIN,                                         // pause_1_len_min: minimum length of pause with bit value 1
+    TELEFUNKEN_1_PAUSE_LEN_MAX,                                         // pause_1_len_max: maximum length of pause with bit value 1
+    TELEFUNKEN_PULSE_LEN_MIN,                                           // pulse_0_len_min: minimum length of pulse with bit value 0
+    TELEFUNKEN_PULSE_LEN_MAX,                                           // pulse_0_len_max: maximum length of pulse with bit value 0
+    TELEFUNKEN_0_PAUSE_LEN_MIN,                                         // pause_0_len_min: minimum length of pause with bit value 0
+    TELEFUNKEN_0_PAUSE_LEN_MAX,                                         // pause_0_len_max: maximum length of pause with bit value 0
+    TELEFUNKEN_ADDRESS_OFFSET,                                          // address_offset:  address offset
+    TELEFUNKEN_ADDRESS_OFFSET + TELEFUNKEN_ADDRESS_LEN,                 // address_end:     end of address
+    TELEFUNKEN_COMMAND_OFFSET,                                          // command_offset:  command offset
+    TELEFUNKEN_COMMAND_OFFSET + TELEFUNKEN_COMMAND_LEN,                 // command_end:     end of command
+    TELEFUNKEN_COMPLETE_DATA_LEN,                                       // complete_len:    complete length of frame
+    TELEFUNKEN_STOP_BIT,                                                // stop_bit:        flag: frame has stop bit
+    TELEFUNKEN_LSB,                                                     // lsb_first:       flag: LSB first
+    TELEFUNKEN_FLAGS                                                    // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER matsushita_param =
+{
+    IRMP_MATSUSHITA_PROTOCOL,                                           // protocol:        ir protocol
+    MATSUSHITA_PULSE_LEN_MIN,                                           // pulse_1_len_min: minimum length of pulse with bit value 1
+    MATSUSHITA_PULSE_LEN_MAX,                                           // pulse_1_len_max: maximum length of pulse with bit value 1
+    MATSUSHITA_1_PAUSE_LEN_MIN,                                         // pause_1_len_min: minimum length of pause with bit value 1
+    MATSUSHITA_1_PAUSE_LEN_MAX,                                         // pause_1_len_max: maximum length of pause with bit value 1
+    MATSUSHITA_PULSE_LEN_MIN,                                           // pulse_0_len_min: minimum length of pulse with bit value 0
+    MATSUSHITA_PULSE_LEN_MAX,                                           // pulse_0_len_max: maximum length of pulse with bit value 0
+    MATSUSHITA_0_PAUSE_LEN_MIN,                                         // pause_0_len_min: minimum length of pause with bit value 0
+    MATSUSHITA_0_PAUSE_LEN_MAX,                                         // pause_0_len_max: maximum length of pause with bit value 0
+    MATSUSHITA_ADDRESS_OFFSET,                                          // address_offset:  address offset
+    MATSUSHITA_ADDRESS_OFFSET + MATSUSHITA_ADDRESS_LEN,                 // address_end:     end of address
+    MATSUSHITA_COMMAND_OFFSET,                                          // command_offset:  command offset
+    MATSUSHITA_COMMAND_OFFSET + MATSUSHITA_COMMAND_LEN,                 // command_end:     end of command
+    MATSUSHITA_COMPLETE_DATA_LEN,                                       // complete_len:    complete length of frame
+    MATSUSHITA_STOP_BIT,                                                // stop_bit:        flag: frame has stop bit
+    MATSUSHITA_LSB,                                                     // lsb_first:       flag: LSB first
+    MATSUSHITA_FLAGS                                                    // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER kaseikyo_param =
+{
+    IRMP_KASEIKYO_PROTOCOL,                                             // protocol:        ir protocol
+    KASEIKYO_PULSE_LEN_MIN,                                             // pulse_1_len_min: minimum length of pulse with bit value 1
+    KASEIKYO_PULSE_LEN_MAX,                                             // pulse_1_len_max: maximum length of pulse with bit value 1
+    KASEIKYO_1_PAUSE_LEN_MIN,                                           // pause_1_len_min: minimum length of pause with bit value 1
+    KASEIKYO_1_PAUSE_LEN_MAX,                                           // pause_1_len_max: maximum length of pause with bit value 1
+    KASEIKYO_PULSE_LEN_MIN,                                             // pulse_0_len_min: minimum length of pulse with bit value 0
+    KASEIKYO_PULSE_LEN_MAX,                                             // pulse_0_len_max: maximum length of pulse with bit value 0
+    KASEIKYO_0_PAUSE_LEN_MIN,                                           // pause_0_len_min: minimum length of pause with bit value 0
+    KASEIKYO_0_PAUSE_LEN_MAX,                                           // pause_0_len_max: maximum length of pause with bit value 0
+    KASEIKYO_ADDRESS_OFFSET,                                            // address_offset:  address offset
+    KASEIKYO_ADDRESS_OFFSET + KASEIKYO_ADDRESS_LEN,                     // address_end:     end of address
+    KASEIKYO_COMMAND_OFFSET,                                            // command_offset:  command offset
+    KASEIKYO_COMMAND_OFFSET + KASEIKYO_COMMAND_LEN,                     // command_end:     end of command
+    KASEIKYO_COMPLETE_DATA_LEN,                                         // complete_len:    complete length of frame
+    KASEIKYO_STOP_BIT,                                                  // stop_bit:        flag: frame has stop bit
+    KASEIKYO_LSB,                                                       // lsb_first:       flag: LSB first
+    KASEIKYO_FLAGS                                                      // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER panasonic_param =
+{
+    IRMP_PANASONIC_PROTOCOL,                                            // protocol:        ir protocol
+    PANASONIC_PULSE_LEN_MIN,                                            // pulse_1_len_min: minimum length of pulse with bit value 1
+    PANASONIC_PULSE_LEN_MAX,                                            // pulse_1_len_max: maximum length of pulse with bit value 1
+    PANASONIC_1_PAUSE_LEN_MIN,                                          // pause_1_len_min: minimum length of pause with bit value 1
+    PANASONIC_1_PAUSE_LEN_MAX,                                          // pause_1_len_max: maximum length of pause with bit value 1
+    PANASONIC_PULSE_LEN_MIN,                                            // pulse_0_len_min: minimum length of pulse with bit value 0
+    PANASONIC_PULSE_LEN_MAX,                                            // pulse_0_len_max: maximum length of pulse with bit value 0
+    PANASONIC_0_PAUSE_LEN_MIN,                                          // pause_0_len_min: minimum length of pause with bit value 0
+    PANASONIC_0_PAUSE_LEN_MAX,                                          // pause_0_len_max: maximum length of pause with bit value 0
+    PANASONIC_ADDRESS_OFFSET,                                           // address_offset:  address offset
+    PANASONIC_ADDRESS_OFFSET + PANASONIC_ADDRESS_LEN,                   // address_end:     end of address
+    PANASONIC_COMMAND_OFFSET,                                           // command_offset:  command offset
+    PANASONIC_COMMAND_OFFSET + PANASONIC_COMMAND_LEN,                   // command_end:     end of command
+    PANASONIC_COMPLETE_DATA_LEN,                                        // complete_len:    complete length of frame
+    PANASONIC_STOP_BIT,                                                 // stop_bit:        flag: frame has stop bit
+    PANASONIC_LSB,                                                      // lsb_first:       flag: LSB first
+    PANASONIC_FLAGS                                                     // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER mitsu_heavy_param =
+{
+    IRMP_MITSU_HEAVY_PROTOCOL,                                          // protocol:        ir protocol
+    MITSU_HEAVY_PULSE_LEN_MIN,                                          // pulse_1_len_min: minimum length of pulse with bit value 1
+    MITSU_HEAVY_PULSE_LEN_MAX,                                          // pulse_1_len_max: maximum length of pulse with bit value 1
+    MITSU_HEAVY_1_PAUSE_LEN_MIN,                                        // pause_1_len_min: minimum length of pause with bit value 1
+    MITSU_HEAVY_1_PAUSE_LEN_MAX,                                        // pause_1_len_max: maximum length of pause with bit value 1
+    MITSU_HEAVY_PULSE_LEN_MIN,                                          // pulse_0_len_min: minimum length of pulse with bit value 0
+    MITSU_HEAVY_PULSE_LEN_MAX,                                          // pulse_0_len_max: maximum length of pulse with bit value 0
+    MITSU_HEAVY_0_PAUSE_LEN_MIN,                                        // pause_0_len_min: minimum length of pause with bit value 0
+    MITSU_HEAVY_0_PAUSE_LEN_MAX,                                        // pause_0_len_max: maximum length of pause with bit value 0
+    MITSU_HEAVY_ADDRESS_OFFSET,                                         // address_offset:  address offset
+    MITSU_HEAVY_ADDRESS_OFFSET + MITSU_HEAVY_ADDRESS_LEN,               // address_end:     end of address
+    MITSU_HEAVY_COMMAND_OFFSET,                                         // command_offset:  command offset
+    MITSU_HEAVY_COMMAND_OFFSET + MITSU_HEAVY_COMMAND_LEN,               // command_end:     end of command
+    MITSU_HEAVY_COMPLETE_DATA_LEN,                                      // complete_len:    complete length of frame
+    MITSU_HEAVY_STOP_BIT,                                               // stop_bit:        flag: frame has stop bit
+    MITSU_HEAVY_LSB,                                                    // lsb_first:       flag: LSB first
+    MITSU_HEAVY_FLAGS                                                   // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER vincent_param =
+{
+    IRMP_VINCENT_PROTOCOL,                                              // protocol:        ir protocol
+    VINCENT_PULSE_LEN_MIN,                                              // pulse_1_len_min: minimum length of pulse with bit value 1
+    VINCENT_PULSE_LEN_MAX,                                              // pulse_1_len_max: maximum length of pulse with bit value 1
+    VINCENT_1_PAUSE_LEN_MIN,                                            // pause_1_len_min: minimum length of pause with bit value 1
+    VINCENT_1_PAUSE_LEN_MAX,                                            // pause_1_len_max: maximum length of pause with bit value 1
+    VINCENT_PULSE_LEN_MIN,                                              // pulse_0_len_min: minimum length of pulse with bit value 0
+    VINCENT_PULSE_LEN_MAX,                                              // pulse_0_len_max: maximum length of pulse with bit value 0
+    VINCENT_0_PAUSE_LEN_MIN,                                            // pause_0_len_min: minimum length of pause with bit value 0
+    VINCENT_0_PAUSE_LEN_MAX,                                            // pause_0_len_max: maximum length of pause with bit value 0
+    VINCENT_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    VINCENT_ADDRESS_OFFSET + VINCENT_ADDRESS_LEN,                       // address_end:     end of address
+    VINCENT_COMMAND_OFFSET,                                             // command_offset:  command offset
+    VINCENT_COMMAND_OFFSET + VINCENT_COMMAND_LEN,                       // command_end:     end of command
+    VINCENT_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame
+    VINCENT_STOP_BIT,                                                   // stop_bit:        flag: frame has stop bit
+    VINCENT_LSB,                                                        // lsb_first:       flag: LSB first
+    VINCENT_FLAGS                                                       // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER recs80_param =
+{
+    IRMP_RECS80_PROTOCOL,                                               // protocol:        ir protocol
+    RECS80_PULSE_LEN_MIN,                                               // pulse_1_len_min: minimum length of pulse with bit value 1
+    RECS80_PULSE_LEN_MAX,                                               // pulse_1_len_max: maximum length of pulse with bit value 1
+    RECS80_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    RECS80_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    RECS80_PULSE_LEN_MIN,                                               // pulse_0_len_min: minimum length of pulse with bit value 0
+    RECS80_PULSE_LEN_MAX,                                               // pulse_0_len_max: maximum length of pulse with bit value 0
+    RECS80_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    RECS80_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    RECS80_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    RECS80_ADDRESS_OFFSET + RECS80_ADDRESS_LEN,                         // address_end:     end of address
+    RECS80_COMMAND_OFFSET,                                              // command_offset:  command offset
+    RECS80_COMMAND_OFFSET + RECS80_COMMAND_LEN,                         // command_end:     end of command
+    RECS80_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    RECS80_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    RECS80_LSB,                                                         // lsb_first:       flag: LSB first
+    RECS80_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rc5_param =
+{
+    IRMP_RC5_PROTOCOL,                                                  // protocol:        ir protocol
+    RC5_BIT_LEN_MIN,                                                    // pulse_1_len_min: here: minimum length of short pulse
+    RC5_BIT_LEN_MAX,                                                    // pulse_1_len_max: here: maximum length of short pulse
+    RC5_BIT_LEN_MIN,                                                    // pause_1_len_min: here: minimum length of short pause
+    RC5_BIT_LEN_MAX,                                                    // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    RC5_ADDRESS_OFFSET,                                                 // address_offset:  address offset
+    RC5_ADDRESS_OFFSET + RC5_ADDRESS_LEN,                               // address_end:     end of address
+    RC5_COMMAND_OFFSET,                                                 // command_offset:  command offset
+    RC5_COMMAND_OFFSET + RC5_COMMAND_LEN,                               // command_end:     end of command
+    RC5_COMPLETE_DATA_LEN,                                              // complete_len:    complete length of frame
+    RC5_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    RC5_LSB,                                                            // lsb_first:       flag: LSB first
+    RC5_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rcii_param =
+{
+    IRMP_RCII_PROTOCOL,                                                 // protocol:        ir protocol
+    RCII_BIT_LEN_MIN,                                                   // pulse_1_len_min: here: minimum length of short pulse
+    RCII_BIT_LEN_MAX,                                                   // pulse_1_len_max: here: maximum length of short pulse
+    RCII_BIT_LEN_MIN,                                                   // pause_1_len_min: here: minimum length of short pause
+    RCII_BIT_LEN_MAX,                                                   // pause_1_len_max: here: maximum length of short pause
+    RCII_BIT_LEN_MIN,                                                                  // pulse_0_len_min: here: not used
+    RCII_BIT_LEN_MAX,                                                                  // pulse_0_len_max: here: not used
+    RCII_BIT_LEN_MIN,                                                                  // pause_0_len_min: here: not used
+    RCII_BIT_LEN_MAX,                                                                  // pause_0_len_max: here: not used
+    RCII_ADDRESS_OFFSET,                                                // address_offset:  address offset
+    RCII_ADDRESS_OFFSET + RCII_ADDRESS_LEN,                             // address_end:     end of address
+    RCII_COMMAND_OFFSET,                                                // command_offset:  command offset
+    RCII_COMMAND_OFFSET + RCII_COMMAND_LEN,                             // command_end:     end of command
+    RCII_COMPLETE_DATA_LEN,                                             // complete_len:    complete length of frame
+    RCII_STOP_BIT,                                                      // stop_bit:        flag: frame has stop bit
+    RCII_LSB,                                                           // lsb_first:       flag: LSB first
+    RCII_FLAGS                                                          // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER s100_param =
+{
+    IRMP_S100_PROTOCOL,                                                 // protocol:        ir protocol
+    S100_BIT_LEN_MIN,                                                   // pulse_1_len_min: here: minimum length of short pulse
+    S100_BIT_LEN_MAX,                                                   // pulse_1_len_max: here: maximum length of short pulse
+    S100_BIT_LEN_MIN,                                                   // pause_1_len_min: here: minimum length of short pause
+    S100_BIT_LEN_MAX,                                                   // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    S100_ADDRESS_OFFSET,                                                // address_offset:  address offset
+    S100_ADDRESS_OFFSET + S100_ADDRESS_LEN,                             // address_end:     end of address
+    S100_COMMAND_OFFSET,                                                // command_offset:  command offset
+    S100_COMMAND_OFFSET + S100_COMMAND_LEN,                             // command_end:     end of command
+    S100_COMPLETE_DATA_LEN,                                             // complete_len:    complete length of frame
+    S100_STOP_BIT,                                                      // stop_bit:        flag: frame has stop bit
+    S100_LSB,                                                           // lsb_first:       flag: LSB first
+    S100_FLAGS                                                          // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER denon_param =
+{
+    IRMP_DENON_PROTOCOL,                                                // protocol:        ir protocol
+    DENON_PULSE_LEN_MIN,                                                // pulse_1_len_min: minimum length of pulse with bit value 1
+    DENON_PULSE_LEN_MAX,                                                // pulse_1_len_max: maximum length of pulse with bit value 1
+    DENON_1_PAUSE_LEN_MIN,                                              // pause_1_len_min: minimum length of pause with bit value 1
+    DENON_1_PAUSE_LEN_MAX,                                              // pause_1_len_max: maximum length of pause with bit value 1
+    DENON_PULSE_LEN_MIN,                                                // pulse_0_len_min: minimum length of pulse with bit value 0
+    DENON_PULSE_LEN_MAX,                                                // pulse_0_len_max: maximum length of pulse with bit value 0
+    DENON_0_PAUSE_LEN_MIN,                                              // pause_0_len_min: minimum length of pause with bit value 0
+    DENON_0_PAUSE_LEN_MAX,                                              // pause_0_len_max: maximum length of pause with bit value 0
+    DENON_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    DENON_ADDRESS_OFFSET + DENON_ADDRESS_LEN,                           // address_end:     end of address
+    DENON_COMMAND_OFFSET,                                               // command_offset:  command offset
+    DENON_COMMAND_OFFSET + DENON_COMMAND_LEN,                           // command_end:     end of command
+    DENON_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    DENON_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    DENON_LSB,                                                          // lsb_first:       flag: LSB first
+    DENON_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rc6_param =
+{
+    IRMP_RC6_PROTOCOL,                                                  // protocol:        ir protocol
+
+    RC6_BIT_PULSE_LEN_MIN,                                              // pulse_1_len_min: here: minimum length of short pulse
+    RC6_BIT_PULSE_LEN_MAX,                                              // pulse_1_len_max: here: maximum length of short pulse
+    RC6_BIT_PAUSE_LEN_MIN,                                              // pause_1_len_min: here: minimum length of short pause
+    RC6_BIT_PAUSE_LEN_MAX,                                              // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    RC6_ADDRESS_OFFSET,                                                 // address_offset:  address offset
+    RC6_ADDRESS_OFFSET + RC6_ADDRESS_LEN,                               // address_end:     end of address
+    RC6_COMMAND_OFFSET,                                                 // command_offset:  command offset
+    RC6_COMMAND_OFFSET + RC6_COMMAND_LEN,                               // command_end:     end of command
+    RC6_COMPLETE_DATA_LEN_SHORT,                                        // complete_len:    complete length of frame
+    RC6_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    RC6_LSB,                                                            // lsb_first:       flag: LSB first
+    RC6_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER recs80ext_param =
+{
+    IRMP_RECS80EXT_PROTOCOL,                                            // protocol:        ir protocol
+    RECS80EXT_PULSE_LEN_MIN,                                            // pulse_1_len_min: minimum length of pulse with bit value 1
+    RECS80EXT_PULSE_LEN_MAX,                                            // pulse_1_len_max: maximum length of pulse with bit value 1
+    RECS80EXT_1_PAUSE_LEN_MIN,                                          // pause_1_len_min: minimum length of pause with bit value 1
+    RECS80EXT_1_PAUSE_LEN_MAX,                                          // pause_1_len_max: maximum length of pause with bit value 1
+    RECS80EXT_PULSE_LEN_MIN,                                            // pulse_0_len_min: minimum length of pulse with bit value 0
+    RECS80EXT_PULSE_LEN_MAX,                                            // pulse_0_len_max: maximum length of pulse with bit value 0
+    RECS80EXT_0_PAUSE_LEN_MIN,                                          // pause_0_len_min: minimum length of pause with bit value 0
+    RECS80EXT_0_PAUSE_LEN_MAX,                                          // pause_0_len_max: maximum length of pause with bit value 0
+    RECS80EXT_ADDRESS_OFFSET,                                           // address_offset:  address offset
+    RECS80EXT_ADDRESS_OFFSET + RECS80EXT_ADDRESS_LEN,                   // address_end:     end of address
+    RECS80EXT_COMMAND_OFFSET,                                           // command_offset:  command offset
+    RECS80EXT_COMMAND_OFFSET + RECS80EXT_COMMAND_LEN,                   // command_end:     end of command
+    RECS80EXT_COMPLETE_DATA_LEN,                                        // complete_len:    complete length of frame
+    RECS80EXT_STOP_BIT,                                                 // stop_bit:        flag: frame has stop bit
+    RECS80EXT_LSB,                                                      // lsb_first:       flag: LSB first
+    RECS80EXT_FLAGS                                                     // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nubert_param =
+{
+    IRMP_NUBERT_PROTOCOL,                                               // protocol:        ir protocol
+    NUBERT_1_PULSE_LEN_MIN,                                             // pulse_1_len_min: minimum length of pulse with bit value 1
+    NUBERT_1_PULSE_LEN_MAX,                                             // pulse_1_len_max: maximum length of pulse with bit value 1
+    NUBERT_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    NUBERT_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    NUBERT_0_PULSE_LEN_MIN,                                             // pulse_0_len_min: minimum length of pulse with bit value 0
+    NUBERT_0_PULSE_LEN_MAX,                                             // pulse_0_len_max: maximum length of pulse with bit value 0
+    NUBERT_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    NUBERT_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    NUBERT_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    NUBERT_ADDRESS_OFFSET + NUBERT_ADDRESS_LEN,                         // address_end:     end of address
+    NUBERT_COMMAND_OFFSET,                                              // command_offset:  command offset
+    NUBERT_COMMAND_OFFSET + NUBERT_COMMAND_LEN,                         // command_end:     end of command
+    NUBERT_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    NUBERT_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    NUBERT_LSB,                                                         // lsb_first:       flag: LSB first
+    NUBERT_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER fan_param =
+{
+    IRMP_FAN_PROTOCOL,                                                  // protocol:        ir protocol
+    FAN_1_PULSE_LEN_MIN,                                                // pulse_1_len_min: minimum length of pulse with bit value 1
+    FAN_1_PULSE_LEN_MAX,                                                // pulse_1_len_max: maximum length of pulse with bit value 1
+    FAN_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    FAN_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    FAN_0_PULSE_LEN_MIN,                                                // pulse_0_len_min: minimum length of pulse with bit value 0
+    FAN_0_PULSE_LEN_MAX,                                                // pulse_0_len_max: maximum length of pulse with bit value 0
+    FAN_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    FAN_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    FAN_ADDRESS_OFFSET,                                                 // address_offset:  address offset
+    FAN_ADDRESS_OFFSET + FAN_ADDRESS_LEN,                               // address_end:     end of address
+    FAN_COMMAND_OFFSET,                                                 // command_offset:  command offset
+    FAN_COMMAND_OFFSET + FAN_COMMAND_LEN,                               // command_end:     end of command
+    FAN_COMPLETE_DATA_LEN,                                              // complete_len:    complete length of frame
+    FAN_STOP_BIT,                                                       // stop_bit:        flag: frame has NO stop bit
+    FAN_LSB,                                                            // lsb_first:       flag: LSB first
+    FAN_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER speaker_param =
+{
+    IRMP_SPEAKER_PROTOCOL,                                              // protocol:        ir protocol
+    SPEAKER_1_PULSE_LEN_MIN,                                            // pulse_1_len_min: minimum length of pulse with bit value 1
+    SPEAKER_1_PULSE_LEN_MAX,                                            // pulse_1_len_max: maximum length of pulse with bit value 1
+    SPEAKER_1_PAUSE_LEN_MIN,                                            // pause_1_len_min: minimum length of pause with bit value 1
+    SPEAKER_1_PAUSE_LEN_MAX,                                            // pause_1_len_max: maximum length of pause with bit value 1
+    SPEAKER_0_PULSE_LEN_MIN,                                            // pulse_0_len_min: minimum length of pulse with bit value 0
+    SPEAKER_0_PULSE_LEN_MAX,                                            // pulse_0_len_max: maximum length of pulse with bit value 0
+    SPEAKER_0_PAUSE_LEN_MIN,                                            // pause_0_len_min: minimum length of pause with bit value 0
+    SPEAKER_0_PAUSE_LEN_MAX,                                            // pause_0_len_max: maximum length of pause with bit value 0
+    SPEAKER_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    SPEAKER_ADDRESS_OFFSET + SPEAKER_ADDRESS_LEN,                       // address_end:     end of address
+    SPEAKER_COMMAND_OFFSET,                                             // command_offset:  command offset
+    SPEAKER_COMMAND_OFFSET + SPEAKER_COMMAND_LEN,                       // command_end:     end of command
+    SPEAKER_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame
+    SPEAKER_STOP_BIT,                                                   // stop_bit:        flag: frame has stop bit
+    SPEAKER_LSB,                                                        // lsb_first:       flag: LSB first
+    SPEAKER_FLAGS                                                       // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER bang_olufsen_param =
+{
+    IRMP_BANG_OLUFSEN_PROTOCOL,                                         // protocol:        ir protocol
+    BANG_OLUFSEN_PULSE_LEN_MIN,                                         // pulse_1_len_min: minimum length of pulse with bit value 1
+    BANG_OLUFSEN_PULSE_LEN_MAX,                                         // pulse_1_len_max: maximum length of pulse with bit value 1
+    BANG_OLUFSEN_1_PAUSE_LEN_MIN,                                       // pause_1_len_min: minimum length of pause with bit value 1
+    BANG_OLUFSEN_1_PAUSE_LEN_MAX,                                       // pause_1_len_max: maximum length of pause with bit value 1
+    BANG_OLUFSEN_PULSE_LEN_MIN,                                         // pulse_0_len_min: minimum length of pulse with bit value 0
+    BANG_OLUFSEN_PULSE_LEN_MAX,                                         // pulse_0_len_max: maximum length of pulse with bit value 0
+    BANG_OLUFSEN_0_PAUSE_LEN_MIN,                                       // pause_0_len_min: minimum length of pause with bit value 0
+    BANG_OLUFSEN_0_PAUSE_LEN_MAX,                                       // pause_0_len_max: maximum length of pause with bit value 0
+    BANG_OLUFSEN_ADDRESS_OFFSET,                                        // address_offset:  address offset
+    BANG_OLUFSEN_ADDRESS_OFFSET + BANG_OLUFSEN_ADDRESS_LEN,             // address_end:     end of address
+    BANG_OLUFSEN_COMMAND_OFFSET,                                        // command_offset:  command offset
+    BANG_OLUFSEN_COMMAND_OFFSET + BANG_OLUFSEN_COMMAND_LEN,             // command_end:     end of command
+    BANG_OLUFSEN_COMPLETE_DATA_LEN,                                     // complete_len:    complete length of frame
+    BANG_OLUFSEN_STOP_BIT,                                              // stop_bit:        flag: frame has stop bit
+    BANG_OLUFSEN_LSB,                                                   // lsb_first:       flag: LSB first
+    BANG_OLUFSEN_FLAGS                                                  // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+
+static uint_fast8_t first_bit;
+
+static const PROGMEM IRMP_PARAMETER grundig_param =
+{
+    IRMP_GRUNDIG_PROTOCOL,                                              // protocol:        ir protocol
+
+    GRUNDIG_NOKIA_IR60_BIT_LEN_MIN,                                     // pulse_1_len_min: here: minimum length of short pulse
+    GRUNDIG_NOKIA_IR60_BIT_LEN_MAX,                                     // pulse_1_len_max: here: maximum length of short pulse
+    GRUNDIG_NOKIA_IR60_BIT_LEN_MIN,                                     // pause_1_len_min: here: minimum length of short pause
+    GRUNDIG_NOKIA_IR60_BIT_LEN_MAX,                                     // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    GRUNDIG_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    GRUNDIG_ADDRESS_OFFSET + GRUNDIG_ADDRESS_LEN,                       // address_end:     end of address
+    GRUNDIG_COMMAND_OFFSET,                                             // command_offset:  command offset
+    GRUNDIG_COMMAND_OFFSET + GRUNDIG_COMMAND_LEN + 1,                   // command_end:     end of command (USE 1 bit MORE to STORE NOKIA DATA!)
+    NOKIA_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame, here: NOKIA instead of GRUNDIG!
+    GRUNDIG_NOKIA_IR60_STOP_BIT,                                        // stop_bit:        flag: frame has stop bit
+    GRUNDIG_NOKIA_IR60_LSB,                                             // lsb_first:       flag: LSB first
+    GRUNDIG_NOKIA_IR60_FLAGS                                            // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER ruwido_param =
+{
+    IRMP_RUWIDO_PROTOCOL,                                               // protocol:        ir protocol
+    SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MIN,                                // pulse_1_len_min: here: minimum length of short pulse
+    SIEMENS_OR_RUWIDO_BIT_PULSE_LEN_MAX,                                // pulse_1_len_max: here: maximum length of short pulse
+    SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MIN,                                // pause_1_len_min: here: minimum length of short pause
+    SIEMENS_OR_RUWIDO_BIT_PAUSE_LEN_MAX,                                // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    RUWIDO_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    RUWIDO_ADDRESS_OFFSET + RUWIDO_ADDRESS_LEN,                         // address_end:     end of address
+    RUWIDO_COMMAND_OFFSET,                                              // command_offset:  command offset
+    RUWIDO_COMMAND_OFFSET + RUWIDO_COMMAND_LEN,                         // command_end:     end of command
+    SIEMENS_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame, here: SIEMENS instead of RUWIDO!
+    SIEMENS_OR_RUWIDO_STOP_BIT,                                         // stop_bit:        flag: frame has stop bit
+    SIEMENS_OR_RUWIDO_LSB,                                              // lsb_first:       flag: LSB first
+    SIEMENS_OR_RUWIDO_FLAGS                                             // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER fdc_param =
+{
+    IRMP_FDC_PROTOCOL,                                                  // protocol:        ir protocol
+    FDC_PULSE_LEN_MIN,                                                  // pulse_1_len_min: minimum length of pulse with bit value 1
+    FDC_PULSE_LEN_MAX,                                                  // pulse_1_len_max: maximum length of pulse with bit value 1
+    FDC_1_PAUSE_LEN_MIN,                                                // pause_1_len_min: minimum length of pause with bit value 1
+    FDC_1_PAUSE_LEN_MAX,                                                // pause_1_len_max: maximum length of pause with bit value 1
+    FDC_PULSE_LEN_MIN,                                                  // pulse_0_len_min: minimum length of pulse with bit value 0
+    FDC_PULSE_LEN_MAX,                                                  // pulse_0_len_max: maximum length of pulse with bit value 0
+    FDC_0_PAUSE_LEN_MIN,                                                // pause_0_len_min: minimum length of pause with bit value 0
+    FDC_0_PAUSE_LEN_MAX,                                                // pause_0_len_max: maximum length of pause with bit value 0
+    FDC_ADDRESS_OFFSET,                                                 // address_offset:  address offset
+    FDC_ADDRESS_OFFSET + FDC_ADDRESS_LEN,                               // address_end:     end of address
+    FDC_COMMAND_OFFSET,                                                 // command_offset:  command offset
+    FDC_COMMAND_OFFSET + FDC_COMMAND_LEN,                               // command_end:     end of command
+    FDC_COMPLETE_DATA_LEN,                                              // complete_len:    complete length of frame
+    FDC_STOP_BIT,                                                       // stop_bit:        flag: frame has stop bit
+    FDC_LSB,                                                            // lsb_first:       flag: LSB first
+    FDC_FLAGS                                                           // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rccar_param =
+{
+    IRMP_RCCAR_PROTOCOL,                                                // protocol:        ir protocol
+    RCCAR_PULSE_LEN_MIN,                                                // pulse_1_len_min: minimum length of pulse with bit value 1
+    RCCAR_PULSE_LEN_MAX,                                                // pulse_1_len_max: maximum length of pulse with bit value 1
+    RCCAR_1_PAUSE_LEN_MIN,                                              // pause_1_len_min: minimum length of pause with bit value 1
+    RCCAR_1_PAUSE_LEN_MAX,                                              // pause_1_len_max: maximum length of pause with bit value 1
+    RCCAR_PULSE_LEN_MIN,                                                // pulse_0_len_min: minimum length of pulse with bit value 0
+    RCCAR_PULSE_LEN_MAX,                                                // pulse_0_len_max: maximum length of pulse with bit value 0
+    RCCAR_0_PAUSE_LEN_MIN,                                              // pause_0_len_min: minimum length of pause with bit value 0
+    RCCAR_0_PAUSE_LEN_MAX,                                              // pause_0_len_max: maximum length of pause with bit value 0
+    RCCAR_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    RCCAR_ADDRESS_OFFSET + RCCAR_ADDRESS_LEN,                           // address_end:     end of address
+    RCCAR_COMMAND_OFFSET,                                               // command_offset:  command offset
+    RCCAR_COMMAND_OFFSET + RCCAR_COMMAND_LEN,                           // command_end:     end of command
+    RCCAR_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    RCCAR_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    RCCAR_LSB,                                                          // lsb_first:       flag: LSB first
+    RCCAR_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER nikon_param =
+{
+    IRMP_NIKON_PROTOCOL,                                                // protocol:        ir protocol
+    NIKON_PULSE_LEN_MIN,                                                // pulse_1_len_min: minimum length of pulse with bit value 1
+    NIKON_PULSE_LEN_MAX,                                                // pulse_1_len_max: maximum length of pulse with bit value 1
+    NIKON_1_PAUSE_LEN_MIN,                                              // pause_1_len_min: minimum length of pause with bit value 1
+    NIKON_1_PAUSE_LEN_MAX,                                              // pause_1_len_max: maximum length of pause with bit value 1
+    NIKON_PULSE_LEN_MIN,                                                // pulse_0_len_min: minimum length of pulse with bit value 0
+    NIKON_PULSE_LEN_MAX,                                                // pulse_0_len_max: maximum length of pulse with bit value 0
+    NIKON_0_PAUSE_LEN_MIN,                                              // pause_0_len_min: minimum length of pause with bit value 0
+    NIKON_0_PAUSE_LEN_MAX,                                              // pause_0_len_max: maximum length of pause with bit value 0
+    NIKON_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    NIKON_ADDRESS_OFFSET + NIKON_ADDRESS_LEN,                           // address_end:     end of address
+    NIKON_COMMAND_OFFSET,                                               // command_offset:  command offset
+    NIKON_COMMAND_OFFSET + NIKON_COMMAND_LEN,                           // command_end:     end of command
+    NIKON_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    NIKON_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    NIKON_LSB,                                                          // lsb_first:       flag: LSB first
+    NIKON_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER kathrein_param =
+{
+    IRMP_KATHREIN_PROTOCOL,                                             // protocol:        ir protocol
+    KATHREIN_1_PULSE_LEN_MIN,                                           // pulse_1_len_min: minimum length of pulse with bit value 1
+    KATHREIN_1_PULSE_LEN_MAX,                                           // pulse_1_len_max: maximum length of pulse with bit value 1
+    KATHREIN_1_PAUSE_LEN_MIN,                                           // pause_1_len_min: minimum length of pause with bit value 1
+    KATHREIN_1_PAUSE_LEN_MAX,                                           // pause_1_len_max: maximum length of pause with bit value 1
+    KATHREIN_0_PULSE_LEN_MIN,                                           // pulse_0_len_min: minimum length of pulse with bit value 0
+    KATHREIN_0_PULSE_LEN_MAX,                                           // pulse_0_len_max: maximum length of pulse with bit value 0
+    KATHREIN_0_PAUSE_LEN_MIN,                                           // pause_0_len_min: minimum length of pause with bit value 0
+    KATHREIN_0_PAUSE_LEN_MAX,                                           // pause_0_len_max: maximum length of pause with bit value 0
+    KATHREIN_ADDRESS_OFFSET,                                            // address_offset:  address offset
+    KATHREIN_ADDRESS_OFFSET + KATHREIN_ADDRESS_LEN,                     // address_end:     end of address
+    KATHREIN_COMMAND_OFFSET,                                            // command_offset:  command offset
+    KATHREIN_COMMAND_OFFSET + KATHREIN_COMMAND_LEN,                     // command_end:     end of command
+    KATHREIN_COMPLETE_DATA_LEN,                                         // complete_len:    complete length of frame
+    KATHREIN_STOP_BIT,                                                  // stop_bit:        flag: frame has stop bit
+    KATHREIN_LSB,                                                       // lsb_first:       flag: LSB first
+    KATHREIN_FLAGS                                                      // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER netbox_param =
+{
+    IRMP_NETBOX_PROTOCOL,                                               // protocol:        ir protocol
+    NETBOX_PULSE_LEN,                                                   // pulse_1_len_min: minimum length of pulse with bit value 1, here: exact value
+    NETBOX_PULSE_REST_LEN,                                              // pulse_1_len_max: maximum length of pulse with bit value 1, here: rest value
+    NETBOX_PAUSE_LEN,                                                   // pause_1_len_min: minimum length of pause with bit value 1, here: exact value
+    NETBOX_PAUSE_REST_LEN,                                              // pause_1_len_max: maximum length of pause with bit value 1, here: rest value
+    NETBOX_PULSE_LEN,                                                   // pulse_0_len_min: minimum length of pulse with bit value 0, here: exact value
+    NETBOX_PULSE_REST_LEN,                                              // pulse_0_len_max: maximum length of pulse with bit value 0, here: rest value
+    NETBOX_PAUSE_LEN,                                                   // pause_0_len_min: minimum length of pause with bit value 0, here: exact value
+    NETBOX_PAUSE_REST_LEN,                                              // pause_0_len_max: maximum length of pause with bit value 0, here: rest value
+    NETBOX_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    NETBOX_ADDRESS_OFFSET + NETBOX_ADDRESS_LEN,                         // address_end:     end of address
+    NETBOX_COMMAND_OFFSET,                                              // command_offset:  command offset
+    NETBOX_COMMAND_OFFSET + NETBOX_COMMAND_LEN,                         // command_end:     end of command
+    NETBOX_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    NETBOX_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    NETBOX_LSB,                                                         // lsb_first:       flag: LSB first
+    NETBOX_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER lego_param =
+{
+    IRMP_LEGO_PROTOCOL,                                                 // protocol:        ir protocol
+    LEGO_PULSE_LEN_MIN,                                                 // pulse_1_len_min: minimum length of pulse with bit value 1
+    LEGO_PULSE_LEN_MAX,                                                 // pulse_1_len_max: maximum length of pulse with bit value 1
+    LEGO_1_PAUSE_LEN_MIN,                                               // pause_1_len_min: minimum length of pause with bit value 1
+    LEGO_1_PAUSE_LEN_MAX,                                               // pause_1_len_max: maximum length of pause with bit value 1
+    LEGO_PULSE_LEN_MIN,                                                 // pulse_0_len_min: minimum length of pulse with bit value 0
+    LEGO_PULSE_LEN_MAX,                                                 // pulse_0_len_max: maximum length of pulse with bit value 0
+    LEGO_0_PAUSE_LEN_MIN,                                               // pause_0_len_min: minimum length of pause with bit value 0
+    LEGO_0_PAUSE_LEN_MAX,                                               // pause_0_len_max: maximum length of pause with bit value 0
+    LEGO_ADDRESS_OFFSET,                                                // address_offset:  address offset
+    LEGO_ADDRESS_OFFSET + LEGO_ADDRESS_LEN,                             // address_end:     end of address
+    LEGO_COMMAND_OFFSET,                                                // command_offset:  command offset
+    LEGO_COMMAND_OFFSET + LEGO_COMMAND_LEN,                             // command_end:     end of command
+    LEGO_COMPLETE_DATA_LEN,                                             // complete_len:    complete length of frame
+    LEGO_STOP_BIT,                                                      // stop_bit:        flag: frame has stop bit
+    LEGO_LSB,                                                           // lsb_first:       flag: LSB first
+    LEGO_FLAGS                                                          // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER irmp16_param =
+{
+    IRMP_IRMP16_PROTOCOL,                                               // protocol:        ir protocol
+    IRMP16_PULSE_LEN_MIN,                                               // pulse_1_len_min: minimum length of pulse with bit value 1
+    IRMP16_PULSE_LEN_MAX,                                               // pulse_1_len_max: maximum length of pulse with bit value 1
+    IRMP16_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    IRMP16_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    IRMP16_PULSE_LEN_MIN,                                               // pulse_0_len_min: minimum length of pulse with bit value 0
+    IRMP16_PULSE_LEN_MAX,                                               // pulse_0_len_max: maximum length of pulse with bit value 0
+    IRMP16_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    IRMP16_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    IRMP16_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    IRMP16_ADDRESS_OFFSET + IRMP16_ADDRESS_LEN,                         // address_end:     end of address
+    IRMP16_COMMAND_OFFSET,                                              // command_offset:  command offset
+    IRMP16_COMMAND_OFFSET + IRMP16_COMMAND_LEN,                         // command_end:     end of command
+    IRMP16_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    IRMP16_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    IRMP16_LSB,                                                         // lsb_first:       flag: LSB first
+    IRMP16_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER gree_param =
+{
+    IRMP_GREE_PROTOCOL,                                               // protocol:        ir protocol
+    GREE_PULSE_LEN_MIN,                                               // pulse_1_len_min: minimum length of pulse with bit value 1
+    GREE_PULSE_LEN_MAX,                                               // pulse_1_len_max: maximum length of pulse with bit value 1
+    GREE_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    GREE_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    GREE_PULSE_LEN_MIN,                                               // pulse_0_len_min: minimum length of pulse with bit value 0
+    GREE_PULSE_LEN_MAX,                                               // pulse_0_len_max: maximum length of pulse with bit value 0
+    GREE_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    GREE_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    GREE_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    GREE_ADDRESS_OFFSET + GREE_ADDRESS_LEN,                         // address_end:     end of address
+    GREE_COMMAND_OFFSET,                                              // command_offset:  command offset
+    GREE_COMMAND_OFFSET + GREE_COMMAND_LEN,                         // command_end:     end of command
+    GREE_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    GREE_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    GREE_LSB,                                                         // lsb_first:       flag: LSB first
+    GREE_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER thomson_param =
+{
+    IRMP_THOMSON_PROTOCOL,                                              // protocol:        ir protocol
+    THOMSON_PULSE_LEN_MIN,                                              // pulse_1_len_min: minimum length of pulse with bit value 1
+    THOMSON_PULSE_LEN_MAX,                                              // pulse_1_len_max: maximum length of pulse with bit value 1
+    THOMSON_1_PAUSE_LEN_MIN,                                            // pause_1_len_min: minimum length of pause with bit value 1
+    THOMSON_1_PAUSE_LEN_MAX,                                            // pause_1_len_max: maximum length of pause with bit value 1
+    THOMSON_PULSE_LEN_MIN,                                              // pulse_0_len_min: minimum length of pulse with bit value 0
+    THOMSON_PULSE_LEN_MAX,                                              // pulse_0_len_max: maximum length of pulse with bit value 0
+    THOMSON_0_PAUSE_LEN_MIN,                                            // pause_0_len_min: minimum length of pause with bit value 0
+    THOMSON_0_PAUSE_LEN_MAX,                                            // pause_0_len_max: maximum length of pause with bit value 0
+    THOMSON_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    THOMSON_ADDRESS_OFFSET + THOMSON_ADDRESS_LEN,                       // address_end:     end of address
+    THOMSON_COMMAND_OFFSET,                                             // command_offset:  command offset
+    THOMSON_COMMAND_OFFSET + THOMSON_COMMAND_LEN,                       // command_end:     end of command
+    THOMSON_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame
+    THOMSON_STOP_BIT,                                                   // stop_bit:        flag: frame has stop bit
+    THOMSON_LSB,                                                        // lsb_first:       flag: LSB first
+    THOMSON_FLAGS                                                       // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER bose_param =
+{
+    IRMP_BOSE_PROTOCOL,                                                 // protocol:        ir protocol
+    BOSE_PULSE_LEN_MIN,                                                 // pulse_1_len_min: minimum length of pulse with bit value 1
+    BOSE_PULSE_LEN_MAX,                                                 // pulse_1_len_max: maximum length of pulse with bit value 1
+    BOSE_1_PAUSE_LEN_MIN,                                               // pause_1_len_min: minimum length of pause with bit value 1
+    BOSE_1_PAUSE_LEN_MAX,                                               // pause_1_len_max: maximum length of pause with bit value 1
+    BOSE_PULSE_LEN_MIN,                                                 // pulse_0_len_min: minimum length of pulse with bit value 0
+    BOSE_PULSE_LEN_MAX,                                                 // pulse_0_len_max: maximum length of pulse with bit value 0
+    BOSE_0_PAUSE_LEN_MIN,                                               // pause_0_len_min: minimum length of pause with bit value 0
+    BOSE_0_PAUSE_LEN_MAX,                                               // pause_0_len_max: maximum length of pause with bit value 0
+    BOSE_ADDRESS_OFFSET,                                                // address_offset:  address offset
+    BOSE_ADDRESS_OFFSET + BOSE_ADDRESS_LEN,                             // address_end:     end of address
+    BOSE_COMMAND_OFFSET,                                                // command_offset:  command offset
+    BOSE_COMMAND_OFFSET + BOSE_COMMAND_LEN,                             // command_end:     end of command
+    BOSE_COMPLETE_DATA_LEN,                                             // complete_len:    complete length of frame
+    BOSE_STOP_BIT,                                                      // stop_bit:        flag: frame has stop bit
+    BOSE_LSB,                                                           // lsb_first:       flag: LSB first
+    BOSE_FLAGS                                                          // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER a1tvbox_param =
+{
+    IRMP_A1TVBOX_PROTOCOL,                                              // protocol:        ir protocol
+
+    A1TVBOX_BIT_PULSE_LEN_MIN,                                          // pulse_1_len_min: here: minimum length of short pulse
+    A1TVBOX_BIT_PULSE_LEN_MAX,                                          // pulse_1_len_max: here: maximum length of short pulse
+    A1TVBOX_BIT_PAUSE_LEN_MIN,                                          // pause_1_len_min: here: minimum length of short pause
+    A1TVBOX_BIT_PAUSE_LEN_MAX,                                          // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    A1TVBOX_ADDRESS_OFFSET,                                             // address_offset:  address offset
+    A1TVBOX_ADDRESS_OFFSET + A1TVBOX_ADDRESS_LEN,                       // address_end:     end of address
+    A1TVBOX_COMMAND_OFFSET,                                             // command_offset:  command offset
+    A1TVBOX_COMMAND_OFFSET + A1TVBOX_COMMAND_LEN,                       // command_end:     end of command
+    A1TVBOX_COMPLETE_DATA_LEN,                                          // complete_len:    complete length of frame
+    A1TVBOX_STOP_BIT,                                                   // stop_bit:        flag: frame has stop bit
+    A1TVBOX_LSB,                                                        // lsb_first:       flag: LSB first
+    A1TVBOX_FLAGS                                                       // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER merlin_param =
+{
+    IRMP_MERLIN_PROTOCOL,                                               // protocol:        ir protocol
+
+    MERLIN_BIT_PULSE_LEN_MIN,                                           // pulse_1_len_min: here: minimum length of short pulse
+    MERLIN_BIT_PULSE_LEN_MAX,                                           // pulse_1_len_max: here: maximum length of short pulse
+    MERLIN_BIT_PAUSE_LEN_MIN,                                           // pause_1_len_min: here: minimum length of short pause
+    MERLIN_BIT_PAUSE_LEN_MAX,                                           // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    MERLIN_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    MERLIN_ADDRESS_OFFSET + MERLIN_ADDRESS_LEN,                         // address_end:     end of address
+    MERLIN_COMMAND_OFFSET,                                              // command_offset:  command offset
+    MERLIN_COMMAND_OFFSET + MERLIN_COMMAND_LEN,                         // command_end:     end of command
+    MERLIN_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    MERLIN_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    MERLIN_LSB,                                                         // lsb_first:       flag: LSB first
+    MERLIN_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER ortek_param =
+{
+    IRMP_ORTEK_PROTOCOL,                                                // protocol:        ir protocol
+
+    ORTEK_BIT_PULSE_LEN_MIN,                                            // pulse_1_len_min: here: minimum length of short pulse
+    ORTEK_BIT_PULSE_LEN_MAX,                                            // pulse_1_len_max: here: maximum length of short pulse
+    ORTEK_BIT_PAUSE_LEN_MIN,                                            // pause_1_len_min: here: minimum length of short pause
+    ORTEK_BIT_PAUSE_LEN_MAX,                                            // pause_1_len_max: here: maximum length of short pause
+    0,                                                                  // pulse_0_len_min: here: not used
+    0,                                                                  // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    ORTEK_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    ORTEK_ADDRESS_OFFSET + ORTEK_ADDRESS_LEN,                           // address_end:     end of address
+    ORTEK_COMMAND_OFFSET,                                               // command_offset:  command offset
+    ORTEK_COMMAND_OFFSET + ORTEK_COMMAND_LEN,                           // command_end:     end of command
+    ORTEK_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    ORTEK_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    ORTEK_LSB,                                                          // lsb_first:       flag: LSB first
+    ORTEK_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER roomba_param =
+{
+    IRMP_ROOMBA_PROTOCOL,                                               // protocol:        ir protocol
+    ROOMBA_1_PULSE_LEN_MIN,                                             // pulse_1_len_min: minimum length of pulse with bit value 1
+    ROOMBA_1_PULSE_LEN_MAX,                                             // pulse_1_len_max: maximum length of pulse with bit value 1
+    ROOMBA_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    ROOMBA_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    ROOMBA_0_PULSE_LEN_MIN,                                             // pulse_0_len_min: minimum length of pulse with bit value 0
+    ROOMBA_0_PULSE_LEN_MAX,                                             // pulse_0_len_max: maximum length of pulse with bit value 0
+    ROOMBA_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    ROOMBA_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    ROOMBA_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    ROOMBA_ADDRESS_OFFSET + ROOMBA_ADDRESS_LEN,                         // address_end:     end of address
+    ROOMBA_COMMAND_OFFSET,                                              // command_offset:  command offset
+    ROOMBA_COMMAND_OFFSET + ROOMBA_COMMAND_LEN,                         // command_end:     end of command
+    ROOMBA_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    ROOMBA_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    ROOMBA_LSB,                                                         // lsb_first:       flag: LSB first
+    ROOMBA_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER rcmm_param =
+{
+    IRMP_RCMM32_PROTOCOL,                                               // protocol:        ir protocol
+
+    RCMM32_BIT_PULSE_LEN_MIN,                                           // pulse_1_len_min: here: minimum length of short pulse
+    RCMM32_BIT_PULSE_LEN_MAX,                                           // pulse_1_len_max: here: maximum length of short pulse
+    0,                                                                  // pause_1_len_min: here: minimum length of short pause
+    0,                                                                  // pause_1_len_max: here: maximum length of short pause
+    RCMM32_BIT_PULSE_LEN_MIN,                                           // pulse_0_len_min: here: not used
+    RCMM32_BIT_PULSE_LEN_MAX,                                           // pulse_0_len_max: here: not used
+    0,                                                                  // pause_0_len_min: here: not used
+    0,                                                                  // pause_0_len_max: here: not used
+    RCMM32_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    RCMM32_ADDRESS_OFFSET + RCMM32_ADDRESS_LEN,                         // address_end:     end of address
+    RCMM32_COMMAND_OFFSET,                                              // command_offset:  command offset
+    RCMM32_COMMAND_OFFSET + RCMM32_COMMAND_LEN,                         // command_end:     end of command
+    RCMM32_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    RCMM32_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    RCMM32_LSB,                                                         // lsb_first:       flag: LSB first
+    RCMM32_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER pentax_param =
+{
+    IRMP_PENTAX_PROTOCOL,                                               // protocol:        ir protocol
+    PENTAX_PULSE_LEN_MIN,                                               // pulse_1_len_min: minimum length of pulse with bit value 1
+    PENTAX_PULSE_LEN_MAX,                                               // pulse_1_len_max: maximum length of pulse with bit value 1
+    PENTAX_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    PENTAX_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    PENTAX_PULSE_LEN_MIN,                                               // pulse_0_len_min: minimum length of pulse with bit value 0
+    PENTAX_PULSE_LEN_MAX,                                               // pulse_0_len_max: maximum length of pulse with bit value 0
+    PENTAX_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    PENTAX_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    PENTAX_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    PENTAX_ADDRESS_OFFSET + PENTAX_ADDRESS_LEN,                         // address_end:     end of address
+    PENTAX_COMMAND_OFFSET,                                              // command_offset:  command offset
+    PENTAX_COMMAND_OFFSET + PENTAX_COMMAND_LEN,                         // command_end:     end of command
+    PENTAX_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    PENTAX_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    PENTAX_LSB,                                                         // lsb_first:       flag: LSB first
+    PENTAX_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER acp24_param =
+{
+    IRMP_ACP24_PROTOCOL,                                                // protocol:        ir protocol
+    ACP24_PULSE_LEN_MIN,                                                // pulse_1_len_min: minimum length of pulse with bit value 1
+    ACP24_PULSE_LEN_MAX,                                                // pulse_1_len_max: maximum length of pulse with bit value 1
+    ACP24_1_PAUSE_LEN_MIN,                                              // pause_1_len_min: minimum length of pause with bit value 1
+    ACP24_1_PAUSE_LEN_MAX,                                              // pause_1_len_max: maximum length of pause with bit value 1
+    ACP24_PULSE_LEN_MIN,                                                // pulse_0_len_min: minimum length of pulse with bit value 0
+    ACP24_PULSE_LEN_MAX,                                                // pulse_0_len_max: maximum length of pulse with bit value 0
+    ACP24_0_PAUSE_LEN_MIN,                                              // pause_0_len_min: minimum length of pause with bit value 0
+    ACP24_0_PAUSE_LEN_MAX,                                              // pause_0_len_max: maximum length of pause with bit value 0
+    ACP24_ADDRESS_OFFSET,                                               // address_offset:  address offset
+    ACP24_ADDRESS_OFFSET + ACP24_ADDRESS_LEN,                           // address_end:     end of address
+    ACP24_COMMAND_OFFSET,                                               // command_offset:  command offset
+    ACP24_COMMAND_OFFSET + ACP24_COMMAND_LEN,                           // command_end:     end of command
+    ACP24_COMPLETE_DATA_LEN,                                            // complete_len:    complete length of frame
+    ACP24_STOP_BIT,                                                     // stop_bit:        flag: frame has stop bit
+    ACP24_LSB,                                                          // lsb_first:       flag: LSB first
+    ACP24_FLAGS                                                         // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER metz_param =
+{
+    IRMP_METZ_PROTOCOL,                                                 // protocol:        ir protocol
+    METZ_PULSE_LEN_MIN,                                                 // pulse_1_len_min: minimum length of pulse with bit value 1
+    METZ_PULSE_LEN_MAX,                                                 // pulse_1_len_max: maximum length of pulse with bit value 1
+    METZ_1_PAUSE_LEN_MIN,                                               // pause_1_len_min: minimum length of pause with bit value 1
+    METZ_1_PAUSE_LEN_MAX,                                               // pause_1_len_max: maximum length of pause with bit value 1
+    METZ_PULSE_LEN_MIN,                                                 // pulse_0_len_min: minimum length of pulse with bit value 0
+    METZ_PULSE_LEN_MAX,                                                 // pulse_0_len_max: maximum length of pulse with bit value 0
+    METZ_0_PAUSE_LEN_MIN,                                               // pause_0_len_min: minimum length of pause with bit value 0
+    METZ_0_PAUSE_LEN_MAX,                                               // pause_0_len_max: maximum length of pause with bit value 0
+    METZ_ADDRESS_OFFSET,                                                // address_offset:  address offset
+    METZ_ADDRESS_OFFSET + METZ_ADDRESS_LEN,                             // address_end:     end of address
+    METZ_COMMAND_OFFSET,                                                // command_offset:  command offset
+    METZ_COMMAND_OFFSET + METZ_COMMAND_LEN,                             // command_end:     end of command
+    METZ_COMPLETE_DATA_LEN,                                             // complete_len:    complete length of frame
+    METZ_STOP_BIT,                                                      // stop_bit:        flag: frame has stop bit
+    METZ_LSB,                                                           // lsb_first:       flag: LSB first
+    METZ_FLAGS                                                          // flags:           some flags
+};
+
+#endif
+
+#if IRMP_SUPPORT_RADIO1_PROTOCOL == 1
+
+static const PROGMEM IRMP_PARAMETER radio1_param =
+{
+    IRMP_RADIO1_PROTOCOL,                                               // protocol:        ir protocol
+
+    RADIO1_1_PULSE_LEN_MIN,                                             // pulse_1_len_min: minimum length of pulse with bit value 1
+    RADIO1_1_PULSE_LEN_MAX,                                             // pulse_1_len_max: maximum length of pulse with bit value 1
+    RADIO1_1_PAUSE_LEN_MIN,                                             // pause_1_len_min: minimum length of pause with bit value 1
+    RADIO1_1_PAUSE_LEN_MAX,                                             // pause_1_len_max: maximum length of pause with bit value 1
+    RADIO1_0_PULSE_LEN_MIN,                                             // pulse_0_len_min: minimum length of pulse with bit value 0
+    RADIO1_0_PULSE_LEN_MAX,                                             // pulse_0_len_max: maximum length of pulse with bit value 0
+    RADIO1_0_PAUSE_LEN_MIN,                                             // pause_0_len_min: minimum length of pause with bit value 0
+    RADIO1_0_PAUSE_LEN_MAX,                                             // pause_0_len_max: maximum length of pause with bit value 0
+    RADIO1_ADDRESS_OFFSET,                                              // address_offset:  address offset
+    RADIO1_ADDRESS_OFFSET + RADIO1_ADDRESS_LEN,                         // address_end:     end of address
+    RADIO1_COMMAND_OFFSET,                                              // command_offset:  command offset
+    RADIO1_COMMAND_OFFSET + RADIO1_COMMAND_LEN,                         // command_end:     end of command
+    RADIO1_COMPLETE_DATA_LEN,                                           // complete_len:    complete length of frame
+    RADIO1_STOP_BIT,                                                    // stop_bit:        flag: frame has stop bit
+    RADIO1_LSB,                                                         // lsb_first:       flag: LSB first
+    RADIO1_FLAGS                                                        // flags:           some flags
+};
+
+#endif
+
+static uint_fast8_t                             irmp_bit;                   // current bit position
+static IRMP_PARAMETER                           irmp_param;
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+static IRMP_PARAMETER                           irmp_param2;
+#endif
+
+static volatile uint_fast8_t                    irmp_ir_detected = FALSE;
+static volatile uint_fast8_t                    irmp_protocol;
+static volatile uint_fast16_t                   irmp_address;
+#if IRMP_32_BIT == 1
+static volatile uint_fast32_t                   irmp_command;
+#else
+static volatile uint_fast16_t                   irmp_command;
+#endif
+static volatile uint_fast16_t                   irmp_id;                // only used for SAMSUNG protocol
+static volatile uint_fast8_t                    irmp_flags;
+// static volatile uint_fast8_t                 irmp_busy_flag;
+
+#if defined(__MBED__)
+// DigitalIn inputPin(IRMP_PIN, PullUp);                                // this requires mbed.h and source to be compiled as cpp
+gpio_t                                          gpioIRin;               // use low level c function instead
+#endif
+
+
+#ifdef ANALYZE
+#define input(x)                                (x)
+static uint_fast8_t                             IRMP_PIN;
+static uint_fast8_t                             radio;
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Initialize IRMP decoder
+ *  @details  Configures IRMP input pin
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef ANALYZE
+void
+irmp_init (void)
+{
+#if defined(PIC_CCS) || defined(PIC_C18)                                // PIC: do nothing
+#elif defined (ARM_STM32_HAL)                                           // STM32 with Hal Library: do nothing
+#elif defined (ARM_STM32)                                               // STM32
+    GPIO_InitTypeDef     GPIO_InitStructure;
+
+    /* GPIOx clock enable */
+#  if defined (ARM_STM32L1XX)
+    RCC_AHBPeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+#  elif defined (ARM_STM32F10X)
+    RCC_APB2PeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+#  elif defined (ARM_STM32F4XX)
+    RCC_AHB1PeriphClockCmd(IRMP_PORT_RCC, ENABLE);
+#  endif
+
+    /* GPIO Configuration */
+    GPIO_InitStructure.GPIO_Pin = IRMP_BIT;
+#  if defined (ARM_STM32L1XX) || defined (ARM_STM32F4XX)
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+#  elif defined (ARM_STM32F10X)
+    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
+    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
+#  endif
+    GPIO_Init(IRMP_PORT, &GPIO_InitStructure);
+
+#elif defined(STELLARIS_ARM_CORTEX_M4)
+    // Enable the GPIO port
+    ROM_SysCtlPeripheralEnable(IRMP_PORT_PERIPH);
+
+    // Set as an input
+    ROM_GPIODirModeSet(IRMP_PORT_BASE, IRMP_PORT_PIN, GPIO_DIR_MODE_IN);
+    ROM_GPIOPadConfigSet(IRMP_PORT_BASE, IRMP_PORT_PIN, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD_WPU);
+
+#elif defined(__SDCC_stm8)                                              // STM8
+    IRMP_GPIO_STRUCT->DDR &= ~(1<<IRMP_BIT);                            // pin is input
+    IRMP_GPIO_STRUCT->CR1 |= (1<<IRMP_BIT);                             // activate pullup
+
+#elif defined (TEENSY_ARM_CORTEX_M4)                                    // TEENSY
+    pinMode(IRMP_PIN, INPUT);
+
+#elif defined(__xtensa__)                                               // ESP8266
+    pinMode(IRMP_BIT_NUMBER, INPUT);
+                                                                        // select pin function
+#  if (IRMP_BIT_NUMBER == 12)
+    PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12);
+//  doesn't work for me:
+//  # elif (IRMP_BIT_NUMBER == 13)
+//  PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U , FUNC_GPIO13);
+#  else
+#   warning Please add PIN_FUNC_SELECT when necessary.
+#  endif
+    GPIO_DIS_OUTPUT(IRMP_BIT_NUMBER);
+
+#elif defined(__MBED__)
+    gpio_init_in_ex(&gpioIRin, IRMP_PIN, IRMP_PINMODE);                 // initialize input for IR diode
+
+#elif defined(_CHIBIOS_HAL_)
+    // ChibiOS HAL automatically initializes all pins according to the board config file, no need to repeat here
+
+#else                                                                   // AVR
+    IRMP_PORT &= ~(1<<IRMP_BIT);                                        // deactivate pullup
+    IRMP_DDR &= ~(1<<IRMP_BIT);                                         // set pin to input
+#endif
+
+#if IRMP_LOGGING == 1
+    irmp_uart_init ();
+#endif
+}
+#endif
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  Get IRMP data
+ *  @details  gets decoded IRMP data
+ *  @param    pointer in order to store IRMP data
+ *  @return    TRUE: successful, FALSE: failed
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+uint_fast8_t
+irmp_get_data (IRMP_DATA * irmp_data_p)
+{
+    uint_fast8_t   rtc = FALSE;
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+    uint_fast8_t   cmd_len = 0;
+#endif
+
+    if (irmp_ir_detected)
+    {
+        switch (irmp_protocol)
+        {
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+            case IRMP_SAMSUNG_PROTOCOL:
+                if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+                {
+                    irmp_command &= 0xff;
+                    irmp_command |= irmp_id << 8;
+                    rtc = TRUE;
+                }
+                break;
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+            case IRMP_SAMSUNG48_PROTOCOL:
+                irmp_command = (irmp_command & 0x00FF) | ((irmp_id & 0x00FF) << 8);
+                rtc = TRUE;
+                break;
+#endif
+#endif
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+            case IRMP_NEC_PROTOCOL:
+                if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+                {
+                    irmp_command &= 0xff;
+                    rtc = TRUE;
+                }
+                else if (irmp_address == 0x87EE)
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("Switching to APPLE protocol\n");
+#endif // ANALYZE
+                    irmp_protocol = IRMP_APPLE_PROTOCOL;
+                    irmp_address = (irmp_command & 0xFF00) >> 8;
+                    irmp_command &= 0x00FF;
+                    rtc = TRUE;
+                }
+                else
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("Switching to ONKYO protocol\n");
+#endif // ANALYZE
+                    irmp_protocol = IRMP_ONKYO_PROTOCOL;
+                    rtc = TRUE;
+                }
+                break;
+#endif
+
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+            case IRMP_VINCENT_PROTOCOL:
+                if ((irmp_command >> 8) == (irmp_command & 0x00FF))
+                {
+                    irmp_command &= 0xff;
+                    rtc = TRUE;
+                }
+                break;
+#endif
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+            case IRMP_BOSE_PROTOCOL:
+                if ((irmp_command >> 8) == (~irmp_command & 0x00FF))
+                {
+                    irmp_command &= 0xff;
+                    rtc = TRUE;
+                }
+                break;
+#endif
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+            case IRMP_MERLIN_PROTOCOL:
+                if (irmp_bit == 10)
+                {
+                    rtc = TRUE;
+                }
+                else if (irmp_bit >= 19 && ((irmp_bit - 3) % 8 == 0))
+                {
+                    if (((irmp_command >> 1) & 1) != (irmp_command & 1))
+                    {
+                        irmp_command >>= 1;
+                        irmp_command |= ((irmp_address & 1) << (irmp_bit - 12));
+                        irmp_address >>= 1;
+                        cmd_len = (irmp_bit - 11) >> 3;
+                        rtc = TRUE;
+                    }
+                }
+                break;
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+            case IRMP_SIEMENS_PROTOCOL:
+            case IRMP_RUWIDO_PROTOCOL:
+                if (((irmp_command >> 1) & 0x0001) == (~irmp_command & 0x0001))
+                {
+                    irmp_command >>= 1;
+                    rtc = TRUE;
+                }
+                break;
+#endif
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+            case IRMP_KATHREIN_PROTOCOL:
+                if (irmp_command != 0x0000)
+                {
+                    rtc = TRUE;
+                }
+                break;
+#endif
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+            case IRMP_RC5_PROTOCOL:
+                irmp_address &= ~0x20;                              // clear toggle bit
+                rtc = TRUE;
+                break;
+#endif
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+            case IRMP_S100_PROTOCOL:
+                irmp_address &= ~0x20;                              // clear toggle bit
+                rtc = TRUE;
+                break;
+#endif
+#if IRMP_SUPPORT_IR60_PROTOCOL == 1
+            case IRMP_IR60_PROTOCOL:
+                if (irmp_command != 0x007d)                         // 0x007d (== 62<<1 + 1) is start instruction frame
+                {
+                    rtc = TRUE;
+                }
+                else
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF("Info IR60: got start instruction frame\n");
+#endif // ANALYZE
+                }
+                break;
+#endif
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+            case IRMP_RCCAR_PROTOCOL:
+                // frame in irmp_data:
+                // Bit 12 11 10 9  8  7  6  5  4  3  2  1  0
+                //     V  D7 D6 D5 D4 D3 D2 D1 D0 A1 A0 C1 C0   //         10 9  8  7  6  5  4  3  2  1  0
+                irmp_address = (irmp_command & 0x000C) >> 2;    // addr:   0  0  0  0  0  0  0  0  0  A1 A0
+                irmp_command = ((irmp_command & 0x1000) >> 2) | // V-Bit:  V  0  0  0  0  0  0  0  0  0  0
+                               ((irmp_command & 0x0003) << 8) | // C-Bits: 0  C1 C0 0  0  0  0  0  0  0  0
+                               ((irmp_command & 0x0FF0) >> 4);  // D-Bits:          D7 D6 D5 D4 D3 D2 D1 D0
+                rtc = TRUE;                                     // Summe:  V  C1 C0 D7 D6 D5 D4 D3 D2 D1 D0
+                break;
+#endif
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1                           // squeeze code to 8 bit, upper bit indicates release-key
+            case IRMP_NETBOX_PROTOCOL:
+                if (irmp_command & 0x1000)                      // last bit set?
+                {
+                    if ((irmp_command & 0x1f) == 0x15)          // key pressed: 101 01 (LSB)
+                    {
+                        irmp_command >>= 5;
+                        irmp_command &= 0x7F;
+                        rtc = TRUE;
+                    }
+                    else if ((irmp_command & 0x1f) == 0x10)     // key released: 000 01 (LSB)
+                    {
+                        irmp_command >>= 5;
+                        irmp_command |= 0x80;
+                        rtc = TRUE;
+                    }
+                    else
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF("error NETBOX: bit6/7 must be 0/1\n");
+#endif // ANALYZE
+                    }
+                }
+                else
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF("error NETBOX: last bit not set\n");
+#endif // ANALYZE
+                }
+                break;
+#endif
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+            case IRMP_LEGO_PROTOCOL:
+            {
+                uint_fast8_t crc = 0x0F ^ ((irmp_command & 0xF000) >> 12) ^ ((irmp_command & 0x0F00) >> 8) ^ ((irmp_command & 0x00F0) >> 4);
+
+                if ((irmp_command & 0x000F) == crc)
+                {
+                    irmp_command >>= 4;
+                    rtc = TRUE;
+                }
+                else
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("CRC error in LEGO protocol\n");
+#endif // ANALYZE
+                    // rtc = TRUE;                              // don't accept codes with CRC errors
+                }
+                break;
+            }
+#endif
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+            case IRMP_METZ_PROTOCOL:
+                irmp_address &= ~0x40;                              // clear toggle bit
+                if (((~irmp_address) & 0x07) == (irmp_address >> 3) && ((~irmp_command) & 0x3f) == (irmp_command >> 6))
+                {
+                    irmp_address >>= 3;
+                    irmp_command >>= 6;
+                    rtc = TRUE;
+                }
+                break;
+#endif
+            default:
+            {
+                rtc = TRUE;
+                break;
+            }
+        }
+
+        if (rtc)
+        {
+            irmp_data_p->protocol = irmp_protocol;
+            irmp_data_p->address  = irmp_address;
+            irmp_data_p->command  = irmp_command;
+            irmp_data_p->flags    = irmp_flags;
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+            irmp_data_p->flags   |= cmd_len;
+#endif
+        }
+        else
+        {
+            irmp_protocol = IRMP_UNKNOWN_PROTOCOL;
+        }
+
+        irmp_command  = 0;                                      // don't reset irmp_protocol here, needed for detection of NEC & JVC repetition frames!
+        irmp_address  = 0;
+        irmp_flags    = 0;
+
+        irmp_ir_detected = FALSE;
+    }
+
+    return rtc;
+}
+
+#if IRMP_USE_CALLBACK == 1
+void
+irmp_set_callback_ptr (void (*cb)(uint_fast8_t))
+{
+    irmp_callback_ptr = cb;
+}
+#endif // IRMP_USE_CALLBACK == 1
+
+// these statics must not be volatile, because they are only used by irmp_store_bit(), which is called by irmp_ISR()
+static uint_fast16_t irmp_tmp_address;                                      // ir address
+#if IRMP_32_BIT == 1
+static uint_fast32_t irmp_tmp_command;                                      // ir command
+#else
+static uint_fast16_t irmp_tmp_command;                                      // ir command
+#endif
+
+#if (IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)) || IRMP_SUPPORT_NEC42_PROTOCOL == 1
+static uint_fast16_t irmp_tmp_address2;                                     // ir address
+static uint_fast16_t irmp_tmp_command2;                                     // ir command
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+static uint_fast16_t irmp_lgair_address;                                    // ir address
+static uint_fast16_t irmp_lgair_command;                                    // ir command
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+static uint_fast16_t irmp_tmp_id;                                           // ir id (only SAMSUNG)
+#endif
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+static uint8_t      xor_check[6];                                           // check kaseikyo "parity" bits
+static uint_fast8_t genre2;                                                 // save genre2 bits here, later copied to MSB in flags
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+static uint_fast8_t  parity;                                                // number of '1' of the first 14 bits, check if even.
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+static uint_fast8_t  check;                                                 // number of '1' of the first 14 bits, check if even.
+static uint_fast8_t  mitsu_parity;                                          // number of '1' of the first 14 bits, check if even.
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  store bit
+ *  @details  store bit in temp address or temp command
+ *  @param    value to store: 0 or 1
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+// verhindert, dass irmp_store_bit() inline compiliert wird:
+// static void irmp_store_bit (uint_fast8_t) __attribute__ ((noinline));
+
+static void
+irmp_store_bit (uint_fast8_t value)
+{
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_ACP24_PROTOCOL)                                                 // squeeze 64 bits into 16 bits:
+    {
+        if (value)
+        {
+            // ACP24-Frame:
+            //           1         2         3         4         5         6
+            // 0123456789012345678901234567890123456789012345678901234567890123456789
+            // N VVMMM    ? ???    t vmA x                 y                     TTTT
+            //
+            // irmp_data_p->command:
+            //
+            //         5432109876543210
+            //         NAVVvMMMmtxyTTTT
+
+            switch (irmp_bit)
+            {
+                case  0: irmp_tmp_command |= (1<<15); break;                                        // N
+                case  2: irmp_tmp_command |= (1<<13); break;                                        // V
+                case  3: irmp_tmp_command |= (1<<12); break;                                        // V
+                case  4: irmp_tmp_command |= (1<<10); break;                                        // M
+                case  5: irmp_tmp_command |= (1<< 9); break;                                        // M
+                case  6: irmp_tmp_command |= (1<< 8); break;                                        // M
+                case 20: irmp_tmp_command |= (1<< 6); break;                                        // t
+                case 22: irmp_tmp_command |= (1<<11); break;                                        // v
+                case 23: irmp_tmp_command |= (1<< 7); break;                                        // m
+                case 24: irmp_tmp_command |= (1<<14); break;                                        // A
+                case 26: irmp_tmp_command |= (1<< 5); break;                                        // x
+                case 44: irmp_tmp_command |= (1<< 4); break;                                        // y
+                case 66: irmp_tmp_command |= (1<< 3); break;                                        // T
+                case 67: irmp_tmp_command |= (1<< 2); break;                                        // T
+                case 68: irmp_tmp_command |= (1<< 1); break;                                        // T
+                case 69: irmp_tmp_command |= (1<< 0); break;                                        // T
+            }
+        }
+    }
+    else
+#endif // IRMP_SUPPORT_ACP24_PROTOCOL
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL)
+    {
+        if (irmp_bit < 14)
+        {
+            if (value)
+            {
+                parity++;
+            }
+        }
+        else if (irmp_bit == 14)
+        {
+            if (value)                                                                                      // value == 1: even parity
+            {
+                if (parity & 0x01)
+                {
+                    parity = PARITY_CHECK_FAILED;
+                }
+                else
+                {
+                    parity = PARITY_CHECK_OK;
+                }
+            }
+            else
+            {
+                if (parity & 0x01)                                                                          // value == 0: odd parity
+                {
+                    parity = PARITY_CHECK_OK;
+                }
+                else
+                {
+                    parity = PARITY_CHECK_FAILED;
+                }
+            }
+        }
+    }
+    else
+#endif
+    {
+        ;
+    }
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+    if (irmp_bit == 0 && irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL)
+    {
+        first_bit = value;
+    }
+    else
+#endif
+
+    if (irmp_bit >= irmp_param.address_offset && irmp_bit < irmp_param.address_end)
+    {
+        if (irmp_param.lsb_first)
+        {
+            irmp_tmp_address |= (((uint_fast16_t) (value)) << (irmp_bit - irmp_param.address_offset));   // CV wants cast
+        }
+        else
+        {
+            irmp_tmp_address <<= 1;
+            irmp_tmp_address |= value;
+        }
+    }
+    else if (irmp_bit >= irmp_param.command_offset && irmp_bit < irmp_param.command_end)
+    {
+        if (irmp_param.lsb_first)
+        {
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+            if (irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL && irmp_bit >= 32)
+            {
+                irmp_tmp_id |= (((uint_fast16_t) (value)) << (irmp_bit - 32));   // CV wants cast
+            }
+            else
+#endif
+            {
+                irmp_tmp_command |= (((uint_fast16_t) (value)) << (irmp_bit - irmp_param.command_offset));   // CV wants cast
+            }
+        }
+        else
+        {
+            irmp_tmp_command <<= 1;
+            irmp_tmp_command |= value;
+        }
+    }
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_NEC_PROTOCOL || irmp_param.protocol == IRMP_NEC42_PROTOCOL)
+    {
+        if (irmp_bit < 8)
+        {
+            irmp_lgair_address <<= 1;                                                               // LGAIR uses MSB
+            irmp_lgair_address |= value;
+        }
+        else if (irmp_bit < 24)
+        {
+            irmp_lgair_command <<= 1;                                                               // LGAIR uses MSB
+            irmp_lgair_command |= value;
+        }
+    }
+    // NO else!
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit >= 13 && irmp_bit < 26)
+    {
+        irmp_tmp_address2 |= (((uint_fast16_t) (value)) << (irmp_bit - 13));                             // CV wants cast
+    }
+    else
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit >= SAMSUNG_ID_OFFSET && irmp_bit < SAMSUNG_ID_OFFSET + SAMSUNG_ID_LEN)
+    {
+        irmp_tmp_id |= (((uint_fast16_t) (value)) << (irmp_bit - SAMSUNG_ID_OFFSET));                    // store with LSB first
+    }
+    else
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL)
+    {
+        if (irmp_bit >= 20 && irmp_bit < 24)
+        {
+            irmp_tmp_command |= (((uint_fast16_t) (value)) << (irmp_bit - 8));      // store 4 system bits (genre 1) in upper nibble with LSB first
+        }
+        else if (irmp_bit >= 24 && irmp_bit < 28)
+        {
+            genre2 |= (((uint_fast8_t) (value)) << (irmp_bit - 20));                // store 4 system bits (genre 2) in upper nibble with LSB first
+        }
+
+        if (irmp_bit < KASEIKYO_COMPLETE_DATA_LEN)
+        {
+            if (value)
+            {
+                xor_check[irmp_bit / 8] |= 1 << (irmp_bit % 8);
+            }
+            else
+            {
+                xor_check[irmp_bit / 8] &= ~(1 << (irmp_bit % 8));
+            }
+        }
+    }
+    else
+#endif
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+    if (irmp_param.protocol == IRMP_MITSU_HEAVY_PROTOCOL)                           // squeeze 64 bits into 16 bits:
+    {
+        if (irmp_bit == 72 )
+        {                                                                           // irmp_tmp_address, irmp_tmp_command received: check parity & compress
+            mitsu_parity = PARITY_CHECK_OK;
+
+            check = irmp_tmp_address >> 8;                                          // inverted upper byte == lower byte?
+            check = ~ check;
+
+            if (check == (irmp_tmp_address & 0xFF))
+            {                                                                       // ok:
+                irmp_tmp_address <<= 8;                                             // throw away upper byte
+            }
+            else
+            {
+                mitsu_parity = PARITY_CHECK_FAILED;
+            }
+
+            check = irmp_tmp_command >> 8;                                          // inverted upper byte == lower byte?
+            check = ~ check;
+            if (check == (irmp_tmp_command & 0xFF))
+            {                                                                       // ok:  pack together
+                irmp_tmp_address |= irmp_tmp_command & 0xFF;                        // byte 1, byte2 in irmp_tmp_address, irmp_tmp_command can be used for byte 3
+            }
+            else
+            {
+                mitsu_parity = PARITY_CHECK_FAILED;
+            }
+            irmp_tmp_command = 0;
+        }
+
+        if (irmp_bit >= 72 )
+        {                                                                           // receive 3. word in irmp_tmp_command
+            irmp_tmp_command <<= 1;
+            irmp_tmp_command |= value;
+        }
+    }
+    else
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+    {
+        ;
+    }
+
+    irmp_bit++;
+}
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  store bit
+ *  @details  store bit in temp address or temp command
+ *  @param    value to store: 0 or 1
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+static void
+irmp_store_bit2 (uint_fast8_t value)
+{
+    uint_fast8_t irmp_bit2;
+
+    if (irmp_param.protocol)
+    {
+        irmp_bit2 = irmp_bit - 2;
+    }
+    else
+    {
+        irmp_bit2 = irmp_bit - 1;
+    }
+
+    if (irmp_bit2 >= irmp_param2.address_offset && irmp_bit2 < irmp_param2.address_end)
+    {
+        irmp_tmp_address2 |= (((uint_fast16_t) (value)) << (irmp_bit2 - irmp_param2.address_offset));   // CV wants cast
+    }
+    else if (irmp_bit2 >= irmp_param2.command_offset && irmp_bit2 < irmp_param2.command_end)
+    {
+        irmp_tmp_command2 |= (((uint_fast16_t) (value)) << (irmp_bit2 - irmp_param2.command_offset));   // CV wants cast
+    }
+}
+#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+
+#ifdef ANALYZE
+static uint32_t s_curSample;
+static uint32_t s_startBitSample;
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ *  ISR routine
+ *  @details  ISR routine, called 10000 times per second
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+uint_fast8_t
+irmp_ISR (void)
+{
+    static uint_fast8_t     irmp_start_bit_detected;                                // flag: start bit detected
+    static uint_fast8_t     wait_for_space;                                         // flag: wait for data bit space
+    static uint_fast8_t     wait_for_start_space;                                   // flag: wait for start bit space
+    static uint_fast8_t     irmp_pulse_time;                                        // count bit time for pulse
+    static PAUSE_LEN        irmp_pause_time;                                        // count bit time for pause
+    static uint_fast16_t    last_irmp_address = 0xFFFF;                             // save last irmp address to recognize key repetition
+#if IRMP_32_BIT == 1
+    static uint_fast32_t    last_irmp_command = 0xFFFFFFFF;                         // save last irmp command to recognize key repetition
+#else
+    static uint_fast16_t    last_irmp_command = 0xFFFF;                             // save last irmp command to recognize key repetition
+#endif
+    static uint_fast16_t    key_repetition_len;                                     // SIRCS repeats frame 2-5 times with 45 ms pause
+    static uint_fast8_t     repetition_frame_number;
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+    static uint_fast16_t    last_irmp_denon_command;                                // save last irmp command to recognize DENON frame repetition
+    static uint_fast16_t    denon_repetition_len = 0xFFFF;                          // denon repetition len of 2nd auto generated frame
+#endif
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || IRMP_SUPPORT_S100_PROTOCOL == 1
+    static uint_fast8_t     rc5_cmd_bit6;                                           // bit 6 of RC5 command is the inverted 2nd start bit
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1
+    static PAUSE_LEN        last_pause;                                             // last pause value
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1 || IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+    static uint_fast8_t     last_value;                                             // last bit value
+#endif
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+    static uint_fast8_t     waiting_for_2nd_pulse = 0;
+#endif
+    uint_fast8_t            irmp_input;                                             // input value
+
+#ifdef ANALYZE
+
+#if 0 // only for test
+    static uint_fast8_t     last_irmp_start_bit_detected = 0xFF;
+    static uint_fast8_t     last_irmp_pulse_time = 0xFF;
+
+    if (last_irmp_start_bit_detected != irmp_start_bit_detected || last_irmp_pulse_time != irmp_pulse_time)
+    {
+        last_irmp_start_bit_detected    = irmp_start_bit_detected;
+        last_irmp_pulse_time            = irmp_pulse_time;
+
+        printf ("%d %d %d\n", time_counter, irmp_start_bit_detected, irmp_pulse_time);
+    }
+#endif // 0
+
+    time_counter++;
+#endif // ANALYZE
+
+#if defined(__SDCC_stm8)
+    irmp_input = input(IRMP_GPIO_STRUCT->IDR)
+#elif defined(__MBED__)
+    //irmp_input = inputPin;
+    irmp_input = gpio_read (&gpioIRin);
+#else
+    irmp_input = input(IRMP_PIN);
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+    if (irmp_callback_ptr)
+    {
+        static uint_fast8_t last_inverted_input;
+
+        if (last_inverted_input != !irmp_input)
+        {
+            (*irmp_callback_ptr) (! irmp_input);
+            last_inverted_input = !irmp_input;
+        }
+    }
+#endif // IRMP_USE_CALLBACK == 1
+
+    irmp_log(irmp_input);                                                       // log ir signal, if IRMP_LOGGING defined
+
+    if (! irmp_ir_detected)                                                     // ir code already detected?
+    {                                                                           // no...
+        if (! irmp_start_bit_detected)                                          // start bit detected?
+        {                                                                       // no...
+            if (! irmp_input)                                                   // receiving burst?
+            {                                                                   // yes...
+//              irmp_busy_flag = TRUE;
+#ifdef ANALYZE
+                if (! irmp_pulse_time)
+                {
+                    s_startBitSample = s_curSample;
+                    ANALYZE_PRINTF("%8.3fms [starting pulse]\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+                }
+#endif // ANALYZE
+                irmp_pulse_time++;                                              // increment counter
+            }
+            else
+            {                                                                   // no...
+                if (irmp_pulse_time)                                            // it's dark....
+                {                                                               // set flags for counting the time of darkness...
+                    irmp_start_bit_detected = 1;
+                    wait_for_start_space    = 1;
+                    wait_for_space          = 0;
+                    irmp_tmp_command        = 0;
+                    irmp_tmp_address        = 0;
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+                    genre2                  = 0;
+#endif
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+                    irmp_tmp_id = 0;
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1) || IRMP_SUPPORT_NEC42_PROTOCOL == 1
+                    irmp_tmp_command2       = 0;
+                    irmp_tmp_address2       = 0;
+#endif
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+                    irmp_lgair_command      = 0;
+                    irmp_lgair_address      = 0;
+#endif
+                    irmp_bit                = 0xff;
+                    irmp_pause_time         = 1;                                // 1st pause: set to 1, not to 0!
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 || IRMP_SUPPORT_S100_PROTOCOL == 1
+                    rc5_cmd_bit6            = 0;                                // fm 2010-03-07: bugfix: reset it after incomplete RC5 frame!
+#endif
+                }
+                else
+                {
+                    if (key_repetition_len < 0xFFFF)                            // avoid overflow of counter
+                    {
+                        key_repetition_len++;
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+                        if (denon_repetition_len < 0xFFFF)                      // avoid overflow of counter
+                        {
+                            denon_repetition_len++;
+
+                            if (denon_repetition_len >= DENON_AUTO_REPETITION_PAUSE_LEN && last_irmp_denon_command != 0)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("%8.3fms warning: did not receive inverted command repetition\n",
+                                                (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+                                last_irmp_denon_command = 0;
+                                denon_repetition_len = 0xFFFF;
+                            }
+                        }
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+                    }
+                }
+            }
+        }
+        else
+        {
+            if (wait_for_start_space)                                           // we have received start bit...
+            {                                                                   // ...and are counting the time of darkness
+                if (irmp_input)                                                 // still dark?
+                {                                                               // yes
+                    irmp_pause_time++;                                          // increment counter
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+                    if (((irmp_pulse_time < NIKON_START_BIT_PULSE_LEN_MIN || irmp_pulse_time > NIKON_START_BIT_PULSE_LEN_MAX) && irmp_pause_time > IRMP_TIMEOUT_LEN) ||
+                         irmp_pause_time > IRMP_TIMEOUT_NIKON_LEN)
+#else
+                    if (irmp_pause_time > IRMP_TIMEOUT_LEN)                     // timeout?
+#endif
+                    {                                                           // yes...
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                        if (irmp_protocol == IRMP_JVC_PROTOCOL)                 // don't show eror if JVC protocol, irmp_pulse_time has been set below!
+                        {
+                            ;
+                        }
+                        else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("%8.3fms error 1: pause after start bit pulse %d too long: %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time);
+                            ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                        }
+
+                        irmp_start_bit_detected = 0;                            // reset flags, let's wait for another start bit
+                        irmp_pulse_time         = 0;
+                        irmp_pause_time         = 0;
+                    }
+                }
+                else
+                {                                                               // receiving first data pulse!
+                    IRMP_PARAMETER * irmp_param_p;
+                    irmp_param_p = (IRMP_PARAMETER *) 0;
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+                    irmp_param2.protocol = 0;
+#endif
+
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("%8.3fms [start-bit: pulse = %2d, pause = %2d]\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+                    if (irmp_pulse_time >= SIRCS_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIRCS_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= SIRCS_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIRCS_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's SIRCS
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = SIRCS, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        SIRCS_START_BIT_PULSE_LEN_MIN, SIRCS_START_BIT_PULSE_LEN_MAX,
+                                        SIRCS_START_BIT_PAUSE_LEN_MIN, SIRCS_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &sircs_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                    if (irmp_protocol == IRMP_JVC_PROTOCOL &&                                                       // last protocol was JVC, awaiting repeat frame
+                        irmp_pulse_time >= JVC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= JVC_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= JVC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= JVC_REPEAT_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NEC or JVC (type 1) repeat frame, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        JVC_START_BIT_PULSE_LEN_MIN, JVC_START_BIT_PULSE_LEN_MAX,
+                                        JVC_REPEAT_START_BIT_PAUSE_LEN_MIN, JVC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+                    if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX)
+                    {
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NEC42, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+                                        NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nec42_param;
+#else
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NEC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+                                        NEC_START_BIT_PAUSE_LEN_MIN, NEC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+#endif
+                    }
+                    else if (irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN        && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+                             irmp_pause_time >= NEC_REPEAT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_REPEAT_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's NEC
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                        if (irmp_protocol == IRMP_JVC_PROTOCOL)                 // last protocol was JVC, awaiting repeat frame
+                        {                                                       // some jvc remote controls use nec repetition frame for jvc repetition frame
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("protocol = JVC repeat frame type 2, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+                                            NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                            irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+                        }
+                        else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("protocol = NEC (repetition frame), start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+                                            NEC_REPEAT_START_BIT_PAUSE_LEN_MIN, NEC_REPEAT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+
+                            irmp_param_p = (IRMP_PARAMETER *) &nec_rep_param;
+                        }
+                    }
+                    else
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                    if (irmp_protocol == IRMP_JVC_PROTOCOL &&                   // last protocol was JVC, awaiting repeat frame
+                        irmp_pulse_time >= NEC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NEC_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= NEC_0_PAUSE_LEN_MIN         && irmp_pause_time <= NEC_0_PAUSE_LEN_MAX)
+                    {                                                           // it's JVC repetition type 3
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = JVC repeat frame type 3, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NEC_START_BIT_PULSE_LEN_MIN, NEC_START_BIT_PULSE_LEN_MAX,
+                                        NEC_0_PAUSE_LEN_MIN, NEC_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nec_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+                    if (irmp_pulse_time >= TELEFUNKEN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= TELEFUNKEN_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= TELEFUNKEN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= TELEFUNKEN_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = TELEFUNKEN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        TELEFUNKEN_START_BIT_PULSE_LEN_MIN, TELEFUNKEN_START_BIT_PULSE_LEN_MAX,
+                                        TELEFUNKEN_START_BIT_PAUSE_LEN_MIN, TELEFUNKEN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &telefunken_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_TELEFUNKEN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+                    if (irmp_pulse_time >= ROOMBA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= ROOMBA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ROOMBA_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = ROOMBA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        ROOMBA_START_BIT_PULSE_LEN_MIN, ROOMBA_START_BIT_PULSE_LEN_MAX,
+                                        ROOMBA_START_BIT_PAUSE_LEN_MIN, ROOMBA_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &roomba_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ACP24_PROTOCOL == 1
+                    if (irmp_pulse_time >= ACP24_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ACP24_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= ACP24_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ACP24_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = ACP24, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        ACP24_START_BIT_PULSE_LEN_MIN, ACP24_START_BIT_PULSE_LEN_MAX,
+                                        ACP24_START_BIT_PAUSE_LEN_MIN, ACP24_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &acp24_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+                    if (irmp_pulse_time >= PENTAX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= PENTAX_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= PENTAX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= PENTAX_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = PENTAX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        PENTAX_START_BIT_PULSE_LEN_MIN, PENTAX_START_BIT_PULSE_LEN_MAX,
+                                        PENTAX_START_BIT_PAUSE_LEN_MIN, PENTAX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &pentax_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_PENTAX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+                    if (irmp_pulse_time >= NIKON_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NIKON_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= NIKON_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NIKON_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NIKON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NIKON_START_BIT_PULSE_LEN_MIN, NIKON_START_BIT_PULSE_LEN_MAX,
+                                        (int)NIKON_START_BIT_PAUSE_LEN_MIN, (int)NIKON_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nikon_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_NIKON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+                    if (irmp_pulse_time >= SAMSUNG_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's SAMSUNG
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = SAMSUNG, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        SAMSUNG_START_BIT_PULSE_LEN_MIN, SAMSUNG_START_BIT_PULSE_LEN_MAX,
+                                        SAMSUNG_START_BIT_PAUSE_LEN_MIN, SAMSUNG_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &samsung_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+                    if (irmp_pulse_time >= SAMSUNGAH_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNGAH_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= SAMSUNGAH_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNGAH_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's SAMSUNGAH
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = SAMSUNGAH, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        SAMSUNGAH_START_BIT_PULSE_LEN_MIN, SAMSUNGAH_START_BIT_PULSE_LEN_MAX,
+                                        SAMSUNGAH_START_BIT_PAUSE_LEN_MIN, SAMSUNGAH_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &samsungah_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SAMSUNGAH_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+                    if (irmp_pulse_time >= MATSUSHITA_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MATSUSHITA_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= MATSUSHITA_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MATSUSHITA_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's MATSUSHITA
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = MATSUSHITA, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        MATSUSHITA_START_BIT_PULSE_LEN_MIN, MATSUSHITA_START_BIT_PULSE_LEN_MAX,
+                                        MATSUSHITA_START_BIT_PAUSE_LEN_MIN, MATSUSHITA_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &matsushita_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_MATSUSHITA_PROTOCOL == 1
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+                    if (irmp_pulse_time >= KASEIKYO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KASEIKYO_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= KASEIKYO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KASEIKYO_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's KASEIKYO
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = KASEIKYO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        KASEIKYO_START_BIT_PULSE_LEN_MIN, KASEIKYO_START_BIT_PULSE_LEN_MAX,
+                                        KASEIKYO_START_BIT_PAUSE_LEN_MIN, KASEIKYO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &kaseikyo_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+                    if (irmp_pulse_time >= PANASONIC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= PANASONIC_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= PANASONIC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= PANASONIC_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's PANASONIC
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = PANASONIC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        PANASONIC_START_BIT_PULSE_LEN_MIN, PANASONIC_START_BIT_PULSE_LEN_MAX,
+                                        PANASONIC_START_BIT_PAUSE_LEN_MIN, PANASONIC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &panasonic_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+                    if (irmp_pulse_time >= MITSU_HEAVY_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MITSU_HEAVY_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's MITSU_HEAVY
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = MITSU_HEAVY, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        MITSU_HEAVY_START_BIT_PULSE_LEN_MIN, MITSU_HEAVY_START_BIT_PULSE_LEN_MAX,
+                                        MITSU_HEAVY_START_BIT_PAUSE_LEN_MIN, MITSU_HEAVY_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &mitsu_heavy_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+
+#if IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+                    if (irmp_pulse_time >= VINCENT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= VINCENT_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= VINCENT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= VINCENT_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's VINCENT
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = VINCENT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        VINCENT_START_BIT_PULSE_LEN_MIN, VINCENT_START_BIT_PULSE_LEN_MAX,
+                                        VINCENT_START_BIT_PAUSE_LEN_MIN, VINCENT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &vincent_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_VINCENT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_METZ_PROTOCOL == 1
+                    if (irmp_pulse_time >= METZ_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= METZ_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= METZ_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= METZ_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = METZ, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        METZ_START_BIT_PULSE_LEN_MIN, METZ_START_BIT_PULSE_LEN_MAX,
+                                        METZ_START_BIT_PAUSE_LEN_MIN, METZ_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &metz_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_METZ_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RADIO1_PROTOCOL == 1
+                    if (irmp_pulse_time >= RADIO1_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RADIO1_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RADIO1_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RADIO1_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RADIO1, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RADIO1_START_BIT_PULSE_LEN_MIN, RADIO1_START_BIT_PULSE_LEN_MAX,
+                                        RADIO1_START_BIT_PAUSE_LEN_MIN, RADIO1_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &radio1_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RRADIO1_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1
+                    if (irmp_pulse_time >= RECS80_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RECS80_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's RECS80
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RECS80, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RECS80_START_BIT_PULSE_LEN_MIN, RECS80_START_BIT_PULSE_LEN_MAX,
+                                        RECS80_START_BIT_PAUSE_LEN_MIN, RECS80_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &recs80_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RECS80_PROTOCOL == 1
+
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+                    if (((irmp_pulse_time >= S100_START_BIT_LEN_MIN     && irmp_pulse_time <= S100_START_BIT_LEN_MAX) ||
+                         (irmp_pulse_time >= 2 * S100_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * S100_START_BIT_LEN_MAX)) &&
+                        ((irmp_pause_time >= S100_START_BIT_LEN_MIN     && irmp_pause_time <= S100_START_BIT_LEN_MAX) ||
+                         (irmp_pause_time >= 2 * S100_START_BIT_LEN_MIN && irmp_pause_time <= 2 * S100_START_BIT_LEN_MAX)))
+                    {                                                           // it's S100
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = S100, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        S100_START_BIT_LEN_MIN, S100_START_BIT_LEN_MAX,
+                                        2 * S100_START_BIT_LEN_MIN, 2 * S100_START_BIT_LEN_MAX,
+                                        S100_START_BIT_LEN_MIN, S100_START_BIT_LEN_MAX,
+                                        2 * S100_START_BIT_LEN_MIN, 2 * S100_START_BIT_LEN_MAX);
+#endif // ANALYZE
+
+                        irmp_param_p = (IRMP_PARAMETER *) &s100_param;
+                        last_pause = irmp_pause_time;
+
+                        if ((irmp_pulse_time > S100_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * S100_START_BIT_LEN_MAX) ||
+                            (irmp_pause_time > S100_START_BIT_LEN_MAX && irmp_pause_time <= 2 * S100_START_BIT_LEN_MAX))
+                        {
+                          last_value  = 0;
+                          rc5_cmd_bit6 = 1<<6;
+                        }
+                        else
+                        {
+                          last_value  = 1;
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_S100_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+                    if (((irmp_pulse_time >= RC5_START_BIT_LEN_MIN     && irmp_pulse_time <= RC5_START_BIT_LEN_MAX) ||
+                         (irmp_pulse_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX)) &&
+                        ((irmp_pause_time >= RC5_START_BIT_LEN_MIN     && irmp_pause_time <= RC5_START_BIT_LEN_MAX) ||
+                         (irmp_pause_time >= 2 * RC5_START_BIT_LEN_MIN && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX)))
+                    {                                                           // it's RC5
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+                        if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX &&
+                            irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("protocol = RC5 or FDC\n");
+                            ANALYZE_PRINTF ("FDC start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX,
+                                            FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX);
+                            ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+                            memcpy_P (&irmp_param2, &fdc_param, sizeof (IRMP_PARAMETER));
+                        }
+                        else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                        if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX &&
+                            irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("protocol = RC5 or RCCAR\n");
+                            ANALYZE_PRINTF ("RCCAR start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX,
+                                            RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX);
+                            ANALYZE_PRINTF ("RC5 start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+                            memcpy_P (&irmp_param2, &rccar_param, sizeof (IRMP_PARAMETER));
+                        }
+                        else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("protocol = RC5, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or pulse: %3d - %3d, pause: %3d - %3d\n",
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+                                            2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX,
+                                            RC5_START_BIT_LEN_MIN, RC5_START_BIT_LEN_MAX,
+                                            2 * RC5_START_BIT_LEN_MIN, 2 * RC5_START_BIT_LEN_MAX);
+#endif // ANALYZE
+                        }
+
+                        irmp_param_p = (IRMP_PARAMETER *) &rc5_param;
+                        last_pause = irmp_pause_time;
+
+                        if ((irmp_pulse_time > RC5_START_BIT_LEN_MAX && irmp_pulse_time <= 2 * RC5_START_BIT_LEN_MAX) ||
+                            (irmp_pause_time > RC5_START_BIT_LEN_MAX && irmp_pause_time <= 2 * RC5_START_BIT_LEN_MAX))
+                        {
+                            last_value  = 0;
+                            rc5_cmd_bit6 = 1<<6;
+                        }
+                        else
+                        {
+                            last_value  = 1;
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_RC5_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+                    if ((irmp_pulse_time >= RCII_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCII_START_BIT_PULSE_LEN_MAX) &&
+                        (irmp_pause_time >= RCII_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCII_START_BIT_PAUSE_LEN_MAX))
+                    {                                                           // it's RCII
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RCII, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RCII_START_BIT_PULSE_LEN_MIN, RCII_START_BIT_PULSE_LEN_MAX,
+                                        RCII_START_BIT_PAUSE_LEN_MIN, RCII_START_BIT_PAUSE_LEN_MAX)
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &rcii_param;
+                        last_pause = irmp_pause_time;
+                        waiting_for_2nd_pulse = 1;
+                        last_value  = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RCII_PROTOCOL == 1
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+                    if ( (irmp_pulse_time >= DENON_PULSE_LEN_MIN && irmp_pulse_time <= DENON_PULSE_LEN_MAX) &&
+                        ((irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX) ||
+                         (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX)))
+                    {                                                           // it's DENON
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = DENON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+                                        DENON_PULSE_LEN_MIN, DENON_PULSE_LEN_MAX,
+                                        DENON_1_PAUSE_LEN_MIN, DENON_1_PAUSE_LEN_MAX,
+                                        DENON_0_PAUSE_LEN_MIN, DENON_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &denon_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+                    if ( (irmp_pulse_time >= THOMSON_PULSE_LEN_MIN && irmp_pulse_time <= THOMSON_PULSE_LEN_MAX) &&
+                        ((irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX) ||
+                         (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX)))
+                    {                                                           // it's THOMSON
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = THOMSON, start bit timings: pulse: %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+                                        THOMSON_PULSE_LEN_MIN, THOMSON_PULSE_LEN_MAX,
+                                        THOMSON_1_PAUSE_LEN_MIN, THOMSON_1_PAUSE_LEN_MAX,
+                                        THOMSON_0_PAUSE_LEN_MIN, THOMSON_0_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &thomson_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+
+#if IRMP_SUPPORT_BOSE_PROTOCOL == 1
+                    if (irmp_pulse_time >= BOSE_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= BOSE_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= BOSE_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BOSE_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = BOSE, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        BOSE_START_BIT_PULSE_LEN_MIN, BOSE_START_BIT_PULSE_LEN_MAX,
+                                        BOSE_START_BIT_PAUSE_LEN_MIN, BOSE_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &bose_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_BOSE_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                    if (irmp_pulse_time >= RC6_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RC6_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RC6_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RC6_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's RC6
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RC6, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RC6_START_BIT_PULSE_LEN_MIN, RC6_START_BIT_PULSE_LEN_MAX,
+                                        RC6_START_BIT_PAUSE_LEN_MIN, RC6_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &rc6_param;
+                        last_pause = 0;
+                        last_value = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+                    if (irmp_pulse_time >= RECS80EXT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RECS80EXT_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RECS80EXT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RECS80EXT_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's RECS80EXT
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RECS80EXT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RECS80EXT_START_BIT_PULSE_LEN_MIN, RECS80EXT_START_BIT_PULSE_LEN_MAX,
+                                        RECS80EXT_START_BIT_PAUSE_LEN_MIN, RECS80EXT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &recs80ext_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+                    if (irmp_pulse_time >= NUBERT_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NUBERT_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= NUBERT_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NUBERT_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's NUBERT
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NUBERT, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NUBERT_START_BIT_PULSE_LEN_MIN, NUBERT_START_BIT_PULSE_LEN_MAX,
+                                        NUBERT_START_BIT_PAUSE_LEN_MIN, NUBERT_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &nubert_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+                    if (irmp_pulse_time >= FAN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FAN_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= FAN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FAN_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's FAN
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = FAN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        FAN_START_BIT_PULSE_LEN_MIN, FAN_START_BIT_PULSE_LEN_MAX,
+                                        FAN_START_BIT_PAUSE_LEN_MIN, FAN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &fan_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_FAN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+                    if (irmp_pulse_time >= SPEAKER_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SPEAKER_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= SPEAKER_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SPEAKER_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's SPEAKER
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = SPEAKER, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        SPEAKER_START_BIT_PULSE_LEN_MIN, SPEAKER_START_BIT_PULSE_LEN_MAX,
+                                        SPEAKER_START_BIT_PAUSE_LEN_MIN, SPEAKER_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &speaker_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+                    if (irmp_pulse_time >= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX &&
+                        irmp_pause_time >= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX)
+                    {                                                           // it's BANG_OLUFSEN
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = BANG_OLUFSEN\n");
+                        ANALYZE_PRINTF ("start bit 1 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        BANG_OLUFSEN_START_BIT1_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PULSE_LEN_MAX,
+                                        BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT1_PAUSE_LEN_MAX);
+                        ANALYZE_PRINTF ("start bit 2 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        BANG_OLUFSEN_START_BIT2_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PULSE_LEN_MAX,
+                                        BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT2_PAUSE_LEN_MAX);
+                        ANALYZE_PRINTF ("start bit 3 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        BANG_OLUFSEN_START_BIT3_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PULSE_LEN_MAX,
+                                        BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX);
+                        ANALYZE_PRINTF ("start bit 4 timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        BANG_OLUFSEN_START_BIT4_PULSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PULSE_LEN_MAX,
+                                        BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MIN, BANG_OLUFSEN_START_BIT4_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &bang_olufsen_param;
+                        last_value = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+                    if (irmp_pulse_time >= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN && irmp_pulse_time <= GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX &&
+                        irmp_pause_time >= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN && irmp_pause_time <= GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX)
+                    {                                                           // it's GRUNDIG
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = GRUNDIG, pre bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        GRUNDIG_NOKIA_IR60_START_BIT_LEN_MIN, GRUNDIG_NOKIA_IR60_START_BIT_LEN_MAX,
+                                        GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MIN, GRUNDIG_NOKIA_IR60_PRE_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &grundig_param;
+                        last_pause = irmp_pause_time;
+                        last_value  = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1 // check MERLIN before RUWIDO!
+                    if (irmp_pulse_time >= MERLIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= MERLIN_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= MERLIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= MERLIN_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's MERLIN
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = MERLIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        MERLIN_START_BIT_PULSE_LEN_MIN, MERLIN_START_BIT_PULSE_LEN_MAX,
+                                        MERLIN_START_BIT_PAUSE_LEN_MIN, MERLIN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &merlin_param;
+                        last_pause = irmp_pause_time;
+                        last_value = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+                    if (((irmp_pulse_time >= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX) ||
+                         (irmp_pulse_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX)) &&
+                        ((irmp_pause_time >= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX) ||
+                         (irmp_pause_time >= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX)))
+                    {                                                           // it's RUWIDO or SIEMENS
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RUWIDO, start bit timings: pulse: %3d - %3d or %3d - %3d, pause: %3d - %3d or %3d - %3d\n",
+                                        SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN,   SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX,
+                                        2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PULSE_LEN_MAX,
+                                        SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN,   SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX,
+                                        2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MIN, 2 * SIEMENS_OR_RUWIDO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &ruwido_param;
+                        last_pause = irmp_pause_time;
+                        last_value  = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+                    if (irmp_pulse_time >= FDC_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= FDC_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= FDC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= FDC_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = FDC, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        FDC_START_BIT_PULSE_LEN_MIN, FDC_START_BIT_PULSE_LEN_MAX,
+                                        FDC_START_BIT_PAUSE_LEN_MIN, FDC_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &fdc_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                    if (irmp_pulse_time >= RCCAR_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RCCAR_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RCCAR, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RCCAR_START_BIT_PULSE_LEN_MIN, RCCAR_START_BIT_PULSE_LEN_MAX,
+                                        RCCAR_START_BIT_PAUSE_LEN_MIN, RCCAR_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &rccar_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+                    if (irmp_pulse_time >= KATHREIN_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's KATHREIN
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = KATHREIN, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        KATHREIN_START_BIT_PULSE_LEN_MIN, KATHREIN_START_BIT_PULSE_LEN_MAX,
+                                        KATHREIN_START_BIT_PAUSE_LEN_MIN, KATHREIN_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &kathrein_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+                    if (irmp_pulse_time >= NETBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= NETBOX_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= NETBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NETBOX_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's NETBOX
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = NETBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        NETBOX_START_BIT_PULSE_LEN_MIN, NETBOX_START_BIT_PULSE_LEN_MAX,
+                                        NETBOX_START_BIT_PAUSE_LEN_MIN, NETBOX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &netbox_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1
+                    if (irmp_pulse_time >= LEGO_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= LEGO_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= LEGO_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= LEGO_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = LEGO, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        LEGO_START_BIT_PULSE_LEN_MIN, LEGO_START_BIT_PULSE_LEN_MAX,
+                                        LEGO_START_BIT_PAUSE_LEN_MIN, LEGO_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &lego_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_LEGO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+                    if (irmp_pulse_time >= IRMP16_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= IRMP16_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= IRMP16_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= IRMP16_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = IRMP16, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        IRMP16_START_BIT_PULSE_LEN_MIN, IRMP16_START_BIT_PULSE_LEN_MAX,
+                                        IRMP16_START_BIT_PAUSE_LEN_MIN, IRMP16_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &irmp16_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_IRMP16_PROTOCOL == 1
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1
+                    if (irmp_pulse_time >= GREE_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= GREE_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= GREE_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= GREE_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = GREE, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        GREE_START_BIT_PULSE_LEN_MIN, GREE_START_BIT_PULSE_LEN_MAX,
+                                        GREE_START_BIT_PAUSE_LEN_MIN, GREE_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &gree_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_GREE_PROTOCOL == 1
+
+#if IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+                    if (irmp_pulse_time >= A1TVBOX_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= A1TVBOX_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= A1TVBOX_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= A1TVBOX_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's A1TVBOX
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = A1TVBOX, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        A1TVBOX_START_BIT_PULSE_LEN_MIN, A1TVBOX_START_BIT_PULSE_LEN_MAX,
+                                        A1TVBOX_START_BIT_PAUSE_LEN_MIN, A1TVBOX_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &a1tvbox_param;
+                        last_pause = 0;
+                        last_value = 1;
+                    }
+                    else
+#endif // IRMP_SUPPORT_A1TVBOX_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+                    if (irmp_pulse_time >= ORTEK_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= ORTEK_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= ORTEK_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= ORTEK_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's ORTEK (Hama)
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = ORTEK, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        ORTEK_START_BIT_PULSE_LEN_MIN, ORTEK_START_BIT_PULSE_LEN_MAX,
+                                        ORTEK_START_BIT_PAUSE_LEN_MIN, ORTEK_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &ortek_param;
+                        last_pause  = 0;
+                        last_value  = 1;
+                        parity      = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+                    if (irmp_pulse_time >= RCMM32_START_BIT_PULSE_LEN_MIN && irmp_pulse_time <= RCMM32_START_BIT_PULSE_LEN_MAX &&
+                        irmp_pause_time >= RCMM32_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_START_BIT_PAUSE_LEN_MAX)
+                    {                                                           // it's RCMM
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = RCMM, start bit timings: pulse: %3d - %3d, pause: %3d - %3d\n",
+                                        RCMM32_START_BIT_PULSE_LEN_MIN, RCMM32_START_BIT_PULSE_LEN_MAX,
+                                        RCMM32_START_BIT_PAUSE_LEN_MIN, RCMM32_START_BIT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                        irmp_param_p = (IRMP_PARAMETER *) &rcmm_param;
+                    }
+                    else
+#endif // IRMP_SUPPORT_RCMM_PROTOCOL == 1
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("protocol = UNKNOWN\n");
+#endif // ANALYZE
+                        irmp_start_bit_detected = 0;                            // wait for another start bit...
+                    }
+
+                    if (irmp_start_bit_detected)
+                    {
+                        memcpy_P (&irmp_param, irmp_param_p, sizeof (IRMP_PARAMETER));
+
+                        if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max);
+                            ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max);
+#endif // ANALYZE
+                        }
+                        else
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_1_len_min, irmp_param.pulse_1_len_max,
+                                            2 * irmp_param.pulse_1_len_min, 2 * irmp_param.pulse_1_len_max);
+                            ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_1_len_min, irmp_param.pause_1_len_max,
+                                            2 * irmp_param.pause_1_len_min, 2 * irmp_param.pause_1_len_max);
+#endif // ANALYZE
+                        }
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+                        if (irmp_param2.protocol)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param2.pulse_0_len_min, irmp_param2.pulse_0_len_max);
+                            ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param2.pause_0_len_min, irmp_param2.pause_0_len_max);
+                            ANALYZE_PRINTF ("pulse_1: %3d - %3d\n", irmp_param2.pulse_1_len_min, irmp_param2.pulse_1_len_max);
+                            ANALYZE_PRINTF ("pause_1: %3d - %3d\n", irmp_param2.pause_1_len_min, irmp_param2.pause_1_len_max);
+#endif // ANALYZE
+                        }
+#endif
+
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_RC6_PROTOCOL)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse_toggle: %3d - %3d\n", RC6_TOGGLE_BIT_LEN_MIN, RC6_TOGGLE_BIT_LEN_MAX);
+#endif // ANALYZE
+                        }
+#endif
+
+                        if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse_0: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+                            ANALYZE_PRINTF ("pause_0: %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max);
+#endif // ANALYZE
+                        }
+                        else
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("pulse: %3d - %3d or %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max,
+                                            2 * irmp_param.pulse_0_len_min, 2 * irmp_param.pulse_0_len_max);
+                            ANALYZE_PRINTF ("pause: %3d - %3d or %3d - %3d\n", irmp_param.pause_0_len_min, irmp_param.pause_0_len_max,
+                                            2 * irmp_param.pause_0_len_min, 2 * irmp_param.pause_0_len_max);
+#endif // ANALYZE
+                        }
+
+#ifdef ANALYZE
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL)
+                        {
+                            ANALYZE_PRINTF ("pulse_r: %3d - %3d\n", irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+                            ANALYZE_PRINTF ("pause_r: %3d - %3d\n", BANG_OLUFSEN_R_PAUSE_LEN_MIN, BANG_OLUFSEN_R_PAUSE_LEN_MAX);
+                        }
+#endif
+
+                        ANALYZE_PRINTF ("command_offset: %2d\n", irmp_param.command_offset);
+                        ANALYZE_PRINTF ("command_len:    %3d\n", irmp_param.command_end - irmp_param.command_offset);
+                        ANALYZE_PRINTF ("complete_len:   %3d\n", irmp_param.complete_len);
+                        ANALYZE_PRINTF ("stop_bit:       %3d\n", irmp_param.stop_bit);
+#endif // ANALYZE
+                    }
+
+                    irmp_bit = 0;
+
+#if IRMP_SUPPORT_MANCHESTER == 1
+                    if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) &&
+                         irmp_param.protocol != IRMP_RUWIDO_PROTOCOL && // Manchester, but not RUWIDO
+                         irmp_param.protocol != IRMP_RC6_PROTOCOL /*** &&    // Manchester, but not RC6
+                         irmp_param.protocol != IRMP_RCII_PROTOCOL ****/)     // Manchester, but not RCII
+                    {
+                        if (irmp_pause_time > irmp_param.pulse_1_len_max && irmp_pause_time <= 2 * irmp_param.pulse_1_len_max)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+                            ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1');
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0 : 1);
+                        }
+                        else if (! last_value)  // && irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+                            ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0');
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0);
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+
+#if IRMP_SUPPORT_SERIAL == 1
+                    if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL)
+                    {
+                        ; // do nothing
+                    }
+                    else
+#endif // IRMP_SUPPORT_SERIAL == 1
+
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_DENON_PROTOCOL)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+                        if (irmp_pause_time >= DENON_1_PAUSE_LEN_MIN && irmp_pause_time <= DENON_1_PAUSE_LEN_MAX)
+                        {                                                       // pause timings correct for "1"?
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('1');                                  // yes, store 1
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_store_bit (1);
+                        }
+                        else // if (irmp_pause_time >= DENON_0_PAUSE_LEN_MIN && irmp_pause_time <= DENON_0_PAUSE_LEN_MAX)
+                        {                                                       // pause timings correct for "0"?
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('0');                                  // yes, store 0
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_store_bit (0);
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL == 1
+#if IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_THOMSON_PROTOCOL)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+                        if (irmp_pause_time >= THOMSON_1_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_1_PAUSE_LEN_MAX)
+                        {                                                       // pause timings correct for "1"?
+#ifdef ANALYZE
+                          ANALYZE_PUTCHAR ('1');                                  // yes, store 1
+                          ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                          irmp_store_bit (1);
+                        }
+                        else // if (irmp_pause_time >= THOMSON_0_PAUSE_LEN_MIN && irmp_pause_time <= THOMSON_0_PAUSE_LEN_MAX)
+                        {                                                       // pause timings correct for "0"?
+#ifdef ANALYZE
+                          ANALYZE_PUTCHAR ('0');                                  // yes, store 0
+                          ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                          irmp_store_bit (0);
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_THOMSON_PROTOCOL == 1
+                    {
+                        ;                                                       // else do nothing
+                    }
+
+                    irmp_pulse_time = 1;                                        // set counter to 1, not 0
+                    irmp_pause_time = 0;
+                    wait_for_start_space = 0;
+                }
+            }
+            else if (wait_for_space)                                            // the data section....
+            {                                                                   // counting the time of darkness....
+                uint_fast8_t got_light = FALSE;
+
+                if (irmp_input)                                                 // still dark?
+                {                                                               // yes...
+                    if (irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 1)
+                    {
+                        if (
+#if IRMP_SUPPORT_MANCHESTER == 1
+                            (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) ||
+#endif
+#if IRMP_SUPPORT_SERIAL == 1
+                            (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) ||
+#endif
+                            (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max))
+                        {
+#ifdef ANALYZE
+                            if (! (irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))
+                            {
+                                ANALYZE_PRINTF ("stop bit detected\n");
+                            }
+#endif // ANALYZE
+                            irmp_param.stop_bit = 0;
+                        }
+                        else
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("error: stop bit timing wrong, irmp_bit = %d, irmp_pulse_time = %d, pulse_0_len_min = %d, pulse_0_len_max = %d\n",
+                                            irmp_bit, irmp_pulse_time, irmp_param.pulse_0_len_min, irmp_param.pulse_0_len_max);
+#endif // ANALYZE
+                            irmp_start_bit_detected = 0;                        // wait for another start bit...
+                            irmp_pulse_time         = 0;
+                            irmp_pause_time         = 0;
+                        }
+                    }
+                    else
+                    {
+                        irmp_pause_time++;                                                          // increment counter
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL &&                           // Sony has a variable number of bits:
+                            irmp_pause_time > SIRCS_PAUSE_LEN_MAX &&                                // minimum is 12
+                            irmp_bit >= 12 - 1)                                                     // pause too long?
+                        {                                                                           // yes, break and close this frame
+                            irmp_param.complete_len = irmp_bit + 1;                                 // set new complete length
+                            got_light = TRUE;                                                       // this is a lie, but helps (generates stop bit)
+                            irmp_tmp_address |= (irmp_bit - SIRCS_MINIMUM_DATA_LEN + 1) << 8;       // new: store number of additional bits in upper byte of address!
+                            irmp_param.command_end = irmp_param.command_offset + irmp_bit + 1;      // correct command length
+                            irmp_pause_time = SIRCS_PAUSE_LEN_MAX - 1;                              // correct pause length
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_MERLIN_PROTOCOL &&                          // Merlin has a variable number of bits:
+                            irmp_pause_time > MERLIN_START_BIT_PAUSE_LEN_MAX &&                     // minimum is 8
+                            irmp_bit >= 8 - 1)                                                      // pause too long?
+                        {                                                                           // yes, break and close this frame
+                            irmp_param.complete_len = irmp_bit;                                     // set new complete length
+                            got_light = TRUE;                                                       // this is a lie, but helps (generates stop bit)
+                            irmp_pause_time = MERLIN_BIT_PAUSE_LEN_MAX - 1;                         // correct pause length
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_FAN_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_FAN_PROTOCOL &&                             // FAN has no stop bit.
+                            irmp_bit >= FAN_COMPLETE_DATA_LEN - 1)                                  // last bit in frame
+                        {                                                                           // yes, break and close this frame
+                            if (irmp_pulse_time <= FAN_0_PULSE_LEN_MAX && irmp_pause_time >= FAN_0_PAUSE_LEN_MIN)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Generating virtual stop bit\n");
+#endif // ANALYZE
+                                got_light = TRUE;                                                   // this is a lie, but helps (generates stop bit)
+                            }
+                            else if (irmp_pulse_time >= FAN_1_PULSE_LEN_MIN && irmp_pause_time >= FAN_1_PAUSE_LEN_MIN)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Generating virtual stop bit\n");
+#endif // ANALYZE
+                                got_light = TRUE;                                                   // this is a lie, but helps (generates stop bit)
+                            }
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_SERIAL == 1
+                        // NETBOX generates no stop bit, here is the timeout condition:
+                        if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL) && irmp_param.protocol == IRMP_NETBOX_PROTOCOL &&
+                            irmp_pause_time >= NETBOX_PULSE_LEN * (NETBOX_COMPLETE_DATA_LEN - irmp_bit))
+                        {
+                            got_light = TRUE;                                                       // this is a lie, but helps (generates stop bit)
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_GRUNDIG_NOKIA_IR60_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && !irmp_param.stop_bit)
+                        {
+                            if (irmp_pause_time > IR60_TIMEOUT_LEN && (irmp_bit == 5 || irmp_bit == 6))
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to IR60 protocol\n");
+#endif // ANALYZE
+                                got_light = TRUE;                                       // this is a lie, but generates a stop bit ;-)
+                                irmp_param.stop_bit = TRUE;                             // set flag
+
+                                irmp_param.protocol         = IRMP_IR60_PROTOCOL;       // change protocol
+                                irmp_param.complete_len     = IR60_COMPLETE_DATA_LEN;   // correct complete len
+                                irmp_param.address_offset   = IR60_ADDRESS_OFFSET;
+                                irmp_param.address_end      = IR60_ADDRESS_OFFSET + IR60_ADDRESS_LEN;
+                                irmp_param.command_offset   = IR60_COMMAND_OFFSET;
+                                irmp_param.command_end      = IR60_COMMAND_OFFSET + IR60_COMMAND_LEN;
+
+                                irmp_tmp_command <<= 1;
+                                irmp_tmp_command |= first_bit;
+                            }
+                            else if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN - 2)
+                            {                                                           // special manchester decoder
+                                irmp_param.complete_len = GRUNDIG_COMPLETE_DATA_LEN;    // correct complete len
+                                got_light = TRUE;                                       // this is a lie, but generates a stop bit ;-)
+                                irmp_param.stop_bit = TRUE;                             // set flag
+                            }
+                            else if (irmp_bit >= GRUNDIG_COMPLETE_DATA_LEN)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to NOKIA protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                irmp_param.protocol         = IRMP_NOKIA_PROTOCOL;      // change protocol
+                                irmp_param.address_offset   = NOKIA_ADDRESS_OFFSET;
+                                irmp_param.address_end      = NOKIA_ADDRESS_OFFSET + NOKIA_ADDRESS_LEN;
+                                irmp_param.command_offset   = NOKIA_COMMAND_OFFSET;
+                                irmp_param.command_end      = NOKIA_COMMAND_OFFSET + NOKIA_COMMAND_LEN;
+
+                                if (irmp_tmp_command & 0x300)
+                                {
+                                    irmp_tmp_address = (irmp_tmp_command >> 8);
+                                    irmp_tmp_command &= 0xFF;
+                                }
+                            }
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_SIEMENS_OR_RUWIDO_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_RUWIDO_PROTOCOL && !irmp_param.stop_bit)
+                        {
+                            if (irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= RUWIDO_COMPLETE_DATA_LEN - 2)
+                            {                                                           // special manchester decoder
+                                irmp_param.complete_len = RUWIDO_COMPLETE_DATA_LEN;     // correct complete len
+                                got_light = TRUE;                                       // this is a lie, but generates a stop bit ;-)
+                                irmp_param.stop_bit = TRUE;                             // set flag
+                            }
+                            else if (irmp_bit >= RUWIDO_COMPLETE_DATA_LEN)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to SIEMENS protocol\n");
+#endif // ANALYZE
+                                irmp_param.protocol         = IRMP_SIEMENS_PROTOCOL;    // change protocol
+                                irmp_param.address_offset   = SIEMENS_ADDRESS_OFFSET;
+                                irmp_param.address_end      = SIEMENS_ADDRESS_OFFSET + SIEMENS_ADDRESS_LEN;
+                                irmp_param.command_offset   = SIEMENS_COMMAND_OFFSET;
+                                irmp_param.command_end      = SIEMENS_COMMAND_OFFSET + SIEMENS_COMMAND_LEN;
+
+                                //                   76543210
+                                // RUWIDO:  AAAAAAAAACCCCCCCp
+                                // SIEMENS: AAAAAAAAAAACCCCCCCCCCp
+                                irmp_tmp_address <<= 2;
+                                irmp_tmp_address |= (irmp_tmp_command >> 6);
+                                irmp_tmp_command &= 0x003F;
+//                              irmp_tmp_command <<= 4;
+                                irmp_tmp_command |= last_value;
+                            }
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_ROOMBA_PROTOCOL &&                          // Roomba has no stop bit
+                            irmp_bit >= ROOMBA_COMPLETE_DATA_LEN - 1)                               // it's the last data bit...
+                        {                                                                           // break and close this frame
+                            if (irmp_pulse_time >= ROOMBA_1_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_1_PULSE_LEN_MAX)
+                            {
+                                irmp_pause_time = ROOMBA_1_PAUSE_LEN_EXACT;
+                            }
+                            else if (irmp_pulse_time >= ROOMBA_0_PULSE_LEN_MIN && irmp_pulse_time <= ROOMBA_0_PULSE_LEN_MAX)
+                            {
+                                irmp_pause_time = ROOMBA_0_PAUSE_LEN;
+                            }
+
+                            got_light = TRUE;                                                       // this is a lie, but helps (generates stop bit)
+                        }
+                        else
+#endif
+#if IRMP_SUPPORT_MANCHESTER == 1
+                        if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER) &&
+                            irmp_pause_time >= 2 * irmp_param.pause_1_len_max && irmp_bit >= irmp_param.complete_len - 2 && !irmp_param.stop_bit)
+                        {                                                       // special manchester decoder
+                            got_light = TRUE;                                   // this is a lie, but generates a stop bit ;-)
+                            irmp_param.stop_bit = TRUE;                         // set flag
+                        }
+                        else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+                        if (irmp_pause_time > IRMP_TIMEOUT_LEN)                 // timeout?
+                        {                                                       // yes...
+                            if (irmp_bit == irmp_param.complete_len - 1 && irmp_param.stop_bit == 0)
+                            {
+                                irmp_bit++;
+                            }
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+                            else if ((irmp_param.protocol == IRMP_NEC_PROTOCOL || irmp_param.protocol == IRMP_NEC42_PROTOCOL) && irmp_bit == 0)
+                            {                                                               // it was a non-standard repetition frame
+#ifdef ANALYZE                                                                              // with 4500us pause instead of 2250us
+                                ANALYZE_PRINTF ("Detected non-standard repetition frame, switching to NEC repetition\n");
+#endif // ANALYZE
+                                if (key_repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX)
+                                {
+                                    irmp_param.stop_bit     = TRUE;                         // set flag
+                                    irmp_param.protocol     = IRMP_NEC_PROTOCOL;            // switch protocol
+                                    irmp_param.complete_len = irmp_bit;                     // patch length: 16 or 17
+                                    irmp_tmp_address = last_irmp_address;                   // address is last address
+                                    irmp_tmp_command = last_irmp_command;                   // command is last command
+                                    irmp_flags |= IRMP_FLAG_REPETITION;
+                                    key_repetition_len = 0;
+                                }
+                                else
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("ignoring NEC repetition frame: timeout occured, key_repetition_len = %d > %d\n",
+                                                    (int)key_repetition_len, (int)NEC_FRAME_REPEAT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                                    irmp_ir_detected = FALSE;
+                                }
+                            }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17))      // it was a JVC stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.protocol     = IRMP_JVC_PROTOCOL;                        // switch protocol
+                                irmp_param.complete_len = irmp_bit;                                 // patch length: 16 or 17
+                                irmp_tmp_command        = (irmp_tmp_address >> 4);                  // set command: upper 12 bits are command bits
+                                irmp_tmp_address        = irmp_tmp_address & 0x000F;                // lower 4 bits are address bits
+                                irmp_start_bit_detected = 1;                                        // tricky: don't wait for another start bit...
+                            }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_NEC_PROTOCOL && (irmp_bit == 28 || irmp_bit == 29))      // it was a LGAIR stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to LGAIR protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.protocol     = IRMP_LGAIR_PROTOCOL;                      // switch protocol
+                                irmp_param.complete_len = irmp_bit;                                 // patch length: 16 or 17
+                                irmp_tmp_command        = irmp_lgair_command;                       // set command: upper 8 bits are command bits
+                                irmp_tmp_address        = irmp_lgair_address;                       // lower 4 bits are address bits
+                                irmp_start_bit_detected = 1;                                        // tricky: don't wait for another start bit...
+                            }
+#endif // IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 32)      // it was a NEC stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to NEC protocol\n");
+#endif // ANALYZE
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.protocol     = IRMP_NEC_PROTOCOL;                        // switch protocol
+                                irmp_param.complete_len = irmp_bit;                                 // patch length: 16 or 17
+
+                                //        0123456789ABC0123456789ABC0123456701234567
+                                // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc
+                                // NEC:   AAAAAAAAaaaaaaaaCCCCCCCCcccccccc
+                                irmp_tmp_address        |= (irmp_tmp_address2 & 0x0007) << 13;      // fm 2012-02-13: 12 -> 13
+                                irmp_tmp_command        = (irmp_tmp_address2 >> 3) | (irmp_tmp_command << 10);
+                            }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL == 1
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && irmp_bit == 28)      // it was a NEC stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to LGAIR protocol\n");
+#endif // ANALYZE
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.protocol     = IRMP_LGAIR_PROTOCOL;                      // switch protocol
+                                irmp_param.complete_len = irmp_bit;                                 // patch length: 16 or 17
+                                irmp_tmp_address        = irmp_lgair_address;
+                                irmp_tmp_command        = irmp_lgair_command;
+                            }
+#endif // IRMP_SUPPORT_LGAIR_PROTOCOL == 1
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_NEC42_PROTOCOL && (irmp_bit == 16 || irmp_bit == 17))  // it was a JVC stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to JVC protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.protocol     = IRMP_JVC_PROTOCOL;                        // switch protocol
+                                irmp_param.complete_len = irmp_bit;                                 // patch length: 16 or 17
+
+                                //        0123456789ABC0123456789ABC0123456701234567
+                                // NEC42: AAAAAAAAAAAAAaaaaaaaaaaaaaCCCCCCCCcccccccc
+                                // JVC:   AAAACCCCCCCCCCCC
+                                irmp_tmp_command        = (irmp_tmp_address >> 4) | (irmp_tmp_address2 << 9);   // set command: upper 12 bits are command bits
+                                irmp_tmp_address        = irmp_tmp_address & 0x000F;                            // lower 4 bits are address bits
+                            }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL && irmp_bit == 32)          // it was a SAMSUNG32 stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol\n");
+#endif // ANALYZE
+                                irmp_param.protocol         = IRMP_SAMSUNG32_PROTOCOL;
+                                irmp_param.command_offset   = SAMSUNG32_COMMAND_OFFSET;
+                                irmp_param.command_end      = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN;
+                                irmp_param.complete_len     = SAMSUNG32_COMPLETE_DATA_LEN;
+                            }
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_RCMM32_PROTOCOL && (irmp_bit == 12 || irmp_bit == 24))  // it was a RCMM stop bit
+                            {
+                                if (irmp_bit == 12)
+                                {
+                                    irmp_tmp_command = (irmp_tmp_address & 0xFF);                   // set command: lower 8 bits are command bits
+                                    irmp_tmp_address >>= 8;                                         // upper 4 bits are address bits
+
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("Switching to RCMM12 protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                    irmp_param.protocol     = IRMP_RCMM12_PROTOCOL;                 // switch protocol
+                                }
+                                else // if ((irmp_bit == 24)
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("Switching to RCMM24 protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                    irmp_param.protocol     = IRMP_RCMM24_PROTOCOL;                 // switch protocol
+                                }
+                                irmp_param.stop_bit     = TRUE;                                     // set flag
+                                irmp_param.complete_len = irmp_bit;                                 // patch length
+                            }
+#endif // IRMP_SUPPORT_RCMM_PROTOCOL == 1
+
+#if IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+                            else if (irmp_param.protocol == IRMP_MATSUSHITA_PROTOCOL && irmp_bit == 22)  // it was a TECHNICS stop bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to TECHNICS protocol, irmp_bit = %d\n", irmp_bit);
+#endif // ANALYZE
+                                // Situation:
+                                // The first 12 bits have been stored in irmp_tmp_command (LSB first)
+                                // The following 10 bits have been stored in irmp_tmp_address (LSB first)
+                                // The code of TECHNICS is:
+                                //   cccccccccccCCCCCCCCCCC (11 times c and 11 times C)
+                                //   ccccccccccccaaaaaaaaaa
+                                // where C is inverted value of c
+
+                                irmp_tmp_address <<= 1;
+                                if (irmp_tmp_command & (1<<11))
+                                {
+                                    irmp_tmp_address |= 1;
+                                    irmp_tmp_command &= ~(1<<11);
+                                }
+
+                                if (irmp_tmp_command == ((~irmp_tmp_address) & 0x07FF))
+                                {
+                                    irmp_tmp_address = 0;
+
+                                    irmp_param.protocol     = IRMP_TECHNICS_PROTOCOL;                   // switch protocol
+                                    irmp_param.complete_len = irmp_bit;                                 // patch length
+                                }
+                                else
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("error 8: TECHNICS frame error\n");
+                                    ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                    irmp_start_bit_detected = 0;                    // wait for another start bit...
+                                    irmp_pulse_time         = 0;
+                                    irmp_pause_time         = 0;
+                                }
+                            }
+#endif // IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+                            else
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("error 2: pause %d after data bit %d too long\n", irmp_pause_time, irmp_bit);
+                                ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                irmp_start_bit_detected = 0;                    // wait for another start bit...
+                                irmp_pulse_time         = 0;
+                                irmp_pause_time         = 0;
+                            }
+                        }
+                    }
+                }
+                else
+                {                                                               // got light now!
+                    got_light = TRUE;
+                }
+
+                if (got_light)
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("%8.3fms [bit %2d: pulse = %3d, pause = %3d] ", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit, irmp_pulse_time, irmp_pause_time);
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_MANCHESTER == 1
+                    if ((irmp_param.flags & IRMP_PARAM_FLAG_IS_MANCHESTER))                                     // Manchester
+                    {
+#if IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+                        if (irmp_param.complete_len == irmp_bit && irmp_param.protocol == IRMP_MERLIN_PROTOCOL)
+                        {
+                            if (last_value == 0)
+                            {
+                                if (irmp_pulse_time >= 2 * irmp_param.pulse_1_len_min && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max &&
+                                    last_pause >= irmp_param.pause_1_len_min && last_pause <= irmp_param.pulse_1_len_max)
+                                {
+                                    irmp_param.complete_len += 2;
+                                    irmp_store_bit(0);
+                                    irmp_store_bit(1);
+                                }
+                            }
+                            else
+                            {
+                                if (last_pause >= 2 * irmp_param.pause_1_len_min && last_pause <= 2 * irmp_param.pulse_1_len_max)
+                                {
+                                    if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max)
+                                    {
+                                        irmp_param.complete_len++;
+                                        irmp_store_bit(0);
+                                    }
+                                    else if (irmp_pulse_time >= 2 * irmp_param.pulse_1_len_min && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max)
+                                    {
+                                        irmp_param.complete_len += 2;
+                                        irmp_store_bit(0);
+                                        irmp_store_bit(1);
+                                    }
+                                }
+                            }
+                        }
+                        else
+#endif
+#if 1
+                        if (irmp_pulse_time > irmp_param.pulse_1_len_max /* && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max */)
+#else // better, but some IR-RCs use asymmetric timings :-/
+                        if (irmp_pulse_time > irmp_param.pulse_1_len_max && irmp_pulse_time <= 2 * irmp_param.pulse_1_len_max &&
+                            irmp_pause_time <= 2 * irmp_param.pause_1_len_max)
+#endif
+                        {
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                            if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN)         // RC6 toggle bit
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('T');
+#endif // ANALYZE
+                                if (irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG)                      // RC6 mode 6A
+                                {
+                                    irmp_store_bit (1);
+                                    last_value = 1;
+                                }
+                                else                                                                            // RC6 mode 0
+                                {
+                                    irmp_store_bit (0);
+                                    last_value = 0;
+                                }
+#ifdef ANALYZE
+                                ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            }
+                            else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '0' : '1');
+#endif // ANALYZE
+                                irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 0  :  1 );
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                                if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 4 && irmp_pulse_time > RC6_TOGGLE_BIT_LEN_MIN)      // RC6 toggle bit
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PUTCHAR ('T');
+#endif // ANALYZE
+                                    irmp_store_bit (1);
+
+                                    if (irmp_pause_time > 2 * irmp_param.pause_1_len_max)
+                                    {
+                                        last_value = 0;
+                                    }
+                                    else
+                                    {
+                                        last_value = 1;
+                                    }
+#ifdef ANALYZE
+                                    ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                }
+                                else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PUTCHAR ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? '1' : '0');
+#endif // ANALYZE
+                                    irmp_store_bit ((irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 :   0 );
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+                                    if (! irmp_param2.protocol)
+#endif
+                                    {
+#ifdef ANALYZE
+                                        ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                    }
+                                    last_value = (irmp_param.flags & IRMP_PARAM_FLAG_1ST_PULSE_IS_1) ? 1 : 0;
+                                }
+                            }
+                        }
+                        else if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max
+                                 /* && irmp_pause_time <= 2 * irmp_param.pause_1_len_max */)
+                        {
+                            uint_fast8_t manchester_value;
+
+                            if (last_pause > irmp_param.pause_1_len_max && last_pause <= 2 * irmp_param.pause_1_len_max)
+                            {
+                                manchester_value = last_value ? 0 : 1;
+                                last_value  = manchester_value;
+                            }
+                            else
+                            {
+                                manchester_value = last_value;
+                            }
+
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR (manchester_value + '0');
+#endif // ANALYZE
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && (IRMP_SUPPORT_FDC_PROTOCOL == 1 || IRMP_SUPPORT_RCCAR_PROTOCOL == 1)
+                            if (! irmp_param2.protocol)
+#endif
+                            {
+#ifdef ANALYZE
+                                ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            }
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                            if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_bit == 1 && manchester_value == 1)     // RC6 mode != 0 ???
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to RC6A protocol\n");
+#endif // ANALYZE
+                                irmp_param.complete_len = RC6_COMPLETE_DATA_LEN_LONG;
+                                irmp_param.address_offset = 5;
+                                irmp_param.address_end = irmp_param.address_offset + 15;
+                                irmp_param.command_offset = irmp_param.address_end + 1;                                 // skip 1 system bit, changes like a toggle bit
+                                irmp_param.command_end = irmp_param.command_offset + 16 - 1;
+                                irmp_tmp_address = 0;
+                            }
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+
+                            irmp_store_bit (manchester_value);
+                        }
+                        else
+                        {
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1
+                            if (irmp_param2.protocol == IRMP_FDC_PROTOCOL &&
+                                irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX &&
+                                ((irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX) ||
+                                 (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX)))
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('?');
+#endif // ANALYZE
+                                irmp_param.protocol = 0;                // switch to FDC, see below
+                            }
+                            else
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                            if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL &&
+                                irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX &&
+                                ((irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX) ||
+                                 (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX)))
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('?');
+#endif // ANALYZE
+                                irmp_param.protocol = 0;                // switch to RCCAR, see below
+                            }
+                            else
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('?');
+                                ANALYZE_NEWLINE ();
+                                ANALYZE_PRINTF ("error 3 manchester: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                                ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                irmp_start_bit_detected = 0;                            // reset flags and wait for next start bit
+                                irmp_pause_time         = 0;
+                            }
+                        }
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_FDC_PROTOCOL == 1
+                        if (irmp_param2.protocol == IRMP_FDC_PROTOCOL && irmp_pulse_time >= FDC_PULSE_LEN_MIN && irmp_pulse_time <= FDC_PULSE_LEN_MAX)
+                        {
+                            if (irmp_pause_time >= FDC_1_PAUSE_LEN_MIN && irmp_pause_time <= FDC_1_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("   1 (FDC)\n");
+#endif // ANALYZE
+                                irmp_store_bit2 (1);
+                            }
+                            else if (irmp_pause_time >= FDC_0_PAUSE_LEN_MIN && irmp_pause_time <= FDC_0_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("   0 (FDC)\n");
+#endif // ANALYZE
+                                irmp_store_bit2 (0);
+                            }
+
+                            if (! irmp_param.protocol)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to FDC protocol\n");
+#endif // ANALYZE
+                                memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER));
+                                irmp_param2.protocol = 0;
+                                irmp_tmp_address = irmp_tmp_address2;
+                                irmp_tmp_command = irmp_tmp_command2;
+                            }
+                        }
+#endif // IRMP_SUPPORT_FDC_PROTOCOL == 1
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+                        if (irmp_param2.protocol == IRMP_RCCAR_PROTOCOL && irmp_pulse_time >= RCCAR_PULSE_LEN_MIN && irmp_pulse_time <= RCCAR_PULSE_LEN_MAX)
+                        {
+                            if (irmp_pause_time >= RCCAR_1_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_1_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("   1 (RCCAR)\n");
+#endif // ANALYZE
+                                irmp_store_bit2 (1);
+                            }
+                            else if (irmp_pause_time >= RCCAR_0_PAUSE_LEN_MIN && irmp_pause_time <= RCCAR_0_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("   0 (RCCAR)\n");
+#endif // ANALYZE
+                                irmp_store_bit2 (0);
+                            }
+
+                            if (! irmp_param.protocol)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Switching to RCCAR protocol\n");
+#endif // ANALYZE
+                                memcpy (&irmp_param, &irmp_param2, sizeof (IRMP_PARAMETER));
+                                irmp_param2.protocol = 0;
+                                irmp_tmp_address = irmp_tmp_address2;
+                                irmp_tmp_command = irmp_tmp_command2;
+                            }
+                        }
+#endif // IRMP_SUPPORT_RCCAR_PROTOCOL == 1
+
+                        last_pause      = irmp_pause_time;
+                        wait_for_space  = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_MANCHESTER == 1
+
+#if IRMP_SUPPORT_SERIAL == 1
+                    if (irmp_param.flags & IRMP_PARAM_FLAG_IS_SERIAL)
+                    {
+                        while (irmp_bit < irmp_param.complete_len && irmp_pulse_time > irmp_param.pulse_1_len_max)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+                            irmp_store_bit (1);
+
+                            if (irmp_pulse_time >= irmp_param.pulse_1_len_min)
+                            {
+                                irmp_pulse_time -= irmp_param.pulse_1_len_min;
+                            }
+                            else
+                            {
+                                irmp_pulse_time = 0;
+                            }
+                        }
+
+                        while (irmp_bit < irmp_param.complete_len && irmp_pause_time > irmp_param.pause_1_len_max)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+                            irmp_store_bit (0);
+
+                            if (irmp_pause_time >= irmp_param.pause_1_len_min)
+                            {
+                                irmp_pause_time -= irmp_param.pause_1_len_min;
+                            }
+                            else
+                            {
+                                irmp_pause_time = 0;
+                            }
+                        }
+#ifdef ANALYZE
+                        ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                        wait_for_space = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_SERIAL == 1
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_SAMSUNG_PROTOCOL && irmp_bit == 16)       // Samsung: 16th bit
+                    {
+                        if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX &&
+                            irmp_pause_time >= SAMSUNG_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_START_BIT_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("SYNC\n");
+#endif // ANALYZE
+                            wait_for_space = 0;
+                            irmp_bit++;
+                        }
+                        else  if (irmp_pulse_time >= SAMSUNG_PULSE_LEN_MIN && irmp_pulse_time <= SAMSUNG_PULSE_LEN_MAX)
+                        {
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("Switching to SAMSUNG48 protocol ");
+#endif // ANALYZE
+                            irmp_param.protocol         = IRMP_SAMSUNG48_PROTOCOL;
+                            irmp_param.command_offset   = SAMSUNG48_COMMAND_OFFSET;
+                            irmp_param.command_end      = SAMSUNG48_COMMAND_OFFSET + SAMSUNG48_COMMAND_LEN;
+                            irmp_param.complete_len     = SAMSUNG48_COMPLETE_DATA_LEN;
+#else
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("Switching to SAMSUNG32 protocol ");
+#endif // ANALYZE
+                            irmp_param.protocol         = IRMP_SAMSUNG32_PROTOCOL;
+                            irmp_param.command_offset   = SAMSUNG32_COMMAND_OFFSET;
+                            irmp_param.command_end      = SAMSUNG32_COMMAND_OFFSET + SAMSUNG32_COMMAND_LEN;
+                            irmp_param.complete_len     = SAMSUNG32_COMPLETE_DATA_LEN;
+#endif
+                            if (irmp_pause_time >= SAMSUNG_1_PAUSE_LEN_MIN && irmp_pause_time <= SAMSUNG_1_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('1');
+                                ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                irmp_store_bit (1);
+                                wait_for_space = 0;
+                            }
+                            else
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PUTCHAR ('0');
+                                ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                irmp_store_bit (0);
+                                wait_for_space = 0;
+                            }
+                        }
+                        else
+                        {                                                           // timing incorrect!
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("error 3 Samsung: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                            ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                            irmp_start_bit_detected = 0;                            // reset flags and wait for next start bit
+                            irmp_pause_time         = 0;
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_SAMSUNG_PROTOCOL
+
+#if IRMP_SUPPORT_NEC16_PROTOCOL
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_NEC42_PROTOCOL &&
+#else // IRMP_SUPPORT_NEC_PROTOCOL instead
+                    if (irmp_param.protocol == IRMP_NEC_PROTOCOL &&
+#endif // IRMP_SUPPORT_NEC42_PROTOCOL == 1
+                        irmp_bit == 8 && irmp_pause_time >= NEC_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= NEC_START_BIT_PAUSE_LEN_MAX)
+                    {
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("Switching to NEC16 protocol\n");
+#endif // ANALYZE
+                        irmp_param.protocol         = IRMP_NEC16_PROTOCOL;
+                        irmp_param.address_offset   = NEC16_ADDRESS_OFFSET;
+                        irmp_param.address_end      = NEC16_ADDRESS_OFFSET + NEC16_ADDRESS_LEN;
+                        irmp_param.command_offset   = NEC16_COMMAND_OFFSET;
+                        irmp_param.command_end      = NEC16_COMMAND_OFFSET + NEC16_COMMAND_LEN;
+                        irmp_param.complete_len     = NEC16_COMPLETE_DATA_LEN;
+                        wait_for_space = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_NEC16_PROTOCOL
+
+#if IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_BANG_OLUFSEN_PROTOCOL)
+                    {
+                        if (irmp_pulse_time >= BANG_OLUFSEN_PULSE_LEN_MIN && irmp_pulse_time <= BANG_OLUFSEN_PULSE_LEN_MAX)
+                        {
+                            if (irmp_bit == 1)                                      // Bang & Olufsen: 3rd bit
+                            {
+                                if (irmp_pause_time >= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_START_BIT3_PAUSE_LEN_MAX)
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("3rd start bit\n");
+#endif // ANALYZE
+                                    wait_for_space = 0;
+                                    irmp_bit++;
+                                }
+                                else
+                                {                                                   // timing incorrect!
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("error 3a B&O: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                                    ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                    irmp_start_bit_detected = 0;                    // reset flags and wait for next start bit
+                                    irmp_pause_time         = 0;
+                                }
+                            }
+                            else if (irmp_bit == 19)                                // Bang & Olufsen: trailer bit
+                            {
+                                if (irmp_pause_time >= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_TRAILER_BIT_PAUSE_LEN_MAX)
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("trailer bit\n");
+#endif // ANALYZE
+                                    wait_for_space = 0;
+                                    irmp_bit++;
+                                }
+                                else
+                                {                                                   // timing incorrect!
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("error 3b B&O: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                                    ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                    irmp_start_bit_detected = 0;                    // reset flags and wait for next start bit
+                                    irmp_pause_time         = 0;
+                                }
+                            }
+                            else
+                            {
+                                if (irmp_pause_time >= BANG_OLUFSEN_1_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_1_PAUSE_LEN_MAX)
+                                {                                                   // pulse & pause timings correct for "1"?
+#ifdef ANALYZE
+                                    ANALYZE_PUTCHAR ('1');
+                                    ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                    irmp_store_bit (1);
+                                    last_value = 1;
+                                    wait_for_space = 0;
+                                }
+                                else if (irmp_pause_time >= BANG_OLUFSEN_0_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_0_PAUSE_LEN_MAX)
+                                {                                                   // pulse & pause timings correct for "0"?
+#ifdef ANALYZE
+                                    ANALYZE_PUTCHAR ('0');
+                                    ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                    irmp_store_bit (0);
+                                    last_value = 0;
+                                    wait_for_space = 0;
+                                }
+                                else if (irmp_pause_time >= BANG_OLUFSEN_R_PAUSE_LEN_MIN && irmp_pause_time <= BANG_OLUFSEN_R_PAUSE_LEN_MAX)
+                                {
+#ifdef ANALYZE
+                                    ANALYZE_PUTCHAR (last_value + '0');
+                                    ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                                    irmp_store_bit (last_value);
+                                    wait_for_space = 0;
+                                }
+                                else
+                                {                                                   // timing incorrect!
+#ifdef ANALYZE
+                                    ANALYZE_PRINTF ("error 3c B&O: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                                    ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                                    irmp_start_bit_detected = 0;                    // reset flags and wait for next start bit
+                                    irmp_pause_time         = 0;
+                                }
+                            }
+                        }
+                        else
+                        {                                                           // timing incorrect!
+#ifdef ANALYZE
+                            ANALYZE_PRINTF ("error 3d B&O: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                            ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                            irmp_start_bit_detected = 0;                            // reset flags and wait for next start bit
+                            irmp_pause_time         = 0;
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_RCMM32_PROTOCOL)
+                    {
+                        if (irmp_pause_time >= RCMM32_BIT_00_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_00_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('0');
+                            ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+                            irmp_store_bit (0);
+                            irmp_store_bit (0);
+                        }
+                        else if (irmp_pause_time >= RCMM32_BIT_01_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_01_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('0');
+                            ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+                            irmp_store_bit (0);
+                            irmp_store_bit (1);
+                        }
+                        else if (irmp_pause_time >= RCMM32_BIT_10_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_10_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('1');
+                            ANALYZE_PUTCHAR ('0');
+#endif // ANALYZE
+                            irmp_store_bit (1);
+                            irmp_store_bit (0);
+                        }
+                        else if (irmp_pause_time >= RCMM32_BIT_11_PAUSE_LEN_MIN && irmp_pause_time <= RCMM32_BIT_11_PAUSE_LEN_MAX)
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('1');
+                            ANALYZE_PUTCHAR ('1');
+#endif // ANALYZE
+                            irmp_store_bit (1);
+                            irmp_store_bit (1);
+                        }
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("\n");
+#endif // ANALYZE
+                        wait_for_space = 0;
+                    }
+                    else
+#endif
+
+                    if (irmp_pulse_time >= irmp_param.pulse_1_len_min && irmp_pulse_time <= irmp_param.pulse_1_len_max &&
+                        irmp_pause_time >= irmp_param.pause_1_len_min && irmp_pause_time <= irmp_param.pause_1_len_max)
+                    {                                                               // pulse & pause timings correct for "1"?
+#ifdef ANALYZE
+                        ANALYZE_PUTCHAR ('1');
+                        ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                        irmp_store_bit (1);
+                        wait_for_space = 0;
+                    }
+                    else if (irmp_pulse_time >= irmp_param.pulse_0_len_min && irmp_pulse_time <= irmp_param.pulse_0_len_max &&
+                             irmp_pause_time >= irmp_param.pause_0_len_min && irmp_pause_time <= irmp_param.pause_0_len_max)
+                    {                                                               // pulse & pause timings correct for "0"?
+#ifdef ANALYZE
+                        ANALYZE_PUTCHAR ('0');
+                        ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                        irmp_store_bit (0);
+                        wait_for_space = 0;
+                    }
+                    else
+#if IRMP_SUPPORT_KATHREIN_PROTOCOL
+
+                    if (irmp_param.protocol == IRMP_KATHREIN_PROTOCOL &&
+                        irmp_pulse_time >= KATHREIN_1_PULSE_LEN_MIN && irmp_pulse_time <= KATHREIN_1_PULSE_LEN_MAX &&
+                        (((irmp_bit == 8 || irmp_bit == 6) &&
+                                irmp_pause_time >= KATHREIN_SYNC_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_SYNC_BIT_PAUSE_LEN_MAX) ||
+                         (irmp_bit == 12 &&
+                                irmp_pause_time >= KATHREIN_START_BIT_PAUSE_LEN_MIN && irmp_pause_time <= KATHREIN_START_BIT_PAUSE_LEN_MAX)))
+
+                    {
+                        if (irmp_bit == 8)
+                        {
+                            irmp_bit++;
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('S');
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_tmp_command <<= 1;
+                        }
+                        else
+                        {
+#ifdef ANALYZE
+                            ANALYZE_PUTCHAR ('S');
+                            ANALYZE_NEWLINE ();
+#endif // ANALYZE
+                            irmp_store_bit (1);
+                        }
+                        wait_for_space = 0;
+                    }
+                    else
+#endif // IRMP_SUPPORT_KATHREIN_PROTOCOL
+                    {                                                               // timing incorrect!
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("error 3: timing not correct: data bit %d,  pulse: %d, pause: %d\n", irmp_bit, irmp_pulse_time, irmp_pause_time);
+                        ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                        irmp_start_bit_detected = 0;                                // reset flags and wait for next start bit
+                        irmp_pause_time         = 0;
+                    }
+
+                    irmp_pulse_time = 1;                                            // set counter to 1, not 0
+                }
+            }
+            else
+            {                                                                       // counting the pulse length ...
+                if (! irmp_input)                                                   // still light?
+                {                                                                   // yes...
+                    irmp_pulse_time++;                                              // increment counter
+                }
+                else
+                {                                                                   // now it's dark!
+                    wait_for_space  = 1;                                            // let's count the time (see above)
+                    irmp_pause_time = 1;                                            // set pause counter to 1, not 0
+
+#if IRMP_SUPPORT_RCII_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_RCII_PROTOCOL && waiting_for_2nd_pulse)
+                    {
+printf ("fm: %d %d\n", irmp_pulse_time * 1000000 / F_INTERRUPTS, RCII_BIT_LEN * 1000000 / F_INTERRUPTS); // fm: Ausgabe ist "1000 466" oder "1533 466"
+#if 0
+                        if (irmp_pulse_time >= RCII_BIT_LEN)
+                        {
+                            irmp_pulse_time -= RCII_BIT_LEN;
+                            last_value = 0;
+                        }
+                        else
+                        {
+                            last_value = 1;
+                        }
+#else // fm: das reicht für RCII
+                            irmp_pulse_time -= RCII_BIT_LEN;
+                            last_value = 0;
+#endif
+
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("RCII: got 2nd pulse, irmp_pulse_time = %d\n", irmp_pulse_time);
+#endif
+                        waiting_for_2nd_pulse = 0;
+                    }
+#endif
+                }
+            }
+
+            if (irmp_start_bit_detected && irmp_bit == irmp_param.complete_len && irmp_param.stop_bit == 0)    // enough bits received?
+            {
+                if (last_irmp_command == irmp_tmp_command && key_repetition_len < AUTO_FRAME_REPETITION_LEN)
+                {
+                    repetition_frame_number++;
+                }
+                else
+                {
+                    repetition_frame_number = 0;
+                }
+
+#if IRMP_SUPPORT_SIRCS_PROTOCOL == 1
+                // if SIRCS protocol and the code will be repeated within 50 ms, we will ignore 2nd and 3rd repetition frame
+                if (irmp_param.protocol == IRMP_SIRCS_PROTOCOL && (repetition_frame_number == 1 || repetition_frame_number == 2))
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: SIRCS auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+                // if ORTEK protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame
+                if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL && repetition_frame_number == 1)
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: ORTEK auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+#if 0 && IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1    // fm 2015-12-02: don't ignore every 2nd frame
+                // if KASEIKYO protocol and the code will be repeated within 50 ms, we will ignore 2nd repetition frame
+                if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL && repetition_frame_number == 1)
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: KASEIKYO auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+#if 0 && IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1     // fm 2015-12-02: don't ignore every 2nd frame
+                // if SAMSUNG32 or SAMSUNG48 protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+                if ((irmp_param.protocol == IRMP_SAMSUNG32_PROTOCOL || irmp_param.protocol == IRMP_SAMSUNG48_PROTOCOL) && (repetition_frame_number & 0x01))
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: SAMSUNG32/SAMSUNG48 auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, key_repetition_len, AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1
+                // if NUBERT protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+                if (irmp_param.protocol == IRMP_NUBERT_PROTOCOL && (repetition_frame_number & 0x01))
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: NUBERT auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+#if IRMP_SUPPORT_SPEAKER_PROTOCOL == 1
+                // if SPEAKER protocol and the code will be repeated within 50 ms, we will ignore every 2nd frame
+                if (irmp_param.protocol == IRMP_SPEAKER_PROTOCOL && (repetition_frame_number & 0x01))
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("code skipped: SPEAKER auto repetition frame #%d, counter = %d, auto repetition len = %d\n",
+                                    repetition_frame_number + 1, (int)key_repetition_len, (int)AUTO_FRAME_REPETITION_LEN);
+#endif // ANALYZE
+                    key_repetition_len = 0;
+                }
+                else
+#endif
+
+                {
+#ifdef ANALYZE
+                    ANALYZE_PRINTF ("%8.3fms code detected, length = %d\n", (double) (time_counter * 1000) / F_INTERRUPTS, irmp_bit);
+#endif // ANALYZE
+                    irmp_ir_detected = TRUE;
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_DENON_PROTOCOL)
+                    {                                                               // check for repetition frame
+                        if ((~irmp_tmp_command & 0x3FF) == last_irmp_denon_command) // command bits must be inverted
+                        {
+                            irmp_tmp_command = last_irmp_denon_command;             // use command received before!
+                            last_irmp_denon_command = 0;
+
+                            irmp_protocol = irmp_param.protocol;                    // store protocol
+                            irmp_address = irmp_tmp_address;                        // store address
+                            irmp_command = irmp_tmp_command;                        // store command
+                        }
+                        else
+                        {
+                            if ((irmp_tmp_command & 0x01) == 0x00)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("%8.3fms info Denon: waiting for inverted command repetition\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+                                last_irmp_denon_command = irmp_tmp_command;
+                                denon_repetition_len = 0;
+                                irmp_ir_detected = FALSE;
+                            }
+                            else
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("%8.3fms warning Denon: got unexpected inverted command, ignoring it\n", (double) (time_counter * 1000) / F_INTERRUPTS);
+#endif // ANALYZE
+                                last_irmp_denon_command = 0;
+                                irmp_ir_detected = FALSE;
+                            }
+                        }
+                    }
+                    else
+#endif // IRMP_SUPPORT_DENON_PROTOCOL
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_GRUNDIG_PROTOCOL && irmp_tmp_command == 0x01ff)
+                    {                                                               // Grundig start frame?
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("Detected GRUNDIG start frame, ignoring it\n");
+#endif // ANALYZE
+                        irmp_ir_detected = FALSE;
+                    }
+                    else
+#endif // IRMP_SUPPORT_GRUNDIG_PROTOCOL
+
+#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1
+                    if (irmp_param.protocol == IRMP_NOKIA_PROTOCOL && irmp_tmp_address == 0x00ff && irmp_tmp_command == 0x00fe)
+                    {                                                               // Nokia start frame?
+#ifdef ANALYZE
+                        ANALYZE_PRINTF ("Detected NOKIA start frame, ignoring it\n");
+#endif // ANALYZE
+                        irmp_ir_detected = FALSE;
+                    }
+                    else
+#endif // IRMP_SUPPORT_NOKIA_PROTOCOL
+                    {
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_NEC_PROTOCOL && irmp_bit == 0)  // repetition frame
+                        {
+                            if (key_repetition_len < NEC_FRAME_REPEAT_PAUSE_LEN_MAX)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Detected NEC repetition frame, key_repetition_len = %d\n", (int)key_repetition_len);
+                                ANALYZE_ONLY_NORMAL_PRINTF("REPETETION FRAME                ");
+#endif // ANALYZE
+                                irmp_tmp_address = last_irmp_address;                   // address is last address
+                                irmp_tmp_command = last_irmp_command;                   // command is last command
+                                irmp_flags |= IRMP_FLAG_REPETITION;
+                                key_repetition_len = 0;
+                            }
+                            else
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("Detected NEC repetition frame, ignoring it: timeout occured, key_repetition_len = %d > %d\n",
+                                                (int)key_repetition_len, (int)NEC_FRAME_REPEAT_PAUSE_LEN_MAX);
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+                        }
+#endif // IRMP_SUPPORT_NEC_PROTOCOL
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_KASEIKYO_PROTOCOL)
+                        {
+                            uint_fast8_t xor_value;
+
+                            xor_value = (xor_check[0] & 0x0F) ^ ((xor_check[0] & 0xF0) >> 4) ^ (xor_check[1] & 0x0F) ^ ((xor_check[1] & 0xF0) >> 4);
+
+                            if (xor_value != (xor_check[2] & 0x0F))
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("error 4: wrong XOR check for customer id: 0x%1x 0x%1x\n", xor_value, xor_check[2] & 0x0F);
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+
+                            xor_value = xor_check[2] ^ xor_check[3] ^ xor_check[4];
+
+                            if (xor_value != xor_check[5])
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("error 5: wrong XOR check for data bits: 0x%02x 0x%02x\n", xor_value, xor_check[5]);
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+
+                            irmp_flags |= genre2;       // write the genre2 bits into MSB of the flag byte
+                        }
+#endif // IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_ORTEK_PROTOCOL)
+                        {
+                            if (parity == PARITY_CHECK_FAILED)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("error 6: parity check failed\n");
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+
+                            if ((irmp_tmp_address & 0x03) == 0x02)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("code skipped: ORTEK end of transmission frame (key release)\n");
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+                            irmp_tmp_address >>= 2;
+                        }
+#endif // IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+
+#if IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_MITSU_HEAVY_PROTOCOL)
+                        {
+                            check = irmp_tmp_command >> 8;                    // inverted upper byte == lower byte?
+                            check = ~ check;
+                            if (check == (irmp_tmp_command & 0xFF)) {         //ok:
+                              irmp_tmp_command &= 0xFF;
+                            }
+                            else  mitsu_parity = PARITY_CHECK_FAILED;
+                            if (mitsu_parity == PARITY_CHECK_FAILED)
+                            {
+#ifdef ANALYZE
+                                ANALYZE_PRINTF ("error 7: parity check failed\n");
+#endif // ANALYZE
+                                irmp_ir_detected = FALSE;
+                            }
+                        }
+#endif // IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_RC6_PROTOCOL && irmp_param.complete_len == RC6_COMPLETE_DATA_LEN_LONG)     // RC6 mode = 6?
+                        {
+                            irmp_protocol = IRMP_RC6A_PROTOCOL;
+                        }
+                        else
+#endif // IRMP_SUPPORT_RC6_PROTOCOL == 1
+                        {
+                            irmp_protocol = irmp_param.protocol;
+                        }
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_FDC_PROTOCOL)
+                        {
+                            if (irmp_tmp_command & 0x000F)                          // released key?
+                            {
+                                irmp_tmp_command = (irmp_tmp_command >> 4) | 0x80;  // yes, set bit 7
+                            }
+                            else
+                            {
+                                irmp_tmp_command >>= 4;                             // no, it's a pressed key
+                            }
+                            irmp_tmp_command |= (irmp_tmp_address << 2) & 0x0F00;   // 000000CCCCAAAAAA -> 0000CCCC00000000
+                            irmp_tmp_address &= 0x003F;
+                        }
+#endif
+
+                        irmp_address = irmp_tmp_address;                            // store address
+#if IRMP_SUPPORT_NEC_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_NEC_PROTOCOL)
+                        {
+                            last_irmp_address = irmp_tmp_address;                   // store as last address, too
+                        }
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_RC5_PROTOCOL)
+                        {
+                            irmp_tmp_command |= rc5_cmd_bit6;                       // store bit 6
+                        }
+#endif
+#if IRMP_SUPPORT_S100_PROTOCOL == 1
+                        if (irmp_param.protocol == IRMP_S100_PROTOCOL)
+                        {
+                            irmp_tmp_command |= rc5_cmd_bit6;                       // store bit 6
+                        }
+#endif
+                        irmp_command = irmp_tmp_command;                            // store command
+
+#if IRMP_SUPPORT_SAMSUNG_PROTOCOL == 1
+                        irmp_id = irmp_tmp_id;
+#endif
+                    }
+                }
+
+                if (irmp_ir_detected)
+                {
+                    if (last_irmp_command == irmp_tmp_command &&
+                        last_irmp_address == irmp_tmp_address &&
+                        key_repetition_len < IRMP_KEY_REPETITION_LEN)
+                    {
+                        irmp_flags |= IRMP_FLAG_REPETITION;
+                    }
+
+                    last_irmp_address = irmp_tmp_address;                           // store as last address, too
+                    last_irmp_command = irmp_tmp_command;                           // store as last command, too
+
+                    key_repetition_len = 0;
+                }
+                else
+                {
+#ifdef ANALYZE
+                    ANALYZE_ONLY_NORMAL_PUTCHAR ('\n');
+#endif // ANALYZE
+                }
+
+                irmp_start_bit_detected = 0;                                        // and wait for next start bit
+                irmp_tmp_command        = 0;
+                irmp_pulse_time         = 0;
+                irmp_pause_time         = 0;
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1
+                if (irmp_protocol == IRMP_JVC_PROTOCOL)                             // the stop bit of JVC frame is also start bit of next frame
+                {                                                                   // set pulse time here!
+                    irmp_pulse_time = ((uint_fast8_t)(F_INTERRUPTS * JVC_START_BIT_PULSE_TIME));
+                }
+#endif // IRMP_SUPPORT_JVC_PROTOCOL == 1
+            }
+        }
+    }
+
+#if defined(STELLARIS_ARM_CORTEX_M4)
+    // Clear the timer interrupt
+    TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
+#endif
+
+#if (defined(_CHIBIOS_RT_) || defined(_CHIBIOS_NIL_)) && IRMP_USE_EVENT == 1
+    if (IRMP_EVENT_THREAD_PTR != NULL && irmp_ir_detected)
+        chEvtSignalI(IRMP_EVENT_THREAD_PTR,IRMP_EVENT_BIT);
+#endif
+
+#if IRMP_USE_IDLE_CALL == 1
+    // check if there is no ongoing transmission or repetition
+    if (!irmp_start_bit_detected && !irmp_pulse_time
+        && key_repetition_len > IRMP_KEY_REPETITION_LEN)
+    {
+        // no ongoing transmission
+        // enough time passed since last decoded signal that a repetition won't affect our output
+
+        irmp_idle();
+    }
+#endif // IRMP_USE_IDLE_CALL
+
+    return (irmp_ir_detected);
+}
+
+#ifdef ANALYZE
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * main functions - for Unix/Linux + Windows only!
+ *
+ * AVR: see main.c!
+ *
+ * Compile it under linux with:
+ * cc irmp.c -o irmp
+ *
+ * usage: ./irmp [-v|-s|-a|-l] < file
+ *
+ * options:
+ *   -v verbose
+ *   -s silent
+ *   -a analyze
+ *   -l list pulse/pauses
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+void print_spectrum (char * text, int * buf, int is_pulse);
+void
+print_spectrum (char * text, int * buf, int is_pulse)
+{
+    int     i;
+    int     j;
+    int     min;
+    int     max;
+    int     max_value = 0;
+    int     value;
+    int     sum = 0;
+    int     counter = 0;
+    double  average = 0;
+    double  tolerance;
+
+    puts ("-----------------------------------------------------------------------------");
+    printf ("%s:\n", text);
+
+    for (i = 0; i < 256; i++)
+    {
+        if (buf[i] > max_value)
+        {
+            max_value = buf[i];
+        }
+    }
+
+    for (i = 1; i < 200; i++)
+    {
+        if (buf[i] > 0)
+        {
+            printf ("%3d ", i);
+            value = (buf[i] * 60) / max_value;
+
+            for (j = 0; j < value; j++)
+            {
+                putchar ('o');
+            }
+            printf (" %d\n", buf[i]);
+
+            sum += i * buf[i];
+            counter += buf[i];
+        }
+        else
+        {
+            max = i - 1;
+
+            if (counter > 0)
+            {
+                average = (float) sum / (float) counter;
+
+                if (is_pulse)
+                {
+                    printf ("pulse ");
+                }
+                else
+                {
+                    printf ("pause ");
+                }
+
+                printf ("avg: %4.1f=%6.1f us, ", average, (1000000. * average) / (float) F_INTERRUPTS);
+                printf ("min: %2d=%6.1f us, ", min, (1000000. * min) / (float) F_INTERRUPTS);
+                printf ("max: %2d=%6.1f us, ", max, (1000000. * max) / (float) F_INTERRUPTS);
+
+                tolerance = (max - average);
+
+                if (average - min > tolerance)
+                {
+                    tolerance = average - min;
+                }
+
+                tolerance = tolerance * 100 / average;
+                printf ("tol: %4.1f%%\n", tolerance);
+            }
+
+            counter = 0;
+            sum = 0;
+            min = i + 1;
+        }
+    }
+}
+
+#define STATE_LEFT_SHIFT    0x01
+#define STATE_RIGHT_SHIFT   0x02
+#define STATE_LEFT_CTRL     0x04
+#define STATE_LEFT_ALT      0x08
+#define STATE_RIGHT_ALT     0x10
+
+#define KEY_ESCAPE          0x1B            // keycode = 0x006e
+#define KEY_MENUE           0x80            // keycode = 0x0070
+#define KEY_BACK            0x81            // keycode = 0x0071
+#define KEY_FORWARD         0x82            // keycode = 0x0072
+#define KEY_ADDRESS         0x83            // keycode = 0x0073
+#define KEY_WINDOW          0x84            // keycode = 0x0074
+#define KEY_1ST_PAGE        0x85            // keycode = 0x0075
+#define KEY_STOP            0x86            // keycode = 0x0076
+#define KEY_MAIL            0x87            // keycode = 0x0077
+#define KEY_FAVORITES       0x88            // keycode = 0x0078
+#define KEY_NEW_PAGE        0x89            // keycode = 0x0079
+#define KEY_SETUP           0x8A            // keycode = 0x007a
+#define KEY_FONT            0x8B            // keycode = 0x007b
+#define KEY_PRINT           0x8C            // keycode = 0x007c
+#define KEY_ON_OFF          0x8E            // keycode = 0x007c
+
+#define KEY_INSERT          0x90            // keycode = 0x004b
+#define KEY_DELETE          0x91            // keycode = 0x004c
+#define KEY_LEFT            0x92            // keycode = 0x004f
+#define KEY_HOME            0x93            // keycode = 0x0050
+#define KEY_END             0x94            // keycode = 0x0051
+#define KEY_UP              0x95            // keycode = 0x0053
+#define KEY_DOWN            0x96            // keycode = 0x0054
+#define KEY_PAGE_UP         0x97            // keycode = 0x0055
+#define KEY_PAGE_DOWN       0x98            // keycode = 0x0056
+#define KEY_RIGHT           0x99            // keycode = 0x0059
+#define KEY_MOUSE_1         0x9E            // keycode = 0x0400
+#define KEY_MOUSE_2         0x9F            // keycode = 0x0800
+
+static uint_fast8_t
+get_fdc_key (uint_fast16_t cmd)
+{
+    static uint8_t key_table[128] =
+    {
+     // 0     1    2    3    4    5    6    7    8     9     A     B     C     D    E    F
+         0,   '^', '1', '2', '3', '4', '5', '6', '7',  '8',  '9',  '0',  0xDF, 0xB4, 0,   '\b',
+        '\t', 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i',  'o',  'p',  0xFC, '+',   0,   0,   'a',
+        's',  'd', 'f', 'g', 'h', 'j', 'k', 'l', 0xF6, 0xE4, '#',  '\r', 0,    '<', 'y', 'x',
+        'c',  'v', 'b', 'n', 'm', ',', '.', '-', 0,    0,    0,    0,    0,    ' ', 0,   0,
+
+         0,   0xB0, '!', '"', 0xA7, '$', '%', '&', '/',  '(',  ')',  '=',  '?',  '`', 0,   '\b',
+        '\t', 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I',  'O',  'P',  0xDC, '*',  0,   0,   'A',
+        'S',  'D', 'F', 'G', 'H', 'J', 'K', 'L', 0xD6, 0xC4, '\'', '\r', 0,    '>', 'Y', 'X',
+        'C',  'V', 'B', 'N', 'M', ';', ':', '_', 0,    0,    0,    0,    0,    ' ', 0,   0
+    };
+    static uint_fast8_t state;
+
+    uint_fast8_t key = 0;
+
+    switch (cmd)
+    {
+        case 0x002C: state |=  STATE_LEFT_SHIFT;    break;              // pressed left shift
+        case 0x00AC: state &= ~STATE_LEFT_SHIFT;    break;              // released left shift
+        case 0x0039: state |=  STATE_RIGHT_SHIFT;   break;              // pressed right shift
+        case 0x00B9: state &= ~STATE_RIGHT_SHIFT;   break;              // released right shift
+        case 0x003A: state |=  STATE_LEFT_CTRL;     break;              // pressed left ctrl
+        case 0x00BA: state &= ~STATE_LEFT_CTRL;     break;              // released left ctrl
+        case 0x003C: state |=  STATE_LEFT_ALT;      break;              // pressed left alt
+        case 0x00BC: state &= ~STATE_LEFT_ALT;      break;              // released left alt
+        case 0x003E: state |=  STATE_RIGHT_ALT;     break;              // pressed left alt
+        case 0x00BE: state &= ~STATE_RIGHT_ALT;     break;              // released left alt
+
+        case 0x006e: key = KEY_ESCAPE;              break;
+        case 0x004b: key = KEY_INSERT;              break;
+        case 0x004c: key = KEY_DELETE;              break;
+        case 0x004f: key = KEY_LEFT;                break;
+        case 0x0050: key = KEY_HOME;                break;
+        case 0x0051: key = KEY_END;                 break;
+        case 0x0053: key = KEY_UP;                  break;
+        case 0x0054: key = KEY_DOWN;                break;
+        case 0x0055: key = KEY_PAGE_UP;             break;
+        case 0x0056: key = KEY_PAGE_DOWN;           break;
+        case 0x0059: key = KEY_RIGHT;               break;
+        case 0x0400: key = KEY_MOUSE_1;             break;
+        case 0x0800: key = KEY_MOUSE_2;             break;
+
+        default:
+        {
+            if (!(cmd & 0x80))                      // pressed key
+            {
+                if (cmd >= 0x70 && cmd <= 0x7F)     // function keys
+                {
+                    key = cmd + 0x10;               // 7x -> 8x
+                }
+                else if (cmd < 64)                  // key listed in key_table
+                {
+                    if (state & (STATE_LEFT_ALT | STATE_RIGHT_ALT))
+                    {
+                        switch (cmd)
+                        {
+                            case 0x0003: key = 0xB2;    break; // upper 2
+                            case 0x0008: key = '{';     break;
+                            case 0x0009: key = '[';     break;
+                            case 0x000A: key = ']';     break;
+                            case 0x000B: key = '}';     break;
+                            case 0x000C: key = '\\';    break;
+                            case 0x001C: key = '~';     break;
+                            case 0x002D: key = '|';     break;
+                            case 0x0034: key = 0xB5;    break; // Mu
+                        }
+                    }
+                    else if (state & (STATE_LEFT_CTRL))
+                    {
+                        if (key_table[cmd] >= 'a' && key_table[cmd] <= 'z')
+                        {
+                            key = key_table[cmd] - 'a' + 1;
+                        }
+                        else
+                        {
+                            key = key_table[cmd];
+                        }
+                    }
+                    else
+                    {
+                        int idx = cmd + ((state & (STATE_LEFT_SHIFT | STATE_RIGHT_SHIFT)) ? 64 : 0);
+
+                        if (key_table[idx])
+                        {
+                            key = key_table[idx];
+                        }
+                    }
+                }
+            }
+            break;
+        }
+    }
+
+    return (key);
+}
+
+static int         analyze = FALSE;
+static int         list = FALSE;
+static IRMP_DATA   irmp_data;
+static int         expected_protocol;
+static int         expected_address;
+static int         expected_command;
+static int         do_check_expected_values;
+
+static void
+next_tick (void)
+{
+    if (! analyze && ! list)
+    {
+        (void) irmp_ISR ();
+
+        if (irmp_get_data (&irmp_data))
+        {
+            uint_fast8_t key;
+
+            ANALYZE_ONLY_NORMAL_PUTCHAR (' ');
+
+            if (verbose)
+            {
+                printf ("%8.3fms ", (double) (time_counter * 1000) / F_INTERRUPTS);
+            }
+
+            if (irmp_data.protocol == IRMP_ACP24_PROTOCOL)
+            {
+                uint16_t    temp = (irmp_data.command & 0x000F) + 15;
+
+                printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, temp=%d",
+                        irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, temp);
+            }
+            else if (irmp_data.protocol == IRMP_FDC_PROTOCOL && (key = get_fdc_key (irmp_data.command)) != 0)
+            {
+                if ((key >= 0x20 && key < 0x7F) || key >= 0xA0)
+                {
+                    printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x, key='%c'",
+                            irmp_data.protocol,  irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key, key);
+                }
+                else if (key == '\r' || key == '\t' || key == KEY_ESCAPE || (key >= 0x80 && key <= 0x9F))                 // function keys
+                {
+                    char * p = (char *) NULL;
+
+                    switch (key)
+                    {
+                        case '\t'                : p = "TAB";           break;
+                        case '\r'                : p = "CR";            break;
+                        case KEY_ESCAPE          : p = "ESCAPE";        break;
+                        case KEY_MENUE           : p = "MENUE";         break;
+                        case KEY_BACK            : p = "BACK";          break;
+                        case KEY_FORWARD         : p = "FORWARD";       break;
+                        case KEY_ADDRESS         : p = "ADDRESS";       break;
+                        case KEY_WINDOW          : p = "WINDOW";        break;
+                        case KEY_1ST_PAGE        : p = "1ST_PAGE";      break;
+                        case KEY_STOP            : p = "STOP";          break;
+                        case KEY_MAIL            : p = "MAIL";          break;
+                        case KEY_FAVORITES       : p = "FAVORITES";     break;
+                        case KEY_NEW_PAGE        : p = "NEW_PAGE";      break;
+                        case KEY_SETUP           : p = "SETUP";         break;
+                        case KEY_FONT            : p = "FONT";          break;
+                        case KEY_PRINT           : p = "PRINT";         break;
+                        case KEY_ON_OFF          : p = "ON_OFF";        break;
+
+                        case KEY_INSERT          : p = "INSERT";        break;
+                        case KEY_DELETE          : p = "DELETE";        break;
+                        case KEY_LEFT            : p = "LEFT";          break;
+                        case KEY_HOME            : p = "HOME";          break;
+                        case KEY_END             : p = "END";           break;
+                        case KEY_UP              : p = "UP";            break;
+                        case KEY_DOWN            : p = "DOWN";          break;
+                        case KEY_PAGE_UP         : p = "PAGE_UP";       break;
+                        case KEY_PAGE_DOWN       : p = "PAGE_DOWN";     break;
+                        case KEY_RIGHT           : p = "RIGHT";         break;
+                        case KEY_MOUSE_1         : p = "KEY_MOUSE_1";   break;
+                        case KEY_MOUSE_2         : p = "KEY_MOUSE_2";   break;
+                        default                  : p = "<UNKNWON>";     break;
+                    }
+
+                    printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x, key=%s",
+                            irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key, p);
+                }
+                else
+                {
+                    printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x, asc=0x%02x",
+                            irmp_data.protocol,  irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags, key);
+                }
+            }
+            else
+            {
+                printf ("p=%2d (%s), a=0x%04x, c=0x%04x, f=0x%02x",
+                        irmp_data.protocol, irmp_protocol_names[irmp_data.protocol], irmp_data.address, irmp_data.command, irmp_data.flags);
+            }
+
+            if (do_check_expected_values)
+            {
+                if (irmp_data.protocol != expected_protocol ||
+                    irmp_data.address  != expected_address  ||
+                    (int)irmp_data.command  != expected_command)
+                {
+                    printf ("\nerror 7: expected values differ: p=%2d (%s), a=0x%04x, c=0x%04x\n",
+                            expected_protocol, irmp_protocol_names[expected_protocol], expected_address, expected_command);
+                }
+                else
+                {
+                    printf (" checked!\n");
+                }
+                do_check_expected_values = FALSE;                           // only check 1st frame in a line!
+            }
+            else
+            {
+                putchar ('\n');
+            }
+        }
+    }
+}
+
+int
+main (int argc, char ** argv)
+{
+    int         i;
+    int         ch;
+    int         last_ch = 0;
+    int         pulse = 0;
+    int         pause = 0;
+
+    int         start_pulses[256];
+    int         start_pauses[256];
+    int         pulses[256];
+    int         pauses[256];
+
+    int         first_pulse = TRUE;
+    int         first_pause = TRUE;
+
+    if (argc == 2)
+    {
+        if (! strcmp (argv[1], "-v"))
+        {
+            verbose = TRUE;
+        }
+        else if (! strcmp (argv[1], "-l"))
+        {
+            list = TRUE;
+        }
+        else if (! strcmp (argv[1], "-a"))
+        {
+            analyze = TRUE;
+        }
+        else if (! strcmp (argv[1], "-s"))
+        {
+            silent = TRUE;
+        }
+        else if (! strcmp (argv[1], "-r"))
+        {
+            radio = TRUE;
+        }
+    }
+
+    for (i = 0; i < 256; i++)
+    {
+        start_pulses[i] = 0;
+        start_pauses[i] = 0;
+        pulses[i] = 0;
+        pauses[i] = 0;
+    }
+
+    IRMP_PIN = 0xFF;
+
+    while ((ch = getchar ()) != EOF)
+    {
+        if (ch == '_' || ch == '0')
+        {
+            if (last_ch != ch)
+            {
+                if (pause > 0)
+                {
+                    if (list)
+                    {
+                        printf ("pause: %d\n", pause);
+                    }
+
+                    if (analyze)
+                    {
+                        if (first_pause)
+                        {
+                            if (pause < 256)
+                            {
+                                start_pauses[pause]++;
+                            }
+                            first_pause = FALSE;
+                        }
+                        else
+                        {
+                            if (pause < 256)
+                            {
+                                pauses[pause]++;
+                            }
+                        }
+                    }
+                }
+                pause = 0;
+            }
+            pulse++;
+            IRMP_PIN = 0x00;
+        }
+        else if (ch == 0xaf || ch == '-' || ch == '1')
+        {
+            if (last_ch != ch)
+            {
+                if (list)
+                {
+                    printf ("pulse: %d ", pulse);
+                }
+
+                if (analyze)
+                {
+                    if (first_pulse)
+                    {
+                        if (pulse < 256)
+                        {
+                            start_pulses[pulse]++;
+                        }
+                        first_pulse = FALSE;
+                    }
+                    else
+                    {
+                        if (pulse < 256)
+                        {
+                            pulses[pulse]++;
+                        }
+                    }
+                }
+                pulse = 0;
+            }
+
+            pause++;
+            IRMP_PIN = 0xff;
+        }
+        else if (ch == '\n')
+        {
+            IRMP_PIN = 0xff;
+            time_counter = 0;
+
+            if (list && pause > 0)
+            {
+                printf ("pause: %d\n", pause);
+            }
+            pause = 0;
+
+            if (! analyze)
+            {
+                for (i = 0; i < (int) ((10000.0 * F_INTERRUPTS) / 10000); i++)               // newline: long pause of 10000 msec
+                {
+                    next_tick ();
+                }
+            }
+            first_pulse = TRUE;
+            first_pause = TRUE;
+        }
+        else if (ch == '#')
+        {
+            time_counter = 0;
+
+            if (analyze)
+            {
+                while ((ch = getchar()) != '\n' && ch != EOF)
+                {
+                    ;
+                }
+            }
+            else
+            {
+                char            buf[1024];
+                char *          p;
+                int             idx = -1;
+
+                puts ("----------------------------------------------------------------------");
+                putchar (ch);
+
+
+                while ((ch = getchar()) != '\n' && ch != EOF)
+                {
+                    if (ch != '\r')                                                         // ignore CR in DOS/Windows files
+                    {
+                        if (ch == '[' && idx == -1)
+                        {
+                            idx = 0;
+                        }
+                        else if (idx >= 0)
+                        {
+                            if (ch == ']')
+                            {
+                                do_check_expected_values = FALSE;
+                                buf[idx] = '\0';
+                                idx = -1;
+
+                                expected_protocol = atoi (buf);
+
+                                if (expected_protocol > 0)
+                                {
+                                    p = buf;
+                                    while (*p)
+                                    {
+                                        if (*p == 'x')
+                                        {
+                                            p++;
+
+                                            if (sscanf (p, "%x", &expected_address) == 1)
+                                            {
+                                                do_check_expected_values = TRUE;
+                                            }
+                                            break;
+                                        }
+                                        p++;
+                                    }
+
+                                    if (do_check_expected_values)
+                                    {
+                                        do_check_expected_values = FALSE;
+
+                                        while (*p)
+                                        {
+                                            if (*p == 'x')
+                                            {
+                                                p++;
+
+                                                if (sscanf (p, "%x", &expected_command) == 1)
+                                                {
+                                                    do_check_expected_values = TRUE;
+                                                }
+                                                break;
+                                            }
+                                            p++;
+                                        }
+
+                                        if (do_check_expected_values)
+                                        {
+                                            // printf ("!%2d %04x %04x!\n", expected_protocol, expected_address, expected_command);
+                                        }
+                                    }
+                                }
+                            }
+                            else if (idx < 1024 - 2)
+                            {
+                                buf[idx++] = ch;
+                            }
+                        }
+                        putchar (ch);
+                    }
+                }
+                putchar ('\n');
+            }
+
+        }
+
+        last_ch = ch;
+
+        next_tick ();
+    }
+
+    if (analyze)
+    {
+        print_spectrum ("START PULSES", start_pulses, TRUE);
+        print_spectrum ("START PAUSES", start_pauses, FALSE);
+        print_spectrum ("PULSES", pulses, TRUE);
+        print_spectrum ("PAUSES", pauses, FALSE);
+        puts ("-----------------------------------------------------------------------------");
+    }
+    return 0;
+}
+
+#endif // ANALYZE
diff --git a/irmp/irmp.h b/irmp/irmp.h
new file mode 100644 (file)
index 0000000..2ed0b7d
--- /dev/null
@@ -0,0 +1,311 @@
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmp.h
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_H_
+#define _IRMP_H_
+
+#ifndef IRMP_USE_AS_LIB
+#  define IRMPCONFIG_STAGE1_H
+#  include "irmpconfig.h"
+#  undef IRMPCONFIG_STAGE1_H
+#endif
+
+#include "irmpsystem.h"
+
+#ifndef IRMP_USE_AS_LIB
+#  define IRMPCONFIG_STAGE2_H
+#  include "irmpconfig.h"
+#  undef IRMPCONFIG_STAGE2_H
+#endif
+
+#if defined (__AVR_XMEGA__)
+#  define _CONCAT(a,b)                          a##b
+#  define CONCAT(a,b)                           _CONCAT(a,b)
+#  define IRMP_PORT_PRE                         CONCAT(PORT, IRMP_PORT_LETTER)
+#  define IRMP_DDR_PRE                          CONCAT(PORT, IRMP_PORT_LETTER)
+#  define IRMP_PIN_PRE                          CONCAT(PORT, IRMP_PORT_LETTER)
+#  define IRMP_PORT                             IRMP_PORT_PRE.OUT
+#  define IRMP_DDR                              IRMP_DDR_PRE.DIR
+#  define IRMP_PIN                              IRMP_PIN_PRE.IN
+#  define IRMP_BIT                              IRMP_BIT_NUMBER
+#  define input(x)                              ((x) & (1 << IRMP_BIT))
+
+#elif defined (ATMEL_AVR)
+#  define _CONCAT(a,b)                          a##b
+#  define CONCAT(a,b)                           _CONCAT(a,b)
+#  define IRMP_PORT                             CONCAT(PORT, IRMP_PORT_LETTER)
+#  define IRMP_DDR                              CONCAT(DDR, IRMP_PORT_LETTER)
+#  define IRMP_PIN                              CONCAT(PIN, IRMP_PORT_LETTER)
+#  define IRMP_BIT                              IRMP_BIT_NUMBER
+#  define input(x)                              ((x) & (1 << IRMP_BIT))
+
+#elif defined (PIC_C18) || defined (PIC_CCS)
+#  define input(x)                              (x)
+
+#elif defined (ARM_STM32)
+#  define _CONCAT(a,b)                          a##b
+#  define CONCAT(a,b)                           _CONCAT(a,b)
+#  define IRMP_PORT                             CONCAT(GPIO, IRMP_PORT_LETTER)
+#  if defined (ARM_STM32L1XX)
+#    define IRMP_PORT_RCC                       CONCAT(RCC_AHBPeriph_GPIO, IRMP_PORT_LETTER)
+#  elif defined (ARM_STM32F10X)
+#    define IRMP_PORT_RCC                       CONCAT(RCC_APB2Periph_GPIO, IRMP_PORT_LETTER)
+#  elif defined (ARM_STM32F4XX)
+#    define IRMP_PORT_RCC                       CONCAT(RCC_AHB1Periph_GPIO, IRMP_PORT_LETTER)
+#  endif
+#  define IRMP_BIT                              CONCAT(GPIO_Pin_, IRMP_BIT_NUMBER)
+#  define IRMP_PIN                              IRMP_PORT   // for use with input(x) below
+#  define input(x)                              (GPIO_ReadInputDataBit(x, IRMP_BIT))
+#  ifndef USE_STDPERIPH_DRIVER
+#    warning The STM32 port of IRMP uses the ST standard peripheral drivers which are not enabled in your build configuration.
+#  endif
+
+#elif defined (ARM_STM32_HAL)
+#  define IRMP_BIT                              IRMP_BIT_NUMBER
+#  define IRMP_PIN                              IRMP_BIT_NUMBER   // for use with input(x) below
+#  define input(x)                              HAL_GPIO_ReadPin(IRMP_PORT_LETTER, x)
+
+#elif defined (STELLARIS_ARM_CORTEX_M4)
+#  define _CONCAT(a,b)                          a##b
+#  define CONCAT(a,b)                           _CONCAT(a,b)
+#  define IRMP_PORT_PERIPH                      CONCAT(SYSCTL_PERIPH_GPIO, IRMP_PORT_LETTER)
+#  define IRMP_PORT_BASE                        CONCAT(GPIO_PORT, CONCAT(IRMP_PORT_LETTER, _BASE))
+#  define IRMP_PORT_PIN                         CONCAT(GPIO_PIN_, IRMP_BIT_NUMBER)
+#  define IRMP_PIN                              IRMP_PORT_PIN
+#  define input(x)                              ((uint8_t)(ROM_GPIOPinRead(IRMP_PORT_BASE, IRMP_PORT_PIN)))
+#  define sei()                                 IntMasterEnable()
+
+#elif defined(__SDCC_stm8)
+#  define _CONCAT(a,b)                          a##b
+#  define CONCAT(a,b)                           _CONCAT(a,b)
+#  define IRMP_GPIO_STRUCT                      CONCAT(GPIO, IRMP_PORT_LETTER)
+#  define IRMP_BIT                              IRMP_BIT_NUMBER
+#  define input(x)                              ((x) & (1 << IRMP_BIT))
+
+#elif defined (TEENSY_ARM_CORTEX_M4)
+#  define input(x)                              ((uint8_t)(digitalReadFast(x)))
+
+#elif defined(__xtensa__)
+#  define IRMP_BIT                              IRMP_BIT_NUMBER
+#  define input(x)                              GPIO_INPUT_GET(IRMP_BIT_NUMBER)
+
+#elif defined(_CHIBIOS_HAL_)
+#  define input(x)                              palReadLine(x)
+
+#endif
+
+#if IRMP_USE_IDLE_CALL == 1
+void irmp_idle(void);                   // the user has to provide an implementation of the irmp_idle() function and link it
+#endif
+
+#if IRMP_SUPPORT_TECHNICS_PROTOCOL == 1
+#  undef IRMP_SUPPORT_MATSUSHITA_PROTOCOL
+#  define IRMP_SUPPORT_MATSUSHITA_PROTOCOL      1
+#endif
+
+#if IRMP_32_BIT == 0 && IRMP_SUPPORT_MERLIN_PROTOCOL == 1
+#  undef IRMP_SUPPORT_MERLIN_PROTOCOL
+#  warning MERLIN protocol disabled, IRMP_32_BIT=1 needed
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1 && IRMP_SUPPORT_RUWIDO_PROTOCOL == 1
+#  warning DENON protocol conflicts wih RUWIDO, please enable only one of both protocols
+#  warning RUWIDO protocol disabled
+#  undef IRMP_SUPPORT_RUWIDO_PROTOCOL
+#  define IRMP_SUPPORT_RUWIDO_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_KASEIKYO_PROTOCOL == 1 && IRMP_SUPPORT_PANASONIC_PROTOCOL == 1
+#  warning KASEIKYO protocol conflicts wih PANASONIC, please enable only one of both protocols
+#  warning PANASONIC protocol disabled
+#  undef IRMP_SUPPORT_PANASONIC_PROTOCOL
+#  define IRMP_SUPPORT_PANASONIC_PROTOCOL       0
+#endif
+
+#if IRMP_SUPPORT_DENON_PROTOCOL == 1 && IRMP_SUPPORT_ACP24_PROTOCOL == 1
+#  warning DENON protocol conflicts wih ACP24, please enable only one of both protocols
+#  warning ACP24 protocol disabled
+#  undef IRMP_SUPPORT_ACP24_PROTOCOL
+#  define IRMP_SUPPORT_ACP24_PROTOCOL           0
+#endif
+
+#if IRMP_SUPPORT_RC6_PROTOCOL == 1 && IRMP_SUPPORT_ROOMBA_PROTOCOL == 1
+#  warning RC6 protocol conflicts wih ROOMBA, please enable only one of both protocols
+#  warning ROOMBA protocol disabled
+#  undef IRMP_SUPPORT_ROOMBA_PROTOCOL
+#  define IRMP_SUPPORT_ROOMBA_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_PANASONIC_PROTOCOL == 1 && IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL == 1
+#  warning PANASONIC protocol conflicts wih MITSU_HEAVY, please enable only one of both protocols
+#  warning MITSU_HEAVY protocol disabled
+#  undef IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL
+#  define IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL      0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+#  warning RC5 protocol conflicts wih ORTEK, please enable only one of both protocols
+#  warning ORTEK protocol disabled
+#  undef IRMP_SUPPORT_ORTEK_PROTOCOL
+#  define IRMP_SUPPORT_ORTEK_PROTOCOL           0
+#endif
+
+#if IRMP_SUPPORT_RC5_PROTOCOL == 1 && IRMP_SUPPORT_S100_PROTOCOL == 1
+#  warning RC5 protocol conflicts wih S100, please enable only one of both protocols
+#  warning S100 protocol disabled
+#  undef IRMP_SUPPORT_S100_PROTOCOL
+#  define IRMP_SUPPORT_S100_PROTOCOL            0
+#endif
+
+#if IRMP_SUPPORT_NUBERT_PROTOCOL == 1 && IRMP_SUPPORT_FAN_PROTOCOL == 1
+#  warning NUBERT protocol conflicts wih FAN, please enable only one of both protocols
+#  warning FAN protocol disabled
+#  undef IRMP_SUPPORT_FAN_PROTOCOL
+#  define IRMP_SUPPORT_FAN_PROTOCOL             0
+#endif
+
+#if IRMP_SUPPORT_FDC_PROTOCOL == 1 && IRMP_SUPPORT_ORTEK_PROTOCOL == 1
+#  warning FDC protocol conflicts wih ORTEK, please enable only one of both protocols
+#  warning ORTEK protocol disabled
+#  undef IRMP_SUPPORT_ORTEK_PROTOCOL
+#  define IRMP_SUPPORT_ORTEK_PROTOCOL           0
+#endif
+
+#if IRMP_SUPPORT_ORTEK_PROTOCOL == 1 && IRMP_SUPPORT_NETBOX_PROTOCOL == 1
+#  warning ORTEK protocol conflicts wih NETBOX, please enable only one of both protocols
+#  warning NETBOX protocol disabled
+#  undef IRMP_SUPPORT_NETBOX_PROTOCOL
+#  define IRMP_SUPPORT_NETBOX_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_GRUNDIG_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1
+#  warning GRUNDIG protocol conflicts wih RCII, please enable only one of both protocols
+#  warning RCII protocol disabled
+#  undef IRMP_SUPPORT_RCII_PROTOCOL
+#  define IRMP_SUPPORT_RCII_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_NOKIA_PROTOCOL == 1 && IRMP_SUPPORT_RCII_PROTOCOL == 1
+#  warning NOKIA protocol conflicts wih RCII, please enable only one of both protocols
+#  warning RCII protocol disabled
+#  undef IRMP_SUPPORT_RCII_PROTOCOL
+#  define IRMP_SUPPORT_RCII_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_SIEMENS_PROTOCOL == 1 && F_INTERRUPTS < 15000
+#  warning F_INTERRUPTS too low, SIEMENS protocol disabled (should be at least 15000)
+#  undef IRMP_SUPPORT_SIEMENS_PROTOCOL
+#  define IRMP_SUPPORT_SIEMENS_PROTOCOL         0
+#endif
+
+#if IRMP_SUPPORT_RUWIDO_PROTOCOL == 1 && F_INTERRUPTS < 15000
+#  warning F_INTERRUPTS too low, RUWIDO protocol disabled (should be at least 15000)
+#  undef IRMP_SUPPORT_RUWIDO_PROTOCOL
+#  define IRMP_SUPPORT_RUWIDO_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_RECS80_PROTOCOL == 1 && F_INTERRUPTS < 15000
+#  warning F_INTERRUPTS too low, RECS80 protocol disabled (should be at least 15000)
+#  undef IRMP_SUPPORT_RECS80_PROTOCOL
+#  define IRMP_SUPPORT_RECS80_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_RECS80EXT_PROTOCOL == 1 && F_INTERRUPTS < 15000
+#  warning F_INTERRUPTS too low, RECS80EXT protocol disabled (should be at least 15000)
+#  undef IRMP_SUPPORT_RECS80EXT_PROTOCOL
+#  define IRMP_SUPPORT_RECS80EXT_PROTOCOL       0
+#endif
+
+#if IRMP_SUPPORT_LEGO_PROTOCOL == 1 && F_INTERRUPTS < 20000
+#  warning F_INTERRUPTS too low, LEGO protocol disabled (should be at least 20000)
+#  undef IRMP_SUPPORT_LEGO_PROTOCOL
+#  define IRMP_SUPPORT_LEGO_PROTOCOL            0
+#endif
+
+#if IRMP_SUPPORT_SAMSUNG48_PROTOCOL == 1 && IRMP_SUPPORT_SAMSUNG_PROTOCOL == 0
+#  warning SAMSUNG48 protocol needs also SAMSUNG protocol, SAMSUNG protocol enabled
+#  undef IRMP_SUPPORT_SAMSUNG_PROTOCOL
+#  define IRMP_SUPPORT_SAMSUNG_PROTOCOL         1
+#endif
+
+#if IRMP_SUPPORT_JVC_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+#  warning JVC protocol needs also NEC protocol, NEC protocol enabled
+#  undef IRMP_SUPPORT_NEC_PROTOCOL
+#  define IRMP_SUPPORT_NEC_PROTOCOL             1
+#endif
+
+#if IRMP_SUPPORT_NEC16_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+#  warning NEC16 protocol needs also NEC protocol, NEC protocol enabled
+#  undef IRMP_SUPPORT_NEC_PROTOCOL
+#  define IRMP_SUPPORT_NEC_PROTOCOL             1
+#endif
+
+#if IRMP_SUPPORT_NEC42_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+#  warning NEC42 protocol needs also NEC protocol, NEC protocol enabled
+#  undef IRMP_SUPPORT_NEC_PROTOCOL
+#  define IRMP_SUPPORT_NEC_PROTOCOL             1
+#endif
+
+#if IRMP_SUPPORT_LGAIR_PROTOCOL == 1 && IRMP_SUPPORT_NEC_PROTOCOL == 0
+#  warning LGAIR protocol needs also NEC protocol, NEC protocol enabled
+#  undef IRMP_SUPPORT_NEC_PROTOCOL
+#  define IRMP_SUPPORT_NEC_PROTOCOL             1
+#endif
+
+#if IRMP_SUPPORT_RCMM_PROTOCOL == 1 && F_INTERRUPTS < 20000
+#  warning F_INTERRUPTS too low, RCMM protocol disabled (should be at least 20000)
+#  undef IRMP_SUPPORT_RCMM_PROTOCOL
+#  define IRMP_SUPPORT_RCMM_PROTOCOL            0
+#endif
+
+#if IRMP_SUPPORT_PENTAX_PROTOCOL == 1 && F_INTERRUPTS > 16000
+#  warning F_INTERRUPTS too high, PENTAX protocol disabled (should be max 16000)
+#  undef IRMP_SUPPORT_PENTAX_PROTOCOL
+#  define IRMP_SUPPORT_PENTAX_PROTOCOL          0
+#endif
+
+#if IRMP_SUPPORT_GREE_PROTOCOL == 1 && F_INTERRUPTS > 16000
+#  warning F_INTERRUPTS too high, GREE protocol disabled (should be max 16000)
+#  undef IRMP_SUPPORT_GREE_PROTOCOL
+#  define IRMP_SUPPORT_GREE_PROTOCOL            0
+#endif
+
+#if F_INTERRUPTS > 20000
+#error F_INTERRUPTS too high (should be not greater than 20000)
+#endif
+
+#include "irmpprotocols.h"
+
+#define IRMP_FLAG_REPETITION            0x01
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern void                             irmp_init (void);
+extern uint_fast8_t                     irmp_get_data (IRMP_DATA *);
+extern uint_fast8_t                     irmp_ISR (void);
+
+#if IRMP_PROTOCOL_NAMES == 1
+extern const char * const               irmp_protocol_names[IRMP_N_PROTOCOLS + 1] PROGMEM;
+#endif
+
+#if IRMP_USE_CALLBACK == 1
+extern void                             irmp_set_callback_ptr (void (*cb)(uint_fast8_t));
+#endif // IRMP_USE_CALLBACK == 1
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IRMP_H_ */
diff --git a/irmp/irmpconfig.h b/irmp/irmpconfig.h
new file mode 100644 (file)
index 0000000..6db651c
--- /dev/null
@@ -0,0 +1,287 @@
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpconfig.h
+ *
+ * DO NOT INCLUDE THIS FILE, WILL BE INCLUDED BY IRMP.H!
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ * Extensions for PIC 12F1820 W.Strobl 2014-07-20
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_H_
+#  error please include only irmp.h, not irmpconfig.h
+#endif
+
+#ifdef IRMPCONFIG_STAGE1_H
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change IRMP_32_BIT here
+ *
+ * Normally, IRMP_32_BIT ist 0. Then we use irmp.command as a 16 bit value.
+ * If you set IRMP_32_BIT to 1, we will use irmp.command as a 32 bit value.
+ * A 32 bit command costs more CPU resources on 8 bit processors (e.g. AVR),
+ * but there are IR protocols which need more than 16 bits for a reasonable
+ * command value.
+ *
+ * If you want to use one of the following protocols, set IRMP_32_BIT to 1,
+ * otherwise set it to 0:
+ *    - MERLIN
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#define IRMP_32_BIT     1                                                                               // use 32 bit command value, 0 or 1
+
+#endif // IRMPCONFIG_STAGE1_H
+#ifdef IRMPCONFIG_STAGE2_H
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change F_INTERRUPTS if you change the number of interrupts per second,
+ * Normally, F_INTERRUPTS should be in the range from 10000 to 15000, typical is 15000
+ * A value above 15000 costs additional program space, absolute maximum value is 20000.
+ * On PIC with XC8/C18 Compiler, use 15151 as the correct value.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef F_INTERRUPTS
+#  define F_INTERRUPTS                          20000   // interrupts per second, min: 10000, max: 20000, typ: 15000
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change settings from 1 to 0 if you want to disable one or more decoders.
+ * This saves program space.
+ *
+ * 1 enable  decoder
+ * 0 disable decoder
+ *
+ * The standard decoders are enabled per default.
+ * Less common protocols are disabled here, you need to enable them manually.
+ *
+ * If you want to use FDC or RCCAR simultaneous with RC5 protocol, additional program space is required.
+ * If you don't need RC5 when using FDC/RCCAR, you should disable RC5.
+ * You cannot enable both DENON and RUWIDO, only enable one of them.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+// typical protocols, disable here!             Enable  Remarks                 F_INTERRUPTS            Program Space
+#define IRMP_SUPPORT_SIRCS_PROTOCOL             1       // Sony SIRCS           >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_NEC_PROTOCOL               1       // NEC + APPLE + ONKYO  >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_SAMSUNG_PROTOCOL           1       // Samsung + Samsg32    >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_KASEIKYO_PROTOCOL          1       // Kaseikyo             >= 10000                 ~250 bytes
+
+// more protocols, enable here!                 Enable  Remarks                 F_INTERRUPTS            Program Space
+#define IRMP_SUPPORT_JVC_PROTOCOL               1       // JVC                  >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_NEC16_PROTOCOL             1       // NEC16                >= 10000                 ~100 bytes
+#define IRMP_SUPPORT_NEC42_PROTOCOL             1       // NEC42                >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_MATSUSHITA_PROTOCOL        1       // Matsushita           >= 10000                  ~50 bytes
+#define IRMP_SUPPORT_DENON_PROTOCOL             1       // DENON, Sharp         >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_RC5_PROTOCOL               1       // RC5                  >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_RC6_PROTOCOL               1       // RC6 & RC6A           >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_IR60_PROTOCOL              1       // IR60 (SDA2008)       >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_GRUNDIG_PROTOCOL           1       // Grundig              >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_SIEMENS_PROTOCOL           1       // Siemens Gigaset      >= 15000                 ~550 bytes
+#define IRMP_SUPPORT_NOKIA_PROTOCOL             1       // Nokia                >= 10000                 ~300 bytes
+
+// exotic protocols, enable here!               Enable  Remarks                 F_INTERRUPTS            Program Space
+#define IRMP_SUPPORT_BOSE_PROTOCOL              1       // BOSE                 >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_KATHREIN_PROTOCOL          1       // Kathrein             >= 10000                 ~200 bytes
+#define IRMP_SUPPORT_NUBERT_PROTOCOL            1       // NUBERT               >= 10000                  ~50 bytes
+#define IRMP_SUPPORT_FAN_PROTOCOL               0       // FAN (ventilator)     >= 10000                  ~50 bytes
+#define IRMP_SUPPORT_SPEAKER_PROTOCOL           1       // SPEAKER (~NUBERT)    >= 10000                  ~50 bytes
+#define IRMP_SUPPORT_BANG_OLUFSEN_PROTOCOL      1       // Bang & Olufsen       >= 10000                 ~200 bytes
+#define IRMP_SUPPORT_RECS80_PROTOCOL            1       // RECS80 (SAA3004)     >= 15000                  ~50 bytes
+#define IRMP_SUPPORT_RECS80EXT_PROTOCOL         1       // RECS80EXT (SAA3008)  >= 15000                  ~50 bytes
+#define IRMP_SUPPORT_THOMSON_PROTOCOL           1       // Thomson              >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_NIKON_PROTOCOL             1       // NIKON camera         >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_NETBOX_PROTOCOL            1       // Netbox keyboard      >= 10000                 ~400 bytes (PROTOTYPE!)
+#define IRMP_SUPPORT_ORTEK_PROTOCOL             0       // ORTEK (Hama)         >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_TELEFUNKEN_PROTOCOL        1       // Telefunken 1560      >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_FDC_PROTOCOL               1       // FDC3402 keyboard     >= 10000 (better 15000)  ~150 bytes (~400 in combination with RC5)
+#define IRMP_SUPPORT_RCCAR_PROTOCOL             1       // RC Car               >= 10000 (better 15000)  ~150 bytes (~500 in combination with RC5)
+#define IRMP_SUPPORT_ROOMBA_PROTOCOL            0       // iRobot Roomba        >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_RUWIDO_PROTOCOL            0       // RUWIDO, T-Home       >= 15000                 ~550 bytes
+#define IRMP_SUPPORT_A1TVBOX_PROTOCOL           1       // A1 TV BOX            >= 15000 (better 20000)  ~300 bytes
+#define IRMP_SUPPORT_LEGO_PROTOCOL              1       // LEGO Power RC        >= 20000                 ~150 bytes
+#define IRMP_SUPPORT_RCMM_PROTOCOL              1       // RCMM 12,24, or 32    >= 20000                 ~150 bytes
+#define IRMP_SUPPORT_LGAIR_PROTOCOL             1       // LG Air Condition     >= 10000                 ~300 bytes
+#define IRMP_SUPPORT_SAMSUNG48_PROTOCOL         1       // Samsung48            >= 10000                 ~100 bytes (SAMSUNG must be enabled!)
+#define IRMP_SUPPORT_MERLIN_PROTOCOL            0       // Merlin               >= 15000 (better 20000)  ~300 bytes
+#define IRMP_SUPPORT_PENTAX_PROTOCOL            0       // Pentax               >= 10000                 ~150 bytes
+#define IRMP_SUPPORT_S100_PROTOCOL              0       // S100                 >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_ACP24_PROTOCOL             0       // ACP24                >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_TECHNICS_PROTOCOL          1       // TECHNICS             >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_PANASONIC_PROTOCOL         0       // PANASONIC Beamer     >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_MITSU_HEAVY_PROTOCOL       1       // Mitsubishi Aircond   >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_VINCENT_PROTOCOL           1       // VINCENT              >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_SAMSUNGAH_PROTOCOL         1       // SAMSUNG AH           >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_IRMP16_PROTOCOL            1       // IRMP specific        >= 15000                 ~250 bytes
+#define IRMP_SUPPORT_GREE_PROTOCOL              0       // GREE CLIMATE         >= 10000                 ~250 bytes
+#define IRMP_SUPPORT_RCII_PROTOCOL              0       // RCII T+A             >= 15000                 ~250 bytes
+#define IRMP_SUPPORT_METZ_PROTOCOL              1       // METZ                 >= 15000                 ~250 bytes
+
+#define IRMP_SUPPORT_RADIO1_PROTOCOL            0       // RADIO, e.g. TEVION   >= 10000                 ~250 bytes (experimental)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ATMEL ATMega/ATTiny/XMega
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined (ATMEL_AVR) || defined (__AVR_XMEGA__)                      // use PB6 as IR input on AVR
+#  define IRMP_PORT_LETTER                      B
+#  define IRMP_BIT_NUMBER                       6
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for PIC C18 or XC8 compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (PIC_C18)                                                 // use RB4 as IR input on PIC (C18 or XC8 compiler)
+#  if defined(__12F1840)
+#    define IRMP_PIN                            RA5                     // on 12F1840 with XC8 compiler
+#  else
+#    define IRMP_PIN                            PORTBbits.RB4           // PIC C18
+#  endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for PIC CCS compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (PIC_CCS)
+#  define IRMP_PIN                              PIN_B4                  // use PB4 as IR input on PIC (CCS compiler)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ARM STM32
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (ARM_STM32)                                               // use C13 as IR input on STM32
+#  define IRMP_PORT_LETTER                      C
+#  define IRMP_BIT_NUMBER                       13
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Hardware pin for ARM STM32 (HAL) - don't change here, define IRMP_RECEIVE_GPIO_Port & IRMP_RECEIVE_PIN in STM32Cube (Main.h)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (ARM_STM32_HAL)                                           // STM32: IRMP_RECEIVE_GPIO_Port & IRMP_RECEIVE_PIN must be defined in STM32Cube
+#  define IRMP_PORT_LETTER                      IRMP_Receive_GPIO_Port
+#  define IRMP_BIT_NUMBER                       IRMP_Receive_Pin
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for Stellaris ARM Cortex M4
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (STELLARIS_ARM_CORTEX_M4)                                 // use B4 as IR input on Stellaris LM4F
+#  define IRMP_PORT_LETTER                      B
+#  define IRMP_BIT_NUMBER                       4
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for STM8
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (SDCC_STM8)                                               // use PA1 as IR input on STM8
+#  define IRMP_PORT_LETTER                      A
+#  define IRMP_BIT_NUMBER                       1
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ESP8266
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (__xtensa__)
+#  define IRMP_BIT_NUMBER                       12                      // use GPIO12 (Pin 7 UEXT) on ESP8266-EVB evaluation board
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for Teensy 3.x with teensyduino gcc compiler
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined (TEENSY_ARM_CORTEX_M4)
+#  define IRMP_PIN                              1                       // use Digital pin 1 as IR input on Teensy
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for MBED
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined(__MBED__)
+#  define IRMP_PIN                              P0_22                   // use P1_27 on LPC1347
+#  define IRMP_PINMODE                          PullUp                  // hardware dependent
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Change hardware pin here for ChibiOS HAL
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif defined(_CHIBIOS_HAL_)
+#  define IRMP_PIN                              LINE_IR_IN              // use pin names as defined in the board config file, prefixed with "LINE_"
+#  define IRMP_LOGGING_SD                       SD1                     // the ChibiOS HAL Serial Driver instance to log to
+                                                                        // (when IRMP_LOGGING is enabled below). 
+                                                                        // Make sure SERIAL_BUFFERS_SIZE is large enough when enabling logging
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Handling of unknown target system: DON'T CHANGE
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#elif !defined (UNIX_OR_WINDOWS)
+#  error target system not defined.
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Set IRMP_LOGGING to 1 if want to log data to UART with 9600Bd
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_LOGGING
+#  define IRMP_LOGGING                          0       // 1: log IR signal (scan), 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use external logging routines
+ * If you enable external logging, you have also to enable IRMP_LOGGING above
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_EXT_LOGGING
+#  define IRMP_EXT_LOGGING                      0       // 1: use external logging, 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Set IRMP_PROTOCOL_NAMES to 1 if want to access protocol names (for logging etc), costs ~300 bytes RAM!
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_PROTOCOL_NAMES
+#  define IRMP_PROTOCOL_NAMES                   0       // 1: access protocol names, 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use Callbacks to indicate input signal
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_USE_CALLBACK
+#  define IRMP_USE_CALLBACK                     0       // 1: use callbacks. 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Call the user-provided irmp_idle() function when IRMP is idle.
+ * Can be used to disable the timer irq and enter a sleep mode to save power
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#ifndef IRMP_USE_IDLE_CALL
+#  define IRMP_USE_IDLE_CALL                    0       // 1: use idle calls. 0: do not. default is 0
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Use ChibiOS Events to signal that valid IR data was received
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#if defined(_CHIBIOS_RT_) || defined(_CHIBIOS_NIL_)
+
+#  ifndef IRMP_USE_EVENT
+#    define IRMP_USE_EVENT                      0       // 1: use event. 0: do not. default is 0
+#  endif
+
+#  if IRMP_USE_EVENT == 1 && !defined(IRMP_EVENT_BIT)
+#    define IRMP_EVENT_BIT                      1                     // event flag or bit to send
+#  endif
+#  if IRMP_USE_EVENT == 1 && !defined(IRMP_EVENT_THREAD_PTR)
+#    define IRMP_EVENT_THREAD_PTR               ir_receive_thread_p   // pointer to the thread to send the event to
+extern thread_t *IRMP_EVENT_THREAD_PTR;                               // the pointer must be defined and initialized elsewhere
+#  endif
+
+#endif // _CHIBIOS_RT_ || _CHIBIOS_NIL_
+
+#endif // _IRMPCONFIG_STAGE2_H_
diff --git a/irmp/irmpprotocols.h b/irmp/irmpprotocols.h
new file mode 100644 (file)
index 0000000..6e8bdfb
--- /dev/null
@@ -0,0 +1,1072 @@
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpprotocols.h - irmp protocols
+ *
+ * DO NOT INCLUDE THIS FILE, WILL BE INCLUDED BY IRMP.H or IRSND.H!
+ *
+ * Copyright (c) 2013-2019 Frank Meyer - frank(at)fli4l.de
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMP_PROTOCOLS_H_
+#define _IRMP_PROTOCOLS_H_
+
+#if !defined(_IRMP_H_) && !defined(_IRSND_H_)
+#  error please include only irmp.h or irsnd.h, not irmpprotocols.h
+#endif
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IR protocols:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP_UNKNOWN_PROTOCOL                    0              // uknown protocol
+#define IRMP_SIRCS_PROTOCOL                      1              // Sony
+#define IRMP_NEC_PROTOCOL                        2              // NEC, Pioneer, JVC, Toshiba, NoName etc.
+#define IRMP_SAMSUNG_PROTOCOL                    3              // Samsung
+#define IRMP_MATSUSHITA_PROTOCOL                 4              // Matsushita
+#define IRMP_KASEIKYO_PROTOCOL                   5              // Kaseikyo (Panasonic etc)
+#define IRMP_RECS80_PROTOCOL                     6              // Philips, Thomson, Nordmende, Telefunken, Saba
+#define IRMP_RC5_PROTOCOL                        7              // Philips etc
+#define IRMP_DENON_PROTOCOL                      8              // Denon, Sharp
+#define IRMP_RC6_PROTOCOL                        9              // Philips etc
+#define IRMP_SAMSUNG32_PROTOCOL                 10              // Samsung32: no sync pulse at bit 16, length 32 instead of 37
+#define IRMP_APPLE_PROTOCOL                     11              // Apple, very similar to NEC
+#define IRMP_RECS80EXT_PROTOCOL                 12              // Philips, Technisat, Thomson, Nordmende, Telefunken, Saba
+#define IRMP_NUBERT_PROTOCOL                    13              // Nubert
+#define IRMP_BANG_OLUFSEN_PROTOCOL              14              // Bang & Olufsen
+#define IRMP_GRUNDIG_PROTOCOL                   15              // Grundig
+#define IRMP_NOKIA_PROTOCOL                     16              // Nokia
+#define IRMP_SIEMENS_PROTOCOL                   17              // Siemens, e.g. Gigaset
+#define IRMP_FDC_PROTOCOL                       18              // FDC keyboard
+#define IRMP_RCCAR_PROTOCOL                     19              // RC Car
+#define IRMP_JVC_PROTOCOL                       20              // JVC (NEC with 16 bits)
+#define IRMP_RC6A_PROTOCOL                      21              // RC6A, e.g. Kathrein, XBOX
+#define IRMP_NIKON_PROTOCOL                     22              // Nikon
+#define IRMP_RUWIDO_PROTOCOL                    23              // Ruwido, e.g. T-Home Mediareceiver
+#define IRMP_IR60_PROTOCOL                      24              // IR60 (SDA2008)
+#define IRMP_KATHREIN_PROTOCOL                  25              // Kathrein
+#define IRMP_NETBOX_PROTOCOL                    26              // Netbox keyboard (bitserial)
+#define IRMP_NEC16_PROTOCOL                     27              // NEC with 16 bits (incl. sync)
+#define IRMP_NEC42_PROTOCOL                     28              // NEC with 42 bits
+#define IRMP_LEGO_PROTOCOL                      29              // LEGO Power Functions RC
+#define IRMP_THOMSON_PROTOCOL                   30              // Thomson
+#define IRMP_BOSE_PROTOCOL                      31              // BOSE
+#define IRMP_A1TVBOX_PROTOCOL                   32              // A1 TV Box
+#define IRMP_ORTEK_PROTOCOL                     33              // ORTEK - Hama
+#define IRMP_TELEFUNKEN_PROTOCOL                34              // Telefunken (1560)
+#define IRMP_ROOMBA_PROTOCOL                    35              // iRobot Roomba vacuum cleaner
+#define IRMP_RCMM32_PROTOCOL                    36              // Fujitsu-Siemens (Activy remote control)
+#define IRMP_RCMM24_PROTOCOL                    37              // Fujitsu-Siemens (Activy keyboard)
+#define IRMP_RCMM12_PROTOCOL                    38              // Fujitsu-Siemens (Activy keyboard)
+#define IRMP_SPEAKER_PROTOCOL                   39              // Another loudspeaker protocol, similar to Nubert
+#define IRMP_LGAIR_PROTOCOL                     40              // LG air conditioner
+#define IRMP_SAMSUNG48_PROTOCOL                 41              // air conditioner with SAMSUNG protocol (48 bits)
+#define IRMP_MERLIN_PROTOCOL                    42              // Merlin (Pollin 620 185)
+#define IRMP_PENTAX_PROTOCOL                    43              // Pentax camera
+#define IRMP_FAN_PROTOCOL                       44              // FAN (ventilator), very similar to NUBERT, but last bit is data bit instead of stop bit
+#define IRMP_S100_PROTOCOL                      45              // very similar to RC5, but 14 instead of 13 data bits
+#define IRMP_ACP24_PROTOCOL                     46              // Stiebel Eltron ACP24 air conditioner
+#define IRMP_TECHNICS_PROTOCOL                  47              // Technics, similar to Matsushita, but 22 instead of 24 bits
+#define IRMP_PANASONIC_PROTOCOL                 48              // Panasonic (Beamer), start bits similar to KASEIKYO
+#define IRMP_MITSU_HEAVY_PROTOCOL               49              // Mitsubishi-Heavy Aircondition, similar timing as Panasonic beamer
+#define IRMP_VINCENT_PROTOCOL                   50              // Vincent
+#define IRMP_SAMSUNGAH_PROTOCOL                 51              // SAMSUNG AH
+#define IRMP_IRMP16_PROTOCOL                    52              // IRMP specific protocol for data transfer, e.g. between two microcontrollers via IR
+#define IRMP_GREE_PROTOCOL                      53              // Gree climate
+#define IRMP_RCII_PROTOCOL                      54              // RC II Infra Red Remote Control Protocol for FM8
+#define IRMP_METZ_PROTOCOL                      55              // METZ
+#define IRMP_ONKYO_PROTOCOL                     56
+
+#define IRMP_RADIO1_PROTOCOL                    57              // Radio protocol (experimental status), do not use it yet!
+
+#define IRMP_N_PROTOCOLS                        57              // number of supported protocols
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * timing constants:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+// fm 22.09.2011: may not be more than 16000L, otherwise some JVC codes will not be accepted
+#define IRMP_TIMEOUT_TIME                       15500.0e-6                  // timeout after 15.5 ms darkness
+#define IRMP_TIMEOUT_TIME_MS                    15500L                      // timeout after 15.5 ms darkness
+
+#if IRMP_SUPPORT_NIKON_PROTOCOL == 1
+#  define IRMP_TIMEOUT_NIKON_TIME               29500.0e-6                  // 2nd timeout after 29.5 ms darkness (only for NIKON!)
+#  define IRMP_TIMEOUT_NIKON_TIME_MS            29500L                      // 2nd timeout after 29.5 ms darkness
+typedef uint16_t    PAUSE_LEN;
+#  define IRMP_TIMEOUT_NIKON_LEN                (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_NIKON_TIME + 0.5)
+#else
+#  if (F_INTERRUPTS * IRMP_TIMEOUT_TIME_MS) / 1000000 >= 254
+typedef uint16_t    PAUSE_LEN;
+#  else
+typedef uint8_t     PAUSE_LEN;
+#  endif
+#endif
+
+#define IRMP_TIMEOUT_LEN                        (PAUSE_LEN)(F_INTERRUPTS * IRMP_TIMEOUT_TIME + 0.5)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * flags of struct IRMP_PARAMETER:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP_PARAM_FLAG_IS_MANCHESTER           0x01
+#define IRMP_PARAM_FLAG_1ST_PULSE_IS_1          0x02
+#define IRMP_PARAM_FLAG_IS_SERIAL               0x04
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SIRCS:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SIRCS_START_BIT_PULSE_TIME              2400.0e-6                       // 2400 usec pulse
+#define SIRCS_START_BIT_PAUSE_TIME               600.0e-6                       //  600 usec pause
+#define SIRCS_1_PULSE_TIME                      1200.0e-6                       // 1200 usec pulse
+#define SIRCS_0_PULSE_TIME                       600.0e-6                       //  600 usec pulse
+#define SIRCS_PAUSE_TIME                         600.0e-6                       //  600 usec pause
+#define SIRCS_FRAMES                            3                               // SIRCS sends each frame 3 times
+#define SIRCS_AUTO_REPETITION_PAUSE_TIME          25.0e-3                       // auto repetition after 25ms
+#define SIRCS_FRAME_REPEAT_PAUSE_TIME             25.0e-3                       // frame repeat after 25ms
+#define SIRCS_ADDRESS_OFFSET                    15                              // skip 15 bits
+#define SIRCS_ADDRESS_LEN                       5                               // read up to 5 address bits
+#define SIRCS_COMMAND_OFFSET                    0                               // skip 0 bits
+#define SIRCS_COMMAND_LEN                       15                              // read 12-15 command bits
+#define SIRCS_MINIMUM_DATA_LEN                  12                              // minimum data length
+#define SIRCS_COMPLETE_DATA_LEN                 20                              // complete length - may be up to 20
+#define SIRCS_STOP_BIT                          0                               // has no stop bit
+#define SIRCS_LSB                               1                               // LSB...MSB
+#define SIRCS_FLAGS                             0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NEC & NEC42 & NEC16 & LGAIR:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NEC_START_BIT_PULSE_TIME                9000.0e-6                       // 9000 usec pulse
+#define NEC_START_BIT_PAUSE_TIME                4500.0e-6                       // 4500 usec pause
+#define NEC_REPEAT_START_BIT_PAUSE_TIME         2250.0e-6                       // 2250 usec pause
+#define NEC_PULSE_TIME                           560.0e-6                       //  560 usec pulse
+#define NEC_1_PAUSE_TIME                        1690.0e-6                       // 1690 usec pause
+#define NEC_0_PAUSE_TIME                         560.0e-6                       //  560 usec pause
+#define NEC_FRAME_REPEAT_PAUSE_TIME               40.0e-3                       // frame repeat after 40ms
+#define NEC_ADDRESS_OFFSET                       0                              // skip 0 bits
+#define NEC_ADDRESS_LEN                         16                              // read 16 address bits
+#define NEC_COMMAND_OFFSET                      16                              // skip 16 bits (8 address + 8 /address)
+#define NEC_COMMAND_LEN                         16                              // read 16 bits (8 command + 8 /command)
+#define NEC_COMPLETE_DATA_LEN                   32                              // complete length
+#define NEC_STOP_BIT                            1                               // has stop bit
+#define NEC_LSB                                 1                               // LSB...MSB
+#define NEC_FLAGS                               0                               // flags
+
+#define NEC42_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define NEC42_ADDRESS_LEN                      13                               // read 13 address bits
+#define NEC42_COMMAND_OFFSET                   26                               // skip 26 bits (2 x 13 address bits)
+#define NEC42_COMMAND_LEN                       8                               // read 8 command bits
+#define NEC42_COMPLETE_DATA_LEN                42                               // complete length (2 x 13 + 2 x 8)
+
+#define LGAIR_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define LGAIR_ADDRESS_LEN                       8                               // read 8 address bits
+#define LGAIR_COMMAND_OFFSET                    8                               // skip 8 bits (8 address)
+#define LGAIR_COMMAND_LEN                      16                               // read 16 bits (16 command)
+#define LGAIR_COMPLETE_DATA_LEN                28                               // complete length (8 address + 16 command + 4 checksum)
+
+#define NEC16_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define NEC16_ADDRESS_LEN                       8                               // read 8 address bits
+#define NEC16_COMMAND_OFFSET                    8                               // skip 8 bits (8 address)
+#define NEC16_COMMAND_LEN                       8                               // read 8 bits (8 command)
+#define NEC16_COMPLETE_DATA_LEN                 16                              // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SAMSUNG & SAMSUNG32 & SAMSUNG48:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SAMSUNG_START_BIT_PULSE_TIME            4500.0e-6                       // 4500 usec pulse
+#define SAMSUNG_START_BIT_PAUSE_TIME            4500.0e-6                       // 4500 usec pause
+#define SAMSUNG_PULSE_TIME                       550.0e-6                       //  550 usec pulse
+#define SAMSUNG_1_PAUSE_TIME                    1500.0e-6                       // 1550 usec pause
+#define SAMSUNG_0_PAUSE_TIME                     500.0e-6                       //  500 usec pause
+
+#define SAMSUNG_FRAME_REPEAT_PAUSE_TIME           25.0e-3                       // frame repeat after 25ms
+#define SAMSUNG_ADDRESS_OFFSET                   0                              // skip 0 bits
+#define SAMSUNG_ADDRESS_LEN                     16                              // read 16 address bits
+#define SAMSUNG_ID_OFFSET                       17                              // skip 16 + 1 sync bit
+#define SAMSUNG_ID_LEN                          4                               // read 4 id bits
+#define SAMSUNG_COMMAND_OFFSET                  21                              // skip 16 + 1 sync + 4 data bits
+#define SAMSUNG_COMMAND_LEN                     16                              // read 16 command bits
+#define SAMSUNG_COMPLETE_DATA_LEN               37                              // complete length
+#define SAMSUNG_STOP_BIT                        1                               // has stop bit
+#define SAMSUNG_LSB                             1                               // LSB...MSB?
+#define SAMSUNG_FLAGS                           0                               // flags
+
+#define SAMSUNG32_COMMAND_OFFSET                16                              // skip 16 bits
+#define SAMSUNG32_COMMAND_LEN                   16                              // read 16 command bits
+#define SAMSUNG32_COMPLETE_DATA_LEN             32                              // complete length
+#define SAMSUNG32_FRAMES                        1                               // SAMSUNG32 sends one frame
+#define SAMSUNG32_AUTO_REPETITION_PAUSE_TIME    47.0e-3                         // repetition after 47 ms
+#define SAMSUNG32_FRAME_REPEAT_PAUSE_TIME       47.0e-3                         // frame repeat after 47ms
+
+#define SAMSUNG48_COMMAND_OFFSET                16                              // skip 16 bits
+#define SAMSUNG48_COMMAND_LEN                   32                              // read 32 command bits
+#define SAMSUNG48_COMPLETE_DATA_LEN             48                              // complete length
+#define SAMSUNG48_FRAMES                        2                               // SAMSUNG48 sends each frame 2 times
+#define SAMSUNG48_AUTO_REPETITION_PAUSE_TIME    5.0e-3                          // repetition after 5 ms
+#define SAMSUNG48_FRAME_REPEAT_PAUSE_TIME       47.0e-3                         // frame repeat after 47ms
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SAMSUNGAH:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SAMSUNGAH_START_BIT_PULSE_TIME          2500.0e-6                       // 2500 usec pulse
+#define SAMSUNGAH_START_BIT_PAUSE_TIME          1900.0e-6                       // 1900 usec pause
+#define SAMSUNGAH_PULSE_TIME                     450.0e-6                       //  450 usec pulse
+#define SAMSUNGAH_1_PAUSE_TIME                  1100.0e-6                       // 1100 usec pause
+#define SAMSUNGAH_0_PAUSE_TIME                   450.0e-6                       //  450 usec pause
+#define SAMSUNGAH_FRAME_REPEAT_PAUSE_TIME         40.0e-3                       // frame repeat after 40ms
+#define SAMSUNGAH_ADDRESS_OFFSET                 0                              // skip 0 bits
+#define SAMSUNGAH_ADDRESS_LEN                   16                              // read 16 address bits, ignore 17..31
+#define SAMSUNGAH_COMMAND_OFFSET                32                              // skip 32 bits
+#define SAMSUNGAH_COMMAND_LEN                   16                              // read 32 bits
+#define SAMSUNGAH_COMPLETE_DATA_LEN             48                              // complete length
+#define SAMSUNGAH_STOP_BIT                      1                               // has stop bit
+#define SAMSUNGAH_LSB                           1                               // LSB...MSB?
+#define SAMSUNGAH_FLAGS                         0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MATSUSHITA:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MATSUSHITA_START_BIT_PULSE_TIME         3488.0e-6                       // 3488 usec pulse
+#define MATSUSHITA_START_BIT_PAUSE_TIME         3488.0e-6                       // 3488 usec pause
+#define MATSUSHITA_PULSE_TIME                    872.0e-6                       //  872 usec pulse
+#define MATSUSHITA_1_PAUSE_TIME                 2616.0e-6                       // 2616 usec pause
+#define MATSUSHITA_0_PAUSE_TIME                  872.0e-6                       //  872 usec pause
+#define MATSUSHITA_FRAME_REPEAT_PAUSE_TIME        40.0e-3                       // frame repeat after 40ms
+#define MATSUSHITA_ADDRESS_OFFSET               12                              // skip 12 bits
+#define MATSUSHITA_ADDRESS_LEN                  12                              // read 12 address bits
+#define MATSUSHITA_COMMAND_OFFSET               0                               // skip 0 bits
+#define MATSUSHITA_COMMAND_LEN                  12                              // read 12 bits (6 custom + 6 command)
+#define MATSUSHITA_COMPLETE_DATA_LEN            24                              // complete length
+#define MATSUSHITA_STOP_BIT                     1                               // has stop bit
+#define MATSUSHITA_LSB                          1                               // LSB...MSB?
+#define MATSUSHITA_FLAGS                        0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * TECHNICS: same timings as MATSUSHITA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define TECHNICS_ADDRESS_LEN                    0                               // read 0 address bits
+#define TECHNICS_COMMAND_LEN                    11                              // read 11 bits
+#define TECHNICS_COMPLETE_DATA_LEN              22                              // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * KASEIKYO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define KASEIKYO_START_BIT_PULSE_TIME           3380.0e-6                       // 3380 usec pulse
+#define KASEIKYO_START_BIT_PAUSE_TIME           1690.0e-6                       // 1690 usec pause
+#define KASEIKYO_PULSE_TIME                      423.0e-6                       //  525 usec pulse
+#define KASEIKYO_1_PAUSE_TIME                   1269.0e-6                       //  525 usec pause
+#define KASEIKYO_0_PAUSE_TIME                    423.0e-6                       // 1690 usec pause
+#define KASEIKYO_AUTO_REPETITION_PAUSE_TIME       74.0e-3                       // repetition after 74 ms
+#define KASEIKYO_FRAME_REPEAT_PAUSE_TIME          74.0e-3                       // frame repeat after 74 ms
+#define KASEIKYO_ADDRESS_OFFSET                  0                              // skip 0 bits
+#define KASEIKYO_ADDRESS_LEN                    16                              // read 16 address bits
+#define KASEIKYO_COMMAND_OFFSET                 28                              // skip 28 bits (16 manufacturer & 4 parity & 8 genre)
+#define KASEIKYO_COMMAND_LEN                    12                              // read 12 command bits (10 real command & 2 id)
+#define KASEIKYO_COMPLETE_DATA_LEN              48                              // complete length
+#define KASEIKYO_STOP_BIT                       1                               // has stop bit
+#define KASEIKYO_LSB                            1                               // LSB...MSB?
+#define KASEIKYO_FRAMES                         1                               // KASEIKYO sends 1 frame
+#define KASEIKYO_FLAGS                          0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * PANASONIC (Beamer), start bit timings similar to KASEIKYO
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define PANASONIC_START_BIT_PULSE_TIME           3600.0e-6                      // 3600 usec pulse
+#define PANASONIC_START_BIT_PAUSE_TIME           1600.0e-6                      // 1690 usec pause
+#define PANASONIC_PULSE_TIME                      565.0e-6                      //  565 usec pulse
+#define PANASONIC_1_PAUSE_TIME                   1140.0e-6                      // 1140 usec pause
+#define PANASONIC_0_PAUSE_TIME                    316.0e-6                      //  316 usec pause
+#define PANASONIC_AUTO_REPETITION_PAUSE_TIME       40.0e-3                      // repetition after 40 ms?
+#define PANASONIC_FRAME_REPEAT_PAUSE_TIME          40.0e-3                      // frame repeat after 40 ms
+#define PANASONIC_ADDRESS_OFFSET                 24                             // skip 24 bits: 010000000000010000000001
+#define PANASONIC_ADDRESS_LEN                    16                             // read 16 address bits
+#define PANASONIC_COMMAND_OFFSET                 40                             // skip 40 bits
+#define PANASONIC_COMMAND_LEN                    16                             // read 16 command bits
+#define PANASONIC_COMPLETE_DATA_LEN              56                             // complete length
+#define PANASONIC_STOP_BIT                       1                              // has stop bit
+#define PANASONIC_LSB                            1                              // LSB...MSB?
+#define PANASONIC_FRAMES                         1                              // PANASONIC sends 1 frame
+#define PANASONIC_FLAGS                          0                              // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MITSUBISHI-Heavy Aircondition, timings similar to PANASONIC beamer
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MITSU_HEAVY_START_BIT_PULSE_TIME          3200.0e-6                     // 3600 usec pulse
+#define MITSU_HEAVY_START_BIT_PAUSE_TIME          1560.0e-6                     // 1690 usec pause
+#define MITSU_HEAVY_PULSE_TIME                     400.0e-6                     //  565 usec pulse
+#define MITSU_HEAVY_1_PAUSE_TIME                  1200.0e-6                     // 1140 usec pause
+#define MITSU_HEAVY_0_PAUSE_TIME                   430.0e-6                     //  316 usec pause
+#define MITSU_HEAVY_FRAME_REPEAT_PAUSE_TIME         40.0e-3                     // frame repeat after 40 ms
+#define MITSU_HEAVY_ADDRESS_OFFSET                 40                           // skip 24 bits: 010000000000010000000001
+#define MITSU_HEAVY_ADDRESS_LEN                    16                           // read 16 address bits
+#define MITSU_HEAVY_COMMAND_OFFSET                 56                           // skip 40 bits
+#define MITSU_HEAVY_COMMAND_LEN                    16                           // read 16 command bits
+#define MITSU_HEAVY_COMPLETE_DATA_LEN              88                           // complete length
+#define MITSU_HEAVY_STOP_BIT                       1                            // has stop bit
+#define MITSU_HEAVY_LSB                            0                            // LSB...MSB?
+#define MITSU_HEAVY_FRAMES                         1                            // PANASONIC sends 1 frame
+#define MITSU_HEAVY_FLAGS                          0                            // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * VINCENT
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define VINCENT_START_BIT_PULSE_TIME            2500.0e-6                       // 2500 usec pulse
+#define VINCENT_START_BIT_PAUSE_TIME            4600.0e-6                       // 4600 usec pause
+#define VINCENT_PULSE_TIME                       550.0e-6                       //  550 usec pulse
+#define VINCENT_1_PAUSE_TIME                    1540.0e-6                       // 1540 usec pause
+#define VINCENT_0_PAUSE_TIME                     550.0e-6                       //  550 usec pause
+#define VINCENT_FRAME_REPEAT_PAUSE_TIME           40.0e-3                       // frame repeat after 40 ms ?
+#define VINCENT_ADDRESS_OFFSET                     0                            // skip 0 bits
+#define VINCENT_ADDRESS_LEN                       16                            // read 16 address bits
+#define VINCENT_COMMAND_OFFSET                    16                            // skip 16 bits
+#define VINCENT_COMMAND_LEN                       16                            // read 16 command bits
+#define VINCENT_COMPLETE_DATA_LEN                 32                            // complete length
+#define VINCENT_STOP_BIT                           1                            // has stop bit
+#define VINCENT_LSB                                0                            // LSB...MSB?
+#define VINCENT_FRAMES                             1                            // VINCENT sends 1 frame
+#define VINCENT_FLAGS                              0                            // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RECS80:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RECS80_START_BIT_PULSE_TIME              158.0e-6                       //  158 usec pulse
+#define RECS80_START_BIT_PAUSE_TIME             7432.0e-6                       // 7432 usec pause
+#define RECS80_PULSE_TIME                        158.0e-6                       //  158 usec pulse
+#define RECS80_1_PAUSE_TIME                     7432.0e-6                       // 7432 usec pause
+#define RECS80_0_PAUSE_TIME                     4902.0e-6                       // 4902 usec pause
+#define RECS80_FRAME_REPEAT_PAUSE_TIME            45.0e-3                       // frame repeat after 45ms
+#define RECS80_ADDRESS_OFFSET                   1                               // skip 1 bit (toggle bit)
+#define RECS80_ADDRESS_LEN                      3                               // read 3 address bits
+#define RECS80_COMMAND_OFFSET                   4                               // skip 4 bits (1 toggle + 3 address)
+#define RECS80_COMMAND_LEN                      6                               // read 6 command bits
+#define RECS80_COMPLETE_DATA_LEN                10                              // complete length
+#define RECS80_STOP_BIT                         1                               // has stop bit
+#define RECS80_LSB                              0                               // MSB...LSB
+#define RECS80_FLAGS                            0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC5:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RC5_BIT_TIME                             889.0e-6                       // 889 usec pulse/pause
+#define RC5_FRAME_REPEAT_PAUSE_TIME               88.9e-3                       // frame repeat after 88.9ms
+
+#define RC5_ADDRESS_OFFSET                      1                               // skip 1 bit (2nd start)
+#define RC5_ADDRESS_LEN                         6                               // read 1 toggle bit (for key repetition detection) + 5 address bits
+#define RC5_COMMAND_OFFSET                      7                               // skip 5 bits (2nd start + 1 toggle + 5 address)
+#define RC5_COMMAND_LEN                         6                               // read 6 command bits
+#define RC5_COMPLETE_DATA_LEN                   13                              // complete length
+#define RC5_STOP_BIT                            0                               // has no stop bit
+#define RC5_LSB                                 0                               // MSB...LSB
+#define RC5_FLAGS                               IRMP_PARAM_FLAG_IS_MANCHESTER   // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RCII:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCII_START_BIT_PULSE_TIME                512.0e-6                       //  512 usec pulse
+#define RCII_START_BIT_PAUSE_TIME               2560.0e-6                       // 2560 usec pause
+#define RCII_START_BIT2_PULSE_TIME              1024.0e-6                       // 1024 usec pulse
+
+#define RCII_BIT_TIME                            512.0e-6                       // 512 usec pulse/pause
+#define RCII_FRAME_REPEAT_PAUSE_TIME             117.76e-3                      // frame repeat after 117.76ms
+
+#define RCII_ADDRESS_OFFSET                     0                               // skip 1 bit (2nd start)
+#define RCII_ADDRESS_LEN                        0                               // no address
+#define RCII_COMMAND_OFFSET                     0                               // command offset is 0
+#define RCII_COMMAND_LEN                        10                              // read 1 + 9 command bits
+#define RCII_COMPLETE_DATA_LEN                  10                              // complete length
+#define RCII_STOP_BIT                           0                               // has no stop bit
+#define RCII_LSB                                0                               // MSB...LSB
+#define RCII_FLAGS                              (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1)  // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * S100: very similar to RC5, but 14 insted of 13 bits
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define S100_BIT_TIME                             889.0e-6                       // 889 usec pulse/pause
+#define S100_FRAME_REPEAT_PAUSE_TIME               88.9e-3                       // frame repeat after 88.9ms
+
+#define S100_ADDRESS_OFFSET                      1                               // skip 1 bit (2nd start)
+#define S100_ADDRESS_LEN                         6                               // read 1 toggle bit (for key repetition detection) + 5 address bits
+#define S100_COMMAND_OFFSET                      7                               // skip 5 bits (2nd start + 1 toggle + 5 address)
+#define S100_COMMAND_LEN                         7                               // read 7 command bits
+#define S100_COMPLETE_DATA_LEN                   14                              // complete length
+#define S100_STOP_BIT                            0                               // has no stop bit
+#define S100_LSB                                 0                               // MSB...LSB
+#define S100_FLAGS                               IRMP_PARAM_FLAG_IS_MANCHESTER   // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * DENON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define DENON_PULSE_TIME                         310.0e-6                       //  310 usec pulse in practice,  275 in theory
+#define DENON_1_PAUSE_TIME                      1780.0e-6                       // 1780 usec pause in practice, 1900 in theory
+#define DENON_0_PAUSE_TIME                       745.0e-6                       //  745 usec pause in practice,  775 in theory
+#define DENON_FRAMES                            2                               // DENON sends each frame 2 times
+#define DENON_AUTO_REPETITION_PAUSE_TIME          45.0e-3                       // inverted repetition after 45ms
+#define DENON_FRAME_REPEAT_PAUSE_TIME             45.0e-3                       // frame repeat after 45ms
+#define DENON_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define DENON_ADDRESS_LEN                       5                               // read 5 address bits
+#define DENON_COMMAND_OFFSET                    5                               // skip 5
+#define DENON_COMMAND_LEN                       10                              // read 10 command bits
+#define DENON_COMPLETE_DATA_LEN                 15                              // complete length
+#define DENON_STOP_BIT                          1                               // has stop bit
+#define DENON_LSB                               0                               // MSB...LSB
+#define DENON_FLAGS                             0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC6:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RC6_START_BIT_PULSE_TIME                2666.0e-6                       // 2.666 msec pulse
+#define RC6_START_BIT_PAUSE_TIME                 889.0e-6                       // 889 usec pause
+#define RC6_TOGGLE_BIT_TIME                      889.0e-6                       // 889 msec pulse/pause
+#define RC6_BIT_TIME                             444.0e-6                       // 444 usec pulse/pause
+#define RC6_BIT_2_TIME                           889.0e-6                       // 889 usec pulse/pause
+#define RC6_BIT_3_TIME                          1333.0e-6                       // 1333 usec pulse/pause
+#define RC6_FRAME_REPEAT_PAUSE_TIME               45.0e-3                       // frame repeat after 45ms
+#define RC6_ADDRESS_OFFSET                      5                               // skip "1" + 3 mode bits + 1 toggle bit
+#define RC6_ADDRESS_LEN                         8                               // read 8 address bits
+#define RC6_COMMAND_OFFSET                      13                              // skip 12 bits ("1" + 3 mode + 1 toggle + 8 address)
+#define RC6_COMMAND_LEN                         8                               // read 8 command bits
+#define RC6_COMPLETE_DATA_LEN_SHORT             21                              // complete length
+#define RC6_COMPLETE_DATA_LEN_LONG              36                              // complete length
+#define RC6_STOP_BIT                            0                               // has no stop bit
+#define RC6_LSB                                 0                               // MSB...LSB
+#define RC6_FLAGS                               (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1)   // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RECS80EXT:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RECS80EXT_START_BIT_PULSE_TIME           158.0e-6                       //  158 usec pulse
+#define RECS80EXT_START_BIT_PAUSE_TIME          3637.0e-6                       // 3637 usec pause
+#define RECS80EXT_PULSE_TIME                     158.0e-6                       //  158 usec pulse
+#define RECS80EXT_1_PAUSE_TIME                  7432.0e-6                       // 7432 usec pause
+#define RECS80EXT_0_PAUSE_TIME                  4902.0e-6                       // 4902 usec pause
+#define RECS80EXT_FRAME_REPEAT_PAUSE_TIME         45.0e-3                       // frame repeat after 45ms
+#define RECS80EXT_ADDRESS_OFFSET                2                               // skip 2 bits (2nd start + 1 toggle)
+#define RECS80EXT_ADDRESS_LEN                   4                               // read 4 address bits
+#define RECS80EXT_COMMAND_OFFSET                6                               // skip 6 bits (2nd start + 1 toggle + 4 address)
+#define RECS80EXT_COMMAND_LEN                   6                               // read 6 command bits
+#define RECS80EXT_COMPLETE_DATA_LEN             12                              // complete length
+#define RECS80EXT_STOP_BIT                      1                               // has stop bit
+#define RECS80EXT_LSB                           0                               // MSB...LSB
+#define RECS80EXT_FLAGS                         0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NUBERT:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NUBERT_START_BIT_PULSE_TIME             1340.0e-6                       // 1340 usec pulse
+#define NUBERT_START_BIT_PAUSE_TIME              340.0e-6                       //  340 usec pause
+#define NUBERT_1_PULSE_TIME                     1340.0e-6                       // 1340 usec pulse
+#define NUBERT_1_PAUSE_TIME                      340.0e-6                       //  340 usec pause
+#define NUBERT_0_PULSE_TIME                      500.0e-6                       //  500 usec pulse
+#define NUBERT_0_PAUSE_TIME                     1300.0e-6                       // 1300 usec pause
+#define NUBERT_FRAMES                           2                               // Nubert sends 2 frames
+#define NUBERT_AUTO_REPETITION_PAUSE_TIME         35.0e-3                       // auto repetition after 35ms
+#define NUBERT_FRAME_REPEAT_PAUSE_TIME            35.0e-3                       // frame repeat after 45ms
+#define NUBERT_ADDRESS_OFFSET                   0                               // skip 0 bits
+#define NUBERT_ADDRESS_LEN                      0                               // read 0 address bits
+#define NUBERT_COMMAND_OFFSET                   0                               // skip 0 bits
+#define NUBERT_COMMAND_LEN                      10                              // read 10 bits
+#define NUBERT_COMPLETE_DATA_LEN                10                              // complete length
+#define NUBERT_STOP_BIT                         1                               // has stop bit
+#define NUBERT_LSB                              0                               // MSB?
+#define NUBERT_FLAGS                            0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * FAN: (ventilator)
+ *
+ * Similar to NUBERT, but
+ *   - has data bit instead of stop bit
+ *   - has NO frame repetition
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define FAN_START_BIT_PULSE_TIME                1280.0e-6                       // 1280 usec pulse
+#define FAN_START_BIT_PAUSE_TIME                 380.0e-6                       //  380 usec pause
+#define FAN_1_PULSE_TIME                        1280.0e-6                       // 1280 usec pulse
+#define FAN_1_PAUSE_TIME                         380.0e-6                       //  380 usec pause
+#define FAN_0_PULSE_TIME                         380.0e-6                       //  380 usec pulse
+#define FAN_0_PAUSE_TIME                        1280.0e-6                       // 1280 usec pause
+#define FAN_FRAMES                              1                               // FAN sends only 1 frame (NUBERT sends 2)
+#define FAN_AUTO_REPETITION_PAUSE_TIME            6.6e-3                        // auto repetition after 6.6ms
+#define FAN_FRAME_REPEAT_PAUSE_TIME               6.6e-3                        // frame repeat after 6.6ms
+#define FAN_ADDRESS_OFFSET                      0                               // skip 0 bits
+#define FAN_ADDRESS_LEN                         0                               // read 0 address bits
+#define FAN_COMMAND_OFFSET                      0                               // skip 0 bits
+#define FAN_COMMAND_LEN                         11                              // read 10 bits
+#define FAN_COMPLETE_DATA_LEN                   11                              // complete length
+#define FAN_STOP_BIT                            0                               // has NO stop bit (fm: this seems to be wrong)
+#define FAN_LSB                                 0                               // MSB
+#define FAN_FLAGS                               0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SPEAKER:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define SPEAKER_START_BIT_PULSE_TIME             440.0e-6                       //  440 usec pulse
+#define SPEAKER_START_BIT_PAUSE_TIME            1250.0e-6                       // 1250 usec pause
+#define SPEAKER_1_PULSE_TIME                    1250.0e-6                       // 1250 usec pulse
+#define SPEAKER_1_PAUSE_TIME                     440.0e-6                       //  440 usec pause
+#define SPEAKER_0_PULSE_TIME                     440.0e-6                       //  440 usec pulse
+#define SPEAKER_0_PAUSE_TIME                    1250.0e-6                       // 1250 usec pause
+#define SPEAKER_FRAMES                          2                               // SPEAKER sends 2 frames
+#define SPEAKER_AUTO_REPETITION_PAUSE_TIME        35.0e-3                       // auto repetition after 35ms
+#define SPEAKER_FRAME_REPEAT_PAUSE_TIME           35.0e-3                       // frame repeat after 45ms
+#define SPEAKER_ADDRESS_OFFSET                  0                               // skip 0 bits
+#define SPEAKER_ADDRESS_LEN                     0                               // read 0 address bits
+#define SPEAKER_COMMAND_OFFSET                  0                               // skip 0 bits
+#define SPEAKER_COMMAND_LEN                     10                              // read 10 bits
+#define SPEAKER_COMPLETE_DATA_LEN               10                              // complete length
+#define SPEAKER_STOP_BIT                        1                               // has stop bit
+#define SPEAKER_LSB                             0                               // MSB?
+#define SPEAKER_FLAGS                           0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * BANG_OLUFSEN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define BANG_OLUFSEN_START_BIT1_PULSE_TIME       200.0e-6                       //   200 usec pulse
+#define BANG_OLUFSEN_START_BIT1_PAUSE_TIME      3125.0e-6                       //  3125 usec pause
+#define BANG_OLUFSEN_START_BIT2_PULSE_TIME       200.0e-6                       //   200 usec pulse
+#define BANG_OLUFSEN_START_BIT2_PAUSE_TIME      3125.0e-6                       //  3125 usec pause
+#define BANG_OLUFSEN_START_BIT3_PULSE_TIME       200.0e-6                       //   200 usec pulse
+#define BANG_OLUFSEN_START_BIT3_PAUSE_TIME     15625.0e-6                       // 15625 usec pause
+#define BANG_OLUFSEN_START_BIT4_PULSE_TIME       200.0e-6                       //   200 usec pulse
+#define BANG_OLUFSEN_START_BIT4_PAUSE_TIME      3125.0e-6                       //  3125 usec pause
+#define BANG_OLUFSEN_PULSE_TIME                  200.0e-6                       //   200 usec pulse
+#define BANG_OLUFSEN_1_PAUSE_TIME               9375.0e-6                       //  9375 usec pause
+#define BANG_OLUFSEN_0_PAUSE_TIME               3125.0e-6                       //  3125 usec pause
+#define BANG_OLUFSEN_R_PAUSE_TIME               6250.0e-6                       //  6250 usec pause (repeat last bit)
+#define BANG_OLUFSEN_TRAILER_BIT_PAUSE_TIME    12500.0e-6                       // 12500 usec pause (trailer bit)
+#define BANG_OLUFSEN_FRAME_REPEAT_PAUSE_TIME      45.0e-3                       // frame repeat after 45ms
+#define BANG_OLUFSEN_ADDRESS_OFFSET             0                               // no address bits
+#define BANG_OLUFSEN_ADDRESS_LEN                0                               // no address bits
+#define BANG_OLUFSEN_COMMAND_OFFSET             3                               // skip startbits 2, 3, 4
+#define BANG_OLUFSEN_COMMAND_LEN                16                              // read 16 command bits
+#define BANG_OLUFSEN_COMPLETE_DATA_LEN          20                              // complete length: startbits 2, 3, 4 + 16 data bits + trailer bit
+#define BANG_OLUFSEN_STOP_BIT                   1                               // has stop bit
+#define BANG_OLUFSEN_LSB                        0                               // MSB...LSB
+#define BANG_OLUFSEN_FLAGS                      0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * GRUNDIG & NOKIA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define GRUNDIG_NOKIA_IR60_BIT_TIME             528.0e-6                        // 528 usec pulse/pause
+#define GRUNDIG_NOKIA_IR60_PRE_PAUSE_TIME       2639.0e-6                       // 2639 usec pause after pre bit
+#define GRUNDIG_NOKIA_IR60_FRAME_REPEAT_PAUSE_TIME  117.76e-3                   // info frame repeat after 117.76 ms
+#define GRUNDIG_NOKIA_IR60_STOP_BIT             0                               // has no stop bit
+#define GRUNDIG_NOKIA_IR60_LSB                  1                               // MSB...LSB
+#define GRUNDIG_NOKIA_IR60_FLAGS                (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1)  // flags
+
+#define GRUNDIG_FRAMES                          2                               // GRUNDIG sends each frame 1+1 times
+#define GRUNDIG_AUTO_REPETITION_PAUSE_TIME      20.0e-3                         // repetition after 20ms
+#define GRUNDIG_ADDRESS_OFFSET                  0                               // no address
+#define GRUNDIG_ADDRESS_LEN                     0                               // no address
+#define GRUNDIG_COMMAND_OFFSET                  1                               // skip 1 start bit
+#define GRUNDIG_COMMAND_LEN                     9                               // read 9 command bits
+#define GRUNDIG_COMPLETE_DATA_LEN               10                              // complete length: 1 start bit + 9 data bits
+
+#define NOKIA_FRAMES                            3                               // NOKIA sends each frame 1 + 1 + 1 times
+#define NOKIA_AUTO_REPETITION_PAUSE_TIME        20.0e-3                         // repetition after 20ms
+#define NOKIA_ADDRESS_OFFSET                    9                               // skip 9 bits (1 start bit + 8 data bits)
+#define NOKIA_ADDRESS_LEN                       8                               // 7 address bits
+#define NOKIA_COMMAND_OFFSET                    1                               // skip 1 bit (1 start bit)
+#define NOKIA_COMMAND_LEN                       8                               // read 8 command bits
+#define NOKIA_COMPLETE_DATA_LEN                 17                              // complete length: 1 start bit + 8 address bits + 8 command bits
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IR60:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IR60_FRAMES                             2                               // IR60 sends each frame 1+1 times
+#define IR60_AUTO_REPETITION_PAUSE_TIME         22.2e-3                         // repetition after 22.2ms
+#define IR60_TIMEOUT_TIME                       5000.0e-6                       // timeout grundig frame, switch to IR60
+#define IR60_ADDRESS_OFFSET                     0                               // skip 1 bits
+#define IR60_ADDRESS_LEN                        0                               // read 0 address bits
+#define IR60_COMMAND_OFFSET                     0                               // skip 1 bit (start bit after pre bit, always 1)
+#define IR60_COMMAND_LEN                        7                               // read 6 command bits
+#define IR60_COMPLETE_DATA_LEN                  7                               // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * SIEMENS & RUWIDO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#if 0
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME    275.0e-6                      //  275 usec pulse
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME    550.0e-6                      //  550 usec pause
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME          275.0e-6                      //  275 usec short pulse
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2        550.0e-6                      //  550 usec long pulse
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME          275.0e-6                      //  275 usec short pause
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2        550.0e-6                      //  550 usec long pause
+#else
+#define SIEMENS_OR_RUWIDO_START_BIT_PULSE_TIME    370.0e-6                      //  370 usec pulse
+#define SIEMENS_OR_RUWIDO_START_BIT_PAUSE_TIME    550.0e-6                      //  550 usec pause
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME          370.0e-6                      //  370 usec short pulse
+#define SIEMENS_OR_RUWIDO_BIT_PULSE_TIME_2        680.0e-6                      //  680 usec long pulse
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME          275.0e-6                      //  275 usec short pause
+#define SIEMENS_OR_RUWIDO_BIT_PAUSE_TIME_2        550.0e-6                      //  550 usec long pause
+#endif
+
+#define SIEMENS_OR_RUWIDO_FRAME_REPEAT_PAUSE_TIME 45.0e-3                       // frame repeat after 45ms
+#define SIEMENS_OR_RUWIDO_STOP_BIT                0                             // has no stop bit
+#define SIEMENS_OR_RUWIDO_LSB                     0                             // MSB...LSB
+#define SIEMENS_OR_RUWIDO_FLAGS                   (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1)  // flags
+
+#define RUWIDO_ADDRESS_OFFSET                   0                               // skip 0 bits
+#define RUWIDO_ADDRESS_LEN                      9                               // read 9 address bits
+#define RUWIDO_COMMAND_OFFSET                   9                               // skip 9 bits
+#define RUWIDO_COMMAND_LEN                      8                               // read 7 + 1 command bits, last bit is only check bit
+#define RUWIDO_COMPLETE_DATA_LEN                17                              // complete length
+
+#define SIEMENS_ADDRESS_OFFSET                  0                               // skip 0 bits
+#define SIEMENS_ADDRESS_LEN                     11                              // read 11 bits
+#define SIEMENS_COMMAND_OFFSET                  11                              // skip 11 bits
+#define SIEMENS_COMMAND_LEN                     11                              // read 10 + 1 command bits, last bit is only check bit
+#define SIEMENS_COMPLETE_DATA_LEN               22                              // complete length
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * FDC:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define FDC_START_BIT_PULSE_TIME                 2085.0e-6                      // 2085 usec pulse
+#define FDC_START_BIT_PAUSE_TIME                  966.0e-6                      //  966 usec pause
+#define FDC_PULSE_TIME                            300.0e-6                      //  300 usec pulse
+#define FDC_1_PAUSE_TIME                          715.0e-6                      //  715 usec pause
+#define FDC_0_PAUSE_TIME                          220.0e-6                      //  220 usec pause
+#define FDC_FRAME_REPEAT_PAUSE_TIME                60.0e-3                      // frame repeat after 60ms
+#define FDC_ADDRESS_OFFSET                       0                              // skip 0 bits
+#define FDC_ADDRESS_LEN                         14                              // read 14 address bits, but use only 6, shift 8 into command
+#define FDC_COMMAND_OFFSET                      20                              // skip 20 bits
+#define FDC_COMMAND_LEN                         12                              // read 12 bits
+#define FDC_COMPLETE_DATA_LEN                   40                              // complete length
+#define FDC_STOP_BIT                            1                               // has stop bit
+#define FDC_LSB                                 1                               // LSB...MSB
+#define FDC_FLAGS                               0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RCCAR:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCCAR_START_BIT_PULSE_TIME               2000.0e-6                      // 2000 usec pulse
+#define RCCAR_START_BIT_PAUSE_TIME               2000.0e-6                      // 2000 usec pause
+#define RCCAR_PULSE_TIME                          600.0e-6                      //  360 usec pulse
+#define RCCAR_1_PAUSE_TIME                        450.0e-6                      //  650 usec pause
+#define RCCAR_0_PAUSE_TIME                        900.0e-6                      //  180 usec pause
+#define RCCAR_FRAME_REPEAT_PAUSE_TIME              40.0e-3                      // frame repeat after 40ms
+#define RCCAR_ADDRESS_OFFSET                     0                              // skip 0 bits
+#define RCCAR_ADDRESS_LEN                        0                              // read 0 address bits
+#define RCCAR_COMMAND_OFFSET                     0                              // skip 0 bits
+#define RCCAR_COMMAND_LEN                       13                              // read 13 bits
+#define RCCAR_COMPLETE_DATA_LEN                 13                              // complete length
+#define RCCAR_STOP_BIT                          1                               // has stop bit
+#define RCCAR_LSB                               1                               // LSB...MSB
+#define RCCAR_FLAGS                             0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * JVC:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define JVC_START_BIT_PULSE_TIME                9000.0e-6                       // 9000 usec pulse
+#define JVC_START_BIT_PAUSE_TIME                4500.0e-6                       // 4500 usec pause
+#define JVC_PULSE_TIME                           560.0e-6                       //  560 usec pulse
+#define JVC_1_PAUSE_TIME                        1690.0e-6                       // 1690 usec pause
+#define JVC_0_PAUSE_TIME                         560.0e-6                       //  560 usec pause
+#define JVC_FRAME_REPEAT_PAUSE_TIME               22.0e-3                       // frame repeat after 22ms
+#define JVC_ADDRESS_OFFSET                       0                              // skip 0 bits
+#define JVC_ADDRESS_LEN                          4                              // read 4 address bits
+#define JVC_COMMAND_OFFSET                       4                              // skip 4 bits
+#define JVC_COMMAND_LEN                         12                              // read 12 bits
+#define JVC_COMPLETE_DATA_LEN                   16                              // complete length
+#define JVC_STOP_BIT                            1                               // has stop bit
+#define JVC_LSB                                 1                               // LSB...MSB
+#define JVC_FLAGS                               0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NIKON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NIKON_START_BIT_PULSE_TIME              2200.0e-6                       //  2200 usec pulse
+#define NIKON_START_BIT_PAUSE_TIME             27100.0e-6                       // 27100 usec pause
+#define NIKON_PULSE_TIME                         500.0e-6                       //   500 usec pulse
+#define NIKON_1_PAUSE_TIME                      3500.0e-6                       //  3500 usec pause
+#define NIKON_0_PAUSE_TIME                      1500.0e-6                       //  1500 usec pause
+#define NIKON_FRAME_REPEAT_PAUSE_TIME             60.0e-3                       // frame repeat after 60ms
+#define NIKON_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define NIKON_ADDRESS_LEN                       0                               // read 0 address bits
+#define NIKON_COMMAND_OFFSET                    0                               // skip 0 bits
+#define NIKON_COMMAND_LEN                       2                               // read 2 bits
+#define NIKON_COMPLETE_DATA_LEN                 2                               // complete length
+#define NIKON_STOP_BIT                          1                               // has stop bit
+#define NIKON_LSB                               0                               // LSB...MSB
+#define NIKON_FLAGS                             0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * KATHREIN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define KATHREIN_START_BIT_PULSE_TIME            210.0e-6                       // 1340 usec pulse
+#define KATHREIN_START_BIT_PAUSE_TIME           6218.0e-6                       //  340 usec pause
+#define KATHREIN_1_PULSE_TIME                    210.0e-6                       // 1340 usec pulse
+#define KATHREIN_1_PAUSE_TIME                   3000.0e-6                       //  340 usec pause
+#define KATHREIN_0_PULSE_TIME                    210.0e-6                       //  500 usec pulse
+#define KATHREIN_0_PAUSE_TIME                   1400.0e-6                       // 1300 usec pause
+#define KATHREIN_SYNC_BIT_PAUSE_LEN_TIME        4600.0e-6                       // 4600 usec sync (on 6th and/or 8th bit)
+#define KATHREIN_FRAMES                         1                               // Kathrein sends 1 frame
+#define KATHREIN_AUTO_REPETITION_PAUSE_TIME     35.0e-3                         // auto repetition after 35ms
+#define KATHREIN_FRAME_REPEAT_PAUSE_TIME        35.0e-3                         // frame repeat after 35ms
+#define KATHREIN_ADDRESS_OFFSET                 1                               // skip 1 bits
+#define KATHREIN_ADDRESS_LEN                    4                               // read 4 address bits
+#define KATHREIN_COMMAND_OFFSET                 5                               // skip 5 bits
+#define KATHREIN_COMMAND_LEN                    7                               // read 7 bits
+#define KATHREIN_COMPLETE_DATA_LEN              13                              // complete length
+#define KATHREIN_STOP_BIT                       1                               // has stop bit
+#define KATHREIN_LSB                            0                               // MSB
+#define KATHREIN_FLAGS                          0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * NETBOX:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define NETBOX_START_BIT_PULSE_TIME             2400.0e-6                       // 2400 usec pulse
+#define NETBOX_START_BIT_PAUSE_TIME              800.0e-6                       //  800 usec pause
+#define NETBOX_PULSE_TIME                        800.0e-6                       //  800 usec pulse
+#define NETBOX_PAUSE_TIME                        800.0e-6                       //  800 usec pause
+#define NETBOX_FRAMES                           1                               // Netbox sends 1 frame
+#define NETBOX_AUTO_REPETITION_PAUSE_TIME       35.0e-3                         // auto repetition after 35ms
+#define NETBOX_FRAME_REPEAT_PAUSE_TIME          35.0e-3                         // frame repeat after 35ms
+#define NETBOX_ADDRESS_OFFSET                   0                               // skip 0 bits
+#define NETBOX_ADDRESS_LEN                      3                               // read 3 address bits
+#define NETBOX_COMMAND_OFFSET                   3                               // skip 3 bits
+#define NETBOX_COMMAND_LEN                      13                              // read 13 bits
+#define NETBOX_COMPLETE_DATA_LEN                16                              // complete length
+#define NETBOX_STOP_BIT                         0                               // has no stop bit
+#define NETBOX_LSB                              1                               // LSB
+#define NETBOX_FLAGS                            IRMP_PARAM_FLAG_IS_SERIAL       // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * LEGO:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define LEGO_START_BIT_PULSE_TIME                158.0e-6                       //  158 usec pulse ( 6 x 1/38kHz)
+#define LEGO_START_BIT_PAUSE_TIME               1026.0e-6                       // 1026 usec pause (39 x 1/38kHz)
+#define LEGO_PULSE_TIME                          158.0e-6                       //  158 usec pulse ( 6 x 1/38kHz)
+#define LEGO_1_PAUSE_TIME                        553.0e-6                       //  553 usec pause (21 x 1/38kHz)
+#define LEGO_0_PAUSE_TIME                        263.0e-6                       //  263 usec pause (10 x 1/38kHz)
+#define LEGO_FRAME_REPEAT_PAUSE_TIME              40.0e-3                       // frame repeat after 40ms
+#define LEGO_ADDRESS_OFFSET                     0                               // skip 0 bits
+#define LEGO_ADDRESS_LEN                        0                               // read 0 address bits
+#define LEGO_COMMAND_OFFSET                     0                               // skip 0 bits
+#define LEGO_COMMAND_LEN                        16                              // read 16 bits (12 command + 4 CRC)
+#define LEGO_COMPLETE_DATA_LEN                  16                              // complete length
+#define LEGO_STOP_BIT                           1                               // has stop bit
+#define LEGO_LSB                                0                               // MSB...LSB
+#define LEGO_FLAGS                              0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * THOMSON:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define THOMSON_PULSE_TIME                       550.0e-6                       //  550 usec pulse
+#define THOMSON_1_PAUSE_TIME                    4500.0e-6                       // 4500 usec pause
+#define THOMSON_0_PAUSE_TIME                    2000.0e-6                       // 2000 usec pause
+#define THOMSON_FRAMES                          1                               // THOMSON sends 1 frame
+#define THOMSON_AUTO_REPETITION_PAUSE_TIME        35.0e-3                       // repetition after 35ms
+#define THOMSON_FRAME_REPEAT_PAUSE_TIME           35.0e-3                       // frame repeat after 35ms
+#define THOMSON_ADDRESS_OFFSET                  0                               // skip 0 bits
+#define THOMSON_ADDRESS_LEN                     4                               // read 4 address bits
+#define THOMSON_COMMAND_OFFSET                  5                               // skip 4 address bits + 1 toggle bit
+#define THOMSON_COMMAND_LEN                     7                               // read 7 command bits
+#define THOMSON_COMPLETE_DATA_LEN               12                              // complete length
+#define THOMSON_STOP_BIT                        1                               // has stop bit
+#define THOMSON_LSB                             0                               // MSB...LSB
+#define THOMSON_FLAGS                           0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * BOSE:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define BOSE_START_BIT_PULSE_TIME               1060.0e-6                       // 1060 usec pulse
+#define BOSE_START_BIT_PAUSE_TIME               1425.0e-6                       // 1425 usec pause
+#define BOSE_PULSE_TIME                          550.0e-6                       //  550 usec pulse
+#define BOSE_1_PAUSE_TIME                       1425.0e-6                       // 1425 usec pause
+#define BOSE_0_PAUSE_TIME                        437.0e-6                       //  437 usec pause
+#define BOSE_FRAMES                             1
+#define BOSE_AUTO_REPETITION_PAUSE_TIME           40.0e-3                       // repetition after 40ms?
+#define BOSE_FRAME_REPEAT_PAUSE_TIME              40.0e-3                       // frame repeat after 40ms?
+#define BOSE_ADDRESS_OFFSET                      0                              // skip 0 bits
+#define BOSE_ADDRESS_LEN                         0                              // read 16 address bits
+#define BOSE_COMMAND_OFFSET                      0                              // skip 16 bits (8 address + 8 /address)
+#define BOSE_COMMAND_LEN                        16                              // read 16 bits (8 command + 8 /command)
+#define BOSE_COMPLETE_DATA_LEN                  16                              // complete length
+#define BOSE_STOP_BIT                           1                               // has stop bit
+#define BOSE_LSB                                1                               // LSB...MSB
+#define BOSE_FLAGS                              0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * A1TVBOX:
+ * In reality A1 TV Box has no start bit with 300/340 usec. There are 2 start bits "10" with 250us pulse + 150us pause + 150us pause + 250us pulse
+ * This is not very easy to detect, because 1st and 2nd pause of both start bits are closely spaced.
+ * So IRMP looks for pseudo start bit with 300/340 usec and ignores the second half of the 2nd bit (250us pulse)
+ * This method only works because the first data bit (which is the 3rd bit) following is always "1":
+ * IRMP treats the first "long" pulse (250us of 2nd start bit + 250us of 1st data bit) of this "1" as a first _short_ pulse.
+ * This is a bug in IRMP's manchester decoder, but a good feature here ;-)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define A1TVBOX_START_BIT_PULSE_TIME            300.0e-6                        // 300 usec pulse
+#define A1TVBOX_START_BIT_PAUSE_TIME            340.0e-6                        // 340 usec pause
+#define A1TVBOX_BIT_PULSE_TIME                  250.0e-6                        // 250 usec pulse
+#define A1TVBOX_BIT_PAUSE_TIME                  150.0e-6                        // 150 usec pulse
+#define A1TVBOX_STOP_BIT                        0                               // has no stop bit
+#define A1TVBOX_LSB                             0                               // MSB...LSB
+#define A1TVBOX_FLAGS                           (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1 )  // flags
+#define A1TVBOX_FRAMES                          1                               // A1TVBOX sends each frame 1 times
+#define A1TVBOX_ADDRESS_OFFSET                  1                               // skip 1 bits
+#define A1TVBOX_ADDRESS_LEN                     8                               // read 8 address bits
+#define A1TVBOX_COMMAND_OFFSET                  9                               // skip 9 bits (start bit + address)
+#define A1TVBOX_COMMAND_LEN                     8                               // read 8 command bits
+#define A1TVBOX_COMPLETE_DATA_LEN               17                              // complete length incl. start bit
+#define A1TVBOX_FRAME_REPEAT_PAUSE_TIME         50.0e-3                         // 50 msec pause between frames, don't know if it is correct
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * MERLIN:
+ * See notes for A1TVBOX
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define MERLIN_START_BIT_PULSE_TIME            210.0e-6                         // 210 usec pulse
+#define MERLIN_START_BIT_PAUSE_TIME            420.0e-6                         // 429 usec pause
+#define MERLIN_BIT_PULSE_TIME                  210.0e-6                         // 210 usec pulse
+#define MERLIN_BIT_PAUSE_TIME                  210.0e-6                         // 210 usec pulse
+#define MERLIN_STOP_BIT                        0                                // has no stop bit
+#define MERLIN_LSB                             0                                // MSB...LSB
+#define MERLIN_FLAGS                           (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1 )  // flags
+#define MERLIN_FRAMES                          1                                // MERLIN sends each frame 1 times
+#define MERLIN_ADDRESS_OFFSET                  2                                // skip 1 bits
+#define MERLIN_ADDRESS_LEN                     9                                // read 9 address bits
+#define MERLIN_COMMAND_OFFSET                  11                               // skip 11 bits (start bit + address)
+#define MERLIN_COMMAND_LEN                     32                               // read up to 32 command bits
+#define MERLIN_COMPLETE_DATA_LEN               45                               // complete length incl. start bit
+#define MERLIN_FRAME_REPEAT_PAUSE_TIME         50.0e-3                          // 50 msec pause between frames, don't know if it is correct
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ORTEK (Hama): 6 address bits + 2 frame type bits + 6 command bits + 1 parity bit + 1 unknown bit + "1" + "0"
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ORTEK_START_BIT_PULSE_TIME              2000.0e-6                       // 2000 usec pulse
+#define ORTEK_START_BIT_PAUSE_TIME              1000.0e-6                       // 1000 usec pause
+#define ORTEK_BIT_TIME                           500.0e-6                       //  500 usec pulse/pause
+#define ORTEK_FRAME_REPEAT_PAUSE_TIME             45.0e-3                       // frame repeat after 45ms
+#define ORTEK_ADDRESS_OFFSET                    0                               // skip 0 bits
+#define ORTEK_ADDRESS_LEN                       8                               // read 6 address bits + 2 special bits
+#define ORTEK_COMMAND_OFFSET                    8                               // skip 6 address bits + 2 special bits
+#define ORTEK_COMMAND_LEN                       6                               // read 6 command bits
+#define ORTEK_COMPLETE_DATA_LEN                 18                              // complete length
+#define ORTEK_STOP_BIT                          0                               // has no stop bit
+#define ORTEK_LSB                               0                               // MSB...LSB
+#define ORTEK_FLAGS                             (IRMP_PARAM_FLAG_IS_MANCHESTER | IRMP_PARAM_FLAG_1ST_PULSE_IS_1)   // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * TELEFUNKEN:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define TELEFUNKEN_START_BIT_PULSE_TIME          600.0e-6                       //  600 usec pulse
+#define TELEFUNKEN_START_BIT_PAUSE_TIME         1500.0e-6                       // 1500 usec pause
+#define TELEFUNKEN_PULSE_TIME                    600.0e-6                       //  600 usec pulse
+#define TELEFUNKEN_1_PAUSE_TIME                 1500.0e-6                       // 1500 usec pause
+#define TELEFUNKEN_0_PAUSE_TIME                  600.0e-6                       //  600 usec pause
+#define TELEFUNKEN_FRAME_REPEAT_PAUSE_TIME        22.0e-3                       // frame repeat after XX ms ?????
+#define TELEFUNKEN_ADDRESS_OFFSET                0                              // skip 0 bits
+#define TELEFUNKEN_ADDRESS_LEN                   0                              // read 0 address bits
+#define TELEFUNKEN_COMMAND_OFFSET                0                              // skip 0 bits
+#define TELEFUNKEN_COMMAND_LEN                  15                              // read 15 bits
+#define TELEFUNKEN_COMPLETE_DATA_LEN            15                              // complete length
+#define TELEFUNKEN_STOP_BIT                     1                               // has stop bit
+#define TELEFUNKEN_LSB                          0                               // LSB...MSB
+#define TELEFUNKEN_FLAGS                        0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ROOMBA
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ROOMBA_START_BIT_PULSE_TIME             2790.0e-6                       // 2790 usec pulse
+#define ROOMBA_START_BIT_PAUSE_TIME              930.0e-6                       //  930 usec pause
+#define ROOMBA_0_PULSE_TIME                      930.0e-6                       //  930 usec pulse
+#define ROOMBA_1_PULSE_TIME                     2790.0e-6                       // 2790 usec pulse
+#define ROOMBA_0_PAUSE_TIME                     2790.0e-6                       // 2790 usec pause
+#define ROOMBA_1_PAUSE_TIME                      930.0e-6                       //  930 usec pause
+#define ROOMBA_FRAME_REPEAT_PAUSE_TIME            18.0e-3                       // frame repeat after 18ms
+#define ROOMBA_ADDRESS_OFFSET                    0                              // skip 0 bits
+#define ROOMBA_ADDRESS_LEN                       0                              // read 0 address bits
+#define ROOMBA_COMMAND_OFFSET                    0                              // skip 0 bits
+#define ROOMBA_COMMAND_LEN                       7                              // read 7 bits
+#define ROOMBA_COMPLETE_DATA_LEN                 7                              // complete length
+#define ROOMBA_STOP_BIT                         0                               // has stop bit (fm: sure?)
+#define ROOMBA_LSB                              0                               // MSB...LSB
+#define ROOMBA_FLAGS                            0                               // flags
+#define ROOMBA_FRAMES                           8                               // ROOMBA sends 8 frames (this is a lie, but more comfortable)
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RC-MM (32, 24, or 12 bit)
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RCMM32_START_BIT_PULSE_TIME              500.0e-6                       // 500 usec pulse
+#define RCMM32_START_BIT_PAUSE_TIME              220.0e-6                       // 220 usec pause
+#define RCMM32_PULSE_TIME                        230.0e-6                       // 230 usec pulse
+#define RCMM32_00_PAUSE_TIME                     220.0e-6                       // 220 usec pause
+#define RCMM32_01_PAUSE_TIME                     370.0e-6                       // 370 usec pause
+#define RCMM32_10_PAUSE_TIME                     540.0e-6                       // 540 usec pause
+#define RCMM32_11_PAUSE_TIME                     720.0e-6                       // 720 usec pause
+
+#define RCMM32_FRAME_REPEAT_PAUSE_TIME            80.0e-3                       // frame repeat after 80 ms
+#define RCMM32_ADDRESS_OFFSET                    0                              // skip 0 bits
+#define RCMM32_ADDRESS_LEN                      16                              //  read 16 address bits
+#define RCMM32_COMMAND_OFFSET                   17                              // skip 17 bits
+#define RCMM32_COMMAND_LEN                      15                              // read 15 bits
+#define RCMM32_COMPLETE_DATA_LEN                32                              // complete length
+#define RCMM32_STOP_BIT                         1                               // has stop bit
+#define RCMM32_LSB                              0                               // LSB...MSB
+#define RCMM32_FLAGS                            0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * PENTAX:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define PENTAX_START_BIT_PULSE_TIME             13000.0e-6                      // 13 msec pulse
+#define PENTAX_START_BIT_PAUSE_TIME              3000.0e-6                      // 3 msec pause
+#define PENTAX_PULSE_TIME                        1000.0e-6                      // 1 msec pulse
+#define PENTAX_1_PAUSE_TIME                      3000.0e-6                      // 3 msec pause
+#define PENTAX_0_PAUSE_TIME                      1000.0e-6                      // 1 msec pause
+#define PENTAX_FRAME_REPEAT_PAUSE_TIME             60.0e-3                      // frame repeat after 60ms
+#define PENTAX_ADDRESS_OFFSET                  0                                // skip 0 bits
+#define PENTAX_ADDRESS_LEN                     0                                // read 0 address bits
+#define PENTAX_COMMAND_OFFSET                  0                                // skip 0 bits
+#define PENTAX_COMMAND_LEN                     6                                // read 6 bits
+#define PENTAX_COMPLETE_DATA_LEN               6                                // complete length
+#define PENTAX_STOP_BIT                        1                                // has stop bit
+#define PENTAX_LSB                             0                                // LSB...MSB
+#define PENTAX_FLAGS                           0                                // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * ACP24: Stiebel Eltron ACP24 air conditioner
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define ACP24_START_BIT_PULSE_TIME               390.0e-6                       //  390 usec pulse
+#define ACP24_START_BIT_PAUSE_TIME               950.0e-6                       //  950 usec pause
+#define ACP24_PULSE_TIME                         390.0e-6                       //  390 usec pulse
+#define ACP24_1_PAUSE_TIME                      1300.0e-6                       // 1300 usec pause
+#define ACP24_0_PAUSE_TIME                       950.0e-6                       //  950 usec pause
+#define ACP24_FRAME_REPEAT_PAUSE_TIME             22.0e-3                       // frame repeat after 22ms?
+#define ACP24_ADDRESS_OFFSET                     0                              // skip 0 bits
+#define ACP24_ADDRESS_LEN                        0                              // read 6 address bits
+#define ACP24_COMMAND_OFFSET                     0                              // skip 6 bits
+#define ACP24_COMMAND_LEN                        0                              // read 0 bits (70 bits will be read and compressed by special routine)
+#define ACP24_COMPLETE_DATA_LEN                 70                              // complete length
+#define ACP24_STOP_BIT                          1                               // has stop bit
+#define ACP24_LSB                               0                               // LSB...MSB
+#define ACP24_FLAGS                             0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * IRMP16:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define IRMP16_START_BIT_PULSE_TIME              842.0e-6                       //  842 usec pulse (32 x 1/38kHz)
+#define IRMP16_START_BIT_PAUSE_TIME             1052.0e-6                       // 1052 usec pause (40 x 1/38kHz)
+#define IRMP16_PULSE_TIME                        421.0e-6                       //  421 usec pulse (16 x 1/38kHz)
+#define IRMP16_1_PAUSE_TIME                      842.0e-6                       //  842 usec pause (32 x 1/38kHz)
+#define IRMP16_0_PAUSE_TIME                      421.0e-6                       //  421 usec pause (16 x 1/38kHz)
+#define IRMP16_FRAME_REPEAT_PAUSE_TIME            40.0e-3                       // frame repeat after 40ms
+#define IRMP16_ADDRESS_OFFSET                   0                               // skip 0 bits
+#define IRMP16_ADDRESS_LEN                      0                               // read 0 address bits
+#define IRMP16_COMMAND_OFFSET                   0                               // skip 0 bits
+#define IRMP16_COMMAND_LEN                      16                              // read 16 bits (12 command + 4 CRC)
+#define IRMP16_COMPLETE_DATA_LEN                16                              // complete length
+#define IRMP16_STOP_BIT                         1                               // has stop bit
+#define IRMP16_LSB                              1                               // LSB...MSB
+#define IRMP16_FLAGS                            0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * GREE - climate:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define GREE_START_BIT_PULSE_TIME              12000.0e-6                       // 12000 usec pulse (32 x 1/38kHz)
+#define GREE_START_BIT_PAUSE_TIME               6000.0e-6                       //  6000 usec pause (40 x 1/38kHz)
+#define GREE_PULSE_TIME                          900.0e-6                       //   900 usec pulse (16 x 1/38kHz)
+#define GREE_1_PAUSE_TIME                        700.0e-6                       //   700 usec pause (32 x 1/38kHz)
+#define GREE_0_PAUSE_TIME                       2100.0e-6                       //  2100 usec pause (16 x 1/38kHz)
+#define GREE_FRAME_REPEAT_PAUSE_TIME              40.0e-3                       // frame repeat after 40ms
+#define GREE_ADDRESS_OFFSET                     0                               // skip 0 bits
+#define GREE_ADDRESS_LEN                        16                              // read 16 address bits
+#define GREE_COMMAND_OFFSET                     16                              // skip 16 bits
+#define GREE_COMMAND_LEN                        16                              // read 16 bits
+#define GREE_COMPLETE_DATA_LEN                  32                              // complete length
+#define GREE_STOP_BIT                           1                               // has stop bit
+#define GREE_LSB                                1                               // LSB...MSB
+#define GREE_FLAGS                              0                               // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * METZ:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define METZ_START_BIT_PULSE_TIME                870.0e-6                       //  870 usec pulse
+#define METZ_START_BIT_PAUSE_TIME               2300.0e-6                       // 2300 usec pause
+#define METZ_PULSE_TIME                          435.0e-6                       //  435 usec pulse
+#define METZ_1_PAUSE_TIME                       1680.0e-6                       // 1680 usec pause
+#define METZ_0_PAUSE_TIME                        960.0e-6                       //  960 usec pause
+#define METZ_FRAME_REPEAT_PAUSE_TIME             122.0e-3                       // frame repeat after 122ms
+#define METZ_ADDRESS_OFFSET                      1                              // skip 1 bit (toggle bit)
+#define METZ_ADDRESS_LEN                         6                              // read 6 address bits
+#define METZ_COMMAND_OFFSET                      7                              // skip 7 bits
+#define METZ_COMMAND_LEN                        13                              // read 13 bits
+#define METZ_COMPLETE_DATA_LEN                  20                              // complete length
+#define METZ_STOP_BIT                            0                              // has no stop bit
+#define METZ_LSB                                 0                              // MSB...LSB
+#define METZ_FLAGS                               0                              // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * RADIO1 - e.g. Tevion
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define RADIO1_START_BIT_PULSE_TIME            3000.0e-6                        // 3000 usec pulse
+#define RADIO1_START_BIT_PAUSE_TIME            7000.0e-6                        // 7000 usec pulse
+#define RADIO1_0_PULSE_TIME                     500.0e-6                        //  500 usec pulse
+#define RADIO1_0_PAUSE_TIME                    1000.0e-6                        // 1000 usec pause
+#define RADIO1_1_PULSE_TIME                    1000.0e-6                        // 1000 usec pulse
+#define RADIO1_1_PAUSE_TIME                     500.0e-6                        //  500 usec pause
+
+#define RADIO1_FRAME_REPEAT_PAUSE_TIME           25.0e-3                        // frame repeat after 25ms
+#define RADIO1_ADDRESS_OFFSET                   4                               // skip 4 bits
+#define RADIO1_ADDRESS_LEN                     16                               // read 16 address bits
+#define RADIO1_COMMAND_OFFSET                  20                               // skip 4 + 16 bits
+#define RADIO1_COMMAND_LEN                      3                               // read 3 command bits
+#define RADIO1_COMPLETE_DATA_LEN               23                               // complete length
+#define RADIO1_STOP_BIT                        1                                // has stop bit
+#define RADIO1_LSB                             1                                // LSB...MSB?
+#define RADIO1_FLAGS                           0                                // flags
+
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * Frame Repetitions:
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+#define AUTO_FRAME_REPETITION_TIME              80.0e-3                         // SIRCS/SAMSUNG32/NUBERT: automatic repetition after 25-50ms
+
+#endif // _IRMP_PROTOCOLS_H_
diff --git a/irmp/irmpsystem.h b/irmp/irmpsystem.h
new file mode 100644 (file)
index 0000000..4b14952
--- /dev/null
@@ -0,0 +1,225 @@
+/*---------------------------------------------------------------------------------------------------------------------------------------------------
+ * irmpsystem.h - system specific includes and defines
+ *
+ * Copyright (c) 2009-2019 Frank Meyer - frank(at)fli4l.de
+ *
+ * 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.
+ *---------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+
+#ifndef _IRMPSYSTEM_H_
+#define _IRMPSYSTEM_H_
+
+#if !defined(_IRMP_H_) && !defined(_IRSND_H_)
+#  error please include only irmp.h or irsnd.h, not irmpsystem.h
+#endif
+
+/* Workaround required when building with xtensa (cross-)toolchains. */
+#ifdef __xtensa__
+#undef __xtensa__
+#endif
+
+#if defined(__18CXX)                                                                // Microchip PIC C18 compiler
+#  define PIC_C18
+#elif defined(__XC8)                                                                // PIC XC8 compiler
+#  include <xc.h>
+#  define PIC_C18
+#elif defined(__PCM__) || defined(__PCB__) || defined(__PCH__)                      // CCS PIC compiler
+#  define PIC_CCS
+#elif defined(STM32L1XX_MD) || defined(STM32L1XX_MDP) || defined(STM32L1XX_HD)      // ARM STM32
+#  include <stm32l1xx.h>
+#  define ARM_STM32
+#  define ARM_STM32L1XX
+#  define F_CPU (SysCtlClockGet())
+#elif defined(STM32F10X_LD) || defined(STM32F10X_LD_VL) \
+   || defined(STM32F10X_MD) || defined(STM32F10X_MD_VL) \
+   || defined(STM32F10X_HD) || defined(STM32F10X_HD_VL) \
+   || defined(STM32F10X_XL) || defined(STM32F10X_CL)                                // ARM STM32
+#  include <stm32f10x.h>
+#  define ARM_STM32
+#  define ARM_STM32F10X
+#  define F_CPU (SysCtlClockGet())
+#elif defined(STM32F4XX)                                                            // ARM STM32
+#  include <stm32f4xx.h>
+#  define ARM_STM32
+#  define ARM_STM32F4XX
+#elif defined(USE_HAL_DRIVER)                                                       // ARM STM32 with HAL Library
+#  include "gpio.h"
+#  if defined(_IRSND_H_)
+#    include"tim.h"
+#  endif
+#  define ARM_STM32_HAL
+#  define F_CPU SystemCoreClock
+#elif defined(__SDCC_stm8)                                                          // STM8
+#  define SDCC_STM8
+#elif defined(TARGET_IS_BLIZZARD_RA2)                                               // TI Stellaris (tested on Stellaris Launchpad with Code Composer Studio)
+#  define STELLARIS_ARM_CORTEX_M4
+#  define F_CPU (SysCtlClockGet())
+#elif defined(__xtensa__)                                                           // ESP8266 (Arduino)
+#  include "Arduino.h"
+#  include "ets_sys.h"
+#  include "osapi.h"
+#  include "gpio.h"
+#  include "os_type.h"
+#  include "c_types.h"
+#  define uint_fast8_t uint8_t
+#  define uint_fast16_t uint16_t
+#elif defined(TEENSYDUINO) && (defined(__MK20DX256__) || defined(__MK20DX128__))    // Teensy 3.x (tested on Teensy 3.1 in Arduino 1.6.5 / Teensyduino 1.2.5)
+#  include <core_pins.h>
+#  define TEENSY_ARM_CORTEX_M4
+#elif defined(unix) || defined(WIN32) || defined(__APPLE__)                         // Unix/Linux or Windows or Apple
+#  define UNIX_OR_WINDOWS
+#elif defined(__MBED__)                                                             // mbed platform
+// #include "mbed.h"                                                                // if mbed.h is used, source must be compiled as cpp
+#include "gpio_api.h"
+#elif defined(IRMP_CHIBIOS_HAL)                                                     // ChibiOS HAL
+#  include "hal.h"
+#else
+#  define ATMEL_AVR                                                                 // ATMEL AVR
+#endif
+
+#include <string.h>
+
+#ifdef UNIX_OR_WINDOWS                                                              // Analyze on Unix/Linux or Windows
+#  include <stdio.h>
+#  include <stdlib.h>
+#  define F_CPU 8000000L
+#  define ANALYZE
+#  ifdef unix
+#    include <stdint.h>
+#  else
+typedef unsigned char                   uint8_t;
+typedef unsigned short                  uint16_t;
+#  endif
+#endif
+
+
+#if defined(ATMEL_AVR)
+#  include <stdint.h>
+#  include <stdio.h>
+#  include <avr/io.h>
+#  include <util/delay.h>
+#  include <avr/pgmspace.h>
+#  include <avr/interrupt.h>
+#  define IRSND_OC2                     0       // OC2
+#  define IRSND_OC2A                    1       // OC2A
+#  define IRSND_OC2B                    2       // OC2B
+#  define IRSND_OC0                     3       // OC0
+#  define IRSND_OC0A                    4       // OC0A
+#  define IRSND_OC0B                    5       // OC0B
+
+#  define IRSND_XMEGA_OC0A              0       // OC0A
+#  define IRSND_XMEGA_OC0B              1       // OC0B
+#  define IRSND_XMEGA_OC0C              2       // OC0C
+#  define IRSND_XMEGA_OC0D              3       // OC0D
+#  define IRSND_XMEGA_OC1A              4       // OC1A
+#  define IRSND_XMEGA_OC1B              5       // OC1B
+
+#elif defined(STELLARIS_ARM_CORTEX_M4)
+
+#  include "inc/hw_ints.h"
+#  include "inc/hw_memmap.h"
+#  include "inc/hw_types.h"
+#  include "inc/hw_gpio.h"
+#  include "driverlib/fpu.h"
+#  include "driverlib/sysctl.h"
+#  include "driverlib/interrupt.h"
+#  include "driverlib/gpio.h"
+#  include "driverlib/rom.h"
+#  include "driverlib/systick.h"
+#  include "driverlib/pin_map.h"
+#  include "driverlib/timer.h"
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+#  define APP_SYSTICKS_PER_SEC          32
+
+#elif defined(ARM_STM32F10X)
+
+#  include "stm32f10x_gpio.h"
+#  include "stm32f10x_rcc.h"
+#  include "stm32f10x_tim.h"
+#  include "misc.h"
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+
+#elif defined(SDCC_STM8)
+
+#  include "stm8s.h"
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+#  define __attribute__(x)
+#  define uint_fast8_t                  uint8_t
+#  define uint_fast16_t                 uint16_t
+
+#elif defined(TEENSY_ARM_CORTEX_M4)
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+
+#elif defined(__xtensa__)
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+
+#elif defined(__MBED__)
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+
+#else
+#  define PROGMEM
+#  define memcpy_P                      memcpy
+
+#endif
+
+#if defined(PIC_CCS) || defined(PIC_C18)
+typedef unsigned char                   uint8_t;
+typedef unsigned short                  uint16_t;
+typedef unsigned char                   uint_fast8_t;
+typedef unsigned short                  uint_fast16_t;
+#endif
+
+#if defined (PIC_C18)                                                               // PIC C18 or XC8 compiler
+#  include <p18cxxx.h>                                                              // main PIC18 h file
+#ifndef __XC8
+#  include <timers.h>                                                               // timer lib
+#  include <pwm.h>                                                                  // pwm lib
+#endif
+#  define IRSND_PIC_CCP1                1                                           // PIC C18 RC2 = PWM1 module
+#  define IRSND_PIC_CCP2                2                                           // PIC C18 RC1 = PWM2 module
+#endif
+
+#ifndef TRUE
+#  define TRUE                          1
+#  define FALSE                         0
+#endif
+
+#if IRMP_32_BIT == 1
+
+typedef struct
+{
+    uint8_t                             protocol;                                   // protocol, e.g. NEC_PROTOCOL
+    uint16_t                            address;                                    // address
+    uint32_t                            command;                                    // command
+    uint8_t                             flags;                                      // flags, e.g. repetition
+} IRMP_DATA;
+
+#else // not IRMP_32_BIT == 1
+
+#if defined(PIC_C18)
+#define IRMP_PACKED_STRUCT
+#else
+#define IRMP_PACKED_STRUCT              __attribute__ ((__packed__))
+#endif
+
+typedef struct IRMP_PACKED_STRUCT
+{
+    uint8_t                             protocol;                                   // protocol, e.g. NEC_PROTOCOL
+    uint16_t                            address;                                    // address
+    uint16_t                            command;                                    // command
+    uint8_t                             flags;                                      // flags, e.g. repetition
+} IRMP_DATA;
+
+#endif // IRMP_32_BIT == 1
+
+#endif // _IRMPSYSTEM_H_
index af245f6ad197f5b75ab8a2782a0399bb43b2a95c..0e3cb64231c3af30431056293fc6b60cf28288dd 100644 (file)
 #include <Python.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
 #include "libsigrokdecode.h"
 
+/*
+ * Static definition of tables ending with an all-zero sentinel entry
+ * may raise warnings when compiling with -Wmissing-field-initializers.
+ * GCC suppresses the warning only with { 0 }, clang wants { } instead.
+ */
+#ifdef __clang__
+#  define ALL_ZERO { }
+#else
+#  define ALL_ZERO { 0 }
+#endif
+
 enum {
        SRD_TERM_ALWAYS_FALSE,
        SRD_TERM_HIGH,
@@ -82,6 +93,8 @@ SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di,
                uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
                const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
 SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match);
+SRD_PRIV int srd_inst_flush(struct srd_decoder_inst *di);
+SRD_PRIV int srd_inst_send_eof(struct srd_decoder_inst *di);
 SRD_PRIV int srd_inst_terminate_reset(struct srd_decoder_inst *di);
 SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di);
 SRD_PRIV void srd_inst_free_all(struct srd_session *sess);
index b9cc921287e876347ceea9d1ea08f61a46046f2e..ea17cb8d014c654708e9a71fef632f697da0916c 100644 (file)
@@ -106,29 +106,34 @@ enum srd_loglevel {
  */
 
 /* Marks public libsigrokdecode API symbols. */
-#ifndef _WIN32
-#define SRD_API __attribute__((visibility("default")))
+#if defined _WIN32
+#  if defined DLL_EXPORT
+#    define SRD_API __declspec(dllexport)
+#  else
+#    define SRD_API extern
+#  endif
 #else
-#define SRD_API
+#  define SRD_API __attribute__((visibility("default")))
 #endif
 
 /* Marks private, non-public libsigrokdecode symbols (not part of the API). */
-#ifndef _WIN32
-#define SRD_PRIV __attribute__((visibility("hidden")))
+#if defined _WIN32
+#  define SRD_PRIV /* EMPTY */
 #else
-#define SRD_PRIV
+#  define SRD_PRIV __attribute__((visibility("hidden")))
 #endif
 
 /*
  * When adding an output type, don't forget to...
- *   - expose it to PDs in controller.c:PyInit_sigrokdecode()
- *   - add a check in module_sigrokdecode.c:Decoder_put()
- *   - add a debug string in type_decoder.c:OUTPUT_TYPES
+ *   - expose it to PDs in module_sigrokdecode.c:PyInit_sigrokdecode()
+ *   - add a check in type_decoder.c:Decoder_put()
+ *   - add a debug string in type_decoder.c:output_type_name()
  */
 enum srd_output_type {
        SRD_OUTPUT_ANN,
        SRD_OUTPUT_PYTHON,
        SRD_OUTPUT_BINARY,
+       SRD_OUTPUT_LOGIC,
        SRD_OUTPUT_META,
 };
 
@@ -188,6 +193,11 @@ struct srd_decoder {
         */
        GSList *binary;
 
+       /**
+        * List of logic output channels (item: id, description).
+        */
+       GSList *logic_output_channels;
+
        /** List of decoder options. */
        GSList *options;
 
@@ -232,6 +242,11 @@ struct srd_decoder_annotation_row {
        GSList *ann_classes;
 };
 
+struct srd_decoder_logic_output_channel {
+       char *id;
+       char *desc;
+};
+
 struct srd_decoder_inst {
        struct srd_decoder *decoder;
        struct srd_session *sess;
@@ -280,6 +295,9 @@ struct srd_decoder_inst {
        /** Requests termination of wait() and decode(). */
        gboolean want_wait_terminate;
 
+       /** Requests that .wait() terminates a Python iteration. */
+       gboolean communicate_eof;
+
        /** Indicates the current state of the decoder stack. */
        int decoder_state;
 
@@ -312,7 +330,12 @@ struct srd_proto_data_annotation {
 struct srd_proto_data_binary {
        int bin_class; /* Index into "struct srd_decoder"->binary. */
        uint64_t size;
-       const unsigned char *data;
+       const uint8_t *data;
+};
+struct srd_proto_data_logic {
+       int logic_group;
+       uint64_t repeat_count; /* Number of times the value in data was repeated. */
+       const uint8_t *data; /* Bitfield containing the states of the logic outputs */
 };
 
 typedef void (*srd_pd_output_callback)(struct srd_proto_data *pdata,
@@ -337,6 +360,7 @@ SRD_API int srd_session_metadata_set(struct srd_session *sess, int key,
 SRD_API int srd_session_send(struct srd_session *sess,
                uint64_t abs_start_samplenum, uint64_t abs_end_samplenum,
                const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize);
+SRD_API int srd_session_send_eof(struct srd_session *sess);
 SRD_API int srd_session_terminate_reset(struct srd_session *sess);
 SRD_API int srd_session_destroy(struct srd_session *sess);
 SRD_API int srd_pd_output_callback_add(struct srd_session *sess,
index 39b5b4392e479a5dfde3fa2985002b50f0e2384c..1fc0c77f85f8bc1883ad58aeb4ddd8580913538f 100644 (file)
@@ -63,6 +63,8 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void)
                goto err_out;
        if (PyModule_AddIntConstant(mod, "OUTPUT_BINARY", SRD_OUTPUT_BINARY) < 0)
                goto err_out;
+       if (PyModule_AddIntConstant(mod, "OUTPUT_LOGIC", SRD_OUTPUT_LOGIC) < 0)
+               goto err_out;
        if (PyModule_AddIntConstant(mod, "OUTPUT_META", SRD_OUTPUT_META) < 0)
                goto err_out;
        /* Expose meta input symbols. */
index 386fb710f5cb7a73506a15e5ea047a7b4f715cbb..ad084074889deee4367928d6b20d504fa15343ad 100644 (file)
--- a/session.c
+++ b/session.c
@@ -278,6 +278,32 @@ SRD_API int srd_session_send(struct srd_session *sess,
        return SRD_OK;
 }
 
+/**
+ * Communicate the end of the stream of sample data to the session.
+ *
+ * @param[in] sess The session. Must not be NULL.
+ *
+ * @return SRD_OK upon success. A (negative) error code otherwise.
+ *
+ * @since 0.6.0
+ */
+SRD_API int srd_session_send_eof(struct srd_session *sess)
+{
+       GSList *d;
+       int ret;
+
+       if (!sess)
+               return SRD_ERR_ARG;
+
+       for (d = sess->di_list; d; d = d->next) {
+               ret = srd_inst_send_eof(d->data);
+               if (ret != SRD_OK)
+                       return ret;
+       }
+
+       return SRD_OK;
+}
+
 /**
  * Terminate currently executing decoders in a session, reset internal state.
  *
diff --git a/srd.c b/srd.c
index 22f47fcf26a869ad8856e90b6f7f34bc76be5741..10dfaf6c1db5690dbc00917f580aba1adf08c176 100644 (file)
--- a/srd.c
+++ b/srd.c
@@ -67,8 +67,8 @@ extern SRD_PRIV int max_session_id;
  * @section sec_irc IRC
  *
  * You can find the sigrok developers in the
- * <a href="irc://chat.freenode.net/sigrok">\#sigrok</a>
- * IRC channel on Freenode.
+ * <a href="ircs://irc.libera.chat/#sigrok">\#sigrok</a>
+ * IRC channel on Libera.Chat.
  *
  * @section sec_website Website
  *
@@ -270,15 +270,46 @@ SRD_API int srd_init(const char *path)
        }
 
        /* Environment variable overrides everything, for debugging. */
+       /*
+        * TODO
+        * Is the comment still applicable and correct or up to date?
+        * This implementation adds paths which were specified by the
+        * env var. Which can shadow files in other locations, or can
+        * extend the set of available decoders. Which need not only
+        * serve for development, it is as beneficial to regular users.
+        * Without shadowing all other files still are found.
+        */
        if ((env_path = g_getenv("SIGROKDECODE_DIR"))) {
                if ((ret = srd_decoder_searchpath_add(env_path)) != SRD_OK) {
                        Py_Finalize();
                        return ret;
                }
        }
+       env_path = g_getenv("SIGROKDECODE_PATH");
+       if (env_path) {
+               char **dir_list, **dir_iter, *dir_item;
+               dir_list = g_strsplit(env_path, G_SEARCHPATH_SEPARATOR_S, 0);
+               for (dir_iter = dir_list; *dir_iter; dir_iter++) {
+                       dir_item = *dir_iter;
+                       if (!dir_item || !*dir_item)
+                               continue;
+                       ret = srd_decoder_searchpath_add(dir_item);
+                       if (ret != SRD_OK) {
+                               Py_Finalize();
+                               return ret;
+                       }
+               }
+               g_strfreev(dir_list);
+       }
 
-       /* Initialize the Python GIL (this also happens to acquire it). */
+#if PY_VERSION_HEX < 0x03090000
+       /*
+        * Initialize and acquire the Python GIL. In Python 3.7+ this
+        * will be done implicitly as part of the Py_InitializeEx()
+        * call above. PyEval_InitThreads() was deprecated in 3.9.
+        */
        PyEval_InitThreads();
+#endif
 
        /* Release the GIL (ignore return value, we don't need it here). */
        (void)PyEval_SaveThread();
index 1b378f1ac6e8b53d2e037310c60bd659923ee0b1..6932cdef49537eb37a3cd17b7f96d20ae844f1da 100644 (file)
@@ -33,10 +33,11 @@ typedef struct {
 /* This is only used for nicer srd_dbg() output. */
 SRD_PRIV const char *output_type_name(unsigned int idx)
 {
-       static const char names[][16] = {
+       static const char *names[] = {
                "OUTPUT_ANN",
                "OUTPUT_PYTHON",
                "OUTPUT_BINARY",
+               "OUTPUT_LOGIC",
                "OUTPUT_META",
                "(invalid)"
        };
@@ -66,16 +67,16 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
 
        /* Should be a list of [annotation class, [string, ...]]. */
        if (!PyList_Check(obj)) {
-               srd_err("Protocol decoder %s submitted an annotation that"
-                       " is not a list", di->decoder->name);
+               srd_err("Protocol decoder %s submitted an annotation that is not a list",
+                               di->decoder->name);
                goto err;
        }
 
        /* Should have 2 elements. */
        if (PyList_Size(obj) != 2) {
-               srd_err("Protocol decoder %s submitted annotation list with "
-                       "%zd elements instead of 2", di->decoder->name,
-                       PyList_Size(obj));
+               ssize_t sz = PyList_Size(obj);
+               srd_err("Protocol decoder %s submitted annotation list with %zd elements instead of 2",
+                               di->decoder->name, sz);
                goto err;
        }
 
@@ -85,27 +86,27 @@ static int convert_annotation(struct srd_decoder_inst *di, PyObject *obj,
         */
        py_tmp = PyList_GetItem(obj, 0);
        if (!PyLong_Check(py_tmp)) {
-               srd_err("Protocol decoder %s submitted annotation list, but "
-                       "first element was not an integer.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted annotation list, but first element was not an integer.",
+                               di->decoder->name);
                goto err;
        }
        ann_class = PyLong_AsLong(py_tmp);
        if (!(pdo = g_slist_nth_data(di->decoder->annotations, ann_class))) {
-               srd_err("Protocol decoder %s submitted data to unregistered "
-                       "annotation class %d.", di->decoder->name, ann_class);
+               srd_err("Protocol decoder %s submitted data to unregistered annotation class %d.",
+                               di->decoder->name, ann_class);
                goto err;
        }
 
        /* Second element must be a list. */
        py_tmp = PyList_GetItem(obj, 1);
        if (!PyList_Check(py_tmp)) {
-               srd_err("Protocol decoder %s submitted annotation list, but "
-                       "second element was not a list.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted annotation list, but second element was not a list.",
+                               di->decoder->name);
                goto err;
        }
        if (py_strseq_to_char(py_tmp, &ann_text) != SRD_OK) {
-               srd_err("Protocol decoder %s submitted annotation list, but "
-                       "second element was malformed.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted annotation list, but second element was malformed.",
+                               di->decoder->name);
                goto err;
        }
 
@@ -123,6 +124,89 @@ err:
        return SRD_ERR_PYTHON;
 }
 
+static void release_logic(struct srd_proto_data_logic *pdl)
+{
+       if (!pdl)
+               return;
+       g_free((void *)pdl->data);
+}
+
+static int convert_logic(struct srd_decoder_inst *di, PyObject *obj,
+               struct srd_proto_data *pdata)
+{
+       struct srd_proto_data_logic *pdl;
+       PyObject *py_tmp;
+       Py_ssize_t size;
+       int logic_group;
+       char *group_name, *buf;
+       PyGILState_STATE gstate;
+
+       gstate = PyGILState_Ensure();
+
+       /* Should be a list of [logic group, bytes]. */
+       if (!PyList_Check(obj)) {
+               srd_err("Protocol decoder %s submitted non-list for SRD_OUTPUT_LOGIC.",
+                       di->decoder->name);
+               goto err;
+       }
+
+       /* Should have 2 elements. */
+       if (PyList_Size(obj) != 2) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list "
+                               "with %zd elements instead of 2", di->decoder->name,
+                               PyList_Size(obj));
+               goto err;
+       }
+
+       /* The first element should be an integer. */
+       py_tmp = PyList_GetItem(obj, 0);
+       if (!PyLong_Check(py_tmp)) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, "
+                       "but first element was not an integer.", di->decoder->name);
+               goto err;
+       }
+       logic_group = PyLong_AsLong(py_tmp);
+       if (!(group_name = g_slist_nth_data(di->decoder->logic_output_channels, logic_group))) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC with "
+                       "unregistered logic group %d.", di->decoder->name, logic_group);
+               goto err;
+       }
+
+       /* Second element should be bytes. */
+       py_tmp = PyList_GetItem(obj, 1);
+       if (!PyBytes_Check(py_tmp)) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC list, "
+                       "but second element was not bytes.", di->decoder->name);
+               goto err;
+       }
+
+       /* Consider an empty set of bytes a bug. */
+       if (PyBytes_Size(py_tmp) == 0) {
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_LOGIC "
+                               "with empty data set.", di->decoder->name);
+               goto err;
+       }
+
+       if (PyBytes_AsStringAndSize(py_tmp, &buf, &size) == -1)
+               goto err;
+
+       PyGILState_Release(gstate);
+
+       pdl = pdata->data;
+       pdl->logic_group = logic_group;
+       /* pdl->repeat_count is set by the caller as it depends on the sample range */
+       if (!(pdl->data = g_try_malloc(size)))
+               return SRD_ERR_MALLOC;
+       memcpy((void *)pdl->data, (const void *)buf, size);
+
+       return SRD_OK;
+
+err:
+       PyGILState_Release(gstate);
+
+       return SRD_ERR_PYTHON;
+}
+
 static void release_binary(struct srd_proto_data_binary *pdb)
 {
        if (!pdb)
@@ -151,38 +235,38 @@ static int convert_binary(struct srd_decoder_inst *di, PyObject *obj,
 
        /* Should have 2 elements. */
        if (PyList_Size(obj) != 2) {
-               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list "
-                               "with %zd elements instead of 2", di->decoder->name,
-                               PyList_Size(obj));
+               ssize_t sz = PyList_Size(obj);
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list with %zd elements instead of 2",
+                               di->decoder->name, sz);
                goto err;
        }
 
        /* The first element should be an integer. */
        py_tmp = PyList_GetItem(obj, 0);
        if (!PyLong_Check(py_tmp)) {
-               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
-                       "but first element was not an integer.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, but first element was not an integer.",
+                               di->decoder->name);
                goto err;
        }
        bin_class = PyLong_AsLong(py_tmp);
        if (!(class_name = g_slist_nth_data(di->decoder->binary, bin_class))) {
-               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with "
-                       "unregistered binary class %d.", di->decoder->name, bin_class);
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with unregistered binary class %d.",
+                               di->decoder->name, bin_class);
                goto err;
        }
 
        /* Second element should be bytes. */
        py_tmp = PyList_GetItem(obj, 1);
        if (!PyBytes_Check(py_tmp)) {
-               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, "
-                       "but second element was not bytes.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY list, but second element was not bytes.",
+                               di->decoder->name);
                goto err;
        }
 
        /* Consider an empty set of bytes a bug. */
        if (PyBytes_Size(py_tmp) == 0) {
-               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY "
-                               "with empty data set.", di->decoder->name);
+               srd_err("Protocol decoder %s submitted SRD_OUTPUT_BINARY with empty data set.",
+                               di->decoder->name);
                goto err;
        }
 
@@ -275,8 +359,8 @@ static int convert_meta(struct srd_proto_data *pdata, PyObject *obj)
 
        if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_INT64)) {
                if (!PyLong_Check(obj)) {
-                       PyErr_Format(PyExc_TypeError, "This output was registered "
-                                       "as 'int', but something else was passed.");
+                       PyErr_Format(PyExc_TypeError,
+                               "This output was registered as 'int', but something else was passed.");
                        goto err;
                }
                intvalue = PyLong_AsLongLong(obj);
@@ -285,8 +369,8 @@ static int convert_meta(struct srd_proto_data *pdata, PyObject *obj)
                pdata->data = g_variant_new_int64(intvalue);
        } else if (g_variant_type_equal(pdata->pdo->meta_type, G_VARIANT_TYPE_DOUBLE)) {
                if (!PyFloat_Check(obj)) {
-                       PyErr_Format(PyExc_TypeError, "This output was registered "
-                                       "as 'float', but something else was passed.");
+                       PyErr_Format(PyExc_TypeError,
+                               "This output was registered as 'float', but something else was passed.");
                        goto err;
                }
                dvalue = PyFloat_AsDouble(obj);
@@ -312,6 +396,13 @@ static void release_meta(GVariant *gvar)
        g_variant_unref(gvar);
 }
 
+PyDoc_STRVAR(Decoder_put_doc,
+       "Put an annotation for the specified span of samples.\n"
+       "\n"
+       "Arguments: start and end sample number, stream id, annotation data.\n"
+       "Annotation data's layout depends on the output stream type."
+);
+
 static PyObject *Decoder_put(PyObject *self, PyObject *args)
 {
        GSList *l;
@@ -321,6 +412,7 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
        struct srd_proto_data pdata;
        struct srd_proto_data_annotation pda;
        struct srd_proto_data_binary pdb;
+       struct srd_proto_data_logic pdl;
        uint64_t start_sample, end_sample;
        int output_id;
        struct srd_pd_callback *cb;
@@ -390,9 +482,9 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                                 start_sample,
                                 end_sample, output_type_name(pdo->output_type),
                                 output_id, pdo->proto_id, next_di->inst_id);
-                       if (!(py_res = PyObject_CallMethod(
-                               next_di->py_inst, "decode", "KKO", start_sample,
-                               end_sample, py_data))) {
+                       py_res = PyObject_CallMethod(next_di->py_inst, "decode",
+                               "KKO", start_sample, end_sample, py_data);
+                       if (!py_res) {
                                srd_exception_catch("Calling %s decode() failed",
                                                        next_di->inst_id);
                        }
@@ -421,6 +513,25 @@ static PyObject *Decoder_put(PyObject *self, PyObject *args)
                        release_binary(pdata.data);
                }
                break;
+       case SRD_OUTPUT_LOGIC:
+               if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
+                       pdata.data = &pdl;
+                       /* Convert from PyDict to srd_proto_data_logic. */
+                       if (convert_logic(di, py_data, &pdata) != SRD_OK) {
+                               /* An error was already logged. */
+                               break;
+                       }
+                       if (end_sample <= start_sample) {
+                               srd_err("Ignored SRD_OUTPUT_LOGIC with invalid sample range.");
+                               break;
+                       }
+                       pdl.repeat_count = (end_sample - start_sample) - 1;
+                       Py_BEGIN_ALLOW_THREADS
+                       cb->cb(&pdata, cb->cb_data);
+                       Py_END_ALLOW_THREADS
+                       release_logic(pdata.data);
+               }
+               break;
        case SRD_OUTPUT_META:
                if ((cb = srd_pd_output_callback_find(di->sess, pdo->output_type))) {
                        /* Annotations need converting from PyObject. */
@@ -450,8 +561,12 @@ err:
        return NULL;
 }
 
-static PyObject *Decoder_register(PyObject *self, PyObject *args,
-               PyObject *kwargs)
+PyDoc_STRVAR(Decoder_register_doc,
+       "Register a new output stream."
+);
+
+static PyObject *Decoder_register(PyObject *self,
+       PyObject *args, PyObject *kwargs)
 {
        struct srd_decoder_inst *di;
        struct srd_pd_output *pdo;
@@ -848,6 +963,23 @@ static int set_skip_condition(struct srd_decoder_inst *di, uint64_t count)
        return SRD_OK;
 }
 
+PyDoc_STRVAR(Decoder_wait_doc,
+       "Wait for one or more conditions to occur.\n"
+       "\n"
+       "Returns the sample data at the next position where the condition\n"
+       "is seen. When the optional condition is missing or empty, the next\n"
+       "sample number is used. The condition can be a dictionary with one\n"
+       "condition's details, or a list of dictionaries specifying multiple\n"
+       "conditions of which at least one condition must be true. Dicts can\n"
+       "contain one or more key/value pairs, all of which must be true for\n"
+       "the dict's condition to be considered true. The key either is a\n"
+       "channel index or a keyword, the value is the operation's parameter.\n"
+       "\n"
+       "Supported parameters for channel number keys: 'h', 'l', 'r', 'f',\n"
+       "or 'e' for level or edge conditions. Other supported keywords:\n"
+       "'skip' to advance over the given number of samples.\n"
+);
+
 static PyObject *Decoder_wait(PyObject *self, PyObject *args)
 {
        int ret;
@@ -957,6 +1089,27 @@ static PyObject *Decoder_wait(PyObject *self, PyObject *args)
                /* Signal the main thread that we handled all samples. */
                g_cond_signal(&di->handled_all_samples_cond);
 
+               /*
+                * When EOF was provided externally, communicate the
+                * Python EOFError exception to .decode() and return
+                * from the .wait() method call. This is motivated by
+                * the use of Python context managers, so that .decode()
+                * methods can "close" incompletely accumulated data
+                * when the sample data is exhausted.
+                */
+               if (di->communicate_eof) {
+                       /* Advance self.samplenum to the (absolute) last sample number. */
+                       py_samplenum = PyLong_FromUnsignedLongLong(di->abs_cur_samplenum);
+                       PyObject_SetAttrString(di->py_inst, "samplenum", py_samplenum);
+                       Py_DECREF(py_samplenum);
+                       /* Raise an EOFError Python exception. */
+                       srd_dbg("%s: %s: Raising EOF from wait().",
+                               di->inst_id, __func__);
+                       g_mutex_unlock(&di->data_mutex);
+                       PyErr_SetString(PyExc_EOFError, "samples exhausted");
+                       goto err;
+               }
+
                /*
                 * When termination of wait() and decode() was requested,
                 * then exit the loop after releasing the mutex.
@@ -981,6 +1134,14 @@ err:
        return NULL;
 }
 
+PyDoc_STRVAR(Decoder_has_channel_doc,
+       "Check whether input data is supplied for a given channel.\n"
+       "\n"
+       "Argument: A channel index.\n"
+       "Returns: A boolean, True if the channel is connected,\n"
+       "False if the channel is open (won't see any input data).\n"
+);
+
 /**
  * Return whether the specified channel was supplied to the decoder.
  *
@@ -996,6 +1157,7 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args)
        int idx, count;
        struct srd_decoder_inst *di;
        PyGILState_STATE gstate;
+       PyObject *bool_ret;
 
        if (!self || !args)
                return NULL;
@@ -1026,7 +1188,9 @@ static PyObject *Decoder_has_channel(PyObject *self, PyObject *args)
 
        PyGILState_Release(gstate);
 
-       return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+       bool_ret = (di->dec_channelmap[idx] == -1) ? Py_False : Py_True;
+       Py_INCREF(bool_ret);
+       return bool_ret;
 
 err:
        PyGILState_Release(gstate);
@@ -1034,16 +1198,26 @@ err:
        return NULL;
 }
 
+PyDoc_STRVAR(Decoder_doc, "sigrok Decoder base class");
+
 static PyMethodDef Decoder_methods[] = {
-       { "put", Decoder_put, METH_VARARGS,
-         "Accepts a dictionary with the following keys: startsample, endsample, data" },
-       { "register", (PyCFunction)(void(*)(void))Decoder_register, METH_VARARGS|METH_KEYWORDS,
-                       "Register a new output stream" },
-       { "wait", Decoder_wait, METH_VARARGS,
-                       "Wait for one or more conditions to occur" },
-       { "has_channel", Decoder_has_channel, METH_VARARGS,
-                       "Report whether a channel was supplied" },
-       {NULL, NULL, 0, NULL}
+       { "put",
+         Decoder_put, METH_VARARGS,
+         Decoder_put_doc,
+       },
+       { "register",
+         (PyCFunction)(void(*)(void))Decoder_register, METH_VARARGS | METH_KEYWORDS,
+         Decoder_register_doc,
+       },
+       { "wait",
+         Decoder_wait, METH_VARARGS,
+         Decoder_wait_doc,
+       },
+       { "has_channel",
+         Decoder_has_channel, METH_VARARGS,
+         Decoder_has_channel_doc,
+       },
+       ALL_ZERO,
 };
 
 /**
@@ -1057,10 +1231,10 @@ SRD_PRIV PyObject *srd_Decoder_type_new(void)
 {
        PyType_Spec spec;
        PyType_Slot slots[] = {
-               { Py_tp_doc, "sigrok Decoder base class" },
+               { Py_tp_doc, Decoder_doc },
                { Py_tp_methods, Decoder_methods },
                { Py_tp_new, (void *)&PyType_GenericNew },
-               { 0, NULL }
+               ALL_ZERO,
        };
        PyObject *py_obj;
        PyGILState_STATE gstate;
diff --git a/util.c b/util.c
index 1e914e3e44a1ee0ae2b0d735cd87f29345f66551..3a5e336a83e7be2db601e660a4ad6c3149b872e6 100644 (file)
--- a/util.c
+++ b/util.c
@@ -115,7 +115,7 @@ err:
 SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **outstrlist)
 {
        PyObject *py_list;
-       Py_ssize_t i;
+       ssize_t idx;
        int ret;
        char *outstr;
        PyGILState_STATE gstate;
@@ -139,10 +139,10 @@ SRD_PRIV int py_attr_as_strlist(PyObject *py_obj, const char *attr, GSList **out
 
        *outstrlist = NULL;
 
-       for (i = 0; i < PyList_Size(py_list); i++) {
-               ret = py_listitem_as_str(py_list, i, &outstr);
+       for (idx = 0; idx < PyList_Size(py_list); idx++) {
+               ret = py_listitem_as_str(py_list, idx, &outstr);
                if (ret < 0) {
-                       srd_dbg("Couldn't get item %" PY_FORMAT_SIZE_T "d.", i);
+                       srd_dbg("Couldn't get item %zd.", idx);
                        goto err;
                }
                *outstrlist = g_slist_append(*outstrlist, outstr);
@@ -217,8 +217,9 @@ err:
 SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx,
                                char **outstr)
 {
-       PyObject *py_value;
        PyGILState_STATE gstate;
+       ssize_t item_idx;
+       PyObject *py_value;
 
        gstate = PyGILState_Ensure();
 
@@ -227,8 +228,9 @@ SRD_PRIV int py_listitem_as_str(PyObject *py_obj, Py_ssize_t idx,
                goto err;
        }
 
-       if (!(py_value = PyList_GetItem(py_obj, idx))) {
-               srd_dbg("Couldn't get list item %" PY_FORMAT_SIZE_T "d.", idx);
+       item_idx = idx;
+       if (!(py_value = PyList_GetItem(py_obj, item_idx))) {
+               srd_dbg("Couldn't get list item %zd.", item_idx);
                goto err;
        }