From 1497821b2ef2159bf83b3c8f859b7dca5f060907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Millet?= Date: Fri, 4 Jun 2021 11:57:28 +0200 Subject: [PATCH] Initial Upload --- .gitignore | 3 + Debug.cpp | 63 + Debug.h | 40 + LICENSE | 674 +++++++ Makefile | 10 + README.md | 90 + RF433any.cpp | 1676 +++++++++++++++++ RF433any.h | 660 +++++++ Serial.cpp | 103 + Serial.h | 48 + examples/output-signal-timings/Makefile | 10 + examples/output-signal-timings/am | 343 ++++ .../output-signal-timings.ino | 102 + library.properties | 10 + schema.fzz | Bin 0 -> 15867 bytes schema.png | Bin 0 -> 126848 bytes testplan/clean.sh | 22 + testplan/decoder/01/code.txt | 12 + testplan/decoder/01/expect3.txt | 2 + testplan/decoder/01/expect4.txt | 2 + testplan/decoder/02/code.txt | 12 + testplan/decoder/02/expect3.txt | 3 + testplan/decoder/02/expect4.txt | 3 + testplan/decoder/03/code.txt | 69 + testplan/decoder/03/expect3.txt | 4 + testplan/decoder/03/expect4.txt | 4 + testplan/decoder/10/code-adf7.txt | 69 + testplan/decoder/10/expect3.txt | 4 + testplan/decoder/10/expect4.txt | 6 + testplan/decoder/11/code-adf8.txt | 69 + testplan/decoder/11/expect3.txt | 4 + testplan/decoder/11/expect4.txt | 6 + testplan/decoder/12/code-adfF.txt | 69 + testplan/decoder/12/expect3.txt | 4 + testplan/decoder/12/expect4.txt | 6 + testplan/decoder/13/code-adf7-err.txt | 69 + testplan/decoder/13/expect3.txt | 5 + testplan/decoder/13/expect4.txt | 6 + testplan/decoder/20/code-portail1.txt | 69 + testplan/decoder/20/expect3.txt | 4 + testplan/decoder/20/expect4.txt | 4 + testplan/decoder/21/code-portail2.txt | 69 + testplan/decoder/21/expect3.txt | 4 + testplan/decoder/21/expect4.txt | 4 + testplan/decoder/22/code-portx.txt | 70 + testplan/decoder/22/expect3.txt | 8 + testplan/decoder/22/expect4.txt | 8 + testplan/decoder/23/code-porty.txt | 70 + testplan/decoder/23/expect3.txt | 8 + testplan/decoder/23/expect4.txt | 8 + testplan/decoder/24/code-portail1-err.txt | 69 + testplan/decoder/24/expect3.txt | 5 + testplan/decoder/24/expect4.txt | 4 + testplan/decoder/25/code-portail1-err2.txt | 55 + testplan/decoder/25/expect3.txt | 7 + testplan/decoder/25/expect4.txt | 7 + testplan/decoder/30/code-sonoff1.txt | 69 + testplan/decoder/30/expect3.txt | 4 + testplan/decoder/30/expect4.txt | 6 + testplan/decoder/31/code-sonoff2.txt | 69 + testplan/decoder/31/expect3.txt | 4 + testplan/decoder/31/expect4.txt | 6 + testplan/decoder/32/code-sonoff1-err.txt | 69 + testplan/decoder/32/expect3.txt | 5 + testplan/decoder/32/expect4.txt | 6 + testplan/decoder/40/code-flo1.txt | 53 + testplan/decoder/40/expect3.txt | 8 + testplan/decoder/40/expect4.txt | 8 + testplan/decoder/41/code-flo2.txt | 60 + testplan/decoder/41/expect3.txt | 8 + testplan/decoder/41/expect4.txt | 8 + testplan/decoder/42/code-flo1-err.txt | 53 + testplan/decoder/42/expect3.txt | 10 + testplan/decoder/42/expect4.txt | 9 + testplan/decoder/50/code-portaila.txt | 70 + testplan/decoder/50/expect3.txt | 4 + testplan/decoder/50/expect4.txt | 4 + testplan/decoder/51/code-portailb.txt | 66 + testplan/decoder/51/expect3.txt | 4 + testplan/decoder/51/expect4.txt | 4 + testplan/decoder/52/code-portailc.txt | 67 + testplan/decoder/52/expect3.txt | 4 + testplan/decoder/52/expect4.txt | 4 + testplan/decoder/53/code-portaild.txt | 66 + testplan/decoder/53/expect3.txt | 4 + testplan/decoder/53/expect4.txt | 4 + testplan/decoder/54/code-portaile.txt | 67 + testplan/decoder/54/expect3.txt | 4 + testplan/decoder/54/expect4.txt | 4 + testplan/decoder/55/code-portailf.txt | 68 + testplan/decoder/55/expect3.txt | 4 + testplan/decoder/55/expect4.txt | 4 + testplan/decoder/56/code-portailg.txt | 68 + testplan/decoder/56/expect3.txt | 4 + testplan/decoder/56/expect4.txt | 4 + testplan/decoder/57/code-portailh.txt | 69 + testplan/decoder/57/expect3.txt | 4 + testplan/decoder/57/expect4.txt | 4 + testplan/exectest.sh | 25 + testplan/read_test_result_from_board.sh | 44 + testplan/test/Makefile | 10 + testplan/test/am | 345 ++++ testplan/test/test.ino | 177 ++ testplan/track/01/code-simple.txt | 35 + testplan/track/01/expect1.txt | 18 + testplan/track/01/expect2.txt | 6 + testplan/track/02/code-err.txt | 12 + testplan/track/02/expect1.txt | 18 + testplan/track/02/expect2.txt | 6 + testplan/track/03/code-err1.txt | 13 + testplan/track/03/expect1.txt | 18 + testplan/track/03/expect2.txt | 6 + testplan/track/04/code-errn.txt | 62 + testplan/track/04/expect1.txt | 86 + testplan/track/04/expect2.txt | 12 + testplan/track/05/code-err.txt | 11 + testplan/track/05/expect1.txt | 2 + testplan/track/05/expect2.txt | 0 testplan/track/06/code-1 | 13 + testplan/track/06/expect1.txt | 18 + testplan/track/06/expect2.txt | 6 + testplan/track/07/code-2 | 15 + testplan/track/07/expect1.txt | 18 + testplan/track/07/expect2.txt | 6 + testplan/track/08/code-2 | 15 + testplan/track/08/expect1.txt | 18 + testplan/track/08/expect2.txt | 0 testplan/track/09/code-flo.txt | 69 + testplan/track/09/expect1.txt | 82 + testplan/track/09/expect2.txt | 22 + testplan/track/10/code-portail.txt | 69 + testplan/track/10/expect1.txt | 50 + testplan/track/10/expect2.txt | 10 + testplan/track/11/code-adf.txt | 57 + testplan/track/11/expect1.txt | 34 + testplan/track/11/expect2.txt | 10 + testplan/track/12/code-adf.txt | 56 + testplan/track/12/expect1.txt | 34 + testplan/track/12/expect2.txt | 10 + testplan/track/13/code-adf.txt | 55 + testplan/track/13/expect1.txt | 34 + testplan/track/13/expect2.txt | 10 + testplan/track/14/code-adf.txt | 54 + testplan/track/14/expect1.txt | 34 + testplan/track/14/expect2.txt | 10 + testplan/tt.sh | 94 + testplan/user/01/code-1.txt | 53 + testplan/user/01/expect5.txt | 8 + testplan/user/02/code-1.txt | 53 + testplan/user/02/expect5.txt | 6 + 150 files changed, 7744 insertions(+) create mode 100644 .gitignore create mode 100644 Debug.cpp create mode 100644 Debug.h create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 RF433any.cpp create mode 100644 RF433any.h create mode 100644 Serial.cpp create mode 100644 Serial.h create mode 100644 examples/output-signal-timings/Makefile create mode 100755 examples/output-signal-timings/am create mode 100644 examples/output-signal-timings/output-signal-timings.ino create mode 100644 library.properties create mode 100644 schema.fzz create mode 100644 schema.png create mode 100755 testplan/clean.sh create mode 100644 testplan/decoder/01/code.txt create mode 100644 testplan/decoder/01/expect3.txt create mode 100644 testplan/decoder/01/expect4.txt create mode 100644 testplan/decoder/02/code.txt create mode 100644 testplan/decoder/02/expect3.txt create mode 100644 testplan/decoder/02/expect4.txt create mode 100644 testplan/decoder/03/code.txt create mode 100644 testplan/decoder/03/expect3.txt create mode 100644 testplan/decoder/03/expect4.txt create mode 100644 testplan/decoder/10/code-adf7.txt create mode 100644 testplan/decoder/10/expect3.txt create mode 100644 testplan/decoder/10/expect4.txt create mode 100644 testplan/decoder/11/code-adf8.txt create mode 100644 testplan/decoder/11/expect3.txt create mode 100644 testplan/decoder/11/expect4.txt create mode 100644 testplan/decoder/12/code-adfF.txt create mode 100644 testplan/decoder/12/expect3.txt create mode 100644 testplan/decoder/12/expect4.txt create mode 100644 testplan/decoder/13/code-adf7-err.txt create mode 100644 testplan/decoder/13/expect3.txt create mode 100644 testplan/decoder/13/expect4.txt create mode 100644 testplan/decoder/20/code-portail1.txt create mode 100644 testplan/decoder/20/expect3.txt create mode 100644 testplan/decoder/20/expect4.txt create mode 100644 testplan/decoder/21/code-portail2.txt create mode 100644 testplan/decoder/21/expect3.txt create mode 100644 testplan/decoder/21/expect4.txt create mode 100644 testplan/decoder/22/code-portx.txt create mode 100644 testplan/decoder/22/expect3.txt create mode 100644 testplan/decoder/22/expect4.txt create mode 100644 testplan/decoder/23/code-porty.txt create mode 100644 testplan/decoder/23/expect3.txt create mode 100644 testplan/decoder/23/expect4.txt create mode 100644 testplan/decoder/24/code-portail1-err.txt create mode 100644 testplan/decoder/24/expect3.txt create mode 100644 testplan/decoder/24/expect4.txt create mode 100644 testplan/decoder/25/code-portail1-err2.txt create mode 100644 testplan/decoder/25/expect3.txt create mode 100644 testplan/decoder/25/expect4.txt create mode 100644 testplan/decoder/30/code-sonoff1.txt create mode 100644 testplan/decoder/30/expect3.txt create mode 100644 testplan/decoder/30/expect4.txt create mode 100644 testplan/decoder/31/code-sonoff2.txt create mode 100644 testplan/decoder/31/expect3.txt create mode 100644 testplan/decoder/31/expect4.txt create mode 100644 testplan/decoder/32/code-sonoff1-err.txt create mode 100644 testplan/decoder/32/expect3.txt create mode 100644 testplan/decoder/32/expect4.txt create mode 100644 testplan/decoder/40/code-flo1.txt create mode 100644 testplan/decoder/40/expect3.txt create mode 100644 testplan/decoder/40/expect4.txt create mode 100644 testplan/decoder/41/code-flo2.txt create mode 100644 testplan/decoder/41/expect3.txt create mode 100644 testplan/decoder/41/expect4.txt create mode 100644 testplan/decoder/42/code-flo1-err.txt create mode 100644 testplan/decoder/42/expect3.txt create mode 100644 testplan/decoder/42/expect4.txt create mode 100644 testplan/decoder/50/code-portaila.txt create mode 100644 testplan/decoder/50/expect3.txt create mode 100644 testplan/decoder/50/expect4.txt create mode 100644 testplan/decoder/51/code-portailb.txt create mode 100644 testplan/decoder/51/expect3.txt create mode 100644 testplan/decoder/51/expect4.txt create mode 100644 testplan/decoder/52/code-portailc.txt create mode 100644 testplan/decoder/52/expect3.txt create mode 100644 testplan/decoder/52/expect4.txt create mode 100644 testplan/decoder/53/code-portaild.txt create mode 100644 testplan/decoder/53/expect3.txt create mode 100644 testplan/decoder/53/expect4.txt create mode 100644 testplan/decoder/54/code-portaile.txt create mode 100644 testplan/decoder/54/expect3.txt create mode 100644 testplan/decoder/54/expect4.txt create mode 100644 testplan/decoder/55/code-portailf.txt create mode 100644 testplan/decoder/55/expect3.txt create mode 100644 testplan/decoder/55/expect4.txt create mode 100644 testplan/decoder/56/code-portailg.txt create mode 100644 testplan/decoder/56/expect3.txt create mode 100644 testplan/decoder/56/expect4.txt create mode 100644 testplan/decoder/57/code-portailh.txt create mode 100644 testplan/decoder/57/expect3.txt create mode 100644 testplan/decoder/57/expect4.txt create mode 100755 testplan/exectest.sh create mode 100755 testplan/read_test_result_from_board.sh create mode 100644 testplan/test/Makefile create mode 100755 testplan/test/am create mode 100644 testplan/test/test.ino create mode 100644 testplan/track/01/code-simple.txt create mode 100644 testplan/track/01/expect1.txt create mode 100644 testplan/track/01/expect2.txt create mode 100644 testplan/track/02/code-err.txt create mode 100644 testplan/track/02/expect1.txt create mode 100644 testplan/track/02/expect2.txt create mode 100644 testplan/track/03/code-err1.txt create mode 100644 testplan/track/03/expect1.txt create mode 100644 testplan/track/03/expect2.txt create mode 100644 testplan/track/04/code-errn.txt create mode 100644 testplan/track/04/expect1.txt create mode 100644 testplan/track/04/expect2.txt create mode 100644 testplan/track/05/code-err.txt create mode 100644 testplan/track/05/expect1.txt create mode 100644 testplan/track/05/expect2.txt create mode 100644 testplan/track/06/code-1 create mode 100644 testplan/track/06/expect1.txt create mode 100644 testplan/track/06/expect2.txt create mode 100644 testplan/track/07/code-2 create mode 100644 testplan/track/07/expect1.txt create mode 100644 testplan/track/07/expect2.txt create mode 100644 testplan/track/08/code-2 create mode 100644 testplan/track/08/expect1.txt create mode 100644 testplan/track/08/expect2.txt create mode 100644 testplan/track/09/code-flo.txt create mode 100644 testplan/track/09/expect1.txt create mode 100644 testplan/track/09/expect2.txt create mode 100644 testplan/track/10/code-portail.txt create mode 100644 testplan/track/10/expect1.txt create mode 100644 testplan/track/10/expect2.txt create mode 100644 testplan/track/11/code-adf.txt create mode 100644 testplan/track/11/expect1.txt create mode 100644 testplan/track/11/expect2.txt create mode 100644 testplan/track/12/code-adf.txt create mode 100644 testplan/track/12/expect1.txt create mode 100644 testplan/track/12/expect2.txt create mode 100644 testplan/track/13/code-adf.txt create mode 100644 testplan/track/13/expect1.txt create mode 100644 testplan/track/13/expect2.txt create mode 100644 testplan/track/14/code-adf.txt create mode 100644 testplan/track/14/expect1.txt create mode 100644 testplan/track/14/expect2.txt create mode 100755 testplan/tt.sh create mode 100644 testplan/user/01/code-1.txt create mode 100644 testplan/user/01/expect5.txt create mode 100644 testplan/user/02/code-1.txt create mode 100644 testplan/user/02/expect5.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36a8603 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build +build-uno +tmpout* diff --git a/Debug.cpp b/Debug.cpp new file mode 100644 index 0000000..1174f14 --- /dev/null +++ b/Debug.cpp @@ -0,0 +1,63 @@ +// Debug.cpp + +/* + Provides some useful functions to output debug from Arduino on the serial + line. + Used and tested with an Arduino nano. +*/ + +/* + Copyright 2021 Sébastien Millet + + `rf433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `rf433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +#include "Debug.h" +#include +#include + +// What items to include in the debug lines: + +static char buffer[150]; +static char progmem_reading_buffer[100]; + +#ifdef __arm__ +// should use uinstd.h to define sbrk but Due causes a conflict +extern "C" char* sbrk(int incr); +#else // __ARM__ +extern char *__brkval; +#endif // __arm__ + +void dbgfunc(const char* file, long int line, const char* progmem_str) { + strcpy_P(progmem_reading_buffer, progmem_str); + Serial.print(progmem_reading_buffer); + Serial.print("\n"); +} + +void dbgffunc(const char* file, long int line, const char* progmem_fmt, ...) { + strcpy_P(progmem_reading_buffer, progmem_fmt); + va_list args; + + // FIXME +#pragma GCC diagnostic ignored "-Wvarargs" + va_start(args, progmem_reading_buffer); + + vsnprintf(buffer, sizeof(buffer), progmem_reading_buffer, args); + va_end(args); + Serial.print(buffer); + Serial.print("\n"); +} + +// vim: ts=4:sw=4:tw=80:et diff --git a/Debug.h b/Debug.h new file mode 100644 index 0000000..b47e7b1 --- /dev/null +++ b/Debug.h @@ -0,0 +1,40 @@ +// Debug.h + +/* + Copyright 2021 Sébastien Millet + + `rf433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `rf433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +#ifndef _DEBUG_H +#define _DEBUG_H + +#define dbg(a) \ + { static const char tmp[] PROGMEM = {a}; \ + dbgfunc(__FILE__, __LINE__, tmp); \ + } + +#define dbgf(a, ...) \ + { static const char tmp[] PROGMEM = {a}; \ + dbgffunc(__FILE__, __LINE__, tmp, __VA_ARGS__); \ + } + +void dbgfunc(const char* file, long int line, const char *msg); +void dbgffunc(const char* file, long int line, const char *format, ...) + __attribute__((format(printf, 3, 4))); + +#endif // _DEBUG_H + +// vim: ts=4:sw=4:tw=80:et diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..87a2a68 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +ARDUINO_DIR = /usr/share/arduino +ARDUINO_LIBS = +ARDMK_DIR = /home/sebastien/.arduino_mk + +# USER_LIB_PATH = /home/sebastien/travail/cpp/seb/arduino/libraries + +BOARD_TAG = uno +MCU = atmega328 + +include $(ARDMK_DIR)/Arduino.mk diff --git a/README.md b/README.md new file mode 100644 index 0000000..de72316 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +RF433any +======== + +Uses a RF433Mhz component plugged on an Arduino to listen to signals and decode +it. + + +Installation +------------ + +Download a zip of this repository, then include it from the Arduino IDE. + + +Schematic +--------- + +1. Arduino board. Tested with NANO and UNO. + +2. Radio Frequence 433Mhz RECEIVER like MX-RM-5V. + +RF433 RECEIVER data pin must be plugged on a board' digital PIN that can +trigger interrupts, that is, D2 or D3. + +This RECEIVER PIN is defined at the time a 'Track' object is created. This +library does not set it at compile time. + +See file schema.fzz (Fritzing format) or schema.png, for a circuit example with +receiver plugged on D2. + + +Usage +----- + +See [examples/output-signal-timings/output-signal-timings.ino](examples/output-signal-timings/output-signal-timings.ino) for an example. + + +More details +------------ + +The library assumes one of the following auto-synchronization protocols (tiret +for high signal, underscore for low radio signal): + + Tri-bit __- versus _-- + + Tri-bit inverted -__ versus --_ + + Manchester _- versus -_ + +The decoder tries to be as flexible as possible to decode any protocols, +without pre-knowledge about signal timings. To be generic enough, only the +_relationships_ between timings is analyzed, to deduct the 'short' and 'long' +duration on 'low' and 'high' radio frequence signal value. No pre-defined +timing is used. + +Most often, 'long' is twice as long as 'short', and the durations on the low +signal are the same as on the high signal, but this library doesn't assume it. +'long' are not necessarily twice as long as 'short', and the high signal +timings can be totally different from the low signal timings. + +The signal can also contain the below: + +* A 'prefix' made of a first succession of 'low, high' durations that don't +match short and long durations encountered later. Such a prefix has been seen +on FLO/R telecommands, likely, to distinguish between FLO (fixed code) and +FLO/R (rolling code). + +* A 'sync' prefix made of a succession of low and high of the same duration. +Note such a prefix could be regarded as Manchester encoding of as many '0' bits +(when using CONVENTION_0, see below). The library assumes that such a sequence, +if seen at the beginning ('short' and 'long' durations are not yet known), +corresponds to a synchronization prefix, not to a Manchester encoding of '0' +bits. + +The signal decoding can be done using two conventions. +Switching from one convention to another for the same signal will simply invert +bit values. + + +Bit value depending on convention +--------------------------------- + +| | Signal shape | CONVENTION_0 | CONVENTION_1 | +| ---------------- | --------------------- | ------------ | ------------ | +| Tri-bit | low short, high long | 0 | 1 | +| Tri-bit | low long, high short | 1 | 0 | +| Tri-bit Inverted | high short, low long | 0 | 1 | +| Tri-bit Inverted | high long, low short | 1 | 0 | +| Manchester | low short, high short | 0 | 1 | +| Manchester | high short, low short | 1 | 0 | + diff --git a/RF433any.cpp b/RF433any.cpp new file mode 100644 index 0000000..e98a30f --- /dev/null +++ b/RF433any.cpp @@ -0,0 +1,1676 @@ +// RF433any.cpp + +// See README.md about the purpose of this library + +/* + Copyright 2021 Sébastien Millet + + `RF433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `RF433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +/* + Schematic + + 1. Arduino board. Tested with NANO and UNO. + 2. Radio Frequence 433Mhz RECEIVER like MX-RM-5V. + + RF433 RECEIVER data pin must be plugged on a board' digital PIN that can + trigger interrupts, that is, D2 or D3. + This RECEIVER PIN is defined at the time a 'Track' object is created. This + library does not set it at compile time. + See file schema.fzz (Fritzing format) or schema.png, for a circuit example + with receiver plugged on D2. +*/ + +/* + +**About the classes Band, Rail and Track** + +1. About none of these - the signal as we see it. + +The Radio-Frequence signal is supposed to be OOK (On-Off Keying), and +auto-synchronized. + +The signal is a succession of low signal and high signal, low when no RF signal +received, high when a RF signal is received. +The coding relies on durations being either 'short' or 'long', and sometimes +much longer (to initialize, and to separate signal pieces). + +The durations can be one of: + - short + - long, typically, twice as long as short + - separator, much longer than the long one (at least 3 or 4 times longer) + - initialization, at least as long as the separator, often much longer. It + serves to make receiver ready to receive coded signal to come. + +A signal structure is as follows: + 1. Initialization (very long high signal) + 2. Succession of low and high signals being 'short' or 'long' + 3. Separator (high signal) + 4. Possibly, repetition of steps 2 and 3 + +The succession of 'short' and 'long' is then decoded into original data, either +based on tri-bit scheme (inverted or not), or, Manchester. + +Note that there can be complexities: + +- After the long initialization high signal, addition of 'intermediate' prefix + to the signal (longer than 'long', but shorter than 'separator'). Seen on a + NICE FLO/R telecommand (/R means Rolling Code), while not seen on NICE FLO + (fix code). The author guesses this prefix serves to let the receiver know the + signal to come is FLO/R instead of FLO. + +- After the long initialization high signal, succession of {low=short, + high=short} followed by a separator. This serves as a synchronization + sequence. + +- While most protocols use same lengths for low and high signals, on NICE FLO/R + this rule is not met, that is: the 'short' and 'long' durations of the low + signal are different from 'short' and 'long' durations of the high signal. + +2. About Rail + +The Rail manages the succession of durations for one, and only one, of signal +realms (low or high). + +That is, if you note dow the signal as usually (by line, one low followed by one +high): + LOW, HIGH + 150, 200 + 145, 400 + 290, 195 + ... + +Then the values below LOW (150, 145, 290, ...) are one Rail, and the values +below HIGH (200, 400, 195, ...) are another Rail. + +3. About Bands + +A band aims to categorize a duration, short or long. Therefore, a Rail is made +of 2 bands, one for the short duration, one for the long duration. + +4. About Tracks + +Rails live their own live but at some point, they must work in conjunction +(start and stop together, and provide final decoded values). This is the purpose +of a Track, that is made of 2 Rails. + +In the end, a Track provides a convenient interface to the caller. + +5. Overall schema + +track -> r_low -> b_short = manage short duration on LOW signal + | `-> b_long = manage long duration on LOW signal + | + `-> r_high -> b_short = manage short duration on HIGH signal + `-> b_long = manage long duration on HIGH signal + +*/ + +#include "RF433any.h" +#include + +void assert_failed(int line) { +#ifdef ASSERT_OUTPUT_TO_SERIAL + Serial.print("\nRF433any.cpp:"); + Serial.print(line); + Serial.println(": assertion failed, aborted."); +#endif + while (1) + ; +} + + +// * **** ********************************************************************* +// * Band ********************************************************************* +// * **** ********************************************************************* + +inline void Band::breset() { + inf = 0; + sup = 0; + mid = 0; +} + +inline bool Band::init(uint16_t d) { +#ifdef DBG_TRACE + dbgf("B> init: %u", d); +#endif + + if (d >= BAND_MIN_D && d <= BAND_MAX_D) { + mid = d; + uint16_t d_divided_by_4 = d >> 2; + inf = d - d_divided_by_4; + sup = d + d_divided_by_4; + got_it = true; + } else { + got_it = false; + } + + return got_it; +} + +inline bool Band::init_sep(uint16_t d) { +#ifdef DBG_TRACE + dbgf("BSEP> init: %u", d); +#endif + + sup = MAX_SEP_DURATION; + inf = d >> 1; + inf += (inf >> 2); + mid = d; + + got_it = true; + return got_it; +} + +inline bool Band::test_value_init_if_needed(uint16_t d) { + if (!mid) { + init(d); + } else { + got_it = (d >= inf && d <= sup); +#ifdef DBG_TRACE + dbgf("B> cmp %u to [%u, %u]", d, inf, sup); +#endif + } +#ifdef DBG_TRACE + dbgf("B> res: %d", got_it); +#endif + return got_it; +} + +inline bool Band::test_value(uint16_t d) { + if (!mid) { + got_it = false; +#ifdef DBG_TRACE + dbgf("BSEP> cmp %u to uninitialized d", d); +#endif + } else { + got_it = (d >= inf && d <= sup); +#ifdef DBG_TRACE + dbgf("BSEP> cmp %u to [%u, %u]", d, inf, sup); +#endif + } +#ifdef DBG_TRACE + dbgf("BSEP> res: %d", got_it); +#endif + return got_it; +} + + +// * **** ********************************************************************* +// * Rail ********************************************************************* +// * **** ********************************************************************* + +Rail::Rail(byte arg_mood):mood(arg_mood) { + rreset(); +} + +inline void Rail::rreset() { + rreset_soft(); + + b_short.breset(); + b_long.breset(); + b_sep.breset(); +} + +inline void Rail::rreset_soft() { + status = RAIL_OPEN; + index = 0; + rec = 0; +} + +inline bool Rail::rail_eat(uint16_t d) { +#ifdef DBG_TRACE + dbgf("R> index = %d, d = %u", index, d); +#endif + + if (status != RAIL_OPEN) + return false; + + byte count_got_it = 0; + if (b_short.test_value_init_if_needed(d)) + ++count_got_it; + if (b_long.test_value_init_if_needed(d)) + ++count_got_it; + + byte band_count = get_band_count(); + +#ifdef DBG_TRACE + dbgf("R> b_short.got_it = %d, b_long.got_it = %d, " + "band_count = %d", b_short.got_it, b_long.got_it, + band_count); + for (int i = 0; i < 2; ++i) { + dbgf("R> [%i]: inf = %u, mid = %u, sup = %u", i, + (i == 0 ? b_short.inf : b_long.inf), + (i == 0 ? b_short.mid : b_long.mid), + (i == 0 ? b_short.sup : b_long.sup)); + } +#endif + + if (band_count == 1 && !count_got_it) { + Band *pband; + // IMPORTANT + // We are using below 'unsigned long' although they are + // initialized using uint16_t values. + // We need 'unsigned long' because later in the code, we check + // whether 'big' is not more than 4 times 'short' (if it is, then + // the coding shape is too distorted and we give up). + // We do this check by calculating 'small << 2', therefore it + // could be that this operation ends up above 16-bit max unsigned + // integer value. + unsigned long small; + unsigned long big; + if (d < b_short.inf) { + pband = &b_short; + small = d; + big = b_short.mid; + } else if (d > b_short.sup) { + pband = &b_long; + small = b_short.mid; + big = d; + } else { + // Should not happen. + // If value is within band range, then why the hell didn't the + // range grab it? + assert(false); + } + +#ifdef DBG_TRACE + dbg("R> P0"); +#endif + + if ((small << 2) >= big) { + if (pband->init(d)) { + +#ifdef DBG_TRACE + dbg("R> P1"); +#endif + + // As we now know who's who (b_short is b_short and b_long + // is b_long, yes), we can adjust boundaries accordingly. + + b_short.inf = (b_short.mid >> 1) - (b_short.mid >> 3); + if (mood == RAIL_MOOD_LAXIST) { + b_short.sup = (b_short.mid + b_long.mid) >> 1; + b_long.inf = b_short.sup + 1; + } + b_long.sup = b_long.mid + (b_long.mid >> 1) + (b_long.mid >> 3); + + count_got_it = 1; + band_count = 2; + + // Test if intervals overlap? + // That is, test if b_short.sup >= b_long.inf? + // Not done for now... + ; + + if (pband == &b_short) { + // The first N signals received ('N' equals 'index') + // happened to be LONG ones => to be recorded as as many + // ONEs. + rec = ((recorded_t)1 << index) - 1; + } + } + } + } + + if (!band_count) { + status = RAIL_ERROR; + return false; + } + + if (!count_got_it || (band_count == 2 && count_got_it == 2)) { + if (!b_sep.mid) { + // BAND_MAX_D is 30000, and multiplying .mid by 2 will produce a + // maximum value of 60000, that's OK for an unsigned 16-bit int. + if (d >= (b_short.mid << 1) && d >= (b_long.mid << 1)) { +#ifdef DBG_TRACE + dbg("R> init b_sep"); +#endif + // We can end up with an overlap between b_sep and b_long. + // Not an issue. + b_sep.init_sep(d); + } else { +#ifdef DBG_TRACE + dbg("R> no init of b_sep (d too small)"); +#endif + } + } + status = (b_sep.test_value(d) ? RAIL_STP_RCVD : RAIL_ERROR); + +#ifdef DBG_TRACE + dbgf("R> rail terminated, status = %d", status); +#endif + + } else { + + if (band_count == 2) { + if (b_short.got_it == b_long.got_it) { + assert(false); + } + last_bit_recorded = (b_short.got_it ? 0 : 1); + rec = (rec << 1) | last_bit_recorded; + } else { + last_bit_recorded = 0; + } + if (++index == (sizeof(rec) << 3)) { + status = RAIL_FULL; + } + + } + + return (status == RAIL_OPEN); +} + +#ifdef RF433ANY_DBG_TRACK +const char* status_names[] = { + "open", + "full", + "stop received", + "closed", + "error" +}; +void Rail::rail_debug() const { + dbgf(" \"bits\":%i,\"v\":0x" FMTRECORDEDT + ",\"railstatus\":\"%s\",\"n\":%d,", index, rec, status_names[status], + (b_short.mid == b_long.mid ? 1 : 2)); + for (byte i = 0; i < 3; ++i) { + dbgf(" \"%s\":{\"inf\":%u,\"mid\":%u,\"sup\":%u}%s", + (i == 0 ? "b_short" : (i == 1 ? "b_long" : "b_sep")), + (i == 0 ? b_short.inf : (i == 1 ? b_long.inf : b_sep.inf)), + (i == 0 ? b_short.mid : (i == 1 ? b_long.mid : b_sep.mid)), + (i == 0 ? b_short.sup : (i == 1 ? b_long.sup : b_sep.sup)), + (i == 2 ? "" : ",") + ); + } +} +#endif + +byte Rail::get_band_count() const { + return b_short.mid == b_long.mid ? (b_short.mid ? 1 : 0) : 2; +} + + +// * **** ********************************************************************* +// * Misc ********************************************************************* +// * **** ********************************************************************* + +#ifdef RF433ANY_DBG_RAWCODE +const char *sts_names[] = { + "CONT", + "XSEP", + "SSEP", + "LSEP", + "2SEP", + "ERR" +}; + +void RawCode::debug_rawcode() const { + dbgf("> nb_sections = %d, initseq = %u", + nb_sections, initseq); + for (byte i = 0; i < nb_sections; ++i) { + const Section *psec = §ions[i]; + dbgf(" %02d %s", i, sts_names[psec->sts]); + dbgf(" sep = %u", psec->ts.sep); + dbgf(" low: [%d] n = %2d, v = 0x" FMTRECORDEDT "", + psec->low_bands, psec->low_bits, psec->low_rec); + dbgf(" high: [%d] n = %2d, v = 0x" FMTRECORDEDT "", + psec->high_bands, psec->high_bits, psec->high_rec); + } +} +#endif + + +// * ********* **************************************************************** +// * BitVector **************************************************************** +// * ********* **************************************************************** + +BitVector::BitVector(): + array(nullptr), + allocated(0), + nb_bits(0) { + +} + +BitVector::~BitVector() { + if (array) + free(array); +} + +void BitVector::add_bit(byte v) { + if (!allocated) + array = (uint8_t*)malloc(1); + if (nb_bits >= (allocated << 3)) { + byte old_allocated = allocated; + + ++allocated; // Could be another formula ('<<= 1', ...) + + array = (uint8_t*)realloc(array, allocated); + for (byte i = old_allocated; i < allocated; ++i) + array[i] = 0; + } + + ++nb_bits; + for (short i = allocated - 1; i >= 0; --i) { + + byte b; + if (i > 0) { + b = !!(array[i - 1] & 0x80); + } else { + // Defensive programming: + // Normally v is 0 or 1, but I normalize it, just in case. + b = !!v; + } + + array[i]= (array[i] << 1) | b; + + } +} + +int BitVector::get_nb_bits() const { + return nb_bits; +} + +byte BitVector::get_nb_bytes() const { + return (nb_bits + 7) >> 3; +} + + // Bit numbering starts at 0 +byte BitVector::get_nth_bit(byte n) const { + assert(n >= 0 && n < nb_bits); + byte index = (n >> 3); + byte bitread = (1 << (n & 0x07)); + return !!(array[index] & bitread); +} + + // Bit numbering starts at 0 +byte BitVector::get_nth_byte(byte n) const { + assert(n >= 0 && n < get_nb_bytes()); + return array[n]; +} + + // *IMPORTANT* + // If no data got received, returns nullptr. So, you must test the + // returned value. + // + // *IMPORTANT (2)* + // The return value is malloc'd so caller must think of freeing it. + // For example: + // char *s = data_to_str_with_malloc(data); + // ... + // if (s) // DON'T FORGET (s can be null) + // free(s); // DON'T FORGET! (if non-null, s must be freed) +char* BitVector::to_str() const { + if (!get_nb_bits()) + return nullptr; + + byte nb_bytes = get_nb_bytes(); + + char *ret = (char*)malloc(nb_bytes * 3); + char tmp[3]; + int j = 0; + for (int i = nb_bytes - 1; i >= 0 ; --i) { + snprintf(tmp, sizeof(tmp), "%02x", get_nth_byte(i)); + ret[j] = tmp[0]; + ret[j + 1] = tmp[1]; + ret[j + 2] = (i > 0 ? ' ' : '\0'); + j += 3; + } + assert(j <= nb_bytes * 3); + + return ret; +} + +short BitVector::cmp(const BitVector *p) const { + assert(p); + short cmp_nb_bits = (get_nb_bits() > p->get_nb_bits()); + if (!cmp_nb_bits) + cmp_nb_bits = -(get_nb_bits() < p->get_nb_bits()); + + if (cmp_nb_bits) + return cmp_nb_bits; + + for (int i = get_nb_bits() - 1; i >= 0; --i) { + byte v1 = get_nth_bit(i); + byte v2 = p->get_nth_bit(i); + if (v1 > v2) + return 1; + if (v1 < v2) + return -1; + } + + return 0; +} + + +// * ******* ****************************************************************** +// * Decoder ****************************************************************** +// * ******* ****************************************************************** + +#ifdef RF433ANY_DBG_DECODER +const char *dec_id_names[] = { + "INC", + "SYN", + "TRI", + "TRN", + "MAN", + "UNK" +}; +#endif + +Decoder::Decoder(byte arg_convention): + next(nullptr), + pdata(new BitVector()), + convention(arg_convention), + nb_errors(0) { + tsext.initseq = 0; + tsext.first_low = 0; + tsext.first_high = 0; + tsext.first_low_ignored = 0; + tsext.last_low = 0; +} + +Decoder::~Decoder() { + if (pdata) + delete pdata; + if (next) + delete next; +} + +Decoder* Decoder::build_decoder(byte id, byte convention) { + switch (id) { + case DEC_ID_RAW_SYNC: + return new DecoderRawSync(0); + case DEC_ID_TRIBIT: + return new DecoderTriBit(convention); + case DEC_ID_TRIBIT_INV: + return new DecoderTriBitInv(convention); + case DEC_ID_MANCHESTER: + return new DecoderManchester(convention); + case DEC_ID_RAW_UNKNOWN_CODING: + return new DecoderRawUnknownCoding(); + default: + assert(false); + } + return nullptr; // Never executed +} + +void Decoder::attach(Decoder *pdec) { + assert(!next); + next = pdec; +} + +void Decoder::detach() { + next = nullptr; +} + +void Decoder::add_data_bit(byte valbit) { + pdata->add_bit(valbit); +} + +byte Decoder::get_nb_errors() const { return nb_errors; } + +int Decoder::get_nb_bits() const { return pdata ? pdata->get_nb_bits() : 0; } + +void Decoder::set_ts(const uint16_t& arg_initseq, const Timings& ts) { + tsext.initseq = arg_initseq; + tsext.low_short = ts.low_short; + tsext.low_long = ts.low_long; + tsext.high_short = ts.high_short; + tsext.high_long = ts.high_long; + tsext.sep = ts.sep; +} + +void Decoder::get_tsext(TimingsExt *p_tsext) const { + *p_tsext = tsext; + p_tsext->first_low_ignored = first_lo_ignored(); +} + +void Decoder::take_into_account_first_low_high(const Section *psec, + bool is_cont_of_prev_sec) { + if (is_cont_of_prev_sec) + return; + tsext.first_low = psec->first_low; + tsext.first_high = psec->first_high; + tsext.last_low = psec->last_low; + + Signal e[2]; + for (short i = 0; i < 2; ++i) { + uint16_t d = (i == 0 ? tsext.first_low : tsext.first_high); + uint16_t short_d = (i == 0 ? psec->ts.low_short : psec->ts.high_short); + uint16_t long_d = (i == 0 ? psec->ts.low_long : psec->ts.high_long); + Band b_short; + Band b_long; + b_short.init(short_d); + b_long.init(long_d); + +// b_short.sup = (b_short.mid + b_long.mid) >> 1; +// b_long.inf = b_short.sup + 1; + + bool is_short = b_short.test_value(d); + bool is_long = b_long.test_value(d); + + if (is_short && !is_long) { + e[i] = Signal::SHORT; + } else if (!is_short && is_long) { + e[i] = Signal::LONG; + } else if (is_short && is_long && short_d == long_d) { + e[i] = Signal::SHORT; + } else { + e[i] = Signal::OTHER; + } + } + + if (e[0] != Signal::OTHER && e[1] != Signal::OTHER) { + add_signal_step(e[0], e[1]); + tsext.first_low = 0; + tsext.first_high = 0; + } +} + +void Decoder::decode_section(const Section *psec, bool is_cont_of_prev_sec) { + take_into_account_first_low_high(psec, is_cont_of_prev_sec); + + byte pos_low = psec->low_bits; + byte pos_high = psec->high_bits; + + while (pos_low >= 1 || pos_high >= 1) { + Signal sd_low = Signal::OTHER; + Signal sd_high = Signal::OTHER; + if (pos_low >= 1) { + --pos_low; + sd_low = ((((recorded_t)1 << pos_low) & psec->low_rec) ? + Signal::LONG : Signal::SHORT); + } + if (pos_high >= 1) { + --pos_high; + sd_high = + ((((recorded_t)1 << pos_high) & psec->high_rec) ? + Signal::LONG : Signal::SHORT); + } + add_signal_step(sd_low, sd_high); + } +} + +uint16_t Decoder::first_lo_ignored() const { + return 0; +} + +const BitVector* Decoder::get_pdata() const { + return pdata; +} + +BitVector* Decoder::take_away_data() { + if (pdata) { + BitVector *ret = pdata; + pdata = nullptr; + return ret; + } else + return nullptr; +} + +#ifdef RF433ANY_DBG_DECODER +void Decoder::dbg_data(byte seq) const { + char *buf = pdata->to_str(); + if (buf) { + dbgf("[%d] Received %d bits%s: %s", seq, get_nb_bits(), + (get_nb_errors() ? "(!)" : ""), buf); + free(buf); + } else { + dbgf("[%d] No data received, type = %s", seq, dec_id_names[get_id()]); + } +} + +void Decoder::dbg_meta(byte disp_level) const { + if (disp_level <= 1) + return; + if (!tsext.first_low && !tsext.first_high) { + if (!tsext.high_short && !tsext.high_long) { + dbgf(" T=%s, E=%u, I=%u, S=%u, L=%u, P=%u, Y=%u, Z=%u", + dec_id_names[get_id()], nb_errors, tsext.initseq, + tsext.low_short, tsext.low_long, tsext.sep, + first_lo_ignored(), tsext.last_low); + } else { + dbgf(" T=%s, E=%u, I=%u, S(lo)=%u, L(lo)=%u, " + "S(hi)=%u, L(hi)=%u, P=%u, Y=%u, Z=%u", + dec_id_names[get_id()], nb_errors, tsext.initseq, + tsext.low_short, tsext.low_long, tsext.high_short, + tsext.high_long, tsext.sep, first_lo_ignored(), + tsext.last_low); + } + } else { + if (!tsext.high_short && !tsext.high_long) { + dbgf(" T=%s, E=%u, I=%u, S=%u, L=%u, P=%u, U=%u, " + "V=%u, Y=%u, Z=%u", + dec_id_names[get_id()], nb_errors, tsext.initseq, + tsext.low_short, tsext.low_long, tsext.sep, tsext.first_low, + tsext.first_high, first_lo_ignored(), tsext.last_low); + } else { + dbgf(" T=%s, E=%u, I=%u, S(lo)=%u, L(lo)=%u, " + "S(hi)=%u, L(hi)=%u, P=%u, U=%u, V=%u, Y=%u, Z=%u", + dec_id_names[get_id()], nb_errors, tsext.initseq, + tsext.low_short, tsext.low_long, tsext.high_short, + tsext.high_long, tsext.sep, tsext.first_low, + tsext.first_high, first_lo_ignored(), tsext.last_low); + } + } +} + +void Decoder::dbg_next(byte disp_level, byte seq) const { + if (next) + next->dbg_decoder(disp_level, seq + 1); +} + +#endif + + +// * ********************** *************************************************** +// * DecoderRawInconsistent *************************************************** +// * ********************** *************************************************** + +#ifdef RF433ANY_DBG_DECODER +void DecoderRawInconsistent::dbg_decoder(byte disp_level, byte seq) const { + dbgf("[%d] Inconsistent signal", seq); + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * ************** *********************************************************** +// * DecoderRawSync *********************************************************** +// * ************** *********************************************************** + +void DecoderRawSync::add_signal_step(Signal lo, Signal hi) { + if (!sync_shape_set) { + sync_shape = lo; + sync_shape_set = true; + } + + if (lo != sync_shape) { + ++nb_errors; + } else if (hi == Signal::OTHER) { + } else if (lo != hi) { + ++nb_errors; + } else { + ++nb_low_high; + } +} + +void DecoderRawSync::add_sync(byte n) { + nb_low_high += n; +} + +int DecoderRawSync::get_nb_bits() const { return nb_low_high; } + +#ifdef RF433ANY_DBG_DECODER +void DecoderRawSync::dbg_decoder(byte disp_level, byte seq) const { + dbgf("[%d] Sync %d", seq, nb_low_high); + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * *********************** ************************************************** +// * DecoderRawUnknownCoding ************************************************** +// * *********************** ************************************************** + +void DecoderRawUnknownCoding::add_signal_step(Signal lo, Signal hi) { + if (hi == Signal::OTHER) { + unused_final_low = lo; + terminates_with_sep = true; + return; + } + + for (short i = 0; i < 2; ++i) { + Signal x = (i ? hi : lo); + add_data_bit(x == Signal::SHORT ? 0 : 1); + } +} + +#ifdef RF433ANY_DBG_DECODER +void DecoderRawUnknownCoding::dbg_decoder(byte disp_level, byte seq) const { + dbgf("[%d] Unknown encoding: %d signal bits", seq, pdata->get_nb_bits()); + + if (disp_level <= 1) + return; + + int n = pdata->get_nb_bits(); + assert(!(n & 1)); + + int sz = ((int)n * 3) / 2 + 4; + char *buf = new char[sz]; + int p = 0; + for (int i = n - 1; i >= 1; i -= 2) { + byte vlo = pdata->get_nth_bit(i); + byte vhi = pdata->get_nth_bit(i - 1); + buf[p] = (vlo ? 'L' : 'S'); + buf[p + 1] = (vhi ? 'L' : 'S'); + buf[p + 2] = ':'; + p += 3; + } + assert(p + 2 < sz); + if (terminates_with_sep) { + if (unused_final_low == Signal::SHORT) + buf[p] = 'S'; + else + buf[p] = 'L'; + buf[p + 1] = 'P'; + buf[p + 2] = '\0'; + } else { + if (!p) + buf[p] = '\0'; + else + buf[p - 1] = '\0'; + } + Serial.print(" Signal: "); + Serial.print(buf); + Serial.print("\n"); + delete buf; + + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * ************* ************************************************************ +// * DecoderTriBit ************************************************************ +// * ************* ************************************************************ + +void DecoderTriBit::add_signal_step(Signal lo, Signal hi) { + if (hi == Signal::OTHER) + return; + + byte valbit; + if (lo == Signal::SHORT && hi == Signal::LONG) + valbit = convention; + else if (lo == Signal::LONG && hi == Signal::SHORT) + valbit = !convention; + else { + ++nb_errors; + return; + } + + add_data_bit(valbit); +} + +#ifdef RF433ANY_DBG_DECODER +void DecoderTriBit::dbg_decoder(byte disp_level, byte seq) const { + dbg_data(seq); + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * **************** ********************************************************* +// * DecoderTriBitInv ********************************************************* +// * **************** ********************************************************* + +void DecoderTriBitInv::add_signal_step(Signal lo, Signal hi) { + if (first_call_to_add_sgn_lo_hi) { + first_call_to_add_sgn_lo_hi = false; + unused_initial_low = lo; + last_hi = hi; + return; + } + + bool add_it = true; + byte valbit; + if (lo == Signal::SHORT && last_hi == Signal::LONG) + valbit = !convention; + else if (lo == Signal::LONG && last_hi == Signal::SHORT) + valbit = convention; + else { + ++nb_errors; + add_it = false; + } + + if (add_it) + add_data_bit(valbit); + + last_hi = hi; +} + +uint16_t DecoderTriBitInv::first_lo_ignored() const { + switch (unused_initial_low) { + case Signal::OTHER: + return 0; + case Signal::SHORT: + return tsext.low_short; + case Signal::LONG: + return tsext.low_long; + default: + assert(false); + }; + return 0; // Never executed +} + +#ifdef RF433ANY_DBG_DECODER +void DecoderTriBitInv::dbg_decoder(byte disp_level, byte seq) const { + dbg_data(seq); + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * ***************** ******************************************************** +// * DecoderManchester ******************************************************** +// * ***************** ******************************************************** + +DecoderManchester::DecoderManchester(byte arg_convention) + :Decoder(arg_convention), + buf_pos(0), + leading_lo_hi_has_been_passed(false) { + for (byte i = 0; i < sizeof(buf) / sizeof(*buf); ++i) { + buf[i] = 0; + } +} + +inline void DecoderManchester::add_buf(byte r) { + assert(buf_pos < sizeof(buf) /sizeof(*buf)); + buf[buf_pos++] = r; +} + +void DecoderManchester::consume_buf() { + if (buf_pos >= 2) { + if (leading_lo_hi_has_been_passed) { + if (buf[0] == 0 && buf[1] == 1) { + add_data_bit(convention); + } else if (buf[0] == 1 && buf[1] == 0) { + add_data_bit(!convention); + } else { + // FIXME: créer register_error pour gérer ça de manière + // cohérente entre les différents descendants de Decoder. + ++nb_errors; + } + } else { + if (buf[0] != 0 || buf[1] != 1) { + ++nb_errors; + } + leading_lo_hi_has_been_passed = true; + } + // Not always necessary, but harmless if done while not necessary + buf[0] = buf[2]; + buf_pos -= 2; + } +} + +void DecoderManchester::add_signal_step(Signal lo, Signal hi) { + if (lo == Signal::OTHER) { + ++nb_errors; + return; + } + + for (byte i = 0; i < 2; ++i) { + Signal sgn = (i == 0 ? lo : hi); + add_buf(i); + if (sgn == Signal::LONG) + add_buf(i); + consume_buf(); + } +} + +#ifdef RF433ANY_DBG_DECODER +void DecoderManchester::dbg_decoder(byte disp_level, byte seq) const { + dbg_data(seq); + dbg_meta(disp_level); + dbg_next(disp_level, seq); +} +#endif + + +// * ***** ******************************************************************** +// * Track ******************************************************************** +// * ***** ******************************************************************** + +#ifdef RF433ANY_DBG_SIMULATE +SerialLine sl; +char buffer[SERIAL_LINE_BUF_LEN]; + +uint16_t sim_timings[SIM_TIMINGS_LEN]; + +uint16_t sim_timings_count = 0; + +unsigned int sim_int_count = 0; +unsigned int sim_int_count_svg; +unsigned int counter; +#endif + +#ifdef DBG_TIMINGS +uint16_t Track::ih_dbg_timings[40]; +uint16_t Track::ih_dbg_exec[40]; +unsigned int Track::ih_dbg_pos = 0; +#endif +volatile IH_timing_t Track::IH_timings[IH_SIZE]; +volatile unsigned char Track::IH_write_head = 0; +volatile unsigned char Track::IH_read_head = 0; +byte Track::IH_max_pending_timings = 0; +bool Track::IH_interrupt_handler_is_attached = false; + + // Set when Track object is created +byte Track::pin_number = 99; + +Track::Track(int arg_pin_number, byte mood): + r_low(mood), + r_high(mood) { + pin_number = arg_pin_number; + treset(); +} + +void Track::treset() { + trk = TRK_WAIT; + rawcode.nb_sections = 0; +} + +void Track::ih_handle_interrupt() { + static unsigned long last_t = 0; + const unsigned long t = micros(); + +#ifdef RF433ANY_DBG_SIMULATE + unsigned long d; + byte r = sim_int_count % 2; + if (sim_int_count >= sim_timings_count) { + d = 100; + sim_int_count = sim_timings_count + 1; + } else { + d = sim_timings[sim_int_count++]; + } + (void)last_t; + (void)t; +#else + unsigned long d = t - last_t; + last_t = t; + byte r = (digitalRead(pin_number) == HIGH ? 1 : 0); +#endif + + if (d > MAX_DURATION) + d = MAX_DURATION; + + unsigned char next_IH_write_head = (IH_write_head + 1) & IH_MASK; + // No ideal solution here: we reached the buffer size, so either we + // write nothing, or, we loose the oldest entry that was the next one to + // read. + // Solution here: we loose oldest entry in buffer and do the write. + if (next_IH_write_head == IH_read_head) { + IH_read_head = (IH_read_head + 1) & IH_MASK; + } + IH_write_head = next_IH_write_head; + IH_timings[IH_write_head].r = r; + IH_timings[IH_write_head].d = d; +} + +void Track::force_stop_recv() { +#ifdef DBG_TRACE + dbg("T> running force_stop_recv()"); +#endif + if (get_trk() == TRK_RECV) { + track_eat(0, 0); + track_eat(1, 0); + do_events(); + } +} + +void Track::reset_border_mgmt() { + count = 0; + first_low = 0; + first_high = 0; + last_low = 0; +} + +inline void Track::track_eat(byte r, uint16_t d) { + +#ifdef DBG_TRACE + dbgf("T> trk = %d, r = %d, d = %u", trk, r, d); +#endif + + if (trk == TRK_WAIT) { + if (r == 1 && d >= TRACK_MIN_INITSEQ_DURATION) { + r_low.rreset(); + r_high.rreset(); + prev_r = r; + rawcode.initseq = d; + rawcode.max_code_d = d - (d >> 2); + reset_border_mgmt(); + trk = TRK_RECV; + } + return; + } else if (trk != TRK_RECV) { + return; + } + + bool enforce_b_to_false = false; + // [COMMENT002] + // We missed an interrupt apparently (two calls with same r), so we + // had better discard the actual signal. + if (r == prev_r) { + enforce_b_to_false = true; + } + prev_r = r; + + ++count; +#ifdef DBG_TRACE + dbgf("T> count = %d", count); +#endif + + if (count == 1) { + if ((d < BAND_MIN_D || d >= rawcode.max_code_d) + && count < TRACK_MIN_BITS && !rawcode.nb_sections) { +#ifdef DBG_TRACE + dbg("T> case 1"); +#endif + treset(); + // WARNING + // Re-entrant call... not ideal. + track_eat(r, d); + } else { +#ifdef DBG_TRACE + dbg("T> case 2"); +#endif + first_low = d; + } + return; + } else if (count == 2) { + if ((d < BAND_MIN_D || d >= rawcode.max_code_d) + && count < TRACK_MIN_BITS && !rawcode.nb_sections) { +#ifdef DBG_TRACE + dbg("T> case 3"); +#endif + treset(); + // WARNING + // Re-entrant call... not ideal. + track_eat(r, d); + } else { +#ifdef DBG_TRACE + dbg("T> case 4"); +#endif + first_high = d; + } + return; + } +#ifdef DBG_TRACE + dbg("T> case 5"); +#endif + + Rail *prail = (r == 0 ? &r_low : &r_high); + if (prail->status != RAIL_OPEN) + return; + + if (r == 0) + last_low = d; + + bool b; + if ((d < BAND_MIN_D || d >= rawcode.max_code_d) + && count < TRACK_MIN_BITS) { + enforce_b_to_false = true; + } else if (abs(r_low.index - r_high.index) >= 2) { + enforce_b_to_false = true; + } else if (!enforce_b_to_false) { + b = prail->rail_eat(d); + } + + if (enforce_b_to_false) { + r = 1; + b = false; + } + + if (r == 1 && (!b || r_low.status != RAIL_OPEN)) { + +#ifdef DBG_TRACE + dbgf("T> b = %d", b); +#endif + + if (r_low.status == RAIL_OPEN) + r_low.status = RAIL_CLOSED; + if (r_high.status == RAIL_OPEN) + r_high.status = RAIL_CLOSED; + + section_term_status_t sts; + if (r_low.status == RAIL_FULL && r_high.status == RAIL_FULL) { + sts = STS_CONTINUED; + } else if (r_high.status == RAIL_STP_RCVD) { + if (r_low.status == RAIL_CLOSED || r_low.status == RAIL_FULL + || r_low.status == RAIL_ERROR) { // FIXME (RAIL_ERROR) + sts = (r_low.last_bit_recorded ? STS_LONG_SEP : STS_SHORT_SEP); + } else if (r_low.status == RAIL_STP_RCVD) { + sts = STS_SEP_SEP; // FIXME (Need STS_X_SEP) + } else { + sts = STS_ERROR; + } + } else { + sts = STS_ERROR; + } + +/* +Tests implemented below reproduce the following decision table + +Notations: + "pr=cont": the previous track terminated as STS_CONTINUED + "pr!=cont": the previous track didn't terminate as STS_CONTINUED + "nbsec": nb_sections + "cur": how did current track end? -> + "sep": it ended with a separator + "err": it ended with an error + "full": it didn't end but record is full + CUR?: shall we record the current track? + NEXT?: what to do next? (reset track, start new section) + + FIXME? + When a section (that is not the first) ends in error, the current section + is discarded _but_ previous sections are kept and shown to the caller + (enter 'DATA' state). + This is questionnable because, why keeping previous section? + Also it leads to different results depending on recorded_t size, that is an + internal, intermediate artefact, the nature of which shall not change + result as seen by caller. + I leave this behavior though, as a tradeoff between strictness and lax. + In a possible, future improvement, I might change this behavior, and + discard previous sections would an error be encountered. + + +---------------+-------- +----------+-------++-------+--------------+ + |nb_bits | nbsec | prev | cur || CUR? | NEXT? | + +---------------+-------- +----------+-------++-------+--------------+ + |bits0 | pr=cont | sep || REC | NEWSEC | + | | | | err || DISC | DATA | + | | | | full || n/a | n/a | + | | nbsec>0 | pr!=cont | sep || DISC | DATA | + | | | | err || DISC | DATA | + | | | | full || n/a | n/a | + |bits>=min_bits | !nbsec | n/a | sep || REC | NEWSEC | + | | | | err || DISC | RESET | + | | | | ful || REC | NEWSEC(CONT) | + | | nbsec>0 | pr=cont | sep || REC | NEWSEC | + | | | | err || DISC | DATA | + | | | | ful || REC | NEWSEC(CONT) | + | | nbsec>0 | pr!=cont | sep || REC | NEWSEC | + | | | | err || DISC | DATA | + | | | | ful || REC | NEWSEC(CONT) | + +---------------+-------- +----------+-------++-------+--------------+ +*/ + + bool record_current_section; + +#ifdef RF433ANY_DBG_TRACK + bool do_track_debug = false; + (void)do_track_debug; +#endif + + if (r_low.index < TRACK_MIN_BITS || r_high.index < TRACK_MIN_BITS) { + record_current_section = + (sts != STS_ERROR + && rawcode.nb_sections + && rawcode.sections[rawcode.nb_sections - 1].sts + == STS_CONTINUED); + +#ifdef RF433ANY_DBG_TRACK + do_track_debug = record_current_section; +#endif + + } else { + record_current_section = (sts != STS_ERROR); + +#ifdef RF433ANY_DBG_TRACK + do_track_debug = true; +#endif + + } + +#ifdef DBG_TRACE + dbgf("T> reccursec=%i, sts=%i", record_current_section, sts); +#endif +#if defined(RF433ANY_DBG_SIMULATE) && defined(RF433ANY_DBG_TRACK) + if (do_track_debug) { + dbgf("%s {", counter >= 2 ? ",\n" : ""); + dbgf(" \"N\":%d,\"start\":%u,\"end\":%u,", + sim_timings_count, sim_int_count_svg, sim_int_count - 1); + track_debug(); + dbg(" }"); + } +#endif + + if (record_current_section) { +#ifdef DBG_TRACE + dbg("T> recording current section"); +#endif + Section *psec = &rawcode.sections[rawcode.nb_sections++]; + psec->sts = sts; + + psec->ts.sep = (sts == STS_SHORT_SEP + || sts == STS_LONG_SEP + || sts == STS_SEP_SEP ? d : 0); + if (r_low.b_short.test_value(r_high.b_short.mid) + && !r_low.b_short.test_value(r_high.b_long.mid) + && !r_low.b_long.test_value(r_high.b_short.mid) + && r_low.b_long.test_value(r_high.b_long.mid)) { + psec->ts.low_short = (r_low.b_short.mid + r_high.b_short.mid) + >> 1; + psec->ts.low_long = (r_low.b_long.mid + r_high.b_long.mid) + >> 1; + psec->ts.high_short = 0; + psec->ts.high_long = 0; + } else { + psec->ts.low_short = r_low.b_short.mid; + psec->ts.low_long = r_low.b_long.mid; + psec->ts.high_short = r_high.b_short.mid; + psec->ts.high_long = r_high.b_long.mid; + } + + psec->low_rec = r_low.rec; + psec->low_bits = r_low.index; + psec->low_bands = r_low.get_band_count(); + psec->high_rec = r_high.rec; + psec->high_bits = r_high.index; + psec->high_bands = r_high.get_band_count(); + + psec->first_low = first_low; + psec->first_high = first_high; + psec->last_low = last_low; + + trk = ((rawcode.nb_sections == RF433ANY_MAX_SECTIONS) + ? TRK_DATA : TRK_RECV); + +#ifdef DBG_TRACE + dbgf("T> rawcode.nb_sections = %d", rawcode.nb_sections); +#endif + + if (trk == TRK_RECV) { +#ifdef DBG_TRACE + dbg("T> keep receiving (soft reset)"); +#endif + r_low.rreset_soft(); + r_high.rreset_soft(); + if (sts != STS_CONTINUED) { + reset_border_mgmt(); + } + } else { +#ifdef DBG_TRACE + dbg("T> stop receiving (data)"); +#endif + } + } else { + if (rawcode.nb_sections) { + trk = TRK_DATA; + } else { + treset(); + // WARNING + // Re-entrant call... not ideal. + track_eat(r, d); + } + } + + } +} + + // Returns true if a timing got processed, false otherwise. + // Do nothing (and returns false) if Track is in the status TRK_DATA. + // NOTE + // When Track is in the TRK_DATA state, no erase can happen + // (track_eat() will exit immediately). + // Therefore the safeguard of explicitly doing nothing if in the status + // TRK_DATA is redundant => it is defensive programming. +bool Track::process_interrupt_timing() { + if (get_trk() == TRK_DATA) + return false; + + unsigned char IH_pending_timings = + (IH_write_head - IH_read_head) & IH_MASK; + if (IH_pending_timings > IH_max_pending_timings) + IH_max_pending_timings = IH_pending_timings; + + bool ret; + + cli(); + if (IH_read_head != IH_write_head) { + IH_timing_t timing = IH_timings[IH_read_head]; + IH_read_head = (IH_read_head + 1) & IH_MASK; + + sei(); +#ifdef DBG_TIMINGS + unsigned long t0 = micros(); +#endif + track_eat(timing.r, timing.d); +#ifdef DBG_TIMINGS + unsigned long d = micros() - t0; + if (d > MAX_DURATION) + d = MAX_DURATION; + ih_dbg_exec[ih_dbg_pos] = d; + if (get_trk() == TRK_WAIT) + ih_dbg_pos = 0; + else { + if (ih_dbg_pos < sizeof(ih_dbg_timings) / sizeof(*ih_dbg_timings)) + ih_dbg_timings[ih_dbg_pos++] = timing.d; + } +#endif + + ret = true; + + } else { + + sei(); + ret = false; + } + + return ret; +} + +void Track::activate_recording() { +#ifndef RF433ANY_DBG_SIMULATE + if (!IH_interrupt_handler_is_attached) { + attachInterrupt(digitalPinToInterrupt(pin_number), &ih_handle_interrupt, + CHANGE); + IH_interrupt_handler_is_attached = true; + } +#endif +} + +void Track::deactivate_recording() { +#ifndef RF433ANY_DBG_SIMULATE + if (IH_interrupt_handler_is_attached) { + detachInterrupt(digitalPinToInterrupt(pin_number)); + IH_interrupt_handler_is_attached = false; + } +#endif +} + +bool Track::do_events() { + activate_recording(); + while (process_interrupt_timing()) + ; + if (get_trk() == TRK_DATA) { + deactivate_recording(); +#ifdef RF433ANY_DBG_RAWCODE + dbgf("IH_max_pending_timings = %d", ih_get_max_pending_timings()); + rawcode.debug_rawcode(); +#endif + return true; + } + return false; +} + +Decoder* Track::get_data_core(byte convention) { + Decoder *pdec_head = nullptr; + Decoder *pdec_tail = nullptr; + Decoder *pdec = nullptr; + + for (byte i = 0; i < rawcode.nb_sections; ++i) { + + const Section *psec = &rawcode.sections[i]; + + if (abs(psec->low_bits - psec->high_bits) >= 2) { + // Defensive programming (should never happen). + if (!pdec) { + pdec = new DecoderRawInconsistent(); + } + + } else if (psec->low_bands == 1 && psec->high_bands == 1) { + byte n = (psec->low_bits < psec->high_bits ? + psec->low_bits : psec->high_bits); + if (pdec) { + pdec->add_sync(n); + } else { + pdec = new DecoderRawSync(n); + pdec->take_into_account_first_low_high(psec, false); + } + + } else if (psec->low_bands == 1 || psec->high_bands == 1) { + if (!pdec) { + pdec = new DecoderRawInconsistent(); + } + + } else { + byte enum_decoders = DEC_ID_START; + bool is_continuation_of_prev_section = pdec; + do { + if (!pdec) + pdec = Decoder::build_decoder(enum_decoders, convention); + + pdec->decode_section(psec, is_continuation_of_prev_section); + + if (!is_continuation_of_prev_section && pdec->get_nb_errors()) { + delete pdec; + pdec = nullptr; + } + } while (!pdec && ++enum_decoders <= DEC_ID_END); + + } + // The last enumerated decoder is DecoderRawUnknownCoding, that + // never produces any error and MUST be chosen in the end (if no + // other worked). + assert(pdec); + + pdec->set_ts((pdec_head ? 0 : rawcode.initseq), psec->ts); + + if (psec->sts != STS_CONTINUED || i == rawcode.nb_sections - 1) { + if (!pdec_head) { + assert(!pdec_tail); + pdec_head = pdec; + pdec_tail = pdec; + } else { + assert(pdec_tail); + pdec_tail->attach(pdec); + pdec_tail = pdec; + } + pdec = nullptr; + } + } + + return pdec_head; +} + +Decoder* Track::get_data(uint16_t filter, byte convention) { + Decoder *pdec0 = get_data_core(convention); + Decoder *prev_pdec = pdec0; + Decoder *pdec = pdec0; + + while (pdec) { + pdec->reset_repeats(); + + bool keep = true; + + if (filter & RF433ANY_FD_DECODED) { + if (!pdec->data_got_decoded()) + keep = false; + } + + if (filter & RF433ANY_FD_NO_ERROR) { + if (pdec->get_nb_errors()) + keep = false; + } + + if (filter & RF433ANY_FD_DEDUP) { + if (pdec != prev_pdec && pdec->get_id() == prev_pdec->get_id()) { + const BitVector *p1; + const BitVector *p2; + if ((p1 = pdec->get_pdata()) && (p2 = prev_pdec->get_pdata())) { + if (!p1->cmp(p2)) { + keep = false; + prev_pdec->inc_repeats(); + } + } + } + } + + if (filter & (RF433ANY_FD_TRI | RF433ANY_FD_TRN | RF433ANY_FD_MAN)) { + if (!(filter & RF433ANY_FD_TRI) + && pdec->get_id() == DEC_ID_TRIBIT) + keep = false; + if (!(filter & RF433ANY_FD_TRN) + && pdec->get_id() == DEC_ID_TRIBIT_INV) + keep = false; + if (!(filter & RF433ANY_FD_MAN) + && pdec->get_id() == DEC_ID_MANCHESTER) + keep = false; + } + + if (keep) { + prev_pdec = pdec; + pdec = pdec->get_next(); + } else { + Decoder *pdec_to_remove = pdec; + if (pdec == pdec0) { + assert(pdec0 == prev_pdec); + pdec0 = pdec->get_next(); + prev_pdec = pdec0; + pdec = pdec0; + } else { + pdec = pdec->get_next(); + prev_pdec->detach(); + prev_pdec->attach(pdec); + } + pdec_to_remove->detach(); + delete pdec_to_remove; + } + } + + return pdec0; +} + +#ifdef DBG_TIMINGS +void Track::dbg_timings() const { + for (unsigned int i = 0; i + 1 < ih_dbg_pos; i += 2) { + dbgf("%4u, %4u | %5u, %5u", ih_dbg_timings[i], ih_dbg_timings[i + 1], + ih_dbg_exec[i], ih_dbg_exec[i + 1]); + } +} +#endif + +#ifdef RF433ANY_DBG_TRACK +const char* trk_names[] = { + "TRK_WAIT", + "TRK_RECV", + "TRK_DATA" +}; +void Track::track_debug() const { + recorded_t xorval = r_low.rec ^ r_high.rec; + dbgf(" \"trk\":%s,\"xorval\":0x" FMTRECORDEDT ",", + trk_names[trk], xorval); + if (trk != TRK_WAIT) { + for (byte i = 0; i < 2; ++i) { + dbgf(" \"%s\":{", (i == 0 ? "r_low" : "r_high")); + (i == 0 ? &r_low : &r_high)->rail_debug(); + dbgf(" }%s", i == 1 ? "" : ","); + } + } + +} +#endif + +// vim: ts=4:sw=4:tw=80:et diff --git a/RF433any.h b/RF433any.h new file mode 100644 index 0000000..6e48ac2 --- /dev/null +++ b/RF433any.h @@ -0,0 +1,660 @@ +// RF433any.h + +/* + Copyright 2021 Sébastien Millet + + `RF433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `RF433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +#ifndef _RF433ANY_H +#define _RF433ANY_H + +// **************************************************************************** +// RF433ANY_TESTPLAN ********************************************************** +#if RF433ANY_TESTPLAN == 1 + +#define RF433ANY_DBG_SIMULATE +#define RF433ANY_DBG_TRACK + +#elif RF433ANY_TESTPLAN == 2 // RF433ANY_TESTPLAN + +#define RF433ANY_DBG_SIMULATE +#define RF433ANY_DBG_RAWCODE + +#elif RF433ANY_TESTPLAN == 3 // RF433ANY_TESTPLAN + +#define RF433ANY_DBG_SIMULATE +#define RF433ANY_DBG_DECODER + +#elif RF433ANY_TESTPLAN == 4 // RF433ANY_TESTPLAN + +#define RF433ANY_DBG_SIMULATE +#define RF433ANY_DBG_DECODER +#define RF433ANY_DBG_SMALL_RECORDED +#define RF433ANY_MAX_SECTIONS 12 + +#elif RF433ANY_TESTPLAN == 5 // RF433ANY_TESTPLAN + +#define RF433ANY_DBG_SIMULATE +#define RF433ANY_DBG_SMALL_RECORDED + +#else // RF433ANY_TESTPLAN + +#ifdef RF433ANY_TESTPLAN +#error "RF433ANY_TESTPLAN macro has an illegal value." +#endif +// RF433ANY_TESTPLAN ********************************************************** +// **************************************************************************** + + +// It is OK to update the below, because if this code is compiled, then we are +// not in the test plan. + +//#define RF433ANY_DBG_SIMULATE +//#define DBG_TRACE +//#define DBG_TIMINGS +//#define RF433ANY_DBG_TRACK +//#define RF433ANY_DBG_RAWCODE +//#define RF433ANY_DBG_DECODER +//#define RF433ANY_DBG_SMALL_RECORDED + +#endif // RF433ANY_TESTPLAN + +#if defined(RF433ANY_DBG_SIMULATE) || defined(DBG_TRACE) \ + || defined(DBG_TIMINGS) || defined(RF433ANY_DBG_TRACK) \ + || defined(RF433ANY_DBG_RAWCODE) || defined(RF433ANY_DBG_DECODER) +#define DEBUG +#endif + +#ifdef RF433ANY_DBG_SIMULATE +#include "Serial.h" +#define SIM_TIMINGS_LEN 140 +#endif + +#ifdef DEBUG + +#include "Debug.h" + +#else + +#define dbg(a) +#define dbgf(...) + +#endif + +#include + +#define MAX_DURATION 65535 +#define MAX_SEP_DURATION 65535 +#ifndef RF433ANY_MAX_SECTIONS +#define RF433ANY_MAX_SECTIONS 8 +#endif + +#define ASSERT_OUTPUT_TO_SERIAL + +#define assert(cond) { \ + if (!(cond)) { \ + assert_failed(__LINE__); \ + } \ +} +void assert_failed(int line); + + +// * **** ********************************************************************* +// * Band ********************************************************************* +// * **** ********************************************************************* + +#define BAND_MIN_D 64 + // IMPORTANT + // Value must be so that BAND_MAX_D * 2 can be stored in a uint16_t. + // That means BAND_MAX_D must be lower than 32768. +#define BAND_MAX_D 30000 + +struct Band { + uint16_t inf; + uint16_t mid; + uint16_t sup; + + bool got_it; + + bool test_value_init_if_needed(uint16_t d); + bool test_value(uint16_t d); + + void breset(); + bool init(uint16_t d); + bool init_sep(uint16_t d); +}; + + +// * **** ********************************************************************* +// * Rail ********************************************************************* +// * **** ********************************************************************* + +#ifdef RF433ANY_DBG_SIMULATE + +#ifdef RF433ANY_DBG_SMALL_RECORDED + +typedef uint8_t recorded_t; +#define FMTRECORDEDT "%02X" + +#else + +typedef uint32_t recorded_t; +#define FMTRECORDEDT "%08lX" + +#endif + +#else // RF433ANY_DBG_SIMULATE + +typedef uint16_t recorded_t; +#define FMTRECORDEDT "%04lx" + +#endif + +#define RAIL_MOOD_STRICT 0 +#define RAIL_MOOD_LAXIST 1 + +#define DEFAULT_RAIL_MOOD RAIL_MOOD_LAXIST + +#define RAIL_OPEN 0 +#define RAIL_FULL 1 +#define RAIL_STP_RCVD 2 +#define RAIL_CLOSED 3 +#define RAIL_ERROR 4 + +class Rail { + friend class Track; + + private: + Band b_short; + Band b_long; + Band b_sep; + + byte last_bit_recorded; + recorded_t rec; + byte status; + byte index; + + byte mood; + + public: + Rail(byte arg_mood); + bool rail_eat(uint16_t d); + void rreset(); + void rreset_soft(); +#ifdef RF433ANY_DBG_TRACK + void rail_debug() const; +#endif + byte get_band_count() const; +}; + + +// * **** ********************************************************************* +// * Misc ********************************************************************* +// * **** ********************************************************************* + +typedef enum { + STS_CONTINUED, + STS_X_SEP, // FIXME + STS_SHORT_SEP, + STS_LONG_SEP, + STS_SEP_SEP, + STS_ERROR +} section_term_status_t; + +struct Timings { + uint16_t low_short; + uint16_t low_long; + uint16_t high_short; + uint16_t high_long; + uint16_t sep; +}; + +struct TimingsExt: public Timings { + uint16_t initseq; + uint16_t first_low; + uint16_t first_high; + uint16_t first_low_ignored; + uint16_t last_low; +}; + +struct Section { + recorded_t low_rec; + unsigned char low_bits :6; + unsigned char low_bands :2; + recorded_t high_rec; + unsigned char high_bits :6; + unsigned char high_bands :2; + + uint16_t first_low; + uint16_t first_high; + uint16_t last_low; + + Timings ts; + + section_term_status_t sts; +}; + +struct RawCode { + uint16_t initseq; + uint16_t max_code_d; + byte nb_sections; + Section sections[RF433ANY_MAX_SECTIONS]; + + void debug_rawcode() const; +}; + + +// * ********* **************************************************************** +// * BitVector **************************************************************** +// * ********* **************************************************************** + +// vector-like of the (very) poor man. No time to make it fancier. +// It'll simply accept to add a bit at the beginning (add_bit), +// to get the number of bits and bytes, and access the Nth bit or byte. +// Also, an iterator would be best to walk through bits, but it is 'TO DO' for +// now. +class BitVector { + private: + uint8_t* array; + byte allocated; + byte nb_bits; + public: + BitVector(); + virtual ~BitVector(); + + virtual void add_bit(byte v); + + virtual int get_nb_bits() const; + virtual byte get_nb_bytes() const; + virtual byte get_nth_bit(byte n) const; + virtual byte get_nth_byte(byte n) const; + + virtual char *to_str() const; + virtual short cmp(const BitVector *p) const; +}; + + +// * ******* ****************************************************************** +// * Decoder ****************************************************************** +// * ******* ****************************************************************** + + // IMPORTANT + // VALUES ARE NOT ARBITRARY. + // CONVENTION_0 must be 0 and CONVENTION_1 must be 1. + // This is due to the decoding that uses a bit value ultimately coming + // from CONVENTION_0 or CONVENTION_1. +#define CONVENTION_0 0 +#define CONVENTION_1 1 + +enum class Signal { + SHORT, + LONG, + OTHER +}; + + // FD = Filter Data + // Bit-mask values, to be used in conjunction +#define RF433ANY_FD_ALL 0 +#define RF433ANY_FD_DECODED 1 +#define RF433ANY_FD_NO_ERROR 2 +#define RF433ANY_FD_DEDUP 4 +#define RF433ANY_FD_TRI 8 +#define RF433ANY_FD_TRN 16 +#define RF433ANY_FD_MAN 32 + +#define DEC_ID_RAW_INCONSISTENT 0 +#define DEC_ID_START 1 // Start of enumeration of real decoders +#define DEC_ID_RAW_SYNC 1 +#define DEC_ID_TRIBIT 2 +#define DEC_ID_TRIBIT_INV 3 +#define DEC_ID_MANCHESTER 4 +#define DEC_ID_RAW_UNKNOWN_CODING 5 // At last we use this one, that'll always + // produce a successful result. +#define DEC_ID_END 5 // End of enumeration of real decoders + +class Decoder { + private: + Decoder *next; + byte repeats; + + protected: + BitVector* pdata; + byte convention; + byte nb_errors; + + TimingsExt tsext; + + void add_data_bit(byte valbit); + virtual void add_signal_step(Signal low, Signal high) = 0; + + public: + Decoder(byte arg_convention); + virtual ~Decoder(); + virtual byte get_id() const = 0; + virtual char get_id_letter() const = 0; + + static Decoder *build_decoder(byte id, byte convention); + + virtual void add_sync(byte n) { } + virtual byte get_nb_errors() const; + virtual int get_nb_bits() const; + + virtual void get_tsext(TimingsExt *p_tsext) const; + virtual void set_ts(const uint16_t& arg_initseq, const Timings& arg_ts); + virtual void decode_section(const Section *psec, + bool is_cont_of_prev_sec); + virtual void take_into_account_first_low_high(const Section *psec, + bool is_cont_of_prev_sec); + virtual uint16_t first_lo_ignored() const; + + virtual void attach(Decoder *pdec); + virtual void detach(); + + virtual bool data_got_decoded() const { return false; } + virtual const BitVector* get_pdata() const; + virtual BitVector* take_away_data(); + virtual Decoder* get_next() const { return next; } + + virtual void reset_repeats() { repeats = 0; } + virtual void inc_repeats() { ++repeats; } + virtual byte get_repeats() const { return repeats; }; + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_data(byte seq) const; + virtual void dbg_meta(byte disp_level) const; + virtual void dbg_decoder(byte disp_level = 1, byte seq = 0) const + = 0; + virtual void dbg_next(byte disp_level, byte seq) const; +#endif +}; + + +// * ********************** *************************************************** +// * DecoderRawInconsistent *************************************************** +// * ********************** *************************************************** + +class DecoderRawInconsistent: public Decoder { + public: + DecoderRawInconsistent(): Decoder(CONVENTION_0) { } + ~DecoderRawInconsistent() { } + + virtual byte get_id() const override { return DEC_ID_RAW_INCONSISTENT; } + virtual char get_id_letter() const override { return 'I'; } + + virtual void add_signal_step(Signal lo, Signal hi) override { } + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif +}; + + +// * ************** *********************************************************** +// * DecoderRawSync *********************************************************** +// * ************** *********************************************************** + +class DecoderRawSync: public Decoder { + private: + byte nb_low_high; + Signal sync_shape; + bool sync_shape_set; + + public: + DecoderRawSync(byte arg_nb_low_high): + Decoder(CONVENTION_0), + nb_low_high(arg_nb_low_high), + sync_shape_set(false) { } + ~DecoderRawSync() { } + + virtual byte get_id() const override { return DEC_ID_RAW_SYNC; } + virtual char get_id_letter() const override { return 'S'; } + + virtual void add_signal_step(Signal lo, Signal hi) override; + + virtual void add_sync(byte n) override; + + virtual int get_nb_bits() const override; + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif + +}; + + +// * *********************** ************************************************** +// * DecoderRawUnknownCoding ************************************************** +// * *********************** ************************************************** + +class DecoderRawUnknownCoding: public Decoder { + private: + Signal unused_final_low; + bool terminates_with_sep; + + public: + DecoderRawUnknownCoding(): + Decoder(CONVENTION_0), + unused_final_low(Signal::OTHER), + terminates_with_sep(false) { } + ~DecoderRawUnknownCoding() { } + + virtual byte get_id() const override + { return DEC_ID_RAW_UNKNOWN_CODING; } + virtual char get_id_letter() const override { return 'U'; } + + virtual void add_signal_step(Signal lo, Signal hi) override; + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif + +}; + + +// * ************* ************************************************************ +// * DecoderTriBit ************************************************************ +// * ************* ************************************************************ + +class DecoderTriBit: public Decoder { + public: + DecoderTriBit(byte arg_convention = CONVENTION_0) + :Decoder(arg_convention) { + } + ~DecoderTriBit() { } + + virtual byte get_id() const override { return DEC_ID_TRIBIT; } + virtual char get_id_letter() const override { return 'T'; } + virtual void add_signal_step(Signal low, Signal high) + override; + + virtual bool data_got_decoded() const override { return true; } + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif + +}; + + +// * **************** ********************************************************* +// * DecoderTriBitInv ********************************************************* +// * **************** ********************************************************* + +class DecoderTriBitInv: public Decoder { + private: + bool first_call_to_add_sgn_lo_hi; + Signal unused_initial_low; + Signal last_hi; + + public: + DecoderTriBitInv(byte arg_convention = CONVENTION_0) + :Decoder(arg_convention), + first_call_to_add_sgn_lo_hi(true), + unused_initial_low(Signal::OTHER) { + } + ~DecoderTriBitInv() { } + + virtual byte get_id() const override { return DEC_ID_TRIBIT_INV; } + virtual char get_id_letter() const override { return 'N'; } + virtual void add_signal_step(Signal low, Signal high) + override; + + virtual bool data_got_decoded() const override { return true; } + + virtual uint16_t first_lo_ignored() const override; + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif + +}; + + +// * ***************** ******************************************************** +// * DecoderManchester ******************************************************** +// * ***************** ******************************************************** + +class DecoderManchester: public Decoder { + private: + byte buf[3]; + byte buf_pos; + // Manchester encoding comes with a mandatory leading 'short low' + // (otherwise we could not distinguish it from the initialization + // sequence). + // Said differently: Manchester needs a leading '0' bit (if + // considering low-then-high is '0'), that is not part of data. + bool leading_lo_hi_has_been_passed; + + void add_buf(byte r); + void consume_buf(); + + public: + DecoderManchester(byte arg_convention = CONVENTION_0); + ~DecoderManchester() { } + + virtual byte get_id() const override { return DEC_ID_MANCHESTER; } + virtual char get_id_letter() const override { return 'M'; } + virtual void add_signal_step(Signal low, Signal high) + override; + + virtual bool data_got_decoded() const override { return true; } + +#ifdef RF433ANY_DBG_DECODER + virtual void dbg_decoder(byte disp_level, byte seq) const override; +#endif + +}; + + +// * ***** ******************************************************************** +// * Track ******************************************************************** +// * ***** ******************************************************************** + +#define TRACK_MIN_INITSEQ_DURATION 4000 +#define TRACK_MIN_BITS 7 + + // IMPORTANT + // IH_MASK must be equal to the size of IH_timings - 1. + // The size of IH_timings must be a power of 2. + // Thus, IH_MASK allows to quickly calculate modulo, while walking through + // IH_timings. +#define IH_SIZE 4 +#define IH_MASK (IH_SIZE - 1) + +struct IH_timing_t { + byte r; + uint16_t d; + + IH_timing_t() { } + IH_timing_t(const volatile IH_timing_t& t) { + r = t.r; + d = t.d; + } +}; + +// NOTE - ABOUT STATIC MEMBER VARIABLES AND FUNCTIONS IN THE TRACK CLASS +// The class is designed so that one object is useful at a time. This comes +// from the fact that we attach interrupt handler to a static method (as is +// mandatory: an object method would not be possible, compiler would block +// because no way to populate 'this' pointer.) +// At last, the distinction between static and non-static members is a bit +// arbitrary. +// I decided that variables and functions _directly_ tied to interrupt handler +// are static, while all others are non-static. +typedef enum {TRK_WAIT, TRK_RECV, TRK_DATA} trk_t; +class Track { + private: + + +#ifdef DBG_TIMINGS + static uint16_t ih_dbg_timings[40]; + static uint16_t ih_dbg_exec[40]; + static unsigned int ih_dbg_pos; +#endif + static byte pin_number; + static volatile IH_timing_t IH_timings[IH_SIZE]; + static volatile unsigned char IH_write_head; + static volatile unsigned char IH_read_head; + static byte IH_max_pending_timings; + static bool IH_interrupt_handler_is_attached; + + volatile trk_t trk; + byte count; + + Rail r_low; + Rail r_high; + byte prev_r; + + uint16_t first_low; + uint16_t first_high; + uint16_t last_low; + + RawCode rawcode; + + void reset_border_mgmt(); + Decoder* get_data_core(byte convention); + + public: + Track(int arg_pin_number, byte mood = DEFAULT_RAIL_MOOD); + + static void ih_handle_interrupt(); + static byte ih_get_max_pending_timings() { + return IH_max_pending_timings; + } + + void treset(); + void track_eat(byte r, uint16_t d); +#ifdef RF433ANY_DBG_TRACK + void track_debug() const; +#endif +#ifdef DBG_TIMINGS + void dbg_timings() const; +#endif + + trk_t get_trk() const { return trk; } + + void force_stop_recv(); + + void activate_recording(); + void deactivate_recording(); + bool process_interrupt_timing(); + bool do_events(); + Decoder* get_data(uint16_t filter, byte convention = CONVENTION_0); +}; + +#endif // _RF433ANY_H + +// vim: ts=4:sw=4:tw=80:et diff --git a/Serial.cpp b/Serial.cpp new file mode 100644 index 0000000..8b6372d --- /dev/null +++ b/Serial.cpp @@ -0,0 +1,103 @@ +// Serial.cpp + +/* + Provides a way to read lines on serial +*/ + +/* + Copyright 2021 Sébastien Millet + + `rf433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `rf433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +#include "Serial.h" +#include + +SerialLine::SerialLine():head(0),got_a_line(false) { }; + +void SerialLine::do_events() { + if (got_a_line) + return; + if (!Serial.available()) + return; + + int b; + do { + b = Serial.read(); + if (b == -1) + break; + buf[head++] = (char)b; + } while (head < SERIAL_LINE_BUF_LEN - 1 && b != '\n' && Serial.available()); + + if (head < SERIAL_LINE_BUF_LEN - 1 && b != '\n') + return; + + buf[head] = '\0'; + + // Remove trailing cr and/or nl + // FIXME? + // WON'T WORK WITH MAC-OS NEWLINES! + // (SEE ABOVE: NO STOP IF ONLY CR ENCOUNTERED) + if (head >= 1 && buf[head - 1] == '\n') + buf[--head] = '\0'; + if (head >= 1 && buf[head - 1] == '\r') + buf[--head] = '\0'; + got_a_line = true; +} + +bool SerialLine::is_line_available() { + do_events(); + return got_a_line; +} + +void SerialLine::reset() { + head = 0; + got_a_line = false; +} + +// Get USB input as a simple line, copied in caller buffer. +// A 'line' is a set of non-null characters followed by 'new line', 'new line' +// being either as per Unix or Windows convention, see below. +// Returns true if a copy was done (there was a line available), false if not +// (in which case, s is not updated). +// The terminating newline character (or 2-character CR-LF sequence) is NOT part +// of the string given to the caller. +// If the line length is above the buffer size (SERIAL_LINE_BUF_LEN), then it'll +// be cut into smaller pieces. +// Because of the way the received buffer is parsed, and when using CR-LF as +// end-of-line marker (default even under Linux), it can result in a empty +// string seen after a first string with a length close to the limit. +// +// About new lines: +// - Works fine with Unix new lines (\n), tested +// - Supposed to work fine with Windows new lines (\r\n), NOT TESTED +// - WON'T WORK WITH MAC-OS NEW LINES (\r) +bool SerialLine::get_line(char *s, size_t len) { + do_events(); + if (!got_a_line) + return false; + snprintf(s, len, buf); + reset(); + return true; +} + +// Same as get_line, but with blocking I/O = +// Wait without time limit, until a line comes in. +void SerialLine::get_line_blocking(char *s, size_t len) { + while (!get_line(s, len)) + ; +} + +// vim: ts=4:sw=4:tw=80:et diff --git a/Serial.h b/Serial.h new file mode 100644 index 0000000..caf9c32 --- /dev/null +++ b/Serial.h @@ -0,0 +1,48 @@ +// Serial.h + +/* + Copyright 2021 Sébastien Millet + + `rf433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `rf433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +#ifndef _SERIAL_H +#define _SERIAL_H + +#include + +#define SERIAL_LINE_BUF_LEN 19 + +class SerialLine { + private: + char buf[SERIAL_LINE_BUF_LEN]; // 16-character strings (then CR+LF then + // NULL-terminating). + size_t head; + bool got_a_line; + void reset(); + + public: + SerialLine(); + + void do_events(); + bool is_line_available(); + bool get_line(char *s, size_t len); + void get_line_blocking(char *s, size_t len); + void split_s_into_func_args(char *s, char **func, char **args) const; +}; + +#endif // _SERIAL_H + +// vim: ts=4:sw=4:tw=80:et diff --git a/examples/output-signal-timings/Makefile b/examples/output-signal-timings/Makefile new file mode 100644 index 0000000..ba06c3f --- /dev/null +++ b/examples/output-signal-timings/Makefile @@ -0,0 +1,10 @@ +ARDUINO_DIR = /usr/share/arduino +ARDUINO_LIBS = RF433any +ARDMK_DIR = /home/sebastien/.arduino_mk + +# USER_LIB_PATH = /home/sebastien/travail/cpp/seb/arduino/libraries + +BOARD_TAG = uno +MCU = atmega328 + +include $(ARDMK_DIR)/Arduino.mk diff --git a/examples/output-signal-timings/am b/examples/output-signal-timings/am new file mode 100755 index 0000000..8ae2812 --- /dev/null +++ b/examples/output-signal-timings/am @@ -0,0 +1,343 @@ +#!/usr/bin/bash + +# am + +# Copyright 2019, 2020, 2021 Sébastien Millet + +# Can perform the following: +# 1. Compile the code +# 2. Upload to Arduino +# 3. Read (continually) what is arriving from the USB port the Arduino is +# connected to + +# Versions history (as of 1.3) +# 1.3 Output from Arduino is recorded in files named with numbers instead of +# date-time string. +# 1.4 Adds -t (--testplan) option, to set TESTPLAN macro +# 1.5 -t (or --testplan) now comes with a value, so as to manage multiple test +# plans. +# 1.6 Updated to work fine with Arch arduino package instead of the manually +# installed (from tar.gz source) package used so far. +# 1.7 Renames archlinux-arduino back to arduino, and created corresponding +# symlink (was cleaner to do s). + +set -euo pipefail + +VERSION=1.7 + +PORT= +BOARD= +SPEED= +FQBN= +BUILDDIR= +RECORDDIR=out +READSPEED= +RECORDFILE= + +UPLOAD="no" +VERBOSE="no" +CATUSB="no" +STTY="no" +RECORDUSB="no" +COMPILE="yes" +TESTPLAN= + +DISPLAYSEP=no + +function finish { + if [ "${DISPLAYSEP}" == "yes" ]; then + echo "-----END ARDUINO OUTPUT-----" | tee -a "${RECORDFILE}" + fi +} + +trap finish EXIT + +function usage { + echo "Usage:" + echo " am [OPTIONS...] FILE" + echo "Compile FILE using arduino-builder." + echo "Example: am sketch.ino" + echo "" + echo "ENVIRONMENT VARIABLES" + echo " If ARDUINO_USER_LIBS is defined and non empty, then arduino-builder" + echo " is called with the supplementary option -libraries followed by" + echo " ARDUINO_USER_LIBS' value." + echo "" + echo "OPTIONS" + echo " -h --help Display this help screen" + echo " -V --version Output version information and quit" + echo " -v --verbose Be more talkative" + echo " -u --upload Upload compiled code into Arduino" + echo " -b --board Board, either 'uno' or 'nano'" + echo " -p --port Port, for ex. '/dev/ttyUSB0'" + echo " -s --speed Upload speed, for ex. 115200" + echo " Normally, speed is infered from device type:" + echo " 115200 for Uno, 57600 for Nano" + echo " -B --fqbn Board Fully Qualified Name, like 'arduino:avr:uno'" + echo " -d --builddir Build directory" + echo " -c --catusb Display (continually) what Arduino writes on USB" + echo " --stty Tune stty properly for later communication (implied" + echo " by --catusb)" + echo " -r --recordusb Write USB (continually) to a file (implies -c)" + echo " --recordfile Output file if -r option is set" + echo " -n --nocompile Don't compile code" + echo " --readspeed Read speed of USB. If not specified, this script" + echo " will try to infere it from source file. If it" + echo " fails, it'll fallback to 9600." + echo " This option is useful only if USB is read" + echo " (-c or --stty option set)" + echo " -t --testplan Set TESTPLAN macro value" + echo " (as if #define TESTPLAN VALUE)" + exit 1 +} + +function version { + echo "am version ${VERSION}" + exit +} + +OPTS=$(getopt -o hVvub:p:s:B:d:crnt: --long help,version,verbose,upload,board:,port:,speed:,fqbn:,builddir:,catusb,stty,recordusb,nocompile,readspeed:,recordfile:,testplan: -n 'am' -- "$@") + +eval set -- "$OPTS" + +while true; do + case "$1" in + -h | --help ) usage; shift ;; + -V | --version ) version; shift ;; + -v | --verbose ) VERBOSE="yes"; shift ;; + -u | --upload ) UPLOAD="yes"; shift ;; + -b | --board ) BOARD="$2"; shift 2 ;; + -p | --port ) PORT="$2"; shift 2 ;; + -s | --speed ) SPEED="$2"; shift 2 ;; + -B | --fqbn ) FQBN="$2"; shift 2 ;; + -d | --builddir ) BUILDDIR="$2"; shift 2 ;; + -c | --catusb ) CATUSB="yes"; shift ;; + -r | --recordusb ) RECORDUSB="yes"; CATUSB="yes"; shift ;; + -n | --nocompile ) COMPILE="no"; shift ;; + --readspeed ) READSPEED="$2"; shift 2 ;; + --recordfile ) RECORDFILE="$2"; shift 2 ;; + --stty ) STTY="yes"; shift ;; + -t | --testplan ) TESTPLAN="$2"; shift 2 ;; + -- ) shift; break ;; + * ) break ;; + esac +done + +FILE=${1:-} +TRAILINGOPTS=${2:-} + +if [ -n "${TRAILINGOPTS}" ]; then + echo "Error: trailing options" + exit 1; +fi +if [ -z "${FILE}" ]; then + echo "Error: no input file" + exit 1; +fi + +set +e + +if [ -n "${BOARD}" ]; then + if [ "${BOARD}" != "uno" ] && [ "${BOARD}" != "nano" ]; then + echo "Error: board '${BOARD}' unknown" + exit 1 + fi +fi + +#ARDUINODIR=$(LANG='' type -a arduino \ +# | tail -n 1 \ +# | sed 's/\S\+\sis\s//') +#ARDUINODIR=$(readlink -f "${ARDUINODIR}") +#ARDUINODIR=$(dirname "${ARDUINODIR}") + +ARDUINODIR=/usr/share/arduino + +COUNTUNO=$(compgen -G '/dev/ttyACM*' | wc -l) +COUNTNANO=$(compgen -G '/dev/ttyUSB*' | wc -l) + +if [ -z "${BOARD}" ]; then + if [ "${COUNTUNO}" -ge 1 ] && [ "${COUNTNANO}" -ge 1 ]; then + echo "Error: cannot guess board, found ${COUNTUNO} uno(s), ${COUNTNANO} nano(s)" + exit 10 + fi + if [ "${COUNTUNO}" -ge 1 ]; then + BOARD=uno + elif [ "${COUNTNANO}" -ge 1 ]; then + BOARD=nano + fi + if [ -z "${BOARD}" ]; then + echo "Error: cannot guess board, none found"; + exit 10 + fi +fi + +if [ "${UPLOAD}" == "yes" ] || [ "${CATUSB}" == "yes" ]; then + if [ -z "${PORT}" ]; then + if [ "${BOARD}" == "uno" ]; then + COUNT=${COUNTUNO} + PORT=$(compgen -G '/dev/ttyACM*') + elif [ "${BOARD}" == "nano" ]; then + COUNT=${COUNTNANO} + PORT=$(compgen -G '/dev/ttyUSB*') + else + echo "FATAL #001, CHECK THIS CODE" + exit 99 + fi + + if [ "${COUNT}" -ge 2 ]; then + echo "Error: cannot guess port, more than 1 board '${BOARD}' found" + exit 10 + fi + if [ -z "${PORT}" ]; then + echo "Error: cannot guess port, none found" + exit 10 + fi + fi + + if [ -z "${SPEED}" ]; then + if [ "${BOARD}" == "uno" ]; then + SPEED=115200 + elif [ "${BOARD}" == "nano" ]; then + SPEED=57600 + else + echo "FATAL #002, CHECK THIS CODE" + exit 99 + fi + fi + + if [ ! -e "${PORT}" ]; then + echo "Error: port not found" + exit 10 + fi +fi + +if [ -z "${FQBN}" ]; then + if [ "${BOARD}" == "uno" ]; then + FQBN="arduino:avr:uno" + elif [ "${BOARD}" == "nano" ]; then + FQBN="arduino:avr:nano:cpu=atmega328old" + else + echo "FATAL #003, CHECK THIS CODE" + exit 99 + fi +fi + +if [ -z "${BUILDDIR}" ]; then + if [[ "${FILE}" == */* ]]; then + BUILDDIR=${FILE%/*} + BUILDDIR="${BUILDDIR%/}/build" + else + BUILDDIR=build + fi +fi + +if [ "${RECORDUSB}" == "yes" ]; then + if [ -z "${RECORDFILE}" ]; then + V=${FILE##*/} + V=${V%.*} + V=${V:-out} + PREV= + for i in {15..00}; do + F="${RECORDDIR}/${V}-$i.txt" + if [ -e "${F}" ] && [ -n "${PREV}" ]; then + mv "${F}" "${PREV}" + fi + PREV="${F}" + done + RECORDFILE="${F}" + mkdir -p "${RECORDDIR}" + fi +else + RECORDFILE="/dev/null" +fi + +if [ "${VERBOSE}" == "yes" ]; then + echo "-- Settings" + echo "Arduino dir: ${ARDUINODIR}" + echo "Board: ${BOARD}" + echo "Port: ${PORT}" + echo "Speed: ${SPEED}" + echo "Fqbn: ${FQBN}" + echo "Upload: ${UPLOAD}" + echo "Catusb: ${CATUSB}" + echo "Recordusb: ${RECORDUSB}" + echo "Record file: ${RECORDFILE}" + echo "Verbose: ${VERBOSE}" + echo "File: ${FILE}" + echo "Build dir: ${BUILDDIR}" +fi + +set -e + +if [ "${COMPILE}" == "yes" ]; then + echo "-- Compile" + + mkdir -p "${BUILDDIR}" + + OPT_LIB= + TMP_ULIB=${ARDUINO_USER_LIBS:-} + if [ -n "${TMP_ULIB}" ]; then + OPT_LIB="-libraries ""${TMP_ULIB}""" + fi + + TESTPLAN_OPT="" + if [ -n "${TESTPLAN}" ]; then + TESTPLAN_OPT="-prefs=build.extra_flags=-DTESTPLAN=${TESTPLAN}" + fi + + # shellcheck disable=SC2086 + # (We don't want to quote OPT_LIB as it can contain multiple options.) + "${ARDUINODIR}/arduino-builder" \ + -hardware "${ARDUINODIR}/hardware" \ + -tools "${ARDUINODIR}/hardware/tools/avr" \ + -tools "${ARDUINODIR}/tools-builder" \ + -built-in-libraries "${ARDUINODIR}/libraries" \ + ${OPT_LIB} \ + -fqbn "${FQBN}" \ + -build-path "${BUILDDIR}" \ + ${TESTPLAN_OPT} \ + "${FILE}" +fi + +FILEBASENAME=${FILE##*/} + +if [ "${UPLOAD}" == "yes" ]; then + echo "-- Upload" + "/usr/bin/avrdude" \ + -q -q -patmega328p -carduino -P"${PORT}" -b"${SPEED}" -D \ + -Uflash:w:"${BUILDDIR}/${FILEBASENAME}".hex:i +fi + +if [ "${CATUSB}" == "yes" ] || [ "${STTY}" == "yes" ]; then + if [ -z "${READSPEED}" ]; then + TFILE=$(mktemp) + gcc -fpreprocessed -dD -x c++ -E "${FILE}" > "${TFILE}" + for sp in 9600 19200 28800 38400 57600 115200; do + if grep ${sp} "${TFILE}" > /dev/null; then + READSPEED=${sp} + fi + done + READSPEED=${READSPEED:-9600} + rm "${TFILE}" + fi + + stty -F "${PORT}" -hupcl -echo "${READSPEED}" + echo "-- usb setup with speed ${READSPEED}" +fi + +if [ "${CATUSB}" == "yes" ]; then + echo "-- Read usb (Ctrl-C to quit)" + DISPLAYSEP=yes + { + echo "speed=${READSPEED}" + echo "fqbn=${FQBN}" + echo "port=${PORT}" + echo "file=${FILE}" + echo "filedate=$(date +"%Y-%m-%dT%H:%M:%SZ" -d @$(stat -c '%Y' "${FILE}"))" + echo "date=$(date +'%Y-%m-%dT%H:%M:%SZ')" + echo "" + echo "-----BEGIN ARDUINO OUTPUT-----" + } | tee "${RECORDFILE}" + tee -a "${RECORDFILE}" < "${PORT}" +fi + diff --git a/examples/output-signal-timings/output-signal-timings.ino b/examples/output-signal-timings/output-signal-timings.ino new file mode 100644 index 0000000..f6314c0 --- /dev/null +++ b/examples/output-signal-timings/output-signal-timings.ino @@ -0,0 +1,102 @@ +// output-signal-timings.ino + +// Example sketch that comes along with RF433any library + +/* + Copyright 2021 Sébastien Millet + + `RF433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `RF433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +// +// Schematic: see RF433any library +// + +#include "RF433any.h" +#include + +#define PIN_RFINPUT 2 + +char serial_printf_buffer[150]; +void serial_printf(const char* msg, ...) + __attribute__((format(printf, 1, 2))); + + // NOTE + // Assume Serial has been initialized (Serial.begin(...)) +void serial_printf(const char* msg, ...) { + va_list args; + + va_start(args, msg); + + vsnprintf(serial_printf_buffer, sizeof(serial_printf_buffer), msg, args); + va_end(args); + Serial.print(serial_printf_buffer); +} + +void setup() { + pinMode(PIN_RFINPUT, INPUT); + Serial.begin(115200); + Serial.print("Waiting for signal\n"); +} + +Track track(PIN_RFINPUT); + +void output_timings(Decoder *pdec) { + TimingsExt tsext; + if (!pdec) + return; + pdec->get_tsext(&tsext); + serial_printf(" I=%u, LS=%u, LL=%u, HS=%u, HL=%u, S=%u, U=%u, V=%u, " + "Y=%u, Z=%u\n", tsext.initseq, tsext.low_short, tsext.low_long, + tsext.high_short, tsext.high_long, tsext.sep, tsext.first_low, + tsext.first_high, tsext.first_low_ignored, tsext.last_low); +} + +void loop() { + track.treset(); + + while (!track.do_events()) + delay(1); + + Decoder *pdec0 = track.get_data(RF433ANY_FD_DEDUP); + Decoder *pdec = pdec0; + while(pdec) { + BitVector *pdata = pdec->take_away_data(); + + serial_printf("Decoded: %s, err: %d, code: %c, " + "rep: %d, bits: %2d", + (pdec->data_got_decoded() ? "yes" : "no "), + pdec->get_nb_errors(), pdec->get_id_letter(), + pdec->get_repeats() + 1, pdec->get_nb_bits()); + + if (pdec->data_got_decoded()) { + Serial.print(", data: "); + if (pdata) { + char *buf = pdata->to_str(); + if (buf) { + Serial.print(buf); + free(buf); + } + delete pdata; + } +// output_timings(pdec); + } + Serial.print("\n"); + pdec = pdec->get_next(); + } + delete pdec0; +} + +// vim: ts=4:sw=4:tw=80:et diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..c9e61f3 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=RF433any +version=0.5 +author=Sébastien Millet +maintainer=Sébastien Millet +sentence=A library to decode any protocol received on a 433 Mhz Radio Frequence receiver like MX-RM-5V +paragraph=Use this library to decode any protocol received on a 433 Mhz Radio Frequence receiver like MX-RM-5V. Allow to display low level data (raw code), user-level data (code received) and code timings. +category=Communication +url=https://github.com/sebmillet/RF433any +architectures=avr +includes=RF433any.h diff --git a/schema.fzz b/schema.fzz new file mode 100644 index 0000000000000000000000000000000000000000..9e11c188513aadea03df79a9a38258a31615aba1 GIT binary patch literal 15867 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

