From c4f3e8d63b04e587af6ccbea4d23b7d6dbfc4a6d Mon Sep 17 00:00:00 2001 From: Hansi Reiser Date: Wed, 3 Apr 2019 17:16:51 +0200 Subject: [PATCH] TTGO Sonde Playground. Initial version --- Licenses/Exceptions | 6 + Licenses/README | 30 ++ LICENSE => Licenses/gpl-2.0.txt | 0 Licenses/lgpl-2.1.txt | 502 ++++++++++++++++++ RX_FSK/RX_FSK.ino | 184 +++++++ libraries/SX1278FSK/SX1278FSK.cpp | 815 ++++++++++++++++++++++++++++++ libraries/SX1278FSK/SX1278FSK.h | 271 ++++++++++ libraries/SondeLib/DFM.cpp | 282 +++++++++++ libraries/SondeLib/DFM.h | 64 +++ libraries/SondeLib/RS41.cpp | 476 +++++++++++++++++ libraries/SondeLib/RS41.h | 57 +++ libraries/SondeLib/Sonde.cpp | 85 ++++ libraries/SondeLib/Sonde.h | 41 ++ libraries/SondeLib/rsc.cpp | 32 ++ libraries/SondeLib/rsc.h | 17 + libraries/SondeLib/rsc_decode.cpp | 436 ++++++++++++++++ 16 files changed, 3298 insertions(+) create mode 100644 Licenses/Exceptions create mode 100644 Licenses/README rename LICENSE => Licenses/gpl-2.0.txt (100%) create mode 100644 Licenses/lgpl-2.1.txt create mode 100644 RX_FSK/RX_FSK.ino create mode 100644 libraries/SX1278FSK/SX1278FSK.cpp create mode 100644 libraries/SX1278FSK/SX1278FSK.h create mode 100644 libraries/SondeLib/DFM.cpp create mode 100644 libraries/SondeLib/DFM.h create mode 100644 libraries/SondeLib/RS41.cpp create mode 100644 libraries/SondeLib/RS41.h create mode 100644 libraries/SondeLib/Sonde.cpp create mode 100644 libraries/SondeLib/Sonde.h create mode 100644 libraries/SondeLib/rsc.cpp create mode 100644 libraries/SondeLib/rsc.h create mode 100644 libraries/SondeLib/rsc_decode.cpp diff --git a/Licenses/Exceptions b/Licenses/Exceptions new file mode 100644 index 0000000..6f8843e --- /dev/null +++ b/Licenses/Exceptions @@ -0,0 +1,6 @@ +GPL License Exceptions: + +- The SX1278FSK library is based on + https://github.com/pdelmo/lora_shield_arduino.git + and licensed under + GNU Lesser General Public License, version 2.1 (SPDX short identifier: LGPL-2.1) diff --git a/Licenses/README b/Licenses/README new file mode 100644 index 0000000..a6f97fd --- /dev/null +++ b/Licenses/README @@ -0,0 +1,30 @@ +TTGOSonde is a Free Software code collection for decoding radiosondes with +the SX1278/ESP32-based TTGO board. The code is copyrighted by Hansi Reiser +(dl9rdz) and several other authors who have provided a valuable base and +additonal contributions. + +You can redistribute TTGOSonde and/or modify it under the terms of version 2 +of the GNU General Public License as published by the Free Software Foundation, +or, at your option, under any later version of the GNU General Public License. +See individual files and the file 'Licenses/Exceptions' for exceptions. + +Also note that the GPL and the other licenses are copyrighted by the Free +Software Foundation and other organizations. + +To avoid bloating individual code files with large license headers, the license +headers in the source files have been replaced with a single line reference to +a Unique License Identifier as defined by the Linux Foundation's SPDX project. +For example, in a source file the full "GPL v2.0 or later" header text will be +replaced by a single line: + + SPDX-License-Identifier: GPL-2.0+ + +The SPDX Unique License Identifiers are available at http://spdx.org/licenses/ + +We use the following License Identifiers in this project: + +Full name SPDX Identifier OSI Approved File name URI +======================================================================================================================================= +GNU General Public License v2.0 only GPL-2.0 Y gpl-2.0.txt https://www.gnu.org/licenses/gpl-2.0.txt +GNU General Public License v2.0 or later GPL-2.0+ Y gpl-2.0.txt https://www.gnu.org/licenses/gpl-2.0.txt +GNU Lesser General Public License v2.1 LGPL-2.1+ Y lgpl-2.txt https://www.gnu.org/licenses/lgpl-2.1.txt diff --git a/LICENSE b/Licenses/gpl-2.0.txt similarity index 100% rename from LICENSE rename to Licenses/gpl-2.0.txt diff --git a/Licenses/lgpl-2.1.txt b/Licenses/lgpl-2.1.txt new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/Licenses/lgpl-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey 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 library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/RX_FSK/RX_FSK.ino b/RX_FSK/RX_FSK.ino new file mode 100644 index 0000000..7352a4c --- /dev/null +++ b/RX_FSK/RX_FSK.ino @@ -0,0 +1,184 @@ +#include +#include + +#include + +#include +#include +#include + +#include + +#define LORA_LED 9 + +// I2C OLED Display works with SSD1306 driver +#define OLED_SDA 4 +#define OLED_SCL 15 +#define OLED_RST 16 + +// UNCOMMENT one of the constructor lines below +U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Unbuffered, basic graphics, software I2C +//U8G2_SSD1306_128X64_NONAME_1_SW_I2C Display(U8G2_R0, /* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Page buffer, SW I2C +//U8G2_SSD1306_128X64_NONAME_F_SW_I2C Display(U8G2_R0, /* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST); // Full framebuffer, SW I2C + +int e; +char my_packet[100]; + +const char* ssid = "DinoGast"; +const char* password = "Schokolade"; + +WiFiServer server(80); + +pthread_t wifithread; + + +int conn = 0; +String currentLine; +WiFiClient client; +unsigned long lastdu; + +void wifiloop(void *arg){ + lastdu=millis(); + while(true) { + if(millis()-lastdu>500) { + // This is too slow to do in main loop + //u8x8.setFont(u8x8_font_chroma48medium8_r); + //u8x8.clearDisplay(); + sonde.updateDisplay(); + lastdu=millis(); + } + + + delay(1); + if(!conn) { + client = server.available(); // listen for incoming clients + if (client) { // if you get a client, + Serial.println("New Client."); // print a message out the serial port + currentLine = ""; // make a String to hold incoming data from the client + conn = 1; + } + } else { + if(!client.connected()) { // loop while the client's connected + conn = 0; + Serial.println("Client no longer connected"); + continue; + } + while (client.available()) { // if there's bytes to read from the client, + char c = client.read(); // read a byte, then + Serial.write(c); // print it out the serial monitor + if (c == '\n') { // if the byte is a newline character + + // if the current line is blank, you got two newline characters in a row. + // that's the end of the client HTTP request, so send a response: + if (currentLine.length() == 0) { + // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK) + // and a content-type so the client knows what's coming, then a blank line: + client.println("HTTP/1.1 200 OK"); + client.println("Content-type:text/html"); + client.println(); + + // the content of the HTTP response follows the header: + client.print("Click here to turn the LED on pin 5 on.
"); + client.print("Click here to turn the LED on pin 5 off.
"); + + // The HTTP response ends with another blank line: + client.println(); + // break out of the while loop: + // close the connection: + client.stop(); + Serial.println("Client Disconnected."); + continue; + } else { // if you got a newline, then clear currentLine: + currentLine = ""; + } + } else if (c != '\r') { // if you got anything else but a carriage return character, + currentLine += c; // add it to the end of the currentLine + } + + // Check to see if the client request was "GET /H" or "GET /L": + if (currentLine.endsWith("GET /H")) { + digitalWrite(5, HIGH); // GET /H turns the LED on + } + if (currentLine.endsWith("GET /L")) { + digitalWrite(5, LOW); // GET /L turns the LED off + } + + } + } + } +} + +void setup() +{ + // Open serial communications and wait for port to open: + Serial.begin(115200); + + u8x8.begin(); + + + pinMode(LORA_LED, OUTPUT); + WiFi.begin(ssid, password); + while(WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + server.begin(); + + xTaskCreatePinnedToCore(wifiloop, "WifiServer", 10240, NULL, 10, NULL, 0); + + + rs41.setup(); + + if(rs41.setFrequency(402700000)==0) { + Serial.println(F("Setting freq: SUCCESS ")); + } else { + Serial.println(F("Setting freq: ERROR ")); + } + float f = sx1278.getFrequency(); + Serial.print("Frequency set to "); + Serial.println(f); + + sx1278.setLNAGain(-48); + int gain = sx1278.getLNAGain(); + Serial.print("RX LNA Gain is "); + Serial.println(gain); + + // Print a success message + Serial.println(F("sx1278 configured finished")); + Serial.println(); + + + Serial.println("Setup finished"); + // int returnValue = pthread_create(&wifithread, NULL, wifiloop, (void *)0); + + // if (returnValue) { + // Serial.println("An error has occurred"); + // } + // xTaskCreate(mainloop, "MainServer", 10240, NULL, 10, NULL); + +} + +void loop() { + Serial.println("Running main loop"); + + + //wifiloop(NULL); + //e = dfm.receiveFrame(); + e = rs41.receiveFrame(); + #if 0 + int rssi = sx1278.getRSSI(); + Serial.print(" RSSI: "); + Serial.print(rssi); + + int gain = sx1278.getLNAGain(); + Serial.print(" LNA Gain: "), + Serial.println(gain); + #endif + +} diff --git a/libraries/SX1278FSK/SX1278FSK.cpp b/libraries/SX1278FSK/SX1278FSK.cpp new file mode 100644 index 0000000..5d113c0 --- /dev/null +++ b/libraries/SX1278FSK/SX1278FSK.cpp @@ -0,0 +1,815 @@ +/* + * Functions for using SX127x in FSK mode (mainly receive) + * Copyright (C) 2019 Hansi Reiser, dl9rdz + * + * Partially based on the SX1278 libraray for managing Semtech modules + * Copyright (C) 2015 Wireless Open Source + * http://wirelessopensource.com + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "SX1278FSK.h" +#include "SPI.h" + +SX1278FSK::SX1278FSK() +{ + // Initialize class variables +}; + + + + +/* +Function: Turns the module ON. +Returns: 0 on success, 1 otherwise +*/ +uint8_t SX1278FSK::ON() +{ + uint8_t state = 2; +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'ON'")); +#endif + + // Powering the module + pinMode(SX1278_SS, OUTPUT); + digitalWrite(SX1278_SS, HIGH); + + //Configure the MISO, MOSI, CS, SPCR. + SPI.begin(); + //Set most significant bit first + SPI.setBitOrder(MSBFIRST); + //Divide the clock frequency + SPI.setClockDivider(SPI_CLOCK_DIV2); + //Set data mode + SPI.setDataMode(SPI_MODE0); + + // Set Maximum Over Current Protection + state = setMaxCurrent(0x1B); + if( state == 0 ) + { +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("## Setting ON with maximum current supply ##")); + Serial.println(); +#endif + } + else + { + return 1; + } + + // set FSK mode + state = setFSK(); + return state; +} + +/* +Function: Turn the module OFF. +Returns: Nothing +*/ +void SX1278FSK::OFF() +{ +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'OFF'")); +#endif + + SPI.end(); + // Powering the module + pinMode(SX1278_SS,OUTPUT); + digitalWrite(SX1278_SS,LOW); + +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("## Setting OFF ##")); + Serial.println(); +#endif +} + +/* +Function: Reads the indicated register. +Returns: The content of the register +Parameters: + address: address register to read from +*/ +byte SX1278FSK::readRegister(byte address) +{ + byte value = 0x00; + + digitalWrite(SX1278_SS,LOW); + + delay(1); + bitClear(address, 7); // Bit 7 cleared to write in registers + SPI.transfer(address); + value = SPI.transfer(0x00); + digitalWrite(SX1278_SS,HIGH); + +#if (SX1278FSK_debug_mode > 1) + if(address!=0x3F) { + Serial.print(F("## Reading: ##\t")); + Serial.print(F("Register ")); + Serial.print(address, HEX); + Serial.print(F(": ")); + Serial.print(value, HEX); + Serial.println(); + } +#endif + + return value; +} + +/* +Function: Writes on the indicated register. +Returns: Nothing +Parameters: + address: address register to write in + data: value to write in the register +*/ +void SX1278FSK::writeRegister(byte address, byte data) +{ + digitalWrite(SX1278_SS,LOW); + + delay(1); + bitSet(address, 7); // Bit 7 set to read from registers + SPI.transfer(address); + SPI.transfer(data); + digitalWrite(SX1278_SS,HIGH); + +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Writing: ##\t")); + Serial.print(F("Register ")); + bitClear(address, 7); + Serial.print(address, HEX); + Serial.print(F(": ")); + Serial.print(data, HEX); + Serial.println(); +#endif + +} + +/* + * Function: Clears the IRQ flags + * + * Configuration registers are accessed through the SPI interface. + * Registers are readable in all device mode including Sleep. However, they + * should be written only in Sleep and Stand-by modes. + * + * Returns: Nothing + */ +void SX1278FSK::clearIRQFlags() +{ + byte st0; + + // Save the previous status + st0 = readRegister(REG_OP_MODE); + // Stdby mode to write in registers + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); + // FSK mode flags1 register + writeRegister(REG_IRQ_FLAGS1, 0xFF); + // FSK mode flags2 register + writeRegister(REG_IRQ_FLAGS2, 0xFF); + // Getting back to previous status + if(st0 != FSK_STANDBY_MODE) { + writeRegister(REG_OP_MODE, st0); + } +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("## FSK flags cleared ##")); +#endif +} + +/* +Function: Sets the module in FSK mode. +Returns: Integer that determines if there has been any error + state = 2 --> The command has not been executed + state = 1 --> There has been an error while executing the command + state = 0 --> The command has been executed with no errors +*/ +uint8_t SX1278FSK::setFSK() +{ + uint8_t state = 2; + byte st0; + byte config1; + +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'setFSK'")); +#endif + + writeRegister(REG_OP_MODE, FSK_SLEEP_MODE); // Sleep mode (mandatory to change mode) + // If we are in LORA mode, above line activate Sleep mode, but does not change mode to FSK + // as mode change is only allowed in sleep mode. Next line changes to FSK + writeRegister(REG_OP_MODE, FSK_SLEEP_MODE); + + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // FSK standby mode + + //writeRegister(REG_FIFO_THRESH, 0x80); // condition to start packet tx + //config1 = readRegister(REG_SYNC_CONFIG); + //config1 = config1 & B00111111; + //writeRegister(REG_SYNC_CONFIG,config1); + + delay(100); + + st0 = readRegister(REG_OP_MODE); // Reading config mode + if( st0 == FSK_STANDBY_MODE ) + { // FSK mode + state = 0; +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("## FSK set with success ##")); + Serial.println(); +#endif + } else { // LoRa mode + state = 1; + Serial.println( st0 ); +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("** There has been an error while setting FSK **")); + Serial.println(); +#endif + } + return state; +} + + +/* Function: Sets FSK bitrate + * Returns: 0 for success, >0 in case of error + * Parameters: bps: requested bitrate + * (raw data rate, for Mancester encoding, the effective bitrate is bps/2) +*/ + +uint8_t SX1278FSK::setBitrate(float bps) +{ + // TODO: Check if FSK mode is active + + // check if bitrate is allowed allowed bitrate + if((bps < 1200) || (bps > 300000)) { + return 1; + } + + // set mode to FSK STANDBY + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); + + // set bit rate + uint16_t bitRate = (SX127X_CRYSTAL_FREQ * 1.0) / bps; + writeRegister(REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8); + writeRegister(REG_BITRATE_LSB, (bitRate & 0x00FF)); + + // also set fractional part + uint16_t fracRate = (SX127X_CRYSTAL_FREQ * 16.0) / bps - bitRate * 16 + 0.5; + writeRegister(REG_BIT_RATE_FRAC, fracRate&0x0F); + return 0; +} +/* Function: Gets configured bitrate + * Returns bitrate in bit/second + */ +float SX1278FSK::getBitrate() +{ + uint8_t fmsb = readRegister(REG_BITRATE_MSB); + uint8_t flsb = readRegister(REG_BITRATE_LSB); + uint8_t ffrac = readRegister(REG_BIT_RATE_FRAC) & 0x0F; + return SX127X_CRYSTAL_FREQ / ( (fmsb<<8) + flsb + ffrac / 16.0 ); +} + +//typedef struct rxbwset { float bw; uint8_t mant; uint8_t rxp; } st_rxbwsettings; + +uint8_t SX1278FSK::setRxBandwidth(float bw) +{ + // TODO: Check if in FSK mode + // + if(bw<2600 || bw>250000) { return 1; /* invalid */ } + + uint8_t rxbwexp = 1; + bw = SX127X_CRYSTAL_FREQ / bw / 8; + while(bw>31) { rxbwexp++; bw/=2.0; } + uint8_t rxbwmant = bw<17?0 : bw<21? 1:2; + + // set mode to FSK STANDBY + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); + + writeRegister(REG_RX_BW, rxbwexp | (rxbwmant<<3)); + return 0; +} + +float SX1278FSK::getRxBandwidth() +{ + uint8_t rxbw = readRegister(REG_RX_BW); + uint8_t rxbwexp = rxbw&0x07; + uint8_t rxbwmant = (rxbw>>3)&0x03; + rxbwmant = 16 + 4*rxbwmant; + return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2)); +} + +uint8_t SX1278FSK::setAFCBandwidth(float bw) +{ + // TODO: Check if in FSK mode + // + if(bw<2600 || bw>250000) { return 1; /* invalid */ } + + uint8_t rxbwexp = 1; + bw = SX127X_CRYSTAL_FREQ / bw / 8; + while(bw>31) { rxbwexp++; bw/=2.0; } + uint8_t rxbwmant = bw<17?0 : bw<21? 1:2; + + // set mode to FSK STANDBY + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); + + writeRegister(REG_AFC_BW, rxbwexp | (rxbwmant<<3)); + return 0; +} + +float SX1278FSK::getAFCBandwidth() +{ + uint8_t rxbw = readRegister(REG_AFC_BW); + uint8_t rxbwexp = rxbw&0x07; + uint8_t rxbwmant = (rxbw>>3)&0x03; + rxbwmant = 16 + 4*rxbwmant; + return SX127X_CRYSTAL_FREQ / ( rxbwmant << (rxbwexp+2)); +} + + +uint8_t SX1278FSK::setFrequency(float freq) { + + // set mode to FSK STANDBY + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); + + uint32_t frf = freq * 1.0 * (1<<19) / SX127X_CRYSTAL_FREQ; + writeRegister(REG_FRF_MSB, (frf&0xff0000)>>16); + writeRegister(REG_FRF_MID, (frf&0x00ff00)>>8); + writeRegister(REG_FRF_LSB, (frf&0x0000ff)); + return 0; +} + +float SX1278FSK::getFrequency() { + uint8_t fmsb = readRegister(REG_FRF_MSB); + uint8_t fmid = readRegister(REG_FRF_MID); + uint8_t flsb = readRegister(REG_FRF_LSB); + return ((fmsb<<16)|(fmid<<8)|flsb) * 1.0 / (1<<19) * SX127X_CRYSTAL_FREQ; +} + + +static int gaintab[]={-999,0,-6,-12,-24,-36,-48,-999}; +int SX1278FSK::getLNAGain() { + int gain = (readRegister(REG_LNA)>>5)&0x07; + return gaintab[gain]; +} +uint8_t SX1278FSK::setLNAGain(int gain) { + uint8_t g=1; + while(gain8) return 1; + for(int i=0; i 1) + Serial.println(); + Serial.println(F("Starting 'getPreambleLength'")); +#endif + + p_length = readRegister(REG_PREAMBLE_MSB_FSK); + p_length = (p_length<<8) | readRegister(REG_PREAMBLE_LSB_FSK); +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Preamble length configured is ")); + Serial.print(p_length, HEX); + Serial.print(F(" ##")); + Serial.println(); +#endif + return p_length; +} + +/* +Function: Sets the preamble length in the module +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +Parameters: +l: length value to set as preamble length. +*/ +uint8_t SX1278FSK::setPreambleLength(uint16_t l) +{ + byte st0; + int8_t state = 2; + +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'setPreambleLength'")); +#endif + + st0 = readRegister(REG_OP_MODE); // Save the previous status + + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set Standby mode to write in registers + // Storing MSB preamble length in FSK mode + writeRegister(REG_PREAMBLE_MSB_FSK, l>>8); + writeRegister(REG_PREAMBLE_LSB_FSK, l&0xFF); + + state = 0; +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Preamble length ")); + Serial.print(l, HEX); + Serial.println(F(" has been successfully set ##")); + Serial.println(); +#endif + + if(st0 != FSK_STANDBY_MODE) { + writeRegister(REG_OP_MODE, st0); // Getting back to previous status + } + return state; +} + +/* +Function: Gets the payload length from the module. +Returns: configured length; -1 on error +*/ +int SX1278FSK::getPayloadLength() +{ + int length; +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'getPayloadLength'")); +#endif + length = readRegister(REG_PAYLOAD_LENGTH_FSK); + +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Payload length configured is ")); + Serial.print(length); + Serial.println(F(" ##")); +#endif + return length; +} + +/* +Function: Sets the payload length from the module. +Returns: 0 for ok, otherwise error +// TODO: Larger than 255 bytes? +*/ +uint8_t SX1278FSK::setPayloadLength(int len) +{ +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("Starting 'setPayloadLength'")); + Serial.println(len); +#endif + uint8_t conf2 = readRegister(REG_PACKET_CONFIG2); + conf2 = (conf2 & 0xF8) | ( (len>>8)&0x7 ); + writeRegister(REG_PACKET_CONFIG2, conf2); + writeRegister(REG_PAYLOAD_LENGTH_FSK, len&0xFF); + return 0; +} + +/* +Function: Gets the current value of RSSI. +Returns: RSSI value +*/ +int16_t SX1278FSK::getRSSI() +{ + int16_t RSSI; + //int rssi_mean = 0; + int total = 1; + + /// FSK mode + // get mean value of RSSI + for(int i = 0; i < total; i++) + { + RSSI = readRegister(REG_RSSI_VALUE_FSK); + //rssi_mean += _RSSI; + } + //rssi_mean = rssi_mean / total; + //RSSI = rssi_mean; + +#if (SX1278FSK_debug_mode > 0) + Serial.print(F("## RSSI value is ")); + Serial.print(RSSI); + Serial.println(F(" ##")); +#endif + return RSSI; +} + + +/* +Function: Gets the current supply limit of the power amplifier, protecting battery chemistries. +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +Parameters: +rate: value to compute the maximum current supply. Maximum current is 45+5*'rate' [mA] +*/ +int SX1278FSK::getMaxCurrent() +{ + int value; + +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'getMaxCurrent'")); +#endif + + value = readRegister(REG_OCP); + + // extract only the OcpTrim value from the OCP register + value &= B00011111; + + if( value <= 15 ) { + value = (45 + (5 * value)); + } else if( value <= 27 ) { + value = (-30 + (10 * value)); + } else { + value = 240; + } +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Maximum current supply configured is ")); + Serial.print(value, DEC); + Serial.println(F(" mA ##")); + Serial.println(); +#endif + return value; +} + +/* +Function: Limits the current supply of the power amplifier, protecting battery chemistries. +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +state = -1 --> Forbidden parameter value for this function +Parameters: +rate: value to compute the maximum current supply. Range: 0x00 to 0x1B. The +Maximum current is: +Imax = 45+5*OcpTrim [mA] if OcpTrim <= 15 (120 mA) / +Imax = -30+10*OcpTrim [mA] if 15 < OcpTrim <= 27 (130 to 240 mA) +Imax = 240mA for higher settings +*/ +int8_t SX1278FSK::setMaxCurrent(uint8_t rate) +{ + int8_t state = 2; + byte st0; + +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'setMaxCurrent'")); +#endif + + // Maximum rate value = 0x1B, because maximum current supply = 240 mA + if (rate > 0x1B) + { + state = -1; +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("** Maximum current supply is 240 mA, ")); + Serial.println(F("so maximum parameter value must be 27 (DEC) or 0x1B (HEX) **")); + Serial.println(); +#endif + } + else + { + // Enable Over Current Protection + rate |= B00100000; + + state = 1; + st0 = readRegister(REG_OP_MODE); // Save the previous status + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Set FSK Standby mode to write in registers + writeRegister(REG_OCP, rate); // Modifying maximum current supply + if(st0 != FSK_STANDBY_MODE) { + writeRegister(REG_OP_MODE, st0); // Getting back to previous status + } + state = 0; + } + return state; +} + + +/* +Function: Configures the module to receive information. +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +*/ +uint8_t SX1278FSK::receive() +{ + uint8_t state = 1; +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'receive'")); +#endif + // TODO: Is there anything else to be done? + // + writeRegister(REG_OP_MODE, FSK_RX_MODE); + state = 0; +#if (SX1278FSK_debug_mode > 1) + Serial.println(F("## Receiving FSK mode activated with success ##")); +#endif + return state; +} + +/* +Function: Configures the module to receive a packet +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +Parameters: + wait: timeout in ms + data: memory where to place received data +*/ +uint8_t SX1278FSK::receivePacketTimeout(uint32_t wait, byte *data) +{ + int di=0; + uint8_t state = 2; + unsigned long previous; + byte value = 0x00; +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'receivePacketTimeout'")); +#endif + // set RX mode + state = receive(); + if(state != 0) { return state; } + + boolean p_received = false; + +#if (SX1278FSK_debug_mode > 0) + Serial.println(F("RX mode sucessfully activated")); +#endif + previous = millis(); + /// FSK mode + value = readRegister(REG_IRQ_FLAGS2); + byte ready=0; + // while not yet done or FIFO not yet empty + while( (!ready || bitRead(value,6)==0) && (millis() - previous < wait) ) + { + if( bitRead(value,2)==1 ) ready=1; + if( bitRead(value, 6) == 0 ) { // FIFO not empty + data[di++] = readRegister(REG_FIFO); + if(di==1) { + int rssi=getRSSI(); + Serial.print("Test: RSSI="); Serial.println(rssi); + } + if(di>520) { + // TODO + Serial.println("TOO MUCH DATA"); + break; + } + previous = millis(); // reset timeout after receiving data + } + value = readRegister(REG_IRQ_FLAGS2); + } + if( !ready || bitRead(value, 6)==0) { +#if 1&&(SX1278FSK_debug_mode > 0) + Serial.println(F("** The timeout has expired **")); + Serial.println(); +#endif + writeRegister(REG_OP_MODE, FSK_STANDBY_MODE); // Setting standby FSK mode + return 1; // TIMEOUT + } + +#if (SX1278FSK_debug_mode > 0) + Serial.println(F("## Packet received:")); + for(unsigned int i = 0; i < di; i++) + { + Serial.print(data[i], HEX); // Printing payload + Serial.print("|"); + } + Serial.println(F(" ##")); +#endif + state = 0; + // Initializing flags + clearIRQFlags(); + + return state; +} + + +#if 0 +/* +Function: It gets the temperature from the measurement block module. +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +*/ +uint8_t SX1278FSK::getTemp() +{ + byte st0; + uint8_t state = 2; + +#if (SX1278FSK_debug_mode > 1) + Serial.println(); + Serial.println(F("Starting 'getTemp'")); +#endif + + st0 = readRegister(REG_OP_MODE); // Save the previous status + + if( _modem == LORA ) + { // Allowing access to FSK registers while in LoRa standby mode + writeRegister(REG_OP_MODE, LORA_STANDBY_FSK_REGS_MODE); + } + + state = 1; + // Saving temperature value + _temp = readRegister(REG_TEMP); + if( _temp & 0x80 ) // The SNR sign bit is 1 + { + // Invert and divide by 4 + _temp = ( ( ~_temp + 1 ) & 0xFF ); + } + else + { + // Divide by 4 + _temp = ( _temp & 0xFF ); + } + + +#if (SX1278FSK_debug_mode > 1) + Serial.print(F("## Temperature is: ")); + Serial.print(_temp); + Serial.println(F(" ##")); + Serial.println(); +#endif + + if( _modem == LORA ) + { + writeRegister(REG_OP_MODE, st0); // Getting back to previous status + } + + state = 0; + return state; +} + +/* +Function: It prints the registers related to RX +Returns: Integer that determines if there has been any error +state = 2 --> The command has not been executed +state = 1 --> There has been an error while executing the command +state = 0 --> The command has been executed with no errors +*/ +void SX1278FSK::showRxRegisters() +{ + Serial.println(F("\n--- Show RX register ---")); + + // variable + byte reg; + + for(int i = 0x00; i < 0x80; i++) + { + reg = readRegister(i); + Serial.print(F("Reg 0x")); + Serial.print(i, HEX); + Serial.print(F(":")); + Serial.print(reg, HEX); + Serial.println(); + delay(100); + } + + Serial.println(F("------------------------")); + +} +#endif + +SX1278FSK sx1278 = SX1278FSK(); diff --git a/libraries/SX1278FSK/SX1278FSK.h b/libraries/SX1278FSK/SX1278FSK.h new file mode 100644 index 0000000..2f2af07 --- /dev/null +++ b/libraries/SX1278FSK/SX1278FSK.h @@ -0,0 +1,271 @@ +/* + * Functions for using SX127x in FSK mode (mainly receive) + * Copyright (C) 2019 Hansi Reiser, dl9rdz + * + * Partially based on the SX1278 libraray for managing Semtech modules + * Copyright (C) 2015 Wireless Open Source + * http://wirelessopensource.com + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef SX1278FSK_h +#define SX1278FSK_h + +/****************************************************************************** + * Includes + ******************************************************************************/ + +#include +#include +#include +#include + +#ifndef inttypes_h + #include +#endif + + +/****************************************************************************** + * Definitions & Declarations + *****************************************************************************/ + +#define SX127X_CRYSTAL_FREQ 32000000 + +#define SX1278FSK_debug_mode 0 + +#define SX1278_SS SS + +//! MACROS // +#define bitRead(value, bit) (((value) >> (bit)) & 0x01) // read a bit +#define bitSet(value, bit) ((value) |= (1UL << (bit))) // set bit to '1' +#define bitClear(value, bit) ((value) &= ~(1UL << (bit))) // set bit to '0' + + +//! REGISTERS // +// FSK Commun LORA +#define REG_FIFO 0x00 +#define REG_OP_MODE 0x01 +#define REG_BITRATE_MSB 0x02 +#define REG_BITRATE_LSB 0x03 +#define REG_FDEV_MSB 0x04 +#define REG_FDEV_LSB 0x05 +#define REG_FRF_MSB 0x06 +#define REG_FRF_MID 0x07 +#define REG_FRF_LSB 0x08 +#define REG_PA_CONFIG 0x09 +#define REG_PA_RAMP 0x0A +#define REG_OCP 0x0B +#define REG_LNA 0x0C +#define REG_RX_CONFIG 0x0D +#define REG_FIFO_ADDR_PTR 0x0D +#define REG_RSSI_CONFIG 0x0E +#define REG_FIFO_TX_BASE_ADDR 0x0E +#define REG_RSSI_COLLISION 0x0F +#define REG_FIFO_RX_BASE_ADDR 0x0F +#define REG_RSSI_THRESH 0x10 +#define REG_FIFO_RX_CURRENT_ADDR 0x10 +#define REG_RSSI_VALUE_FSK 0x11 +#define REG_IRQ_FLAGS_MASK 0x11 +#define REG_RX_BW 0x12 +#define REG_IRQ_FLAGS 0x12 +#define REG_AFC_BW 0x13 +#define REG_RX_NB_BYTES 0x13 +#define REG_OOK_PEAK 0x14 +#define REG_RX_HEADER_CNT_VALUE_MSB 0x14 +#define REG_OOK_FIX 0x15 +#define REG_RX_HEADER_CNT_VALUE_LSB 0x15 +#define REG_OOK_AVG 0x16 +#define REG_RX_PACKET_CNT_VALUE_MSB 0x16 +#define REG_RX_PACKET_CNT_VALUE_LSB 0x17 +#define REG_MODEM_STAT 0x18 +#define REG_PKT_SNR_VALUE 0x19 +#define REG_AFC_FEI 0x1A +#define REG_PKT_RSSI_VALUE 0x1A +#define REG_AFC_MSB 0x1B +#define REG_RSSI_VALUE_LORA 0x1B +#define REG_AFC_LSB 0x1C +#define REG_HOP_CHANNEL 0x1C +#define REG_FEI_MSB 0x1D +#define REG_MODEM_CONFIG1 0x1D +#define REG_FEI_LSB 0x1E +#define REG_MODEM_CONFIG2 0x1E +#define REG_PREAMBLE_DETECT 0x1F +#define REG_SYMB_TIMEOUT_LSB 0x1F +#define REG_RX_TIMEOUT1 0x20 +#define REG_PREAMBLE_MSB_LORA 0x20 +#define REG_RX_TIMEOUT2 0x21 +#define REG_PREAMBLE_LSB_LORA 0x21 +#define REG_RX_TIMEOUT3 0x22 +#define REG_PAYLOAD_LENGTH_LORA 0x22 +#define REG_RX_DELAY 0x23 +#define REG_MAX_PAYLOAD_LENGTH 0x23 +#define REG_OSC 0x24 +#define REG_HOP_PERIOD 0x24 +#define REG_PREAMBLE_MSB_FSK 0x25 +#define REG_FIFO_RX_BYTE_ADDR 0x25 +#define REG_PREAMBLE_LSB_FSK 0x26 +#define REG_MODEM_CONFIG3 0x26 +#define REG_SYNC_CONFIG 0x27 +#define REG_SYNC_VALUE1 0x28 +#define REG_FEI_MSB 0x28 +#define REG_SYNC_VALUE2 0x29 +#define REG_FEI_MID 0x29 +#define REG_SYNC_VALUE3 0x2A +#define REG_FEI_LSB 0x2A +#define REG_SYNC_VALUE4 0x2B +#define REG_SYNC_VALUE5 0x2C +#define REG_RSSI_WIDEBAND 0x2C +#define REG_SYNC_VALUE6 0x2D +#define REG_SYNC_VALUE7 0x2E +#define REG_SYNC_VALUE8 0x2F +#define REG_PACKET_CONFIG1 0x30 +#define REG_PACKET_CONFIG2 0x31 +#define REG_DETECT_OPTIMIZE 0x31 +#define REG_PAYLOAD_LENGTH_FSK 0x32 +#define REG_NODE_ADRS 0x33 +#define REG_INVERT_IQ 0x33 +#define REG_BROADCAST_ADRS 0x34 +#define REG_FIFO_THRESH 0x35 +#define REG_SEQ_CONFIG1 0x36 +#define REG_SEQ_CONFIG2 0x37 +#define REG_DETECTION_THRESHOLD 0x37 +#define REG_TIMER_RESOL 0x38 +#define REG_TIMER1_COEF 0x39 +#define REG_SYNC_WORD 0x39 +#define REG_TIMER2_COEF 0x3A +#define REG_IMAGE_CAL 0x3B +#define REG_TEMP 0x3C +#define REG_LOW_BAT 0x3D +#define REG_IRQ_FLAGS1 0x3E +#define REG_IRQ_FLAGS2 0x3F +#define REG_DIO_MAPPING1 0x40 +#define REG_DIO_MAPPING2 0x41 +#define REG_VERSION 0x42 +#define REG_PLL_HOP 0x44 +#define REG_TCXO 0x4B +#define REG_PA_DAC 0x4D +#define REG_FORMER_TEMP 0x5B +#define REG_BIT_RATE_FRAC 0x5D +#define REG_AGC_REF 0x61 +#define REG_AGC_THRESH1 0x62 +#define REG_AGC_THRESH2 0x63 +#define REG_AGC_THRESH3 0x64 +#define REG_PLL 0x70 + + +//FSK MODES: +const uint8_t FSK_SLEEP_MODE = 0x00; +const uint8_t FSK_STANDBY_MODE = 0x01; +const uint8_t FSK_TX_MODE = 0x03; +const uint8_t FSK_RX_MODE = 0x05; + + +/****************************************************************************** + * SX1278FSK Class + * Functions and variables for managing SX127x transceiver chips in FSK mode, + * mainly for receiving radiosonde transmissions + ******************************************************************************/ +class SX1278FSK +{ +public: + // class constructor + SX1278FSK(); + + // Turn on SX1278 module (return 0 on sucess, 1 otherwise) + uint8_t ON(); + + // Turn off SX1278 module + void OFF(); + + // Read internal register + byte readRegister(byte address); + + // Write internal register + void writeRegister(byte address, byte data); + + // Clear IRQ flags + void clearIRQFlags(); + + // Activate FSK mode (return 0 on success, 1 otherwise) + uint8_t setFSK(); + + // Configures bitrate register (closest approximation to requested bitrate) + uint8_t setBitrate(float bps); + float getBitrate(); + + // Configures RX bandwidth (next largest supported bandwith if exact value not possible) + uint8_t setRxBandwidth(float bps); + float getRxBandwidth(); + + // Configures AFC bandwidth (next largest supported bandwith if exact value not possible) + uint8_t setAFCBandwidth(float bps); + float getAFCBandwidth(); + + // Configures RX frequency (closest approximation to requested frequency) + uint8_t setFrequency(float freq); + float getFrequency(); + + int getLNAGain(); + uint8_t setLNAGain(int gain); + + uint8_t getRxConf(); + uint8_t setRxConf(uint8_t conf); + + uint8_t setSyncConf(uint8_t conf, int len, const uint8_t *syncpattern); + uint8_t getSyncConf(); + + uint8_t setPreambleDetect(uint8_t conf); + uint8_t getPreambleDetect(); + + uint8_t setPacketConfig(uint8_t conf1, uint8_t conf2); + uint16_t getPacketConfig(); + + // Get configured preamble length (used for TX only?) + uint16_t getPreambleLength(); + + // Sets the preamble length. + uint8_t setPreambleLength(uint16_t l); + + // Gets the payload length (expected length for receive) + int getPayloadLength(); + uint8_t setPayloadLength(int len); + + // Get current RSSI value + int16_t getRSSI(); + + // Get the maximum current supply by the module. + int getMaxCurrent(); + + // Set the maximum current supply by the module. + int8_t setMaxCurrent(uint8_t rate); + + // Put the module in reception mode. + //return '0' on success, '1' otherwise + uint8_t receive(); + + // Receive a packet + uint8_t receivePacketTimeout(uint32_t wait, byte *data); + + + +#if 0 + //! It gets the internal temperature of the module. + /*! + It stores in global '_temp' variable the module temperature. + \return '0' on success, '1' otherwise + */ + uint8_t getTemp(); + + //! It prints the registers related to RX via USB + /*! + * \return void + */ + void showRxRegisters(); +#endif + +}; + +extern SX1278FSK sx1278; + +#endif diff --git a/libraries/SondeLib/DFM.cpp b/libraries/SondeLib/DFM.cpp new file mode 100644 index 0000000..1396fc9 --- /dev/null +++ b/libraries/SondeLib/DFM.cpp @@ -0,0 +1,282 @@ + +/* DFM decoder functions */ +#include "DFM.h" +#include "SX1278FSK.h" + +#define DFM_DEBUG 1 + +#if DFM_DEBUG +#define DFM_DBG(x) x +#else +#define DFM_DBG(x) +#endif + +int DFM::setup(int inverse) +{ +#if DFM_DEBUG + Serial.println("Setup sx1278 for DFM sonde"); +#endif + if(sx1278.ON()!=0) { + DFM_DBG(Serial.println("Setting SX1278 power on FAILED")); + return 1; + } + if(sx1278.setFSK()!=0) { + DFM_DBG(Serial.println("Setting FSM mode FAILED")); + return 1; + } + if(sx1278.setBitrate(2500)!=0) { + DFM_DBG(Serial.println("Setting bitrate 2500bit/s FAILED")); + return 1; + } +#if DFM_DEBUG + float br = sx1278.getBitrate(); + Serial.print("Exact bitrate is "); + Serial.println(br); +#endif + + if(sx1278.setAFCBandwidth(25000)!=0) { + DFM_DBG(Serial.println("Setting AFC bandwidth 25 kHz FAILED")); + return 1; + } + if(sx1278.setRxBandwidth(12000)!=0) { + DFM_DBG(Serial.println("Setting RX bandwidth 12kHz FAILED")); + return 1; + } + // Enable auto-AFC, auto-AGC, RX Trigger by preamble + if(sx1278.setRxConf(0x1E)!=0) { + DFM_DBG(Serial.println("Setting RX Config FAILED")); + return 1; + } + // Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte + //char header[] = "0110.0101 0110.0110 1010.0101 1010.1010"; + + const char *SYNC=inverse?"\x9A\x99\x5A\x55":"\x65\x66\xA5\xAA"; + if(sx1278.setSyncConf(0x53, 4, (const uint8_t *)SYNC)!=0) { + DFM_DBG(Serial.println("Setting SYNC Config FAILED")); + return 1; + } + if(sx1278.setPreambleDetect(0xA8)!=0) { + DFM_DBG(Serial.println("Setting PreambleDetect FAILED")); + return 1; + } + + // Packet config 1: fixed len, mancecer, no crc, no address filter + // Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0) + if(sx1278.setPacketConfig(0x28, 0x40)!=0) { + DFM_DBG(Serial.println("Setting Packet config FAILED")); + return 1; + } + DFM_DBG(Serial.println("Setting SX1278 config for DFM finished\n"); Serial.println()); + return 0; +} + +int DFM::setFrequency(float frequency) { + return sx1278.setFrequency(frequency); +} + +#define bit(value,bitpos) ((value>>(7-bitpos))&0x01) +// Input: str: packed data, MSB first +void DFM::deinterleave(uint8_t *str, int L, uint8_t *block) { + int i, j; + for (j = 0; j < B; j++) { // L = 7 (CFG), 13 (DAT1, DAT2) + for (i = 0; i < L; i++) { + block[B*i+j] = bit( str[(L*j+i)/8], (L*j+i)&7 )?0:1; + } + } +} + +uint32_t DFM::bits2val(const uint8_t *bits, int len) { + uint32_t val = 0; + for (int j = 0; j < len; j++) { + val |= (bits[j] << (len-1-j)); + } + return val; +} + +// Error correction for hamming code +// returns 0: ok >0: 1 error was corrected -1: uncorrectable error +int DFM::check(uint8_t code[8]) { + int i, j; + uint32_t synval = 0; + uint8_t syndrom[4]; + int ret=0; + + for (i = 0; i < 4; i++) { + syndrom[i] = 0; + for (j = 0; j < 8; j++) { + syndrom[i] ^= H[i][j] & code[j]; + } + } + synval = bits2val(syndrom, 4); + if (synval) { + ret = -1; + for (j = 0; j < 8; j++) { // 1-bit-error + if (synval == He[j]) { // reicht auf databits zu pruefen, d.h. + ret = j+1; // (systematischer Code) He[0..3] + break; + } + } + } + else ret = 0; + if (ret > 0) code[ret-1] ^= 0x1; + + return ret; +} + +// Extended (8,4) Hamming code +// Return number of corrected bits, -1 if uncorrectable error +int DFM::hamming(uint8_t *ham, int L, uint8_t *sym) { + int i, j; + int ret = 0; // DFM: length L = 7 or 13 + for (i = 0; i < L; i++) { // L bytes (4bit data, 4bit parity) + if (use_ecc) { + int res = check(ham+8*i); + if(ret>=0 && res>=0) ret += res; else ret=-1; + } + // systematic Hamming code: copy bits 0..3 + for (j = 0; j < 4; j++) { + sym[4*i+j] = ham[8*i+j]; + } + } + return ret; +} + +DFM::DFM() { +} + +void DFM::printRaw(char *label, int len, int ret, uint8_t *data) +{ + Serial.print(label); Serial.print("("); + Serial.print(ret); + Serial.print("):"); + int i; + for(i=0; i>4)==0x06 && type==0) { // DFM-6 ID + lowid = ((cfg[0]&0x0F)<<20) | (cfg[1]<<12) | (cfg[2]<<4) | (cfg[3]&0x0f); + Serial.print("DFM-06 ID: "); Serial.print(lowid, HEX); + } + if((cfg[0]>>4)==0x0A) { // DMF-9 ID + type=9; + if(cfg[3]==1) { + lowid = (cfg[1]<<8) | cfg[2]; + idgood |= 1; + } else { + highid = (cfg[1]<<8) | cfg[2]; + idgood |= 2; + } + if(idgood==3) { + uint32_t dfmid = (highid<<16) | lowid; + Serial.print("DFM-09 ID: "); Serial.print(dfmid); + } + } +} + +int DFM::decodeDAT(uint8_t *dat) +{ + Serial.print(" DAT["); Serial.print(dat[6]); Serial.print("]: "); + switch(dat[6]) { + case 0: + Serial.print("Packet counter: "); Serial.print(dat[3]); + break; + case 1: + { + int val = (((uint16_t)dat[4])<<8) + (uint16_t)dat[5]; + Serial.print("UTC-msec: "); Serial.print(val); + } + break; + case 2: + { + float lat, vh; + lat = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + ((uint32_t)dat[3]); + vh = (dat[4]<<8) + dat[5]; + Serial.print("GPS-lat: "); Serial.print(lat*0.0000001); + Serial.print(", hor-V: "); Serial.print(vh*0.01); + } + break; + case 3: + { + float lon, dir; + lon = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + (uint32_t)dat[3]; + dir = ((uint16_t)dat[4]<<8) + dat[5]; + Serial.print("GPS-lon: "); Serial.print(lon*0.0000001); + Serial.print(", dir: "); Serial.print(dir*0.01); + } + break; + case 4: + { + float hei, vv; + hei = ((uint32_t)dat[0]<<24) + ((uint32_t)dat[1]<<16) + ((uint32_t)dat[2]<<8) + dat[3]; + vv = (int16_t)( (dat[4]<<8) | dat[5] ); + Serial.print("GPS-height: "); Serial.print(hei*0.01); + Serial.print(", vv: "); Serial.print(vv*0.01); + } + break; + case 8: + { + int y = (dat[0]<<4) + (dat[1]>>4); + int m = dat[1]&0x0F; + int d = dat[2]>>3; + int h = ((dat[2]&0x07)<<2) + (dat[3]>>6); + int mi = (dat[3]&0x3F); + char buf[100]; + snprintf(buf, 100, "%04d-%02d-%02d %02d:%02dz", y, m, d, h, mi); + Serial.print("Date: "); Serial.print(buf); + } + break; + default: + Serial.print("(?)"); + break; + } +} + +int DFM::bitsToBytes(uint8_t *bits, uint8_t *bytes, int len) +{ + int i; + for(i=0; i +#include +#include +#ifndef inttypes_h + #include +#endif + +#define DFM_NORMAL 0 +#define DFM_INVERSE 1 + +/* Main class */ +class DFM +{ +private: + void deinterleave(uint8_t *str, int L, uint8_t *block); + uint32_t bits2val(const uint8_t *bits, int len); + int check(uint8_t code[8]); + int hamming(uint8_t *ham, int L, uint8_t *sym); + void printRaw(char *prefix, int len, int ret, uint8_t* data); + int decodeCFG(uint8_t *cfg); + int decodeDAT(uint8_t *dat); + int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len); + +#define B 8 +#define S 4 + uint8_t hamming_conf[ 7*B]; // 7*8=56 + uint8_t hamming_dat1[13*B]; // 13*8=104 + uint8_t hamming_dat2[13*B]; + + uint8_t block_conf[ 7*S]; // 7*4=28 + uint8_t block_dat1[13*S]; // 13*4=52 + uint8_t block_dat2[13*S]; + + uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix + {{ 0, 1, 1, 1, 1, 0, 0, 0}, + { 1, 0, 1, 1, 0, 1, 0, 0}, + { 1, 1, 0, 1, 0, 0, 1, 0}, + { 1, 1, 1, 0, 0, 0, 0, 1}}; + uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H: + // 1-bit-error-Syndrome + +public: + DFM(); + int setup(int inverse); + int setFrequency(float frequency); + int receiveFrame(); + + int use_ecc = 1; +}; + +extern DFM dfm; + +#endif diff --git a/libraries/SondeLib/RS41.cpp b/libraries/SondeLib/RS41.cpp new file mode 100644 index 0000000..082e1d8 --- /dev/null +++ b/libraries/SondeLib/RS41.cpp @@ -0,0 +1,476 @@ + +/* RS41 decoder functions */ +#include "RS41.h" +#include "SX1278FSK.h" +#include "rsc.h" +#include "Sonde.h" + +#define RS41_DEBUG 1 + +#if RS41_DEBUG +#define RS41_DBG(x) x +#else +#define RS41_DBG(x) +#endif + +static uint16_t CRCTAB[256]; + +#define X2C_DIVR(a, b) ((b) != 0.0f ? (a)/(b) : (a)) +#define X2C_DIVL(a, b) ((a)/(b)) +static uint32_t X2C_LSH(uint32_t a, int32_t length, int32_t n) +{ + uint32_t m; + + m = 0; + m = (length == 32) ? 0xFFFFFFFFl : (1 << length) - 1; + if (n > 0) { + if (n >= (int32_t)length) + return 0; + return (a << n) & m; + } + + if (n <= (int32_t)-length) + return 0; + return (a >> -n) & m; +} + +static double atang2(double x, double y) +{ + double w; + if (fabs(x)>fabs(y)) { + w = (double)atan((float)(X2C_DIVL(y,x))); + if (x<0.0) { + if (y>0.0) w = 3.1415926535898+w; + else w = w-3.1415926535898; + } + } + else if (y!=0.0) { + w = (double)(1.5707963267949f-atan((float)(X2C_DIVL(x, + y)))); + if (y<0.0) w = w-3.1415926535898; + } + else w = 0.0; + return w; +} /* end atang2() */ + + +static void Gencrctab(void) +{ + uint16_t j; + uint16_t i; + uint16_t crc; + for (i = 0U; i<=255U; i++) { + crc = (uint16_t)(i*256U); + for (j = 0U; j<=7U; j++) { + if ((0x8000U & crc)) crc = X2C_LSH(crc,16,1)^0x1021U; + else crc = X2C_LSH(crc,16,1); + } /* end for */ + CRCTAB[i] = X2C_LSH(crc,16,-8)|X2C_LSH(crc,16,8); + } /* end for */ +} /* end Gencrctab() */ + +int RS41::setup() +{ +#if RS41_DEBUG + Serial.println("Setup sx1278 for RS41 sonde"); +#endif + Gencrctab(); + initrsc(); + + if(sx1278.ON()!=0) { + RS41_DBG(Serial.println("Setting SX1278 power on FAILED")); + return 1; + } + if(sx1278.setFSK()!=0) { + RS41_DBG(Serial.println("Setting FSM mode FAILED")); + return 1; + } + if(sx1278.setBitrate(4800)!=0) { + RS41_DBG(Serial.println("Setting bitrate 4800bit/s FAILED")); + return 1; + } +#if RS41_DEBUG + float br = sx1278.getBitrate(); + Serial.print("Exact bitrate is "); + Serial.println(br); +#endif + + if(sx1278.setAFCBandwidth(25000)!=0) { + RS41_DBG(Serial.println("Setting AFC bandwidth 25 kHz FAILED")); + return 1; + } + if(sx1278.setRxBandwidth(12000)!=0) { + RS41_DBG(Serial.println("Setting RX bandwidth 12kHz FAILED")); + return 1; + } + // Enable auto-AFC, auto-AGC, RX Trigger by preamble + if(sx1278.setRxConf(0x1E)!=0) { + RS41_DBG(Serial.println("Setting RX Config FAILED")); + return 1; + } + // Set autostart_RX to 01, preamble 0, SYNC detect==on, syncsize=3 (==4 byte + //char header[] = "0110.0101 0110.0110 1010.0101 1010.1010"; + + //const char *SYNC="\x10\xB6\xCA\x11\x22\x96\x12\xF8"; + const char *SYNC="\x08\x6D\x53\x88\x44\x69\x48\x1F"; + if(sx1278.setSyncConf(0x57, 8, (const uint8_t *)SYNC)!=0) { + RS41_DBG(Serial.println("Setting SYNC Config FAILED")); + return 1; + } + if(sx1278.setPreambleDetect(0xA8)!=0) { + RS41_DBG(Serial.println("Setting PreambleDetect FAILED")); + return 1; + } + + // Packet config 1: fixed len, no mancecer, no crc, no address filter + // Packet config 2: packet mode, no home ctrl, no beackn, msb(packetlen)=0) + if(sx1278.setPacketConfig(0x08, 0x40)!=0) { + RS41_DBG(Serial.println("Setting Packet config FAILED")); + return 1; + } + RS41_DBG(Serial.println("Setting SX1278 config for RS41 finished\n"); Serial.println()); + return 0; +} + +int RS41::setFrequency(float frequency) { + return sx1278.setFrequency(frequency); +} + +uint32_t RS41::bits2val(const uint8_t *bits, int len) { + uint32_t val = 0; + for (int j = 0; j < len; j++) { + val |= (bits[j] << (len-1-j)); + } + return val; +} + +RS41::RS41() { +} + +/* RS41 reed solomon decoder, from dxlAPRS + */ +static int32_t reedsolomon41(byte buf[], uint32_t buf_len, uint32_t len2) +{ + uint32_t i; + int32_t res1; + /*tb1, */ + int32_t res; + char b1[256]; + char b[256]; + uint32_t eraspos[24]; + uint32_t tmp; + for (i = 0UL; i<=255UL; i++) { + b[i] = 0; + b1[i] = 0; + } /* end for */ + tmp = len2; + i = 0UL; + if (i<=tmp) for (;; i++) { + b[230UL-i] = buf[i*2UL+56UL]; + b1[230UL-i] = buf[i*2UL+57UL]; + if (i==tmp) break; + } /* end for */ + for (i = 0UL; i<=23UL; i++) { + b[254UL-i] = buf[i+8UL]; + b1[254UL-i] = buf[i+32UL]; + } /* end for */ + res = decodersc(b, eraspos, 0); + res1 = decodersc(b1, eraspos, 0); + if (res>0L && res<=12L) { + tmp = len2; + i = 0UL; + if (i<=tmp) for (;; i++) { + buf[i*2UL+56UL] = b[230UL-i]; + if (i==tmp) break; + } /* end for */ + for (i = 0UL; i<=23UL; i++) { + buf[i+8UL] = b[254UL-i]; + } /* end for */ + } + if (res1>0L && res1<=12L) { + tmp = len2; + i = 0UL; + if (i<=tmp) for (;; i++) { + buf[i*2UL+57UL] = b1[230UL-i]; + if (i==tmp) break; + } /* end for */ + for (i = 0UL; i<=23UL; i++) { + buf[i+32UL] = b1[254UL-i]; + } /* end for */ + } + if (res<0L || res1<0L) return -1L; + else return res+res1; + return 0; +} /* end reedsolomon41() */ + + + + +static char crcrs(const byte frame[], uint32_t frame_len, + int32_t from, int32_t to) +{ + uint16_t crc; + int32_t i; + int32_t tmp; + crc = 0xFFFFU; + tmp = to-3L; + i = from; + if (i<=tmp) for (;; i++) { + crc = X2C_LSH(crc,16,-8)^CRCTAB[(uint32_t)((crc^(uint16_t)(uint8_t)frame[i])&0xFFU)]; + if (i==tmp) break; + } /* end for */ + return frame[to-1L]==(char)crc && frame[to-2L]==(char)X2C_LSH(crc, + 16,-8); +} /* end crcrs() */ + +static int32_t getint32(const byte frame[], uint32_t frame_len, + uint32_t p) +{ + uint32_t n; + uint32_t i; + n = 0UL; + for (i = 3UL;; i--) { + n = n*256UL+(uint32_t)(uint8_t)frame[p+i]; + if (i==0UL) break; + } /* end for */ + return (int32_t)n; +} /* end getint32() */ + + +static uint32_t getcard16(const byte frame[], uint32_t frame_len, + uint32_t p) +{ + return (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t) + frame[p+1UL]; +} /* end getcard16() */ + + +static int32_t getint16(const byte frame[], uint32_t frame_len, + uint32_t p) +{ + uint32_t n; + n = (uint32_t)(uint8_t)frame[p]+256UL*(uint32_t)(uint8_t) + frame[p+1UL]; + if (n>=32768UL) return (int32_t)(n-65536UL); + return (int32_t)n; +} /* end getint16() */ + +static void wgs84r(double x, double y, double z, + double * lat, double * long0, + double * heig) +{ + double sl; + double ct; + double st; + double t; + double rh; + double xh; + double h; + h = x*x+y*y; + if (h>0.0) { + rh = (double)sqrt((float)h); + xh = x+rh; + *long0 = atang2(xh, y)*2.0; + if (*long0>3.1415926535898) *long0 = *long0-6.2831853071796; + t = (double)atan((float)(X2C_DIVL(z*1.003364089821, + rh))); + st = (double)sin((float)t); + ct = (double)cos((float)t); + *lat = (double)atan((float) + (X2C_DIVL(z+4.2841311513312E+4*st*st*st, + rh-4.269767270718E+4*ct*ct*ct))); + sl = (double)sin((float)*lat); + *heig = X2C_DIVL(rh,(double)cos((float)*lat))-(double)(X2C_DIVR(6.378137E+6f, + sqrt((float)(1.0-6.6943799901413E-3*sl*sl)))); + } + else { + *lat = 0.0; + *long0 = 0.0; + *heig = 0.0; + } +/* lat:=atan(z/(rh*(1.0 - E2))); */ +/* heig:=sqrt(h + z*z) - EARTHA; */ +} /* end wgs84r() */ +static void posrs41(const byte b[], uint32_t b_len, uint32_t p) +{ + double dir; + double vu; + double ve; + double vn; + double vz; + double vy; + double vx; + double heig; + double long0; + double lat; + double z; + double y; + double x; + x = (double)getint32(b, b_len, p)*0.01; + y = (double)getint32(b, b_len, p+4UL)*0.01; + z = (double)getint32(b, b_len, p+8UL)*0.01; + wgs84r(x, y, z, &lat, &long0, &heig); + Serial.print(" "); + si.lat = (float)(X2C_DIVL(lat,1.7453292519943E-2)); + Serial.print(si.lat); + Serial.print(" "); + si.lon = (float)(X2C_DIVL(long0,1.7453292519943E-2)); + Serial.print(si.lon); + if (heig<1.E+5 && heig>(-1.E+5)) { + Serial.print(" "); + Serial.print((uint32_t)heig); + Serial.print("m"); + } + /*speed */ + vx = (double)getint16(b, b_len, p+12UL)*0.01; + vy = (double)getint16(b, b_len, p+14UL)*0.01; + vz = (double)getint16(b, b_len, p+16UL)*0.01; + vn = (-(vx*(double)sin((float)lat)*(double) + cos((float)long0))-vy*(double) + sin((float)lat)*(double)sin((float) + long0))+vz*(double)cos((float)lat); + ve = -(vx*(double)sin((float)long0))+vy*(double) + cos((float)long0); + vu = vx*(double)cos((float)lat)*(double) + cos((float)long0)+vy*(double) + cos((float)lat)*(double)sin((float) + long0)+vz*(double)sin((float)lat); + dir = X2C_DIVL(atang2(vn, ve),1.7453292519943E-2); + if (dir<0.0) dir = 360.0+dir; + Serial.print(" "); + si.hs = sqrt((float)(vn*vn+ve*ve))*3.6f; + Serial.print(si.hs); + Serial.print("km/h "); + Serial.print(dir); + Serial.print("deg "); + Serial.print((float)vu); + si.vs = vu; + Serial.print("m/s "); + Serial.print(getcard16(b, b_len, p+18UL)&255UL); + Serial.print("Sats"); + si.hei = heig; + si.validPos = true; +} /* end posrs41() */ + + + + +void RS41::decode41(byte *data, int MAXLEN) +{ + char buf[128]; + + int32_t corr = reedsolomon41(data, 560, 131); // try short frame first + if(corr<0) { + corr = reedsolomon41(data, 560, 230); // try long frame + } + Serial.print("RS result:"); + Serial.print(corr); + Serial.println(); + int p = 57; // 8 byte header, 48 byte RS + while(pMAXLEN) break; + +#if 1 + // DEBUG OUTPUT + Serial.print("@"); + Serial.print(p-2); + Serial.print(": ID:"); + Serial.print(typ, HEX); + Serial.print(", len="); + Serial.print(len); + Serial.print(": "); + for(int i=0; i>4]; +} + + +static uint8_t scramble[64] = {150U,131U,62U,81U,177U,73U,8U,152U,50U,5U,89U, + 14U,249U,68U,198U,38U,33U,96U,194U,234U,121U,93U,109U,161U, + 84U,105U,71U,12U,220U,232U,92U,241U,247U,118U,130U,127U,7U, + 153U,162U,44U,147U,124U,48U,99U,245U,16U,46U,97U,208U,188U, + 180U,182U,6U,170U,244U,35U,120U,110U,59U,174U,191U,123U,76U, + 193U}; + +static byte data[800]; + +#define MAXLEN (320) +int RS41::receiveFrame() { + //sx1278.setPayloadLength(518-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header) + sx1278.setPayloadLength(MAXLEN-8); // Expect 320-8 bytes or 518-8 bytes (8 byte header) + + sx1278.writeRegister(REG_OP_MODE, FSK_RX_MODE); + int e = sx1278.receivePacketTimeout(3000, data+8); + if(e) { Serial.println("TIMEOUT"); return 1; } //if timeout... return 1 + + for(int i=0; i +#include +#include +#ifndef inttypes_h + #include +#endif + +/* Main class */ +class RS41 +{ +private: + uint32_t bits2val(const uint8_t *bits, int len); + void printRaw(uint8_t *data, int len); + int bitsToBytes(uint8_t *bits, uint8_t *bytes, int len); + void decode41(byte *data, int maxlen); + +#define B 8 +#define S 4 + uint8_t hamming_conf[ 7*B]; // 7*8=56 + uint8_t hamming_dat1[13*B]; // 13*8=104 + uint8_t hamming_dat2[13*B]; + + uint8_t block_conf[ 7*S]; // 7*4=28 + uint8_t block_dat1[13*S]; // 13*4=52 + uint8_t block_dat2[13*S]; + + uint8_t H[4][8] = // extended Hamming(8,4) particy check matrix + {{ 0, 1, 1, 1, 1, 0, 0, 0}, + { 1, 0, 1, 1, 0, 1, 0, 0}, + { 1, 1, 0, 1, 0, 0, 1, 0}, + { 1, 1, 1, 0, 0, 0, 0, 1}}; + uint8_t He[8] = { 0x7, 0xB, 0xD, 0xE, 0x8, 0x4, 0x2, 0x1}; // Spalten von H: + // 1-bit-error-Syndrome + +public: + RS41(); + int setup(); + int setFrequency(float frequency); + int receiveFrame(); + + int use_ecc = 1; +}; + +extern RS41 rs41; + +#endif diff --git a/libraries/SondeLib/Sonde.cpp b/libraries/SondeLib/Sonde.cpp new file mode 100644 index 0000000..67612ba --- /dev/null +++ b/libraries/SondeLib/Sonde.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include "Sonde.h" + +extern U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8; + +SondeInfo si = { "RS41", 403.450, "P1234567", true, 48.1234, 14.9876, 543, 3.97, -0.5, true, 120 }; + +static unsigned char kmh_tiles[] U8X8_PROGMEM = { + 0x1F, 0x04, 0x0A, 0x11, 0x00, 0x1F, 0x02, 0x04, 0x42, 0x3F, 0x10, 0x08, 0xFC, 0x22, 0x20, 0xF8 + }; +static unsigned char ms_tiles[] U8X8_PROGMEM = { + 0x1F, 0x02, 0x04, 0x02, 0x1F, 0x40, 0x20, 0x10, 0x08, 0x04, 0x12, 0xA4, 0xA4, 0xA4, 0x40, 0x00 + }; + +void Sonde::updateDisplayPos() { + char buf[16]; + u8x8.setFont(u8x8_font_7x14_1x2_r); + if(si.validPos) { + snprintf(buf, 16, "%2.5f", si.lat); + u8x8.drawString(0,2,buf); + snprintf(buf, 16, "%2.5f", si.lon); + u8x8.drawString(0,4,buf); + } else { + u8x8.drawString(0,2," "); + u8x8.drawString(0,4," "); + } +} + +void Sonde::updateDisplayPos2() { + char buf[16]; + u8x8.setFont(u8x8_font_chroma48medium8_r); + if(!si.validPos) { + u8x8.drawString(10,2," "); + u8x8.drawString(10,3," "); + u8x8.drawString(10,4," "); + return; + } + snprintf(buf, 16, si.hei>999?"%5.0fm":"%3.1fm", si.hei); + u8x8.drawString((10+6-strlen(buf)),2,buf); + snprintf(buf, 16, si.hs>99?"%3.0f":"%2.1f", si.hs); + u8x8.drawString((10+4-strlen(buf)),3,buf); + snprintf(buf, 16, "%+2.1f", si.vs); + u8x8.drawString((10+4-strlen(buf)),4,buf); + u8x8.drawTile(14,3,2,kmh_tiles); + u8x8.drawTile(14,4,2,ms_tiles); +} + +void Sonde::updateDisplayID() { + u8x8.setFont(u8x8_font_chroma48medium8_r); + if(si.validID) { + u8x8.drawString(0,1, si.id); + } else { + u8x8.drawString(0,1, "nnnnnnnn "); + } +} + +void Sonde::updateDisplayRSSI() { + char buf[16]; + u8x8.setFont(u8x8_font_7x14_1x2_r); + snprintf(buf, 16, "%ddB ", si.rssi); + u8x8.drawString(0,6,buf); +} + +void Sonde::updateDisplayRXConfig() { + char buf[16]; + u8x8.setFont(u8x8_font_chroma48medium8_r); + u8x8.drawString(0,0, si.type); + snprintf(buf, 16, "%3.3f MHz", si.freq); + u8x8.drawString(5,0, buf); + +} + +void Sonde::updateDisplay() +{ + char buf[16]; + updateDisplayRXConfig(); + updateDisplayID(); + updateDisplayPos(); + updateDisplayPos2(); + updateDisplayRSSI(); +} + +Sonde sonde = Sonde(); diff --git a/libraries/SondeLib/Sonde.h b/libraries/SondeLib/Sonde.h new file mode 100644 index 0000000..18a9510 --- /dev/null +++ b/libraries/SondeLib/Sonde.h @@ -0,0 +1,41 @@ + + +#ifndef Sonde_h +#define Sonde_H + +typedef struct st_sondeinfo { + // receiver configuration + char type[5]; + float freq; + // decoded ID + char id[10]; + bool validID; + // decoded position + float lat; + float lon; + float hei; + float vs; + float hs; + bool validPos; + // RSSI from receiver + int rssi; +} SondeInfo; + +extern SondeInfo si; + +class Sonde +{ +private: +public: + void updateDisplayPos(); + void updateDisplayPos2(); + void updateDisplayID(); + void updateDisplayRSSI(); + void updateDisplayRXConfig(); + void updateDisplay(); +}; + +extern Sonde sonde; + +#endif + diff --git a/libraries/SondeLib/rsc.cpp b/libraries/SondeLib/rsc.cpp new file mode 100644 index 0000000..2b87a26 --- /dev/null +++ b/libraries/SondeLib/rsc.cpp @@ -0,0 +1,32 @@ +/* + * dxlAPRS toolchain + * + * Copyright (C) Christian Rabler + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef inttypes_h + #include +#endif + +#define N 255 +#define R 24 +#define K (N-R) + +void *init_rs_char(int symsize,int gfpoly,int fcr,int prim,int nroots,int pad); +int decode_rs_char(void *arg, + unsigned char *data, int *eras_pos, int no_eras); + +void *rs; + +void initrsc() +{ + rs = init_rs_char( 8, 0x11d, 0, 1, R, 0); +} + + +int decodersc(char *data, uint32_t *eras_pos, uint32_t no_eras) +{ + return decode_rs_char(rs, (unsigned char *)data, (int *)eras_pos, no_eras); +} diff --git a/libraries/SondeLib/rsc.h b/libraries/SondeLib/rsc.h new file mode 100644 index 0000000..a73575c --- /dev/null +++ b/libraries/SondeLib/rsc.h @@ -0,0 +1,17 @@ +/* + * dxlAPRS toolchain + * + * Copyright (C) Christian Rabler + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef rsc_H_ +#define rsc_H_ + + +long decodersc(char [], uint32_t [], uint32_t); + +void initrsc(void); + + +#endif /* rsc_H_ */ diff --git a/libraries/SondeLib/rsc_decode.cpp b/libraries/SondeLib/rsc_decode.cpp new file mode 100644 index 0000000..eb102b4 --- /dev/null +++ b/libraries/SondeLib/rsc_decode.cpp @@ -0,0 +1,436 @@ +/* + * Copyright 2016 Hannes Schmelzer, OE5HPM + * doing several cleanups and architecture changes, no functional change yet + * + * General purpose Reed-Solomon decoder for 8-bit symbols or less + * Copyright 2003 Phil Karn, KA9Q + * May be used under the terms of the GNU Lesser General Public License (LGPL) + * + * The guts of the Reed-Solomon decoder, meant to be #included + * into a function body with the following typedefs, macros and variables supplied + * according to the code parameters: + + * data_t - a typedef for the data symbol + * data_t data[] - array of rs->nn data and parity symbols to be corrected in place + * retval - an integer lvalue into which the decoder's return code is written + * NROOTS - the number of roots in the RS code generator polynomial, + * which is the same as the number of parity symbols in a block. + Integer variable or literal. + * rs->nn - the total number of symbols in a RS block. Integer variable or literal. + * rs->pad - the number of pad symbols in a block. Integer variable or literal. + * rs->alpha_to - The address of an array of rs->nn elements to convert Galois field + * elements in index (log) form to polynomial form. Read only. + * rs->index_of - The address of an array of rs->nn elements to convert Galois field + * elements in polynomial form to index (log) form. Read only. + * MODNN - a function to reduce its argument modulo rs->nn. May be inline or a macro. + * rs->fcr - An integer literal or variable specifying the first consecutive root of the + * Reed-Solomon generator polynomial. Integer variable or literal. + * rs->prim - The primitive root of the generator poly. Integer variable or literal. + * DEBUG - If set to 1 or more, do various internal consistency checking. Leave this + * undefined for production code + + * The memset(), memmove(), and memcpy() functions are used. The appropriate header + * file declaring these functions (usually ) must be included by the calling + * program. + */ +#include +#include +#include + +struct rs { + unsigned int magic; /* struct magic */ + int mm; /* Bits per symbol */ + int nn; /* Symbols per block (= (1<= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} + +#define MODNN(x) modnn(rs, x) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAGIC 0xABCD6722 + +void free_rs_char(void *arg) +{ + struct rs *rs = (struct rs *)arg; + + if (rs == NULL) + return; + if (rs->magic != MAGIC) + return; + + if (rs->alpha_to != NULL) + free(rs->alpha_to); + if (rs->index_of != NULL) + free(rs->index_of); + if (rs->genpoly != NULL) + free(rs->genpoly); + free(rs); +} + +/* Initialize a Reed-Solomon codec + * symsize = symbol size, bits + * gfpoly = Field generator polynomial coefficients + * fcr = first root of RS code generator polynomial, index form + * prim = primitive element to generate polynomial roots + * nroots = RS code generator polynomial degree (number of roots) + * pad = padding bytes at front of shortened block + */ +void *init_rs_char(int symsize, int gfpoly, int fcr, int prim, + int nroots, int pad) +{ + struct rs *rs; + + int i, j, sr,root,iprim; + + /* Check parameter ranges */ + if (symsize < 0 || symsize > 8*sizeof(unsigned char)) + return NULL; + if (fcr < 0 || fcr >= (1<= (1<= (1<= ((1<magic = MAGIC; + + rs->mm = symsize; + rs->nn = (1<pad = pad; + + rs->alpha_to = (unsigned char *)malloc(sizeof(unsigned char)*(rs->nn+1)); + if (rs->alpha_to == NULL) { + free(rs); + return NULL; + } + rs->index_of = (unsigned char *)malloc(sizeof(unsigned char)*(rs->nn+1)); + if (rs->index_of == NULL) { + free(rs->alpha_to); + free(rs); + return NULL; + } + + /* Generate Galois field lookup tables */ + rs->index_of[0] = rs->nn; /* log(zero) = -inf */ + rs->alpha_to[rs->nn] = 0; /* alpha**-inf = 0 */ + sr = 1; + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if (sr & (1<nn; + } + if (sr != 1) { + /* field generator polynomial is not primitive! */ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + + /* Form RS code generator polynomial from its roots */ + rs->genpoly = (unsigned char *)malloc(sizeof(unsigned char)*(nroots+1)); + if(rs->genpoly == NULL) { + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + + /* Find prim-th root of 1, used in decoding */ + for (iprim = 1; (iprim % prim) != 0; iprim += rs->nn) + ; + rs->iprim = iprim / prim; + + rs->genpoly[0] = 1; + for (i = 0, root = fcr*prim; i < nroots; i++, root += prim) { + rs->genpoly[i+1] = 1; + + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--) { + if (rs->genpoly[j] != 0) + rs->genpoly[j] = rs->genpoly[j-1] ^ rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[j]] + root)]; + else + rs->genpoly[j] = rs->genpoly[j-1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = rs->alpha_to[modnn(rs,rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + + return rs; +} + +int decode_rs_char(void *arg, + unsigned char *data, int *eras_pos, int no_eras) +{ + struct rs *rs = (struct rs *)arg; + + if (rs == NULL) + return -1; + if (rs->magic != MAGIC) + return -1; + + int retval; + int deg_lambda, el, deg_omega; + int i, j, r,k; + + unsigned char u,q,tmp,num1,num2,den,discr_r; + unsigned char lambda[rs->nroots+1], s[rs->nroots]; /* Err+Eras Locator poly + * and syndrome poly */ + unsigned char b[rs->nroots+1], t[rs->nroots+1], omega[rs->nroots+1]; + unsigned char root[rs->nroots], reg[rs->nroots+1], loc[rs->nroots]; + int syn_error, count; + + /* form the syndromes; i.e., evaluate data(x) at roots of g(x) */ + for (i = 0; i < rs->nroots; i++) + s[i] = data[0]; + + for (j = 1; j < rs->nn-rs->pad; j++) { + for(i=0;inroots;i++) { + if(s[i] == 0) { + s[i] = data[j]; + } else { + s[i] = data[j] ^ rs->alpha_to[MODNN(rs->index_of[s[i]] + (rs->fcr+i)*rs->prim)]; + } + } + } + + /* Convert syndromes to index form, checking for nonzero condition */ + syn_error = 0; + for (i = 0; i < rs->nroots; i++) { + syn_error |= s[i]; + s[i] = rs->index_of[s[i]]; + } + + if (!syn_error) { + /* if syndrome is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + count = 0; + goto finish; + } + memset(&lambda[1], 0, rs->nroots*sizeof(lambda[0])); + lambda[0] = 1; + + if (no_eras > 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = rs->alpha_to[MODNN(rs->prim*(rs->nn-1-eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = MODNN(rs->prim*(rs->nn-1-eras_pos[i])); + for (j = i+1; j > 0; j--) { + tmp = rs->index_of[lambda[j - 1]]; + if(tmp != rs->nn) + lambda[j] ^= rs->alpha_to[MODNN(u + tmp)]; + } + } + + #if DEBUG >= 1 + /* Test code that verifies the erasure locator polynomial just constructed + Needed only for decoder debugging. */ + + /* find roots of the erasure location polynomial */ + for(i=1;i<=no_eras;i++) + reg[i] = rs->index_of[lambda[i]]; + + count = 0; + for (i = 1,k=rs->iprim-1; i <= rs->nn; i++,k = MODNN(k+rs->iprim)) { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != rs->nn) { + reg[j] = MODNN(reg[j] + j); + q ^= rs->alpha_to[reg[j]]; + } + if (q != 0) + continue; + /* store root and error location number indices */ + root[count] = i; + loc[count] = k; + count++; + } + if (count != no_eras) { + printf("count = %d no_eras = %d\n lambda(x) is WRONG\n",count,no_eras); + count = -1; + goto finish; + } + #if DEBUG >= 2 + printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + printf("%d ", loc[i]); + printf("\n"); + #endif + #endif + } + for (i = 0; i < rs->nroots+1; i++) + b[i] = rs->index_of[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= rs->nroots) { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++) { + if ((lambda[i] != 0) && (s[r-i-1] != rs->nn)) { + discr_r ^= rs->alpha_to[MODNN(rs->index_of[lambda[i]] + s[r-i-1])]; + } + } + discr_r = rs->index_of[discr_r]; /* Index form */ + if (discr_r == rs->nn) { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1],b,rs->nroots*sizeof(b[0])); + b[0] = rs->nn; + } else { + /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0 ; i < rs->nroots; i++) { + if(b[i] != rs->nn) + t[i+1] = lambda[i+1] ^ rs->alpha_to[MODNN(discr_r + b[i])]; + else + t[i+1] = lambda[i+1]; + } + if (2 * el <= r + no_eras - 1) { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= rs->nroots; i++) + b[i] = (lambda[i] == 0) ? rs->nn : MODNN(rs->index_of[lambda[i]] - discr_r + rs->nn); + } else { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1],b,rs->nroots*sizeof(b[0])); + b[0] = rs->nn; + } + memcpy(lambda,t,(rs->nroots+1)*sizeof(t[0])); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for (i = 0;i < rs->nroots+1; i++){ + lambda[i] = rs->index_of[lambda[i]]; + if(lambda[i] != rs->nn) + deg_lambda = i; + } + /* Find roots of the error+erasure locator polynomial by Chien search */ + memcpy(®[1], &lambda[1], rs->nroots*sizeof(reg[0])); + count = 0; /* Number of roots of lambda(x) */ + for (i = 1,k=rs->iprim-1; i <= rs->nn; i++,k = MODNN(k+rs->iprim)) { + q = 1; /* lambda[0] is always 0 */ + for (j = deg_lambda; j > 0; j--) { + if (reg[j] != rs->nn) { + reg[j] = MODNN(reg[j] + j); + q ^= rs->alpha_to[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ + #if DEBUG>=2 + printf("count %d root %d loc %d\n",count,i,k); + #endif + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if(++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**rs->nroots). in index form. Also find deg(omega). + */ + deg_omega = deg_lambda-1; + for (i = 0; i <= deg_omega;i++) { + tmp = 0; + for (j = i; j >= 0; j--) { + if ((s[i - j] != rs->nn) && (lambda[j] != rs->nn)) + tmp ^= rs->alpha_to[MODNN(s[i - j] + lambda[j])]; + } + omega[i] = rs->index_of[tmp]; + } + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(rs->fcr-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count-1; j >=0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != rs->nn) + num1 ^= rs->alpha_to[MODNN(omega[i] + i * root[j])]; + } + num2 = rs->alpha_to[MODNN(root[j] * (rs->fcr - 1) + rs->nn)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = MIN(deg_lambda, rs->nroots-1) & ~1; i >= 0; i -=2) { + if(lambda[i+1] != rs->nn) + den ^= rs->alpha_to[MODNN(lambda[i+1] + i * root[j])]; + } + #if DEBUG >= 1 + if (den == 0) { + printf("\n ERROR: denominator = 0\n"); + count = -1; + goto finish; + } + #endif + /* Apply error to data */ + if (num1 != 0 && loc[j] >= rs->pad) { + data[loc[j]-rs->pad] ^= rs->alpha_to[MODNN(rs->index_of[num1] + rs->index_of[num2] + rs->nn - rs->index_of[den])]; + } + } + finish: + if(eras_pos != NULL) { + for (i = 0; i < count; i++) + eras_pos[i] = loc[i]; + } + retval = count; + + return retval; +}