al{i6{ 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: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%0AnaXMEv_4Z~HJ?kFv0o=_S4I$a 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 literal 0 HcmV?d00001 diff --git a/testplan/clean.sh b/testplan/clean.sh new file mode 100755 index 0000000..0641f21 --- /dev/null +++ b/testplan/clean.sh @@ -0,0 +1,22 @@ +#!/usr/bin/bash + +set -euo pipefail + +cd track + +for d in [0-9][0-9]; do + rm -f "${d}"/tmpout*.txt +done + +cd ../decoder + +for d in [0-9][0-9]; do + rm -f "${d}"/tmpout*.txt +done + +cd ../user + +for d in [0-9][0-9]; do + rm -f "${d}"/tmpout*.txt +done + diff --git a/testplan/decoder/01/code.txt b/testplan/decoder/01/code.txt new file mode 100644 index 0000000..719c5cd --- /dev/null +++ b/testplan/decoder/01/code.txt @@ -0,0 +1,12 @@ +0 , 9000 +1236, 576 +536, 1280 +1232, 608 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +528, 1316 +522, 7020 +0, 0 diff --git a/testplan/decoder/01/expect3.txt b/testplan/decoder/01/expect3.txt new file mode 100644 index 0000000..e0d0741 --- /dev/null +++ b/testplan/decoder/01/expect3.txt @@ -0,0 +1,2 @@ +[0] Received 9 bits: 01 6e + T=TRI, E=0, I=9000, S=572, L=1256, P=7020, Y=0, Z=522 diff --git a/testplan/decoder/01/expect4.txt b/testplan/decoder/01/expect4.txt new file mode 100644 index 0000000..c183e24 --- /dev/null +++ b/testplan/decoder/01/expect4.txt @@ -0,0 +1,2 @@ +[0] Received 9 bits: 01 6e + T=TRI, E=0, I=9000, S=572, L=1256, P=7020, Y=0, Z=528 diff --git a/testplan/decoder/02/code.txt b/testplan/decoder/02/code.txt new file mode 100644 index 0000000..ca09eef --- /dev/null +++ b/testplan/decoder/02/code.txt @@ -0,0 +1,12 @@ +0 , 9000 +1236, 576 +536, 1280 +1232, 608 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +1232, 1316 +522, 7020 +0, 0 diff --git a/testplan/decoder/02/expect3.txt b/testplan/decoder/02/expect3.txt new file mode 100644 index 0000000..2974998 --- /dev/null +++ b/testplan/decoder/02/expect3.txt @@ -0,0 +1,3 @@ +[0] Unknown encoding: 18 signal bits + Signal: LS:SL:LS:LS:SL:LS:LS:LS:LL:SP + T=UNK, E=0, I=9000, S=572, L=1256, P=7020, Y=0, Z=522 diff --git a/testplan/decoder/02/expect4.txt b/testplan/decoder/02/expect4.txt new file mode 100644 index 0000000..795253c --- /dev/null +++ b/testplan/decoder/02/expect4.txt @@ -0,0 +1,3 @@ +[0] Unknown encoding: 18 signal bits + Signal: LS:SL:LS:LS:SL:LS:LS:LS:LL:SP + T=UNK, E=0, I=9000, S=572, L=1256, P=7020, Y=0, Z=1232 diff --git a/testplan/decoder/03/code.txt b/testplan/decoder/03/code.txt new file mode 100644 index 0000000..427be11 --- /dev/null +++ b/testplan/decoder/03/code.txt @@ -0,0 +1,69 @@ +0, 6908 +1272, 520 +564, 1276 +1172, 632 +1160, 640 +472, 1284 +1252, 556 +592, 1240 +1292, 500 +572, 1200 +612, 1172 +1284, 528 +1260, 576 +528, 1336 +1164, 692 +428, 1380 +1172, 588 +520, 1228 +1268, 528 +1300, 512 +580, 1224 +1272, 552 +1268, 564 +560, 1300 +1216, 640 +448, 1356 +432, 1336 +468, 1292 +520, 1260 +560, 1252 +568, 1260 +560, 1284 +560, 1208 +560, 7036 +1152, 608 +484, 1272 +1232, 540 +1280, 540 +584, 1240 +1276, 548 +572, 1260 +1272, 540 +544, 1292 +476, 1352 +1136, 672 +1132, 656 +468, 1300 +1232, 588 +560, 1276 +1268, 524 +564, 1232 +1248, 556 +1240, 564 +528, 1320 +1172, 688 +1128, 728 +408, 1376 +1188, 580 +528, 1232 +548, 1236 +556, 1240 +552, 1260 +552, 1268 +552, 1308 +504, 1376 +452, 1352 +408, 6984 +1260, 520 +572, 1224 diff --git a/testplan/decoder/03/expect3.txt b/testplan/decoder/03/expect3.txt new file mode 100644 index 0000000..92d94c5 --- /dev/null +++ b/testplan/decoder/03/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 32 bits: b5 35 6d 00 + T=TRI, E=0, I=6908, S=598, L=1224, P=7036, Y=0, Z=560 +[1] Received 32 bits: b5 35 6d 00 + T=TRI, E=0, I=0, S=598, L=1224, P=6984, Y=0, Z=408 diff --git a/testplan/decoder/03/expect4.txt b/testplan/decoder/03/expect4.txt new file mode 100644 index 0000000..8c911cf --- /dev/null +++ b/testplan/decoder/03/expect4.txt @@ -0,0 +1,4 @@ +[0] Received 32 bits: b5 35 6d 00 + T=TRI, E=0, I=6908, S=598, L=1224, P=7036, Y=0, Z=572 +[1] Received 32 bits: b5 35 6d 00 + T=TRI, E=0, I=0, S=598, L=1224, P=6984, Y=0, Z=544 diff --git a/testplan/decoder/10/code-adf7.txt b/testplan/decoder/10/code-adf7.txt new file mode 100644 index 0000000..dd6acb1 --- /dev/null +++ b/testplan/decoder/10/code-adf7.txt @@ -0,0 +1,69 @@ +0, 5436 +1256, 1068 +1268, 2184 +1216, 1092 +1216, 1096 +1204, 1096 +1208, 1096 +1212, 1096 +2356, 2272 +1180, 1128 +2328, 2288 +1172, 1144 +1164, 1140 +2328, 1148 +1152, 1172 +1148, 2332 +2292, 2324 +2288, 2340 +1128, 1188 +2272, 1192 +1120, 2356 +1128, 1188 +1112, 1192 +1120, 1196 +2272, 1192 +1112, 1204 +1112, 6724 +1120, 1192 +1120, 2356 +1112, 1192 +1112, 1200 +1112, 1204 +1120, 1192 +1112, 1200 +2264, 2356 +1120, 1204 +2260, 2352 +1112, 1208 +1104, 1212 +2248, 1224 +1096, 1216 +1108, 2356 +2248, 2380 +2252, 2368 +1096, 1212 +2264, 1220 +1088, 2364 +1112, 1212 +1096, 1216 +1092, 1224 +2244, 1224 +1088, 1224 +1088, 6740 +1096, 1224 +1100, 2368 +1092, 1216 +1096, 1224 +1088, 1220 +1096, 1224 +1088, 1220 +2252, 2368 +1088, 1220 +2252, 2376 +1092, 1224 +1088, 1220 +2256, 1228 +1080, 1224 +1092, 2376 +2244, 2388 diff --git a/testplan/decoder/10/expect3.txt b/testplan/decoder/10/expect3.txt new file mode 100644 index 0000000..47f607c --- /dev/null +++ b/testplan/decoder/10/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=5436, S=1180, L=2270, P=6724, Y=0, Z=1112 +[1] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=0, S=1180, L=2270, P=6740, Y=0, Z=1088 diff --git a/testplan/decoder/10/expect4.txt b/testplan/decoder/10/expect4.txt new file mode 100644 index 0000000..61e2139 --- /dev/null +++ b/testplan/decoder/10/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=5436, S=1180, L=2270, P=6724, Y=0, Z=1180 +[1] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=0, S=1180, L=2270, P=6740, Y=0, Z=1120 +[2] Received 9 bits: 00 fd + T=MAN, E=0, I=0, S=1180, L=2270, P=0, Y=0, Z=1088 diff --git a/testplan/decoder/11/code-adf8.txt b/testplan/decoder/11/code-adf8.txt new file mode 100644 index 0000000..d2ab0ef --- /dev/null +++ b/testplan/decoder/11/code-adf8.txt @@ -0,0 +1,69 @@ +0, 5568 +1120, 2348 +2264, 1212 +1096, 1200 +1108, 2364 +1104, 1196 +1112, 1192 +2260, 2360 +1100, 1200 +2260, 2364 +1104, 1208 +1088, 1220 +2248, 1216 +1096, 1216 +1100, 2364 +2248, 2384 +2252, 2360 +1104, 1208 +2260, 1216 +1092, 2368 +1096, 1216 +1088, 1224 +1096, 1216 +1092, 1216 +1096, 1220 +1088, 1216 +1100, 5576 +1104, 2364 +2260, 1216 +1100, 1220 +1088, 2372 +1096, 1212 +1096, 1224 +2244, 2372 +1096, 1224 +2248, 2372 +1096, 1224 +1080, 1232 +2244, 1216 +1092, 1232 +1080, 2384 +2244, 2376 +2236, 2392 +1080, 1232 +2236, 1220 +1104, 2368 +1092, 1232 +1084, 1224 +1088, 1228 +1084, 1224 +1084, 1232 +1084, 1232 +1080, 5584 +1100, 2384 +2244, 1224 +1092, 1224 +1084, 2384 +1088, 1228 +1080, 1224 +2240, 2392 +1088, 1216 +2244, 2380 +1088, 1224 +1092, 1224 +2236, 1232 +1088, 1224 +1080, 2384 +2236, 2392 +2236, 2380 diff --git a/testplan/decoder/11/expect3.txt b/testplan/decoder/11/expect3.txt new file mode 100644 index 0000000..22e6402 --- /dev/null +++ b/testplan/decoder/11/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 32 bits: 8e dc 56 7f + T=MAN, E=0, I=5568, S=1154, L=2314, P=5576, Y=0, Z=1100 +[1] Received 32 bits: 8e dc 56 7f + T=MAN, E=0, I=0, S=1154, L=2314, P=5584, Y=0, Z=1080 diff --git a/testplan/decoder/11/expect4.txt b/testplan/decoder/11/expect4.txt new file mode 100644 index 0000000..893dc13 --- /dev/null +++ b/testplan/decoder/11/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 32 bits: 8e dc 56 7f + T=MAN, E=0, I=5568, S=1154, L=2314, P=5576, Y=0, Z=2260 +[1] Received 32 bits: 8e dc 56 7f + T=MAN, E=0, I=0, S=1154, L=2314, P=5584, Y=0, Z=2248 +[2] Received 11 bits: 04 76 + T=MAN, E=0, I=0, S=1154, L=2314, P=0, Y=0, Z=2244 diff --git a/testplan/decoder/12/code-adfF.txt b/testplan/decoder/12/code-adfF.txt new file mode 100644 index 0000000..b611fa1 --- /dev/null +++ b/testplan/decoder/12/code-adfF.txt @@ -0,0 +1,69 @@ +0, 5468 +1240, 2272 +1264, 1044 +1224, 1084 +1224, 1088 +1208, 1092 +1216, 1096 +1212, 1088 +2356, 2264 +1180, 1128 +2340, 2288 +1172, 1132 +1168, 1140 +2320, 1164 +1148, 1176 +1132, 2336 +2296, 2332 +2288, 2340 +1124, 1188 +2272, 1196 +1120, 2348 +1120, 1196 +1120, 1192 +1112, 1200 +2272, 1204 +1112, 1204 +1104, 6716 +1128, 2348 +1112, 1208 +1116, 1196 +1112, 1204 +1104, 1204 +1120, 1200 +1104, 1204 +2264, 2356 +1116, 1196 +2264, 2360 +1108, 1216 +1104, 1204 +2256, 1212 +1096, 1220 +1096, 2360 +2256, 2380 +2248, 2372 +1100, 1216 +2252, 1216 +1088, 2376 +1092, 1216 +1096, 1232 +1092, 1216 +2244, 1224 +1100, 1216 +1088, 6732 +1100, 2384 +1096, 1220 +1096, 1208 +1096, 1224 +1088, 1220 +1096, 1216 +1096, 1216 +2252, 2368 +1096, 1224 +2244, 2376 +1092, 1216 +1100, 1216 +2252, 1224 +1080, 1232 +1080, 2380 +2248, 2376 diff --git a/testplan/decoder/12/expect3.txt b/testplan/decoder/12/expect3.txt new file mode 100644 index 0000000..8ac85c3 --- /dev/null +++ b/testplan/decoder/12/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 32 bits: fe dc 56 78 + T=MAN, E=0, I=5468, S=1154, L=2310, P=6716, Y=0, Z=1104 +[1] Received 32 bits: fe dc 56 78 + T=MAN, E=0, I=0, S=1154, L=2310, P=6732, Y=0, Z=1088 diff --git a/testplan/decoder/12/expect4.txt b/testplan/decoder/12/expect4.txt new file mode 100644 index 0000000..6b185e6 --- /dev/null +++ b/testplan/decoder/12/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 32 bits: fe dc 56 78 + T=MAN, E=0, I=5468, S=1154, L=2310, P=6716, Y=0, Z=1180 +[1] Received 32 bits: fe dc 56 78 + T=MAN, E=0, I=0, S=1154, L=2310, P=6732, Y=0, Z=1116 +[2] Received 9 bits: 01 fd + T=MAN, E=0, I=0, S=1154, L=2310, P=0, Y=0, Z=1096 diff --git a/testplan/decoder/13/code-adf7-err.txt b/testplan/decoder/13/code-adf7-err.txt new file mode 100644 index 0000000..dc71bd7 --- /dev/null +++ b/testplan/decoder/13/code-adf7-err.txt @@ -0,0 +1,69 @@ +0, 5436 +1256, 1068 +1268, 2184 +1216, 1092 +1216, 1096 +1204, 1096 +1208, 1096 +1212, 1096 +2356, 2272 +1180, 1128 +2328, 2288 +1172, 1144 +1164, 1140 +2328, 1148 +1152, 1172 +1148, 2332 +2292, 2324 +2288, 2340 +1128, 1188 +2272, 1192 +1120, 2356 +1128, 1188 +1112, 1192 +1120, 1196 +2272, 1192 +1112, 1204 +1112, 6724 +1120, 1192 +1120, 2356 +1112, 1192 +1112, 1200 +1112, 1204 +1120, 1192 +1112, 1200 +2264, 2356 +1120, 1204 +2260, 2352 +1112, 1208 +1104, 1212 +2248, 1224 +1096, 1216 +1108, 2356 +2248, 2380 +1108, 2368 +1096, 1212 +2264, 1220 +1088, 2364 +1112, 1212 +1096, 1216 +1092, 1224 +2244, 1224 +1088, 1224 +1088, 6740 +1096, 1224 +1100, 2368 +1092, 1216 +1096, 1224 +1088, 1220 +1096, 1224 +1088, 1220 +2252, 2368 +1088, 1220 +2252, 2376 +1092, 1224 +1088, 1220 +2256, 1228 +1080, 1224 +1092, 2376 +2244, 2388 diff --git a/testplan/decoder/13/expect3.txt b/testplan/decoder/13/expect3.txt new file mode 100644 index 0000000..eb2046c --- /dev/null +++ b/testplan/decoder/13/expect3.txt @@ -0,0 +1,5 @@ +[0] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=5436, S=1180, L=2270, P=6724, Y=0, Z=1112 +[1] Unknown encoding: 50 signal bits + Signal: SS:SL:SS:SS:SS:SS:SS:LL:SS:LL:SS:SS:LS:SS:SL:LL:SL:SS:LS:SL:SS:SS:SS:LS:SS:SP + T=UNK, E=0, I=0, S=1180, L=2270, P=6740, Y=0, Z=1088 diff --git a/testplan/decoder/13/expect4.txt b/testplan/decoder/13/expect4.txt new file mode 100644 index 0000000..307e83e --- /dev/null +++ b/testplan/decoder/13/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 32 bits: 7e dc 56 78 + T=MAN, E=0, I=5436, S=1180, L=2270, P=6724, Y=0, Z=1180 +[1] Received 27 bits(!): 03 f6 e2 a3 + T=MAN, E=4, I=0, S=1180, L=2270, P=6740, Y=0, Z=1120 +[2] Received 9 bits: 00 fd + T=MAN, E=0, I=0, S=1180, L=2270, P=0, Y=0, Z=1088 diff --git a/testplan/decoder/20/code-portail1.txt b/testplan/decoder/20/code-portail1.txt new file mode 100644 index 0000000..d358515 --- /dev/null +++ b/testplan/decoder/20/code-portail1.txt @@ -0,0 +1,69 @@ +0, 14916 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/20/expect3.txt b/testplan/decoder/20/expect3.txt new file mode 100644 index 0000000..4cbd517 --- /dev/null +++ b/testplan/decoder/20/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=356, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/20/expect4.txt b/testplan/decoder/20/expect4.txt new file mode 100644 index 0000000..ff7e8d3 --- /dev/null +++ b/testplan/decoder/20/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=344 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=356, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/21/code-portail2.txt b/testplan/decoder/21/code-portail2.txt new file mode 100644 index 0000000..b2336ba --- /dev/null +++ b/testplan/decoder/21/code-portail2.txt @@ -0,0 +1,69 @@ +0, 14924 +340, 364 +348, 364 +348, 360 +348, 380 +332, 380 +332, 384 +328, 384 +332, 388 +324, 376 +336, 376 +340, 380 +340, 3740 +332, 760 +708, 368 +344, 760 +716, 368 +348, 752 +340, 752 +708, 380 +340, 760 +336, 760 +700, 392 +708, 384 +696, 392 +700, 400 +320, 772 +696, 392 +332, 760 +328, 768 +324, 768 +700, 396 +700, 384 +708, 396 +324, 768 +324, 768 +320, 768 +324, 768 +696, 400 +316, 772 +688, 408 +692, 400 +692, 404 +684, 408 +320, 776 +316, 768 +320, 776 +312, 784 +684, 400 +328, 768 +324, 768 +320, 776 +692, 400 +320, 776 +316, 776 +692, 404 +692, 400 +316, 772 +312, 776 +308, 780 +688, 408 +684, 408 +320, 776 +308, 784 +684, 408 +684, 408 +684, 404 +692, 400 +692, 3000 diff --git a/testplan/decoder/21/expect3.txt b/testplan/decoder/21/expect3.txt new file mode 100644 index 0000000..b44d5fe --- /dev/null +++ b/testplan/decoder/21/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14924, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3740, Y=0, Z=340 +[1] Received 55 bits: 29 3d 1c 2f 08 98 cf + T=TRI, E=0, I=0, S=356, L=734, P=3000, Y=0, Z=316 diff --git a/testplan/decoder/21/expect4.txt b/testplan/decoder/21/expect4.txt new file mode 100644 index 0000000..3fb7e01 --- /dev/null +++ b/testplan/decoder/21/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14924, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3740, Y=0, Z=324 +[1] Received 55 bits: 29 3d 1c 2f 08 98 cf + T=TRI, E=0, I=0, S=356, L=734, P=3000, Y=0, Z=336 diff --git a/testplan/decoder/22/code-portx.txt b/testplan/decoder/22/code-portx.txt new file mode 100644 index 0000000..8eb2715 --- /dev/null +++ b/testplan/decoder/22/code-portx.txt @@ -0,0 +1,70 @@ +0, 14916 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +328, 15284 +340, 388 +340, 380 +340, 376 +344, 384 +332, 388 +332, 388 +332, 384 +344, 376 +340, 388 +332, 388 +332, 384 +336, 3740 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +336, 3740 +0, 0 diff --git a/testplan/decoder/22/expect3.txt b/testplan/decoder/22/expect3.txt new file mode 100644 index 0000000..5918b88 --- /dev/null +++ b/testplan/decoder/22/expect3.txt @@ -0,0 +1,8 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=348 +[1] Received 33 bits: 01 42 3c 68 c0 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=344 +[2] Sync 11 + T=SYN, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=336 +[3] Received 9 bits: 01 42 + T=TRI, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=336 diff --git a/testplan/decoder/22/expect4.txt b/testplan/decoder/22/expect4.txt new file mode 100644 index 0000000..f41ea2c --- /dev/null +++ b/testplan/decoder/22/expect4.txt @@ -0,0 +1,8 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=344 +[1] Received 33 bits: 01 42 3c 68 c0 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=356 +[2] Sync 11 + T=SYN, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=340 +[3] Received 9 bits: 01 42 + T=TRI, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=356 diff --git a/testplan/decoder/23/code-porty.txt b/testplan/decoder/23/code-porty.txt new file mode 100644 index 0000000..7ce518e --- /dev/null +++ b/testplan/decoder/23/code-porty.txt @@ -0,0 +1,70 @@ +0, 14916 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +328, 15284 +340, 388 +340, 380 +340, 376 +344, 384 +332, 388 +332, 388 +332, 384 +344, 376 +340, 388 +332, 388 +332, 384 +336, 3740 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +340, 752 +328, 15284 diff --git a/testplan/decoder/23/expect3.txt b/testplan/decoder/23/expect3.txt new file mode 100644 index 0000000..61b8aeb --- /dev/null +++ b/testplan/decoder/23/expect3.txt @@ -0,0 +1,8 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=348 +[1] Received 33 bits: 01 42 3c 68 c0 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=344 +[2] Sync 11 + T=SYN, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=336 +[3] Received 10 bits: 02 84 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=328 diff --git a/testplan/decoder/23/expect4.txt b/testplan/decoder/23/expect4.txt new file mode 100644 index 0000000..35699c0 --- /dev/null +++ b/testplan/decoder/23/expect4.txt @@ -0,0 +1,8 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=344 +[1] Received 33 bits: 01 42 3c 68 c0 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=356 +[2] Sync 11 + T=SYN, E=0, I=0, S=356, L=734, P=3740, Y=0, Z=340 +[3] Received 10 bits: 02 84 + T=TRI, E=0, I=0, S=356, L=734, P=15284, Y=0, Z=356 diff --git a/testplan/decoder/24/code-portail1-err.txt b/testplan/decoder/24/code-portail1-err.txt new file mode 100644 index 0000000..15e0991 --- /dev/null +++ b/testplan/decoder/24/code-portail1-err.txt @@ -0,0 +1,69 @@ +0, 14916 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +717, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 384 diff --git a/testplan/decoder/24/expect3.txt b/testplan/decoder/24/expect3.txt new file mode 100644 index 0000000..b131121 --- /dev/null +++ b/testplan/decoder/24/expect3.txt @@ -0,0 +1,5 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=348 +[1] Unknown encoding: 66 signal bits + Signal: LS:SL:LS:SL:SL:SL:SL:LS:SL:SL:SL:LS:LS:LS:LS:SL:SL:SL:LS:LS:SL:LS:SL:SL:SL:LS:LS:SL:SL:SL:SL:SL:LL + T=UNK, E=0, I=0, S=356, L=734, P=0, Y=0, Z=717 diff --git a/testplan/decoder/24/expect4.txt b/testplan/decoder/24/expect4.txt new file mode 100644 index 0000000..cd146cf --- /dev/null +++ b/testplan/decoder/24/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=344 +[1] Received 48 bits(!): a1 1e 34 60 22 63 + T=TRI, E=1, I=0, S=356, L=734, P=0, Y=0, Z=356 diff --git a/testplan/decoder/25/code-portail1-err2.txt b/testplan/decoder/25/code-portail1-err2.txt new file mode 100644 index 0000000..65ead3f --- /dev/null +++ b/testplan/decoder/25/code-portail1-err2.txt @@ -0,0 +1,55 @@ +0, 14916 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +380, 380 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 384 +332, 760 +696, 392 +700, 392 +328, 15284 +340, 388 +340, 380 +340, 376 +344, 384 +332, 388 +332, 388 +332, 388 +332, 384 +336, 3740 +708, 388 +332, 760 +708, 384 +332, 752 +340, 752 +336, 752 +340, 752 +708, 392 diff --git a/testplan/decoder/25/expect3.txt b/testplan/decoder/25/expect3.txt new file mode 100644 index 0000000..023447f --- /dev/null +++ b/testplan/decoder/25/expect3.txt @@ -0,0 +1,7 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=348 +[1] Unknown encoding: 48 signal bits + Signal: LS:SL:SS:SL:SL:LS:SL:SL:SL:SL:LS:LS:SL:SL:SL:LS:LS:SL:SL:LS:LS:SL:LS:LS:SP + T=UNK, E=0, I=0, S=356, L=736, P=15284, Y=0, Z=328 +[2] Sync 8 + T=SYN, E=0, I=0, S=356, L=736, P=3740, Y=0, Z=336 diff --git a/testplan/decoder/25/expect4.txt b/testplan/decoder/25/expect4.txt new file mode 100644 index 0000000..acd2de4 --- /dev/null +++ b/testplan/decoder/25/expect4.txt @@ -0,0 +1,7 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=348, L(lo)=348, S(hi)=364, L(hi)=364, P=3724, Y=0, Z=344 +[1] Unknown encoding: 48 signal bits + Signal: LS:SL:SS:SL:SL:LS:SL:SL:SL:SL:LS:LS:SL:SL:SL:LS:LS:SL:SL:LS:LS:SL:LS:LS:SP + T=UNK, E=0, I=0, S=356, L=736, P=15284, Y=0, Z=340 +[2] Sync 8 + T=SYN, E=0, I=0, S=356, L=736, P=3740, Y=0, Z=336 diff --git a/testplan/decoder/30/code-sonoff1.txt b/testplan/decoder/30/code-sonoff1.txt new file mode 100644 index 0000000..0ffa9be --- /dev/null +++ b/testplan/decoder/30/code-sonoff1.txt @@ -0,0 +1,69 @@ +0, 5652 +1180, 300 +440, 924 +1164, 236 +1136, 264 +1156, 244 +408, 952 +416, 948 +1144, 256 +400, 964 +1124, 276 +400, 968 +400, 976 +1104, 304 +1088, 312 +392, 984 +1104, 296 +384, 992 +384, 992 +1096, 304 +380, 1000 +360, 1012 +1108, 304 +356, 1012 +372, 1008 +364, 10664 +1104, 308 +368, 1000 +1092, 316 +1084, 316 +1080, 328 +360, 1008 +372, 1008 +1084, 324 +364, 1008 +1080, 328 +356, 1012 +360, 1016 +1076, 332 +1076, 324 +356, 1032 +1068, 332 +340, 1040 +340, 1028 +1064, 340 +344, 1044 +332, 1040 +1052, 352 +340, 1036 +332, 1052 +324, 10688 +1068, 340 +340, 1028 +1072, 332 +1068, 344 +1052, 348 +340, 1052 +320, 1052 +1044, 348 +328, 1052 +1044, 356 +324, 1060 +328, 1052 +1036, 356 +1056, 352 +328, 1068 +1028, 364 +328, 1060 +308, 1068 diff --git a/testplan/decoder/30/expect3.txt b/testplan/decoder/30/expect3.txt new file mode 100644 index 0000000..f599694 --- /dev/null +++ b/testplan/decoder/30/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=5652, S=338, L=1044, P=10664, Y=0, Z=364 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=338, L=1044, P=10688, Y=0, Z=324 diff --git a/testplan/decoder/30/expect4.txt b/testplan/decoder/30/expect4.txt new file mode 100644 index 0000000..c882413 --- /dev/null +++ b/testplan/decoder/30/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=5652, S=338, L=1044, P=10664, Y=0, Z=400 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=338, L=1044, P=10688, Y=0, Z=364 +[2] Received 17 bits: 01 72 9a + T=TRI, E=0, I=0, S=338, L=1044, P=0, Y=0, Z=328 diff --git a/testplan/decoder/31/code-sonoff2.txt b/testplan/decoder/31/code-sonoff2.txt new file mode 100644 index 0000000..2eb3fe0 --- /dev/null +++ b/testplan/decoder/31/code-sonoff2.txt @@ -0,0 +1,69 @@ +0, 10680 +1100, 304 +368, 984 +1088, 304 +1092, 316 +1076, 324 +356, 1008 +356, 1012 +1076, 320 +348, 1024 +1064, 332 +344, 1028 +348, 1024 +1076, 332 +1076, 332 +340, 1048 +1052, 336 +340, 1044 +340, 1040 +1052, 340 +340, 1052 +324, 1044 +1052, 344 +340, 1052 +324, 1052 +328, 10700 +1060, 328 +348, 1044 +1052, 340 +1068, 340 +1060, 348 +344, 1044 +324, 1056 +1036, 348 +332, 1060 +1040, 360 +328, 1052 +328, 1052 +1036, 356 +1052, 364 +320, 1060 +1036, 372 +308, 1064 +312, 1060 +1036, 364 +320, 1068 +316, 1060 +1032, 372 +316, 1060 +320, 1060 +316, 10708 +1040, 356 +332, 1052 +1048, 352 +1048, 364 +1044, 360 +316, 1060 +320, 1052 +1044, 372 +312, 1060 +1036, 372 +308, 1068 +312, 1068 +1036, 348 +1060, 356 +328, 1060 +1028, 380 +312, 1060 +308, 1064 diff --git a/testplan/decoder/31/expect3.txt b/testplan/decoder/31/expect3.txt new file mode 100644 index 0000000..f71c6ee --- /dev/null +++ b/testplan/decoder/31/expect3.txt @@ -0,0 +1,4 @@ +[0] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=10680, S=336, L=1036, P=10700, Y=0, Z=328 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=336, L=1036, P=10708, Y=0, Z=316 diff --git a/testplan/decoder/31/expect4.txt b/testplan/decoder/31/expect4.txt new file mode 100644 index 0000000..e7586a3 --- /dev/null +++ b/testplan/decoder/31/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=10680, S=336, L=1036, P=10700, Y=0, Z=348 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=336, L=1036, P=10708, Y=0, Z=332 +[2] Received 17 bits: 01 72 9a + T=TRI, E=0, I=0, S=336, L=1036, P=0, Y=0, Z=312 diff --git a/testplan/decoder/32/code-sonoff1-err.txt b/testplan/decoder/32/code-sonoff1-err.txt new file mode 100644 index 0000000..90ae501 --- /dev/null +++ b/testplan/decoder/32/code-sonoff1-err.txt @@ -0,0 +1,69 @@ +0, 5652 +1180, 300 +440, 924 +1164, 236 +1136, 264 +1156, 244 +408, 952 +416, 948 +1144, 256 +400, 964 +400, 276 +400, 968 +400, 976 +1104, 304 +1088, 312 +392, 984 +1104, 296 +384, 992 +384, 992 +1096, 304 +380, 1000 +360, 1012 +1108, 304 +356, 1012 +372, 1008 +364, 10664 +1104, 308 +368, 1000 +1092, 316 +1084, 316 +1080, 328 +360, 1008 +372, 1008 +1084, 324 +364, 1008 +1080, 328 +356, 1012 +360, 1016 +1076, 332 +1076, 324 +356, 1032 +1068, 332 +340, 1040 +340, 1028 +1064, 340 +344, 1044 +332, 1040 +1052, 352 +340, 1036 +332, 1052 +324, 10688 +1068, 340 +340, 1028 +1072, 332 +1068, 344 +1052, 348 +340, 1052 +320, 1052 +1044, 348 +328, 1052 +1044, 356 +324, 1060 +328, 1052 +1036, 356 +1056, 352 +328, 1068 +1028, 364 +328, 1060 +308, 1068 diff --git a/testplan/decoder/32/expect3.txt b/testplan/decoder/32/expect3.txt new file mode 100644 index 0000000..6c0eade --- /dev/null +++ b/testplan/decoder/32/expect3.txt @@ -0,0 +1,5 @@ +[0] Unknown encoding: 48 signal bits + Signal: LS:SL:LS:LS:LS:SL:SL:LS:SL:SS:SL:SL:LS:LS:SL:LS:SL:SL:LS:SL:SL:LS:SL:SL:SP + T=UNK, E=0, I=5652, S=338, L=1044, P=10664, Y=0, Z=364 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=338, L=1044, P=10688, Y=0, Z=324 diff --git a/testplan/decoder/32/expect4.txt b/testplan/decoder/32/expect4.txt new file mode 100644 index 0000000..7cceb4a --- /dev/null +++ b/testplan/decoder/32/expect4.txt @@ -0,0 +1,6 @@ +[0] Received 23 bits(!): 5c 8d 24 + T=TRI, E=1, I=5652, S=338, L=1044, P=10664, Y=0, Z=400 +[1] Received 24 bits: b9 4d 24 + T=TRI, E=0, I=0, S=338, L=1044, P=10688, Y=0, Z=364 +[2] Received 17 bits: 01 72 9a + T=TRI, E=0, I=0, S=338, L=1044, P=0, Y=0, Z=328 diff --git a/testplan/decoder/40/code-flo1.txt b/testplan/decoder/40/code-flo1.txt new file mode 100644 index 0000000..2ae4afd --- /dev/null +++ b/testplan/decoder/40/code-flo1.txt @@ -0,0 +1,53 @@ +0, 23908 +700, 644 +1340, 1356 +668, 632 +1388, 1296 +728, 604 +1388, 1292 +720, 612 +1372, 1308 +700, 632 +1344, 1352 +640, 688 +1320, 1368 +656, 23912 +724, 624 +1396, 1296 +712, 620 +1368, 1336 +672, 664 +1332, 1372 +636, 700 +1312, 1376 +664, 660 +1352, 1340 +668, 664 +1336, 1352 +664, 23936 +672, 668 +1324, 1372 +644, 692 +1320, 1372 +664, 660 +1352, 1344 +676, 656 +1344, 1344 +672, 668 +1328, 1368 +640, 700 +1308, 1380 +628, 23956 +680, 656 +1360, 1328 +692, 640 +1368, 1336 +688, 656 +1336, 1360 +640, 700 +1308, 1380 +636, 700 +1320, 1376 +648, 684 +1332, 1356 +656, 23936 diff --git a/testplan/decoder/40/expect3.txt b/testplan/decoder/40/expect3.txt new file mode 100644 index 0000000..45b017e --- /dev/null +++ b/testplan/decoder/40/expect3.txt @@ -0,0 +1,8 @@ +[0] Received 12 bits: 05 55 + T=TRN, E=0, I=23908, S=650, L=1348, P=23912, Y=650, Z=656 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=664 +[2] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23956, Y=650, Z=628 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=656 diff --git a/testplan/decoder/40/expect4.txt b/testplan/decoder/40/expect4.txt new file mode 100644 index 0000000..fe76ad1 --- /dev/null +++ b/testplan/decoder/40/expect4.txt @@ -0,0 +1,8 @@ +[0] Received 12 bits: 05 55 + T=TRN, E=0, I=23908, S=650, L=1348, P=23912, Y=650, Z=700 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=664 +[2] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23956, Y=650, Z=672 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=636 diff --git a/testplan/decoder/41/code-flo2.txt b/testplan/decoder/41/code-flo2.txt new file mode 100644 index 0000000..9404587 --- /dev/null +++ b/testplan/decoder/41/code-flo2.txt @@ -0,0 +1,60 @@ +0, 23908 +736, 596 +1368, 1344 +676, 656 +1336, 1336 +680, 636 +1372, 1308 +720, 604 +1396, 1292 +708, 616 +1376, 1320 +664, 676 +1324, 1380 +636, 23956 +700, 636 +1372, 1308 +700, 632 +1368, 1328 +704, 640 +1360, 1348 +664, 680 +1320, 1384 +632, 692 +1332, 1364 +672, 664 +1352, 1344 +636, 23964 +644, 700 +1328, 1360 +664, 676 +1344, 1344 +672, 672 +1344, 1352 +656, 676 +1332, 1372 +636, 704 +1296, 1396 +624, 708 +1316, 1380 +640, 23952 +680, 652 +1344, 1348 +676, 664 +1328, 1376 +632, 708 +1308, 1388 +636, 700 +1320, 1368 +656, 684 +1340, 1356 +648, 684 +1328, 1368 +648, 23952 +640, 700 +1316, 1380 +648, 688 +1336, 1360 +664, 668 +1344, 1360 +664, 668 diff --git a/testplan/decoder/41/expect3.txt b/testplan/decoder/41/expect3.txt new file mode 100644 index 0000000..0c0581a --- /dev/null +++ b/testplan/decoder/41/expect3.txt @@ -0,0 +1,8 @@ +[0] Received 12 bits: 05 55 + T=TRN, E=0, I=23908, S=666, L=1356, P=23956, Y=666, Z=636 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23964, Y=666, Z=636 +[2] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23952, Y=666, Z=640 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23952, Y=666, Z=648 diff --git a/testplan/decoder/41/expect4.txt b/testplan/decoder/41/expect4.txt new file mode 100644 index 0000000..e262718 --- /dev/null +++ b/testplan/decoder/41/expect4.txt @@ -0,0 +1,8 @@ +[0] Received 12 bits: 05 55 + T=TRN, E=0, I=23908, S=666, L=1356, P=23956, Y=666, Z=708 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23964, Y=666, Z=632 +[2] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23952, Y=666, Z=636 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=666, L=1356, P=23952, Y=666, Z=656 diff --git a/testplan/decoder/42/code-flo1-err.txt b/testplan/decoder/42/code-flo1-err.txt new file mode 100644 index 0000000..950471a --- /dev/null +++ b/testplan/decoder/42/code-flo1-err.txt @@ -0,0 +1,53 @@ +0, 23908 +700, 644 +1340, 1356 +668, 632 +1388, 1296 +728, 604 +720, 612 +1388, 1292 +1372, 1308 +700, 632 +1344, 1352 +640, 688 +1320, 1368 +656, 23912 +724, 624 +1396, 1296 +712, 620 +1368, 1336 +672, 664 +1332, 1372 +636, 700 +1312, 1376 +664, 660 +1352, 1340 +668, 664 +1336, 1352 +664, 23936 +680, 656 +1360, 1328 +692, 640 +1368, 1336 +688, 656 +1336, 1360 +640, 700 +1308, 1380 +636, 700 +636, 1376 +648, 684 +1332, 1356 +656, 23936 +672, 668 +1324, 1372 +644, 692 +1320, 1372 +664, 660 +1352, 1344 +676, 656 +1344, 1344 +672, 668 +1328, 1368 +640, 700 +1308, 1380 +628, 23956 diff --git a/testplan/decoder/42/expect3.txt b/testplan/decoder/42/expect3.txt new file mode 100644 index 0000000..9e4dd85 --- /dev/null +++ b/testplan/decoder/42/expect3.txt @@ -0,0 +1,10 @@ +[0] Unknown encoding: 24 signal bits + Signal: SS:LL:SS:LL:SS:SS:LL:LL:SS:LL:SS:LL:SP + T=UNK, E=0, I=23908, S=650, L=1348, P=23912, Y=0, Z=656 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=664 +[2] Unknown encoding: 24 signal bits + Signal: SS:LL:SS:LL:SS:LL:SS:LL:SS:SL:SS:LL:SP + T=UNK, E=0, I=0, S=650, L=1348, P=23936, Y=0, Z=656 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23956, Y=650, Z=628 diff --git a/testplan/decoder/42/expect4.txt b/testplan/decoder/42/expect4.txt new file mode 100644 index 0000000..d473b22 --- /dev/null +++ b/testplan/decoder/42/expect4.txt @@ -0,0 +1,9 @@ +[0] Unknown encoding: 24 signal bits + Signal: SS:LL:SS:LL:SS:SS:LL:LL:SS:LL:SS:LL:SP + T=UNK, E=0, I=23908, S=650, L=1348, P=23912, Y=0, Z=700 +[1] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23936, Y=650, Z=664 +[2] Received 11 bits(!): 02 ad + T=TRN, E=1, I=0, S=650, L=1348, P=23936, Y=650, Z=636 +[3] Received 12 bits: 05 55 + T=TRN, E=0, I=0, S=650, L=1348, P=23956, Y=650, Z=672 diff --git a/testplan/decoder/50/code-portaila.txt b/testplan/decoder/50/code-portaila.txt new file mode 100644 index 0000000..2127ebf --- /dev/null +++ b/testplan/decoder/50/code-portaila.txt @@ -0,0 +1,70 @@ +0, 14916 +2500, 2500 +356, 364 +348, 364 +348, 360 +348, 372 +340, 372 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/50/expect3.txt b/testplan/decoder/50/expect3.txt new file mode 100644 index 0000000..b9c1cae --- /dev/null +++ b/testplan/decoder/50/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/50/expect4.txt b/testplan/decoder/50/expect4.txt new file mode 100644 index 0000000..b56ac39 --- /dev/null +++ b/testplan/decoder/50/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 11 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/51/code-portailb.txt b/testplan/decoder/51/code-portailb.txt new file mode 100644 index 0000000..62e1524 --- /dev/null +++ b/testplan/decoder/51/code-portailb.txt @@ -0,0 +1,66 @@ +0, 14916 +2500, 2500 +356, 364 +340, 368 +348, 372 +340, 372 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/51/expect3.txt b/testplan/decoder/51/expect3.txt new file mode 100644 index 0000000..8da1537 --- /dev/null +++ b/testplan/decoder/51/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 7 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/51/expect4.txt b/testplan/decoder/51/expect4.txt new file mode 100644 index 0000000..991092e --- /dev/null +++ b/testplan/decoder/51/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 7 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/52/code-portailc.txt b/testplan/decoder/52/code-portailc.txt new file mode 100644 index 0000000..f6696f3 --- /dev/null +++ b/testplan/decoder/52/code-portailc.txt @@ -0,0 +1,67 @@ +0, 14916 +2500, 2500 +356, 364 +340, 368 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/52/expect3.txt b/testplan/decoder/52/expect3.txt new file mode 100644 index 0000000..0595f23 --- /dev/null +++ b/testplan/decoder/52/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 8 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/52/expect4.txt b/testplan/decoder/52/expect4.txt new file mode 100644 index 0000000..8403b04 --- /dev/null +++ b/testplan/decoder/52/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 8 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=2500, V=2500, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/53/code-portaild.txt b/testplan/decoder/53/code-portaild.txt new file mode 100644 index 0000000..736d4aa --- /dev/null +++ b/testplan/decoder/53/code-portaild.txt @@ -0,0 +1,66 @@ +0, 14916 +356, 364 +340, 368 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/53/expect3.txt b/testplan/decoder/53/expect3.txt new file mode 100644 index 0000000..e122720 --- /dev/null +++ b/testplan/decoder/53/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 8 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/53/expect4.txt b/testplan/decoder/53/expect4.txt new file mode 100644 index 0000000..0089a4e --- /dev/null +++ b/testplan/decoder/53/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 8 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/54/code-portaile.txt b/testplan/decoder/54/code-portaile.txt new file mode 100644 index 0000000..a0214d0 --- /dev/null +++ b/testplan/decoder/54/code-portaile.txt @@ -0,0 +1,67 @@ +0, 14916 +356, 364 +340, 368 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/54/expect3.txt b/testplan/decoder/54/expect3.txt new file mode 100644 index 0000000..0c357a3 --- /dev/null +++ b/testplan/decoder/54/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 9 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/54/expect4.txt b/testplan/decoder/54/expect4.txt new file mode 100644 index 0000000..4ce5671 --- /dev/null +++ b/testplan/decoder/54/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 9 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/55/code-portailf.txt b/testplan/decoder/55/code-portailf.txt new file mode 100644 index 0000000..8abc913 --- /dev/null +++ b/testplan/decoder/55/code-portailf.txt @@ -0,0 +1,68 @@ +0, 14916 +356, 364 +340, 368 +348, 372 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/55/expect3.txt b/testplan/decoder/55/expect3.txt new file mode 100644 index 0000000..3ce09f4 --- /dev/null +++ b/testplan/decoder/55/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 10 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/55/expect4.txt b/testplan/decoder/55/expect4.txt new file mode 100644 index 0000000..2fc9d0f --- /dev/null +++ b/testplan/decoder/55/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 10 + T=SYN, E=0, I=14916, S(lo)=340, L(lo)=340, S(hi)=368, L(hi)=368, P=3724, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=354, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/56/code-portailg.txt b/testplan/decoder/56/code-portailg.txt new file mode 100644 index 0000000..7d6c8e9 --- /dev/null +++ b/testplan/decoder/56/code-portailg.txt @@ -0,0 +1,68 @@ +0, 14916 +3000, 3000 +356, 364 +340, 368 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/56/expect3.txt b/testplan/decoder/56/expect3.txt new file mode 100644 index 0000000..bd3c65f --- /dev/null +++ b/testplan/decoder/56/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 9 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=3000, V=3000, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/56/expect4.txt b/testplan/decoder/56/expect4.txt new file mode 100644 index 0000000..07edaa6 --- /dev/null +++ b/testplan/decoder/56/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 9 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=3000, V=3000, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/decoder/57/code-portailh.txt b/testplan/decoder/57/code-portailh.txt new file mode 100644 index 0000000..d3c1a03 --- /dev/null +++ b/testplan/decoder/57/code-portailh.txt @@ -0,0 +1,69 @@ +0, 14916 +1200, 1200 +356, 364 +340, 368 +348, 372 +348, 372 +340, 372 +344, 364 +344, 364 +340, 380 +340, 368 +340, 368 +348, 3724 +716, 380 +340, 752 +716, 380 +348, 744 +340, 752 +340, 744 +356, 740 +720, 372 +356, 740 +344, 756 +340, 744 +724, 376 +716, 376 +708, 380 +712, 388 +340, 744 +340, 752 +340, 752 +716, 380 +716, 376 +340, 752 +716, 376 +340, 752 +344, 752 +340, 752 +716, 380 +716, 376 +344, 752 +340, 752 +340, 756 +332, 760 +332, 760 +344, 752 +340, 752 +336, 760 +708, 384 +336, 760 +332, 752 +344, 752 +716, 376 +340, 756 +340, 752 +716, 384 +708, 388 +332, 760 +336, 752 +340, 752 +716, 380 +708, 392 +328, 760 +340, 752 +708, 384 +704, 388 +708, 384 +708, 388 +708, 3000 diff --git a/testplan/decoder/57/expect3.txt b/testplan/decoder/57/expect3.txt new file mode 100644 index 0000000..777f9bd --- /dev/null +++ b/testplan/decoder/57/expect3.txt @@ -0,0 +1,4 @@ +[0] Sync 10 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=1200, V=1200, Y=0, Z=348 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=344 diff --git a/testplan/decoder/57/expect4.txt b/testplan/decoder/57/expect4.txt new file mode 100644 index 0000000..90b9192 --- /dev/null +++ b/testplan/decoder/57/expect4.txt @@ -0,0 +1,4 @@ +[0] Sync 10 + T=SYN, E=0, I=14916, S(lo)=356, L(lo)=356, S(hi)=364, L(hi)=364, P=3724, U=1200, V=1200, Y=0, Z=340 +[1] Received 55 bits: 50 8f 1a 30 08 98 cf + T=TRI, E=0, I=0, S=360, L=734, P=3000, Y=0, Z=356 diff --git a/testplan/exectest.sh b/testplan/exectest.sh new file mode 100755 index 0000000..cd6dcc2 --- /dev/null +++ b/testplan/exectest.sh @@ -0,0 +1,25 @@ +#!/usr/bin/bash + +set -euo pipefail + +INPFILE=${1:-} +OUTFILE=${2:-} +PORT=${3:-} + +if [ -z "${PORT}" ] || [ -n "${4:-}" ]; then + echo "Usage:" + echo " exectest.sh INPFILE OUTFILE PORT" + echo "Example:" + echo " exectest.sh codes.txt results.txt /dev/ttyUSB0" + exit 1 +fi + +../read_test_result_from_board.sh "${PORT}" > "${OUTFILE}" & + +sleep 0.2 + +cat "${INPFILE}" > "${PORT}" +echo "." > "${PORT}" + +wait + diff --git a/testplan/read_test_result_from_board.sh b/testplan/read_test_result_from_board.sh new file mode 100755 index 0000000..5d14e84 --- /dev/null +++ b/testplan/read_test_result_from_board.sh @@ -0,0 +1,44 @@ +#!/usr/bin/bash + +# read_test_result_from_board.sh + +# Copyright 2021 Sébastien Millet + +# Read the test result from the board on PORT specified as argument, typically +# /dev/ttyUSB0. +# The test result is supposed enclosed between +# ----- BEGIN TEST ----- +# and +# ----- END TEST ----- +# lines. + +set -euo pipefail + +# Trick to read line by line in bash: +# https://stackoverflow.com/questions/10929453/read-a-file-line-by-line-assigning-the-value-to-a-variable + +REC=0 + +PORT=${1:-} + +if [ -z "${PORT}" ] || [ -n "${2-}" ]; then + echo "Usage:" + echo " read_test_result_from_board.sh PORT" + echo "For example:" + echo " read_test_result_from_board.sh /dev/ttyUSB0" + exit 1 +fi + +while IFS= read -r l; do + if [ "${l}" == "----- END TEST -----" ]; then + exit 0 + fi + if [ $REC -eq 1 ]; then + echo "${l}" + fi + if [ "${l}" == "----- BEGIN TEST -----" ]; then + REC=1 + fi +done < "${PORT}" + +exit 1 diff --git a/testplan/test/Makefile b/testplan/test/Makefile new file mode 100644 index 0000000..ba06c3f --- /dev/null +++ b/testplan/test/Makefile @@ -0,0 +1,10 @@ +ARDUINO_DIR = /usr/share/arduino +ARDUINO_LIBS = RF433any +ARDMK_DIR = /home/sebastien/.arduino_mk + +# USER_LIB_PATH = /home/sebastien/travail/cpp/seb/arduino/libraries + +BOARD_TAG = uno +MCU = atmega328 + +include $(ARDMK_DIR)/Arduino.mk diff --git a/testplan/test/am b/testplan/test/am new file mode 100755 index 0000000..e0d6bd7 --- /dev/null +++ b/testplan/test/am @@ -0,0 +1,345 @@ +#!/usr/bin/bash + +# am +# Has an update specific to RF433any library (RF433ANY_TESTPLAN macro used +# instead of TESTPLAN). + +# Copyright 2019, 2020, 2021 Sébastien Millet + +# Can perform the following: +# 1. Compile the code +# 2. Upload to Arduino +# 3. Read (continually) what is arriving from the USB port the Arduino is +# connected to + +# Versions history (as of 1.3) +# 1.3 Output from Arduino is recorded in files named with numbers instead of +# date-time string. +# 1.4 Adds -t (--testplan) option, to set TESTPLAN macro +# 1.5 -t (or --testplan) now comes with a value, so as to manage multiple test +# plans. +# 1.6 Updated to work fine with Arch arduino package instead of the manually +# installed (from tar.gz source) package used so far. +# 1.7 Renames archlinux-arduino back to arduino, and created corresponding +# symlink (was cleaner to do s). + +set -euo pipefail + +VERSION=1.7 + +PORT= +BOARD= +SPEED= +FQBN= +BUILDDIR= +RECORDDIR=out +READSPEED= +RECORDFILE= + +UPLOAD="no" +VERBOSE="no" +CATUSB="no" +STTY="no" +RECORDUSB="no" +COMPILE="yes" +TESTPLAN= + +DISPLAYSEP=no + +function finish { + if [ "${DISPLAYSEP}" == "yes" ]; then + echo "-----END ARDUINO OUTPUT-----" | tee -a "${RECORDFILE}" + fi +} + +trap finish EXIT + +function usage { + echo "Usage:" + echo " am [OPTIONS...] FILE" + echo "Compile FILE using arduino-builder." + echo "Example: am sketch.ino" + echo "" + echo "ENVIRONMENT VARIABLES" + echo " If ARDUINO_USER_LIBS is defined and non empty, then arduino-builder" + echo " is called with the supplementary option -libraries followed by" + echo " ARDUINO_USER_LIBS' value." + echo "" + echo "OPTIONS" + echo " -h --help Display this help screen" + echo " -V --version Output version information and quit" + echo " -v --verbose Be more talkative" + echo " -u --upload Upload compiled code into Arduino" + echo " -b --board Board, either 'uno' or 'nano'" + echo " -p --port Port, for ex. '/dev/ttyUSB0'" + echo " -s --speed Upload speed, for ex. 115200" + echo " Normally, speed is infered from device type:" + echo " 115200 for Uno, 57600 for Nano" + echo " -B --fqbn Board Fully Qualified Name, like 'arduino:avr:uno'" + echo " -d --builddir Build directory" + echo " -c --catusb Display (continually) what Arduino writes on USB" + echo " --stty Tune stty properly for later communication (implied" + echo " by --catusb)" + echo " -r --recordusb Write USB (continually) to a file (implies -c)" + echo " --recordfile Output file if -r option is set" + echo " -n --nocompile Don't compile code" + echo " --readspeed Read speed of USB. If not specified, this script" + echo " will try to infere it from source file. If it" + echo " fails, it'll fallback to 9600." + echo " This option is useful only if USB is read" + echo " (-c or --stty option set)" + echo " -t --testplan Set TESTPLAN macro value" + echo " (as if #define TESTPLAN VALUE)" + exit 1 +} + +function version { + echo "am version ${VERSION}" + exit +} + +OPTS=$(getopt -o hVvub:p:s:B:d:crnt: --long help,version,verbose,upload,board:,port:,speed:,fqbn:,builddir:,catusb,stty,recordusb,nocompile,readspeed:,recordfile:,testplan: -n 'am' -- "$@") + +eval set -- "$OPTS" + +while true; do + case "$1" in + -h | --help ) usage; shift ;; + -V | --version ) version; shift ;; + -v | --verbose ) VERBOSE="yes"; shift ;; + -u | --upload ) UPLOAD="yes"; shift ;; + -b | --board ) BOARD="$2"; shift 2 ;; + -p | --port ) PORT="$2"; shift 2 ;; + -s | --speed ) SPEED="$2"; shift 2 ;; + -B | --fqbn ) FQBN="$2"; shift 2 ;; + -d | --builddir ) BUILDDIR="$2"; shift 2 ;; + -c | --catusb ) CATUSB="yes"; shift ;; + -r | --recordusb ) RECORDUSB="yes"; CATUSB="yes"; shift ;; + -n | --nocompile ) COMPILE="no"; shift ;; + --readspeed ) READSPEED="$2"; shift 2 ;; + --recordfile ) RECORDFILE="$2"; shift 2 ;; + --stty ) STTY="yes"; shift ;; + -t | --testplan ) TESTPLAN="$2"; shift 2 ;; + -- ) shift; break ;; + * ) break ;; + esac +done + +FILE=${1:-} +TRAILINGOPTS=${2:-} + +if [ -n "${TRAILINGOPTS}" ]; then + echo "Error: trailing options" + exit 1; +fi +if [ -z "${FILE}" ]; then + echo "Error: no input file" + exit 1; +fi + +set +e + +if [ -n "${BOARD}" ]; then + if [ "${BOARD}" != "uno" ] && [ "${BOARD}" != "nano" ]; then + echo "Error: board '${BOARD}' unknown" + exit 1 + fi +fi + +#ARDUINODIR=$(LANG='' type -a arduino \ +# | tail -n 1 \ +# | sed 's/\S\+\sis\s//') +#ARDUINODIR=$(readlink -f "${ARDUINODIR}") +#ARDUINODIR=$(dirname "${ARDUINODIR}") + +ARDUINODIR=/usr/share/arduino + +COUNTUNO=$(compgen -G '/dev/ttyACM*' | wc -l) +COUNTNANO=$(compgen -G '/dev/ttyUSB*' | wc -l) + +if [ -z "${BOARD}" ]; then + if [ "${COUNTUNO}" -ge 1 ] && [ "${COUNTNANO}" -ge 1 ]; then + echo "Error: cannot guess board, found ${COUNTUNO} uno(s), ${COUNTNANO} nano(s)" + exit 10 + fi + if [ "${COUNTUNO}" -ge 1 ]; then + BOARD=uno + elif [ "${COUNTNANO}" -ge 1 ]; then + BOARD=nano + fi + if [ -z "${BOARD}" ]; then + echo "Error: cannot guess board, none found"; + exit 10 + fi +fi + +if [ "${UPLOAD}" == "yes" ] || [ "${CATUSB}" == "yes" ]; then + if [ -z "${PORT}" ]; then + if [ "${BOARD}" == "uno" ]; then + COUNT=${COUNTUNO} + PORT=$(compgen -G '/dev/ttyACM*') + elif [ "${BOARD}" == "nano" ]; then + COUNT=${COUNTNANO} + PORT=$(compgen -G '/dev/ttyUSB*') + else + echo "FATAL #001, CHECK THIS CODE" + exit 99 + fi + + if [ "${COUNT}" -ge 2 ]; then + echo "Error: cannot guess port, more than 1 board '${BOARD}' found" + exit 10 + fi + if [ -z "${PORT}" ]; then + echo "Error: cannot guess port, none found" + exit 10 + fi + fi + + if [ -z "${SPEED}" ]; then + if [ "${BOARD}" == "uno" ]; then + SPEED=115200 + elif [ "${BOARD}" == "nano" ]; then + SPEED=57600 + else + echo "FATAL #002, CHECK THIS CODE" + exit 99 + fi + fi + + if [ ! -e "${PORT}" ]; then + echo "Error: port not found" + exit 10 + fi +fi + +if [ -z "${FQBN}" ]; then + if [ "${BOARD}" == "uno" ]; then + FQBN="arduino:avr:uno" + elif [ "${BOARD}" == "nano" ]; then + FQBN="arduino:avr:nano:cpu=atmega328old" + else + echo "FATAL #003, CHECK THIS CODE" + exit 99 + fi +fi + +if [ -z "${BUILDDIR}" ]; then + if [[ "${FILE}" == */* ]]; then + BUILDDIR=${FILE%/*} + BUILDDIR="${BUILDDIR%/}/build" + else + BUILDDIR=build + fi +fi + +if [ "${RECORDUSB}" == "yes" ]; then + if [ -z "${RECORDFILE}" ]; then + V=${FILE##*/} + V=${V%.*} + V=${V:-out} + PREV= + for i in {15..00}; do + F="${RECORDDIR}/${V}-$i.txt" + if [ -e "${F}" ] && [ -n "${PREV}" ]; then + mv "${F}" "${PREV}" + fi + PREV="${F}" + done + RECORDFILE="${F}" + mkdir -p "${RECORDDIR}" + fi +else + RECORDFILE="/dev/null" +fi + +if [ "${VERBOSE}" == "yes" ]; then + echo "-- Settings" + echo "Arduino dir: ${ARDUINODIR}" + echo "Board: ${BOARD}" + echo "Port: ${PORT}" + echo "Speed: ${SPEED}" + echo "Fqbn: ${FQBN}" + echo "Upload: ${UPLOAD}" + echo "Catusb: ${CATUSB}" + echo "Recordusb: ${RECORDUSB}" + echo "Record file: ${RECORDFILE}" + echo "Verbose: ${VERBOSE}" + echo "File: ${FILE}" + echo "Build dir: ${BUILDDIR}" +fi + +set -e + +if [ "${COMPILE}" == "yes" ]; then + echo "-- Compile" + + mkdir -p "${BUILDDIR}" + + OPT_LIB= + TMP_ULIB=${ARDUINO_USER_LIBS:-} + if [ -n "${TMP_ULIB}" ]; then + OPT_LIB="-libraries ""${TMP_ULIB}""" + fi + + TESTPLAN_OPT="" + if [ -n "${TESTPLAN}" ]; then + TESTPLAN_OPT="-prefs=build.extra_flags=-DRF433ANY_TESTPLAN=${TESTPLAN}" + fi + + # shellcheck disable=SC2086 + # (We don't want to quote OPT_LIB as it can contain multiple options.) + "${ARDUINODIR}/arduino-builder" \ + -hardware "${ARDUINODIR}/hardware" \ + -tools "${ARDUINODIR}/hardware/tools/avr" \ + -tools "${ARDUINODIR}/tools-builder" \ + -built-in-libraries "${ARDUINODIR}/libraries" \ + ${OPT_LIB} \ + -fqbn "${FQBN}" \ + -build-path "${BUILDDIR}" \ + ${TESTPLAN_OPT} \ + "${FILE}" +fi + +FILEBASENAME=${FILE##*/} + +if [ "${UPLOAD}" == "yes" ]; then + echo "-- Upload" + "/usr/bin/avrdude" \ + -q -q -patmega328p -carduino -P"${PORT}" -b"${SPEED}" -D \ + -Uflash:w:"${BUILDDIR}/${FILEBASENAME}".hex:i +fi + +if [ "${CATUSB}" == "yes" ] || [ "${STTY}" == "yes" ]; then + if [ -z "${READSPEED}" ]; then + TFILE=$(mktemp) + gcc -fpreprocessed -dD -x c++ -E "${FILE}" > "${TFILE}" + for sp in 9600 19200 28800 38400 57600 115200; do + if grep ${sp} "${TFILE}" > /dev/null; then + READSPEED=${sp} + fi + done + READSPEED=${READSPEED:-9600} + rm "${TFILE}" + fi + + stty -F "${PORT}" -hupcl -echo "${READSPEED}" + echo "-- usb setup with speed ${READSPEED}" +fi + +if [ "${CATUSB}" == "yes" ]; then + echo "-- Read usb (Ctrl-C to quit)" + DISPLAYSEP=yes + { + echo "speed=${READSPEED}" + echo "fqbn=${FQBN}" + echo "port=${PORT}" + echo "file=${FILE}" + echo "filedate=$(date +"%Y-%m-%dT%H:%M:%SZ" -d @$(stat -c '%Y' "${FILE}"))" + echo "date=$(date +'%Y-%m-%dT%H:%M:%SZ')" + echo "" + echo "-----BEGIN ARDUINO OUTPUT-----" + } | tee "${RECORDFILE}" + tee -a "${RECORDFILE}" < "${PORT}" +fi + diff --git a/testplan/test/test.ino b/testplan/test/test.ino new file mode 100644 index 0000000..618803d --- /dev/null +++ b/testplan/test/test.ino @@ -0,0 +1,177 @@ +// test.ino + +// Perform RF433any library test plan + +/* + Copyright 2021 Sébastien Millet + + `RF433any' is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + `RF433any' 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program. If not, see + . +*/ + +// +// Schematic: see RF433any library +// + +#define PIN_RFINPUT 2 + +#include "RF433any.h" +#include "Serial.h" +#include + +extern uint16_t sim_timings_count; +extern unsigned int counter; +extern unsigned int sim_int_count; +extern char buffer[SERIAL_LINE_BUF_LEN]; +extern SerialLine sl; +extern uint16_t sim_timings[SIM_TIMINGS_LEN]; +extern unsigned int sim_int_count_svg; + +bool filter_mask_set; +uint16_t filter_mask; + +void setup() { + pinMode(PIN_RFINPUT, INPUT); + Serial.begin(115200); +} + +Track track(PIN_RFINPUT); + +void read_simulated_timings_from_usb() { + filter_mask_set = false; + sim_timings_count = 0; + sim_int_count = 0; + counter = 0; + buffer[0] = '\0'; + for ( ; + strcmp(buffer, "."); + sl.get_line_blocking(buffer, sizeof(buffer)) + ) { + + if (!strlen(buffer)) + continue; + + char *p = buffer; + while (*p != ',' && *p != '\0') + ++p; + if (*p != ',') { + dbg("FATAL: each line must have a ',' character!"); + assert(false); + } + + *p = '\0'; + unsigned int l = atoi(buffer); + unsigned int h = atoi(p + 1); + + if (sim_timings_count >= + sizeof(sim_timings) / sizeof(*sim_timings) - 1) { + dbg("FATAL: timings buffer full!"); + assert(false); + } + +#if RF433ANY_TESTPLAN == 5 + if (!filter_mask_set) { + filter_mask_set = true; + filter_mask = l; + } else { + sim_timings[sim_timings_count++] = l; + sim_timings[sim_timings_count++] = h; + } +#else + sim_timings[sim_timings_count++] = l; + sim_timings[sim_timings_count++] = h; +#endif + } +} + +#if RF433ANY_TESTPLAN == 5 +void output_decoder(Decoder *pdec) { + while(pdec) { + int nb_bits = pdec->get_nb_bits(); + BitVector *pdata = pdec->take_away_data(); + + dbgf("Decoded: %s, err: %d, code: %c, " + "rep: %d, bits: %2d", + (pdec->data_got_decoded() ? "yes" : "no "), + pdec->get_nb_errors(), pdec->get_id_letter(), + pdec->get_repeats() + 1, nb_bits); + + if (pdec->data_got_decoded()) { + Serial.print(" Data: "); + if (pdata) { + char *buf = pdata->to_str(); + if (buf) { + Serial.print(buf); + free(buf); + } + delete pdata; + } + Serial.print("\n"); + } + pdec = pdec->get_next(); + } +} +#endif + +void loop() { + if (sim_int_count >= sim_timings_count) + read_simulated_timings_from_usb(); + + if (!counter) { + delay(100); + dbg("----- BEGIN TEST -----"); +#ifdef RF433ANY_DBG_TRACK + dbg("["); +#endif + } + + ++counter; + + track.treset(); + sim_int_count_svg = sim_int_count; + while (track.get_trk() != TRK_DATA && sim_int_count <= sim_timings_count) { + for (int i = 0; i < 2; ++i) { + Track::ih_handle_interrupt(); + } + track.do_events(); + } + track.force_stop_recv(); + +#ifdef DBG_TIMINGS + track.dbg_timings(); +#endif + +#ifdef RF433ANY_DBG_TRACK + if (sim_int_count >= sim_timings_count) { + dbg("]"); + } +#endif + + Decoder *pdec = track.get_data(filter_mask); + if (pdec) { +#ifdef RF433ANY_DBG_DECODER + pdec->dbg_decoder(2); +#endif +#if RF433ANY_TESTPLAN == 5 + output_decoder(pdec); +#endif + delete pdec; + } + + if (sim_int_count >= sim_timings_count) { + dbg("----- END TEST -----"); + } +} + +// vim: ts=4:sw=4:tw=80:et diff --git a/testplan/track/01/code-simple.txt b/testplan/track/01/code-simple.txt new file mode 100644 index 0000000..f73b450 --- /dev/null +++ b/testplan/track/01/code-simple.txt @@ -0,0 +1,35 @@ +0 , 9604 +1236, 576 +536, 1280 +1232, 608 +1232, 596 +528, 1292 +1228, 600 +1228, 608 +528, 1316 +1228, 632 +524, 1332 +528, 1316 +1228, 628 +532, 1328 +1232, 596 +528, 1276 +1228, 600 +524, 1284 +1224, 596 +1224, 604 +524, 1312 +1224, 624 +1224, 628 +524, 1336 +1224, 604 +520, 1316 +520, 1324 +520, 1284 +524, 1344 +524, 1296 +524, 1304 +520, 1328 +521, 1268 +522, 7020 +0, 0 diff --git a/testplan/track/01/expect1.txt b/testplan/track/01/expect1.txt new file mode 100644 index 0000000..be501a8 --- /dev/null +++ b/testplan/track/01/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":70,"start":0,"end":69, + "trk":TRK_RECV,"xorval":0x244048FF, + "r_low":{ + "bits":32,"v":0x6D2ADA00,"railstatus":"full","n":2, + "b_short":{"inf":201,"mid":536,"sup":884}, + "b_long":{"inf":885,"mid":1232,"sup":2002}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":31,"v":0x496A92FF,"railstatus":"stop received","n":2, + "b_short":{"inf":228,"mid":608,"sup":944}, + "b_long":{"inf":945,"mid":1280,"sup":2080}, + "b_sep":{"inf":4387,"mid":7020,"sup":65535} + } + } +] diff --git a/testplan/track/01/expect2.txt b/testplan/track/01/expect2.txt new file mode 100644 index 0000000..b3ac3e1 --- /dev/null +++ b/testplan/track/01/expect2.txt @@ -0,0 +1,6 @@ +IH_max_pending_timings = 2 +> nb_sections = 1, initseq = 9604 + 00 SSEP + sep = 7020 + low: [2] n = 32, v = 0x6D2ADA00 + high: [2] n = 31, v = 0x496A92FF diff --git a/testplan/track/02/code-err.txt b/testplan/track/02/code-err.txt new file mode 100644 index 0000000..ef11348 --- /dev/null +++ b/testplan/track/02/code-err.txt @@ -0,0 +1,12 @@ +0, 75000, + 248, 500, + 248, 496, + 244, 504, + 244, 504, + 240, 504, + 244, 504, + 244, 504, + 240, 508, + 240, 3868, + 228, 888, +100, 100, diff --git a/testplan/track/02/expect1.txt b/testplan/track/02/expect1.txt new file mode 100644 index 0000000..b0e15e1 --- /dev/null +++ b/testplan/track/02/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":24,"start":0,"end":21, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +] diff --git a/testplan/track/02/expect2.txt b/testplan/track/02/expect2.txt new file mode 100644 index 0000000..2008113 --- /dev/null +++ b/testplan/track/02/expect2.txt @@ -0,0 +1,6 @@ +IH_max_pending_timings = 2 +> nb_sections = 1, initseq = 9464 + 00 SSEP + sep = 3868 + low: [1] n = 8, v = 0x00000000 + high: [1] n = 7, v = 0x00000000 diff --git a/testplan/track/03/code-err1.txt b/testplan/track/03/code-err1.txt new file mode 100644 index 0000000..da20736 --- /dev/null +++ b/testplan/track/03/code-err1.txt @@ -0,0 +1,13 @@ +0, 65000, + 249, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 240, 504, + 400, 504, + 240, 508, + 900, 3868, + 100, 10, + 100, 100, +0, 14999, diff --git a/testplan/track/03/expect1.txt b/testplan/track/03/expect1.txt new file mode 100644 index 0000000..327b8aa --- /dev/null +++ b/testplan/track/03/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":26,"start":0,"end":21, + "trk":TRK_RECV,"xorval":0x00000002, + "r_low":{ + "bits":7,"v":0x00000002,"railstatus":"stop received","n":2, + "b_short":{"inf":93,"mid":248,"sup":324}, + "b_long":{"inf":325,"mid":400,"sup":650}, + "b_sep":{"inf":562,"mid":900,"sup":65535} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +] diff --git a/testplan/track/03/expect2.txt b/testplan/track/03/expect2.txt new file mode 100644 index 0000000..06ecb58 --- /dev/null +++ b/testplan/track/03/expect2.txt @@ -0,0 +1,6 @@ +IH_max_pending_timings = 3 +> nb_sections = 1, initseq = 65000 + 00 2SEP + sep = 3868 + low: [2] n = 7, v = 0x00000002 + high: [1] n = 7, v = 0x00000000 diff --git a/testplan/track/04/code-errn.txt b/testplan/track/04/code-errn.txt new file mode 100644 index 0000000..7c01ef0 --- /dev/null +++ b/testplan/track/04/code-errn.txt @@ -0,0 +1,62 @@ +0, 15000, + 248, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 240, 504, + 244, 504, + 240, 508, + 240, 3868, + 228, 888, +100, 100, + 0, 15000, + 249, 500, + 248, 496, + 244, 504, + 240, 504, + 400, 504, + 240, 508, + 240, 508, + 240, 508, + 940, 3868, + 900, 888, + 100, 100, +0, 15000, + 249, 500, + 248, 496, + 244, 504, + 240, 504, + 400, 504, + 400, 504, + 400, 504, + 240, 508, + 940, 10, + 0, 0, + 100, 100, +0, 15000, + 249, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 240, 504, + 400, 504, + 240, 508, + 900, 500, + 100, 10, + 100, 100, +0, 14999, + 247, 501, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 240, 508, + 240, 508, + 240, 508, + 2000, 1008, + 100, 500, + 100, 100, +100, 100, +100, 100, diff --git a/testplan/track/04/expect1.txt b/testplan/track/04/expect1.txt new file mode 100644 index 0000000..ea3fbb2 --- /dev/null +++ b/testplan/track/04/expect1.txt @@ -0,0 +1,86 @@ +[ + { + "N":124,"start":0,"end":21, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +, + { + "N":124,"start":26,"end":45, + "trk":TRK_RECV,"xorval":0x00000008, + "r_low":{ + "bits":7,"v":0x00000008,"railstatus":"stop received","n":2, + "b_short":{"inf":93,"mid":248,"sup":324}, + "b_long":{"inf":325,"mid":400,"sup":650}, + "b_sep":{"inf":587,"mid":940,"sup":65535} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +, + { + "N":124,"start":50,"end":69, + "trk":TRK_RECV,"xorval":0x0000000E, + "r_low":{ + "bits":7,"v":0x0000000E,"railstatus":"stop received","n":2, + "b_short":{"inf":93,"mid":248,"sup":324}, + "b_long":{"inf":325,"mid":400,"sup":650}, + "b_sep":{"inf":587,"mid":940,"sup":65535} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"error","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":0,"mid":0,"sup":0} + } + } +, + { + "N":124,"start":50,"end":93, + "trk":TRK_RECV,"xorval":0x00000002, + "r_low":{ + "bits":7,"v":0x00000002,"railstatus":"stop received","n":2, + "b_short":{"inf":93,"mid":248,"sup":324}, + "b_long":{"inf":325,"mid":400,"sup":650}, + "b_sep":{"inf":562,"mid":900,"sup":65535} + }, + "r_high":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":372,"mid":496,"sup":620}, + "b_long":{"inf":372,"mid":496,"sup":620}, + "b_sep":{"inf":0,"mid":0,"sup":0} + } + } +, + { + "N":124,"start":50,"end":117, + "trk":TRK_RECV,"xorval":0x00000001, + "r_low":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":1250,"mid":2000,"sup":65535} + }, + "r_high":{ + "bits":8,"v":0x00000001,"railstatus":"closed","n":2, + "b_short":{"inf":186,"mid":496,"sup":752}, + "b_long":{"inf":753,"mid":1008,"sup":1638}, + "b_sep":{"inf":0,"mid":0,"sup":0} + } + } +] diff --git a/testplan/track/04/expect2.txt b/testplan/track/04/expect2.txt new file mode 100644 index 0000000..c6b1e5d --- /dev/null +++ b/testplan/track/04/expect2.txt @@ -0,0 +1,12 @@ +IH_max_pending_timings = 3 +> nb_sections = 1, initseq = 15000 + 00 SSEP + sep = 3868 + low: [1] n = 8, v = 0x00000000 + high: [1] n = 7, v = 0x00000000 +IH_max_pending_timings = 3 +> nb_sections = 1, initseq = 15000 + 00 2SEP + sep = 3868 + low: [2] n = 7, v = 0x00000008 + high: [1] n = 7, v = 0x00000000 diff --git a/testplan/track/05/code-err.txt b/testplan/track/05/code-err.txt new file mode 100644 index 0000000..3dfaf9f --- /dev/null +++ b/testplan/track/05/code-err.txt @@ -0,0 +1,11 @@ +0, 15000, + 248, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 244, 504, + 244, 504, + 240, 3868, + 228, 888, +100, 100, diff --git a/testplan/track/05/expect1.txt b/testplan/track/05/expect1.txt new file mode 100644 index 0000000..0d4f101 --- /dev/null +++ b/testplan/track/05/expect1.txt @@ -0,0 +1,2 @@ +[ +] diff --git a/testplan/track/05/expect2.txt b/testplan/track/05/expect2.txt new file mode 100644 index 0000000..e69de29 diff --git a/testplan/track/06/code-1 b/testplan/track/06/code-1 new file mode 100644 index 0000000..2db85a6 --- /dev/null +++ b/testplan/track/06/code-1 @@ -0,0 +1,13 @@ +0, 15000, +0, 45000, + 248, 500, + 248, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 244, 504, + 244, 504, + 240, 3868, + 228, 888, +100, 100, diff --git a/testplan/track/06/expect1.txt b/testplan/track/06/expect1.txt new file mode 100644 index 0000000..d89124d --- /dev/null +++ b/testplan/track/06/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":26,"start":0,"end":23, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":375,"mid":500,"sup":625}, + "b_long":{"inf":375,"mid":500,"sup":625}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +] diff --git a/testplan/track/06/expect2.txt b/testplan/track/06/expect2.txt new file mode 100644 index 0000000..e3fdc58 --- /dev/null +++ b/testplan/track/06/expect2.txt @@ -0,0 +1,6 @@ +IH_max_pending_timings = 3 +> nb_sections = 1, initseq = 45000 + 00 SSEP + sep = 3868 + low: [1] n = 8, v = 0x00000000 + high: [1] n = 7, v = 0x00000000 diff --git a/testplan/track/07/code-2 b/testplan/track/07/code-2 new file mode 100644 index 0000000..b94c002 --- /dev/null +++ b/testplan/track/07/code-2 @@ -0,0 +1,15 @@ +0, 15000, +0, 15000, + 248, 500, +248, 15000, + 248, 500, + 248, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 244, 504, + 244, 504, + 240, 3868, + 228, 888, +100, 100, diff --git a/testplan/track/07/expect1.txt b/testplan/track/07/expect1.txt new file mode 100644 index 0000000..85e2e5b --- /dev/null +++ b/testplan/track/07/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":30,"start":0,"end":27, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":375,"mid":500,"sup":625}, + "b_long":{"inf":375,"mid":500,"sup":625}, + "b_sep":{"inf":2417,"mid":3868,"sup":65535} + } + } +] diff --git a/testplan/track/07/expect2.txt b/testplan/track/07/expect2.txt new file mode 100644 index 0000000..18dc65b --- /dev/null +++ b/testplan/track/07/expect2.txt @@ -0,0 +1,6 @@ +IH_max_pending_timings = 3 +> nb_sections = 1, initseq = 15000 + 00 SSEP + sep = 3868 + low: [1] n = 8, v = 0x00000000 + high: [1] n = 7, v = 0x00000000 diff --git a/testplan/track/08/code-2 b/testplan/track/08/code-2 new file mode 100644 index 0000000..c01069a --- /dev/null +++ b/testplan/track/08/code-2 @@ -0,0 +1,15 @@ +0, 15000, +0, 15000, + 248, 500, +248, 15000, + 248, 500, + 248, 500, + 248, 496, + 244, 504, + 240, 504, + 240, 504, + 244, 504, + 244, 504, + 2400, 504, + 228, 888, +100, 100, diff --git a/testplan/track/08/expect1.txt b/testplan/track/08/expect1.txt new file mode 100644 index 0000000..c6f140c --- /dev/null +++ b/testplan/track/08/expect1.txt @@ -0,0 +1,18 @@ +[ + { + "N":30,"start":0,"end":27, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":7,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":186,"mid":248,"sup":310}, + "b_long":{"inf":186,"mid":248,"sup":310}, + "b_sep":{"inf":1500,"mid":2400,"sup":65535} + }, + "r_high":{ + "bits":8,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":375,"mid":500,"sup":625}, + "b_long":{"inf":375,"mid":500,"sup":625}, + "b_sep":{"inf":0,"mid":0,"sup":0} + } + } +] diff --git a/testplan/track/08/expect2.txt b/testplan/track/08/expect2.txt new file mode 100644 index 0000000..e69de29 diff --git a/testplan/track/09/code-flo.txt b/testplan/track/09/code-flo.txt new file mode 100644 index 0000000..76ea731 --- /dev/null +++ b/testplan/track/09/code-flo.txt @@ -0,0 +1,69 @@ +0, 8484 +608, 724 +1292, 1396 +1284, 1412 +624, 716 +1292, 1404 +596, 736 +612, 720 +596, 752 +1276, 1420 +1268, 1420 +596, 736 +1280, 1424 +608, 24028 +608, 728 +1300, 1412 +596, 736 +1288, 1424 +592, 748 +1284, 1420 +596, 736 +1284, 1428 +612, 720 +1280, 1424 +592, 752 +1280, 1420 +600, 24052 +588, 752 +1268, 1420 +604, 736 +1300, 1412 +588, 744 +1296, 1412 +592, 748 +1276, 1428 +588, 752 +1260, 1440 +580, 760 +1280, 1420 +584, 24048 +612, 736 +1260, 1440 +580, 760 +1268, 1436 +580, 760 +1256, 1440 +596, 744 +1276, 1424 +596, 736 +1292, 1404 +596, 752 +1264, 1432 +576, 24056 +572, 776 +1256, 1448 +580, 752 +1276, 1424 +592, 744 +1284, 1420 +616, 728 +1268, 1424 +588, 752 +1256, 1448 +568, 776 +1240, 1464 +580, 24040 +576, 764 +1260, 1440 +616, 716 diff --git a/testplan/track/09/expect1.txt b/testplan/track/09/expect1.txt new file mode 100644 index 0000000..11e0732 --- /dev/null +++ b/testplan/track/09/expect1.txt @@ -0,0 +1,82 @@ +[ + { + "N":138,"start":0,"end":29, + "trk":TRK_RECV,"xorval":0x00000B97, + "r_low":{ + "bits":12,"v":0x00000D1A,"railstatus":"closed","n":2, + "b_short":{"inf":234,"mid":624,"sup":958}, + "b_long":{"inf":959,"mid":1292,"sup":2099}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":11,"v":0x0000068D,"railstatus":"stop received","n":2, + "b_short":{"inf":269,"mid":716,"sup":1056}, + "b_long":{"inf":1057,"mid":1396,"sup":2268}, + "b_sep":{"inf":15017,"mid":24028,"sup":65535} + } + } + { + "N":138,"start":0,"end":55, + "trk":TRK_RECV,"xorval":0x00000FFF, + "r_low":{ + "bits":12,"v":0x00000AAA,"railstatus":"closed","n":2, + "b_short":{"inf":234,"mid":624,"sup":958}, + "b_long":{"inf":959,"mid":1292,"sup":2099}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":11,"v":0x00000555,"railstatus":"stop received","n":2, + "b_short":{"inf":269,"mid":716,"sup":1056}, + "b_long":{"inf":1057,"mid":1396,"sup":2268}, + "b_sep":{"inf":15017,"mid":24028,"sup":65535} + } + } + { + "N":138,"start":0,"end":81, + "trk":TRK_RECV,"xorval":0x00000FFF, + "r_low":{ + "bits":12,"v":0x00000AAA,"railstatus":"closed","n":2, + "b_short":{"inf":234,"mid":624,"sup":958}, + "b_long":{"inf":959,"mid":1292,"sup":2099}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":11,"v":0x00000555,"railstatus":"stop received","n":2, + "b_short":{"inf":269,"mid":716,"sup":1056}, + "b_long":{"inf":1057,"mid":1396,"sup":2268}, + "b_sep":{"inf":15017,"mid":24028,"sup":65535} + } + } + { + "N":138,"start":0,"end":107, + "trk":TRK_RECV,"xorval":0x00000FFF, + "r_low":{ + "bits":12,"v":0x00000AAA,"railstatus":"closed","n":2, + "b_short":{"inf":234,"mid":624,"sup":958}, + "b_long":{"inf":959,"mid":1292,"sup":2099}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":11,"v":0x00000555,"railstatus":"stop received","n":2, + "b_short":{"inf":269,"mid":716,"sup":1056}, + "b_long":{"inf":1057,"mid":1396,"sup":2268}, + "b_sep":{"inf":15017,"mid":24028,"sup":65535} + } + } + { + "N":138,"start":0,"end":133, + "trk":TRK_RECV,"xorval":0x00000FFF, + "r_low":{ + "bits":12,"v":0x00000AAA,"railstatus":"closed","n":2, + "b_short":{"inf":234,"mid":624,"sup":958}, + "b_long":{"inf":959,"mid":1292,"sup":2099}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":11,"v":0x00000555,"railstatus":"stop received","n":2, + "b_short":{"inf":269,"mid":716,"sup":1056}, + "b_long":{"inf":1057,"mid":1396,"sup":2268}, + "b_sep":{"inf":15017,"mid":24028,"sup":65535} + } + } +] diff --git a/testplan/track/09/expect2.txt b/testplan/track/09/expect2.txt new file mode 100644 index 0000000..625e1b1 --- /dev/null +++ b/testplan/track/09/expect2.txt @@ -0,0 +1,22 @@ +IH_max_pending_timings = 3 +> nb_sections = 5, initseq = 8484 + 00 SSEP + sep = 24028 + low: [2] n = 12, v = 0x00000D1A + high: [2] n = 11, v = 0x0000068D + 01 SSEP + sep = 24052 + low: [2] n = 12, v = 0x00000AAA + high: [2] n = 11, v = 0x00000555 + 02 SSEP + sep = 24048 + low: [2] n = 12, v = 0x00000AAA + high: [2] n = 11, v = 0x00000555 + 03 SSEP + sep = 24056 + low: [2] n = 12, v = 0x00000AAA + high: [2] n = 11, v = 0x00000555 + 04 SSEP + sep = 24040 + low: [2] n = 12, v = 0x00000AAA + high: [2] n = 11, v = 0x00000555 diff --git a/testplan/track/10/code-portail.txt b/testplan/track/10/code-portail.txt new file mode 100644 index 0000000..516461b --- /dev/null +++ b/testplan/track/10/code-portail.txt @@ -0,0 +1,69 @@ +, 5656 +304, 416 +296, 420 +296, 416 +296, 416 +308, 404 +300, 416 +296, 416 +300, 412 +300, 416 +296, 416 +300, 412 +300, 3764 +676, 416 +676, 416 +304, 788 +676, 424 +672, 420 +296, 800 +296, 796 +672, 424 +296, 796 +296, 800 +668, 424 +296, 800 +668, 424 +296, 796 +296, 800 +296, 796 +672, 424 +668, 424 +672, 424 +668, 424 +296, 796 +668, 424 +668, 424 +296, 800 +296, 804 +656, 428 +288, 804 +296, 804 +664, 432 +656, 436 +656, 432 +668, 432 +288, 800 +296, 804 +288, 808 +648, 440 +288, 804 +284, 812 +288, 804 +656, 440 +280, 820 +272, 812 +652, 448 +648, 444 +272, 820 +272, 820 +276, 820 +648, 444 +648, 444 +272, 820 +280, 812 +656, 440 +656, 436 +648, 448 +648, 444 +648, 448 diff --git a/testplan/track/10/expect1.txt b/testplan/track/10/expect1.txt new file mode 100644 index 0000000..0522954 --- /dev/null +++ b/testplan/track/10/expect1.txt @@ -0,0 +1,50 @@ +[ + { + "N":138,"start":0,"end":27, + "trk":TRK_RECV,"xorval":0x00000000, + "r_low":{ + "bits":11,"v":0x00000000,"railstatus":"closed","n":1, + "b_short":{"inf":222,"mid":296,"sup":370}, + "b_long":{"inf":222,"mid":296,"sup":370}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":10,"v":0x00000000,"railstatus":"stop received","n":1, + "b_short":{"inf":315,"mid":420,"sup":525}, + "b_long":{"inf":315,"mid":420,"sup":525}, + "b_sep":{"inf":2352,"mid":3764,"sup":65535} + } + } + { + "N":138,"start":0,"end":93, + "trk":TRK_RECV,"xorval":0xFFFFFFFF, + "r_low":{ + "bits":32,"v":0xB251EC9E,"railstatus":"full","n":2, + "b_short":{"inf":111,"mid":296,"sup":486}, + "b_long":{"inf":487,"mid":676,"sup":1098}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":32,"v":0x4DAE1361,"railstatus":"full","n":2, + "b_short":{"inf":158,"mid":420,"sup":604}, + "b_long":{"inf":605,"mid":788,"sup":1280}, + "b_sep":{"inf":2352,"mid":3764,"sup":65535} + } + } + { + "N":138,"start":0,"end":138, + "trk":TRK_RECV,"xorval":0x007FFFFF, + "r_low":{ + "bits":23,"v":0x0011319F,"railstatus":"error","n":2, + "b_short":{"inf":111,"mid":296,"sup":486}, + "b_long":{"inf":487,"mid":676,"sup":1098}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":23,"v":0x006ECE60,"railstatus":"error","n":2, + "b_short":{"inf":158,"mid":420,"sup":604}, + "b_long":{"inf":605,"mid":788,"sup":1280}, + "b_sep":{"inf":2352,"mid":3764,"sup":65535} + } + } +] diff --git a/testplan/track/10/expect2.txt b/testplan/track/10/expect2.txt new file mode 100644 index 0000000..73d4ab6 --- /dev/null +++ b/testplan/track/10/expect2.txt @@ -0,0 +1,10 @@ +IH_max_pending_timings = 3 +> nb_sections = 2, initseq = 5656 + 00 SSEP + sep = 3764 + low: [1] n = 11, v = 0x00000000 + high: [1] n = 10, v = 0x00000000 + 01 CONT + sep = 0 + low: [2] n = 32, v = 0xB251EC9E + high: [2] n = 32, v = 0x4DAE1361 diff --git a/testplan/track/11/code-adf.txt b/testplan/track/11/code-adf.txt new file mode 100644 index 0000000..154681a --- /dev/null +++ b/testplan/track/11/code-adf.txt @@ -0,0 +1,57 @@ +0,5288 +1300, 1212 +1268, 2436 +2540, 1204 +1236, 1220 +1276, 1196 +1268, 1216 +1252, 1228 +1244, 1256 +1220, 1248 +1244, 1248 +1248, 1224 +1260, 1212 +1240, 1244 +1236, 2472 +1264, 1200 +1288, 1212 +2496, 1244 +1252, 1224 +1260, 2424 +2516, 1240 +1220, 2480 +2552, 2436 +2488, 1232 +1280, 2420 +1272, 1212 +2504, 2464 +1288, 5304 +1248, 1252 +1236, 2456 +2516, 1212 +1280, 1208 +1240, 1236 +1224, 1260 +1236, 1268 +1228, 1236 +1240, 1232 +1232, 1252 +1216, 1256 +1240, 1244 +1224, 1268 +1196, 2488 +1248, 1244 +1236, 1240 +2504, 1240 +1248, 1232 +1228, 2464 +2496, 1240 +1216, 2484 +2496, 2484 +2516, 1224 +1224, 2464 +1260, 1224 +2512, 2464 +1264, 5324 +1216, 1268 +1248, 2452 diff --git a/testplan/track/11/expect1.txt b/testplan/track/11/expect1.txt new file mode 100644 index 0000000..b58187d --- /dev/null +++ b/testplan/track/11/expect1.txt @@ -0,0 +1,34 @@ +[ + { + "N":114,"start":0,"end":57, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":26,"v":0x010004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":25,"v":0x010010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } + { + "N":114,"start":0,"end":111, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":26,"v":0x010004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":25,"v":0x010010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } +] diff --git a/testplan/track/11/expect2.txt b/testplan/track/11/expect2.txt new file mode 100644 index 0000000..7de6ece --- /dev/null +++ b/testplan/track/11/expect2.txt @@ -0,0 +1,10 @@ +IH_max_pending_timings = 3 +> nb_sections = 2, initseq = 5288 + 00 SSEP + sep = 5304 + low: [2] n = 26, v = 0x010004B2 + high: [2] n = 25, v = 0x010010B5 + 01 SSEP + sep = 5324 + low: [2] n = 26, v = 0x010004B2 + high: [2] n = 25, v = 0x010010B5 diff --git a/testplan/track/12/code-adf.txt b/testplan/track/12/code-adf.txt new file mode 100644 index 0000000..56ba0f4 --- /dev/null +++ b/testplan/track/12/code-adf.txt @@ -0,0 +1,56 @@ +0,5288 +1300, 1212 +1268, 2436 +2540, 1204 +1236, 1220 +1268, 1216 +1252, 1228 +1244, 1256 +1220, 1248 +1244, 1248 +1248, 1224 +1260, 1212 +1240, 1244 +1236, 2472 +1264, 1200 +1288, 1212 +2496, 1244 +1252, 1224 +1260, 2424 +2516, 1240 +1220, 2480 +2552, 2436 +2488, 1232 +1280, 2420 +1272, 1212 +2504, 2464 +1288, 5304 +1248, 1252 +1236, 2456 +2516, 1212 +1280, 1208 +1240, 1236 +1224, 1260 +1236, 1268 +1228, 1236 +1240, 1232 +1232, 1252 +1216, 1256 +1240, 1244 +1224, 1268 +1196, 2488 +1248, 1244 +1236, 1240 +2504, 1240 +1248, 1232 +1228, 2464 +2496, 1240 +1216, 2484 +2496, 2484 +2516, 1224 +1224, 2464 +1260, 1224 +2512, 2464 +1264, 5324 +1216, 1268 +1248, 2452 diff --git a/testplan/track/12/expect1.txt b/testplan/track/12/expect1.txt new file mode 100644 index 0000000..ab88d60 --- /dev/null +++ b/testplan/track/12/expect1.txt @@ -0,0 +1,34 @@ +[ + { + "N":112,"start":0,"end":55, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":25,"v":0x008004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":24,"v":0x008010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } + { + "N":112,"start":0,"end":109, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":26,"v":0x010004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":25,"v":0x010010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } +] diff --git a/testplan/track/12/expect2.txt b/testplan/track/12/expect2.txt new file mode 100644 index 0000000..33406d4 --- /dev/null +++ b/testplan/track/12/expect2.txt @@ -0,0 +1,10 @@ +IH_max_pending_timings = 3 +> nb_sections = 2, initseq = 5288 + 00 SSEP + sep = 5304 + low: [2] n = 25, v = 0x008004B2 + high: [2] n = 24, v = 0x008010B5 + 01 SSEP + sep = 5324 + low: [2] n = 26, v = 0x010004B2 + high: [2] n = 25, v = 0x010010B5 diff --git a/testplan/track/13/code-adf.txt b/testplan/track/13/code-adf.txt new file mode 100644 index 0000000..a0edeab --- /dev/null +++ b/testplan/track/13/code-adf.txt @@ -0,0 +1,55 @@ +0,5288 +1300, 1212 +1268, 2436 +2540, 1204 +1268, 1216 +1252, 1228 +1244, 1256 +1220, 1248 +1244, 1248 +1248, 1224 +1260, 1212 +1240, 1244 +1236, 2472 +1264, 1200 +1288, 1212 +2496, 1244 +1252, 1224 +1260, 2424 +2516, 1240 +1220, 2480 +2552, 2436 +2488, 1232 +1280, 2420 +1272, 1212 +2504, 2464 +1288, 5304 +1248, 1252 +1236, 2456 +2516, 1212 +1280, 1208 +1240, 1236 +1224, 1260 +1236, 1268 +1228, 1236 +1240, 1232 +1232, 1252 +1216, 1256 +1240, 1244 +1224, 1268 +1196, 2488 +1248, 1244 +1236, 1240 +2504, 1240 +1248, 1232 +1228, 2464 +2496, 1240 +1216, 2484 +2496, 2484 +2516, 1224 +1224, 2464 +1260, 1224 +2512, 2464 +1264, 5324 +1216, 1268 +1248, 2452 diff --git a/testplan/track/13/expect1.txt b/testplan/track/13/expect1.txt new file mode 100644 index 0000000..ef1aa34 --- /dev/null +++ b/testplan/track/13/expect1.txt @@ -0,0 +1,34 @@ +[ + { + "N":110,"start":0,"end":53, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":24,"v":0x004004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":23,"v":0x004010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } + { + "N":110,"start":0,"end":107, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":26,"v":0x010004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":25,"v":0x010010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } +] diff --git a/testplan/track/13/expect2.txt b/testplan/track/13/expect2.txt new file mode 100644 index 0000000..bcd7f76 --- /dev/null +++ b/testplan/track/13/expect2.txt @@ -0,0 +1,10 @@ +IH_max_pending_timings = 3 +> nb_sections = 2, initseq = 5288 + 00 SSEP + sep = 5304 + low: [2] n = 24, v = 0x004004B2 + high: [2] n = 23, v = 0x004010B5 + 01 SSEP + sep = 5324 + low: [2] n = 26, v = 0x010004B2 + high: [2] n = 25, v = 0x010010B5 diff --git a/testplan/track/14/code-adf.txt b/testplan/track/14/code-adf.txt new file mode 100644 index 0000000..91e58e4 --- /dev/null +++ b/testplan/track/14/code-adf.txt @@ -0,0 +1,54 @@ +0,5288 +1300, 1212 +1268, 2436 +2540, 1204 +1268, 1216 +1252, 1228 +1244, 1256 +1220, 1248 +1248, 1224 +1260, 1212 +1240, 1244 +1236, 2472 +1264, 1200 +1288, 1212 +2496, 1244 +1252, 1224 +1260, 2424 +2516, 1240 +1220, 2480 +2552, 2436 +2488, 1232 +1280, 2420 +1272, 1212 +2504, 2464 +1288, 5304 +1248, 1252 +1236, 2456 +2516, 1212 +1280, 1208 +1240, 1236 +1224, 1260 +1236, 1268 +1228, 1236 +1240, 1232 +1232, 1252 +1216, 1256 +1240, 1244 +1224, 1268 +1196, 2488 +1248, 1244 +1236, 1240 +2504, 1240 +1248, 1232 +1228, 2464 +2496, 1240 +1216, 2484 +2496, 2484 +2516, 1224 +1224, 2464 +1260, 1224 +2512, 2464 +1264, 5324 +1216, 1268 +1248, 2452 diff --git a/testplan/track/14/expect1.txt b/testplan/track/14/expect1.txt new file mode 100644 index 0000000..a60a8df --- /dev/null +++ b/testplan/track/14/expect1.txt @@ -0,0 +1,34 @@ +[ + { + "N":108,"start":0,"end":51, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":23,"v":0x002004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":22,"v":0x002010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } + { + "N":108,"start":0,"end":105, + "trk":TRK_RECV,"xorval":0x00001407, + "r_low":{ + "bits":26,"v":0x010004B2,"railstatus":"closed","n":2, + "b_short":{"inf":476,"mid":1268,"sup":1904}, + "b_long":{"inf":1905,"mid":2540,"sup":4127}, + "b_sep":{"inf":0,"mid":0,"sup":0} + }, + "r_high":{ + "bits":25,"v":0x010010B5,"railstatus":"stop received","n":2, + "b_short":{"inf":452,"mid":1204,"sup":1820}, + "b_long":{"inf":1821,"mid":2436,"sup":3958}, + "b_sep":{"inf":3315,"mid":5304,"sup":65535} + } + } +] diff --git a/testplan/track/14/expect2.txt b/testplan/track/14/expect2.txt new file mode 100644 index 0000000..339d3a3 --- /dev/null +++ b/testplan/track/14/expect2.txt @@ -0,0 +1,10 @@ +IH_max_pending_timings = 3 +> nb_sections = 2, initseq = 5288 + 00 SSEP + sep = 5304 + low: [2] n = 23, v = 0x002004B2 + high: [2] n = 22, v = 0x002010B5 + 01 SSEP + sep = 5324 + low: [2] n = 26, v = 0x010004B2 + high: [2] n = 25, v = 0x010010B5 diff --git a/testplan/tt.sh b/testplan/tt.sh new file mode 100755 index 0000000..501a9ae --- /dev/null +++ b/testplan/tt.sh @@ -0,0 +1,94 @@ +#!/usr/bin/bash + +# tt.sh + +# Execute test plan of RF433any library +# Requires an Arduino plugged on PC + +# Accepts one optional argument, the test number to execute. +# Without argument, runs all tests. + +# Copyright 2021 Sébastien Millet +# +# `RF433any' is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# `RF433any' 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program. If not, see +# . + +set -euo pipefail + +PORT=/dev/ttyUSB0 + +PASSED=0 +FAILED=0 + +cd .. + +START=1 +STOP=5 +if [ -n "${1:-}" ]; then + START="$1"; + STOP="$1"; +fi + +for ((i=START; i<=STOP; i++)); do + + echo "== ROUND $i" + + testplan/test/am testplan/test/test.ino -u --stty -t "${i}" + + sleep 2 + + if [ "${i}" -le 2 ]; then + cd testplan/track + elif [ "${i}" -le 4 ]; then + cd testplan/decoder + elif [ "${i}" -le 5 ]; then + cd testplan/user + else + + echo "Unknown testplan number, aborted." + exit 99 + fi + + for d in [0-9][0-9]; do + inpfile=$(ls "${d}"/code*) + tmpout="${d}/tmpout${i}.txt" + expfile="${d}/expect${i}.txt" + ../exectest.sh "${inpfile}" "${tmpout}" "${PORT}" + echo -n "$i:${d}" + if cmp "${expfile}" "${tmpout}" > /dev/null 2> /dev/null; then + PASSED=$((PASSED + 1)) + echo " test ok" + else + FAILED=$((FAILED + 1)) + echo " ** TEST KO, actual output differs from expected" + fi + done + + cd ../.. + +done + +echo "------" +echo "PASSED: ${PASSED}" +echo "FAILED: ${FAILED}" + +if [ "${FAILED}" -eq 0 ]; then + echo "OK" +else + echo + echo "**************" + echo "***** KO *****" + echo "**************" + exit 1 +fi diff --git a/testplan/user/01/code-1.txt b/testplan/user/01/code-1.txt new file mode 100644 index 0000000..c1c9399 --- /dev/null +++ b/testplan/user/01/code-1.txt @@ -0,0 +1,53 @@ +0, 0 +0, 9000 +596, 596 +506, 506 +600, 600 +600, 600 +600, 600 +600, 600 +600, 600 +600, 600 +600 , 9000 +1236, 576 +536, 1280 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +528, 1316 +529, 1319 +522, 7020 +1236, 576 +536, 1280 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +528, 1316 +529, 1319 +522, 7020 +576, 1236, +576, 536, +1280, 1232, +596, 528, +1292, 1228, +600, 1228, +600, 1228, +608, 528, +1316, 529, +1316, 529, +529, 7020 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +1309, 23912 +0, 0 +0, 0 diff --git a/testplan/user/01/expect5.txt b/testplan/user/01/expect5.txt new file mode 100644 index 0000000..15eee35 --- /dev/null +++ b/testplan/user/01/expect5.txt @@ -0,0 +1,8 @@ +Decoded: no , err: 0, code: S, rep: 1, bits: 8 +Decoded: yes, err: 0, code: T, rep: 1, bits: 9 + Data: 01 5c +Decoded: yes, err: 0, code: T, rep: 1, bits: 9 + Data: 01 5c +Decoded: yes, err: 1, code: N, rep: 1, bits: 9 + Data: 01 5c +Decoded: no , err: 0, code: U, rep: 1, bits: 14 diff --git a/testplan/user/02/code-1.txt b/testplan/user/02/code-1.txt new file mode 100644 index 0000000..7c82182 --- /dev/null +++ b/testplan/user/02/code-1.txt @@ -0,0 +1,53 @@ +1, 0 +0, 9000 +596, 596 +506, 506 +600, 600 +600, 600 +600, 600 +600, 600 +600, 600 +600, 600 +600 , 9000 +1236, 576 +536, 1280 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +528, 1316 +529, 1319 +522, 7020 +1236, 576 +536, 1280 +1232, 596 +528, 1292 +1228, 600 +1228, 600 +1228, 608 +528, 1316 +529, 1319 +522, 7020 +576, 1236, +576, 536, +1280, 1232, +596, 528, +1292, 1228, +600, 1228, +600, 1228, +608, 528, +1316, 529, +1316, 529, +529, 7020 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +700, 644 +1309, 23912 +0, 0 +0, 0 diff --git a/testplan/user/02/expect5.txt b/testplan/user/02/expect5.txt new file mode 100644 index 0000000..610d636 --- /dev/null +++ b/testplan/user/02/expect5.txt @@ -0,0 +1,6 @@ +Decoded: yes, err: 0, code: T, rep: 1, bits: 9 + Data: 01 5c +Decoded: yes, err: 0, code: T, rep: 1, bits: 9 + Data: 01 5c +Decoded: yes, err: 1, code: N, rep: 1, bits: 9 + Data: 01 5c