diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d040499 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.o +*.d +.vscode +./*.blacklist +./*.whitelist +./*.interlink +./*.terminal +./*.service +configure.mk +configure.h +configure.sql +reflector.cfg +wiresx/configure.php +src/ulxd diff --git a/README.md b/README.md new file mode 100644 index 0000000..60a9f18 --- /dev/null +++ b/README.md @@ -0,0 +1,159 @@ +# XLXD (or XRFD) + +The XLX Multiprotocol Gateway Reflector Server is part of the software system for a Digital Voice Network. The sources are published under GPL Licenses. + +## Introduction + +This will build **either** a new kind of XLX reflector **or** a tri-mode XRF reflector. This build support *dual-stack* operation, so the server on which it's running, must have both an IPv4 and IPv6 routable address if you are going to configure a dual-stack reflector. This XLX is different from the original because it can support out-going DExtra links, by adding a new DExtra Peer type *and* it has many changes designed to increase reliability and stability. The XRF reflector built from this source supports inbound DExtra, DPlus **and DCS connections**. Like XLX, XRF also supports out-going DExtra linking. Please note that for now, only one DExtra peer link per node is supported. + +This is an improved version of the multi-protocol Reflector. Nearly all std::vector containers have been replaced with std::list containers. This is a better choice for any collection where it is common to delete elements that are not at the end of the collection. Also in this package, no classes are derived from any standard containers. Because standard containers don't have a virtual destructor, derriving from them is ill-advised and while the original XLX server worked using such derivations, it represents a possible serious problem when considering future development. Also, the clean-up routines designed to be executed when shutting down were unreachable as designed and this has been fixed. Servers built on this code will shutdown gracefully in a few seconds. In original version, long sleep times in certain threads were preventing a polite systemd shutdown and this has been fixed. The C++ warning flag, -W has been turned on and a significant number of warning have been fixed. For thread management, the standard thread (std::thread) library calls have been replaced with std::future. Futures don't need static functions and this elimintates the need for passing *this* pointers to the thead. All heap memory is now managed with smart pointers, so all calls to *delete* have been removed. + +Only systemd-based operating systems are supported. Debian or Ubuntu is recommended. If you want to install this on a non-systemd based OS, you are on your own. Also, by default, ambed and xlxd or xrfd are built without gdb support. Finally, this repository is designed so that you don't have to modify any file in the repository when you build your system. Any file you need to modify to properly configure your reflector will be a file you copy from you locally cloned repo. This makes it easier to update the source code when this repository is updated. Follow the instructions below to build your transcoding XLX reflector or tri-mode XRF reflector. + +## Usage + +The packages which are described in this document are designed to install server software which is used for the D-Star network infrastructure. It requires a 24/7 internet connection which can support 20 voice streams or more to connect repeaters and hot-spot dongles! + +- The server can build a reflector that support IPv4, IPv6 or both (dual stack). +- The public IP addresses should have a DNS record which must be published in the common host files. + +## Installation + +Below are instructions for building either an XLX or XRF reflector. If you are planning on an XLX reflector without a transcoder, you can help your users by naming modules with names that attract D-Star or non-D-Star client. You name modules in the config.inc.php file mentioned below. + +### After a clean installation of Debian make sure to run update and upgrade + +```bash +sudo apt update +sudo apt upgrade +``` + +### Required packages (some of these will probably already be installed) + +```bash +sudo apt install git +sudo apt install apache2 php5 +sudo apt install build-essential +sudo apt install g++ +# the following is only needed for XLX, not for XRF +sudo apt install libmariadb-dev-compat +# the following is needed if you plan on supporting local YSF frequency registration database +sudo apt install php-mysql mariadb-server mariadb-client +``` + +### Download the repository and enter the directory + +```bash +git clone https://github.com/n7tae/new-xlxd.git +cd new-xlxd +``` + +### Create and edit your blacklist, whitelist and linking files + +If you are building an XLX reflector: + +```bash +cp ../config/xlxd.blacklist . +cp ../config/xlxd.whitelist . +cp ../config/xlxd.interlink . +cp ../config/xlxd.terminal . +``` + +If you are building an XRF reflector (please note the name changes, especially for the interlink file): + +```bash +cp ../config/xlxd.blacklist xrfd.blacklist +cp ../config/xlxd.whitelist xrfd.whitelist +cp ../config/xlxd.interlink xrfd.interlink +cp ../config/xlxd.terminal xrfd.terminal +``` + +If you are not going to support G3 linking, you don't need to copy the .terminal file. Use your favorite editor to modify each of these files. If you want a totally open network, the blacklist and whitelist files are ready to go. The blacklist determine which callsigns can't use the reflector. The whitelist determines which callsigns can use the reflector. The interlink file sets up the XLX<--->XLX inter-linking and/or out-going XRF peer linking. + +### Configuring your reflector + +Configuring, compiling and maintaining your reflector build is easy! Start the configuration script in the base directory of you cloned repo: + +```bash +./rconfig +``` + +There are only a few things that need to be specified. Most important are, the reflector callsign and the IP addresses for the IPv4 and IPv6 listen ports and a transcoder port, if there is a transcoder. Dual-stack operation is enabled by specifying both an IPv4 and IPv6 address. IPv4-only single stack can be specified by leaving the IPv6 address set to `none`. It's even possible to operate in an IPv6-only configuration by leaving the IPv4 address to the default `none`. You can override the ip addresses for any of the supported protocol and this is done in a sub-menu. This would allow you to install other Ham-related services that might use the same ports, like a Smart Group Server. + +Obviously the transcoder is only specified for an XLX reflector. If your reflector is configured with a transcoder, you can specify which configured modules will be transcoded. If you are building an XLX system with a transcoder, you can also specify which channels get transcoder support. There are also true/false flags to prevent G3 support and so that you can build executables that will support gdb debugging. + +You can support your own YSF frequency database. This is very useful for hot-spots that use YSF linking. These linked hot-spots can then use the *WiresX* command on their radios to be able to connect to any configured XLX module. Users can register their TX and RX frequency (typically the same for most hot-spot configurations) on http:<*xlx url*>/wiresx/login.php. Once their hot-spot is registered, XLX will return the correct frequency for their hot-spot when a *WiresX* command is sent to the reflector. You'll need to enable YSF auto-linking, specify a default module and define a database name, user and user password. When you write you XLX configuration, a database **configure.sql** script will be built to not only create the database and database user, but also the table for the hot-spot frequency data. + +Be sure to write out the configuration files and look over the up to seven different configration files that are created. The first file, reflector.cfg is the memory file for rconfig so that if you start that script again, it will remember how you left things. There are one or two `.h` files for the reflector and ambed and there are one or two `.mk` files for the reflector and ambed makefiles. You should **not** modify these files by hand unless you really know exactly how they work. The rconfig script will not start if it detects that an XLX or XRF server is already running. You can override this behavior in expert mode: `./rconfig expert`. If you do change the configuration after you have already compiled the code, it is safest if you clean the repo and then recompile. + +### Compling and installing your system + +After you have written your configutation files, you can build and install your system: + +```bash +./radmin +``` + +Use this command to compile and install your system. It can also be used to uninstall your system. It will use the information in reflector.cfg to perform each task. This radmin menu can also perform other tasks like restarting the reflector or transcoder process. It can also be used to update the software, if the system is uninstalled. + +### Stoping and starting the services manually + +```bash +sudo systemctl stop xlxd # (or xrfd) +sudo systemctl stop ambed +``` + +You can start each component by replacing `stop` with `start`, or you can restart each by using `restart`. + +### Copy dashboard to /var/www + +There are two supplied, one for XRF systems and one for XLX systems. + +```bash +sudo cp -r ~/xlxd/dashboard.xlx /var/www/db # or dashboard.xrf +``` + +Please note that your www root directory might be some place else. There is one file that needs configuration. Edit the copied files, not the ones from the repository: + +- **pgs/config.inc.php** - At a minimum set your email address, country and comment. **Do not** enable the calling home feature if you built an XRF reflector. This feature is for **XLX systems only**. + +If you have configured support of hot-spot frequency registation, recursively copy the **wiresx** directory where the index.php file is for your dashboard. Also from the build directory, create the database and database user and hot-spot frequency table: + +```bash +sudo mysql < configure.sql +``` + +## Updating xlxd and ambed + +Updating can be performed entirely in the radmin script, but just in case there is a new version of the radmin script, you can start first with a simple `git pull`. If any .h or .cpp fiiles have updates, you can then start radmin and do a clean and compile and then uninstall and install: `cl, co, us, is`. Follow that with a `rl` to watch the reflector log, or an `rt` to watch the transcoder while it comes up. + +If rconfig was updated with the `git pull`, it might be wise to run it first to see if there have been any new options added to the code base. If so, be sure to write out the new configuration files before exiting rconfig. THen you can rebuild and reinstall your reflector. + +If you are change any configuration after your reflector has been compiled, be sure to do a clean/compile/uninstall/reinstall to sync your system to the new configuration. + +## Firewall settings + +XLX Server requires the following ports to be open and forwarded properly for in- and outgoing network traffic: + +- TCP port 80 (http) optional TCP port 443 (https) +- UDP port 10002 (XLX interlink) +- UDP port 42000 (YSF protocol) +- UDP port 30001 (DExtra protocol) +- UPD port 20001 (DPlus protocol) +- UDP port 30051 (DCS protocol) +- UDP port 8880 (DMR+ DMO mode) +- UDP port 62030 (MMDVM protocol) +- UDP port 10100 (AMBE controller port) +- UDP port 10101 - 10199 (AMBE transcoding port) # only needed if your transcoder and reflector aren't running on the same domain. +- UDP port 12345 - 12346 (Icom Terminal presence and request port) +- UDP port 40000 (Icom Terminal dv port) + +## YSF Master Server + +Pay attention, the XLX Server acts as an YSF Master, which provides 26 wires-x rooms. +It has nothing to do with the regular YSFReflector network, hence you don’t need to register your XLX at ysfreflector.de ! + +## Copyright + +- Copyright © 2016 Jean-Luc Deltombe LX3JL and Luc Engelmann LX1IQ +- Copyright © 2020 Thomas A. Early N7TAE diff --git a/src/BMClient.cpp b/src/BMClient.cpp new file mode 100644 index 0000000..843c2c4 --- /dev/null +++ b/src/BMClient.cpp @@ -0,0 +1,53 @@ +// +// cbmclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/01/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "BMClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CBmClient::CBmClient() +{ +} + +CBmClient::CBmClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CBmClient::CBmClient(const CBmClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CBmClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} diff --git a/src/BMClient.h b/src/BMClient.h new file mode 100644 index 0000000..2c16f4c --- /dev/null +++ b/src/BMClient.h @@ -0,0 +1,54 @@ +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. + +// ulxd -- The ultimate Reflector +// Copyright © 2021 Thomas A. Early N7TAE +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "Client.h" +#include "ULXClient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CBmClient : public CClient +{ +public: + // constructors + CBmClient(); + CBmClient(const CCallsign &, const CIp &, char = ' '); + CBmClient(const CBmClient &); + + // destructor + virtual ~CBmClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + int GetProtocolRevision(void) const { return XLX_PROTOCOL_REVISION_2; } + const char *GetProtocolName(void) const { return "XLX"; } + int GetCodec(void) const { return CODEC_AMBE2PLUS; } + bool IsPeer(void) const { return true; } + + // status + bool IsAlive(void) const; + + // reporting + void WriteXml(std::ofstream &) {} +}; diff --git a/src/BMPeer.cpp b/src/BMPeer.cpp new file mode 100644 index 0000000..1bd456c --- /dev/null +++ b/src/BMPeer.cpp @@ -0,0 +1,67 @@ +// +// cbmpeer.cpp +// xlxd +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "BMPeer.h" +#include "BMClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CBmPeer::CBmPeer() +{ +} + +CBmPeer::CBmPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) + : CPeer(callsign, ip, modules, version) +{ + std::cout << "Adding BM peer" << std::endl; + + // and construct all xlx clients + for ( unsigned i = 0; i < ::strlen(modules); i++ ) + { + // create and append to vector + m_Clients.push_back(std::make_shared(callsign, ip, modules[i])); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CBmPeer::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CBmPeer::GetProtocolRevision(const CVersion &version) +{ + return XLX_PROTOCOL_REVISION_2; +} diff --git a/src/BMPeer.h b/src/BMPeer.h new file mode 100644 index 0000000..1170599 --- /dev/null +++ b/src/BMPeer.h @@ -0,0 +1,59 @@ +// +// BMPeer.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/01/2017. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cbmpeer_h +#define cbmpeer_h + + +#include "Peer.h" +#include "BMClient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CBmPeer : public CPeer +{ +public: + // constructors + CBmPeer(); + CBmPeer(const CCallsign &, const CIp &, const char *, const CVersion &); + CBmPeer(const CBmPeer &) = delete; + + // status + bool IsAlive(void) const; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + const char *GetProtocolName(void) const { return "XLX"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cbmpeer_h */ diff --git a/src/Buffer.cpp b/src/Buffer.cpp new file mode 100644 index 0000000..d5fef04 --- /dev/null +++ b/src/Buffer.cpp @@ -0,0 +1,267 @@ +// +// cbuffer.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Buffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CBuffer::CBuffer(uint8_t *buffer, int len) +{ + m_data.resize(len); + ::memcpy(m_data.data(), buffer, len); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// set + +void CBuffer::Set(uint8_t *buffer, int len) +{ + m_data.resize(len); + ::memcpy(m_data.data(), buffer, len); +} + +void CBuffer::Set(const char *sz) +{ + m_data.resize(::strlen(sz)+1); + ::strcpy((char *)m_data.data(), sz); +} + +void CBuffer::Append(const uint8_t *buffer, int len) +{ + int n = (int)m_data.size(); + m_data.resize(n+len); + ::memcpy(&(m_data.data()[n]), buffer, len); +} + +void CBuffer::Append(uint8_t ui, int len) +{ + int n = (int)m_data.size(); + m_data.resize(n+len); + ::memset(&(m_data.data()[n]), ui, len); +} + +void CBuffer::Append(uint8_t ui) +{ + int n = (int)m_data.size(); + m_data.resize(n+sizeof(uint8_t)); + ::memcpy(&(m_data.data()[n]), &ui, sizeof(uint8_t)); +} + +void CBuffer::Append(uint16_t ui) +{ + int n = (int)m_data.size(); + m_data.resize(n+sizeof(uint16_t)); + ::memcpy(&(m_data.data()[n]), &ui, sizeof(uint16_t)); +} + +void CBuffer::Append(uint32_t ui) +{ + int n = (int)m_data.size(); + m_data.resize(n+sizeof(uint32_t)); + ::memcpy(&(m_data.data()[n]), &ui, sizeof(uint32_t)); +} + +void CBuffer::Append(const char *sz) +{ + Append((uint8_t *)sz, (int)strlen(sz)); + Append((uint8_t)0x00); +} + +void CBuffer::ReplaceAt(int i, uint8_t ui) +{ + if ( m_data.size() < (i+sizeof(uint8_t)) ) + { + m_data.resize(i+sizeof(uint8_t)); + } + *(uint8_t *)(&(m_data.data()[i])) = ui; +} + +void CBuffer::ReplaceAt(int i, uint16_t ui) +{ + if ( m_data.size() < (i+sizeof(uint16_t)) ) + { + m_data.resize(i+sizeof(uint16_t)); + } + *(uint16_t *)(&(m_data.data()[i])) = ui; +} + +void CBuffer::ReplaceAt(int i, uint32_t ui) +{ + if ( m_data.size() < (i+sizeof(uint32_t)) ) + { + m_data.resize(i+sizeof(uint32_t)); + } + *(uint32_t *)(&(m_data.data()[i])) = ui; +} + +void CBuffer::ReplaceAt(int i, const uint8_t *ptr, int len) +{ + if ( m_data.size() < unsigned(i+len) ) + { + m_data.resize(i+len); + } + ::memcpy(&(m_data.data()[i]), ptr, len); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +int CBuffer::Compare(uint8_t *buffer, int len) const +{ + int result = -1; + if ( m_data.size() >= unsigned(len) ) + { + result = ::memcmp(m_data.data(), buffer, len); + } + return result; +} + +int CBuffer::Compare(uint8_t *buffer, int off, int len) const +{ + int result = -1; + if ( m_data.size() >= unsigned(off+len) ) + { + result = ::memcmp(&(m_data.data()[off]), buffer, len); + } + return result; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operator + +bool CBuffer::operator ==(const CBuffer &Buffer) const +{ + if ( m_data.size() == Buffer.m_data.size() ) + { + return (::memcmp((const char *)m_data.data(), (const char *)Buffer.m_data.data(), m_data.size()) == 0); + } + return false; +} + +bool CBuffer::operator ==(const char *sz) const +{ + if ( m_data.size() == ::strlen(sz) ) + { + return (::memcmp((const char *)m_data.data(), sz, m_data.size()) == 0); + } + return false; +} + +CBuffer::operator const char *() const +{ + return (const char *)m_data.data(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// debug + +void CBuffer::DebugDump(std::ofstream &debugout) const +{ + // dump an hex line + for ( unsigned int i = 0; i < m_data.size(); i++ ) + { + char sz[16]; + //sprintf(sz, "%02X", m_data.data()[i]); + sprintf(sz, "0x%02X", m_data.data()[i]); + debugout << sz; + if ( i == m_data.size()-1 ) + { + debugout << std::endl; + } + else + { + debugout << ','; + } + } +} + +void CBuffer::DebugDumpAscii(std::ofstream &debugout) const +{ + // dump an ascii line + for ( unsigned int i = 0; i < m_data.size(); i++ ) + { + char c = m_data.data()[i]; + if ( isascii(c) ) + { + debugout << c; + } + else + { + debugout << '.'; + } + if ( i == m_data.size()-1 ) + { + debugout << std::endl; + } + } +} + +void CBuffer::Dump(const std::string &title) +{ + std::cout << title << ":" << std::endl; + + unsigned int offset = 0U; + unsigned int length = m_data.size(); + + while (length > 0U) { + std::string output; + + unsigned int bytes = (length > 16U) ? 16U : length; + + char temp[10U]; + for (unsigned i = 0U; i < bytes; i++) { + ::sprintf(temp, "%02X ", m_data[offset + i]); + output += temp; + } + + for (unsigned int i = bytes; i < 16U; i++) + output += " "; + + output += " *"; + + for (unsigned i = 0U; i < bytes; i++) { + unsigned char c = m_data[offset + i]; + + if (::isprint(c)) + output += c; + else + output += '.'; + } + + output += '*'; + + ::sprintf(temp, "%04X: ", offset); + std::cout << temp << output << std::endl; + + offset += 16U; + + if (length >= 16U) + length -= 16U; + else + length = 0U; + } +} diff --git a/src/Buffer.h b/src/Buffer.h new file mode 100644 index 0000000..c9a6f5c --- /dev/null +++ b/src/Buffer.h @@ -0,0 +1,85 @@ +// +// Buffer.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cbuffer_h +#define cbuffer_h + +#include +#include +#include "Main.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CBuffer +{ +public: + CBuffer() {} + CBuffer(uint8_t *, int); + + // destructor + virtual ~CBuffer() {}; + + // set + void Set(uint8_t *, int); + void Set(const char *); + void Append(const uint8_t *, int); + void Append(uint8_t, int); + void Append(uint8_t); + void Append(uint16_t); + void Append(uint32_t); + void Append(const char *); + void ReplaceAt(int, uint8_t); + void ReplaceAt(int, uint16_t); + void ReplaceAt(int, uint32_t); + void ReplaceAt(int, const uint8_t *, int); + + // operation + int Compare(uint8_t *, int) const; + int Compare(uint8_t *, int, int) const; + + // operator + bool operator ==(const CBuffer &) const; + bool operator ==(const char *) const; + operator const char *() const; + + // debug + void DebugDump(std::ofstream &) const; + void DebugDumpAscii(std::ofstream &) const; + void Dump(const std::string &title); + + // pass through + void clear() { m_data.clear(); } + std::vector::size_type size() const { return m_data.size(); } + uint8_t *data() { return m_data.data(); } + const uint8_t *data() const { return m_data.data(); } + void resize(std::vector::size_type len, uint8_t val = 0) { m_data.resize(len, val); } + uint8_t at(std::vector::size_type i) const { return m_data.at(i); } + +protected: + std::vector m_data; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cbuffer_h */ diff --git a/src/CBPTC19696.cpp b/src/CBPTC19696.cpp new file mode 100644 index 0000000..e298340 --- /dev/null +++ b/src/CBPTC19696.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 by Ian Wraith + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CBPT19696.h" + +#include "Hamming.h" +#include "Utils.h" + +#include +#include +#include + +CBPTC19696::CBPTC19696() +{ +} + +CBPTC19696::~CBPTC19696() +{ +} + +// The main decode function +void CBPTC19696::decode(const unsigned char* in, unsigned char* out) +{ + assert(in != nullptr); + assert(out != nullptr); + + // Get the raw binary + decodeExtractBinary(in); + + // Deinterleave + decodeDeInterleave(); + + // Error check + decodeErrorCheck(); + + // Extract Data + decodeExtractData(out); +} + +// The main encode function +void CBPTC19696::encode(const unsigned char* in, unsigned char* out) +{ + assert(in != nullptr); + assert(out != nullptr); + + // Extract Data + encodeExtractData(in); + + // Error check + encodeErrorCheck(); + + // Deinterleave + encodeInterleave(); + + // Get the raw binary + encodeExtractBinary(out); +} + +void CBPTC19696::decodeExtractBinary(const unsigned char* in) +{ + // First block + CUtils::byteToBitsBE(in[0U], m_rawData + 0U); + CUtils::byteToBitsBE(in[1U], m_rawData + 8U); + CUtils::byteToBitsBE(in[2U], m_rawData + 16U); + CUtils::byteToBitsBE(in[3U], m_rawData + 24U); + CUtils::byteToBitsBE(in[4U], m_rawData + 32U); + CUtils::byteToBitsBE(in[5U], m_rawData + 40U); + CUtils::byteToBitsBE(in[6U], m_rawData + 48U); + CUtils::byteToBitsBE(in[7U], m_rawData + 56U); + CUtils::byteToBitsBE(in[8U], m_rawData + 64U); + CUtils::byteToBitsBE(in[9U], m_rawData + 72U); + CUtils::byteToBitsBE(in[10U], m_rawData + 80U); + CUtils::byteToBitsBE(in[11U], m_rawData + 88U); + CUtils::byteToBitsBE(in[12U], m_rawData + 96U); + + // Handle the two bits + bool bits[8U]; + CUtils::byteToBitsBE(in[20U], bits); + m_rawData[98U] = bits[6U]; + m_rawData[99U] = bits[7U]; + + // Second block + CUtils::byteToBitsBE(in[21U], m_rawData + 100U); + CUtils::byteToBitsBE(in[22U], m_rawData + 108U); + CUtils::byteToBitsBE(in[23U], m_rawData + 116U); + CUtils::byteToBitsBE(in[24U], m_rawData + 124U); + CUtils::byteToBitsBE(in[25U], m_rawData + 132U); + CUtils::byteToBitsBE(in[26U], m_rawData + 140U); + CUtils::byteToBitsBE(in[27U], m_rawData + 148U); + CUtils::byteToBitsBE(in[28U], m_rawData + 156U); + CUtils::byteToBitsBE(in[29U], m_rawData + 164U); + CUtils::byteToBitsBE(in[30U], m_rawData + 172U); + CUtils::byteToBitsBE(in[31U], m_rawData + 180U); + CUtils::byteToBitsBE(in[32U], m_rawData + 188U); +} + +// Deinterleave the raw data +void CBPTC19696::decodeDeInterleave() +{ + for (unsigned int i = 0U; i < 196U; i++) + m_deInterData[i] = false; + + // The first bit is R(3) which is not used so can be ignored + for (unsigned int a = 0U; a < 196U; a++) + { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 181U) % 196U; + // Shuffle the data + m_deInterData[a] = m_rawData[interleaveSequence]; + } +} + +// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code +void CBPTC19696::decodeErrorCheck() +{ + bool fixing; + unsigned int count = 0U; + do + { + fixing = false; + + // Run through each of the 15 columns + bool col[13U]; + for (unsigned int c = 0U; c < 15U; c++) + { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) + { + col[a] = m_deInterData[pos]; + pos = pos + 15U; + } + + if (CHamming::decode1393(col)) + { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) + { + m_deInterData[pos] = col[a]; + pos = pos + 15U; + } + + fixing = true; + } + } + + // Run through each of the 9 rows containing data + for (unsigned int r = 0U; r < 9U; r++) + { + unsigned int pos = (r * 15U) + 1U; + if (CHamming::decode15113_2(m_deInterData + pos)) + fixing = true; + } + + count++; + } + while (fixing && count < 5U); +} + +// Extract the 96 bits of payload +void CBPTC19696::decodeExtractData(unsigned char* data) const +{ + bool bData[96U]; + unsigned int pos = 0U; + for (unsigned int a = 4U; a <= 11U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 16U; a <= 26U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 31U; a <= 41U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 46U; a <= 56U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 61U; a <= 71U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 76U; a <= 86U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 91U; a <= 101U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 106U; a <= 116U; a++, pos++) + bData[pos] = m_deInterData[a]; + + for (unsigned int a = 121U; a <= 131U; a++, pos++) + bData[pos] = m_deInterData[a]; + + CUtils::bitsToByteBE(bData + 0U, data[0U]); + CUtils::bitsToByteBE(bData + 8U, data[1U]); + CUtils::bitsToByteBE(bData + 16U, data[2U]); + CUtils::bitsToByteBE(bData + 24U, data[3U]); + CUtils::bitsToByteBE(bData + 32U, data[4U]); + CUtils::bitsToByteBE(bData + 40U, data[5U]); + CUtils::bitsToByteBE(bData + 48U, data[6U]); + CUtils::bitsToByteBE(bData + 56U, data[7U]); + CUtils::bitsToByteBE(bData + 64U, data[8U]); + CUtils::bitsToByteBE(bData + 72U, data[9U]); + CUtils::bitsToByteBE(bData + 80U, data[10U]); + CUtils::bitsToByteBE(bData + 88U, data[11U]); +} + +// Extract the 96 bits of payload +void CBPTC19696::encodeExtractData(const unsigned char* in) +{ + bool bData[96U]; + CUtils::byteToBitsBE(in[0U], bData + 0U); + CUtils::byteToBitsBE(in[1U], bData + 8U); + CUtils::byteToBitsBE(in[2U], bData + 16U); + CUtils::byteToBitsBE(in[3U], bData + 24U); + CUtils::byteToBitsBE(in[4U], bData + 32U); + CUtils::byteToBitsBE(in[5U], bData + 40U); + CUtils::byteToBitsBE(in[6U], bData + 48U); + CUtils::byteToBitsBE(in[7U], bData + 56U); + CUtils::byteToBitsBE(in[8U], bData + 64U); + CUtils::byteToBitsBE(in[9U], bData + 72U); + CUtils::byteToBitsBE(in[10U], bData + 80U); + CUtils::byteToBitsBE(in[11U], bData + 88U); + + for (unsigned int i = 0U; i < 196U; i++) + m_deInterData[i] = false; + + unsigned int pos = 0U; + for (unsigned int a = 4U; a <= 11U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 16U; a <= 26U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 31U; a <= 41U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 46U; a <= 56U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 61U; a <= 71U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 76U; a <= 86U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 91U; a <= 101U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 106U; a <= 116U; a++, pos++) + m_deInterData[a] = bData[pos]; + + for (unsigned int a = 121U; a <= 131U; a++, pos++) + m_deInterData[a] = bData[pos]; +} + +// Check each row with a Hamming (15,11,3) code and each column with a Hamming (13,9,3) code +void CBPTC19696::encodeErrorCheck() +{ + + // Run through each of the 9 rows containing data + for (unsigned int r = 0U; r < 9U; r++) + { + unsigned int pos = (r * 15U) + 1U; + CHamming::encode15113_2(m_deInterData + pos); + } + + // Run through each of the 15 columns + bool col[13U]; + for (unsigned int c = 0U; c < 15U; c++) + { + unsigned int pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) + { + col[a] = m_deInterData[pos]; + pos = pos + 15U; + } + + CHamming::encode1393(col); + + pos = c + 1U; + for (unsigned int a = 0U; a < 13U; a++) + { + m_deInterData[pos] = col[a]; + pos = pos + 15U; + } + } +} + +// Interleave the raw data +void CBPTC19696::encodeInterleave() +{ + for (unsigned int i = 0U; i < 196U; i++) + m_rawData[i] = false; + + // The first bit is R(3) which is not used so can be ignored + for (unsigned int a = 0U; a < 196U; a++) + { + // Calculate the interleave sequence + unsigned int interleaveSequence = (a * 181U) % 196U; + // Unshuffle the data + m_rawData[interleaveSequence] = m_deInterData[a]; + } +} + +void CBPTC19696::encodeExtractBinary(unsigned char* data) +{ + // First block + CUtils::bitsToByteBE(m_rawData + 0U, data[0U]); + CUtils::bitsToByteBE(m_rawData + 8U, data[1U]); + CUtils::bitsToByteBE(m_rawData + 16U, data[2U]); + CUtils::bitsToByteBE(m_rawData + 24U, data[3U]); + CUtils::bitsToByteBE(m_rawData + 32U, data[4U]); + CUtils::bitsToByteBE(m_rawData + 40U, data[5U]); + CUtils::bitsToByteBE(m_rawData + 48U, data[6U]); + CUtils::bitsToByteBE(m_rawData + 56U, data[7U]); + CUtils::bitsToByteBE(m_rawData + 64U, data[8U]); + CUtils::bitsToByteBE(m_rawData + 72U, data[9U]); + CUtils::bitsToByteBE(m_rawData + 80U, data[10U]); + CUtils::bitsToByteBE(m_rawData + 88U, data[11U]); + + // Handle the two bits + unsigned char byte; + CUtils::bitsToByteBE(m_rawData + 96U, byte); + data[12U] = (data[12U] & 0x3FU) | ((byte >> 0) & 0xC0U); + data[20U] = (data[20U] & 0xFCU) | ((byte >> 4) & 0x03U); + + // Second block + CUtils::bitsToByteBE(m_rawData + 100U, data[21U]); + CUtils::bitsToByteBE(m_rawData + 108U, data[22U]); + CUtils::bitsToByteBE(m_rawData + 116U, data[23U]); + CUtils::bitsToByteBE(m_rawData + 124U, data[24U]); + CUtils::bitsToByteBE(m_rawData + 132U, data[25U]); + CUtils::bitsToByteBE(m_rawData + 140U, data[26U]); + CUtils::bitsToByteBE(m_rawData + 148U, data[27U]); + CUtils::bitsToByteBE(m_rawData + 156U, data[28U]); + CUtils::bitsToByteBE(m_rawData + 164U, data[29U]); + CUtils::bitsToByteBE(m_rawData + 172U, data[30U]); + CUtils::bitsToByteBE(m_rawData + 180U, data[31U]); + CUtils::bitsToByteBE(m_rawData + 188U, data[32U]); +} diff --git a/src/CBPTC19696.h b/src/CBPTC19696.h new file mode 100644 index 0000000..cbf584c --- /dev/null +++ b/src/CBPTC19696.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(BPTC19696_H) +#define BPTC19696_H + +class CBPTC19696 +{ +public: + CBPTC19696(); + ~CBPTC19696(); + + void decode(const unsigned char* in, unsigned char* out); + + void encode(const unsigned char* in, unsigned char* out); + +private: + bool m_rawData[196]; + bool m_deInterData[196]; + + void decodeExtractBinary(const unsigned char* in); + void decodeErrorCheck(); + void decodeDeInterleave(); + void decodeExtractData(unsigned char* data) const; + + void encodeExtractData(const unsigned char* in); + void encodeInterleave(); + void encodeErrorCheck(); + void encodeExtractBinary(unsigned char* data); +}; + +#endif diff --git a/src/CNotificationQueue.h b/src/CNotificationQueue.h new file mode 100644 index 0000000..3d50154 --- /dev/null +++ b/src/CNotificationQueue.h @@ -0,0 +1,68 @@ +// +// NotificationQueue.h +// xlxd +// +// Created by Jean-Luc on 05/12/2015. +// Copyright © 2015 Jean-Luc. All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + + +#ifndef cnotificationqueue_h +#define cnotificationqueue_h + +#include +#include + +#include "Notification.h" + + +//////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CNotificationQueue +{ +public: + // constructor + CNotificationQueue() {} + + // destructor + ~CNotificationQueue() {} + + // lock + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + + // pass thru + CNotification front() { return queue.front(); } + void pop() { queue.pop(); } + void push(CNotification note) { queue.push(note); } + bool empty() const { return queue.empty(); } + +protected: + // data + std::mutex m_Mutex; + std::queue queue; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cnotificationqueue_h */ diff --git a/src/CRC.cpp b/src/CRC.cpp new file mode 100644 index 0000000..93b20d1 --- /dev/null +++ b/src/CRC.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "CRC.h" + +#include "Utils.h" + +#include +#include +#include +#include + +const uint8_t CRC8_TABLE[] = +{ + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, + 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, + 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, + 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, + 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, + 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, + 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, + 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, + 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, + 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, + 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, + 0xFA, 0xFD, 0xF4, 0xF3, 0x01 +}; + +const uint16_t CCITT16_TABLE1[] = +{ + 0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU, + 0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U, + 0x1081U, 0x0108U, 0x3393U, 0x221aU, 0x56a5U, 0x472cU, 0x75b7U, 0x643eU, + 0x9cc9U, 0x8d40U, 0xbfdbU, 0xae52U, 0xdaedU, 0xcb64U, 0xf9ffU, 0xe876U, + 0x2102U, 0x308bU, 0x0210U, 0x1399U, 0x6726U, 0x76afU, 0x4434U, 0x55bdU, + 0xad4aU, 0xbcc3U, 0x8e58U, 0x9fd1U, 0xeb6eU, 0xfae7U, 0xc87cU, 0xd9f5U, + 0x3183U, 0x200aU, 0x1291U, 0x0318U, 0x77a7U, 0x662eU, 0x54b5U, 0x453cU, + 0xbdcbU, 0xac42U, 0x9ed9U, 0x8f50U, 0xfbefU, 0xea66U, 0xd8fdU, 0xc974U, + 0x4204U, 0x538dU, 0x6116U, 0x709fU, 0x0420U, 0x15a9U, 0x2732U, 0x36bbU, + 0xce4cU, 0xdfc5U, 0xed5eU, 0xfcd7U, 0x8868U, 0x99e1U, 0xab7aU, 0xbaf3U, + 0x5285U, 0x430cU, 0x7197U, 0x601eU, 0x14a1U, 0x0528U, 0x37b3U, 0x263aU, + 0xdecdU, 0xcf44U, 0xfddfU, 0xec56U, 0x98e9U, 0x8960U, 0xbbfbU, 0xaa72U, + 0x6306U, 0x728fU, 0x4014U, 0x519dU, 0x2522U, 0x34abU, 0x0630U, 0x17b9U, + 0xef4eU, 0xfec7U, 0xcc5cU, 0xddd5U, 0xa96aU, 0xb8e3U, 0x8a78U, 0x9bf1U, + 0x7387U, 0x620eU, 0x5095U, 0x411cU, 0x35a3U, 0x242aU, 0x16b1U, 0x0738U, + 0xffcfU, 0xee46U, 0xdcddU, 0xcd54U, 0xb9ebU, 0xa862U, 0x9af9U, 0x8b70U, + 0x8408U, 0x9581U, 0xa71aU, 0xb693U, 0xc22cU, 0xd3a5U, 0xe13eU, 0xf0b7U, + 0x0840U, 0x19c9U, 0x2b52U, 0x3adbU, 0x4e64U, 0x5fedU, 0x6d76U, 0x7cffU, + 0x9489U, 0x8500U, 0xb79bU, 0xa612U, 0xd2adU, 0xc324U, 0xf1bfU, 0xe036U, + 0x18c1U, 0x0948U, 0x3bd3U, 0x2a5aU, 0x5ee5U, 0x4f6cU, 0x7df7U, 0x6c7eU, + 0xa50aU, 0xb483U, 0x8618U, 0x9791U, 0xe32eU, 0xf2a7U, 0xc03cU, 0xd1b5U, + 0x2942U, 0x38cbU, 0x0a50U, 0x1bd9U, 0x6f66U, 0x7eefU, 0x4c74U, 0x5dfdU, + 0xb58bU, 0xa402U, 0x9699U, 0x8710U, 0xf3afU, 0xe226U, 0xd0bdU, 0xc134U, + 0x39c3U, 0x284aU, 0x1ad1U, 0x0b58U, 0x7fe7U, 0x6e6eU, 0x5cf5U, 0x4d7cU, + 0xc60cU, 0xd785U, 0xe51eU, 0xf497U, 0x8028U, 0x91a1U, 0xa33aU, 0xb2b3U, + 0x4a44U, 0x5bcdU, 0x6956U, 0x78dfU, 0x0c60U, 0x1de9U, 0x2f72U, 0x3efbU, + 0xd68dU, 0xc704U, 0xf59fU, 0xe416U, 0x90a9U, 0x8120U, 0xb3bbU, 0xa232U, + 0x5ac5U, 0x4b4cU, 0x79d7U, 0x685eU, 0x1ce1U, 0x0d68U, 0x3ff3U, 0x2e7aU, + 0xe70eU, 0xf687U, 0xc41cU, 0xd595U, 0xa12aU, 0xb0a3U, 0x8238U, 0x93b1U, + 0x6b46U, 0x7acfU, 0x4854U, 0x59ddU, 0x2d62U, 0x3cebU, 0x0e70U, 0x1ff9U, + 0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U, + 0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U +}; + +const uint16_t CCITT16_TABLE2[] = +{ + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + + +bool CCRC::checkFiveBit(bool* in, unsigned int tcrc) +{ + assert(in != nullptr); + + unsigned int crc; + encodeFiveBit(in, crc); + + return crc == tcrc; +} + +void CCRC::encodeFiveBit(const bool* in, unsigned int& tcrc) +{ + assert(in != nullptr); + + unsigned short total = 0U; + for (unsigned int i = 0U; i < 72U; i += 8U) + { + unsigned char c; + CUtils::bitsToByteBE(in + i, c); + total += c; + } + + total %= 31U; + + tcrc = total; +} + +void CCRC::addCCITT162(unsigned char *in, unsigned int length) +{ + assert(in != nullptr); + assert(length > 2U); + + union + { + uint16_t crc16; + uint8_t crc8[2U]; + }; + + crc16 = 0U; + + for (unsigned i = 0U; i < (length - 2U); i++) + crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]]; + + crc16 = ~crc16; + + in[length - 1U] = crc8[0U]; + in[length - 2U] = crc8[1U]; +} + +bool CCRC::checkCCITT162(const unsigned char *in, unsigned int length) +{ + assert(in != nullptr); + assert(length > 2U); + + union + { + uint16_t crc16; + uint8_t crc8[2U]; + }; + + crc16 = 0U; + + for (unsigned i = 0U; i < (length - 2U); i++) + crc16 = (uint16_t(crc8[0U]) << 8) ^ CCITT16_TABLE2[crc8[1U] ^ in[i]]; + + crc16 = ~crc16; + + return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U]; +} + +void CCRC::addCCITT161(unsigned char *in, unsigned int length) +{ + assert(in != nullptr); + assert(length > 2U); + + union + { + uint16_t crc16; + uint8_t crc8[2U]; + }; + + crc16 = 0xFFFFU; + + for (unsigned int i = 0U; i < (length - 2U); i++) + crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]]; + + crc16 = ~crc16; + + in[length - 2U] = crc8[0U]; + in[length - 1U] = crc8[1U]; +} + +bool CCRC::checkCCITT161(const unsigned char *in, unsigned int length) +{ + assert(in != nullptr); + assert(length > 2U); + + union + { + uint16_t crc16; + uint8_t crc8[2U]; + }; + + crc16 = 0xFFFFU; + + for (unsigned int i = 0U; i < (length - 2U); i++) + crc16 = uint16_t(crc8[1U]) ^ CCITT16_TABLE1[crc8[0U] ^ in[i]]; + + crc16 = ~crc16; + + return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U]; +} + +unsigned char CCRC::crc8(const unsigned char *in, unsigned int length) +{ + assert(in != nullptr); + + uint8_t crc = 0U; + + for (unsigned int i = 0U; i < length; i++) + crc = CRC8_TABLE[crc ^ in[i]]; + + return crc; +} + +unsigned char CCRC::addCRC(const unsigned char* in, unsigned int length) +{ + assert(in != nullptr); + + unsigned char crc = 0U; + + for (unsigned int i = 0U; i < length; i++) + crc += in[i]; + + return crc; +} diff --git a/src/CRC.h b/src/CRC.h new file mode 100644 index 0000000..90bca98 --- /dev/null +++ b/src/CRC.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CRC_H) +#define CRC_H + +class CCRC +{ +public: + static bool checkFiveBit(bool* in, unsigned int tcrc); + static void encodeFiveBit(const bool* in, unsigned int& tcrc); + + static void addCCITT161(unsigned char* in, unsigned int length); + static void addCCITT162(unsigned char* in, unsigned int length); + + static bool checkCCITT161(const unsigned char* in, unsigned int length); + static bool checkCCITT162(const unsigned char* in, unsigned int length); + + static unsigned char crc8(const unsigned char* in, unsigned int length); + + static unsigned char addCRC(const unsigned char* in, unsigned int length); +}; + +#endif diff --git a/src/Callsign.cpp b/src/Callsign.cpp new file mode 100644 index 0000000..92e2466 --- /dev/null +++ b/src/Callsign.cpp @@ -0,0 +1,370 @@ +// +// ccallsign.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include +#include "cdmriddirfile.h" +#include "DMRIdDirHttp.h" +#include "Callsign.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CCallsign::CCallsign() +{ + // blank all + ::memset(m_Callsign, ' ', sizeof(m_Callsign)); + ::memset(m_Suffix, ' ', sizeof(m_Suffix)); + m_Module = ' '; +#ifndef NO_XLX + m_uiDmrid = 0; +#endif +} + +CCallsign::CCallsign(const char *sz, uint32 dmrid) +{ + // blank all + ::memset(m_Callsign, ' ', sizeof(m_Callsign)); + ::memset(m_Suffix, ' ', sizeof(m_Suffix)); + m_Module = ' '; +#ifndef NO_XLX + m_uiDmrid = dmrid; +#endif + + // and populate + if ( ::strlen(sz) > 0 ) + { + // callsign valid + ::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1)); + if ( strlen(sz) >= sizeof(m_Callsign) ) + { + m_Module = sz[sizeof(m_Callsign)-1]; + } +#ifndef NO_XLX + // dmrid ok ? + if ( m_uiDmrid == 0 ) + { + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); + } + } + else if ( m_uiDmrid != 0 ) + { + g_DmridDir.Lock(); + { + const CCallsign *callsign = g_DmridDir.FindCallsign(m_uiDmrid); + if ( callsign != nullptr ) + { + ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); + } + } + g_DmridDir.Unlock(); +#endif + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CCallsign::IsValid(void) const +{ + bool valid = true; + int i; + + // callsign + // first 3 chars are letter or number but cannot be all number + int iNum = 0; + for ( i = 0; i < 3; i++ ) + { + valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]); + if ( IsNumber(m_Callsign[i]) ) + { + iNum++; + } + } + valid &= (iNum < 3); + // all remaining char are letter, number or space + for ( ; i < CALLSIGN_LEN; i++) + { + valid &= IsLetter(m_Callsign[i]) || IsNumber(m_Callsign[i]) || IsSpace(m_Callsign[i]); + } + + // prefix + // all chars are number, uppercase or space + for ( i = 0; i < CALLSUFFIX_LEN; i++ ) + { + valid &= IsLetter(m_Suffix[i]) || IsNumber(m_Suffix[i]) || IsSpace(m_Suffix[i]); + } + + // module + // is an letter or space + valid &= IsLetter(m_Module) || IsSpace(m_Module); + + // dmrid is not tested, as it can be nullptr + // if station does is not dmr registered + + // done + return valid; +} + +bool CCallsign::HasSuffix(void) const +{ + bool has = false; + for ( int i = 0; i < CALLSUFFIX_LEN; i++ ) + { + has |= (m_Suffix[i] != ' '); + } + return has; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// set + +void CCallsign::SetCallsign(const char *sz, bool UpdateDmrid) +{ + // set callsign + ::memset(m_Callsign, ' ', sizeof(m_Callsign)); + m_Module = ' '; + ::memcpy(m_Callsign, sz, MIN(strlen(sz), sizeof(m_Callsign)-1)); + if ( strlen(sz) >= sizeof(m_Callsign) ) + { + m_Module = sz[sizeof(m_Callsign)-1]; + } +#ifndef NO_XLX + // and update dmrid + if ( UpdateDmrid ) + { + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); + } +#endif +} + +void CCallsign::SetCallsign(const uint8 *buffer, int len, bool UpdateDmrid) +{ + // set callsign + ::memset(m_Callsign, ' ', sizeof(m_Callsign)); + m_Module = ' '; + ::memcpy(m_Callsign, buffer, MIN(len, (int)sizeof(m_Callsign)-1)); + for ( unsigned i = 0; i < sizeof(m_Callsign); i++ ) + { + if ( m_Callsign[i] == 0 ) + { + m_Callsign[i] = ' '; + } + } + if ( (len >= (int)sizeof(m_Callsign)) && ((char)buffer[sizeof(m_Callsign)-1] != 0) ) + { + m_Module = (char)buffer[sizeof(m_Callsign)-1]; + } +#ifndef NO_XLX + if ( UpdateDmrid ) + { + g_DmridDir.Lock(); + { + m_uiDmrid = g_DmridDir.FindDmrid(*this); + } + g_DmridDir.Unlock(); + } +#endif +} + +#ifndef NO_XLX +void CCallsign::SetDmrid(uint32 dmrid, bool UpdateCallsign) +{ + m_uiDmrid = dmrid; + if ( UpdateCallsign ) + { + g_DmridDir.Lock(); + { + const CCallsign *callsign = g_DmridDir.FindCallsign(dmrid); + if ( callsign != nullptr ) + { + ::memcpy(m_Callsign, callsign->m_Callsign, sizeof(m_Callsign)); + } + } + g_DmridDir.Unlock(); + } +} + +void CCallsign::SetDmrid(const uint8 *buffer, bool UpdateCallsign) +{ + char sz[9]; + ::memcpy(sz, buffer, 8); + sz[8] = 0; + SetDmrid((uint32)::strtol(sz, nullptr, 16), UpdateCallsign); +} +#endif + +void CCallsign::SetModule(char c) +{ + m_Module = c; +} + + +void CCallsign::SetSuffix(const char *sz) +{ + ::memset(m_Suffix, ' ', sizeof(m_Suffix)); + ::memcpy(m_Suffix, sz, MIN(strlen(sz), sizeof(m_Suffix))); +} + +void CCallsign::SetSuffix(const uint8 *buffer, int len) +{ + len = MIN(len, (int)sizeof(m_Suffix)); + ::memset(m_Suffix, ' ', sizeof(m_Suffix)); + ::memcpy(m_Suffix, buffer, len); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// modify + +void CCallsign::PatchCallsign(int off, const uint8 *patch, int len) +{ + if ( off < CALLSIGN_LEN ) + { + ::memcpy(m_Callsign, patch, MIN(len, (int)sizeof(m_Callsign) - off)); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +void CCallsign::GetCallsign(uint8 *buffer) const +{ + ::memcpy(buffer, m_Callsign, sizeof(m_Callsign)); + if ( HasModule() ) + { + buffer[sizeof(m_Callsign)-1] = m_Module; + } +} + +void CCallsign::GetCallsignString(char *sz) const +{ + unsigned i; + for ( i = 0; (i < sizeof(m_Callsign)) && (m_Callsign[i] != ' '); i++ ) + { + sz[i] = m_Callsign[i]; + } + sz[i] = 0; +} + +void CCallsign::GetSuffix(uint8 *buffer) const +{ + ::memcpy(buffer, m_Suffix, sizeof(m_Suffix)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// compare + +bool CCallsign::HasSameCallsign(const CCallsign &Callsign) const +{ + return (::memcmp(m_Callsign, Callsign.m_Callsign, sizeof(m_Callsign)) == 0); +} + +bool CCallsign::HasSameCallsignWithWildcard(const CCallsign &callsign) const +{ + bool same = true; + bool done = false; + + for ( unsigned i = 0; (i < sizeof(m_Callsign)) && same && !done; i++ ) + { + if ( !(done = ((m_Callsign[i] == '*') || (callsign[i] == '*'))) ) + { + same &= (m_Callsign[i] == callsign[i]); + } + } + return same; +} + +bool CCallsign::HasLowerCallsign(const CCallsign &Callsign) const +{ + return (::memcmp(m_Callsign, Callsign.m_Callsign, sizeof(m_Callsign)) < 0); +} + +bool CCallsign::HasSameModule(const CCallsign &Callsign) const +{ + return (m_Module == Callsign.m_Module); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CCallsign::operator ==(const CCallsign &callsign) const +{ + return ((::memcmp(callsign.m_Callsign, m_Callsign, sizeof(m_Callsign)) == 0) && (m_Module == callsign.m_Module) + && (::memcmp(callsign.m_Suffix, m_Suffix, sizeof(m_Suffix)) == 0) +#ifndef NO_XLX + && (m_uiDmrid == callsign.m_uiDmrid) +#endif + ); +} + +CCallsign::operator const char *() const +{ + // empty + ::memset(m_sz, 0, sizeof(m_sz)); + // callsign + ::memcpy(m_sz, m_Callsign, sizeof(m_Callsign)); + // module + if ( HasModule() ) + { + m_sz[sizeof(m_Callsign)] = m_Module; + } + // suffix + if ( HasSuffix() ) + { + ::strcat(m_sz, " / "); + ::strncat(m_sz, m_Suffix, sizeof(m_Suffix)); + } + + // done + return m_sz; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// helper + +bool CCallsign::IsNumber(char c) const +{ + return ((c >= '0') && (c <= '9')); +} + +bool CCallsign::IsLetter(char c) const +{ + return ((c >= 'A') && (c <= 'Z')); +} + +bool CCallsign::IsSpace(char c) const +{ + return (c == ' '); +} diff --git a/src/Callsign.h b/src/Callsign.h new file mode 100644 index 0000000..bb858ee --- /dev/null +++ b/src/Callsign.h @@ -0,0 +1,101 @@ +// +// Callsign.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef ccallsign_h +#define ccallsign_h + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define CALLSIGN_LEN 8 +#define CALLSUFFIX_LEN 4 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CCallsign +{ +public: + // contructors + CCallsign(); + CCallsign(const char *, uint32 = 0); + + // status + bool IsValid(void) const; + bool HasSuffix(void) const; + bool HasModule(void) const { return m_Module != ' '; } + + // set + void SetCallsign(const char *, bool = true); + void SetCallsign(const uint8 *, int, bool = true); +#ifndef NO_XLX + void SetDmrid(uint32, bool = true); + void SetDmrid(const uint8 *, bool = true); +#endif + void SetModule(char); + void SetSuffix(const char *); + void SetSuffix(const uint8 *, int); + + // modify + void PatchCallsign(int, const uint8 *, int); + + // get + void GetCallsign(uint8 *) const; + void GetCallsignString(char *) const; +#ifndef NO_XLX + uint32 GetDmrid(void) const { return m_uiDmrid; } +#endif + void GetSuffix(uint8 *) const; + char GetModule(void) const { return m_Module; } + + // compare + bool HasSameCallsign(const CCallsign &) const; + bool HasSameCallsignWithWildcard(const CCallsign &) const; + bool HasLowerCallsign(const CCallsign &) const; + bool HasSameModule(const CCallsign &) const; + + // operators + bool operator ==(const CCallsign &) const; + operator const char *() const; + +protected: + // helper + bool IsNumber(char) const; + bool IsLetter(char) const; + bool IsSpace(char) const; + +protected: + // data + char m_Callsign[CALLSIGN_LEN]; + char m_Suffix[CALLSUFFIX_LEN]; + char m_Module; + mutable char m_sz[CALLSIGN_LEN+CALLSUFFIX_LEN+5]; +#ifndef NO_XLX + uint32 m_uiDmrid; +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* ccallsign_h */ diff --git a/src/CallsignList.cpp b/src/CallsignList.cpp new file mode 100644 index 0000000..c94a4c4 --- /dev/null +++ b/src/CallsignList.cpp @@ -0,0 +1,230 @@ +// +// ccallsignlist.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include "Main.h" +#include "CallsignList.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CCallsignList::CCallsignList() +{ + m_Filename = nullptr; + ::memset(&m_LastModTime, 0, sizeof(time_t)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// file io + +bool CCallsignList::LoadFromFile(const char *filename) +{ + bool ok = false; + char sz[256]; + char szStar[2] = "*"; + + // and load + std::ifstream file (filename); + if ( file.is_open() ) + { + Lock(); + + // empty list + m_Callsigns.clear(); + // fill with file content + while ( file.getline(sz, sizeof(sz)).good() ) + { + // remove leading & trailing spaces + char *szt = TrimWhiteSpaces(sz); + + // crack it + if ( (::strlen(szt) > 0) && (szt[0] != '#') ) + { + // 1st token is callsign + if ( (szt = ::strtok(szt, " ,\t")) != nullptr ) + { + CCallsign callsign(szt); + // 2nd token is modules list + szt = ::strtok(nullptr, " ,\t"); + // if token absent, use wildcard + if ( szt == nullptr ) + { + szt = szStar; + } + // and add to list + m_Callsigns.push_back(CCallsignListItem(callsign, CIp(), szt)); + } + } + } + // close file + file.close(); + + // keep file path + m_Filename = filename; + + // update time + GetLastModTime(&m_LastModTime); + + // and done + Unlock(); + ok = true; + std::cout << "Gatekeeper loaded " << m_Callsigns.size() << " lines from " << filename << std::endl; + } + else + { + std::cout << "Gatekeeper cannot find " << filename << std::endl; + } + + return ok; +} + +bool CCallsignList::ReloadFromFile(void) +{ + bool ok = false; + + if ( m_Filename != nullptr ) + { + ok = LoadFromFile(m_Filename); + } + return ok; +} + +bool CCallsignList::NeedReload(void) +{ + bool needReload = false; + + time_t time; + if ( GetLastModTime(&time) ) + { + needReload = time != m_LastModTime; + } + return needReload; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// compare + +bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign) const +{ + for ( const auto &item : m_Callsigns ) + { + if (item.HasSameCallsignWithWildcard(callsign)) + return true; + } + + return false; +} + +bool CCallsignList::IsCallsignListedWithWildcard(const CCallsign &callsign, char module) const +{ + for ( const auto &item : m_Callsigns ) + { + if (item.HasSameCallsignWithWildcard(callsign) && ((module == ' ') || item.HasModuleListed(module)) ) + return true; + } + + return false; +} + +bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char module) const +{ + for ( const auto &item : m_Callsigns ) + { + if (item.HasSameCallsign(callsign) && item.HasModuleListed(module)) + return true; + } + + return false; +} + +bool CCallsignList::IsCallsignListed(const CCallsign &callsign, char *modules) const +{ + for ( const auto &item : m_Callsigns ) + { + if (item.HasSameCallsign(callsign) && item.CheckListedModules(modules)) + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// find + +CCallsignListItem *CCallsignList::FindListItem(const CCallsign &Callsign) +{ + for ( auto &item : m_Callsigns ) + { + if ( item.GetCallsign().HasSameCallsign(Callsign) ) + { + return &item; + } + } + + return nullptr; + +} + +//////////////////////////////////////////////////////////////////////////////////////// +// helpers + +char *CCallsignList::TrimWhiteSpaces(char *str) +{ + char *end; + + // Trim leading space & tabs + while((*str == ' ') || (*str == '\t')) str++; + + // All spaces? + if(*str == 0) + return str; + + // Trim trailing space, tab or lf + end = str + ::strlen(str) - 1; + while((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) end--; + + // Write new null terminator + *(end+1) = 0; + + return str; +} + +bool CCallsignList::GetLastModTime(time_t *time) +{ + bool ok = false; + + if ( m_Filename != nullptr ) + { + struct stat fileStat; + if( ::stat(m_Filename, &fileStat) != -1 ) + { + *time = fileStat.st_mtime; + ok = true; + } + } + return ok; +} diff --git a/src/CallsignList.h b/src/CallsignList.h new file mode 100644 index 0000000..9d98654 --- /dev/null +++ b/src/CallsignList.h @@ -0,0 +1,81 @@ +// +// CallsignList.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 30/12/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef ccallsignlist_h +#define ccallsignlist_h + +#include "Main.h" +#include "CallsignListItem.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CCallsignList +{ +public: + // constructor + CCallsignList(); + + // destructor + virtual ~CCallsignList() {} + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // file io + virtual bool LoadFromFile(const char *); + bool ReloadFromFile(void); + bool NeedReload(void); + + // compare + bool IsCallsignListedWithWildcard(const CCallsign &) const; + bool IsCallsignListedWithWildcard(const CCallsign &, char) const; + bool IsCallsignListed(const CCallsign &, char) const; + bool IsCallsignListed(const CCallsign &, char*) const; + + // pass-thru + bool empty() const { return m_Callsigns.empty(); } + std::list::iterator begin() { return m_Callsigns.begin(); } + std::list::iterator end() { return m_Callsigns.end(); } + + // find + CCallsignListItem *FindListItem(const CCallsign &); + +protected: + bool GetLastModTime(time_t *); + char *TrimWhiteSpaces(char *); + + // data + std::mutex m_Mutex; + const char * m_Filename; + time_t m_LastModTime; + std::list m_Callsigns; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* ccallsignlist_h */ diff --git a/src/CallsignListItem.cpp b/src/CallsignListItem.cpp new file mode 100644 index 0000000..3ec0697 --- /dev/null +++ b/src/CallsignListItem.cpp @@ -0,0 +1,146 @@ +// +// ccallsignlistitem.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "CallsignListItem.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CCallsignListItem::CCallsignListItem() +{ + ::memset(m_Modules, 0, sizeof(m_Modules)); + ::memset(m_szUrl, 0, sizeof(m_szUrl)); +} + +CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const CIp &ip, const char *modules) +{ + m_Callsign = callsign; + ::memset(m_szUrl, 0, sizeof(m_szUrl)); + m_Ip = ip; + if ( modules != nullptr ) + { + ::memset(m_Modules, 0, sizeof(m_Modules)); + if ( modules[0] == '*' ) + { + for ( char i = 0; i < NB_OF_MODULES; i++ ) + { + m_Modules[i] = 'A' + i; + } + } + else + { + int n = MIN(::strlen(modules), sizeof(m_Modules)-1); + int j = 0; + for ( int i = 0; i < n; i++ ) + { + if ( (modules[i] - 'A') < NB_OF_MODULES ) + { + m_Modules[j++] = modules[i]; + } + } + } + } +} + +CCallsignListItem::CCallsignListItem(const CCallsign &callsign, const char *url, const char *modules) +{ + m_Callsign = callsign; + ::strncpy(m_szUrl, url, URL_MAXLEN); + m_Ip = CIp(m_szUrl); + if ( modules != nullptr ) + { + ::memset(m_Modules, 0, sizeof(m_Modules)); + if ( modules[0] == '*' ) + { + for ( char i = 0; i < NB_OF_MODULES; i++ ) + { + m_Modules[i] = 'A' + i; + } + } + else + { + int n = MIN(::strlen(modules), sizeof(m_Modules)-1); + int j = 0; + for ( int i = 0; i < n; i++ ) + { + if ( (modules[i] - 'A') < NB_OF_MODULES ) + { + m_Modules[j++] = modules[i]; + } + } + } + } +} + +CCallsignListItem::CCallsignListItem(const CCallsignListItem &item) +{ + m_Callsign = item.m_Callsign; + ::memcpy(m_szUrl, item.m_szUrl, sizeof(m_szUrl)); + m_Ip = item.m_Ip; + ::memcpy(m_Modules, item.m_Modules, sizeof(m_Modules)); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// compare + +bool CCallsignListItem::HasSameCallsign(const CCallsign &callsign) const +{ + return m_Callsign.HasSameCallsign(callsign); +} + +bool CCallsignListItem::HasSameCallsignWithWildcard(const CCallsign &callsign) const +{ + return m_Callsign.HasSameCallsignWithWildcard(callsign); +} + +bool CCallsignListItem::HasModuleListed(char module) const +{ + return (::strchr(m_Modules, (int)module) != nullptr); +} + +bool CCallsignListItem::CheckListedModules(char *Modules) const +{ + bool listed = false; + + if ( Modules != nullptr ) + { + // build a list of common modules + char list[27]; + list[0] = 0; + // + for ( unsigned i = 0; i < ::strlen(Modules); i++ ) + { + if ( HasModuleListed(Modules[i]) ) + { + ::strncat(list, &(Modules[i]), 1); + listed = true; + } + } + ::strcpy(Modules, list); + } + return listed; +} diff --git a/src/CallsignListItem.h b/src/CallsignListItem.h new file mode 100644 index 0000000..cc33155 --- /dev/null +++ b/src/CallsignListItem.h @@ -0,0 +1,77 @@ +// +// CallsignListItem.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef ccallsignlistitem_h +#define ccallsignlistitem_h + +#include "Main.h" +#include "Callsign.h" +#include "IP.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define URL_MAXLEN 256 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CCallsignListItem +{ +public: + // constructor + CCallsignListItem(); + CCallsignListItem(const CCallsign &, const CIp &, const char *); + CCallsignListItem(const CCallsign &, const char *, const char *); + CCallsignListItem(const CCallsignListItem &); + + // destructor + virtual ~CCallsignListItem() {} + + // compare + bool HasSameCallsign(const CCallsign &) const; + bool HasSameCallsignWithWildcard(const CCallsign &) const; + bool HasModuleListed(char) const; + bool CheckListedModules(char*) const; + + // get + const CCallsign &GetCallsign(void) const { return m_Callsign; } + const CIp &GetIp(void) const { return m_Ip; } + const char *GetModules(void) { return m_Modules; } + + // update + void ResolveIp(void) { m_Ip = CIp(m_szUrl); } + +protected: + // data + CCallsign m_Callsign; + char m_szUrl[URL_MAXLEN+1]; + CIp m_Ip; + char m_Modules[27]; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// + +#endif /* ccallsignlistitem_h */ diff --git a/src/Client.cpp b/src/Client.cpp new file mode 100644 index 0000000..0ffbdb2 --- /dev/null +++ b/src/Client.cpp @@ -0,0 +1,122 @@ +// +// cclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Client.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CClient::CClient() +{ + m_ReflectorModule = ' '; + m_ModuleMastered = ' '; + m_LastKeepaliveTime.Now(); + m_ConnectTime = std::time(nullptr); + m_LastHeardTime = std::time(nullptr); +} + +CClient::CClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) +{ + m_ReflectorModule = reflectorModule; + m_Callsign = callsign; + m_Ip = ip; + m_ModuleMastered = ' '; + m_LastKeepaliveTime.Now(); + m_ConnectTime = std::time(nullptr); + m_LastHeardTime = std::time(nullptr); +} + +CClient::CClient(const CClient &client) +{ + m_Callsign = client.m_Callsign; + m_Ip = client.m_Ip; + m_ReflectorModule = client.m_ReflectorModule; + m_ModuleMastered = client.m_ModuleMastered; + m_LastKeepaliveTime = client.m_LastKeepaliveTime; + m_ConnectTime = client.m_ConnectTime; + m_LastHeardTime = client.m_LastHeardTime; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +void CClient::Alive(void) +{ + m_LastKeepaliveTime.Now(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CClient::operator ==(const CClient &client) const +{ + return ((client.m_Callsign == m_Callsign) && + (client.m_Ip == m_Ip) && + (client.m_ReflectorModule == m_ReflectorModule)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// reporting + +void CClient::WriteXml(std::ofstream &xmlFile) +{ + xmlFile << "" << std::endl; + xmlFile << "\t" << m_Callsign << "" << std::endl; + xmlFile << "\t" << m_Ip.GetAddress() << "" << std::endl; + xmlFile << "\t" << m_ReflectorModule << "" << std::endl; + xmlFile << "\t" << GetProtocolName() << "" << std::endl; + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_ConnectTime))) + { + xmlFile << "\t" << mbstr << "" << std::endl; + } + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + xmlFile << "\t" << mbstr << "" << std::endl; + } + xmlFile << "" << std::endl; +} + +void CClient::GetJsonObject(char *Buffer) +{ + char sz[512]; + char mbstr[100]; + char cs[16]; + + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + m_Callsign.GetCallsignString(cs); + + ::sprintf(sz, "{\"callsign\":\"%s\",\"module\":\"%c\",\"linkedto\":\"%c\",\"time\":\"%s\"}", + cs, + m_Callsign.GetModule(), + m_ReflectorModule, + mbstr); + ::strcat(Buffer, sz); + } +} diff --git a/src/Client.h b/src/Client.h new file mode 100644 index 0000000..27eaf90 --- /dev/null +++ b/src/Client.h @@ -0,0 +1,103 @@ +// +// Client.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cclient_h +#define cclient_h + +#include "Timer.h" +#include "IP.h" +#include "Callsign.h" +#include "Buffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CClient +{ +public: + // constructors + CClient(); + CClient(const CCallsign &, const CIp &, char = ' '); + CClient(const CClient &); + + // destructor + virtual ~CClient() {}; + + // operators + bool operator ==(const CClient &) const; + + // get + const CCallsign &GetCallsign(void) const { return m_Callsign; } + const CIp &GetIp(void) const { return m_Ip; } + bool HasModule(void) const { return m_Callsign.HasModule(); } + char GetModule(void) const { return m_Callsign.GetModule(); } + bool HasReflectorModule(void) const { return m_ReflectorModule != ' '; } + char GetReflectorModule(void) const { return m_ReflectorModule; } + + // set + void SetModule(char c) { m_Callsign.SetModule(c); } + void SetReflectorModule(char c) { m_ReflectorModule = c; } + + // identity + virtual int GetProtocol(void) const { return PROTOCOL_NONE; } + virtual int GetProtocolRevision(void) const { return 0; } + virtual int GetCodec(void) const { return CODEC_NONE; } + virtual const char *GetProtocolName(void) const { return "none"; } + virtual bool IsNode(void) const { return false; } + virtual bool IsPeer(void) const { return false; } + virtual bool IsDextraDongle(void) const { return false; } + virtual void SetDextraDongle(void) { } + + // status + virtual void Alive(void); + virtual bool IsAlive(void) const { return false; } + virtual bool IsAMaster(void) const { return (m_ModuleMastered != ' '); } + virtual void SetMasterOfModule(char c) { m_ModuleMastered = c; } + virtual void NotAMaster(void) { m_ModuleMastered = ' '; } + virtual void Heard(void) { m_LastHeardTime = std::time(nullptr); } + + // reporting + virtual void WriteXml(std::ofstream &); + virtual void GetJsonObject(char *); + +protected: + // data + CCallsign m_Callsign; + CIp m_Ip; + + // linked to + char m_ReflectorModule; + + // status + char m_ModuleMastered; + CTimePoint m_LastKeepaliveTime; + std::time_t m_ConnectTime; + std::time_t m_LastHeardTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cclient_h */ diff --git a/src/Clients.cpp b/src/Clients.cpp new file mode 100644 index 0000000..5bdba73 --- /dev/null +++ b/src/Clients.cpp @@ -0,0 +1,247 @@ +// +// cclients.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Reflector.h" +#include "Clients.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CClients::CClients() +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructors + +CClients::~CClients() +{ + m_Mutex.lock(); + m_Clients.clear(); + m_Mutex.unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// manage Clients + +void CClients::AddClient(std::shared_ptr client) +{ + // first check if client already exists + for ( auto it=begin(); it!=end(); it++ ) + { + if (*client == *(*it)) + // if found, just do nothing + // so *client keep pointing on a valid object + // on function return + { + // delete new one + return; + } + } + + // and append + m_Clients.push_back(client); + std::cout << "New client " << client->GetCallsign() << " at " << client->GetIp() << " added with protocol " << client->GetProtocolName(); + if ( client->GetReflectorModule() != ' ' ) + { + std::cout << " on module " << client->GetReflectorModule(); + } + std::cout << std::endl; + // notify + g_Reflector.OnClientsChanged(); +} + +void CClients::RemoveClient(std::shared_ptr client) +{ + // look for the client + bool found = false; + for ( auto it=begin(); it!=end(); it++ ) + { + // compare objetc pointers + if ( *it == client ) + { + // found it ! + if ( !(*it)->IsAMaster() ) + { + // remove it + std::cout << "Client " << (*it)->GetCallsign() << " at " << (*it)->GetIp() << " removed with protocol " << (*it)->GetProtocolName(); + if ( (*it)->GetReflectorModule() != ' ' ) + { + std::cout << " on module " << (*it)->GetReflectorModule(); + } + std::cout << std::endl; + m_Clients.erase(it); + // notify + g_Reflector.OnClientsChanged(); + break; + } + } + } +} + +bool CClients::IsClient(std::shared_ptr client) const +{ + for ( auto it=cbegin(); it!=cend(); it++ ) + { + if (*it == client) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// find Clients + +std::shared_ptr CClients::FindClient(const CIp &Ip) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( (*it)->GetIp() == Ip ) + { + return *it; + } + } + + // done + return nullptr; +} + +std::shared_ptr CClients::FindClient(const CIp &Ip, int Protocol) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( ((*it)->GetIp() == Ip) && ((*it)->GetProtocol() == Protocol)) + { + return *it; + } + } + + // done + return nullptr; +} + +std::shared_ptr CClients::FindClient(const CIp &Ip, int Protocol, char ReflectorModule) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( ((*it)->GetIp() == Ip) && ((*it)->GetReflectorModule() == ReflectorModule) && ((*it)->GetProtocol() == Protocol) ) + { + return *it; + } + } + + // done + return nullptr; +} + +std::shared_ptr CClients::FindClient(const CCallsign &Callsign, const CIp &Ip, int Protocol) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( (*it)->GetCallsign().HasSameCallsign(Callsign) && ((*it)->GetIp() == Ip) && ((*it)->GetProtocol() == Protocol) ) + { + return *it; + } + } + + return nullptr; +} + +std::shared_ptr CClients::FindClient(const CCallsign &Callsign, char module, const CIp &Ip, int Protocol) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( (*it)->GetCallsign().HasSameCallsign(Callsign) && ((*it)->GetModule() == module) && ((*it)->GetIp() == Ip) && ((*it)->GetProtocol() == Protocol) ) + { + return *it; + } + } + + return nullptr; +} + +std::shared_ptr CClients::FindClient(const CCallsign &Callsign, int Protocol) +{ + // find client + for ( auto it=begin(); it!=end(); it++ ) + { + if ( ((*it)->GetProtocol() == Protocol) && (*it)->GetCallsign().HasSameCallsign(Callsign) ) + { + return *it; + } + } + + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// iterate on clients + +std::shared_ptr CClients::FindNextClient(int Protocol, std::list>::iterator &it) +{ + while ( it != end() ) + { + if ( (*it)->GetProtocol() == Protocol ) + { + return *it++; + } + it++; + } + return nullptr; +} + +std::shared_ptr CClients::FindNextClient(const CIp &Ip, int Protocol, std::list>::iterator &it) +{ + while ( it != end() ) + { + if ( ((*it)->GetProtocol() == Protocol) && ((*it)->GetIp() == Ip) ) + { + return *it++; + } + it++; + } + return nullptr; +} + +std::shared_ptr CClients::FindNextClient(const CCallsign &Callsign, const CIp &Ip, int Protocol, std::list>::iterator &it) +{ + while ( it != end() ) + { + if ( ((*it)->GetProtocol() == Protocol) && ((*it)->GetIp() == Ip) && (*it)->GetCallsign().HasSameCallsign(Callsign) ) + { + return *it++; + } + it++; + } + return nullptr; +} diff --git a/src/Clients.h b/src/Clients.h new file mode 100644 index 0000000..1b9f219 --- /dev/null +++ b/src/Clients.h @@ -0,0 +1,85 @@ +// +// Clients.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cclients_h +#define cclients_h + +#include "Client.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CClients +{ +public: + // constructors + CClients(); + + // destructors + virtual ~CClients(); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // manage Clients + int GetSize(void) const { return (int)m_Clients.size(); } + void AddClient(std::shared_ptr); + void RemoveClient(std::shared_ptr); + bool IsClient(std::shared_ptr) const; + + // pass-thru + std::list>::iterator begin() { return m_Clients.begin(); } + std::list>::iterator end() { return m_Clients.end(); } + std::list>::const_iterator cbegin() const { return m_Clients.cbegin(); } + std::list>::const_iterator cend() const { return m_Clients.cend(); } + + // find clients + std::shared_ptr FindClient(const CIp &); + std::shared_ptr FindClient(const CIp &, int); + std::shared_ptr FindClient(const CIp &, int, char); + std::shared_ptr FindClient(const CCallsign &, const CIp &, int); + std::shared_ptr FindClient(const CCallsign &, char, const CIp &, int); + std::shared_ptr FindClient(const CCallsign &, int); + + // iterate on clients + std::shared_ptr FindNextClient(int, std::list>::iterator &); + std::shared_ptr FindNextClient(const CIp &, int, std::list>::iterator &); + std::shared_ptr FindNextClient(const CCallsign &, const CIp &, int, std::list>::iterator &); + +protected: + // data + std::mutex m_Mutex; + std::list> m_Clients; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cclients_h */ diff --git a/src/CodecStream.cpp b/src/CodecStream.cpp new file mode 100644 index 0000000..df48f3f --- /dev/null +++ b/src/CodecStream.cpp @@ -0,0 +1,266 @@ +// +// ccodecstream.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "CodecStream.h" +#include "DVFramePacket.h" +#include "Reflector.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CCodecStream::CCodecStream(CPacketStream *PacketStream, uint16 uiId, uint8 uiCodecIn, uint8 uiCodecOut) +{ + keep_running = true; + m_uiStreamId = uiId; + m_uiPid = 0; + m_uiCodecIn = uiCodecIn; + m_uiCodecOut = uiCodecOut; + m_bConnected = false; + m_fPingMin = -1; + m_fPingMax = -1; + m_fPingSum = 0; + m_fPingCount = 0; + m_uiTotalPackets = 0; + m_uiTimeoutPackets = 0; + m_PacketStream = PacketStream; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CCodecStream::~CCodecStream() +{ + // close socket + m_Socket.Close(); + + // kill threads + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// initialization + +bool CCodecStream::Init(uint16 uiPort) +{ + m_bConnected = keep_running = false; // prepare for the worst + + // create the send to address + m_uiPort = uiPort; + auto s = g_Reflector.GetTranscoderIp(); + m_Ip.Initialize(strchr(s, ':') ? AF_INET6 : AF_INET, m_uiPort, s); + + if (0 == strncasecmp(s, "none", 4)) + { + return false; // the user has disabled the transcoder + } + + // create socket address, family based on transcoder listen address +#ifdef LISTEN_IPV4 +#ifdef LISTEN_IPV6 + const auto paddr = (AF_INET == m_Ip.GetFamily()) ? g_Reflector.m_Address.GetV4Address(PROTOCOL_ANY) : g_Reflector.m_Address.GetV6Address(PROTOCOL_ANY); +#else + const auto paddr = g_Reflector.m_Address.GetV4Address(PROTOCOL_ANY); +#endif +#else + const auto paddr = g_Reflector.m_Address.GetV6Address(PROTOCOL_ANY); +#endif + CIp ip(m_Ip.GetFamily(), m_uiPort, paddr.c_str()); + + // create our socket + if (ip.IsSet()) + { + if (! m_Socket.Open(ip)) + { + std::cerr << "Error opening socket on IP address " << m_Ip << std::endl; + return false; + } + } + else + { + std::cerr << "Could not initialize Codec Stream on " << paddr << std::endl; + return false; + } + + keep_running = m_bConnected = true; + m_Future = std::async(std::launch::async, &CCodecStream::Thread, this); + + return true; +} + +void CCodecStream::Close(void) +{ + // close socket + keep_running = m_bConnected = false; + m_Socket.Close(); + + // kill threads + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +bool CCodecStream::IsEmpty(void) const +{ + return (m_LocalQueue.empty() && m_PacketStream->empty()); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CCodecStream::Thread() +{ + while (keep_running) + { + Task(); + } +} + +void CCodecStream::Task(void) +{ + CBuffer Buffer; + CIp Ip; + uint8 Ambe[AMBE_SIZE]; + uint8 DStarSync[] = { 0x55,0x2D,0x16 }; + + // any packet from transcoder + if ( m_Socket.Receive(Buffer, Ip, 5) ) + { + // crack + if ( IsValidAmbePacket(Buffer, Ambe) ) + { + // tickle + m_TimeoutTimer.Now(); + + // update statistics + double ping = m_StatsTimer.DurationSinceNow(); + if ( m_fPingMin == -1 ) + { + m_fPingMin = ping; + m_fPingMax = ping; + + } + else + { + m_fPingMin = MIN(m_fPingMin, ping); + m_fPingMax = MAX(m_fPingMax, ping); + + } + m_fPingSum += ping; + m_fPingCount += 1; + + // pop the original packet + if ( !m_LocalQueue.empty() ) + { + auto Packet = m_LocalQueue.front(); + auto Frame = (CDvFramePacket *)Packet.get(); + m_LocalQueue.pop(); + // todo: check the PID + // update content with transcoded ambe + Frame->SetAmbe(m_uiCodecOut, Ambe); + // tag syncs in DvData + if ( (m_uiCodecOut == CODEC_AMBEPLUS) && (Frame->GetPacketId() % 21) == 0 ) + { + Frame->SetDvData(DStarSync); + } + // and push it back to client + m_PacketStream->Lock(); + m_PacketStream->push(Packet); + m_PacketStream->Unlock(); + } + else + { + std::cout << "Unexpected transcoded packet received from ambed" << std::endl; + } + } + } + + // anything in our queue + while ( !empty() ) + { + // yes, pop it from queue + auto Packet = front(); + auto Frame = (CDvFramePacket *)Packet.get(); + pop(); + + // yes, send to ambed + // this assume that thread pushing the Packet + // have verified that the CodecStream is connected + // and that the packet needs transcoding + m_StatsTimer.Now(); + m_uiTotalPackets++; + EncodeAmbePacket(&Buffer, Frame->GetAmbe(m_uiCodecIn)); + m_Socket.Send(Buffer, m_Ip, m_uiPort); + + // and push to our local queue + m_LocalQueue.push(Packet); + } + + // handle timeout + if ( !m_LocalQueue.empty() && (m_TimeoutTimer.DurationSinceNow() >= (TRANSCODER_AMBEPACKET_TIMEOUT/1000.0f)) ) + { + //std::cout << "ambed packet timeout" << std::endl; + m_uiTimeoutPackets++; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +/// packet decoding helpers + +bool CCodecStream::IsValidAmbePacket(const CBuffer &Buffer, uint8 *Ambe) +{ + bool valid = false; + + if ( (Buffer.size() == 11) && (Buffer.data()[0] == m_uiCodecOut) ) + { + ::memcpy(Ambe, &(Buffer.data()[2]), 9); + valid = true; + } + return valid; +} + +//////////////////////////////////////////////////////////////////////////////////////// +/// packet encoding helpers + +void CCodecStream::EncodeAmbePacket(CBuffer *Buffer, const uint8 *Ambe) +{ + Buffer->clear(); + Buffer->Append(m_uiCodecIn); + Buffer->Append(m_uiPid); + Buffer->Append((uint8 *)Ambe, 9); +} diff --git a/src/CodecStream.h b/src/CodecStream.h new file mode 100644 index 0000000..3d08609 --- /dev/null +++ b/src/CodecStream.h @@ -0,0 +1,116 @@ +// +// CodecStream.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef ccodecstream_h +#define ccodecstream_h + +#include "Semaphore.h" +#include "UDPSocket.h" +#include "PacketQueue.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +// frame sizes +#define AMBE_SIZE 9 +#define AMBEPLUS_SIZE 9 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPacketStream; + +class CCodecStream : public CPacketQueue +{ +public: + // constructor + CCodecStream(CPacketStream *, uint16, uint8, uint8); + + // destructor + virtual ~CCodecStream(); + + // initialization + bool Init(uint16); + void Close(void); + + // get + bool IsConnected(void) const { return m_bConnected; } + uint16 GetStreamId(void) const { return m_uiStreamId; } + double GetPingMin(void) const { return m_fPingMin; } + double GetPingMax(void) const { return m_fPingMax; } + double GetPingAve(void) const { return (m_fPingCount != 0) ? m_fPingSum/m_fPingCount : 0; } + uint32 GetTotalPackets(void) const { return m_uiTotalPackets; } + uint32 GetTimeoutPackets(void) const { return m_uiTimeoutPackets; } + bool IsEmpty(void) const; + + // task + void Thread(void); + void Task(void); + + +protected: + // packet decoding helpers + bool IsValidAmbePacket(const CBuffer &, uint8 *); + + // packet encoding helpers + void EncodeAmbePacket(CBuffer *, const uint8 *); + + +protected: + // data + uint16 m_uiStreamId; + uint16 m_uiPort; + uint8 m_uiPid; + uint8 m_uiCodecIn; + uint8 m_uiCodecOut; + + // socket + CIp m_Ip; + CUdpSocket m_Socket; + bool m_bConnected; + + // associated packet stream + CPacketStream *m_PacketStream; + CPacketQueue m_LocalQueue; + + // thread + std::atomic keep_running; + std::future m_Future; + CTimePoint m_TimeoutTimer; + CTimePoint m_StatsTimer; + + // statistics + double m_fPingMin; + double m_fPingMax; + double m_fPingSum; + double m_fPingCount; + uint32 m_uiTotalPackets; + uint32 m_uiTimeoutPackets; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* ccodecstream_h */ diff --git a/src/DCSClient.cpp b/src/DCSClient.cpp new file mode 100644 index 0000000..2075384 --- /dev/null +++ b/src/DCSClient.cpp @@ -0,0 +1,52 @@ +// +// cdcsclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DCSClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CDcsClient::CDcsClient() +{ +} + +CDcsClient::CDcsClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CDcsClient::CDcsClient(const CDcsClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDcsClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < DCS_KEEPALIVE_TIMEOUT); +} diff --git a/src/DCSClient.h b/src/DCSClient.h new file mode 100644 index 0000000..bf58065 --- /dev/null +++ b/src/DCSClient.h @@ -0,0 +1,60 @@ +// +// DCSClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdcsclient_h +#define cdcsclient_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDcsClient : public CClient +{ +public: + // constructors + CDcsClient(); + CDcsClient(const CCallsign &, const CIp &, char = ' '); + CDcsClient(const CDcsClient &); + + // destructor + virtual ~CDcsClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_DCS; } + const char *GetProtocolName(void) const { return "DCS"; } + int GetCodec(void) const { return CODEC_AMBEPLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#endif /* cdcsclient_h */ diff --git a/src/DCSProtocol.cpp b/src/DCSProtocol.cpp new file mode 100644 index 0000000..c09476d --- /dev/null +++ b/src/DCSProtocol.cpp @@ -0,0 +1,513 @@ +// +// cdcsprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "DCSClient.h" +#include "cdcsprotocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CDcsProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + + // done + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CDcsProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + char ToLinkModule; + std::unique_ptr Header; + std::unique_ptr Frame; + + // handle incoming packets +#if DSTAR_IPV6==true +#if DSTAR_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvPacket(Buffer, Header, Frame) ) + { + // callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DCS, Header->GetRpt2Module()) ) + { + OnDvHeaderPacketIn(Header, Ip); + + if ( !Frame->IsLastPacket() ) + { + //std::cout << "DCS DV frame" << std::endl; + OnDvFramePacketIn(Frame, &Ip); + } + else + { + //std::cout << "DCS DV last frame" << std::endl; + OnDvLastFramePacketIn((std::unique_ptr &)Frame, &Ip); + } + } + } + else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule) ) + { + std::cout << "DCS connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DCS) && g_Reflector.IsValidModule(ToLinkModule) ) + { + // valid module ? + if ( g_Reflector.IsValidModule(ToLinkModule) ) + { + // acknowledge the request + EncodeConnectAckPacket(Callsign, ToLinkModule, &Buffer); + Send(Buffer, Ip); + + // create the client and append + g_Reflector.GetClients()->AddClient(std::make_shared(Callsign, Ip, ToLinkModule)); + g_Reflector.ReleaseClients(); + } + else + { + std::cout << "DCS node " << Callsign << " connect attempt on non-existing module" << std::endl; + + // deny the request + EncodeConnectNackPacket(Callsign, ToLinkModule, &Buffer); + Send(Buffer, Ip); + } + } + else + { + // deny the request + EncodeConnectNackPacket(Callsign, ToLinkModule, &Buffer); + Send(Buffer, Ip); + } + + } + else if ( IsValidDisconnectPacket(Buffer, &Callsign) ) + { + std::cout << "DCS disconnect packet from " << Callsign << " at " << Ip << std::endl; + + // find client + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Ip, PROTOCOL_DCS); + if ( client != nullptr ) + { + // remove it + clients->RemoveClient(client); + // and acknowledge the disconnect + EncodeConnectNackPacket(Callsign, ' ', &Buffer); + Send(Buffer, Ip); + } + g_Reflector.ReleaseClients(); + } + else if ( IsValidKeepAlivePacket(Buffer, &Callsign) ) + { + //std::cout << "DCS keepalive packet from " << Callsign << " at " << Ip << std::endl; + + // find all clients with that callsign & ip and keep them alive + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DCS, it)) != nullptr ) + { + client->Alive(); + } + g_Reflector.ReleaseClients(); + } + else if ( IsIgnorePacket(Buffer) ) + { + // valid but ignore packet + //std::cout << "DCS ignored packet from " << Ip << std::endl; + } + else + { + // invalid packet + std::string title("Unknown DCS packet from "); + title += Ip.GetAddress(); + Buffer.Dump(title); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + // keep client alive + if ( m_LastKeepaliveTime.DurationSinceNow() > DCS_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CDcsProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + // no stream open yet, open a new one + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DCS); + if ( client ) + { + // get client callsign + rpt1 = client->GetCallsign(); + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + } + // release + g_Reflector.ReleaseClients(); + + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CDcsProtocol::HandleQueue(void) +{ + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // get our sender's id + int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); + + // check if it's header and update cache + if ( packet->IsDvHeader() ) + { + // this relies on queue feeder setting valid module id + m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet); + m_StreamsCache[iModId].m_iSeqCounter = 0; + } + else + { + // encode it + CBuffer buffer; + if ( packet->IsLastPacket() ) + { + EncodeDvLastPacket( + m_StreamsCache[iModId].m_dvHeader, + (const CDvFramePacket &)*packet, + m_StreamsCache[iModId].m_iSeqCounter++, + &buffer); + } + else if ( packet->IsDvFrame() ) + { + EncodeDvPacket( + m_StreamsCache[iModId].m_dvHeader, + (const CDvFramePacket &)*packet, + m_StreamsCache[iModId].m_iSeqCounter++, + &buffer); + } + + // send it + if ( buffer.size() > 0 ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DCS, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + Send(buffer, client->GetIp()); + + } + } + g_Reflector.ReleaseClients(); + } + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CDcsProtocol::HandleKeepalives(void) +{ + // DCS protocol sends and monitors keepalives packets + // event if the client is currently streaming + // so, send keepalives to all + CBuffer keepalive1; + EncodeKeepAlivePacket(&keepalive1); + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DCS, it)) != nullptr ) + { + // encode client's specific keepalive packet + CBuffer keepalive2; + EncodeKeepAlivePacket(&keepalive2, client); + + // send keepalive + Send(keepalive1, client->GetIp()); + Send(keepalive2, client->GetIp()); + + // is this client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // check it's still with us + else if ( !client->IsAlive() ) + { + // no, disconnect + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect, client); + Send(disconnect, client->GetIp()); + + // remove it + std::cout << "DCS client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CDcsProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule) +{ + bool valid = false; + if ( Buffer.size() == 519 ) + { + callsign->SetCallsign(Buffer.data(), 8); + callsign->SetModule(Buffer.data()[8]); + *reflectormodule = Buffer.data()[9]; + valid = (callsign->IsValid() && IsLetter(*reflectormodule)); + } + return valid; +} + +bool CDcsProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if ((Buffer.size() == 11) && (Buffer.data()[9] == ' ')) + { + callsign->SetCallsign(Buffer.data(), 8); + callsign->SetModule(Buffer.data()[8]); + valid = callsign->IsValid(); + } + else if ((Buffer.size() == 19) && (Buffer.data()[9] == ' ') && (Buffer.data()[10] == 0x00)) + { + callsign->SetCallsign(Buffer.data(), 8); + callsign->SetModule(Buffer.data()[8]); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDcsProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if ( (Buffer.size() == 17) || (Buffer.size() == 15) || (Buffer.size() == 22) ) + { + callsign->SetCallsign(Buffer.data(), 8); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDcsProtocol::IsValidDvPacket(const CBuffer &Buffer, std::unique_ptr &header, std::unique_ptr &frame) +{ + uint8 tag[] = { '0','0','0','1' }; + + if ( (Buffer.size() >= 100) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // get the header + header = std::unique_ptr(new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[4]), *((uint16 *)&(Buffer.data()[43])), 0x80)); + + // get the frame + if ( Buffer.data()[45] & 0x40U ) + { + // it's the last frame + frame = std::unique_ptr(new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[46]), *((uint16 *)&(Buffer.data()[43])), Buffer.data()[45])); + } + else + { + // it's a regular DV frame + frame = std::unique_ptr(new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[46]), *((uint16 *)&(Buffer.data()[43])), Buffer.data()[45])); + } + + // check validity of packets + if ( header && header->IsValid() && frame && frame->IsValid() ) + return true; + } + return false; +} + +bool CDcsProtocol::IsIgnorePacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, }; + + if ( Buffer.size() == 15 && Buffer.Compare(tag, sizeof(tag)) == 0 ) + return true; + return false; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CDcsProtocol::EncodeKeepAlivePacket(CBuffer *Buffer) +{ + Buffer->Set(GetReflectorCallsign()); +} + +void CDcsProtocol::EncodeKeepAlivePacket(CBuffer *Buffer, std::shared_ptrClient) +{ + uint8 tag[] = { 0x0A,0x00,0x20,0x20 }; + + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN-1); + Buffer->Append((uint8)Client->GetReflectorModule()); + Buffer->Append((uint8)' '); + Buffer->Append((uint8 *)(const char *)Client->GetCallsign(), CALLSIGN_LEN-1); + Buffer->Append((uint8)Client->GetModule()); + Buffer->Append((uint8)Client->GetModule()); + Buffer->Append(tag, sizeof(tag)); +} + +void CDcsProtocol::EncodeConnectAckPacket(const CCallsign &Callsign, char ReflectorModule, CBuffer *Buffer) +{ + uint8 tag[] = { 'A','C','K',0x00 }; + uint8 cs[CALLSIGN_LEN]; + + Callsign.GetCallsign(cs); + Buffer->Set(cs, CALLSIGN_LEN-1); + Buffer->Append((uint8)' '); + Buffer->Append((uint8)Callsign.GetModule()); + Buffer->Append((uint8)ReflectorModule); + Buffer->Append(tag, sizeof(tag)); +} + +void CDcsProtocol::EncodeConnectNackPacket(const CCallsign &Callsign, char ReflectorModule, CBuffer *Buffer) +{ + uint8 tag[] = { 'N','A','K',0x00 }; + uint8 cs[CALLSIGN_LEN]; + + Callsign.GetCallsign(cs); + Buffer->Set(cs, CALLSIGN_LEN-1); + Buffer->Append((uint8)' '); + Buffer->Append((uint8)Callsign.GetModule()); + Buffer->Append((uint8)ReflectorModule); + Buffer->Append(tag, sizeof(tag)); +} + +void CDcsProtocol::EncodeDisconnectPacket(CBuffer *Buffer, std::shared_ptrClient) +{ + Buffer->Set((uint8 *)(const char *)Client->GetCallsign(), CALLSIGN_LEN-1); + Buffer->Append((uint8)' '); + Buffer->Append((uint8)Client->GetModule()); + Buffer->Append((uint8)0x00); + Buffer->Append((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN-1); + Buffer->Append((uint8)' '); + Buffer->Append((uint8)0x00); +} + +void CDcsProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32 iSeq, CBuffer *Buffer) const +{ + uint8 tag[] = { '0','0','0','1' }; + struct dstar_header DstarHeader; + + Header.ConvertToDstarStruct(&DstarHeader); + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header) - sizeof(uint16)); + Buffer->Append(DvFrame.GetStreamId()); + Buffer->Append((uint8)(DvFrame.GetPacketId() % 21)); + Buffer->Append((uint8 *)DvFrame.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)DvFrame.GetDvData(), DVDATA_SIZE); + Buffer->Append((uint8)((iSeq >> 0) & 0xFF)); + Buffer->Append((uint8)((iSeq >> 8) & 0xFF)); + Buffer->Append((uint8)((iSeq >> 16) & 0xFF)); + Buffer->Append((uint8)0x01); + Buffer->Append((uint8)0x00, 38); +} + +void CDcsProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Header, const CDvFramePacket &DvFrame, uint32 iSeq, CBuffer *Buffer) const +{ + EncodeDvPacket(Header, DvFrame, iSeq, Buffer); + (Buffer->data())[45] |= 0x40; +} diff --git a/src/DCSProtocol.h b/src/DCSProtocol.h new file mode 100644 index 0000000..d802bcc --- /dev/null +++ b/src/DCSProtocol.h @@ -0,0 +1,94 @@ +// +// cdcsprotocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdcsprotocol_h +#define cdcsprotocol_h + +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDcsStreamCacheItem +{ +public: + CDcsStreamCacheItem() { m_iSeqCounter = 0; } + ~CDcsStreamCacheItem() {} + + CDvHeaderPacket m_dvHeader; + uint32 m_iSeqCounter; +}; + +class CDcsProtocol : public CProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // packet decoding helpers + bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *); + bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); + bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); + bool IsValidDvPacket(const CBuffer &, std::unique_ptr &, std::unique_ptr &); + bool IsIgnorePacket(const CBuffer &); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *); + void EncodeKeepAlivePacket(CBuffer *, std::shared_ptr); + void EncodeConnectAckPacket(const CCallsign &, char, CBuffer *); + void EncodeConnectNackPacket(const CCallsign &, char, CBuffer *); + void EncodeDisconnectPacket(CBuffer *, std::shared_ptr); + void EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket &, uint32, CBuffer *) const; + void EncodeDvLastPacket(const CDvHeaderPacket &, const CDvFramePacket &, uint32, CBuffer *) const; + +protected: + // for keep alive + CTimePoint m_LastKeepaliveTime; + + // for queue header caches + std::array m_StreamsCache; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdcsprotocol_h */ diff --git a/src/DExtraClient.cpp b/src/DExtraClient.cpp new file mode 100644 index 0000000..20fa003 --- /dev/null +++ b/src/DExtraClient.cpp @@ -0,0 +1,55 @@ +// +// cdextraclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DExtraClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CDextraClient::CDextraClient() +{ + m_ProtRev = 0; +} + +CDextraClient::CDextraClient(const CCallsign &callsign, const CIp &ip, char reflectorModule, int protRev) + : CClient(callsign, ip, reflectorModule) +{ + m_ProtRev = protRev; +} + +CDextraClient::CDextraClient(const CDextraClient &client) + : CClient(client) +{ + m_ProtRev = client.m_ProtRev; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDextraClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < DEXTRA_KEEPALIVE_TIMEOUT); +} diff --git a/src/DExtraClient.h b/src/DExtraClient.h new file mode 100644 index 0000000..13ff2f0 --- /dev/null +++ b/src/DExtraClient.h @@ -0,0 +1,64 @@ +// +// DExtraClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdextraclient_h +#define cdextraclient_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDextraClient : public CClient +{ +public: + // constructors + CDextraClient(); + CDextraClient(const CCallsign &, const CIp &, char = ' ', int = 0); + CDextraClient(const CDextraClient &); + + // destructor + virtual ~CDextraClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_DEXTRA; } + int GetProtocolRevision(void) const { return m_ProtRev; } + const char *GetProtocolName(void) const { return "DExtra"; } + int GetCodec(void) const { return CODEC_AMBEPLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; + +protected: + // data + int m_ProtRev; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdextraclient_h */ diff --git a/src/DExtraPeer.cpp b/src/DExtraPeer.cpp new file mode 100644 index 0000000..ffb547d --- /dev/null +++ b/src/DExtraPeer.cpp @@ -0,0 +1,73 @@ +// +// cdextrapeer.cpp +// xlxd +// +// Created by Antony Chazapis (SV9OAN) on 25/2/2018. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "DExtraPeer.h" +#include "DExtraClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CDextraPeer::CDextraPeer() +{ +} + +CDextraPeer::CDextraPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) + : CPeer(callsign, ip, modules, version) +{ + std::cout << "Adding DExtra peer" << std::endl; + + // and construct the DExtra clients + for ( unsigned i = 0; i < ::strlen(modules); i++ ) + { + // create and append to vector + m_Clients.push_back(std::make_shared(callsign, ip, modules[i], version.GetMajor())); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDextraPeer::IsAlive(void) const +{ + for ( auto it=cbegin(); it!=cend(); it++ ) + { + if (! (*it)->IsAlive()) + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CDextraPeer::GetProtocolRevision(const CVersion &version) +{ + return version.GetMajor(); +} diff --git a/src/DExtraPeer.h b/src/DExtraPeer.h new file mode 100644 index 0000000..be1b571 --- /dev/null +++ b/src/DExtraPeer.h @@ -0,0 +1,58 @@ +// +// DExtraPeer.h +// xlxd +// +// Created by Antony Chazapis (SV9OAN) on 25/2/2018. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdextrapeer_h +#define cdextrapeer_h + +#include "Peer.h" +#include "DExtraClient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDextraPeer : public CPeer +{ +public: + // constructors + CDextraPeer(); + CDextraPeer(const CCallsign &, const CIp &, const char *, const CVersion &); + CDextraPeer(const CDextraPeer &) = delete; + + // status + bool IsAlive(void) const; + + // identity + int GetProtocol(void) const { return PROTOCOL_DEXTRA; } + const char *GetProtocolName(void) const { return "DExtra"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdextrapeer_h */ diff --git a/src/DExtraProtocol.cpp b/src/DExtraProtocol.cpp new file mode 100644 index 0000000..48c4f12 --- /dev/null +++ b/src/DExtraProtocol.cpp @@ -0,0 +1,641 @@ +// +// cdextraprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "DExtraPeer.h" +#include "DExtraClient.h" +#include "DExtraProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CDextraProtocol::Initialize(const char *type, int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + m_LastPeersLinkTime.Now(); + + // done + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CDextraProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + char ToLinkModule; + int ProtRev; + std::unique_ptr Header; + std::unique_ptr Frame; + std::unique_ptr LastFrame; + + // any incoming packet ? +#if DSTAR_IPV6==true +#if DSTAR_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvFramePacket(Buffer, Frame) ) + { + OnDvFramePacketIn(Frame, &Ip); + } + else if ( IsValidDvHeaderPacket(Buffer, Header) ) + { + // callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DEXTRA, Header->GetRpt2Module()) ) + { + OnDvHeaderPacketIn(Header, Ip); + } + } + else if ( IsValidDvLastFramePacket(Buffer, LastFrame) ) + { + OnDvLastFramePacketIn(LastFrame, &Ip); + } + else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule, &ProtRev) ) + { + std::cout << "DExtra connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << " rev " << ProtRev << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DEXTRA) ) + { + // valid module ? + if ( g_Reflector.IsValidModule(ToLinkModule) ) + { + // is this an ack for a link request? + CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + CCallsignListItem *item = list->FindListItem(Callsign); + if ( item != nullptr && Callsign.GetModule() == item->GetModules()[1] && ToLinkModule == item->GetModules()[0] ) + { + std::cout << "DExtra ack packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // already connected ? + CPeers *peers = g_Reflector.GetPeers(); + if ( peers->FindPeer(Callsign, Ip, PROTOCOL_DEXTRA) == nullptr ) + { + // create the new peer + // this also create one client per module + // append the peer to reflector peer list + // this also add all new clients to reflector client list + peers->AddPeer(std::make_shared(Callsign, Ip, std::string(1, ToLinkModule).c_str(), CVersion(2, 0, 0))); + } + g_Reflector.ReleasePeers(); + } + else + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer, ProtRev); + Send(Buffer, Ip); + + // create the client and append + g_Reflector.GetClients()->AddClient(std::make_shared(Callsign, Ip, ToLinkModule, ProtRev)); + g_Reflector.ReleaseClients(); + } + g_GateKeeper.ReleasePeerList(); + } + else + { + std::cout << "DExtra node " << Callsign << " connect attempt on non-existing module" << std::endl; + + // deny the request + EncodeConnectNackPacket(&Buffer); + Send(Buffer, Ip); + } + } + else + { + // deny the request + EncodeConnectNackPacket(&Buffer); + Send(Buffer, Ip); + } + } + else if ( IsValidDisconnectPacket(Buffer, &Callsign) ) + { + std::cout << "DExtra disconnect packet from " << Callsign << " at " << Ip << std::endl; + + // find client & remove it + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Ip, PROTOCOL_DEXTRA); + if ( client != nullptr ) + { + // ack disconnect packet + if ( client->GetProtocolRevision() == 1 ) + { + EncodeDisconnectedPacket(&Buffer); + Send(Buffer, Ip); + } + else if ( client->GetProtocolRevision() == 2 ) + { + Send(Buffer, Ip); + } + // and remove it + clients->RemoveClient(client); + } + g_Reflector.ReleaseClients(); + } + else if ( IsValidKeepAlivePacket(Buffer, &Callsign) ) + { + //std::cout << "DExtra keepalive packet from " << Callsign << " at " << Ip << std::endl; + + // find all clients with that callsign & ip and keep them alive + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DEXTRA, it)) != nullptr ) + { + client->Alive(); + } + g_Reflector.ReleaseClients(); + } + else + { + std::string title("Unknown DExtra packet from "); + title += Ip.GetAddress(); + Buffer.Dump(title); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + // keep alive + if ( m_LastKeepaliveTime.DurationSinceNow() > DEXTRA_KEEPALIVE_PERIOD ) + { + // handle keep alives + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } + + // peer connections + if ( m_LastPeersLinkTime.DurationSinceNow() > DEXTRA_RECONNECT_PERIOD ) + { + // handle remote peers connections + HandlePeerLinks(); + + // update time + m_LastPeersLinkTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CDextraProtocol::HandleQueue(void) +{ + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // encode it + CBuffer buffer; + if ( EncodeDvPacket(*packet, &buffer) ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + int n = packet->IsDvHeader() ? 5 : 1; + for ( int i = 0; i < n; i++ ) + { + Send(buffer, client->GetIp()); + } + } + } + g_Reflector.ReleaseClients(); + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CDextraProtocol::HandleKeepalives(void) +{ + // DExtra protocol sends and monitors keepalives packets + // event if the client is currently streaming + // so, send keepalives to all + CBuffer keepalive; + EncodeKeepAlivePacket(&keepalive); + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DEXTRA, it)) != nullptr ) + { + // send keepalive + Send(keepalive, client->GetIp()); + + // client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // otherwise check if still with us + else if ( !client->IsAlive() ) + { + CPeers *peers = g_Reflector.GetPeers(); + std::shared_ptrpeer = peers->FindPeer(client->GetCallsign(), client->GetIp(), PROTOCOL_DEXTRA); + if ( peer != nullptr && peer->GetReflectorModules()[0] == client->GetReflectorModule() ) + { + // no, but this is a peer client, so it will be handled below + } + else + { + // no, disconnect + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect, client->GetReflectorModule()); + Send(disconnect, client->GetIp()); + + // remove it + std::cout << "DExtra client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + g_Reflector.ReleasePeers(); + } + + } + g_Reflector.ReleaseClients(); + + // iterate on peers + CPeers *peers = g_Reflector.GetPeers(); + auto pit = peers->begin(); + std::shared_ptrpeer = nullptr; + while ( (peer = peers->FindNextPeer(PROTOCOL_DEXTRA, pit)) != nullptr ) + { + // keepalives are sent between clients + + // some client busy or still with us ? + if ( !peer->IsAMaster() && !peer->IsAlive() ) + { + // no, disconnect all clients + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect, peer->GetReflectorModules()[0]); + CClients *clients = g_Reflector.GetClients(); + for ( auto cit=peer->cbegin(); cit!=peer->cend(); cit++ ) + { + Send(disconnect, (*cit)->GetIp()); + } + g_Reflector.ReleaseClients(); + + // remove it + std::cout << "DExtra peer " << peer->GetCallsign() << " keepalive timeout" << std::endl; + peers->RemovePeer(peer); + } + } + g_Reflector.ReleasePeers(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Peers helpers + +void CDextraProtocol::HandlePeerLinks(void) +{ + CBuffer buffer; + + // get the list of peers + CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + CPeers *peers = g_Reflector.GetPeers(); + + // check if all our connected peers are still listed by gatekeeper + // if not, disconnect + auto pit = peers->begin(); + std::shared_ptrpeer = nullptr; + while ( (peer = peers->FindNextPeer(PROTOCOL_DEXTRA, pit)) != nullptr ) + { + if ( list->FindListItem(peer->GetCallsign()) == nullptr ) + { + // send disconnect packet + EncodeDisconnectPacket(&buffer, peer->GetReflectorModules()[0]); + Send(buffer, peer->GetIp()); + std::cout << "Sending disconnect packet to XRF peer " << peer->GetCallsign() << " at " << peer->GetIp() << std::endl; + // remove client + peers->RemovePeer(peer); + } + } + + // check if all ours peers listed by gatekeeper are connected + // if not, connect or reconnect + for ( auto it=list->begin(); it!=list->end(); it++ ) + { + if ( !(*it).GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + continue; + if ( strlen((*it).GetModules()) != 2 ) + continue; + if ( peers->FindPeer((*it).GetCallsign(), PROTOCOL_DEXTRA) == nullptr ) + { + // resolve again peer's IP in case it's a dynamic IP + (*it).ResolveIp(); + // send connect packet to re-initiate peer link + EncodeConnectPacket(&buffer, (*it).GetModules()); + Send(buffer, (*it).GetIp(), DEXTRA_PORT); + std::cout << "Sending connect packet to XRF peer " << (*it).GetCallsign() << " @ " << (*it).GetIp() << " for module " << (*it).GetModules()[1] << " (module " << (*it).GetModules()[0] << ")" << std::endl; + } + } + + // done + g_Reflector.ReleasePeers(); + g_GateKeeper.ReleasePeerList(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CDextraProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + // no stream open yet, open a new one + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DEXTRA); + if ( client ) + { + // get client callsign + rpt1 = client->GetCallsign(); + // apply protocol revision details + if ( client->GetProtocolRevision() == 2 ) + { + // update Header RPT2 module letter with + // the module the client is linked to + auto m = client->GetReflectorModule(); + Header->SetRpt2Module(m); + rpt2.SetModule(m); + } + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + } + // release + g_Reflector.ReleaseClients(); + + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CDextraProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule, int *revision) +{ + bool valid = false; + if ((Buffer.size() == 11) && (Buffer.data()[9] != ' ')) + { + callsign->SetCallsign(Buffer.data(), 8); + callsign->SetModule(Buffer.data()[8]); + *reflectormodule = Buffer.data()[9]; + *revision = (Buffer.data()[10] == 11) ? 1 : 0; + valid = (callsign->IsValid() && IsLetter(*reflectormodule)); + // detect revision + if ( (Buffer.data()[10] == 11) ) + { + *revision = 1; + } + else if ( callsign->HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + { + *revision = 2; + } + else + { + *revision = 0; + } + } + return valid; +} + +bool CDextraProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if ((Buffer.size() == 11) && (Buffer.data()[9] == ' ')) + { + callsign->SetCallsign(Buffer.data(), 8); + callsign->SetModule(Buffer.data()[8]); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDextraProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if (Buffer.size() == 9) + { + callsign->SetCallsign(Buffer.data(), 8); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDextraProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, std::unique_ptr &header) +{ + if ( 56==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x10U==Buffer.data()[4] && 0x20U==Buffer.data()[8] ) + { + // create packet + header = std::unique_ptr(new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), 0x80)); + // check validity of packet + if ( header && header->IsValid() ) + return true; + } + return false; +} + +bool CDextraProtocol::IsValidDvFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 27==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && 0U==(Buffer.data()[14] & 0x40U) ) + { + // create packet + dvframe = std::unique_ptr(new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), Buffer.data()[14])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +bool CDextraProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 27==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && (Buffer.data()[14] & 0x40) ) + { + // create packet + dvframe = std::unique_ptr(new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), Buffer.data()[14])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CDextraProtocol::EncodeKeepAlivePacket(CBuffer *Buffer) +{ + Buffer->Set(GetReflectorCallsign()); +} + +void CDextraProtocol::EncodeConnectPacket(CBuffer *Buffer, const char *Modules) +{ + uint8 lm = (uint8)Modules[0]; + uint8 rm = (uint8)Modules[1]; + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN); + Buffer->Append(lm); + Buffer->Append(rm); + Buffer->Append((uint8)0); +} + +void CDextraProtocol::EncodeConnectAckPacket(CBuffer *Buffer, int ProtRev) +{ + // is it for a XRF or repeater + if ( ProtRev == 2 ) + { + // XRFxxx + uint8 rm = (Buffer->data())[8]; + uint8 lm = (Buffer->data())[9]; + Buffer->clear(); + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN); + Buffer->Append(lm); + Buffer->Append(rm); + Buffer->Append((uint8)0); + } + else + { + // regular repeater + uint8 tag[] = { 'A','C','K',0 }; + Buffer->resize(Buffer->size()-1); + Buffer->Append(tag, sizeof(tag)); + } +} + +void CDextraProtocol::EncodeConnectNackPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'N','A','K',0 }; + Buffer->resize(Buffer->size()-1); + Buffer->Append(tag, sizeof(tag)); +} + +void CDextraProtocol::EncodeDisconnectPacket(CBuffer *Buffer, char Module) +{ + uint8 tag[] = { ' ',0 }; + Buffer->Set((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN); + Buffer->Append((uint8)Module); + Buffer->Append(tag, sizeof(tag)); +} + +void CDextraProtocol::EncodeDisconnectedPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'D','I','S','C','O','N','N','E','C','T','E','D' }; + Buffer->Set(tag, sizeof(tag)); +} + +bool CDextraProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + struct dstar_header DstarHeader; + + Packet.ConvertToDstarStruct(&DstarHeader); + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)0x80); + Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header)); + + return true; +} + +bool CDextraProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)(Packet.GetPacketId() % 21)); + Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE); + + return true; + +} + +bool CDextraProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag1[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + uint8 tag2[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x1A,0xC6 }; + + Buffer->Set(tag1, sizeof(tag1)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40)); + Buffer->Append(tag2, sizeof(tag2)); + + return true; +} diff --git a/src/DExtraProtocol.h b/src/DExtraProtocol.h new file mode 100644 index 0000000..2984348 --- /dev/null +++ b/src/DExtraProtocol.h @@ -0,0 +1,104 @@ +// +// DExtraProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdextraprotocol_h +#define cdextraprotocol_h + +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +// note on protocol revisions: +// +// rev 0: +// this is standard protocol implementation +// +// rev 1: +// this is specific UP4DAR umplementation +// the protocol is detected using byte(10) of connect packet (value is 11) +// the protocol require a specific non-standard disconnect acqknowleding packet +// +// rev 2: +// this is specific to KI4KLF dxrfd reflector +// the protocol is detected by looking at "XRF" in connect packet callsign +// the protocol require a specific connect ack packet +// the protocol also implement a workaround for detecting stream's module +// as dxrfd soes not set DV header RPT2 properly. +// the protocol assumes that a dxrfd can only be linked to one module at a time + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDextraProtocol : public CProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandlePeerLinks(void); + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // packet decoding helpers + bool IsValidConnectPacket( const CBuffer &, CCallsign *, char *, int *); + bool IsValidDisconnectPacket( const CBuffer &, CCallsign *); + bool IsValidKeepAlivePacket( const CBuffer &, CCallsign *); + bool IsValidDvHeaderPacket( const CBuffer &, std::unique_ptr &); + bool IsValidDvFramePacket( const CBuffer &, std::unique_ptr &); + bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr &); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *); + void EncodeConnectPacket(CBuffer *, const char *); + void EncodeConnectAckPacket(CBuffer *, int); + void EncodeConnectNackPacket(CBuffer *); + void EncodeDisconnectPacket(CBuffer *, char); + void EncodeDisconnectedPacket(CBuffer *); + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; + bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; + bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; + +protected: + // time + CTimePoint m_LastKeepaliveTime; + CTimePoint m_LastPeersLinkTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdextraprotocol_h */ diff --git a/src/DMRIdDir.cpp b/src/DMRIdDir.cpp new file mode 100644 index 0000000..ffe0ffc --- /dev/null +++ b/src/DMRIdDir.cpp @@ -0,0 +1,151 @@ +// +// cdmriddir.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "Reflector.h" +#include "DMRIdDir.h" +#include "cdmriddirfile.h" +#include "DMRIdDirHttp.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor & destructor + +CDmridDir::CDmridDir() +{ + keep_running = true; +} + +CDmridDir::~CDmridDir() +{ + Close(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// init & close + +bool CDmridDir::Init(void) +{ + // load content + Reload(); + + // reset run flag + keep_running = true; + + // start thread; + m_Future = std::async(std::launch::async, &CDmridDir::Thread, this); + + return true; +} + +void CDmridDir::Close(void) +{ + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CDmridDir::Thread() +{ + while (keep_running) + { + // Wait DMRIDDB_REFRESH_RATE minutes + for (int i=0; i<30*DMRIDDB_REFRESH_RATE && keep_running; i++) + CTimePoint::TaskSleepFor(2000); + + // have lists files changed ? + if ( NeedReload() ) + { + Reload(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Reload + +bool CDmridDir::Reload(void) +{ + CBuffer buffer; + bool ok = false; + + if ( LoadContent(&buffer) ) + { + Lock(); + { + ok = RefreshContent(buffer); + } + Unlock(); + } + return ok; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// find + +const CCallsign *CDmridDir::FindCallsign(uint32 dmrid) +{ + auto found = m_CallsignMap.find(dmrid); + if ( found != m_CallsignMap.end() ) + { + return &(found->second); + } + return nullptr; +} + +uint32 CDmridDir::FindDmrid(const CCallsign &callsign) +{ + auto found = m_DmridMap.find(callsign); + if ( found != m_DmridMap.end() ) + { + return (found->second); + } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// syntax helpers + +bool CDmridDir::IsValidDmrid(const char *sz) +{ + bool ok = false; + size_t n = ::strlen(sz); + if ( (n > 0) && (n <= 8) ) + { + ok = true; + for ( size_t i = 0; (i < n) && ok; i++ ) + { + ok &= ::isdigit(sz[i]); + } + } + return ok; +} diff --git a/src/DMRIdDir.h b/src/DMRIdDir.h new file mode 100644 index 0000000..d526697 --- /dev/null +++ b/src/DMRIdDir.h @@ -0,0 +1,95 @@ +// +// DMRIdDir.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmriddir_h +#define cdmriddir_h + +#include +#include +#include +#include +#include "Buffer.h" +#include "Callsign.h" + +// compare function for std::map::find + +struct CDmridDirCallsignCompare +{ + bool operator() (const CCallsign &cs1, const CCallsign &cs2) const + { return cs1.HasLowerCallsign(cs2);} +}; + + +//////////////////////////////////////////////////////////////////////////////////////// + +class CDmridDir +{ +public: + // constructor + CDmridDir(); + + // destructor + ~CDmridDir(); + + // init & close + virtual bool Init(void); + virtual void Close(void); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // refresh + virtual bool LoadContent(CBuffer *) { return false; } + virtual bool RefreshContent(const CBuffer &) { return false; } + + // find + const CCallsign *FindCallsign(uint32); + uint32 FindDmrid(const CCallsign &); + +protected: + // thread + void Thread(); + + // reload helpers + bool Reload(void); + virtual bool NeedReload(void) { return false; } + bool IsValidDmrid(const char *); + +protected: + // data + std::map m_CallsignMap; + std::map m_DmridMap; + + // Lock() + std::mutex m_Mutex; + + // thread + std::atomic keep_running; + std::future m_Future; + +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdmriddir_h */ diff --git a/src/DMRIdDirHttp.cpp b/src/DMRIdDirHttp.cpp new file mode 100644 index 0000000..bf5beb0 --- /dev/null +++ b/src/DMRIdDirHttp.cpp @@ -0,0 +1,182 @@ +// +// cdmriddirhttp.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "Reflector.h" +#include "DMRIdDirHttp.h" + +#if (DMRIDDB_USE_RLX_SERVER == 1) +CDmridDirHttp g_DmridDir; +#endif + + + + +//////////////////////////////////////////////////////////////////////////////////////// +// refresh + +bool CDmridDirHttp::LoadContent(CBuffer *buffer) +{ + // get file from xlxapi server + return HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, buffer); +} + +bool CDmridDirHttp::RefreshContent(const CBuffer &buffer) +{ + bool ok = false; + + // clear directory + m_CallsignMap.clear(); + m_DmridMap.clear(); + + // scan file + if ( buffer.size() > 0 ) + { + char *ptr1 = (char *)buffer.data(); + char *ptr2; + // get next line + while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr ) + { + *ptr2 = 0; + // get items + char *dmrid; + char *callsign; + if ( ((dmrid = ::strtok(ptr1, ";")) != nullptr) && IsValidDmrid(dmrid) ) + { + if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) ) + { + // new entry + uint32 ui = atoi(dmrid); + CCallsign cs(callsign, ui); + if ( cs.IsValid() ) + { + m_CallsignMap.insert(std::pair(ui, cs)); + m_DmridMap.insert(std::pair(cs,ui)); + } + } + } + // next line + ptr1 = ptr2+1; + } + // done + ok = true; + } + + // report + std::cout << "Read " << m_DmridMap.size() << " DMR ids from xlxapi.rlx.lu database " << std::endl; + + // done + return ok; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// httpd helpers + +#define DMRID_HTTPGET_SIZEMAX (256) + +bool CDmridDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer) +{ + bool ok = false; + int sock_id; + + // open socket + if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) + { + // get hostname address + struct sockaddr_in servaddr; + struct hostent *hp; + ::memset(&servaddr,0,sizeof(servaddr)); + if( (hp = gethostbyname(hostname)) != nullptr ) + { + // dns resolved + ::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length); + servaddr.sin_port = htons(port); + servaddr.sin_family = AF_INET; + + // connect + if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) + { + // send the GET request + char request[DMRID_HTTPGET_SIZEMAX]; + ::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: xlxd\r\n\r\n", + filename, (const char *)g_Reflector.GetCallsign()); + ::write(sock_id, request, strlen(request)); + + // config receive timeouts + fd_set read_set; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + FD_ZERO(&read_set); + FD_SET(sock_id, &read_set); + + // get the reply back + buffer->clear(); + bool done = false; + do + { + char buf[1440]; + ssize_t len = 0; + select(sock_id+1, &read_set, nullptr, nullptr, &timeout); + //if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) ) + //if ( ret >= 0 ) + //{ + usleep(5000); + len = read(sock_id, buf, 1440); + if ( len > 0 ) + { + buffer->Append((uint8 *)buf, (int)len); + ok = true; + } + //} + done = (len <= 0); + + } + while (!done); + buffer->Append((uint8)0); + + // and disconnect + close(sock_id); + } + else + { + std::cout << "Cannot establish connection with host " << hostname << std::endl; + } + } + else + { + std::cout << "Host " << hostname << " not found" << std::endl; + } + + } + else + { + std::cout << "Failed to open wget socket" << std::endl; + } + + // done + return ok; +} diff --git a/src/DMRIdDirHttp.h b/src/DMRIdDirHttp.h new file mode 100644 index 0000000..60056d1 --- /dev/null +++ b/src/DMRIdDirHttp.h @@ -0,0 +1,52 @@ +// +// DMRIdDirHttp.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/12/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmriddirhttp_h +#define cdmriddirhttp_h + +#include "DMRIdDir.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CDmridDirHttp : public CDmridDir +{ +public: + // constructor + CDmridDirHttp() {} + + // destructor + ~CDmridDirHttp() {} + + // refresh + bool LoadContent(CBuffer *); + bool RefreshContent(const CBuffer &); + +protected: + // reload helpers + bool NeedReload(void) { return true; } + bool HttpGet(const char *, const char *, int, CBuffer *); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdmriddirhttp_h */ diff --git a/src/DMRMMDVMClient.cpp b/src/DMRMMDVMClient.cpp new file mode 100644 index 0000000..24132e6 --- /dev/null +++ b/src/DMRMMDVMClient.cpp @@ -0,0 +1,52 @@ +// +// cdmrmmdvmclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DMMMDVMClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CDmrmmdvmClient::CDmrmmdvmClient() +{ +} + +CDmrmmdvmClient::CDmrmmdvmClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CDmrmmdvmClient::CDmrmmdvmClient(const CDmrmmdvmClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDmrmmdvmClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < DMRMMDVM_KEEPALIVE_TIMEOUT); +} diff --git a/src/DMRMMDVMClient.h b/src/DMRMMDVMClient.h new file mode 100644 index 0000000..f61b16d --- /dev/null +++ b/src/DMRMMDVMClient.h @@ -0,0 +1,60 @@ +// +// DMMMDVMClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmrmmdvmclient_h +#define cdmrmmdvmclient_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDmrmmdvmClient : public CClient +{ +public: + // constructors + CDmrmmdvmClient(); + CDmrmmdvmClient(const CCallsign &, const CIp &, char = ' '); + CDmrmmdvmClient(const CDmrmmdvmClient &); + + // destructor + virtual ~CDmrmmdvmClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_DMRMMDVM; } + const char *GetProtocolName(void) const { return "DMRMmdvm"; } + int GetCodec(void) const { return CODEC_AMBE2PLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#endif /* cdmrmmdvmclient_h */ diff --git a/src/DMRMMDVMProtocol.cpp b/src/DMRMMDVMProtocol.cpp new file mode 100644 index 0000000..0abdcc4 --- /dev/null +++ b/src/DMRMMDVMProtocol.cpp @@ -0,0 +1,1160 @@ +// +// cdmrmmdvmprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 104/03/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "DMMMDVMClient.h" +#include "DMMMDVMProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" +#include "CBPT19696.h" +#include "RS129.h" +#include "Golay2087.h" +#include "QR1676.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define CMD_NONE 0 +#define CMD_LINK 1 +#define CMD_UNLINK 2 + +//////////////////////////////////////////////////////////////////////////////////////// +// constants + +static uint8 g_DmrSyncBSVoice[] = { 0x07,0x55,0xFD,0x7D,0xF7,0x5F,0x70 }; +static uint8 g_DmrSyncBSData[] = { 0x0D,0xFF,0x57,0xD7,0x5D,0xF5,0xD0 }; +static uint8 g_DmrSyncMSVoice[] = { 0x07,0xF7,0xD5,0xDD,0x57,0xDF,0xD0 }; +static uint8 g_DmrSyncMSData[] = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 }; + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CDmrmmdvmProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + + // random number generator + time_t t; + ::srand((unsigned) time(&t)); + m_uiAuthSeed = (uint32)rand(); + + // done + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CDmrmmdvmProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + int iRssi; + uint8 Cmd; + uint8 CallType; + std::unique_ptr Header; + std::unique_ptr LastFrame; + std::array, 3> Frames; + + // handle incoming packets +#if DMR_IPV6==true +#if DMR_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + //Buffer.DebugDump(g_Reflector.m_DebugFile); + // crack the packet + if ( IsValidDvFramePacket(Buffer, Frames) ) + { + for ( int i = 0; i < 3; i++ ) + { + OnDvFramePacketIn(Frames.at(i), &Ip); + } + } + else if ( IsValidDvHeaderPacket(Buffer, Header, &Cmd, &CallType) ) + { + // callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DMRMMDVM) ) + { + // handle it + OnDvHeaderPacketIn(Header, Ip, Cmd, CallType); + } + } + else if ( IsValidDvLastFramePacket(Buffer, LastFrame) ) + { + OnDvLastFramePacketIn(LastFrame, &Ip); + } + else if ( IsValidConnectPacket(Buffer, &Callsign, Ip) ) + { + std::cout << "DMRmmdvm connect packet from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRMMDVM) ) + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer, Callsign, m_uiAuthSeed); + Send(Buffer, Ip); + } + else + { + // deny the request + EncodeNackPacket(&Buffer, Callsign); + Send(Buffer, Ip); + } + + } + else if ( IsValidAuthenticationPacket(Buffer, &Callsign, Ip) ) + { + std::cout << "DMRmmdvm authentication packet from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRMMDVM) ) + { + // acknowledge the request + EncodeAckPacket(&Buffer, Callsign); + Send(Buffer, Ip); + + // add client if needed + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Callsign, Ip, PROTOCOL_DMRMMDVM); + // client already connected ? + if ( client == nullptr ) + { + std::cout << "DMRmmdvm login from " << Callsign << " at " << Ip << std::endl; + + // create the client and append + clients->AddClient(std::make_shared(Callsign, Ip)); + } + else + { + client->Alive(); + } + // and done + g_Reflector.ReleaseClients(); + } + else + { + // deny the request + EncodeNackPacket(&Buffer, Callsign); + Send(Buffer, Ip); + } + + } + else if ( IsValidDisconnectPacket(Buffer, &Callsign) ) + { + std::cout << "DMRmmdvm disconnect packet from " << Callsign << " at " << Ip << std::endl; + + // find client & remove it + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Ip, PROTOCOL_DMRMMDVM); + if ( client != nullptr ) + { + clients->RemoveClient(client); + } + g_Reflector.ReleaseClients(); + } + else if ( IsValidConfigPacket(Buffer, &Callsign, Ip) ) + { + std::cout << "DMRmmdvm configuration packet from " << Callsign << " at " << Ip << std::endl; + + // acknowledge the request + EncodeAckPacket(&Buffer, Callsign); + Send(Buffer, Ip); + } + else if ( IsValidKeepAlivePacket(Buffer, &Callsign) ) + { + //std::cout << "DMRmmdvm keepalive packet from " << Callsign << " at " << Ip << std::endl; + + // find all clients with that callsign & ip and keep them alive + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(Callsign, Ip, PROTOCOL_DMRMMDVM, it)) != nullptr ) + { + // acknowledge + EncodeKeepAlivePacket(&Buffer, client); + Send(Buffer, Ip); + + // and mark as alive + client->Alive(); + } + g_Reflector.ReleaseClients(); + } + else if ( IsValidRssiPacket(Buffer, &Callsign, &iRssi) ) + { + // std::cout << "DMRmmdvm RSSI packet from " << Callsign << " at " << Ip << std::endl + + // ignore... + } + else if ( IsValidOptionPacket(Buffer, &Callsign) ) + { + std::cout << "DMRmmdvm options packet from " << Callsign << " at " << Ip << std::endl; + + // acknowledge the request + EncodeAckPacket(&Buffer, Callsign); + Send(Buffer, Ip); + } + else if ( Buffer.size() != 55 ) + { + std::string title("Unknown DMRMMDVM packet from "); + title += Ip.GetAddress(); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + + // keep client alive + if ( m_LastKeepaliveTime.DurationSinceNow() > DMRMMDVM_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } + +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CDmrmmdvmProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip, uint8 cmd, uint8 CallType) +{ + bool lastheard = false; + + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + // no stream open yet, open a new one + // firstfind this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DMRMMDVM); + if ( client ) + { + // process cmd if any + if ( !client->HasReflectorModule() ) + { + // not linked yet + if ( cmd == CMD_LINK ) + { + if ( g_Reflector.IsValidModule(rpt2.GetModule()) ) + { + std::cout << "DMRmmdvm client " << client->GetCallsign() << " linking on module " << rpt2.GetModule() << std::endl; + // link + client->SetReflectorModule(rpt2.GetModule()); + } + else + { + std::cout << "DMRMMDVM node " << rpt1 << " link attempt on non-existing module" << std::endl; + } + } + } + else + { + // already linked + if ( cmd == CMD_UNLINK ) + { + std::cout << "DMRmmdvm client " << client->GetCallsign() << " unlinking" << std::endl; + // unlink + client->SetReflectorModule(' '); + } + else + { + // replace rpt2 module with currently linked module + auto m = client->GetReflectorModule(); + Header->SetRpt2Module(m); + rpt2.SetModule(m); + } + } + + // and now, re-check module is valid && that it's not a private call + if ( g_Reflector.IsValidModule(rpt2.GetModule()) && (CallType == DMR_GROUP_CALL) ) + { + // yes, try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + lastheard = true; + } + } + } + else + { + lastheard = true; + } + + // release + g_Reflector.ReleaseClients(); + + // update last heard + if ( lastheard ) + { + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CDmrmmdvmProtocol::HandleQueue(void) +{ + + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // get our sender's id + int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); + + // encode + CBuffer buffer; + + // check if it's header + if ( packet->IsDvHeader() ) + { + // update local stream cache + // this relies on queue feeder setting valid module id + m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet); + m_StreamsCache[iModId].m_uiSeqId = 0; + + // encode it + EncodeDvHeaderPacket((CDvHeaderPacket &)*packet, m_StreamsCache[iModId].m_uiSeqId, &buffer); + m_StreamsCache[iModId].m_uiSeqId = 1; + } + // check if it's a last frame + else if ( packet->IsLastPacket() ) + { + // encode it + EncodeDvLastPacket(m_StreamsCache[iModId].m_dvHeader, m_StreamsCache[iModId].m_uiSeqId, &buffer); + m_StreamsCache[iModId].m_uiSeqId = (m_StreamsCache[iModId].m_uiSeqId + 1) & 0xFF; + } + // otherwise, just a regular DV frame + else + { + // update local stream cache or send triplet when needed + switch ( packet->GetDmrPacketSubid() ) + { + case 1: + m_StreamsCache[iModId].m_dvFrame0 = CDvFramePacket((const CDvFramePacket &)*packet); + break; + case 2: + m_StreamsCache[iModId].m_dvFrame1 = CDvFramePacket((const CDvFramePacket &)*packet); + break; + case 3: + EncodeDvPacket( + m_StreamsCache[iModId].m_dvHeader, + m_StreamsCache[iModId].m_dvFrame0, + m_StreamsCache[iModId].m_dvFrame1, + (const CDvFramePacket &)*packet, + m_StreamsCache[iModId].m_uiSeqId, + &buffer); + m_StreamsCache[iModId].m_uiSeqId = (m_StreamsCache[iModId].m_uiSeqId + 1) & 0xFF; + break; + default: + break; + } + } + + // send it + if ( buffer.size() > 0 ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DMRMMDVM, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + Send(buffer, client->GetIp()); + + } + } + g_Reflector.ReleaseClients(); + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CDmrmmdvmProtocol::HandleKeepalives(void) +{ + // DMRhomebrew protocol keepalive request is client tasks + // here, just check that all clients are still alive + // and disconnect them if not + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DMRMMDVM, it)) != nullptr ) + { + // is this client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // check it's still with us + else if ( !client->IsAlive() ) + { + // no, disconnect + CBuffer disconnect; + Send(disconnect, client->GetIp()); + + // remove it + std::cout << "DMRmmdvm client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CDmrmmdvmProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign) +{ + uint8 tag[] = { 'R','P','T','P','I','N','G' }; + + bool valid = false; + if ( (Buffer.size() == 11) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],Buffer.data()[7])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) +{ + uint8 tag[] = { 'R','P','T','L' }; + + bool valid = false; + if ( (Buffer.size() == 8) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + if ( !valid) + { + std::cout << "Invalid callsign in DMRmmdvm RPTL packet from IP: " << Ip << " CS:" << *callsign << " DMRID:" << callsign->GetDmrid() << std::endl; + } + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidAuthenticationPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) +{ + uint8 tag[] = { 'R','P','T','K' }; + + bool valid = false; + if ( (Buffer.size() == 40) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + if ( !valid) + { + std::cout << "Invalid callsign in DMRmmdvm RPTK packet from IP: " << Ip << " CS:" << *callsign << " DMRID:" << callsign->GetDmrid() << std::endl; + } + + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + uint8 tag[] = { 'R','P','T','C','L' }; + + bool valid = false; + if ( (Buffer.size() == 13) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidConfigPacket(const CBuffer &Buffer, CCallsign *callsign, const CIp &Ip) +{ + uint8 tag[] = { 'R','P','T','C' }; + + bool valid = false; + if ( (Buffer.size() == 302) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + if ( !valid) + { + std::cout << "Invalid callsign in DMRmmdvm RPTC packet from IP: " << Ip << " CS:" << *callsign << " DMRID:" << callsign->GetDmrid() << std::endl; + } + + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidOptionPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + uint8 tag[] = { 'R','P','T','O' }; + + bool valid = false; + if ( (Buffer.size() >= 8) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],Buffer.data()[4])); + callsign->SetDmrid(uiRptrId, true); + callsign->SetModule(MMDVM_MODULE_ID); + valid = callsign->IsValid(); + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidRssiPacket(const CBuffer &Buffer, CCallsign *callsign, int *rssi) +{ + uint8 tag[] = { 'R','P','T','I','N','T','R' }; + + bool valid = false; + if ( (Buffer.size() == 17) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // ignore rest of it, as not used + // dmrid is asci hex on 8 bytes + // rssi is ascii :x-xx + valid = true; + } + return valid; +} + +bool CDmrmmdvmProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, std::unique_ptr &header, uint8 *cmd, uint8 *CallType) +{ + uint8 tag[] = { 'D','M','R','D' }; + + *cmd = CMD_NONE; + + if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // frame details + uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4; + uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1; + uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL; + uint8 uiSlotType = Buffer.data()[15] & 0x0F; + //std::cout << (int)uiSlot << std::endl; + if ( (uiFrameType == DMRMMDVM_FRAMETYPE_DATASYNC) && + (uiSlot == DMRMMDVM_REFLECTOR_SLOT) && + (uiSlotType == MMDVM_SLOTTYPE_HEADER) ) + { + // extract sync + uint8 dmrsync[7]; + dmrsync[0] = Buffer.data()[33] & 0x0F; + ::memcpy(&dmrsync[1], &Buffer.data()[34], 5); + dmrsync[6] = Buffer.data()[39] & 0xF0; + // and check + if ( (::memcmp(dmrsync, g_DmrSyncMSData, sizeof(dmrsync)) == 0) || + (::memcmp(dmrsync, g_DmrSyncBSData, sizeof(dmrsync)) == 0)) + { + // get payload + //CBPTC19696 bptc; + //uint8 lcdata[12]; + //bptc.decode(&(Buffer.data()[20]), lcdata); + + // crack DMR header + //uint8 uiSeqId = Buffer.data()[4]; + uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0)); + uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0)); + uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11])); + //uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F); + uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]); + + // call type + *CallType = uiCallType; + + // link/unlink command ? + if ( uiDstId == 4000 ) + { + *cmd = CMD_UNLINK; + } + else if ( DmrDstIdToModule(uiDstId) != ' ' ) + { + *cmd = CMD_LINK; + } + else + { + *cmd = CMD_NONE; + } + + // build DVHeader + CCallsign csMY = CCallsign("", uiSrcId); + CCallsign rpt1 = CCallsign("", uiRptrId); + rpt1.SetModule(MMDVM_MODULE_ID); + CCallsign rpt2 = m_ReflectorCallsign; + rpt2.SetModule(DmrDstIdToModule(uiDstId)); + + // and packet + header = std::unique_ptr(new CDvHeaderPacket(uiSrcId, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, 0, 0)); + if ( header && header->IsValid() ) + return true; + } + } + } + return false; +} + +bool CDmrmmdvmProtocol::IsValidDvFramePacket(const CBuffer &Buffer, std::array, 3> &frames) +{ + uint8 tag[] = { 'D','M','R','D' }; + + if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // frame details + uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4; + uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1; + uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL; + if ( ((uiFrameType == DMRMMDVM_FRAMETYPE_VOICE) || (uiFrameType == DMRMMDVM_FRAMETYPE_VOICESYNC)) && + (uiSlot == DMRMMDVM_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) ) + { + // crack DMR header + //uint8 uiSeqId = Buffer.data()[4]; + //uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0)); + //uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0)); + //uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11])); + uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F); + uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]); + + // crack payload + uint8 dmrframe[33]; + uint8 dmr3ambe[27]; + uint8 dmrambe[9]; + uint8 dmrsync[7]; + // get the 33 bytes ambe + memcpy(dmrframe, &(Buffer.data()[20]), 33); + // extract the 3 ambe frames + memcpy(dmr3ambe, dmrframe, 14); + dmr3ambe[13] &= 0xF0; + dmr3ambe[13] |= (dmrframe[19] & 0x0F); + memcpy(&dmr3ambe[14], &dmrframe[20], 13); + // extract sync + dmrsync[0] = dmrframe[13] & 0x0F; + ::memcpy(&dmrsync[1], &dmrframe[14], 5); + dmrsync[6] = dmrframe[19] & 0xF0; + + // debug + //CBuffer dump; + //dump.Set(dmrsync, 6); + //dump.DebugDump(g_Reflector.m_DebugFile); + + // and create 3 dv frames + // frame1 + memcpy(dmrambe, &dmr3ambe[0], 9); + frames[0] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1)); + + // frame2 + memcpy(dmrambe, &dmr3ambe[9], 9); + frames[1] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2)); + + // frame3 + memcpy(dmrambe, &dmr3ambe[18], 9); + frames[2] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3)); + + // check + if (frames[0] && frames[1] && frames[2]) + return true; + } + } + return false; +} + +bool CDmrmmdvmProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer, std::unique_ptr &frame) +{ + uint8 tag[] = { 'D','M','R','D' }; + + if ( (Buffer.size() == 55) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // frame details + uint8 uiFrameType = (Buffer.data()[15] & 0x30) >> 4; + uint8 uiSlot = (Buffer.data()[15] & 0x80) ? DMR_SLOT2 : DMR_SLOT1; + //uint8 uiCallType = (Buffer.data()[15] & 0x40) ? DMR_PRIVATE_CALL : DMR_GROUP_CALL; + uint8 uiSlotType = Buffer.data()[15] & 0x0F; + //std::cout << (int)uiSlot << std::endl; + if ( (uiFrameType == DMRMMDVM_FRAMETYPE_DATASYNC) && + (uiSlot == DMRMMDVM_REFLECTOR_SLOT) && + (uiSlotType == MMDVM_SLOTTYPE_TERMINATOR) ) + { + // extract sync + uint8 dmrsync[7]; + dmrsync[0] = Buffer.data()[33] & 0x0F; + ::memcpy(&dmrsync[1], &Buffer.data()[34], 5); + dmrsync[6] = Buffer.data()[39] & 0xF0; + // and check + if ( (::memcmp(dmrsync, g_DmrSyncMSData, sizeof(dmrsync)) == 0) || + (::memcmp(dmrsync, g_DmrSyncBSData, sizeof(dmrsync)) == 0)) + { + // get payload + //CBPTC19696 bptc; + //uint8 lcdata[12]; + //bptc.decode(&(Buffer.data()[20]), lcdata); + + // crack DMR header + //uint8 uiSeqId = Buffer.data()[4]; + //uint32 uiSrcId = MAKEDWORD(MAKEWORD(Buffer.data()[7],Buffer.data()[6]),MAKEWORD(Buffer.data()[5],0)); + //uint32 uiDstId = MAKEDWORD(MAKEWORD(Buffer.data()[10],Buffer.data()[9]),MAKEWORD(Buffer.data()[8],0)); + //uint32 uiRptrId = MAKEDWORD(MAKEWORD(Buffer.data()[14],Buffer.data()[13]),MAKEWORD(Buffer.data()[12],Buffer.data()[11])); + //uint8 uiVoiceSeq = (Buffer.data()[15] & 0x0F); + uint32 uiStreamId = *(uint32 *)(&Buffer.data()[16]); + + // dummy ambe + uint8 ambe[9]; + ::memset(ambe, 0, sizeof(ambe)); + + + // and packet + frame = std::unique_ptr(new CDvLastFramePacket(ambe, dmrsync, uiStreamId, 0, 0)); + if (frame) + return true; + } + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CDmrmmdvmProtocol::EncodeKeepAlivePacket(CBuffer *Buffer, std::shared_ptrClient) +{ + uint8 tag[] = { 'M','S','T','P','O','N','G' }; + + Buffer->Set(tag, sizeof(tag)); + uint32 uiDmrId = Client->GetCallsign().GetDmrid(); + Buffer->Append((uint8 *)&uiDmrId, 4); +} + +void CDmrmmdvmProtocol::EncodeAckPacket(CBuffer *Buffer, const CCallsign &Callsign) +{ + uint8 tag[] = { 'R','P','T','A','C','K' }; + + Buffer->Set(tag, sizeof(tag)); +} + +void CDmrmmdvmProtocol::EncodeConnectAckPacket(CBuffer *Buffer, const CCallsign &Callsign, uint32 AuthSeed) +{ + uint8 tag[] = { 'R','P','T','A','C','K' }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(AuthSeed); +} + +void CDmrmmdvmProtocol::EncodeNackPacket(CBuffer *Buffer, const CCallsign &Callsign) +{ + uint8 tag[] = { 'M','S','T','N','A','K' }; + + Buffer->Set(tag, sizeof(tag)); +} + +void CDmrmmdvmProtocol::EncodeClosePacket(CBuffer *Buffer, std::shared_ptrClient) +{ + uint8 tag[] = { 'M','S','T','C','L' }; + + Buffer->Set(tag, sizeof(tag)); +} + + +bool CDmrmmdvmProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, uint8 seqid, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','M','R','D' }; + + Buffer->Set(tag, sizeof(tag)); + + // DMR header + // uiSeqId + Buffer->Append((uint8)seqid); + // uiSrcId + uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid(); + AppendDmrIdToBuffer(Buffer, uiSrcId); + // uiDstId = TG9 + uint32 uiDstId = 9; // ModuleToDmrDestId(Packet.GetRpt2Module()); + AppendDmrIdToBuffer(Buffer, uiDstId); + // uiRptrId + uint32 uiRptrId = Packet.GetRpt1Callsign().GetDmrid(); + AppendDmrRptrIdToBuffer(Buffer, uiRptrId); + // uiBitField + uint8 uiBitField = + (DMRMMDVM_FRAMETYPE_DATASYNC << 4) | + ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00) | + MMDVM_SLOTTYPE_HEADER; + Buffer->Append((uint8)uiBitField); + // uiStreamId + uint32 uiStreamId = Packet.GetStreamId(); + Buffer->Append((uint32)uiStreamId); + + // Payload + AppendVoiceLCToBuffer(Buffer, uiSrcId); + + // BER + Buffer->Append((uint8)0); + + // RSSI + Buffer->Append((uint8)0); + + // done + return true; +} + +void CDmrmmdvmProtocol::EncodeDvPacket( + const CDvHeaderPacket &Header, + const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2, + uint8 seqid, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','M','R','D' }; + Buffer->Set(tag, sizeof(tag)); + + // DMR header + // uiSeqId + Buffer->Append((uint8)seqid); + // uiSrcId + uint32 uiSrcId = Header.GetMyCallsign().GetDmrid(); + AppendDmrIdToBuffer(Buffer, uiSrcId); + // uiDstId = TG9 + uint32 uiDstId = 9; // ModuleToDmrDestId(Header.GetRpt2Module()); + AppendDmrIdToBuffer(Buffer, uiDstId); + // uiRptrId + uint32 uiRptrId = Header.GetRpt1Callsign().GetDmrid(); + AppendDmrRptrIdToBuffer(Buffer, uiRptrId); + // uiBitField + uint8 uiBitField = + ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00); + if ( DvFrame0.GetDmrPacketId() == 0 ) + { + uiBitField |= (DMRMMDVM_FRAMETYPE_VOICESYNC << 4); + } + else + { + uiBitField |= (DMRMMDVM_FRAMETYPE_VOICE << 4); + } + uiBitField |= (DvFrame0.GetDmrPacketId() & 0x0F); + Buffer->Append((uint8)uiBitField); + + // uiStreamId + uint32 uiStreamId = Header.GetStreamId(); + Buffer->Append((uint32)uiStreamId); + + // Payload + // frame0 + Buffer->ReplaceAt(20, DvFrame0.GetAmbePlus(), 9); + // 1/2 frame1 + Buffer->ReplaceAt(29, DvFrame1.GetAmbePlus(), 5); + Buffer->ReplaceAt(33, (uint8)(Buffer->at(33) & 0xF0)); + // 1/2 frame1 + Buffer->ReplaceAt(39, DvFrame1.GetAmbePlus()+4, 5); + Buffer->ReplaceAt(39, (uint8)(Buffer->at(39) & 0x0F)); + // frame2 + Buffer->ReplaceAt(44, DvFrame2.GetAmbePlus(), 9); + + // sync or embedded signaling + ReplaceEMBInBuffer(Buffer, DvFrame0.GetDmrPacketId()); + + // debug + //CBuffer dump; + //dump.Set(&(Buffer->data()[33]), 7); + //dump.DebugDump(g_Reflector.m_DebugFile); + + // BER + Buffer->Append((uint8)0); + + // RSSI + Buffer->Append((uint8)0); +} + + +void CDmrmmdvmProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Packet, uint8 seqid, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','M','R','D' }; + + Buffer->Set(tag, sizeof(tag)); + + // DMR header + // uiSeqId + Buffer->Append((uint8)seqid); + // uiSrcId + uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid(); + AppendDmrIdToBuffer(Buffer, uiSrcId); + // uiDstId + uint32 uiDstId = 9; //ModuleToDmrDestId(Packet.GetRpt2Module()); + AppendDmrIdToBuffer(Buffer, uiDstId); + // uiRptrId + uint32 uiRptrId = Packet.GetRpt1Callsign().GetDmrid(); + AppendDmrRptrIdToBuffer(Buffer, uiRptrId); + // uiBitField + uint8 uiBitField = + (DMRMMDVM_FRAMETYPE_DATASYNC << 4) | + ((DMRMMDVM_REFLECTOR_SLOT == DMR_SLOT2) ? 0x80 : 0x00) | + MMDVM_SLOTTYPE_TERMINATOR; + Buffer->Append((uint8)uiBitField); + // uiStreamId + uint32 uiStreamId = Packet.GetStreamId(); + Buffer->Append((uint32)uiStreamId); + + // Payload + AppendTerminatorLCToBuffer(Buffer, uiSrcId); + + // BER + Buffer->Append((uint8)0); + + // RSSI + Buffer->Append((uint8)0); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// DestId to Module helper + +char CDmrmmdvmProtocol::DmrDstIdToModule(uint32 tg) const +{ + // is it a 4xxx ? + if ( (tg >= 4001) && (tg <= (4000 + NB_OF_MODULES)) ) + { + return ((char)(tg - 4001) + 'A'); + } + return ' '; +} + +uint32 CDmrmmdvmProtocol::ModuleToDmrDestId(char m) const +{ + return (uint32)(m - 'A')+4001; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Buffer & LC helpers + +void CDmrmmdvmProtocol::AppendVoiceLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const +{ + uint8 payload[33]; + + // fill payload + CBPTC19696 bptc; + ::memset(payload, 0, sizeof(payload)); + // LC data + uint8 lc[12]; + { + ::memset(lc, 0, sizeof(lc)); + // uiDstId = TG9 + lc[5] = 9; + // uiSrcId + lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId)); + lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId)); + lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId)); + // parity + uint8 parity[4]; + CRS129::encode(lc, 9, parity); + lc[9] = parity[2] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + lc[10] = parity[1] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + lc[11] = parity[0] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + } + // sync + ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData)); + // slot type + { + // slot type + uint8 slottype[3]; + ::memset(slottype, 0, sizeof(slottype)); + slottype[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + slottype[0] |= (DMR_DT_VOICE_LC_HEADER << 0) & 0x0FU; + CGolay2087::encode(slottype); + payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU); + payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U); + payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU); + payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU); + + } + // and encode + bptc.encode(lc, payload); + + // and append + buffer->Append(payload, sizeof(payload)); +} + +void CDmrmmdvmProtocol::AppendTerminatorLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const +{ + uint8 payload[33]; + + // fill payload + CBPTC19696 bptc; + ::memset(payload, 0, sizeof(payload)); + // LC data + uint8 lc[12]; + { + ::memset(lc, 0, sizeof(lc)); + // uiDstId = TG9 + lc[5] = 9; + // uiSrcId + lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId)); + lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId)); + lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId)); + // parity + uint8 parity[4]; + CRS129::encode(lc, 9, parity); + lc[9] = parity[2] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK; + lc[10] = parity[1] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK; + lc[11] = parity[0] ^ DMR_TERMINATOR_WITH_LC_CRC_MASK; + } + // sync + ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData)); + // slot type + { + // slot type + uint8 slottype[3]; + ::memset(slottype, 0, sizeof(slottype)); + slottype[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + slottype[0] |= (DMR_DT_TERMINATOR_WITH_LC << 0) & 0x0FU; + CGolay2087::encode(slottype); + payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU); + payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U); + payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU); + payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU); + } + // and encode + bptc.encode(lc, payload); + + // and append + buffer->Append(payload, sizeof(payload)); +} + +void CDmrmmdvmProtocol::ReplaceEMBInBuffer(CBuffer *buffer, uint8 uiDmrPacketId) const +{ + // voice packet A ? + if ( uiDmrPacketId == 0 ) + { + // sync + buffer->ReplaceAt(33, (uint8)(buffer->at(33) | (g_DmrSyncBSVoice[0] & 0x0F))); + buffer->ReplaceAt(34, g_DmrSyncBSVoice+1, 5); + buffer->ReplaceAt(39, (uint8)(buffer->at(39) | (g_DmrSyncBSVoice[6] & 0xF0))); + } + // voice packet B,C,D,E ? + else if ( (uiDmrPacketId >= 1) && (uiDmrPacketId <= 4 ) ) + { + // EMB LC + uint8 emb[2]; + emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + //emb[0] |= PI ? 0x08U : 0x00; + //emb[0] |= (LCSS << 1) & 0x06; + emb[1] = 0x00; + // encode + CQR1676::encode(emb); + // and append + buffer->ReplaceAt(33, (uint8)((buffer->at(33) & 0xF0) | ((emb[0U] >> 4) & 0x0F))); + buffer->ReplaceAt(34, (uint8)((buffer->at(34) & 0x0F) | ((emb[0U] << 4) & 0xF0))); + buffer->ReplaceAt(34, (uint8)(buffer->at(34) & 0xF0)); + buffer->ReplaceAt(35, (uint8)0); + buffer->ReplaceAt(36, (uint8)0); + buffer->ReplaceAt(37, (uint8)0); + buffer->ReplaceAt(38, (uint8)(buffer->at(38) & 0x0F)); + buffer->ReplaceAt(38, (uint8)((buffer->at(38) & 0xF0) | ((emb[1U] >> 4) & 0x0F))); + buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0x0F) | ((emb[1U] << 4) & 0xF0))); + } + // voice packet F + else + { + // nullptr + uint8 emb[2]; + emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + //emb[0] |= PI ? 0x08U : 0x00; + //emb[0] |= (LCSS << 1) & 0x06; + emb[1] = 0x00; + // encode + CQR1676::encode(emb); + // and append + buffer->ReplaceAt(33, (uint8)((buffer->at(33) & 0xF0) | ((emb[0U] >> 4) & 0x0F))); + buffer->ReplaceAt(34, (uint8)((buffer->at(34) & 0x0F) | ((emb[0U] << 4) & 0xF0))); + buffer->ReplaceAt(34, (uint8)(buffer->at(34) & 0xF0)); + buffer->ReplaceAt(35, (uint8)0); + buffer->ReplaceAt(36, (uint8)0); + buffer->ReplaceAt(37, (uint8)0); + buffer->ReplaceAt(38, (uint8)(buffer->at(38) & 0x0F)); + buffer->ReplaceAt(38, (uint8)((buffer->at(38) & 0xF0) | ((emb[1U] >> 4) & 0x0F))); + buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0x0F) | ((emb[1U] << 4) & 0xF0))); + } +} + +void CDmrmmdvmProtocol::AppendDmrIdToBuffer(CBuffer *buffer, uint32 id) const +{ + buffer->Append((uint8)LOBYTE(HIWORD(id))); + buffer->Append((uint8)HIBYTE(LOWORD(id))); + buffer->Append((uint8)LOBYTE(LOWORD(id))); +} + +void CDmrmmdvmProtocol::AppendDmrRptrIdToBuffer(CBuffer *buffer, uint32 id) const +{ + buffer->Append((uint8)HIBYTE(HIWORD(id))); + buffer->Append((uint8)LOBYTE(HIWORD(id))); + buffer->Append((uint8)HIBYTE(LOWORD(id))); + buffer->Append((uint8)LOBYTE(LOWORD(id))); +} diff --git a/src/DMRMMDVMProtocol.h b/src/DMRMMDVMProtocol.h new file mode 100644 index 0000000..3ddbd4e --- /dev/null +++ b/src/DMRMMDVMProtocol.h @@ -0,0 +1,136 @@ +// +// DMMMDVMProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 04/03/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmrmmdvmprotocol_h +#define cdmrmmdvmprotocol_h + +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +// frame type +#define DMRMMDVM_FRAMETYPE_VOICE 0 +#define DMRMMDVM_FRAMETYPE_VOICESYNC 1 +#define DMRMMDVM_FRAMETYPE_DATASYNC 2 + +// slot type +#define MMDVM_SLOTTYPE_HEADER 1 +#define MMDVM_SLOTTYPE_TERMINATOR 2 + +// DMRMMDVM Module ID +#define MMDVM_MODULE_ID 'B' + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDmrmmdvmStreamCacheItem +{ +public: + CDmrmmdvmStreamCacheItem() {} + ~CDmrmmdvmStreamCacheItem() {} + + CDvHeaderPacket m_dvHeader; + CDvFramePacket m_dvFrame0; + CDvFramePacket m_dvFrame1; + + uint8 m_uiSeqId; +}; + + +class CDmrmmdvmProtocol : public CProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &, uint8, uint8); + + // packet decoding helpers + bool IsValidConnectPacket(const CBuffer &, CCallsign *, const CIp &); + bool IsValidAuthenticationPacket(const CBuffer &, CCallsign *, const CIp &); + bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); + bool IsValidConfigPacket(const CBuffer &, CCallsign *, const CIp &); + bool IsValidOptionPacket(const CBuffer &, CCallsign *); + bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); + bool IsValidRssiPacket(const CBuffer &, CCallsign *, int *); + bool IsValidDvHeaderPacket(const CBuffer &, std::unique_ptr &, uint8 *, uint8 *); + bool IsValidDvFramePacket(const CBuffer &, std::array, 3> &); + bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr &); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *, std::shared_ptr); + void EncodeAckPacket(CBuffer *, const CCallsign &); + void EncodeConnectAckPacket(CBuffer *, const CCallsign &, uint32); + void EncodeNackPacket(CBuffer *, const CCallsign &); + void EncodeClosePacket(CBuffer *, std::shared_ptr); + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, uint8, CBuffer *) const; + void EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket &, const CDvFramePacket &, const CDvFramePacket &, uint8, CBuffer *) const; + void EncodeDvLastPacket(const CDvHeaderPacket &, uint8, CBuffer *) const; + + // dmr DstId to Module helper + char DmrDstIdToModule(uint32) const; + uint32 ModuleToDmrDestId(char) const; + + // Buffer & LC helpers + void AppendVoiceLCToBuffer(CBuffer *, uint32) const; + void AppendTerminatorLCToBuffer(CBuffer *, uint32) const; + void ReplaceEMBInBuffer(CBuffer *, uint8) const; + void AppendDmrIdToBuffer(CBuffer *, uint32) const; + void AppendDmrRptrIdToBuffer(CBuffer *, uint32) const; + + +protected: + // for keep alive + CTimePoint m_LastKeepaliveTime; + + // for stream id + uint16 m_uiStreamId; + + // for queue header caches + std::array m_StreamsCache; + + // for authentication + uint32 m_uiAuthSeed; +}; + +//////////////////////////////////////////////////////////////////////////////////////// + +#endif /* cdmrmmdvmprotocol_h */ diff --git a/src/DMRPlusClient.cpp b/src/DMRPlusClient.cpp new file mode 100644 index 0000000..9c791b2 --- /dev/null +++ b/src/DMRPlusClient.cpp @@ -0,0 +1,52 @@ +// +// cdmrplusclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL on 10/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "cdmrplusclient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CDmrplusClient::CDmrplusClient() +{ +} + +CDmrplusClient::CDmrplusClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CDmrplusClient::CDmrplusClient(const CDmrplusClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDmrplusClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < DMRPLUS_KEEPALIVE_TIMEOUT); +} diff --git a/src/DMRPlusClient.h b/src/DMRPlusClient.h new file mode 100644 index 0000000..c032140 --- /dev/null +++ b/src/DMRPlusClient.h @@ -0,0 +1,59 @@ +// +// cdmrplusclient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmrplusclient_h +#define cdmrplusclient_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDmrplusClient : public CClient +{ +public: + // constructors + CDmrplusClient(); + CDmrplusClient(const CCallsign &, const CIp &, char = ' '); + CDmrplusClient(const CDmrplusClient &); + + // destructor + virtual ~CDmrplusClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_DMRPLUS; } + const char *GetProtocolName(void) const { return "DMRplus"; } + int GetCodec(void) const { return CODEC_AMBE2PLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdmrplusclient_h */ diff --git a/src/DMRPlusProtocol.cpp b/src/DMRPlusProtocol.cpp new file mode 100644 index 0000000..f2e2910 --- /dev/null +++ b/src/DMRPlusProtocol.cpp @@ -0,0 +1,788 @@ +// +// cdmrplusprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "cdmrplusclient.h" +#include "DMRPlusProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" +#include "DMRIdDir.h" +#include "CBPT19696.h" +#include "RS129.h" +#include "Golay2087.h" +#include "QR1676.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constants + +static uint8 g_DmrSyncBSVoice[] = { 0x07,0x55,0xFD,0x7D,0xF7,0x5F,0x70 }; +static uint8 g_DmrSyncBSData[] = { 0x0D,0xFF,0x57,0xD7,0x5D,0xF5,0xD0 }; +static uint8 g_DmrSyncMSVoice[] = { 0x07,0xF7,0xD5,0xDD,0x57,0xDF,0xD0 }; +static uint8 g_DmrSyncMSData[] = { 0x0D,0x5D,0x7F,0x77,0xFD,0x75,0x70 }; + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CDmrplusProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + + // random number generator + time_t t; + ::srand((unsigned) time(&t)); + + // done + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CDmrplusProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + char ToLinkModule; + std::unique_ptr Header; + std::array, 3> Frames; + + // handle incoming packets +#if DMR_IPV6==true +#if DMR_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvFramePacket(Ip, Buffer, Frames) ) + { + for ( int i = 0; i < 3; i++ ) + { + OnDvFramePacketIn(Frames[i], &Ip); + } + } + else if ( IsValidDvHeaderPacket(Ip, Buffer, Header) ) + { + // callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DMRPLUS) ) + { + // handle it + OnDvHeaderPacketIn(Header, Ip); + } + } + else if ( IsValidConnectPacket(Buffer, &Callsign, &ToLinkModule, Ip) ) + { + //std::cout << "DMRplus keepalive/connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DMRPLUS) ) + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer); + Send(Buffer, Ip); + + // add client if needed + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Callsign, Ip, PROTOCOL_DMRPLUS); + // client already connected ? + if ( client == nullptr ) + { + std::cout << "DMRplus connect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // create the client and append + clients->AddClient(std::make_shared(Callsign, Ip, ToLinkModule)); + } + else + { + client->Alive(); + } + // and done + g_Reflector.ReleaseClients(); + } + else + { + // deny the request + EncodeConnectNackPacket(&Buffer); + Send(Buffer, Ip); + } + + } + else if ( IsValidDisconnectPacket(Buffer, &Callsign, &ToLinkModule) ) + { + std::cout << "DMRplus disconnect packet for module " << ToLinkModule << " from " << Callsign << " at " << Ip << std::endl; + + // find client & remove it + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Ip, PROTOCOL_DMRPLUS); + if ( client != nullptr ) + { + clients->RemoveClient(client); + } + g_Reflector.ReleaseClients(); + } + else + { + std::string title("Unknown DMR+ packet from "); + title += Ip.GetAddress(); + Buffer.Dump(title); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + + // keep client alive + if ( m_LastKeepaliveTime.DurationSinceNow() > DMRPLUS_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CDmrplusProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // no stream open yet, open a new one + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DMRPLUS); + if ( client ) + { + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + } + // release + g_Reflector.ReleaseClients(); + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CDmrplusProtocol::HandleQueue(void) +{ + + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // get our sender's id + int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); + + // encode + CBuffer buffer; + + // check if it's header + if ( packet->IsDvHeader() ) + { + // update local stream cache + // this relies on queue feeder setting valid module id + m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet); + m_StreamsCache[iModId].m_uiSeqId = 4; + + // encode it + EncodeDvHeaderPacket((const CDvHeaderPacket &)*packet, &buffer); + } + else + { + // update local stream cache or send triplet when needed + switch ( packet->GetDmrPacketSubid() ) + { + case 1: + m_StreamsCache[iModId].m_dvFrame0 = CDvFramePacket((const CDvFramePacket &)*packet); + break; + case 2: + m_StreamsCache[iModId].m_dvFrame1 = CDvFramePacket((const CDvFramePacket &)*packet); + break; + case 3: + EncodeDvPacket( + m_StreamsCache[iModId].m_dvHeader, + m_StreamsCache[iModId].m_dvFrame0, + m_StreamsCache[iModId].m_dvFrame1, + (const CDvFramePacket &)*packet, + m_StreamsCache[iModId].m_uiSeqId, + &buffer); + m_StreamsCache[iModId].m_uiSeqId = GetNextSeqId(m_StreamsCache[iModId].m_uiSeqId); + break; + default: + break; + } + + } + + // send it + if ( buffer.size() > 0 ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + Send(buffer, client->GetIp()); + } + } + g_Reflector.ReleaseClients(); + + // debug + //buffer.DebugDump(g_Reflector.m_DebugFile); + } + } + m_Queue.Unlock(); +} + +void CDmrplusProtocol::SendBufferToClients(const CBuffer &buffer, uint8 module) +{ + if ( buffer.size() > 0 ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == module) ) + { + // no, send the packet + Send(buffer, client->GetIp()); + } + } + g_Reflector.ReleaseClients(); + + // debug + //buffer.DebugDump(g_Reflector.m_DebugFile); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CDmrplusProtocol::HandleKeepalives(void) +{ + // DMRplus protocol keepalive request is client tasks + // here, just check that all clients are still alive + // and disconnect them if not + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DMRPLUS, it)) != nullptr ) + { + // is this client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // check it's still with us + else if ( !client->IsAlive() ) + { + // no, disconnect + //CBuffer disconnect; + //EncodeDisconnectPacket(&disconnect, client); + //Send(disconnect, client->GetIp()); + + // remove it + std::cout << "DMRplus client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CDmrplusProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule, const CIp &Ip) +{ + bool valid = false; + if ( Buffer.size() == 31 ) + { + char sz[9]; + ::memcpy(sz, Buffer.data(), 8); + sz[8] = 0; + uint32 dmrid = atoi(sz); + callsign->SetDmrid(dmrid, true); + callsign->SetModule(DMRPLUS_MODULE_ID); + ::memcpy(sz, &Buffer.data()[8], 4); + sz[4] = 0; + *reflectormodule = DmrDstIdToModule(atoi(sz)); + valid = (callsign->IsValid() && (std::isupper(*reflectormodule) || (*reflectormodule == ' ')) ); + if ( !valid) + { + std::cout << "Invalid callsign in DMR+ connect packet from IP: " << Ip << " CS:" << *callsign << " DMRID:" << callsign->GetDmrid() << " ReflectorModule:" << *reflectormodule << std::endl; + } + } + return valid; +} + +bool CDmrplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *reflectormodule) +{ + bool valid = false; + if ( Buffer.size() == 32 ) + { + char sz[9]; + ::memcpy(sz, Buffer.data(), 8); + sz[8] = 0; + uint32 dmrid = atoi(sz); + callsign->SetDmrid(dmrid, true); + callsign->SetModule(DMRPLUS_MODULE_ID); + *reflectormodule = Buffer.data()[11] - '0' + 'A'; + valid = (callsign->IsValid() && std::isupper(*reflectormodule)); + } + return valid; +} + +bool CDmrplusProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CBuffer &Buffer, std::unique_ptr &Header) +{ + uint8 uiPacketType = Buffer.data()[8]; + if ( (Buffer.size() == 72) && ( uiPacketType == 2 ) ) + { + // frame details + uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1; + uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL; + uint8 uiColourCode = Buffer.data()[20] & 0x0F; + if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) ) + { + // more frames details + //uint8 uiSeqId = Buffer.data()[4]; + //uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type + uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF; + uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF; + + // build DVHeader + CCallsign csMY = CCallsign("", uiSrcId); + CCallsign rpt1 = CCallsign("", uiSrcId); + rpt1.SetModule(DMRPLUS_MODULE_ID); + CCallsign rpt2 = m_ReflectorCallsign; + rpt2.SetModule(DmrDstIdToModule(uiDstId)); + uint32 uiStreamId = IpToStreamId(Ip); + + // and packet + Header = std::unique_ptr(new CDvHeaderPacket(uiSrcId, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, 0, 0)); + if (Header && Header->IsValid()) + return true; + } + } + return false; +} + +bool CDmrplusProtocol::IsValidDvFramePacket(const CIp &Ip, const CBuffer &Buffer, std::array, 3> &frames) +{ + uint8 uiPacketType = Buffer.data()[8]; + if ( (Buffer.size() == 72) && ((uiPacketType == 1) || (uiPacketType == 3)) ) + { + // frame details + uint8 uiSlot = (Buffer.data()[16] == 0x22) ? DMR_SLOT2 : DMR_SLOT1; + uint8 uiCallType = (Buffer.data()[62] == 1) ? DMR_GROUP_CALL : DMR_PRIVATE_CALL; + uint8 uiColourCode = Buffer.data()[20] & 0x0F; + if ( (uiSlot == DMRPLUS_REFLECTOR_SLOT) && (uiCallType == DMR_GROUP_CALL) && (uiColourCode == DMRPLUS_REFLECTOR_COLOUR) ) + { + // more frames details + //uint8 uiSeqId = Buffer.data()[4]; + uint8 uiVoiceSeq = (Buffer.data()[18] & 0x0F) - 7; // aka slot type + //uint32 uiDstId = *(uint32 *)(&Buffer.data()[64]) & 0x00FFFFFF; + //uint32 uiSrcId = *(uint32 *)(&Buffer.data()[68]) & 0x00FFFFFF; + + // crack payload + uint8 dmrframe[33]; + uint8 dmr3ambe[27]; + uint8 dmrambe[9]; + uint8 dmrsync[7]; + // get the 33 bytes ambe + memcpy(dmrframe, &(Buffer.data()[26]), 33); + // handle endianess + SwapEndianess(dmrframe, sizeof(dmrframe)); + // extract the 3 ambe frames + memcpy(dmr3ambe, dmrframe, 14); + dmr3ambe[13] &= 0xF0; + dmr3ambe[13] |= (dmrframe[19] & 0x0F); + memcpy(&dmr3ambe[14], &dmrframe[20], 13); + // extract sync + dmrsync[0] = dmrframe[13] & 0x0F; + ::memcpy(&dmrsync[1], &dmrframe[14], 5); + dmrsync[6] = dmrframe[19] & 0xF0; + + // and create 3 dv frames + uint32 uiStreamId = IpToStreamId(Ip); + // frame1 + memcpy(dmrambe, &dmr3ambe[0], 9); + frames[0] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1)); + + // frame2 + memcpy(dmrambe, &dmr3ambe[9], 9); + frames[1] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2)); + + // frame3 + memcpy(dmrambe, &dmr3ambe[18], 9); + if ( uiPacketType == 3 ) + { + frames[2] = std::unique_ptr(new CDvLastFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3)); + } + else + { + frames[2] = std::unique_ptr(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3)); + } + + // check + if (frames[0] && frames[1] && frames[2]) + return true; + } + } + return false; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CDmrplusProtocol::EncodeConnectAckPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'A','C','K',' ','O','K',0x0A,0x00 }; + Buffer->Set(tag, sizeof(tag)); +} + +void CDmrplusProtocol::EncodeConnectNackPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'N','A','K',' ','O','K',0x0A,0x00 }; + Buffer->Set(tag, sizeof(tag)); +} + +bool CDmrplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02, + 0x00,0x05,0x01,0x02,0x00,0x00,0x00 + } ; + Buffer->Set(tag, sizeof(tag)); + + // uiSeqId + //Buffer->ReplaceAt(4, 2); + // uiPktType + //Buffer->ReplaceAt(8, 2); + // uiSlot + Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222)); + // uiSlotType + Buffer->Append((uint16)0xEEEE); + // uiColourCode + uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4); + Buffer->Append((uint8)uiColourCode); + Buffer->Append((uint8)uiColourCode); + // uiFrameType + Buffer->Append((uint16)0x1111); + // reserved + Buffer->Append((uint16)0x0000); + // payload + uint32 uiSrcId = Packet.GetMyCallsign().GetDmrid() & 0x00FFFFFF; + uint32 uiDstId = ModuleToDmrDestId(Packet.GetRpt2Module()) & 0x00FFFFFF; + Buffer->Append((uint8)0x00, 34); + Buffer->ReplaceAt(36, HIBYTE(HIWORD(uiSrcId))); + Buffer->ReplaceAt(38, LOBYTE(HIWORD(uiSrcId))); + Buffer->ReplaceAt(40, HIBYTE(LOWORD(uiSrcId))); + Buffer->ReplaceAt(42, LOBYTE(LOWORD(uiSrcId))); + + // reserved + Buffer->Append((uint16)0x0000); + // uiCallType + Buffer->Append((uint8)0x01); + // reserved + Buffer->Append((uint8)0x00); + // uiDstId + Buffer->Append(uiDstId); + // uiSrcId + Buffer->Append(uiSrcId); + + // done + return true; +} + +void CDmrplusProtocol::EncodeDvPacket +(const CDvHeaderPacket &Header, + const CDvFramePacket &DvFrame0, const CDvFramePacket &DvFrame1, const CDvFramePacket &DvFrame2, + uint8 seqid, CBuffer *Buffer) const +{ + + uint8 tag[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x00,0x05,0x01,0x02,0x00,0x00,0x00 + } ; + Buffer->Set(tag, sizeof(tag)); + + // uiSeqId + Buffer->ReplaceAt(4, seqid); + // uiPktType + //Buffer->ReplaceAt(8, 1); + // uiSlot + Buffer->Append((uint16)((DMRPLUS_REFLECTOR_SLOT == DMR_SLOT1) ? 0x1111 : 0x2222)); + // uiVoiceSeq + uint8 uiVoiceSeq = (DvFrame0.GetDmrPacketId() + 7) | ((DvFrame0.GetDmrPacketId() + 7) << 4); + Buffer->Append((uint8)uiVoiceSeq); + Buffer->Append((uint8)uiVoiceSeq); + // uiColourCode + uint8 uiColourCode = DMRPLUS_REFLECTOR_COLOUR | (DMRPLUS_REFLECTOR_COLOUR << 4); + Buffer->Append((uint8)uiColourCode); + Buffer->Append((uint8)uiColourCode); + // uiFrameType + Buffer->Append((uint16)0x1111); + // reserved + Buffer->Append((uint16)0x0000); + + // payload + uint32 uiSrcId = Header.GetMyCallsign().GetDmrid() & 0x00FFFFFF; + uint32 uiDstId = ModuleToDmrDestId(Header.GetRpt2Module()) & 0x00FFFFFF; + // frame0 + Buffer->ReplaceAt(26, DvFrame0.GetAmbePlus(), 9); + // 1/2 frame1 + Buffer->ReplaceAt(35, DvFrame1.GetAmbePlus(), 5); + Buffer->ReplaceAt(39, (uint8)(Buffer->at(39) & 0xF0)); + // 1/2 frame1 + Buffer->ReplaceAt(45, DvFrame1.GetAmbePlus()+4, 5); + Buffer->ReplaceAt(45, (uint8)(Buffer->at(45) & 0x0F)); + // frame2 + Buffer->ReplaceAt(50, DvFrame2.GetAmbePlus(), 9); + + // sync or embedded signaling + ReplaceEMBInBuffer(Buffer, DvFrame0.GetDmrPacketId()); + + // reserved + Buffer->Append((uint16)0x0000); + Buffer->Append((uint8)0x00); + // uiCallType + Buffer->Append((uint8)0x01); + // reserved + Buffer->Append((uint8)0x00); + // uiDstId + Buffer->Append(uiDstId); + // uiSrcId + Buffer->Append(uiSrcId); + + // handle indianess + SwapEndianess(&(Buffer->data()[26]), 34); +} + +void CDmrplusProtocol::SwapEndianess(uint8 *buffer, int len) const +{ + for ( int i = 0; i < len; i += 2 ) + { + uint8 t = buffer[i]; + buffer[i] = buffer[i+1]; + buffer[i+1] = t; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// SeqId helper + +uint8 CDmrplusProtocol::GetNextSeqId(uint8 uiSeqId) const +{ + return (uiSeqId + 1) & 0xFF; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DestId to Module helper + +char CDmrplusProtocol::DmrDstIdToModule(uint32 tg) const +{ + // is it a 4xxx ? + if ( (tg >= 4001) && (tg <= (4000 + NB_OF_MODULES)) ) + { + return ((char)(tg - 4001) + 'A'); + } + return ' '; +} + +uint32 CDmrplusProtocol::ModuleToDmrDestId(char m) const +{ + return (uint32)(m - 'A')+4001; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Buffer & LC helpers + +void CDmrplusProtocol::AppendVoiceLCToBuffer(CBuffer *buffer, uint32 uiSrcId) const +{ + uint8 payload[34]; + + // fill payload + CBPTC19696 bptc; + ::memset(payload, 0, sizeof(payload)); + // LC data + uint8 lc[12]; + { + ::memset(lc, 0, sizeof(lc)); + // uiDstId = TG9 + lc[5] = 9; + // uiSrcId + lc[6] = (uint8)LOBYTE(HIWORD(uiSrcId)); + lc[7] = (uint8)HIBYTE(LOWORD(uiSrcId)); + lc[8] = (uint8)LOBYTE(LOWORD(uiSrcId)); + // parity + uint8 parity[4]; + CRS129::encode(lc, 9, parity); + lc[9] = parity[2] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + lc[10] = parity[1] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + lc[11] = parity[0] ^ DMR_VOICE_LC_HEADER_CRC_MASK; + } + // sync + ::memcpy(payload+13, g_DmrSyncBSData, sizeof(g_DmrSyncBSData)); + // slot type + { + // slot type + uint8 slottype[3]; + ::memset(slottype, 0, sizeof(slottype)); + slottype[0] = (DMRPLUS_REFLECTOR_COLOUR << 4) & 0xF0; + slottype[0] |= (DMR_DT_VOICE_LC_HEADER << 0) & 0x0FU; + CGolay2087::encode(slottype); + payload[12U] = (payload[12U] & 0xC0U) | ((slottype[0U] >> 2) & 0x3FU); + payload[13U] = (payload[13U] & 0x0FU) | ((slottype[0U] << 6) & 0xC0U) | ((slottype[1U] >> 2) & 0x30U); + payload[19U] = (payload[19U] & 0xF0U) | ((slottype[1U] >> 2) & 0x0FU); + payload[20U] = (payload[20U] & 0x03U) | ((slottype[1U] << 6) & 0xC0U) | ((slottype[2U] >> 2) & 0x3CU); + + } + // and encode + bptc.encode(lc, payload); + + // and append + buffer->Append(payload, sizeof(payload)); +} + +void CDmrplusProtocol::ReplaceEMBInBuffer(CBuffer *buffer, uint8 uiDmrPacketId) const +{ + // voice packet A ? + if ( uiDmrPacketId == 0 ) + { + // sync + buffer->ReplaceAt(39, (uint8)(buffer->at(39) | (g_DmrSyncBSVoice[0] & 0x0F))); + buffer->ReplaceAt(40, g_DmrSyncBSVoice+1, 5); + buffer->ReplaceAt(45, (uint8)(buffer->at(45) | (g_DmrSyncBSVoice[6] & 0xF0))); + } + // voice packet B,C,D,E ? + else if ( (uiDmrPacketId >= 1) && (uiDmrPacketId <= 4 ) ) + { + // EMB LC + uint8 emb[2]; + emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + //emb[0] |= PI ? 0x08U : 0x00; + //emb[0] |= (LCSS << 1) & 0x06; + emb[1] = 0x00; + // encode + CQR1676::encode(emb); + // and append + buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F))); + buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0))); + buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0)); + buffer->ReplaceAt(41, (uint8)0); + buffer->ReplaceAt(42, (uint8)0); + buffer->ReplaceAt(43, (uint8)0); + buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F)); + buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F))); + buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0))); + } + // voice packet F + else + { + // nullptr + uint8 emb[2]; + emb[0] = (DMRMMDVM_REFLECTOR_COLOUR << 4) & 0xF0; + //emb[0] |= PI ? 0x08U : 0x00; + //emb[0] |= (LCSS << 1) & 0x06; + emb[1] = 0x00; + // encode + CQR1676::encode(emb); + // and append + buffer->ReplaceAt(39, (uint8)((buffer->at(39) & 0xF0) | ((emb[0U] >> 4) & 0x0F))); + buffer->ReplaceAt(40, (uint8)((buffer->at(40) & 0x0F) | ((emb[0U] << 4) & 0xF0))); + buffer->ReplaceAt(40, (uint8)(buffer->at(40) & 0xF0)); + buffer->ReplaceAt(41, (uint8)0); + buffer->ReplaceAt(42, (uint8)0); + buffer->ReplaceAt(43, (uint8)0); + buffer->ReplaceAt(44, (uint8)(buffer->at(44) & 0x0F)); + buffer->ReplaceAt(44, (uint8)((buffer->at(44) & 0xF0) | ((emb[1U] >> 4) & 0x0F))); + buffer->ReplaceAt(45, (uint8)((buffer->at(45) & 0x0F) | ((emb[1U] << 4) & 0xF0))); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// uiStreamId helpers + + +// uiStreamId helpers +uint32 CDmrplusProtocol::IpToStreamId(const CIp &ip) const +{ + return ip.GetAddr() ^ (uint32)(MAKEDWORD(ip.GetPort(), ip.GetPort())); +} diff --git a/src/DMRPlusProtocol.h b/src/DMRPlusProtocol.h new file mode 100644 index 0000000..c0b7c15 --- /dev/null +++ b/src/DMRPlusProtocol.h @@ -0,0 +1,116 @@ +// +// DMRPlusProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdmrplusprotocol_h +#define cdmrplusprotocol_h + +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +// DMR Plus Module ID +#define DMRPLUS_MODULE_ID 'B' + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDmrplusStreamCacheItem +{ +public: + CDmrplusStreamCacheItem() { m_uiSeqId = 0x77; } + + CDvHeaderPacket m_dvHeader; + CDvFramePacket m_dvFrame0; + CDvFramePacket m_dvFrame1; + + uint8 m_uiSeqId; +}; + + +class CDmrplusProtocol : public CProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int pytpe, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + void SendBufferToClients(const CBuffer &, uint8); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // packet decoding helpers + bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *, const CIp &); + bool IsValidDisconnectPacket(const CBuffer &, CCallsign *, char *); + bool IsValidDvHeaderPacket(const CIp &, const CBuffer &, std::unique_ptr &); + bool IsValidDvFramePacket(const CIp &, const CBuffer &, std::array, 3> &); + + // packet encoding helpers + void EncodeConnectAckPacket(CBuffer *); + void EncodeConnectNackPacket(CBuffer *); + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; + void EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket &, const CDvFramePacket &, const CDvFramePacket &, uint8, CBuffer *) const; + void SwapEndianess(uint8 *, int) const; + + // dmr SeqId helper + uint8 GetNextSeqId(uint8) const; + + // dmr DstId to Module helper + char DmrDstIdToModule(uint32) const; + uint32 ModuleToDmrDestId(char) const; + + // uiStreamId helpers + uint32 IpToStreamId(const CIp &) const; + + // Buffer & LC helpers + void AppendVoiceLCToBuffer(CBuffer *, uint32) const; + void AppendTerminatorLCToBuffer(CBuffer *, uint32) const; + void ReplaceEMBInBuffer(CBuffer *, uint8) const; + + +protected: + // for keep alive + CTimePoint m_LastKeepaliveTime; + + // for queue header caches + std::array m_StreamsCache; +}; + +//////////////////////////////////////////////////////////////////////////////////////// + + +#endif /* cdmrplusprotocol_h */ diff --git a/src/DPlusClient.cpp b/src/DPlusClient.cpp new file mode 100644 index 0000000..87cbf96 --- /dev/null +++ b/src/DPlusClient.cpp @@ -0,0 +1,61 @@ +// +// cdplusclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DPlusClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CDplusClient::CDplusClient() +{ + m_bDextraDongle = false; +} + +CDplusClient::CDplusClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ + m_bDextraDongle = false; +} + +CDplusClient::CDplusClient(const CDplusClient &client) + : CClient(client) +{ + m_bDextraDongle = client.m_bDextraDongle; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CDplusClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < DPLUS_KEEPALIVE_TIMEOUT); +} + +void CDplusClient::SetMasterOfModule(char c) +{ + CClient::SetMasterOfModule(c); + SetReflectorModule(c); +} diff --git a/src/DPlusClient.h b/src/DPlusClient.h new file mode 100644 index 0000000..fefc468 --- /dev/null +++ b/src/DPlusClient.h @@ -0,0 +1,66 @@ +// +// DPlusClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdplusclient_h +#define cdplusclient_h + +#include "cclient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDplusClient : public CClient +{ +public: + // constructors + CDplusClient(); + CDplusClient(const CCallsign &, const CIp &, char = ' '); + CDplusClient(const CDplusClient &); + + // destructor + virtual ~CDplusClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_DPLUS; } + const char *GetProtocolName(void) const { return "DPlus"; } + int GetCodec(void) const { return CODEC_AMBEPLUS; } + bool IsNode(void) const { return true; } + bool IsDextraDongle(void) const { return m_bDextraDongle; } + void SetDextraDongle(void) { m_bDextraDongle = true; } + + // status + bool IsAlive(void) const; + void SetMasterOfModule(char); + +protected: + // data + bool m_bDextraDongle; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdplusclient_h */ diff --git a/src/DPlusProtocol.cpp b/src/DPlusProtocol.cpp new file mode 100644 index 0000000..6a15dce --- /dev/null +++ b/src/DPlusProtocol.cpp @@ -0,0 +1,532 @@ +// +// cdplusprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "DPlusClient.h" +#include "DPlusProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CDplusProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + + // done + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CDplusProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + std::unique_ptr Header; + std::unique_ptr Frame; + std::unique_ptr LastFrame; + + // handle incoming packets +#if DSTAR_IPV6==true +#if DSTAR_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvFramePacket(Buffer, Frame) ) + { + OnDvFramePacketIn(Frame, &Ip); + } + else if ( IsValidDvHeaderPacket(Buffer, Header) ) + { + // is muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_DPLUS, Header->GetRpt2Module()) ) + { + // handle it + OnDvHeaderPacketIn(Header, Ip); + } + } + else if ( IsValidDvLastFramePacket(Buffer, LastFrame) ) + { + OnDvLastFramePacketIn(LastFrame, &Ip); + } + else if ( IsValidConnectPacket(Buffer) ) + { + std::cout << "DPlus connect request packet from " << Ip << std::endl; + + // acknowledge the request + Send(Buffer, Ip); + } + else if ( IsValidLoginPacket(Buffer, &Callsign) ) + { + std::cout << "DPlus login packet from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_DPLUS) ) + { + // acknowledge the request + EncodeLoginAckPacket(&Buffer); + Send(Buffer, Ip); + + // create the client and append + g_Reflector.GetClients()->AddClient(std::make_shared(Callsign, Ip)); + g_Reflector.ReleaseClients(); + } + else + { + // deny the request + EncodeLoginNackPacket(&Buffer); + Send(Buffer, Ip); + } + + } + else if ( IsValidDisconnectPacket(Buffer) ) + { + std::cout << "DPlus disconnect packet from " << Ip << std::endl; + + // find client + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Ip, PROTOCOL_DPLUS); + if ( client != nullptr ) + { + // remove it + clients->RemoveClient(client); + // and acknowledge the disconnect + EncodeDisconnectPacket(&Buffer); + Send(Buffer, Ip); + } + g_Reflector.ReleaseClients(); + } + else if ( IsValidKeepAlivePacket(Buffer) ) + { + //std::cout << "DPlus keepalive packet from " << Ip << std::endl; + + // find all clients with that callsign & ip and keep them alive + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(Ip, PROTOCOL_DPLUS, it)) != nullptr ) + { + client->Alive(); + } + g_Reflector.ReleaseClients(); + } + else + { + std::string title("Unknown DPlus packet from "); + title += Ip.GetAddress(); + Buffer.Dump(title); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + // keep client alive + if ( m_LastKeepaliveTime.DurationSinceNow() > DPLUS_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CDplusProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + // no stream open yet, open a new one + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // first, check module is valid + if ( g_Reflector.IsValidModule(rpt2.GetModule()) ) + { + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_DPLUS); + if ( client ) + { + // now we know if it's a dextra dongle or a genuine dplus node + if ( Header->GetRpt2Callsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + { + client->SetDextraDongle(); + } + // now we know its module, let's update it + if ( !client->HasModule() ) + { + client->SetModule(rpt1.GetModule()); + } + // get client callsign + rpt1 = client->GetCallsign(); + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + } + // release + g_Reflector.ReleaseClients(); + + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } + else + { + std::cout << "DPlus node " << rpt1 << " link attempt on non-existing module" << std::endl; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CDplusProtocol::HandleQueue(void) +{ + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // get our sender's id + int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); + + // check if it's header and update cache + if ( packet->IsDvHeader() ) + { + // this relies on queue feeder setting valid module id + m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((const CDvHeaderPacket &)*packet); + m_StreamsCache[iModId].m_iSeqCounter = 0; + } + + // encode it + CBuffer buffer; + if ( EncodeDvPacket(*packet, &buffer) ) + { + // and push it to all our clients who are not streaming in + // note that for dplus protocol, all stream of all modules are push to all clients + // it's client who decide which stream he's interrrested in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DPLUS, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() ) + { + // check if client is a dextra dongle + // then replace RPT2 with XRF instead of REF + // if the client type is not yet known, send bothheaders + if ( packet->IsDvHeader() ) + { + // sending header in Dplus is client specific + SendDvHeader((CDvHeaderPacket *)packet.get(), (CDplusClient *)client.get()); + } + else if ( packet->IsDvFrame() ) + { + // and send the DV frame + Send(buffer, client->GetIp()); + + // is it time to insert a DVheader copy ? + if ( (m_StreamsCache[iModId].m_iSeqCounter++ % 21) == 20 ) + { + // yes, clone it + CDvHeaderPacket packet2(m_StreamsCache[iModId].m_dvHeader); + // and send it + SendDvHeader(&packet2, (CDplusClient *)client.get()); + } + } + else + { + // otherwise, send the original packet + Send(buffer, client->GetIp()); + } + } + } + g_Reflector.ReleaseClients(); + } + } + m_Queue.Unlock(); +} + +void CDplusProtocol::SendDvHeader(CDvHeaderPacket *packet, CDplusClient *client) +{ + // encode it + CBuffer buffer; + if ( EncodeDvPacket(*packet, &buffer) ) + { + if ( (client->IsDextraDongle() || !client->HasModule()) ) + { + // clone the packet and patch it + CDvHeaderPacket packet2(*((CDvHeaderPacket *)packet)); + CCallsign rpt2 = packet2.GetRpt2Callsign(); + rpt2.PatchCallsign(0, (const uint8 *)"XRF", 3); + packet2.SetRpt2Callsign(rpt2); + + // encode it + CBuffer buffer2; + if ( EncodeDvPacket(packet2, &buffer2) ) + { + // and send it + Send(buffer2, client->GetIp()); + } + + // client type known ? + if ( !client->HasModule() ) + { + // no, send also the genuine packet + Send(buffer, client->GetIp()); + } + } + else + { + // otherwise, send the original packet + Send(buffer, client->GetIp()); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CDplusProtocol::HandleKeepalives(void) +{ + // send keepalives + CBuffer keepalive; + EncodeKeepAlivePacket(&keepalive); + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_DPLUS, it)) != nullptr ) + { + // send keepalive + //std::cout << "Sending DPlus packet @ " << client->GetIp() << std::endl; + Send(keepalive, client->GetIp()); + + // is this client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // check it's still with us + else if ( !client->IsAlive() ) + { + // no, disconnect + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect); + Send(disconnect, client->GetIp()); + + // and remove it + std::cout << "DPlus client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CDplusProtocol::IsValidConnectPacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 0x05,0x00,0x18,0x00,0x01 }; + return (Buffer == CBuffer(tag, sizeof(tag))); +} + +bool CDplusProtocol::IsValidLoginPacket(const CBuffer &Buffer, CCallsign *Callsign) +{ + uint8 Tag[] = { 0x1C,0xC0,0x04,0x00 }; + bool valid = false; + + if ( (Buffer.size() == 28) &&(::memcmp(Buffer.data(), Tag, sizeof(Tag)) == 0) ) + { + Callsign->SetCallsign(&(Buffer.data()[4]), 8); + valid = Callsign->IsValid(); + } + return valid; +} + +bool CDplusProtocol::IsValidDisconnectPacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 0x05,0x00,0x18,0x00,0x00 }; + return (Buffer == CBuffer(tag, sizeof(tag))); +} + +bool CDplusProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 0x03,0x60,0x00 }; + return (Buffer == CBuffer(tag, sizeof(tag))); +} + +bool CDplusProtocol::IsValidDvHeaderPacket(const CBuffer &Buffer, std::unique_ptr &header) +{ + if ( 58==Buffer.size() && 0x3AU==Buffer.data()[0] && 0x80U==Buffer.data()[1] && 0==Buffer.Compare((uint8 *)"DSVT", 2, 4) && 0x10U==Buffer.data()[6] && 0x20U==Buffer.data()[10] ) + { + // create packet + header = std::unique_ptr(new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[17]), *((uint16 *)&(Buffer.data()[14])), 0x80)); + // check validity of packet + if ( header && header->IsValid() ) + return true; + } + return false; +} + +bool CDplusProtocol::IsValidDvFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 29==Buffer.size() && 0x1DU==Buffer.data()[0] && 0x80U==Buffer.data()[1] && 0==Buffer.Compare((uint8 *)"DSVT", 2, 4) && 0x20U==Buffer.data()[6] && 0x20U==Buffer.data()[10] ) + { + // create packet + dvframe = std::unique_ptr(new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[17]), *((uint16 *)&(Buffer.data()[14])), Buffer.data()[16])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +bool CDplusProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 32==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 2, 4) && 0x20U==Buffer.data()[0] && 0x80U==Buffer.data()[1] && 0x20U==Buffer.data()[6] && 0x20U==Buffer.data()[10] ) + { + // create packet + dvframe = std::unique_ptr(new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[17]), *((uint16 *)&(Buffer.data()[14])), Buffer.data()[16])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CDplusProtocol::EncodeKeepAlivePacket(CBuffer *Buffer) +{ + uint8 tag[] = { 0x03,0x60,0x00 }; + Buffer->Set(tag, sizeof(tag)); +} + +void CDplusProtocol::EncodeLoginAckPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 0x08,0xC0,0x04,0x00,'O','K','R','W' }; + Buffer->Set(tag, sizeof(tag)); +} + +void CDplusProtocol::EncodeLoginNackPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 0x08,0xC0,0x04,0x00,'B','U','S','Y' }; + Buffer->Set(tag, sizeof(tag)); +} + +void CDplusProtocol::EncodeDisconnectPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 0x05,0x00,0x18,0x00,0x00 }; + Buffer->Set(tag, sizeof(tag)); +} + + +bool CDplusProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 0x3A,0x80,0x44,0x53,0x56,0x54,0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + struct dstar_header DstarHeader; + + Packet.ConvertToDstarStruct(&DstarHeader); + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)0x80); + Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header)); + + return true; +} + +bool CDplusProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 0x1D,0x80,0x44,0x53,0x56,0x54,0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)(Packet.GetPacketId() % 21)); + Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE); + + return true; + +} + +bool CDplusProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag1[] = { 0x20,0x80,0x44,0x53,0x56,0x54,0x20,0x00,0x81,0x00,0x20,0x00,0x01,0x02 }; + uint8 tag2[] = { 0x55,0xC8,0x7A,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x25,0x1A,0xC6 }; + + Buffer->Set(tag1, sizeof(tag1)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40)); + Buffer->Append(tag2, sizeof(tag2)); + + return true; +} diff --git a/src/DPlusProtocol.h b/src/DPlusProtocol.h new file mode 100644 index 0000000..631f06b --- /dev/null +++ b/src/DPlusProtocol.h @@ -0,0 +1,99 @@ +// +// DPlusProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdplusprotocol_h +#define cdplusprotocol_h + +#include "Timer.h" +#include "Protocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDplusClient; + +class CDPlusStreamCacheItem +{ +public: + CDPlusStreamCacheItem() { m_iSeqCounter = 0; } + + CDvHeaderPacket m_dvHeader; + uint8 m_iSeqCounter; +}; + +class CDplusProtocol : public CProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + void SendDvHeader(CDvHeaderPacket *, CDplusClient *); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // packet decoding helpers + bool IsValidConnectPacket(const CBuffer &); + bool IsValidLoginPacket(const CBuffer &, CCallsign *); + bool IsValidDisconnectPacket(const CBuffer &); + bool IsValidKeepAlivePacket(const CBuffer &); + bool IsValidDvHeaderPacket(const CBuffer &, std::unique_ptr &); + bool IsValidDvFramePacket(const CBuffer &, std::unique_ptr &); + bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr &); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *); + void EncodeLoginAckPacket(CBuffer *); + void EncodeLoginNackPacket(CBuffer *); + void EncodeDisconnectPacket(CBuffer *); + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; + bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; + bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; + + +protected: + // for keep alive + CTimePoint m_LastKeepaliveTime; + + // for queue header caches + std::array m_StreamsCache; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdplusprotocol_h */ diff --git a/src/DVFramePacket.cpp b/src/DVFramePacket.cpp new file mode 100644 index 0000000..94cdd44 --- /dev/null +++ b/src/DVFramePacket.cpp @@ -0,0 +1,157 @@ +// +// cdvframepacket.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#include "Main.h" +#include +#include "DVFramePacket.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CDvFramePacket::CDvFramePacket() +{ + ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe)); + ::memset(m_uiDvData, 0, sizeof(m_uiDvData)); +#ifndef NO_XLX + ::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus)); + ::memset(m_uiDvSync, 0, sizeof(m_uiDvSync)); +#endif +}; + +// dstar constructor + +CDvFramePacket::CDvFramePacket(const struct dstar_dvframe *dvframe, uint16 sid, uint8 pid) + : CPacket(sid, pid) +{ + ::memcpy(m_uiAmbe, dvframe->AMBE, sizeof(m_uiAmbe)); + ::memcpy(m_uiDvData, dvframe->DVDATA, sizeof(m_uiDvData)); +#ifndef NO_XLX + ::memset(m_uiAmbePlus, 0, sizeof(m_uiAmbePlus)); + ::memset(m_uiDvSync, 0, sizeof(m_uiDvSync)); +#endif +} + +#ifndef NO_XLX +// dmr constructor + +CDvFramePacket::CDvFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid, uint8 pid, uint8 spid) + : CPacket(sid, pid, spid) +{ + ::memcpy(m_uiAmbePlus, ambe, sizeof(m_uiAmbePlus)); + ::memcpy(m_uiDvSync, sync, sizeof(m_uiDvSync)); + ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe)); + ::memset(m_uiDvData, 0, sizeof(m_uiDvData)); +} + +// ysf constructor + +CDvFramePacket::CDvFramePacket(const uint8 *ambe, uint16 sid, uint8 pid, uint8 spid, uint8 fid) + : CPacket(sid, pid, spid, fid) +{ + ::memcpy(m_uiAmbePlus, ambe, sizeof(m_uiAmbePlus)); + ::memset(m_uiDvSync, 0, sizeof(m_uiDvSync)); + ::memset(m_uiAmbe, 0, sizeof(m_uiAmbe)); + ::memset(m_uiDvData, 0, sizeof(m_uiDvData)); +} + +// xlx constructor + +CDvFramePacket::CDvFramePacket +(uint16 sid, + uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata, + uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync) + : CPacket(sid, dstarpid, dmrpid, dprspid, 0xFF, 0xFF, 0xFF) +{ + ::memcpy(m_uiAmbe, dstarambe, sizeof(m_uiAmbe)); + ::memcpy(m_uiDvData, dstardvdata, sizeof(m_uiDvData)); + ::memcpy(m_uiAmbePlus, dmrambe, sizeof(m_uiAmbePlus)); + ::memcpy(m_uiDvSync, dmrsync, sizeof(m_uiDvSync)); +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// virtual duplication + +std::unique_ptr CDvFramePacket::Duplicate(void) const +{ + return std::unique_ptr(new CDvFramePacket(*this)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +const uint8 *CDvFramePacket::GetAmbe(uint8 uiCodec) const +{ + switch (uiCodec) + { + case CODEC_AMBEPLUS: + return m_uiAmbe; +#ifndef NO_XLX + case CODEC_AMBE2PLUS: + return m_uiAmbePlus; +#endif + default: + return nullptr; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// set + +void CDvFramePacket::SetDvData(uint8 *DvData) +{ + ::memcpy(m_uiDvData, DvData, sizeof(m_uiDvData)); +} + +void CDvFramePacket::SetAmbe(uint8 uiCodec, uint8 *Ambe) +{ + switch (uiCodec) + { + case CODEC_AMBEPLUS: + ::memcpy(m_uiAmbe, Ambe, sizeof(m_uiAmbe)); + break; +#ifndef NO_XLX + case CODEC_AMBE2PLUS: + ::memcpy(m_uiAmbePlus, Ambe, sizeof(m_uiAmbe)); + break; +#endif + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CDvFramePacket::operator ==(const CDvFramePacket &DvFrame) const +{ + return ( (::memcmp(m_uiAmbe, DvFrame.m_uiAmbe, sizeof(m_uiAmbe)) == 0) + && (::memcmp(m_uiDvData, DvFrame.m_uiDvData, sizeof(m_uiDvData)) == 0) +#ifndef NO_XLX + && (::memcmp(m_uiAmbePlus, DvFrame.m_uiAmbePlus, sizeof(m_uiAmbePlus)) == 0) + && (::memcmp(m_uiDvSync, DvFrame.m_uiDvSync, sizeof(m_uiDvSync)) == 0) +#endif + ); +} diff --git a/src/DVFramePacket.h b/src/DVFramePacket.h new file mode 100644 index 0000000..52a0762 --- /dev/null +++ b/src/DVFramePacket.h @@ -0,0 +1,108 @@ +// +// DVFramePacket.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdvframepacket_h +#define cdvframepacket_h + +#include "Packet.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// defines + +#define AMBE_SIZE 9 +#define DVDATA_SIZE 3 + +#define AMBEPLUS_SIZE 9 +#define DVSYNC_SIZE 7 + +// typedef & structures + +struct __attribute__ ((__packed__))dstar_dvframe +{ + uint8 AMBE[AMBE_SIZE]; + uint8 DVDATA[DVDATA_SIZE]; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDvFramePacket : public CPacket +{ + //friend class CCodecStream; +public: + // constructor + CDvFramePacket(); + CDvFramePacket(const struct dstar_dvframe *, uint16, uint8); +#ifndef NO_XLX + CDvFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8); + CDvFramePacket(const uint8 *, uint16, uint8, uint8, uint8); + CDvFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *); +#endif + + // virtual duplication + std::unique_ptr Duplicate(void) const; + + // identity + bool IsDvFrame(void) const { return true; } +#ifndef NO_XLX + bool HasTranscodableAmbe(void) const { return true; } +#endif + + // get + const uint8 *GetAmbe(uint8) const; + const uint8 *GetAmbe(void) const { return m_uiAmbe; } +#ifndef NO_XLX + const uint8 *GetAmbePlus(void) const { return m_uiAmbePlus; } + const uint8 *GetDvSync(void) const { return m_uiDvSync; } +#endif + const uint8 *GetDvData(void) const { return m_uiDvData; } + + // set + void SetDvData(uint8 *); + void SetAmbe(uint8, uint8 *); + + // operators + bool operator ==(const CDvFramePacket &) const; + +protected: + // get + uint8 *GetAmbeData(void) { return m_uiAmbe; } +#ifndef NO_XLX + uint8 *GetAmbePlusData(void) { return m_uiAmbePlus; } +#endif + +protected: + // data (dstar) + uint8 m_uiAmbe[AMBE_SIZE]; + uint8 m_uiDvData[DVDATA_SIZE]; +#ifndef NO_XLX + // data (dmr) + uint8 m_uiAmbePlus[AMBEPLUS_SIZE]; + uint8 m_uiDvSync[DVSYNC_SIZE]; +#endif +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdvframepacket_h */ diff --git a/src/DVHeaderPacket.cpp b/src/DVHeaderPacket.cpp new file mode 100644 index 0000000..7ceb682 --- /dev/null +++ b/src/DVHeaderPacket.cpp @@ -0,0 +1,159 @@ +// +// cdvheaderpacket.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include +#include "DMRIdDir.h" +#include "DVHeaderPacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CDvHeaderPacket::CDvHeaderPacket() +{ + m_uiFlag1 = 0; + m_uiFlag2 = 0; + m_uiFlag3 = 0; + m_uiCrc = 0; +} + +// dstar constructor + +CDvHeaderPacket::CDvHeaderPacket(const struct dstar_header *buffer, uint16 sid, uint8 pid) + : CPacket(sid, pid) +{ + m_uiFlag1 = buffer->Flag1; + m_uiFlag2 = buffer->Flag2; + m_uiFlag3 = buffer->Flag3; + m_csUR.SetCallsign(buffer->UR, CALLSIGN_LEN); + m_csRPT1.SetCallsign(buffer->RPT1, CALLSIGN_LEN); + m_csRPT2.SetCallsign(buffer->RPT2, CALLSIGN_LEN); + m_csMY.SetCallsign(buffer->MY, CALLSIGN_LEN); + m_csMY.SetSuffix(buffer->SUFFIX, CALLSUFFIX_LEN); + m_uiCrc = buffer->Crc; +} + +#ifndef NO_XLX +// dmr constructor + +CDvHeaderPacket::CDvHeaderPacket(uint32 my, const CCallsign &ur, const CCallsign &rpt1, const CCallsign &rpt2, uint16 sid, uint8 pid, uint8 spid) + : CPacket(sid, pid, spid) +{ + m_uiFlag1 = 0; + m_uiFlag2 = 0; + m_uiFlag3 = 0; + m_uiCrc = 0; + m_csUR = ur; + m_csRPT1 = rpt1; + m_csRPT2 = rpt2; + m_csMY = CCallsign("", my); +} + +// YSF constructor + +CDvHeaderPacket::CDvHeaderPacket(const CCallsign &my, const CCallsign &ur, const CCallsign &rpt1, const CCallsign &rpt2, uint16 sid, uint8 pid) + : CPacket(sid, pid, 0, 0) +{ + m_uiFlag1 = 0; + m_uiFlag2 = 0; + m_uiFlag3 = 0; + m_uiCrc = 0; + m_csUR = ur; + m_csRPT1 = rpt1; + m_csRPT2 = rpt2; + m_csMY = my; +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// virtual duplication + +std::unique_ptr CDvHeaderPacket::Duplicate(void) const +{ + return std::unique_ptr(new CDvHeaderPacket(*this)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// conversion + +void CDvHeaderPacket::ConvertToDstarStruct(struct dstar_header *buffer) const +{ + ::memset(buffer, 0, sizeof(struct dstar_header)); + buffer->Flag1 = m_uiFlag1; + buffer->Flag2 = m_uiFlag2; + buffer->Flag3 = m_uiFlag3; + m_csUR.GetCallsign(buffer->UR); + m_csRPT1.GetCallsign(buffer->RPT1); + m_csRPT2.GetCallsign(buffer->RPT2); + m_csMY.GetCallsign(buffer->MY); + m_csMY.GetSuffix(buffer->SUFFIX); + buffer->Crc = m_uiCrc; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// get valid + +bool CDvHeaderPacket::IsValid(void) const +{ + bool valid = CPacket::IsValid(); + + valid &= m_csRPT1.IsValid(); + valid &= m_csRPT2.IsValid(); + valid &= m_csMY.IsValid(); + + return valid; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CDvHeaderPacket::operator ==(const CDvHeaderPacket &Header) const +{ + return ( (m_uiFlag1 == Header.m_uiFlag1) && + (m_uiFlag2 == Header.m_uiFlag2) && + (m_uiFlag3 == Header.m_uiFlag3) && + (m_csUR == Header.m_csUR) && + (m_csRPT1 == Header.m_csRPT1) && + (m_csRPT2 == Header.m_csRPT2) && + (m_csMY == Header.m_csMY) ); +} + +#ifdef IMPLEMENT_CDVHEADERPACKET_CONST_CHAR_OPERATOR +CDvHeaderPacket::operator const char *() const +{ + char *sz = (char *)(const char *)m_sz; + + std::sprintf(sz, "%02X %02X %02X\n%s\n%s\n%s\n%s", + m_uiFlag1, m_uiFlag2, m_uiFlag3, + (const char *)m_csUR, + (const char *)m_csRPT1, + (const char *)m_csRPT2, + (const char *)m_csMY); + + return m_sz; +} +#endif diff --git a/src/DVHeaderPacket.h b/src/DVHeaderPacket.h new file mode 100644 index 0000000..9c493c6 --- /dev/null +++ b/src/DVHeaderPacket.h @@ -0,0 +1,121 @@ +// +// DVHeaderPacket.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdvheaderpacket_h +#define cdvheaderpacket_h + +#include "Callsign.h" +#include "Packet.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// implementation details + +//#define IMPLEMENT_CDVHEADERPACKET_CONST_CHAR_OPERATOR + +//////////////////////////////////////////////////////////////////////////////////////// +// typedef & structures + +struct __attribute__ ((__packed__))dstar_header +{ + // flags + uint8 Flag1; + uint8 Flag2; + uint8 Flag3; + // callsigns + uint8 RPT2[CALLSIGN_LEN]; + uint8 RPT1[CALLSIGN_LEN]; + uint8 UR[CALLSIGN_LEN]; + uint8 MY[CALLSIGN_LEN]; + uint8 SUFFIX[CALLSUFFIX_LEN]; + // crc + uint16 Crc; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDvHeaderPacket : public CPacket +{ +public: + // constructor + CDvHeaderPacket(); + CDvHeaderPacket(const struct dstar_header *, uint16, uint8); +#ifndef NO_XLX + CDvHeaderPacket(uint32, const CCallsign &, const CCallsign &, const CCallsign &, uint16, uint8, uint8); + CDvHeaderPacket(const CCallsign &, const CCallsign &, const CCallsign &, const CCallsign &, uint16, uint8); +#endif + + // virtual duplication + std::unique_ptr Duplicate(void) const; + + // identity + bool IsDvHeader(void) const { return true; } + + // conversion + void ConvertToDstarStruct(struct dstar_header *) const; + + // get valid + bool IsValid(void) const; + + // get callsigns + const CCallsign &GetUrCallsign(void) const { return m_csUR; } + const CCallsign &GetRpt1Callsign(void) const { return m_csRPT1; } + const CCallsign &GetRpt2Callsign(void) const { return m_csRPT2; } + const CCallsign &GetMyCallsign(void) const { return m_csMY; } + + // get modules + char GetUrModule(void) const { return m_csUR.GetModule(); } + char GetRpt1Module(void) const { return m_csRPT1.GetModule(); } + char GetRpt2Module(void) const { return m_csRPT2.GetModule(); } + char GetMyModule(void) const { return m_csMY.GetModule(); } + + // set callsigns + void SetRpt2Callsign(const CCallsign &cs) { m_csRPT2 = cs; } + void SetRpt2Module(char c) { m_csRPT2.SetModule(c); } + + // operators + bool operator ==(const CDvHeaderPacket &) const; +#ifdef IMPLEMENT_CDVHEADERPACKET_CONST_CHAR_OPERATOR + operator const char *() const; +#endif + +protected: + // data + uint8 m_uiFlag1; + uint8 m_uiFlag2; + uint8 m_uiFlag3; + CCallsign m_csUR; + CCallsign m_csRPT1; + CCallsign m_csRPT2; + CCallsign m_csMY; + uint16 m_uiCrc; +#ifdef IMPLEMENT_CDVHEADERPACKET_CONST_CHAR_OPERATOR + // buffer + char m_sz[256]; +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdvheaderpacket_h */ diff --git a/src/DVLastFramePacket.cpp b/src/DVLastFramePacket.cpp new file mode 100644 index 0000000..323dc7b --- /dev/null +++ b/src/DVLastFramePacket.cpp @@ -0,0 +1,83 @@ +// +// cdvlastframepacket.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 03/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DVLastFramePacket.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CDvLastFramePacket::CDvLastFramePacket() +{ +} + +// dstar constructor + +CDvLastFramePacket::CDvLastFramePacket(const struct dstar_dvframe *DvFrame, uint16 sid, uint8 pid) + : CDvFramePacket(DvFrame, sid, pid) +{ +} + +#ifndef NO_XLX +// dmr constructor + +CDvLastFramePacket::CDvLastFramePacket(const uint8 *ambe, const uint8 *sync, uint16 sid, uint8 pid, uint8 spid) + : CDvFramePacket(ambe, sync, sid, pid, spid) +{ +} + +// dstar + dmr constructor + +CDvLastFramePacket::CDvLastFramePacket +(uint16 sid, + uint8 dstarpid, const uint8 *dstarambe, const uint8 *dstardvdata, + uint8 dmrpid, uint8 dprspid, const uint8 *dmrambe, const uint8 *dmrsync) + : CDvFramePacket(sid, dstarpid, dstarambe, dstardvdata, dmrpid, dprspid, dmrambe, dmrsync) +{ +} + +// ysf constructor + +CDvLastFramePacket::CDvLastFramePacket(const uint8 *ambe, uint16 sid, uint8 pid, uint8 spid, uint8 fid) + : CDvFramePacket(ambe, sid, pid, spid, fid) +{ +} +#endif + + +// copy constructor + +CDvLastFramePacket::CDvLastFramePacket(const CDvLastFramePacket &DvFrame) + : CDvFramePacket(DvFrame) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// virtual duplication + +std::unique_ptr CDvLastFramePacket::Duplicate(void) const +{ + return std::unique_ptr(new CDvLastFramePacket(*this)); +} diff --git a/src/DVLastFramePacket.h b/src/DVLastFramePacket.h new file mode 100644 index 0000000..b3ebe85 --- /dev/null +++ b/src/DVLastFramePacket.h @@ -0,0 +1,61 @@ +// +// DVLastFramePacket.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 03/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cdvlastframepacket_h +#define cdvlastframepacket_h + + +#include "DVFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// defines + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CDvLastFramePacket : public CDvFramePacket +{ +public: + // constructor + CDvLastFramePacket(); + CDvLastFramePacket(const struct dstar_dvframe *, uint16, uint8); +#ifndef NO_XLX + CDvLastFramePacket(const uint8 *, const uint8 *, uint16, uint8, uint8); + CDvLastFramePacket(const uint8 *, uint16, uint8, uint8, uint8); + CDvLastFramePacket(uint16, uint8, const uint8 *, const uint8 *, uint8, uint8, const uint8 *, const uint8 *); +#endif + CDvLastFramePacket(const CDvLastFramePacket &); + + // virtual duplication + std::unique_ptr Duplicate(void) const; + + // identity + bool IsLastPacket(void) const { return true; } + bool HasTranscodableAmbe(void) const { return false; } +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cdvlastframepacket_h */ diff --git a/src/G3Client.cpp b/src/G3Client.cpp new file mode 100644 index 0000000..e7b00f3 --- /dev/null +++ b/src/G3Client.cpp @@ -0,0 +1,53 @@ +// +// cg3client.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 03/06/2019. +// Copyright © 2019 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "G3Client.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CG3Client::CG3Client() +{ +} + +CG3Client::CG3Client(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ + +} + +CG3Client::CG3Client(const CG3Client &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CG3Client::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < G3_KEEPALIVE_TIMEOUT); +} diff --git a/src/G3Client.h b/src/G3Client.h new file mode 100644 index 0000000..9f08d50 --- /dev/null +++ b/src/G3Client.h @@ -0,0 +1,62 @@ +// +// G3Client.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 03/06/2019. +// Copyright © 2019 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cg3client_h +#define cg3client_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CG3Client : public CClient +{ +public: + // constructors + CG3Client(); + CG3Client(const CCallsign &, const CIp &, char = ' '); + CG3Client(const CG3Client &); + + // destructor + virtual ~CG3Client() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_G3; } + const char *GetProtocolName(void) const { return "Terminal/AP"; } + int GetCodec(void) const { return CODEC_AMBEPLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; + +protected: + // data +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cg3client_h */ diff --git a/src/G3Protocol.cpp b/src/G3Protocol.cpp new file mode 100644 index 0000000..965118c --- /dev/null +++ b/src/G3Protocol.cpp @@ -0,0 +1,785 @@ +// +// cg3protocol.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 03/06/2019. +// Copyright © 2019 Marius Petrescu (YO2LOJ). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include +#include "G3Client.h" +#include "G3Protocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + +#include +#include + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CG3Protocol::Initialize(const char */*type*/, const int /*type*/, const uint16 /*port*/, const bool /*has_ipv4*/, const bool /*has_ipv6*/) +// everything is hard coded until ICOM gets their act together and start supporting IPv6 +{ + ReadOptions(); + + // init reflector apparent callsign + m_ReflectorCallsign = g_Reflector.GetCallsign(); + + // reset stop flag + keep_running = true; + + // update the reflector callsign + //m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)"XLX", 3); + + // create our sockets + auto s = g_Reflector.m_Address.GetV4Address(PROTOCOL_G3); + CIp ip(AF_INET, G3_DV_PORT, s.c_str()); + if ( ip.IsSet() ) + { + if (! m_Socket4.Open(ip)) + return false; + } + else + return false; + + std::cout << "Listening on " << ip << std::endl; + + //create helper socket + ip.SetPort(G3_PRESENCE_PORT); + if (! m_PresenceSocket.Open(ip)) + { + std::cerr << "Error opening socket on port UDP" << G3_PRESENCE_PORT << " on ip " << ip << std::endl; + return false; + } + + ip.SetPort(G3_CONFIG_PORT); + if (! m_ConfigSocket.Open(ip)) + { + std::cerr << "Error opening G3 config socket on port UDP" << G3_CONFIG_PORT << " on ip " << ip << std::endl; + return false; + } + + if (! m_IcmpRawSocket.Open(IPPROTO_ICMP)) + { + std::cerr << "Error opening raw socket for ICMP" << std::endl; + return false; + } + + // start helper threads + m_Future = std::async(std::launch::async, &CG3Protocol::Thread, this); + m_PresenceFuture = std::async(std::launch::async, &CG3Protocol::PresenceThread, this); + m_ConfigFuture = std::async(std::launch::async, &CG3Protocol::ConfigThread, this); + m_IcmpFuture = std::async(std::launch::async, &CG3Protocol::IcmpThread, this); + + // update time + m_LastKeepaliveTime.Now(); + + std::cout << "Initialized G3 Protocol, all threads started" << std::endl; + return true; +} + +void CG3Protocol::Close(void) +{ + if (m_PresenceFuture.valid()) + { + m_PresenceFuture.get(); + } + + if (m_ConfigFuture.valid()) + { + m_ConfigFuture.get(); + } + + if (m_IcmpFuture.valid()) + { + m_IcmpFuture.get(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// private threads + +void CG3Protocol::PresenceThread() +{ + while (keep_running) + { + PresenceTask(); + } +} + +void CG3Protocol::ConfigThread() +{ + while (keep_running) + { + ConfigTask(); + } +} + +void CG3Protocol::IcmpThread() +{ + while (keep_running) + { + IcmpTask(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// presence task + +void CG3Protocol::PresenceTask(void) +{ + CBuffer Buffer; + CIp ReqIp; + CCallsign Callsign; + CCallsign Owner; + CCallsign Terminal; + + + if ( m_PresenceSocket.Receive(Buffer, ReqIp, 20) ) + { + + CIp Ip(ReqIp); + Ip.SetPort(G3_DV_PORT); + + if (Buffer.size() == 32) + { + Callsign.SetCallsign(&Buffer.data()[8], 8); + Owner.SetCallsign(&Buffer.data()[16], 8); + Terminal.SetCallsign(&Buffer.data()[24], 8); + + std::cout << "Presence from owner " << Owner << " on " << Ip << " as " << Callsign << " on terminal " << Terminal << std::endl; + + // accept + Buffer.data()[2] = 0x80; // response + Buffer.data()[3] = 0x00; // ok + + if (m_GwAddress == 0) + { + Buffer.Append(*(uint32 *)m_ConfigSocket.GetLocalAddr()); + } + else + { + Buffer.Append(m_GwAddress); + } + + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrextant = nullptr; + while ( (extant = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + CIp ClIp = extant->GetIp(); + if (ClIp.GetAddr() == Ip.GetAddr()) + { + break; + } + } + + if (extant == nullptr) + { + it = clients->begin(); + + // do we already have a client with the same call (IP changed)? + while ( (extant = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + if (extant->GetCallsign().HasSameCallsign(Terminal)) + { + //delete old client + clients->RemoveClient(extant); + break; + } + } + + // create new client and append + clients->AddClient(std::make_shared(Terminal, Ip)); + } + else + { + // client changed callsign + if (!extant->GetCallsign().HasSameCallsign(Terminal)) + { + //delete old client + clients->RemoveClient(extant); + + // create new client and append + clients->AddClient(std::make_shared(Terminal, Ip)); + } + } + g_Reflector.ReleaseClients(); + + m_PresenceSocket.Send(Buffer, ReqIp); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// configuration task + +void CG3Protocol::ConfigTask(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Call; + bool isRepeaterCall; + + if ( m_ConfigSocket.Receive(&Buffer, &Ip, 20) != -1 ) + { + if (Buffer.size() == 16) + { + if (memcmp(&Buffer.data()[8], " ", 8) == 0) + { + Call.SetCallsign(GetReflectorCallsign(), 8); + } + else + { + Call.SetCallsign(&Buffer.data()[8], 8); + } + + isRepeaterCall = ((Buffer.data()[2] & 0x10) == 0x10); + + std::cout << "Config request from " << Ip << " for " << Call << " (" << ((char *)(isRepeaterCall)?"repeater":"routed") << ")" << std::endl; + + //std::cout << "Local address: " << inet_ntoa(*m_ConfigSocket.GetLocalAddr()) << std::endl; + + Buffer.data()[2] |= 0x80; // response + + if (isRepeaterCall) + { + if ((Call.HasSameCallsign(GetReflectorCallsign())) && (g_Reflector.IsValidModule(Call.GetModule()))) + { + Buffer.data()[3] = 0x00; // ok + } + else + { + std::cout << "Module " << Call << " invalid" << std::endl; + Buffer.data()[3] = 0x01; // reject + } + } + else + { + // reject routed calls for now + Buffer.data()[3] = 0x01; // reject + } + + char module = Call.GetModule(); + + if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) + { + // restricted + std::cout << "Module " << Call << " restricted by configuration" << std::endl; + Buffer.data()[3] = 0x01; // reject + } + + // UR + Buffer.resize(8); + Buffer.Append((uint8 *)(const char *)Call, CALLSIGN_LEN - 1); + Buffer.Append((uint8)module); + + // RPT1 + Buffer.Append((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN - 1); + Buffer.Append((uint8)'G'); + + // RPT2 + Buffer.Append((uint8 *)(const char *)GetReflectorCallsign(), CALLSIGN_LEN - 1); + + if (isRepeaterCall) + { + Buffer.Append((uint8)Call.GetModule()); + } + else + { + // routed - no module for now + Buffer.Append((uint8)' '); + } + + if (Buffer.data()[3] == 0x00) + { + std::cout << "External G3 gateway address " << inet_ntoa(*(in_addr *)&m_GwAddress) << std::endl; + + if (m_GwAddress == 0) + { + Buffer.Append(*(uint32 *)m_ConfigSocket.GetLocalAddr()); + } + else + { + Buffer.Append(m_GwAddress); + } + } + else + { + Buffer.Append(0u); + } + + m_ConfigSocket.Send(Buffer, Ip); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// icmp task + +void CG3Protocol::IcmpTask(void) +{ + uint8_t Buffer[RAW_BUFFER_LENMAX]; + CIp Ip; + int iIcmpType; + + if ((iIcmpType = m_IcmpRawSocket.IcmpReceive(Buffer, &Ip, 20)) != -1) + { + if (iIcmpType == ICMP_DEST_UNREACH) + { + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + CIp ClientIp = client->GetIp(); + if (ClientIp.GetAddr() == Ip.GetAddr()) + { + clients->RemoveClient(client); + } + } + g_Reflector.ReleaseClients(); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// DV task + +void CG3Protocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + char ToLinkModule; + int ProtRev; + std::unique_ptr Header; + std::unique_ptr Frame; + std::unique_ptr LastFrame; + + // any incoming packet ? + if ( m_Socket4.Receive(Buffer, Ip, 20) ) + { + CIp ClIp; + CIp *BaseIp = nullptr; + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + ClIp = client->GetIp(); + if (ClIp.GetAddr() == Ip.GetAddr()) + { + BaseIp = &ClIp; + client->Alive(); + // supress host checks - no ping needed to trigger potential ICMPs + // the regular data flow will do it + m_LastKeepaliveTime.Now(); + break; + } + } + g_Reflector.ReleaseClients(); + + if (BaseIp != nullptr) + { + // crack the packet + if ( IsValidDvFramePacket(Buffer, Frame) ) + { + OnDvFramePacketIn(Frame, BaseIp); + } + else if ( IsValidDvHeaderPacket(Buffer, Header) ) + { + // callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_G3, Header->GetRpt2Module()) ) + { + // handle it + OnDvHeaderPacketIn(Header, *BaseIp); + } + } + else if ( IsValidDvLastFramePacket(Buffer, LastFrame) ) + { + OnDvLastFramePacketIn(LastFrame, BaseIp); + } + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + // keep alive during idle if needed + if ( m_LastKeepaliveTime.DurationSinceNow() > G3_KEEPALIVE_PERIOD ) + { + // handle keep alives + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + + // reload option if needed - called once every G3_KEEPALIVE_PERIOD + NeedReload(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CG3Protocol::HandleQueue(void) +{ + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // supress host checks + m_LastKeepaliveTime.Now(); + + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // encode it + CBuffer buffer; + if ( EncodeDvPacket(*packet, &buffer) ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // not busy, send the packet + int n = packet->IsDvHeader() ? 5 : 1; + for ( int i = 0; i < n; i++ ) + { + Send(buffer, client->GetIp()); + } + } + } + g_Reflector.ReleaseClients(); + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CG3Protocol::HandleKeepalives(void) +{ + // G3 Terminal mode does not support keepalive + // We will send some short packed and expect + // A ICMP unreachable on failure + CBuffer keepalive((uint8 *)"PING", 4); + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + if (!client->IsAlive()) + { + clients->RemoveClient(client); + } + else + { + // send keepalive packet + Send(keepalive, client->GetIp()); + } + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CG3Protocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId(), &Ip); + + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + // no stream open yet, open a new one + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // find this client + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + CIp ClIp = client->GetIp(); + if (ClIp.GetAddr() == Ip.GetAddr()) + { + break; + } + } + + if ( client ) + { + + // move it to the proper module + if (m_ReflectorCallsign.HasSameCallsign(rpt2)) + { + if (client->GetReflectorModule() != rpt2.GetModule()) + { + auto new_module = rpt2.GetModule(); + if (strchr(m_Modules.c_str(), '*') || strchr(m_Modules.c_str(), new_module)) + { + client->SetReflectorModule(new_module); + } + else + { + g_Reflector.ReleaseClients(); + return; + } + } + + // get client callsign + rpt1 = client->GetCallsign(); + + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } + } + // release + g_Reflector.ReleaseClients(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CG3Protocol::IsValidDvHeaderPacket(const CBuffer &Buffer, std::unique_ptr &header) +{ + if ( 56==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x10U==Buffer.data()[4] && 0x20U==Buffer.data()[8] ) + { + // create packet + header = std::unique_ptr(new CDvHeaderPacket((struct dstar_header *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), 0x80)); + // check validity of packet + if ( header && header->IsValid() ) + return true; + } + return false; +} + +bool CG3Protocol::IsValidDvFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 27==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && 0U==(Buffer.data()[14] & 0x40U) ) + { + // create packet + dvframe = std::unique_ptr(new CDvFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), Buffer.data()[14])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +bool CG3Protocol::IsValidDvLastFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + if ( 27==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && (Buffer.data()[14] & 0x40U) ) + { + // create packet + dvframe = std::unique_ptr(new CDvLastFramePacket((struct dstar_dvframe *)&(Buffer.data()[15]), *((uint16 *)&(Buffer.data()[12])), Buffer.data()[14])); + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +bool CG3Protocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x10,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + struct dstar_header DstarHeader; + + Packet.ConvertToDstarStruct(&DstarHeader); + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)0x80); + Buffer->Append((uint8 *)&DstarHeader, sizeof(struct dstar_header)); + + return true; +} + +bool CG3Protocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)(Packet.GetPacketId() % 21)); + Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE); + + return true; + +} + +bool CG3Protocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag1[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + uint8 tag2[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x1A,0xC6 }; + + Buffer->Set(tag1, sizeof(tag1)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40)); + Buffer->Append(tag2, sizeof(tag2)); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// option helpers + +char *CG3Protocol::TrimWhiteSpaces(char *str) +{ + char *end; + while ((*str == ' ') || (*str == '\t')) str++; + if (*str == 0) + return str; + end = str + strlen(str) - 1; + while ((end > str) && ((*end == ' ') || (*end == '\t') || (*end == '\r'))) + end--; + *(end + 1) = 0; + return str; +} + + +void CG3Protocol::NeedReload(void) +{ + struct stat fileStat; + + if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) + { + if (m_LastModTime != fileStat.st_mtime) + { + ReadOptions(); + + // we have new options - iterate on clients for potential removal + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_G3, it)) != nullptr ) + { + char module = client->GetReflectorModule(); + if (!strchr(m_Modules.c_str(), module) && !strchr(m_Modules.c_str(), '*')) + { + clients->RemoveClient(client); + } + } + g_Reflector.ReleaseClients(); + } + } +} + +void CG3Protocol::ReadOptions(void) +{ + char sz[256]; + int opts = 0; + + + std::ifstream file(TERMINALOPTIONS_PATH); + if (file.is_open()) + { + m_GwAddress = 0u; + m_Modules = "*"; + + while (file.getline(sz, sizeof(sz)).good()) + { + char *szt = TrimWhiteSpaces(sz); + char *szval; + + if ((::strlen(szt) > 0) && szt[0] != '#') + { + if ((szt = ::strtok(szt, " ,\t")) != nullptr) + { + if ((szval = ::strtok(nullptr, " ,\t")) != nullptr) + { + if (::strncmp(szt, "address", 7) == 0) + { + in_addr addr = { .s_addr = inet_addr(szval) }; + if (addr.s_addr) + { + std::cout << "G3 handler address set to " << inet_ntoa(addr) << std::endl; + m_GwAddress = addr.s_addr; + opts++; + } + } + else if (strncmp(szt, "modules", 7) == 0) + { + std::cout << "G3 handler module list set to " << szval << std::endl; + m_Modules = szval; + opts++; + } + else + { + // unknown option - ignore + } + } + } + } + } + std::cout << "G3 handler loaded " << opts << " options from file " << TERMINALOPTIONS_PATH << std::endl; + file.close(); + + struct stat fileStat; + + if (::stat(TERMINALOPTIONS_PATH, &fileStat) != -1) + { + m_LastModTime = fileStat.st_mtime; + } + } +} diff --git a/src/G3Protocol.h b/src/G3Protocol.h new file mode 100644 index 0000000..ea260ca --- /dev/null +++ b/src/G3Protocol.h @@ -0,0 +1,133 @@ +// +// G3Protocol.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 03/06/2019. +// Copyright © 2019 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cg3protocol_h +#define cg3protocol_h + +#include +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" +#include "RawSocket.h" +#include "UDPMsgSocket.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +// note on the G3 terminal/AP protocol: +// +// There are 3 steps in handling an incoming connection +// +// 1 - Notification of terminal call on port UDP 12346, Presence port, a CUdpSocket. +// - Call will be rejected if in blacklisted +// +// 2 - Destination request on port UDP 12345, Config port, a CUdpMsgSocket. +// - Calls to specific callsigns will be accepted for a default module +// - Repeater calls will be accepted for local modules +// - All other calls are rejected +// +// 3 - Actual D-star flow like in Dextra to/from port 40000 +// 2 distinct sockets where used in the initial protocol +// later firmwares implement a single bidirectional socket +// +// Alive monitoring is done via a "PING" to remote port 40000. We will get an +// ICMP unreachable on terminal mode close or on station shut down if routing is done +// correctly. Otherwise a long timeout is used (e.g. 1h) + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CG3Protocol : public CProtocol +{ +public: + // constructor + CG3Protocol() : m_GwAddress(0u), m_Modules("*"), m_LastModTime(0) {} + + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // close + void Close(void); + + // task + void Task(void); + +protected: + // threads + void PresenceThread(void); + void ConfigThread(void); + void IcmpThread(void); + + // helper tasks + void PresenceTask(void); + void ConfigTask(void); + void IcmpTask(void); + + // config + void ReadOptions(void); + + // helper + char *TrimWhiteSpaces(char *); + void NeedReload(void); + + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // packet decoding helpers + bool IsValidDvHeaderPacket(const CBuffer &, std::unique_ptr &); + bool IsValidDvFramePacket(const CBuffer &, std::unique_ptr &); + bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr &); + + // packet encoding helpers + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; + bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; + bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; + +protected: + std::future m_PresenceFuture, m_ConfigFuture, m_IcmpFuture; + + // time + CTimePoint m_LastKeepaliveTime; + + // sockets + CUdpSocket m_PresenceSocket; + CUdpMsgSocket m_ConfigSocket; + CRawSocket m_IcmpRawSocket; + + // optional params + uint32 m_GwAddress; + std::string m_Modules; + time_t m_LastModTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cg3protocol_h */ diff --git a/src/GateKeeper.cpp b/src/GateKeeper.cpp new file mode 100644 index 0000000..744331a --- /dev/null +++ b/src/GateKeeper.cpp @@ -0,0 +1,280 @@ +// +// cgatekeeper.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Timer.h" +#include "GateKeeper.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +CGateKeeper g_GateKeeper; + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CGateKeeper::CGateKeeper() +{ + keep_running = true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CGateKeeper::~CGateKeeper() +{ + Close(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// init & clode + +bool CGateKeeper::Init(void) +{ + + // load lists from files + m_NodeWhiteList.LoadFromFile(WHITELIST_PATH); + m_NodeBlackList.LoadFromFile(BLACKLIST_PATH); + m_PeerList.LoadFromFile(INTERLINKLIST_PATH); + + // reset run flag + keep_running = true; + + // start thread; + m_Future = std::async(std::launch::async, &CGateKeeper::Thread, this); + + return true; +} + +void CGateKeeper::Close(void) +{ + // kill threads + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// authorisations + +bool CGateKeeper::MayLink(const CCallsign &callsign, const CIp &ip, int protocol, char *modules) const +{ + bool ok = true; + + switch (protocol) + { + // repeaters + case PROTOCOL_DEXTRA: + case PROTOCOL_DPLUS: + case PROTOCOL_DCS: +#ifndef NO_XLX + case PROTOCOL_DMRPLUS: + case PROTOCOL_DMRMMDVM: + case PROTOCOL_YSF: +#endif +#ifndef NO_G3 + case PROTOCOL_G3: +#endif + // first check is IP & callsigned listed OK + ok &= IsNodeListedOk(callsign, ip); + // todo: then apply any protocol specific authorisation for the operation + break; + +#ifndef NO_XLX + // XLX interlinks + case PROTOCOL_XLX: + ok &= IsPeerListedOk(callsign, ip, modules); + break; +#endif + + // unsupported + case PROTOCOL_NONE: + default: + ok = false; + break; + } + + // report + if ( !ok ) + { + std::cout << "Gatekeeper blocking linking of " << callsign << " @ " << ip << " using protocol " << protocol << std::endl; + } + + // done + return ok; +} + +bool CGateKeeper::MayTransmit(const CCallsign &callsign, const CIp &ip, int protocol, char module) const +{ + bool ok = true; + + switch (protocol) + { + // repeaters, protocol specific + case PROTOCOL_ANY: + case PROTOCOL_DEXTRA: + case PROTOCOL_DPLUS: + case PROTOCOL_DCS: +#ifndef NO_XLX + case PROTOCOL_DMRPLUS: + case PROTOCOL_DMRMMDVM: + case PROTOCOL_YSF: +#endif +#ifndef NO_G3 + case PROTOCOL_G3: +#endif + // first check is IP & callsigned listed OK + ok = ok && IsNodeListedOk(callsign, ip, module); + // todo: then apply any protocol specific authorisation for the operation + break; + +#ifndef NO_XLX + // XLX interlinks + case PROTOCOL_XLX: + ok = ok && IsPeerListedOk(callsign, ip, module); + break; +#endif + + // unsupported + case PROTOCOL_NONE: + default: + ok = false; + break; + } + + // report + if ( !ok ) + { + std::cout << "Gatekeeper blocking transmitting of " << callsign << " @ " << ip << " using protocol " << protocol << std::endl; + } + + // done + return ok; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CGateKeeper::Thread() +{ + while (keep_running) + { + // Wait 30 seconds + for (int i=0; i<15 && keep_running; i++) + CTimePoint::TaskSleepFor(2000); + + // have lists files changed ? + if ( m_NodeWhiteList.NeedReload() ) + { + m_NodeWhiteList.ReloadFromFile(); + } + if ( m_NodeBlackList.NeedReload() ) + { + m_NodeBlackList.ReloadFromFile(); + } + if ( m_PeerList.NeedReload() ) + { + m_PeerList.ReloadFromFile(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operation helpers + +bool CGateKeeper::IsNodeListedOk(const CCallsign &callsign, const CIp &ip, char module) const +{ + bool ok = true; + + // first check IP + + // next, check callsign + if ( ok ) + { + // first check if callsign is in white list + // note if white list is empty, everybody is authorized + const_cast(m_NodeWhiteList).Lock(); + if ( !m_NodeWhiteList.empty() ) + { + ok = m_NodeWhiteList.IsCallsignListedWithWildcard(callsign, module); + } + const_cast(m_NodeWhiteList).Unlock(); + + // then check if not blacklisted + const_cast(m_NodeBlackList).Lock(); + ok &= !m_NodeBlackList.IsCallsignListedWithWildcard(callsign); + const_cast(m_NodeBlackList).Unlock(); + } + + // done + return ok; + +} + +bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char module) const +{ + bool ok = true; + + // first check IP + + // next, check callsign + if ( ok ) + { + // look for an exact match in the list + const_cast(m_PeerList).Lock(); + if ( !m_PeerList.empty() ) + { + ok = m_PeerList.IsCallsignListed(callsign, module); + } + const_cast(m_PeerList).Unlock(); + } + + // done + return ok; +} + +bool CGateKeeper::IsPeerListedOk(const CCallsign &callsign, const CIp &ip, char *modules) const +{ + bool ok = true; + + // first check IP + + // next, check callsign + if ( ok ) + { + // look for an exact match in the list + const_cast(m_PeerList).Lock(); + if ( !m_PeerList.empty() ) + { + ok = m_PeerList.IsCallsignListed(callsign, modules); + } + const_cast(m_PeerList).Unlock(); + } + + // done + return ok; +} diff --git a/src/GateKeeper.h b/src/GateKeeper.h new file mode 100644 index 0000000..bff57ed --- /dev/null +++ b/src/GateKeeper.h @@ -0,0 +1,80 @@ +// +// GateKeeper.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 07/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cgatekeeper_h +#define cgatekeeper_h + +#include "Main.h" +#include "Callsign.h" +#include "IP.h" +#include "CallsignList.h" +#include "PeerCallsignList.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CGateKeeper +{ +public: + // constructor + CGateKeeper(); + + // destructor + virtual ~CGateKeeper(); + + // init & clode + bool Init(void); + void Close(void); + + // authorizations + bool MayLink(const CCallsign &, const CIp &, int, char * = nullptr) const; + bool MayTransmit(const CCallsign &, const CIp &, int = PROTOCOL_ANY, char = ' ') const; + + // peer list handeling + CPeerCallsignList *GetPeerList(void) { m_PeerList.Lock(); return &m_PeerList; } + void ReleasePeerList(void) { m_PeerList.Unlock(); } + +protected: + // thread + void Thread(); + + // operation helpers + bool IsNodeListedOk(const CCallsign &, const CIp &, char = ' ') const; + bool IsPeerListedOk(const CCallsign &, const CIp &, char) const; + bool IsPeerListedOk(const CCallsign &, const CIp &, char *) const; + +protected: + // data + CCallsignList m_NodeWhiteList; + CCallsignList m_NodeBlackList; + CPeerCallsignList m_PeerList; + + // thread + std::atomic keep_running; + std::future m_Future; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cgatekeeper_h */ diff --git a/src/Golay2087.cpp b/src/Golay2087.cpp new file mode 100644 index 0000000..3e34543 --- /dev/null +++ b/src/Golay2087.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Golay2087.h" + +#include +#include + +const unsigned int ENCODING_TABLE_2087[] = +{ + 0x0000U, 0xB08EU, 0xE093U, 0x501DU, 0x70A9U, 0xC027U, 0x903AU, 0x20B4U, 0x60DCU, 0xD052U, 0x804FU, 0x30C1U, + 0x1075U, 0xA0FBU, 0xF0E6U, 0x4068U, 0x7036U, 0xC0B8U, 0x90A5U, 0x202BU, 0x009FU, 0xB011U, 0xE00CU, 0x5082U, + 0x10EAU, 0xA064U, 0xF079U, 0x40F7U, 0x6043U, 0xD0CDU, 0x80D0U, 0x305EU, 0xD06CU, 0x60E2U, 0x30FFU, 0x8071U, + 0xA0C5U, 0x104BU, 0x4056U, 0xF0D8U, 0xB0B0U, 0x003EU, 0x5023U, 0xE0ADU, 0xC019U, 0x7097U, 0x208AU, 0x9004U, + 0xA05AU, 0x10D4U, 0x40C9U, 0xF047U, 0xD0F3U, 0x607DU, 0x3060U, 0x80EEU, 0xC086U, 0x7008U, 0x2015U, 0x909BU, + 0xB02FU, 0x00A1U, 0x50BCU, 0xE032U, 0x90D9U, 0x2057U, 0x704AU, 0xC0C4U, 0xE070U, 0x50FEU, 0x00E3U, 0xB06DU, + 0xF005U, 0x408BU, 0x1096U, 0xA018U, 0x80ACU, 0x3022U, 0x603FU, 0xD0B1U, 0xE0EFU, 0x5061U, 0x007CU, 0xB0F2U, + 0x9046U, 0x20C8U, 0x70D5U, 0xC05BU, 0x8033U, 0x30BDU, 0x60A0U, 0xD02EU, 0xF09AU, 0x4014U, 0x1009U, 0xA087U, + 0x40B5U, 0xF03BU, 0xA026U, 0x10A8U, 0x301CU, 0x8092U, 0xD08FU, 0x6001U, 0x2069U, 0x90E7U, 0xC0FAU, 0x7074U, + 0x50C0U, 0xE04EU, 0xB053U, 0x00DDU, 0x3083U, 0x800DU, 0xD010U, 0x609EU, 0x402AU, 0xF0A4U, 0xA0B9U, 0x1037U, + 0x505FU, 0xE0D1U, 0xB0CCU, 0x0042U, 0x20F6U, 0x9078U, 0xC065U, 0x70EBU, 0xA03DU, 0x10B3U, 0x40AEU, 0xF020U, + 0xD094U, 0x601AU, 0x3007U, 0x8089U, 0xC0E1U, 0x706FU, 0x2072U, 0x90FCU, 0xB048U, 0x00C6U, 0x50DBU, 0xE055U, + 0xD00BU, 0x6085U, 0x3098U, 0x8016U, 0xA0A2U, 0x102CU, 0x4031U, 0xF0BFU, 0xB0D7U, 0x0059U, 0x5044U, 0xE0CAU, + 0xC07EU, 0x70F0U, 0x20EDU, 0x9063U, 0x7051U, 0xC0DFU, 0x90C2U, 0x204CU, 0x00F8U, 0xB076U, 0xE06BU, 0x50E5U, + 0x108DU, 0xA003U, 0xF01EU, 0x4090U, 0x6024U, 0xD0AAU, 0x80B7U, 0x3039U, 0x0067U, 0xB0E9U, 0xE0F4U, 0x507AU, + 0x70CEU, 0xC040U, 0x905DU, 0x20D3U, 0x60BBU, 0xD035U, 0x8028U, 0x30A6U, 0x1012U, 0xA09CU, 0xF081U, 0x400FU, + 0x30E4U, 0x806AU, 0xD077U, 0x60F9U, 0x404DU, 0xF0C3U, 0xA0DEU, 0x1050U, 0x5038U, 0xE0B6U, 0xB0ABU, 0x0025U, + 0x2091U, 0x901FU, 0xC002U, 0x708CU, 0x40D2U, 0xF05CU, 0xA041U, 0x10CFU, 0x307BU, 0x80F5U, 0xD0E8U, 0x6066U, + 0x200EU, 0x9080U, 0xC09DU, 0x7013U, 0x50A7U, 0xE029U, 0xB034U, 0x00BAU, 0xE088U, 0x5006U, 0x001BU, 0xB095U, + 0x9021U, 0x20AFU, 0x70B2U, 0xC03CU, 0x8054U, 0x30DAU, 0x60C7U, 0xD049U, 0xF0FDU, 0x4073U, 0x106EU, 0xA0E0U, + 0x90BEU, 0x2030U, 0x702DU, 0xC0A3U, 0xE017U, 0x5099U, 0x0084U, 0xB00AU, 0xF062U, 0x40ECU, 0x10F1U, 0xA07FU, + 0x80CBU, 0x3045U, 0x6058U, 0xD0D6U +}; + +const unsigned int DECODING_TABLE_1987[] = +{ + 0x00000U, 0x00001U, 0x00002U, 0x00003U, 0x00004U, 0x00005U, 0x00006U, 0x00007U, 0x00008U, 0x00009U, 0x0000AU, 0x0000BU, 0x0000CU, + 0x0000DU, 0x0000EU, 0x24020U, 0x00010U, 0x00011U, 0x00012U, 0x00013U, 0x00014U, 0x00015U, 0x00016U, 0x00017U, 0x00018U, 0x00019U, + 0x0001AU, 0x0001BU, 0x0001CU, 0x0001DU, 0x48040U, 0x01480U, 0x00020U, 0x00021U, 0x00022U, 0x00023U, 0x00024U, 0x00025U, 0x00026U, + 0x24008U, 0x00028U, 0x00029U, 0x0002AU, 0x24004U, 0x0002CU, 0x24002U, 0x24001U, 0x24000U, 0x00030U, 0x00031U, 0x00032U, 0x08180U, + 0x00034U, 0x00C40U, 0x00036U, 0x00C42U, 0x00038U, 0x43000U, 0x0003AU, 0x43002U, 0x02902U, 0x24012U, 0x02900U, 0x24010U, 0x00040U, + 0x00041U, 0x00042U, 0x00043U, 0x00044U, 0x00045U, 0x00046U, 0x00047U, 0x00048U, 0x00049U, 0x0004AU, 0x02500U, 0x0004CU, 0x0004DU, + 0x48010U, 0x48011U, 0x00050U, 0x00051U, 0x00052U, 0x21200U, 0x00054U, 0x00C20U, 0x48008U, 0x48009U, 0x00058U, 0x00059U, 0x48004U, + 0x48005U, 0x48002U, 0x48003U, 0x48000U, 0x48001U, 0x00060U, 0x00061U, 0x00062U, 0x00063U, 0x00064U, 0x00C10U, 0x10300U, 0x0B000U, + 0x00068U, 0x00069U, 0x01880U, 0x01881U, 0x40181U, 0x40180U, 0x24041U, 0x24040U, 0x00070U, 0x00C04U, 0x00072U, 0x00C06U, 0x00C01U, + 0x00C00U, 0x00C03U, 0x00C02U, 0x05204U, 0x00C0CU, 0x48024U, 0x48025U, 0x05200U, 0x00C08U, 0x48020U, 0x48021U, 0x00080U, 0x00081U, + 0x00082U, 0x00083U, 0x00084U, 0x00085U, 0x00086U, 0x00087U, 0x00088U, 0x00089U, 0x0008AU, 0x50200U, 0x0008CU, 0x0A800U, 0x01411U, + 0x01410U, 0x00090U, 0x00091U, 0x00092U, 0x08120U, 0x00094U, 0x00095U, 0x04A00U, 0x01408U, 0x00098U, 0x00099U, 0x01405U, 0x01404U, + 0x01403U, 0x01402U, 0x01401U, 0x01400U, 0x000A0U, 0x000A1U, 0x000A2U, 0x08110U, 0x000A4U, 0x000A5U, 0x42400U, 0x42401U, 0x000A8U, + 0x000A9U, 0x01840U, 0x01841U, 0x40141U, 0x40140U, 0x24081U, 0x24080U, 0x000B0U, 0x08102U, 0x08101U, 0x08100U, 0x000B4U, 0x08106U, + 0x08105U, 0x08104U, 0x20A01U, 0x20A00U, 0x08109U, 0x08108U, 0x01423U, 0x01422U, 0x01421U, 0x01420U, 0x000C0U, 0x000C1U, 0x000C2U, + 0x000C3U, 0x000C4U, 0x000C5U, 0x000C6U, 0x000C7U, 0x000C8U, 0x000C9U, 0x01820U, 0x01821U, 0x20600U, 0x40120U, 0x16000U, 0x16001U, + 0x000D0U, 0x000D1U, 0x42801U, 0x42800U, 0x03100U, 0x18200U, 0x03102U, 0x18202U, 0x000D8U, 0x000D9U, 0x48084U, 0x01444U, 0x48082U, + 0x01442U, 0x48080U, 0x01440U, 0x000E0U, 0x32000U, 0x01808U, 0x04600U, 0x40109U, 0x40108U, 0x0180CU, 0x4010AU, 0x01802U, 0x40104U, + 0x01800U, 0x01801U, 0x40101U, 0x40100U, 0x01804U, 0x40102U, 0x0A408U, 0x08142U, 0x08141U, 0x08140U, 0x00C81U, 0x00C80U, 0x00C83U, + 0x00C82U, 0x0A400U, 0x0A401U, 0x01810U, 0x01811U, 0x40111U, 0x40110U, 0x01814U, 0x40112U, 0x00100U, 0x00101U, 0x00102U, 0x00103U, + 0x00104U, 0x00105U, 0x00106U, 0x41800U, 0x00108U, 0x00109U, 0x0010AU, 0x02440U, 0x0010CU, 0x0010DU, 0x0010EU, 0x02444U, 0x00110U, + 0x00111U, 0x00112U, 0x080A0U, 0x00114U, 0x00115U, 0x00116U, 0x080A4U, 0x00118U, 0x00119U, 0x15000U, 0x15001U, 0x02822U, 0x02823U, + 0x02820U, 0x02821U, 0x00120U, 0x00121U, 0x00122U, 0x08090U, 0x00124U, 0x00125U, 0x10240U, 0x10241U, 0x00128U, 0x00129U, 0x0012AU, + 0x24104U, 0x09400U, 0x400C0U, 0x02810U, 0x24100U, 0x00130U, 0x08082U, 0x08081U, 0x08080U, 0x31001U, 0x31000U, 0x02808U, 0x08084U, + 0x02806U, 0x0808AU, 0x02804U, 0x08088U, 0x02802U, 0x02803U, 0x02800U, 0x02801U, 0x00140U, 0x00141U, 0x00142U, 0x02408U, 0x00144U, + 0x00145U, 0x10220U, 0x10221U, 0x00148U, 0x02402U, 0x02401U, 0x02400U, 0x400A1U, 0x400A0U, 0x02405U, 0x02404U, 0x00150U, 0x00151U, + 0x00152U, 0x02418U, 0x03080U, 0x03081U, 0x03082U, 0x03083U, 0x09801U, 0x09800U, 0x02411U, 0x02410U, 0x48102U, 0x09804U, 0x48100U, + 0x48101U, 0x00160U, 0x00161U, 0x10204U, 0x10205U, 0x10202U, 0x40088U, 0x10200U, 0x10201U, 0x40085U, 0x40084U, 0x02421U, 0x02420U, + 0x40081U, 0x40080U, 0x10208U, 0x40082U, 0x41402U, 0x080C2U, 0x41400U, 0x080C0U, 0x00D01U, 0x00D00U, 0x10210U, 0x10211U, 0x40095U, + 0x40094U, 0x02844U, 0x080C8U, 0x40091U, 0x40090U, 0x02840U, 0x02841U, 0x00180U, 0x00181U, 0x00182U, 0x08030U, 0x00184U, 0x14400U, + 0x22201U, 0x22200U, 0x00188U, 0x00189U, 0x0018AU, 0x08038U, 0x40061U, 0x40060U, 0x40063U, 0x40062U, 0x00190U, 0x08022U, 0x08021U, + 0x08020U, 0x03040U, 0x03041U, 0x08025U, 0x08024U, 0x40C00U, 0x40C01U, 0x08029U, 0x08028U, 0x2C000U, 0x2C001U, 0x01501U, 0x01500U, + 0x001A0U, 0x08012U, 0x08011U, 0x08010U, 0x40049U, 0x40048U, 0x08015U, 0x08014U, 0x06200U, 0x40044U, 0x30400U, 0x08018U, 0x40041U, + 0x40040U, 0x40043U, 0x40042U, 0x08003U, 0x08002U, 0x08001U, 0x08000U, 0x08007U, 0x08006U, 0x08005U, 0x08004U, 0x0800BU, 0x0800AU, + 0x08009U, 0x08008U, 0x40051U, 0x40050U, 0x02880U, 0x0800CU, 0x001C0U, 0x001C1U, 0x64000U, 0x64001U, 0x03010U, 0x40028U, 0x08C00U, + 0x08C01U, 0x40025U, 0x40024U, 0x02481U, 0x02480U, 0x40021U, 0x40020U, 0x40023U, 0x40022U, 0x03004U, 0x03005U, 0x08061U, 0x08060U, + 0x03000U, 0x03001U, 0x03002U, 0x03003U, 0x0300CU, 0x40034U, 0x30805U, 0x30804U, 0x03008U, 0x40030U, 0x30801U, 0x30800U, 0x4000DU, + 0x4000CU, 0x08051U, 0x08050U, 0x40009U, 0x40008U, 0x10280U, 0x4000AU, 0x40005U, 0x40004U, 0x01900U, 0x40006U, 0x40001U, 0x40000U, + 0x40003U, 0x40002U, 0x14800U, 0x08042U, 0x08041U, 0x08040U, 0x03020U, 0x40018U, 0x08045U, 0x08044U, 0x40015U, 0x40014U, 0x08049U, + 0x08048U, 0x40011U, 0x40010U, 0x40013U, 0x40012U, 0x00200U, 0x00201U, 0x00202U, 0x00203U, 0x00204U, 0x00205U, 0x00206U, 0x00207U, + 0x00208U, 0x00209U, 0x0020AU, 0x50080U, 0x0020CU, 0x0020DU, 0x0020EU, 0x50084U, 0x00210U, 0x00211U, 0x00212U, 0x21040U, 0x00214U, + 0x00215U, 0x04880U, 0x04881U, 0x00218U, 0x00219U, 0x0E001U, 0x0E000U, 0x0021CU, 0x0021DU, 0x04888U, 0x0E004U, 0x00220U, 0x00221U, + 0x00222U, 0x00223U, 0x00224U, 0x00225U, 0x10140U, 0x10141U, 0x00228U, 0x00229U, 0x0022AU, 0x24204U, 0x12401U, 0x12400U, 0x24201U, + 0x24200U, 0x00230U, 0x00231U, 0x00232U, 0x21060U, 0x2A000U, 0x2A001U, 0x2A002U, 0x2A003U, 0x20881U, 0x20880U, 0x20883U, 0x20882U, + 0x05040U, 0x05041U, 0x05042U, 0x24210U, 0x00240U, 0x00241U, 0x00242U, 0x21010U, 0x00244U, 0x46000U, 0x10120U, 0x10121U, 0x00248U, + 0x00249U, 0x0024AU, 0x21018U, 0x20480U, 0x20481U, 0x20482U, 0x20483U, 0x00250U, 0x21002U, 0x21001U, 0x21000U, 0x18081U, 0x18080U, + 0x21005U, 0x21004U, 0x12800U, 0x12801U, 0x21009U, 0x21008U, 0x05020U, 0x05021U, 0x48200U, 0x48201U, 0x00260U, 0x00261U, 0x10104U, + 0x04480U, 0x10102U, 0x10103U, 0x10100U, 0x10101U, 0x62002U, 0x62003U, 0x62000U, 0x62001U, 0x05010U, 0x05011U, 0x10108U, 0x10109U, + 0x0500CU, 0x21022U, 0x21021U, 0x21020U, 0x05008U, 0x00E00U, 0x10110U, 0x10111U, 0x05004U, 0x05005U, 0x05006U, 0x21028U, 0x05000U, + 0x05001U, 0x05002U, 0x05003U, 0x00280U, 0x00281U, 0x00282U, 0x50008U, 0x00284U, 0x00285U, 0x04810U, 0x22100U, 0x00288U, 0x50002U, + 0x50001U, 0x50000U, 0x20440U, 0x20441U, 0x50005U, 0x50004U, 0x00290U, 0x00291U, 0x04804U, 0x04805U, 0x04802U, 0x18040U, 0x04800U, + 0x04801U, 0x20821U, 0x20820U, 0x50011U, 0x50010U, 0x0480AU, 0x01602U, 0x04808U, 0x01600U, 0x002A0U, 0x002A1U, 0x04441U, 0x04440U, + 0x002A4U, 0x002A5U, 0x04830U, 0x04444U, 0x06100U, 0x20810U, 0x50021U, 0x50020U, 0x06104U, 0x20814U, 0x50025U, 0x50024U, 0x20809U, + 0x20808U, 0x13000U, 0x08300U, 0x04822U, 0x2080CU, 0x04820U, 0x04821U, 0x20801U, 0x20800U, 0x20803U, 0x20802U, 0x20805U, 0x20804U, + 0x04828U, 0x20806U, 0x002C0U, 0x002C1U, 0x04421U, 0x04420U, 0x20408U, 0x18010U, 0x2040AU, 0x18012U, 0x20404U, 0x20405U, 0x50041U, + 0x50040U, 0x20400U, 0x20401U, 0x20402U, 0x20403U, 0x18005U, 0x18004U, 0x21081U, 0x21080U, 0x18001U, 0x18000U, 0x04840U, 0x18002U, + 0x20414U, 0x1800CU, 0x21089U, 0x21088U, 0x20410U, 0x18008U, 0x20412U, 0x1800AU, 0x04403U, 0x04402U, 0x04401U, 0x04400U, 0x10182U, + 0x04406U, 0x10180U, 0x04404U, 0x01A02U, 0x0440AU, 0x01A00U, 0x04408U, 0x20420U, 0x40300U, 0x20422U, 0x40302U, 0x04413U, 0x04412U, + 0x04411U, 0x04410U, 0x18021U, 0x18020U, 0x10190U, 0x18022U, 0x20841U, 0x20840U, 0x01A10U, 0x20842U, 0x05080U, 0x05081U, 0x05082U, + 0x05083U, 0x00300U, 0x00301U, 0x00302U, 0x00303U, 0x00304U, 0x00305U, 0x10060U, 0x22080U, 0x00308U, 0x00309U, 0x28800U, 0x28801U, + 0x44402U, 0x44403U, 0x44400U, 0x44401U, 0x00310U, 0x00311U, 0x10C01U, 0x10C00U, 0x00314U, 0x00315U, 0x10070U, 0x10C04U, 0x00318U, + 0x00319U, 0x28810U, 0x10C08U, 0x44412U, 0x00000U, 0x44410U, 0x44411U, 0x00320U, 0x60400U, 0x10044U, 0x10045U, 0x10042U, 0x0C800U, + 0x10040U, 0x10041U, 0x06080U, 0x06081U, 0x06082U, 0x06083U, 0x1004AU, 0x0C808U, 0x10048U, 0x10049U, 0x58008U, 0x08282U, 0x08281U, + 0x08280U, 0x10052U, 0x0C810U, 0x10050U, 0x10051U, 0x58000U, 0x58001U, 0x58002U, 0x08288U, 0x02A02U, 0x02A03U, 0x02A00U, 0x02A01U, + 0x00340U, 0x00341U, 0x10024U, 0x10025U, 0x10022U, 0x10023U, 0x10020U, 0x10021U, 0x34001U, 0x34000U, 0x02601U, 0x02600U, 0x1002AU, + 0x34004U, 0x10028U, 0x10029U, 0x0C400U, 0x0C401U, 0x21101U, 0x21100U, 0x60800U, 0x60801U, 0x10030U, 0x10031U, 0x0C408U, 0x34010U, + 0x21109U, 0x21108U, 0x60808U, 0x60809U, 0x10038U, 0x28420U, 0x10006U, 0x10007U, 0x10004U, 0x10005U, 0x10002U, 0x10003U, 0x10000U, + 0x10001U, 0x1000EU, 0x40284U, 0x1000CU, 0x1000DU, 0x1000AU, 0x40280U, 0x10008U, 0x10009U, 0x10016U, 0x10017U, 0x10014U, 0x10015U, + 0x10012U, 0x10013U, 0x10010U, 0x10011U, 0x05104U, 0x44802U, 0x44801U, 0x44800U, 0x05100U, 0x05101U, 0x10018U, 0x28400U, 0x00380U, + 0x00381U, 0x22005U, 0x22004U, 0x22003U, 0x22002U, 0x22001U, 0x22000U, 0x06020U, 0x06021U, 0x50101U, 0x50100U, 0x11800U, 0x11801U, + 0x22009U, 0x22008U, 0x45001U, 0x45000U, 0x08221U, 0x08220U, 0x04902U, 0x22012U, 0x04900U, 0x22010U, 0x06030U, 0x45008U, 0x08229U, + 0x08228U, 0x11810U, 0x11811U, 0x04908U, 0x22018U, 0x06008U, 0x06009U, 0x08211U, 0x08210U, 0x100C2U, 0x22022U, 0x100C0U, 0x22020U, + 0x06000U, 0x06001U, 0x06002U, 0x06003U, 0x06004U, 0x40240U, 0x06006U, 0x40242U, 0x08203U, 0x08202U, 0x08201U, 0x08200U, 0x08207U, + 0x08206U, 0x08205U, 0x08204U, 0x06010U, 0x20900U, 0x08209U, 0x08208U, 0x61002U, 0x20904U, 0x61000U, 0x61001U, 0x29020U, 0x29021U, + 0x100A4U, 0x22044U, 0x100A2U, 0x22042U, 0x100A0U, 0x22040U, 0x20504U, 0x40224U, 0x0D005U, 0x0D004U, 0x20500U, 0x40220U, 0x0D001U, + 0x0D000U, 0x03204U, 0x18104U, 0x08261U, 0x08260U, 0x03200U, 0x18100U, 0x03202U, 0x18102U, 0x11421U, 0x11420U, 0x00000U, 0x11422U, + 0x03208U, 0x18108U, 0x0D011U, 0x0D010U, 0x29000U, 0x29001U, 0x10084U, 0x04500U, 0x10082U, 0x40208U, 0x10080U, 0x10081U, 0x06040U, + 0x40204U, 0x06042U, 0x40206U, 0x40201U, 0x40200U, 0x10088U, 0x40202U, 0x29010U, 0x08242U, 0x08241U, 0x08240U, 0x10092U, 0x40218U, + 0x10090U, 0x10091U, 0x11401U, 0x11400U, 0x11403U, 0x11402U, 0x40211U, 0x40210U, 0x10098U, 0x40212U, 0x00400U, 0x00401U, 0x00402U, + 0x00403U, 0x00404U, 0x00405U, 0x00406U, 0x00407U, 0x00408U, 0x00409U, 0x0040AU, 0x02140U, 0x0040CU, 0x0040DU, 0x01091U, 0x01090U, + 0x00410U, 0x00411U, 0x00412U, 0x00413U, 0x00414U, 0x00860U, 0x01089U, 0x01088U, 0x00418U, 0x38000U, 0x01085U, 0x01084U, 0x01083U, + 0x01082U, 0x01081U, 0x01080U, 0x00420U, 0x00421U, 0x00422U, 0x00423U, 0x00424U, 0x00850U, 0x42080U, 0x42081U, 0x00428U, 0x00429U, + 0x48801U, 0x48800U, 0x09100U, 0x12200U, 0x24401U, 0x24400U, 0x00430U, 0x00844U, 0x00432U, 0x00846U, 0x00841U, 0x00840U, 0x1C000U, + 0x00842U, 0x00438U, 0x0084CU, 0x010A5U, 0x010A4U, 0x00849U, 0x00848U, 0x010A1U, 0x010A0U, 0x00440U, 0x00441U, 0x00442U, 0x02108U, + 0x00444U, 0x00830U, 0x70001U, 0x70000U, 0x00448U, 0x02102U, 0x02101U, 0x02100U, 0x20280U, 0x20281U, 0x02105U, 0x02104U, 0x00450U, + 0x00824U, 0x00452U, 0x00826U, 0x00821U, 0x00820U, 0x00823U, 0x00822U, 0x24802U, 0x02112U, 0x24800U, 0x02110U, 0x00829U, 0x00828U, + 0x48400U, 0x010C0U, 0x00460U, 0x00814U, 0x04281U, 0x04280U, 0x00811U, 0x00810U, 0x00813U, 0x00812U, 0x54000U, 0x54001U, 0x02121U, + 0x02120U, 0x00819U, 0x00818U, 0x0081BU, 0x0081AU, 0x00805U, 0x00804U, 0x41100U, 0x00806U, 0x00801U, 0x00800U, 0x00803U, 0x00802U, + 0x0A080U, 0x0080CU, 0x0A082U, 0x0080EU, 0x00809U, 0x00808U, 0x0080BU, 0x0080AU, 0x00480U, 0x00481U, 0x00482U, 0x00483U, 0x00484U, + 0x14100U, 0x42020U, 0x01018U, 0x00488U, 0x00489U, 0x01015U, 0x01014U, 0x20240U, 0x01012U, 0x01011U, 0x01010U, 0x00490U, 0x00491U, + 0x0100DU, 0x0100CU, 0x0100BU, 0x0100AU, 0x01009U, 0x01008U, 0x40900U, 0x01006U, 0x01005U, 0x01004U, 0x01003U, 0x01002U, 0x01001U, + 0x01000U, 0x004A0U, 0x004A1U, 0x42004U, 0x04240U, 0x42002U, 0x42003U, 0x42000U, 0x42001U, 0x30102U, 0x30103U, 0x30100U, 0x30101U, + 0x4200AU, 0x01032U, 0x42008U, 0x01030U, 0x25000U, 0x25001U, 0x08501U, 0x08500U, 0x008C1U, 0x008C0U, 0x42010U, 0x01028U, 0x0A040U, + 0x0A041U, 0x01025U, 0x01024U, 0x01023U, 0x01022U, 0x01021U, 0x01020U, 0x004C0U, 0x49000U, 0x04221U, 0x04220U, 0x20208U, 0x20209U, + 0x08900U, 0x08901U, 0x20204U, 0x20205U, 0x02181U, 0x02180U, 0x20200U, 0x20201U, 0x20202U, 0x01050U, 0x0A028U, 0x008A4U, 0x0104DU, + 0x0104CU, 0x008A1U, 0x008A0U, 0x01049U, 0x01048U, 0x0A020U, 0x0A021U, 0x01045U, 0x01044U, 0x20210U, 0x01042U, 0x01041U, 0x01040U, + 0x04203U, 0x04202U, 0x04201U, 0x04200U, 0x00891U, 0x00890U, 0x42040U, 0x04204U, 0x0A010U, 0x0A011U, 0x01C00U, 0x04208U, 0x20220U, + 0x40500U, 0x20222U, 0x40502U, 0x0A008U, 0x00884U, 0x04211U, 0x04210U, 0x00881U, 0x00880U, 0x00883U, 0x00882U, 0x0A000U, 0x0A001U, + 0x0A002U, 0x0A003U, 0x0A004U, 0x00888U, 0x01061U, 0x01060U, 0x00500U, 0x00501U, 0x00502U, 0x02048U, 0x00504U, 0x14080U, 0x00506U, + 0x14082U, 0x00508U, 0x02042U, 0x02041U, 0x02040U, 0x09020U, 0x09021U, 0x44200U, 0x02044U, 0x00510U, 0x00511U, 0x10A01U, 0x10A00U, + 0x4A001U, 0x4A000U, 0x4A003U, 0x4A002U, 0x40880U, 0x40881U, 0x02051U, 0x02050U, 0x40884U, 0x01182U, 0x01181U, 0x01180U, 0x00520U, + 0x60200U, 0x00522U, 0x60202U, 0x09008U, 0x09009U, 0x0900AU, 0x0900BU, 0x09004U, 0x09005U, 0x30080U, 0x02060U, 0x09000U, 0x09001U, + 0x09002U, 0x09003U, 0x41042U, 0x08482U, 0x41040U, 0x08480U, 0x00941U, 0x00940U, 0x41044U, 0x00942U, 0x09014U, 0x09015U, 0x02C04U, + 0x08488U, 0x09010U, 0x09011U, 0x02C00U, 0x02C01U, 0x00540U, 0x0200AU, 0x02009U, 0x02008U, 0x08882U, 0x0200EU, 0x08880U, 0x0200CU, + 0x02003U, 0x02002U, 0x02001U, 0x02000U, 0x02007U, 0x02006U, 0x02005U, 0x02004U, 0x0C200U, 0x0C201U, 0x41020U, 0x02018U, 0x00921U, + 0x00920U, 0x41024U, 0x00922U, 0x02013U, 0x02012U, 0x02011U, 0x02010U, 0x02017U, 0x02016U, 0x02015U, 0x02014U, 0x41012U, 0x0202AU, + 0x41010U, 0x02028U, 0x26000U, 0x00910U, 0x10600U, 0x10601U, 0x02023U, 0x02022U, 0x02021U, 0x02020U, 0x09040U, 0x40480U, 0x02025U, + 0x02024U, 0x41002U, 0x00904U, 0x41000U, 0x41001U, 0x00901U, 0x00900U, 0x41004U, 0x00902U, 0x4100AU, 0x02032U, 0x41008U, 0x02030U, + 0x00909U, 0x00908U, 0x28201U, 0x28200U, 0x00580U, 0x14004U, 0x00582U, 0x14006U, 0x14001U, 0x14000U, 0x08840U, 0x14002U, 0x40810U, + 0x40811U, 0x30020U, 0x020C0U, 0x14009U, 0x14008U, 0x01111U, 0x01110U, 0x40808U, 0x40809U, 0x08421U, 0x08420U, 0x14011U, 0x14010U, + 0x01109U, 0x01108U, 0x40800U, 0x40801U, 0x40802U, 0x01104U, 0x40804U, 0x01102U, 0x01101U, 0x01100U, 0x03801U, 0x03800U, 0x30008U, + 0x08410U, 0x14021U, 0x14020U, 0x42100U, 0x42101U, 0x30002U, 0x30003U, 0x30000U, 0x30001U, 0x09080U, 0x40440U, 0x30004U, 0x30005U, + 0x08403U, 0x08402U, 0x08401U, 0x08400U, 0x08407U, 0x08406U, 0x08405U, 0x08404U, 0x40820U, 0x40821U, 0x30010U, 0x08408U, 0x40824U, + 0x01122U, 0x01121U, 0x01120U, 0x08806U, 0x0208AU, 0x08804U, 0x02088U, 0x08802U, 0x14040U, 0x08800U, 0x08801U, 0x02083U, 0x02082U, + 0x02081U, 0x02080U, 0x20300U, 0x40420U, 0x08808U, 0x02084U, 0x03404U, 0x03405U, 0x08814U, 0x02098U, 0x03400U, 0x03401U, 0x08810U, + 0x08811U, 0x40840U, 0x40841U, 0x02091U, 0x02090U, 0x40844U, 0x01142U, 0x01141U, 0x01140U, 0x04303U, 0x04302U, 0x04301U, 0x04300U, + 0x40409U, 0x40408U, 0x08820U, 0x08821U, 0x40405U, 0x40404U, 0x30040U, 0x020A0U, 0x40401U, 0x40400U, 0x40403U, 0x40402U, 0x41082U, + 0x08442U, 0x41080U, 0x08440U, 0x00981U, 0x00980U, 0x41084U, 0x00982U, 0x0A100U, 0x11200U, 0x0A102U, 0x11202U, 0x40411U, 0x40410U, + 0x40413U, 0x40412U, 0x00600U, 0x00601U, 0x00602U, 0x00603U, 0x00604U, 0x00605U, 0x00606U, 0x00607U, 0x00608U, 0x05800U, 0x0060AU, + 0x05802U, 0x200C0U, 0x12020U, 0x44100U, 0x44101U, 0x00610U, 0x00611U, 0x10901U, 0x10900U, 0x51000U, 0x51001U, 0x51002U, 0x10904U, + 0x00618U, 0x05810U, 0x01285U, 0x01284U, 0x51008U, 0x01282U, 0x01281U, 0x01280U, 0x00620U, 0x60100U, 0x040C1U, 0x040C0U, 0x12009U, + 0x12008U, 0x21800U, 0x21801U, 0x12005U, 0x12004U, 0x12007U, 0x12006U, 0x12001U, 0x12000U, 0x12003U, 0x12002U, 0x00630U, 0x00A44U, + 0x040D1U, 0x040D0U, 0x00A41U, 0x00A40U, 0x21810U, 0x00A42U, 0x12015U, 0x12014U, 0x00000U, 0x12016U, 0x12011U, 0x12010U, 0x12013U, + 0x12012U, 0x00640U, 0x00641U, 0x040A1U, 0x040A0U, 0x20088U, 0x20089U, 0x2008AU, 0x040A4U, 0x20084U, 0x20085U, 0x19000U, 0x02300U, + 0x20080U, 0x20081U, 0x20082U, 0x20083U, 0x0C100U, 0x0C101U, 0x21401U, 0x21400U, 0x00A21U, 0x00A20U, 0x00A23U, 0x00A22U, 0x20094U, + 0x20095U, 0x19010U, 0x21408U, 0x20090U, 0x20091U, 0x20092U, 0x28120U, 0x04083U, 0x04082U, 0x04081U, 0x04080U, 0x00A11U, 0x00A10U, + 0x10500U, 0x04084U, 0x200A4U, 0x0408AU, 0x04089U, 0x04088U, 0x200A0U, 0x12040U, 0x200A2U, 0x12042U, 0x00A05U, 0x00A04U, 0x04091U, + 0x04090U, 0x00A01U, 0x00A00U, 0x00A03U, 0x00A02U, 0x05404U, 0x00A0CU, 0x28105U, 0x28104U, 0x05400U, 0x00A08U, 0x28101U, 0x28100U, + 0x00680U, 0x00681U, 0x04061U, 0x04060U, 0x20048U, 0x20049U, 0x2004AU, 0x04064U, 0x20044U, 0x20045U, 0x50401U, 0x50400U, 0x20040U, + 0x20041U, 0x20042U, 0x01210U, 0x68002U, 0x68003U, 0x68000U, 0x68001U, 0x04C02U, 0x0120AU, 0x04C00U, 0x01208U, 0x20054U, 0x01206U, + 0x01205U, 0x01204U, 0x20050U, 0x01202U, 0x01201U, 0x01200U, 0x18800U, 0x04042U, 0x04041U, 0x04040U, 0x42202U, 0x04046U, 0x42200U, + 0x04044U, 0x20064U, 0x0404AU, 0x04049U, 0x04048U, 0x20060U, 0x12080U, 0x20062U, 0x12082U, 0x18810U, 0x04052U, 0x04051U, 0x04050U, + 0x4C009U, 0x4C008U, 0x42210U, 0x04054U, 0x20C01U, 0x20C00U, 0x20C03U, 0x20C02U, 0x4C001U, 0x4C000U, 0x01221U, 0x01220U, 0x2000CU, + 0x04022U, 0x04021U, 0x04020U, 0x20008U, 0x20009U, 0x2000AU, 0x04024U, 0x20004U, 0x20005U, 0x20006U, 0x04028U, 0x20000U, 0x20001U, + 0x20002U, 0x20003U, 0x2001CU, 0x04032U, 0x04031U, 0x04030U, 0x20018U, 0x18400U, 0x2001AU, 0x18402U, 0x20014U, 0x20015U, 0x20016U, + 0x01244U, 0x20010U, 0x20011U, 0x20012U, 0x01240U, 0x04003U, 0x04002U, 0x04001U, 0x04000U, 0x20028U, 0x04006U, 0x04005U, 0x04004U, + 0x20024U, 0x0400AU, 0x04009U, 0x04008U, 0x20020U, 0x20021U, 0x20022U, 0x0400CU, 0x04013U, 0x04012U, 0x04011U, 0x04010U, 0x00A81U, + 0x00A80U, 0x04015U, 0x04014U, 0x0A200U, 0x11100U, 0x04019U, 0x04018U, 0x20030U, 0x20031U, 0x50800U, 0x50801U, 0x00700U, 0x60020U, + 0x10811U, 0x10810U, 0x4400AU, 0x60024U, 0x44008U, 0x44009U, 0x44006U, 0x02242U, 0x44004U, 0x02240U, 0x44002U, 0x44003U, 0x44000U, + 0x44001U, 0x0C040U, 0x10802U, 0x10801U, 0x10800U, 0x0C044U, 0x10806U, 0x10805U, 0x10804U, 0x23000U, 0x23001U, 0x10809U, 0x10808U, + 0x44012U, 0x44013U, 0x44010U, 0x44011U, 0x60001U, 0x60000U, 0x60003U, 0x60002U, 0x60005U, 0x60004U, 0x10440U, 0x10441U, 0x60009U, + 0x60008U, 0x44024U, 0x6000AU, 0x09200U, 0x12100U, 0x44020U, 0x44021U, 0x60011U, 0x60010U, 0x10821U, 0x10820U, 0x07003U, 0x07002U, + 0x07001U, 0x07000U, 0x23020U, 0x60018U, 0x28045U, 0x28044U, 0x09210U, 0x28042U, 0x28041U, 0x28040U, 0x0C010U, 0x0C011U, 0x02209U, + 0x02208U, 0x10422U, 0x10423U, 0x10420U, 0x10421U, 0x02203U, 0x02202U, 0x02201U, 0x02200U, 0x20180U, 0x20181U, 0x44040U, 0x02204U, + 0x0C000U, 0x0C001U, 0x0C002U, 0x10840U, 0x0C004U, 0x0C005U, 0x0C006U, 0x10844U, 0x0C008U, 0x0C009U, 0x02211U, 0x02210U, 0x0C00CU, + 0x28022U, 0x28021U, 0x28020U, 0x60041U, 0x60040U, 0x10404U, 0x04180U, 0x10402U, 0x10403U, 0x10400U, 0x10401U, 0x02223U, 0x02222U, + 0x02221U, 0x02220U, 0x1040AU, 0x28012U, 0x10408U, 0x28010U, 0x0C020U, 0x0C021U, 0x41200U, 0x41201U, 0x00B01U, 0x00B00U, 0x10410U, + 0x28008U, 0x11081U, 0x11080U, 0x28005U, 0x28004U, 0x28003U, 0x28002U, 0x28001U, 0x28000U, 0x52040U, 0x14204U, 0x22405U, 0x22404U, + 0x14201U, 0x14200U, 0x22401U, 0x22400U, 0x20144U, 0x20145U, 0x44084U, 0x022C0U, 0x20140U, 0x20141U, 0x44080U, 0x44081U, 0x40A08U, + 0x10882U, 0x10881U, 0x10880U, 0x14211U, 0x14210U, 0x1A008U, 0x10884U, 0x40A00U, 0x40A01U, 0x40A02U, 0x01304U, 0x1A002U, 0x01302U, + 0x1A000U, 0x01300U, 0x60081U, 0x60080U, 0x04141U, 0x04140U, 0x60085U, 0x60084U, 0x104C0U, 0x04144U, 0x06400U, 0x06401U, 0x30200U, + 0x30201U, 0x06404U, 0x40640U, 0x30204U, 0x30205U, 0x08603U, 0x08602U, 0x08601U, 0x08600U, 0x00000U, 0x08606U, 0x08605U, 0x08604U, + 0x11041U, 0x11040U, 0x30210U, 0x11042U, 0x11045U, 0x11044U, 0x1A020U, 0x01320U, 0x52000U, 0x52001U, 0x04121U, 0x04120U, 0x20108U, + 0x20109U, 0x08A00U, 0x08A01U, 0x20104U, 0x20105U, 0x02281U, 0x02280U, 0x20100U, 0x20101U, 0x20102U, 0x20103U, 0x0C080U, 0x0C081U, + 0x0C082U, 0x04130U, 0x0C084U, 0x06808U, 0x08A10U, 0x08A11U, 0x11021U, 0x11020U, 0x11023U, 0x11022U, 0x20110U, 0x06800U, 0x20112U, + 0x06802U, 0x04103U, 0x04102U, 0x04101U, 0x04100U, 0x10482U, 0x04106U, 0x10480U, 0x04104U, 0x11011U, 0x11010U, 0x04109U, 0x04108U, + 0x20120U, 0x40600U, 0x20122U, 0x40602U, 0x11009U, 0x11008U, 0x22800U, 0x04110U, 0x1100DU, 0x1100CU, 0x22804U, 0x04114U, 0x11001U, + 0x11000U, 0x11003U, 0x11002U, 0x11005U, 0x11004U, 0x28081U, 0x28080U +}; + +#define X18 0x00040000 /* vector representation of X^{18} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK8 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +unsigned int CGolay2087::getSyndrome1987(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X18; + + if (pattern >= X11) + { + while (pattern & MASK8) + { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned char CGolay2087::decode(const unsigned char* data) +{ + assert(data != nullptr); + + unsigned int code = (data[0U] << 11) + (data[1U] << 3) + (data[2U] >> 5); + unsigned int syndrome = getSyndrome1987(code); + unsigned int error_pattern = DECODING_TABLE_1987[syndrome]; + + if (error_pattern != 0x00U) + code ^= error_pattern; + + return code >> 11; +} + +void CGolay2087::encode(unsigned char* data) +{ + assert(data != nullptr); + + unsigned int value = data[0U]; + + unsigned int cksum = ENCODING_TABLE_2087[value]; + + data[1U] = cksum & 0xFFU; + data[2U] = cksum >> 8; +} diff --git a/src/Golay2087.h b/src/Golay2087.h new file mode 100644 index 0000000..3f6a25f --- /dev/null +++ b/src/Golay2087.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Golay2087_H +#define Golay2087_H + +class CGolay2087 +{ +public: + static void encode(unsigned char* data); + + static unsigned char decode(const unsigned char* data); + +private: + static unsigned int getSyndrome1987(unsigned int pattern); +}; + +#endif diff --git a/src/Golay24128.cpp b/src/Golay24128.cpp new file mode 100644 index 0000000..6f810e6 --- /dev/null +++ b/src/Golay24128.cpp @@ -0,0 +1,1116 @@ +/* + * Copyright (C) 2010,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2002 by Robert H. Morelos-Zaragoza. All rights reserved. + */ + +#include "Golay24128.h" + +#include +#include + +const unsigned int ENCODING_TABLE_23127[] = +{ + 0x000000U, 0x0018EAU, 0x00293EU, 0x0031D4U, 0x004A96U, 0x00527CU, 0x0063A8U, 0x007B42U, 0x008DC6U, 0x00952CU, + 0x00A4F8U, 0x00BC12U, 0x00C750U, 0x00DFBAU, 0x00EE6EU, 0x00F684U, 0x010366U, 0x011B8CU, 0x012A58U, 0x0132B2U, + 0x0149F0U, 0x01511AU, 0x0160CEU, 0x017824U, 0x018EA0U, 0x01964AU, 0x01A79EU, 0x01BF74U, 0x01C436U, 0x01DCDCU, + 0x01ED08U, 0x01F5E2U, 0x0206CCU, 0x021E26U, 0x022FF2U, 0x023718U, 0x024C5AU, 0x0254B0U, 0x026564U, 0x027D8EU, + 0x028B0AU, 0x0293E0U, 0x02A234U, 0x02BADEU, 0x02C19CU, 0x02D976U, 0x02E8A2U, 0x02F048U, 0x0305AAU, 0x031D40U, + 0x032C94U, 0x03347EU, 0x034F3CU, 0x0357D6U, 0x036602U, 0x037EE8U, 0x03886CU, 0x039086U, 0x03A152U, 0x03B9B8U, + 0x03C2FAU, 0x03DA10U, 0x03EBC4U, 0x03F32EU, 0x040D98U, 0x041572U, 0x0424A6U, 0x043C4CU, 0x04470EU, 0x045FE4U, + 0x046E30U, 0x0476DAU, 0x04805EU, 0x0498B4U, 0x04A960U, 0x04B18AU, 0x04CAC8U, 0x04D222U, 0x04E3F6U, 0x04FB1CU, + 0x050EFEU, 0x051614U, 0x0527C0U, 0x053F2AU, 0x054468U, 0x055C82U, 0x056D56U, 0x0575BCU, 0x058338U, 0x059BD2U, + 0x05AA06U, 0x05B2ECU, 0x05C9AEU, 0x05D144U, 0x05E090U, 0x05F87AU, 0x060B54U, 0x0613BEU, 0x06226AU, 0x063A80U, + 0x0641C2U, 0x065928U, 0x0668FCU, 0x067016U, 0x068692U, 0x069E78U, 0x06AFACU, 0x06B746U, 0x06CC04U, 0x06D4EEU, + 0x06E53AU, 0x06FDD0U, 0x070832U, 0x0710D8U, 0x07210CU, 0x0739E6U, 0x0742A4U, 0x075A4EU, 0x076B9AU, 0x077370U, + 0x0785F4U, 0x079D1EU, 0x07ACCAU, 0x07B420U, 0x07CF62U, 0x07D788U, 0x07E65CU, 0x07FEB6U, 0x0803DAU, 0x081B30U, + 0x082AE4U, 0x08320EU, 0x08494CU, 0x0851A6U, 0x086072U, 0x087898U, 0x088E1CU, 0x0896F6U, 0x08A722U, 0x08BFC8U, + 0x08C48AU, 0x08DC60U, 0x08EDB4U, 0x08F55EU, 0x0900BCU, 0x091856U, 0x092982U, 0x093168U, 0x094A2AU, 0x0952C0U, + 0x096314U, 0x097BFEU, 0x098D7AU, 0x099590U, 0x09A444U, 0x09BCAEU, 0x09C7ECU, 0x09DF06U, 0x09EED2U, 0x09F638U, + 0x0A0516U, 0x0A1DFCU, 0x0A2C28U, 0x0A34C2U, 0x0A4F80U, 0x0A576AU, 0x0A66BEU, 0x0A7E54U, 0x0A88D0U, 0x0A903AU, + 0x0AA1EEU, 0x0AB904U, 0x0AC246U, 0x0ADAACU, 0x0AEB78U, 0x0AF392U, 0x0B0670U, 0x0B1E9AU, 0x0B2F4EU, 0x0B37A4U, + 0x0B4CE6U, 0x0B540CU, 0x0B65D8U, 0x0B7D32U, 0x0B8BB6U, 0x0B935CU, 0x0BA288U, 0x0BBA62U, 0x0BC120U, 0x0BD9CAU, + 0x0BE81EU, 0x0BF0F4U, 0x0C0E42U, 0x0C16A8U, 0x0C277CU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3EU, 0x0C6DEAU, 0x0C7500U, + 0x0C8384U, 0x0C9B6EU, 0x0CAABAU, 0x0CB250U, 0x0CC912U, 0x0CD1F8U, 0x0CE02CU, 0x0CF8C6U, 0x0D0D24U, 0x0D15CEU, + 0x0D241AU, 0x0D3CF0U, 0x0D47B2U, 0x0D5F58U, 0x0D6E8CU, 0x0D7666U, 0x0D80E2U, 0x0D9808U, 0x0DA9DCU, 0x0DB136U, + 0x0DCA74U, 0x0DD29EU, 0x0DE34AU, 0x0DFBA0U, 0x0E088EU, 0x0E1064U, 0x0E21B0U, 0x0E395AU, 0x0E4218U, 0x0E5AF2U, + 0x0E6B26U, 0x0E73CCU, 0x0E8548U, 0x0E9DA2U, 0x0EAC76U, 0x0EB49CU, 0x0ECFDEU, 0x0ED734U, 0x0EE6E0U, 0x0EFE0AU, + 0x0F0BE8U, 0x0F1302U, 0x0F22D6U, 0x0F3A3CU, 0x0F417EU, 0x0F5994U, 0x0F6840U, 0x0F70AAU, 0x0F862EU, 0x0F9EC4U, + 0x0FAF10U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD452U, 0x0FE586U, 0x0FFD6CU, 0x1007B4U, 0x101F5EU, 0x102E8AU, 0x103660U, + 0x104D22U, 0x1055C8U, 0x10641CU, 0x107CF6U, 0x108A72U, 0x109298U, 0x10A34CU, 0x10BBA6U, 0x10C0E4U, 0x10D80EU, + 0x10E9DAU, 0x10F130U, 0x1104D2U, 0x111C38U, 0x112DECU, 0x113506U, 0x114E44U, 0x1156AEU, 0x11677AU, 0x117F90U, + 0x118914U, 0x1191FEU, 0x11A02AU, 0x11B8C0U, 0x11C382U, 0x11DB68U, 0x11EABCU, 0x11F256U, 0x120178U, 0x121992U, + 0x122846U, 0x1230ACU, 0x124BEEU, 0x125304U, 0x1262D0U, 0x127A3AU, 0x128CBEU, 0x129454U, 0x12A580U, 0x12BD6AU, + 0x12C628U, 0x12DEC2U, 0x12EF16U, 0x12F7FCU, 0x13021EU, 0x131AF4U, 0x132B20U, 0x1333CAU, 0x134888U, 0x135062U, + 0x1361B6U, 0x13795CU, 0x138FD8U, 0x139732U, 0x13A6E6U, 0x13BE0CU, 0x13C54EU, 0x13DDA4U, 0x13EC70U, 0x13F49AU, + 0x140A2CU, 0x1412C6U, 0x142312U, 0x143BF8U, 0x1440BAU, 0x145850U, 0x146984U, 0x14716EU, 0x1487EAU, 0x149F00U, + 0x14AED4U, 0x14B63EU, 0x14CD7CU, 0x14D596U, 0x14E442U, 0x14FCA8U, 0x15094AU, 0x1511A0U, 0x152074U, 0x15389EU, + 0x1543DCU, 0x155B36U, 0x156AE2U, 0x157208U, 0x15848CU, 0x159C66U, 0x15ADB2U, 0x15B558U, 0x15CE1AU, 0x15D6F0U, + 0x15E724U, 0x15FFCEU, 0x160CE0U, 0x16140AU, 0x1625DEU, 0x163D34U, 0x164676U, 0x165E9CU, 0x166F48U, 0x1677A2U, + 0x168126U, 0x1699CCU, 0x16A818U, 0x16B0F2U, 0x16CBB0U, 0x16D35AU, 0x16E28EU, 0x16FA64U, 0x170F86U, 0x17176CU, + 0x1726B8U, 0x173E52U, 0x174510U, 0x175DFAU, 0x176C2EU, 0x1774C4U, 0x178240U, 0x179AAAU, 0x17AB7EU, 0x17B394U, + 0x17C8D6U, 0x17D03CU, 0x17E1E8U, 0x17F902U, 0x18046EU, 0x181C84U, 0x182D50U, 0x1835BAU, 0x184EF8U, 0x185612U, + 0x1867C6U, 0x187F2CU, 0x1889A8U, 0x189142U, 0x18A096U, 0x18B87CU, 0x18C33EU, 0x18DBD4U, 0x18EA00U, 0x18F2EAU, + 0x190708U, 0x191FE2U, 0x192E36U, 0x1936DCU, 0x194D9EU, 0x195574U, 0x1964A0U, 0x197C4AU, 0x198ACEU, 0x199224U, + 0x19A3F0U, 0x19BB1AU, 0x19C058U, 0x19D8B2U, 0x19E966U, 0x19F18CU, 0x1A02A2U, 0x1A1A48U, 0x1A2B9CU, 0x1A3376U, + 0x1A4834U, 0x1A50DEU, 0x1A610AU, 0x1A79E0U, 0x1A8F64U, 0x1A978EU, 0x1AA65AU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD18U, + 0x1AECCCU, 0x1AF426U, 0x1B01C4U, 0x1B192EU, 0x1B28FAU, 0x1B3010U, 0x1B4B52U, 0x1B53B8U, 0x1B626CU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E8U, 0x1BA53CU, 0x1BBDD6U, 0x1BC694U, 0x1BDE7EU, 0x1BEFAAU, 0x1BF740U, 0x1C09F6U, 0x1C111CU, + 0x1C20C8U, 0x1C3822U, 0x1C4360U, 0x1C5B8AU, 0x1C6A5EU, 0x1C72B4U, 0x1C8430U, 0x1C9CDAU, 0x1CAD0EU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64CU, 0x1CE798U, 0x1CFF72U, 0x1D0A90U, 0x1D127AU, 0x1D23AEU, 0x1D3B44U, 0x1D4006U, 0x1D58ECU, + 0x1D6938U, 0x1D71D2U, 0x1D8756U, 0x1D9FBCU, 0x1DAE68U, 0x1DB682U, 0x1DCDC0U, 0x1DD52AU, 0x1DE4FEU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D0U, 0x1E2604U, 0x1E3EEEU, 0x1E45ACU, 0x1E5D46U, 0x1E6C92U, 0x1E7478U, 0x1E82FCU, 0x1E9A16U, + 0x1EABC2U, 0x1EB328U, 0x1EC86AU, 0x1ED080U, 0x1EE154U, 0x1EF9BEU, 0x1F0C5CU, 0x1F14B6U, 0x1F2562U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E20U, 0x1F6FF4U, 0x1F771EU, 0x1F819AU, 0x1F9970U, 0x1FA8A4U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E6U, + 0x1FE232U, 0x1FFAD8U, 0x200F68U, 0x201782U, 0x202656U, 0x203EBCU, 0x2045FEU, 0x205D14U, 0x206CC0U, 0x20742AU, + 0x2082AEU, 0x209A44U, 0x20AB90U, 0x20B37AU, 0x20C838U, 0x20D0D2U, 0x20E106U, 0x20F9ECU, 0x210C0EU, 0x2114E4U, + 0x212530U, 0x213DDAU, 0x214698U, 0x215E72U, 0x216FA6U, 0x21774CU, 0x2181C8U, 0x219922U, 0x21A8F6U, 0x21B01CU, + 0x21CB5EU, 0x21D3B4U, 0x21E260U, 0x21FA8AU, 0x2209A4U, 0x22114EU, 0x22209AU, 0x223870U, 0x224332U, 0x225BD8U, + 0x226A0CU, 0x2272E6U, 0x228462U, 0x229C88U, 0x22AD5CU, 0x22B5B6U, 0x22CEF4U, 0x22D61EU, 0x22E7CAU, 0x22FF20U, + 0x230AC2U, 0x231228U, 0x2323FCU, 0x233B16U, 0x234054U, 0x2358BEU, 0x23696AU, 0x237180U, 0x238704U, 0x239FEEU, + 0x23AE3AU, 0x23B6D0U, 0x23CD92U, 0x23D578U, 0x23E4ACU, 0x23FC46U, 0x2402F0U, 0x241A1AU, 0x242BCEU, 0x243324U, + 0x244866U, 0x24508CU, 0x246158U, 0x2479B2U, 0x248F36U, 0x2497DCU, 0x24A608U, 0x24BEE2U, 0x24C5A0U, 0x24DD4AU, + 0x24EC9EU, 0x24F474U, 0x250196U, 0x25197CU, 0x2528A8U, 0x253042U, 0x254B00U, 0x2553EAU, 0x25623EU, 0x257AD4U, + 0x258C50U, 0x2594BAU, 0x25A56EU, 0x25BD84U, 0x25C6C6U, 0x25DE2CU, 0x25EFF8U, 0x25F712U, 0x26043CU, 0x261CD6U, + 0x262D02U, 0x2635E8U, 0x264EAAU, 0x265640U, 0x266794U, 0x267F7EU, 0x2689FAU, 0x269110U, 0x26A0C4U, 0x26B82EU, + 0x26C36CU, 0x26DB86U, 0x26EA52U, 0x26F2B8U, 0x27075AU, 0x271FB0U, 0x272E64U, 0x27368EU, 0x274DCCU, 0x275526U, + 0x2764F2U, 0x277C18U, 0x278A9CU, 0x279276U, 0x27A3A2U, 0x27BB48U, 0x27C00AU, 0x27D8E0U, 0x27E934U, 0x27F1DEU, + 0x280CB2U, 0x281458U, 0x28258CU, 0x283D66U, 0x284624U, 0x285ECEU, 0x286F1AU, 0x2877F0U, 0x288174U, 0x28999EU, + 0x28A84AU, 0x28B0A0U, 0x28CBE2U, 0x28D308U, 0x28E2DCU, 0x28FA36U, 0x290FD4U, 0x29173EU, 0x2926EAU, 0x293E00U, + 0x294542U, 0x295DA8U, 0x296C7CU, 0x297496U, 0x298212U, 0x299AF8U, 0x29AB2CU, 0x29B3C6U, 0x29C884U, 0x29D06EU, + 0x29E1BAU, 0x29F950U, 0x2A0A7EU, 0x2A1294U, 0x2A2340U, 0x2A3BAAU, 0x2A40E8U, 0x2A5802U, 0x2A69D6U, 0x2A713CU, + 0x2A87B8U, 0x2A9F52U, 0x2AAE86U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C4U, 0x2AE410U, 0x2AFCFAU, 0x2B0918U, 0x2B11F2U, + 0x2B2026U, 0x2B38CCU, 0x2B438EU, 0x2B5B64U, 0x2B6AB0U, 0x2B725AU, 0x2B84DEU, 0x2B9C34U, 0x2BADE0U, 0x2BB50AU, + 0x2BCE48U, 0x2BD6A2U, 0x2BE776U, 0x2BFF9CU, 0x2C012AU, 0x2C19C0U, 0x2C2814U, 0x2C30FEU, 0x2C4BBCU, 0x2C5356U, + 0x2C6282U, 0x2C7A68U, 0x2C8CECU, 0x2C9406U, 0x2CA5D2U, 0x2CBD38U, 0x2CC67AU, 0x2CDE90U, 0x2CEF44U, 0x2CF7AEU, + 0x2D024CU, 0x2D1AA6U, 0x2D2B72U, 0x2D3398U, 0x2D48DAU, 0x2D5030U, 0x2D61E4U, 0x2D790EU, 0x2D8F8AU, 0x2D9760U, + 0x2DA6B4U, 0x2DBE5EU, 0x2DC51CU, 0x2DDDF6U, 0x2DEC22U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0CU, 0x2E2ED8U, 0x2E3632U, + 0x2E4D70U, 0x2E559AU, 0x2E644EU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CAU, 0x2EA31EU, 0x2EBBF4U, 0x2EC0B6U, 0x2ED85CU, + 0x2EE988U, 0x2EF162U, 0x2F0480U, 0x2F1C6AU, 0x2F2DBEU, 0x2F3554U, 0x2F4E16U, 0x2F56FCU, 0x2F6728U, 0x2F7FC2U, + 0x2F8946U, 0x2F91ACU, 0x2FA078U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3AU, 0x2FEAEEU, 0x2FF204U, 0x3008DCU, 0x301036U, + 0x3021E2U, 0x303908U, 0x30424AU, 0x305AA0U, 0x306B74U, 0x30739EU, 0x30851AU, 0x309DF0U, 0x30AC24U, 0x30B4CEU, + 0x30CF8CU, 0x30D766U, 0x30E6B2U, 0x30FE58U, 0x310BBAU, 0x311350U, 0x312284U, 0x313A6EU, 0x31412CU, 0x3159C6U, + 0x316812U, 0x3170F8U, 0x31867CU, 0x319E96U, 0x31AF42U, 0x31B7A8U, 0x31CCEAU, 0x31D400U, 0x31E5D4U, 0x31FD3EU, + 0x320E10U, 0x3216FAU, 0x32272EU, 0x323FC4U, 0x324486U, 0x325C6CU, 0x326DB8U, 0x327552U, 0x3283D6U, 0x329B3CU, + 0x32AAE8U, 0x32B202U, 0x32C940U, 0x32D1AAU, 0x32E07EU, 0x32F894U, 0x330D76U, 0x33159CU, 0x332448U, 0x333CA2U, + 0x3347E0U, 0x335F0AU, 0x336EDEU, 0x337634U, 0x3380B0U, 0x33985AU, 0x33A98EU, 0x33B164U, 0x33CA26U, 0x33D2CCU, + 0x33E318U, 0x33FBF2U, 0x340544U, 0x341DAEU, 0x342C7AU, 0x343490U, 0x344FD2U, 0x345738U, 0x3466ECU, 0x347E06U, + 0x348882U, 0x349068U, 0x34A1BCU, 0x34B956U, 0x34C214U, 0x34DAFEU, 0x34EB2AU, 0x34F3C0U, 0x350622U, 0x351EC8U, + 0x352F1CU, 0x3537F6U, 0x354CB4U, 0x35545EU, 0x35658AU, 0x357D60U, 0x358BE4U, 0x35930EU, 0x35A2DAU, 0x35BA30U, + 0x35C172U, 0x35D998U, 0x35E84CU, 0x35F0A6U, 0x360388U, 0x361B62U, 0x362AB6U, 0x36325CU, 0x36491EU, 0x3651F4U, + 0x366020U, 0x3678CAU, 0x368E4EU, 0x3696A4U, 0x36A770U, 0x36BF9AU, 0x36C4D8U, 0x36DC32U, 0x36EDE6U, 0x36F50CU, + 0x3700EEU, 0x371804U, 0x3729D0U, 0x37313AU, 0x374A78U, 0x375292U, 0x376346U, 0x377BACU, 0x378D28U, 0x3795C2U, + 0x37A416U, 0x37BCFCU, 0x37C7BEU, 0x37DF54U, 0x37EE80U, 0x37F66AU, 0x380B06U, 0x3813ECU, 0x382238U, 0x383AD2U, + 0x384190U, 0x38597AU, 0x3868AEU, 0x387044U, 0x3886C0U, 0x389E2AU, 0x38AFFEU, 0x38B714U, 0x38CC56U, 0x38D4BCU, + 0x38E568U, 0x38FD82U, 0x390860U, 0x39108AU, 0x39215EU, 0x3939B4U, 0x3942F6U, 0x395A1CU, 0x396BC8U, 0x397322U, + 0x3985A6U, 0x399D4CU, 0x39AC98U, 0x39B472U, 0x39CF30U, 0x39D7DAU, 0x39E60EU, 0x39FEE4U, 0x3A0DCAU, 0x3A1520U, + 0x3A24F4U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB6U, 0x3A6E62U, 0x3A7688U, 0x3A800CU, 0x3A98E6U, 0x3AA932U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD270U, 0x3AE3A4U, 0x3AFB4EU, 0x3B0EACU, 0x3B1646U, 0x3B2792U, 0x3B3F78U, 0x3B443AU, 0x3B5CD0U, + 0x3B6D04U, 0x3B75EEU, 0x3B836AU, 0x3B9B80U, 0x3BAA54U, 0x3BB2BEU, 0x3BC9FCU, 0x3BD116U, 0x3BE0C2U, 0x3BF828U, + 0x3C069EU, 0x3C1E74U, 0x3C2FA0U, 0x3C374AU, 0x3C4C08U, 0x3C54E2U, 0x3C6536U, 0x3C7DDCU, 0x3C8B58U, 0x3C93B2U, + 0x3CA266U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD924U, 0x3CE8F0U, 0x3CF01AU, 0x3D05F8U, 0x3D1D12U, 0x3D2CC6U, 0x3D342CU, + 0x3D4F6EU, 0x3D5784U, 0x3D6650U, 0x3D7EBAU, 0x3D883EU, 0x3D90D4U, 0x3DA100U, 0x3DB9EAU, 0x3DC2A8U, 0x3DDA42U, + 0x3DEB96U, 0x3DF37CU, 0x3E0052U, 0x3E18B8U, 0x3E296CU, 0x3E3186U, 0x3E4AC4U, 0x3E522EU, 0x3E63FAU, 0x3E7B10U, + 0x3E8D94U, 0x3E957EU, 0x3EA4AAU, 0x3EBC40U, 0x3EC702U, 0x3EDFE8U, 0x3EEE3CU, 0x3EF6D6U, 0x3F0334U, 0x3F1BDEU, + 0x3F2A0AU, 0x3F32E0U, 0x3F49A2U, 0x3F5148U, 0x3F609CU, 0x3F7876U, 0x3F8EF2U, 0x3F9618U, 0x3FA7CCU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8EU, 0x3FED5AU, 0x3FF5B0U, 0x40063AU, 0x401ED0U, 0x402F04U, 0x4037EEU, 0x404CACU, 0x405446U, + 0x406592U, 0x407D78U, 0x408BFCU, 0x409316U, 0x40A2C2U, 0x40BA28U, 0x40C16AU, 0x40D980U, 0x40E854U, 0x40F0BEU, + 0x41055CU, 0x411DB6U, 0x412C62U, 0x413488U, 0x414FCAU, 0x415720U, 0x4166F4U, 0x417E1EU, 0x41889AU, 0x419070U, + 0x41A1A4U, 0x41B94EU, 0x41C20CU, 0x41DAE6U, 0x41EB32U, 0x41F3D8U, 0x4200F6U, 0x42181CU, 0x4229C8U, 0x423122U, + 0x424A60U, 0x42528AU, 0x42635EU, 0x427BB4U, 0x428D30U, 0x4295DAU, 0x42A40EU, 0x42BCE4U, 0x42C7A6U, 0x42DF4CU, + 0x42EE98U, 0x42F672U, 0x430390U, 0x431B7AU, 0x432AAEU, 0x433244U, 0x434906U, 0x4351ECU, 0x436038U, 0x4378D2U, + 0x438E56U, 0x4396BCU, 0x43A768U, 0x43BF82U, 0x43C4C0U, 0x43DC2AU, 0x43EDFEU, 0x43F514U, 0x440BA2U, 0x441348U, + 0x44229CU, 0x443A76U, 0x444134U, 0x4459DEU, 0x44680AU, 0x4470E0U, 0x448664U, 0x449E8EU, 0x44AF5AU, 0x44B7B0U, + 0x44CCF2U, 0x44D418U, 0x44E5CCU, 0x44FD26U, 0x4508C4U, 0x45102EU, 0x4521FAU, 0x453910U, 0x454252U, 0x455AB8U, + 0x456B6CU, 0x457386U, 0x458502U, 0x459DE8U, 0x45AC3CU, 0x45B4D6U, 0x45CF94U, 0x45D77EU, 0x45E6AAU, 0x45FE40U, + 0x460D6EU, 0x461584U, 0x462450U, 0x463CBAU, 0x4647F8U, 0x465F12U, 0x466EC6U, 0x46762CU, 0x4680A8U, 0x469842U, + 0x46A996U, 0x46B17CU, 0x46CA3EU, 0x46D2D4U, 0x46E300U, 0x46FBEAU, 0x470E08U, 0x4716E2U, 0x472736U, 0x473FDCU, + 0x47449EU, 0x475C74U, 0x476DA0U, 0x47754AU, 0x4783CEU, 0x479B24U, 0x47AAF0U, 0x47B21AU, 0x47C958U, 0x47D1B2U, + 0x47E066U, 0x47F88CU, 0x4805E0U, 0x481D0AU, 0x482CDEU, 0x483434U, 0x484F76U, 0x48579CU, 0x486648U, 0x487EA2U, + 0x488826U, 0x4890CCU, 0x48A118U, 0x48B9F2U, 0x48C2B0U, 0x48DA5AU, 0x48EB8EU, 0x48F364U, 0x490686U, 0x491E6CU, + 0x492FB8U, 0x493752U, 0x494C10U, 0x4954FAU, 0x49652EU, 0x497DC4U, 0x498B40U, 0x4993AAU, 0x49A27EU, 0x49BA94U, + 0x49C1D6U, 0x49D93CU, 0x49E8E8U, 0x49F002U, 0x4A032CU, 0x4A1BC6U, 0x4A2A12U, 0x4A32F8U, 0x4A49BAU, 0x4A5150U, + 0x4A6084U, 0x4A786EU, 0x4A8EEAU, 0x4A9600U, 0x4AA7D4U, 0x4ABF3EU, 0x4AC47CU, 0x4ADC96U, 0x4AED42U, 0x4AF5A8U, + 0x4B004AU, 0x4B18A0U, 0x4B2974U, 0x4B319EU, 0x4B4ADCU, 0x4B5236U, 0x4B63E2U, 0x4B7B08U, 0x4B8D8CU, 0x4B9566U, + 0x4BA4B2U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF0U, 0x4BEE24U, 0x4BF6CEU, 0x4C0878U, 0x4C1092U, 0x4C2146U, 0x4C39ACU, + 0x4C42EEU, 0x4C5A04U, 0x4C6BD0U, 0x4C733AU, 0x4C85BEU, 0x4C9D54U, 0x4CAC80U, 0x4CB46AU, 0x4CCF28U, 0x4CD7C2U, + 0x4CE616U, 0x4CFEFCU, 0x4D0B1EU, 0x4D13F4U, 0x4D2220U, 0x4D3ACAU, 0x4D4188U, 0x4D5962U, 0x4D68B6U, 0x4D705CU, + 0x4D86D8U, 0x4D9E32U, 0x4DAFE6U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A4U, 0x4DE570U, 0x4DFD9AU, 0x4E0EB4U, 0x4E165EU, + 0x4E278AU, 0x4E3F60U, 0x4E4422U, 0x4E5CC8U, 0x4E6D1CU, 0x4E75F6U, 0x4E8372U, 0x4E9B98U, 0x4EAA4CU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10EU, 0x4EE0DAU, 0x4EF830U, 0x4F0DD2U, 0x4F1538U, 0x4F24ECU, 0x4F3C06U, 0x4F4744U, 0x4F5FAEU, + 0x4F6E7AU, 0x4F7690U, 0x4F8014U, 0x4F98FEU, 0x4FA92AU, 0x4FB1C0U, 0x4FCA82U, 0x4FD268U, 0x4FE3BCU, 0x4FFB56U, + 0x50018EU, 0x501964U, 0x5028B0U, 0x50305AU, 0x504B18U, 0x5053F2U, 0x506226U, 0x507ACCU, 0x508C48U, 0x5094A2U, + 0x50A576U, 0x50BD9CU, 0x50C6DEU, 0x50DE34U, 0x50EFE0U, 0x50F70AU, 0x5102E8U, 0x511A02U, 0x512BD6U, 0x51333CU, + 0x51487EU, 0x515094U, 0x516140U, 0x5179AAU, 0x518F2EU, 0x5197C4U, 0x51A610U, 0x51BEFAU, 0x51C5B8U, 0x51DD52U, + 0x51EC86U, 0x51F46CU, 0x520742U, 0x521FA8U, 0x522E7CU, 0x523696U, 0x524DD4U, 0x52553EU, 0x5264EAU, 0x527C00U, + 0x528A84U, 0x52926EU, 0x52A3BAU, 0x52BB50U, 0x52C012U, 0x52D8F8U, 0x52E92CU, 0x52F1C6U, 0x530424U, 0x531CCEU, + 0x532D1AU, 0x5335F0U, 0x534EB2U, 0x535658U, 0x53678CU, 0x537F66U, 0x5389E2U, 0x539108U, 0x53A0DCU, 0x53B836U, + 0x53C374U, 0x53DB9EU, 0x53EA4AU, 0x53F2A0U, 0x540C16U, 0x5414FCU, 0x542528U, 0x543DC2U, 0x544680U, 0x545E6AU, + 0x546FBEU, 0x547754U, 0x5481D0U, 0x54993AU, 0x54A8EEU, 0x54B004U, 0x54CB46U, 0x54D3ACU, 0x54E278U, 0x54FA92U, + 0x550F70U, 0x55179AU, 0x55264EU, 0x553EA4U, 0x5545E6U, 0x555D0CU, 0x556CD8U, 0x557432U, 0x5582B6U, 0x559A5CU, + 0x55AB88U, 0x55B362U, 0x55C820U, 0x55D0CAU, 0x55E11EU, 0x55F9F4U, 0x560ADAU, 0x561230U, 0x5623E4U, 0x563B0EU, + 0x56404CU, 0x5658A6U, 0x566972U, 0x567198U, 0x56871CU, 0x569FF6U, 0x56AE22U, 0x56B6C8U, 0x56CD8AU, 0x56D560U, + 0x56E4B4U, 0x56FC5EU, 0x5709BCU, 0x571156U, 0x572082U, 0x573868U, 0x57432AU, 0x575BC0U, 0x576A14U, 0x5772FEU, + 0x57847AU, 0x579C90U, 0x57AD44U, 0x57B5AEU, 0x57CEECU, 0x57D606U, 0x57E7D2U, 0x57FF38U, 0x580254U, 0x581ABEU, + 0x582B6AU, 0x583380U, 0x5848C2U, 0x585028U, 0x5861FCU, 0x587916U, 0x588F92U, 0x589778U, 0x58A6ACU, 0x58BE46U, + 0x58C504U, 0x58DDEEU, 0x58EC3AU, 0x58F4D0U, 0x590132U, 0x5919D8U, 0x59280CU, 0x5930E6U, 0x594BA4U, 0x59534EU, + 0x59629AU, 0x597A70U, 0x598CF4U, 0x59941EU, 0x59A5CAU, 0x59BD20U, 0x59C662U, 0x59DE88U, 0x59EF5CU, 0x59F7B6U, + 0x5A0498U, 0x5A1C72U, 0x5A2DA6U, 0x5A354CU, 0x5A4E0EU, 0x5A56E4U, 0x5A6730U, 0x5A7FDAU, 0x5A895EU, 0x5A91B4U, + 0x5AA060U, 0x5AB88AU, 0x5AC3C8U, 0x5ADB22U, 0x5AEAF6U, 0x5AF21CU, 0x5B07FEU, 0x5B1F14U, 0x5B2EC0U, 0x5B362AU, + 0x5B4D68U, 0x5B5582U, 0x5B6456U, 0x5B7CBCU, 0x5B8A38U, 0x5B92D2U, 0x5BA306U, 0x5BBBECU, 0x5BC0AEU, 0x5BD844U, + 0x5BE990U, 0x5BF17AU, 0x5C0FCCU, 0x5C1726U, 0x5C26F2U, 0x5C3E18U, 0x5C455AU, 0x5C5DB0U, 0x5C6C64U, 0x5C748EU, + 0x5C820AU, 0x5C9AE0U, 0x5CAB34U, 0x5CB3DEU, 0x5CC89CU, 0x5CD076U, 0x5CE1A2U, 0x5CF948U, 0x5D0CAAU, 0x5D1440U, + 0x5D2594U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED6U, 0x5D6F02U, 0x5D77E8U, 0x5D816CU, 0x5D9986U, 0x5DA852U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD310U, 0x5DE2C4U, 0x5DFA2EU, 0x5E0900U, 0x5E11EAU, 0x5E203EU, 0x5E38D4U, 0x5E4396U, 0x5E5B7CU, + 0x5E6AA8U, 0x5E7242U, 0x5E84C6U, 0x5E9C2CU, 0x5EADF8U, 0x5EB512U, 0x5ECE50U, 0x5ED6BAU, 0x5EE76EU, 0x5EFF84U, + 0x5F0A66U, 0x5F128CU, 0x5F2358U, 0x5F3BB2U, 0x5F40F0U, 0x5F581AU, 0x5F69CEU, 0x5F7124U, 0x5F87A0U, 0x5F9F4AU, + 0x5FAE9EU, 0x5FB674U, 0x5FCD36U, 0x5FD5DCU, 0x5FE408U, 0x5FFCE2U, 0x600952U, 0x6011B8U, 0x60206CU, 0x603886U, + 0x6043C4U, 0x605B2EU, 0x606AFAU, 0x607210U, 0x608494U, 0x609C7EU, 0x60ADAAU, 0x60B540U, 0x60CE02U, 0x60D6E8U, + 0x60E73CU, 0x60FFD6U, 0x610A34U, 0x6112DEU, 0x61230AU, 0x613BE0U, 0x6140A2U, 0x615848U, 0x61699CU, 0x617176U, + 0x6187F2U, 0x619F18U, 0x61AECCU, 0x61B626U, 0x61CD64U, 0x61D58EU, 0x61E45AU, 0x61FCB0U, 0x620F9EU, 0x621774U, + 0x6226A0U, 0x623E4AU, 0x624508U, 0x625DE2U, 0x626C36U, 0x6274DCU, 0x628258U, 0x629AB2U, 0x62AB66U, 0x62B38CU, + 0x62C8CEU, 0x62D024U, 0x62E1F0U, 0x62F91AU, 0x630CF8U, 0x631412U, 0x6325C6U, 0x633D2CU, 0x63466EU, 0x635E84U, + 0x636F50U, 0x6377BAU, 0x63813EU, 0x6399D4U, 0x63A800U, 0x63B0EAU, 0x63CBA8U, 0x63D342U, 0x63E296U, 0x63FA7CU, + 0x6404CAU, 0x641C20U, 0x642DF4U, 0x64351EU, 0x644E5CU, 0x6456B6U, 0x646762U, 0x647F88U, 0x64890CU, 0x6491E6U, + 0x64A032U, 0x64B8D8U, 0x64C39AU, 0x64DB70U, 0x64EAA4U, 0x64F24EU, 0x6507ACU, 0x651F46U, 0x652E92U, 0x653678U, + 0x654D3AU, 0x6555D0U, 0x656404U, 0x657CEEU, 0x658A6AU, 0x659280U, 0x65A354U, 0x65BBBEU, 0x65C0FCU, 0x65D816U, + 0x65E9C2U, 0x65F128U, 0x660206U, 0x661AECU, 0x662B38U, 0x6633D2U, 0x664890U, 0x66507AU, 0x6661AEU, 0x667944U, + 0x668FC0U, 0x66972AU, 0x66A6FEU, 0x66BE14U, 0x66C556U, 0x66DDBCU, 0x66EC68U, 0x66F482U, 0x670160U, 0x67198AU, + 0x67285EU, 0x6730B4U, 0x674BF6U, 0x67531CU, 0x6762C8U, 0x677A22U, 0x678CA6U, 0x67944CU, 0x67A598U, 0x67BD72U, + 0x67C630U, 0x67DEDAU, 0x67EF0EU, 0x67F7E4U, 0x680A88U, 0x681262U, 0x6823B6U, 0x683B5CU, 0x68401EU, 0x6858F4U, + 0x686920U, 0x6871CAU, 0x68874EU, 0x689FA4U, 0x68AE70U, 0x68B69AU, 0x68CDD8U, 0x68D532U, 0x68E4E6U, 0x68FC0CU, + 0x6909EEU, 0x691104U, 0x6920D0U, 0x69383AU, 0x694378U, 0x695B92U, 0x696A46U, 0x6972ACU, 0x698428U, 0x699CC2U, + 0x69AD16U, 0x69B5FCU, 0x69CEBEU, 0x69D654U, 0x69E780U, 0x69FF6AU, 0x6A0C44U, 0x6A14AEU, 0x6A257AU, 0x6A3D90U, + 0x6A46D2U, 0x6A5E38U, 0x6A6FECU, 0x6A7706U, 0x6A8182U, 0x6A9968U, 0x6AA8BCU, 0x6AB056U, 0x6ACB14U, 0x6AD3FEU, + 0x6AE22AU, 0x6AFAC0U, 0x6B0F22U, 0x6B17C8U, 0x6B261CU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5EU, 0x6B6C8AU, 0x6B7460U, + 0x6B82E4U, 0x6B9A0EU, 0x6BABDAU, 0x6BB330U, 0x6BC872U, 0x6BD098U, 0x6BE14CU, 0x6BF9A6U, 0x6C0710U, 0x6C1FFAU, + 0x6C2E2EU, 0x6C36C4U, 0x6C4D86U, 0x6C556CU, 0x6C64B8U, 0x6C7C52U, 0x6C8AD6U, 0x6C923CU, 0x6CA3E8U, 0x6CBB02U, + 0x6CC040U, 0x6CD8AAU, 0x6CE97EU, 0x6CF194U, 0x6D0476U, 0x6D1C9CU, 0x6D2D48U, 0x6D35A2U, 0x6D4EE0U, 0x6D560AU, + 0x6D67DEU, 0x6D7F34U, 0x6D89B0U, 0x6D915AU, 0x6DA08EU, 0x6DB864U, 0x6DC326U, 0x6DDBCCU, 0x6DEA18U, 0x6DF2F2U, + 0x6E01DCU, 0x6E1936U, 0x6E28E2U, 0x6E3008U, 0x6E4B4AU, 0x6E53A0U, 0x6E6274U, 0x6E7A9EU, 0x6E8C1AU, 0x6E94F0U, + 0x6EA524U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE66U, 0x6EEFB2U, 0x6EF758U, 0x6F02BAU, 0x6F1A50U, 0x6F2B84U, 0x6F336EU, + 0x6F482CU, 0x6F50C6U, 0x6F6112U, 0x6F79F8U, 0x6F8F7CU, 0x6F9796U, 0x6FA642U, 0x6FBEA8U, 0x6FC5EAU, 0x6FDD00U, + 0x6FECD4U, 0x6FF43EU, 0x700EE6U, 0x70160CU, 0x7027D8U, 0x703F32U, 0x704470U, 0x705C9AU, 0x706D4EU, 0x7075A4U, + 0x708320U, 0x709BCAU, 0x70AA1EU, 0x70B2F4U, 0x70C9B6U, 0x70D15CU, 0x70E088U, 0x70F862U, 0x710D80U, 0x71156AU, + 0x7124BEU, 0x713C54U, 0x714716U, 0x715FFCU, 0x716E28U, 0x7176C2U, 0x718046U, 0x7198ACU, 0x71A978U, 0x71B192U, + 0x71CAD0U, 0x71D23AU, 0x71E3EEU, 0x71FB04U, 0x72082AU, 0x7210C0U, 0x722114U, 0x7239FEU, 0x7242BCU, 0x725A56U, + 0x726B82U, 0x727368U, 0x7285ECU, 0x729D06U, 0x72ACD2U, 0x72B438U, 0x72CF7AU, 0x72D790U, 0x72E644U, 0x72FEAEU, + 0x730B4CU, 0x7313A6U, 0x732272U, 0x733A98U, 0x7341DAU, 0x735930U, 0x7368E4U, 0x73700EU, 0x73868AU, 0x739E60U, + 0x73AFB4U, 0x73B75EU, 0x73CC1CU, 0x73D4F6U, 0x73E522U, 0x73FDC8U, 0x74037EU, 0x741B94U, 0x742A40U, 0x7432AAU, + 0x7449E8U, 0x745102U, 0x7460D6U, 0x74783CU, 0x748EB8U, 0x749652U, 0x74A786U, 0x74BF6CU, 0x74C42EU, 0x74DCC4U, + 0x74ED10U, 0x74F5FAU, 0x750018U, 0x7518F2U, 0x752926U, 0x7531CCU, 0x754A8EU, 0x755264U, 0x7563B0U, 0x757B5AU, + 0x758DDEU, 0x759534U, 0x75A4E0U, 0x75BC0AU, 0x75C748U, 0x75DFA2U, 0x75EE76U, 0x75F69CU, 0x7605B2U, 0x761D58U, + 0x762C8CU, 0x763466U, 0x764F24U, 0x7657CEU, 0x76661AU, 0x767EF0U, 0x768874U, 0x76909EU, 0x76A14AU, 0x76B9A0U, + 0x76C2E2U, 0x76DA08U, 0x76EBDCU, 0x76F336U, 0x7706D4U, 0x771E3EU, 0x772FEAU, 0x773700U, 0x774C42U, 0x7754A8U, + 0x77657CU, 0x777D96U, 0x778B12U, 0x7793F8U, 0x77A22CU, 0x77BAC6U, 0x77C184U, 0x77D96EU, 0x77E8BAU, 0x77F050U, + 0x780D3CU, 0x7815D6U, 0x782402U, 0x783CE8U, 0x7847AAU, 0x785F40U, 0x786E94U, 0x78767EU, 0x7880FAU, 0x789810U, + 0x78A9C4U, 0x78B12EU, 0x78CA6CU, 0x78D286U, 0x78E352U, 0x78FBB8U, 0x790E5AU, 0x7916B0U, 0x792764U, 0x793F8EU, + 0x7944CCU, 0x795C26U, 0x796DF2U, 0x797518U, 0x79839CU, 0x799B76U, 0x79AAA2U, 0x79B248U, 0x79C90AU, 0x79D1E0U, + 0x79E034U, 0x79F8DEU, 0x7A0BF0U, 0x7A131AU, 0x7A22CEU, 0x7A3A24U, 0x7A4166U, 0x7A598CU, 0x7A6858U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDCU, 0x7AAF08U, 0x7AB7E2U, 0x7ACCA0U, 0x7AD44AU, 0x7AE59EU, 0x7AFD74U, 0x7B0896U, 0x7B107CU, + 0x7B21A8U, 0x7B3942U, 0x7B4200U, 0x7B5AEAU, 0x7B6B3EU, 0x7B73D4U, 0x7B8550U, 0x7B9DBAU, 0x7BAC6EU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72CU, 0x7BE6F8U, 0x7BFE12U, 0x7C00A4U, 0x7C184EU, 0x7C299AU, 0x7C3170U, 0x7C4A32U, 0x7C52D8U, + 0x7C630CU, 0x7C7BE6U, 0x7C8D62U, 0x7C9588U, 0x7CA45CU, 0x7CBCB6U, 0x7CC7F4U, 0x7CDF1EU, 0x7CEECAU, 0x7CF620U, + 0x7D03C2U, 0x7D1B28U, 0x7D2AFCU, 0x7D3216U, 0x7D4954U, 0x7D51BEU, 0x7D606AU, 0x7D7880U, 0x7D8E04U, 0x7D96EEU, + 0x7DA73AU, 0x7DBFD0U, 0x7DC492U, 0x7DDC78U, 0x7DEDACU, 0x7DF546U, 0x7E0668U, 0x7E1E82U, 0x7E2F56U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5414U, 0x7E65C0U, 0x7E7D2AU, 0x7E8BAEU, 0x7E9344U, 0x7EA290U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D2U, + 0x7EE806U, 0x7EF0ECU, 0x7F050EU, 0x7F1DE4U, 0x7F2C30U, 0x7F34DAU, 0x7F4F98U, 0x7F5772U, 0x7F66A6U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9022U, 0x7FA1F6U, 0x7FB91CU, 0x7FC25EU, 0x7FDAB4U, 0x7FEB60U, 0x7FF38AU, 0x800C74U, 0x80149EU, + 0x80254AU, 0x803DA0U, 0x8046E2U, 0x805E08U, 0x806FDCU, 0x807736U, 0x8081B2U, 0x809958U, 0x80A88CU, 0x80B066U, + 0x80CB24U, 0x80D3CEU, 0x80E21AU, 0x80FAF0U, 0x810F12U, 0x8117F8U, 0x81262CU, 0x813EC6U, 0x814584U, 0x815D6EU, + 0x816CBAU, 0x817450U, 0x8182D4U, 0x819A3EU, 0x81ABEAU, 0x81B300U, 0x81C842U, 0x81D0A8U, 0x81E17CU, 0x81F996U, + 0x820AB8U, 0x821252U, 0x822386U, 0x823B6CU, 0x82402EU, 0x8258C4U, 0x826910U, 0x8271FAU, 0x82877EU, 0x829F94U, + 0x82AE40U, 0x82B6AAU, 0x82CDE8U, 0x82D502U, 0x82E4D6U, 0x82FC3CU, 0x8309DEU, 0x831134U, 0x8320E0U, 0x83380AU, + 0x834348U, 0x835BA2U, 0x836A76U, 0x83729CU, 0x838418U, 0x839CF2U, 0x83AD26U, 0x83B5CCU, 0x83CE8EU, 0x83D664U, + 0x83E7B0U, 0x83FF5AU, 0x8401ECU, 0x841906U, 0x8428D2U, 0x843038U, 0x844B7AU, 0x845390U, 0x846244U, 0x847AAEU, + 0x848C2AU, 0x8494C0U, 0x84A514U, 0x84BDFEU, 0x84C6BCU, 0x84DE56U, 0x84EF82U, 0x84F768U, 0x85028AU, 0x851A60U, + 0x852BB4U, 0x85335EU, 0x85481CU, 0x8550F6U, 0x856122U, 0x8579C8U, 0x858F4CU, 0x8597A6U, 0x85A672U, 0x85BE98U, + 0x85C5DAU, 0x85DD30U, 0x85ECE4U, 0x85F40EU, 0x860720U, 0x861FCAU, 0x862E1EU, 0x8636F4U, 0x864DB6U, 0x86555CU, + 0x866488U, 0x867C62U, 0x868AE6U, 0x86920CU, 0x86A3D8U, 0x86BB32U, 0x86C070U, 0x86D89AU, 0x86E94EU, 0x86F1A4U, + 0x870446U, 0x871CACU, 0x872D78U, 0x873592U, 0x874ED0U, 0x87563AU, 0x8767EEU, 0x877F04U, 0x878980U, 0x87916AU, + 0x87A0BEU, 0x87B854U, 0x87C316U, 0x87DBFCU, 0x87EA28U, 0x87F2C2U, 0x880FAEU, 0x881744U, 0x882690U, 0x883E7AU, + 0x884538U, 0x885DD2U, 0x886C06U, 0x8874ECU, 0x888268U, 0x889A82U, 0x88AB56U, 0x88B3BCU, 0x88C8FEU, 0x88D014U, + 0x88E1C0U, 0x88F92AU, 0x890CC8U, 0x891422U, 0x8925F6U, 0x893D1CU, 0x89465EU, 0x895EB4U, 0x896F60U, 0x89778AU, + 0x89810EU, 0x8999E4U, 0x89A830U, 0x89B0DAU, 0x89CB98U, 0x89D372U, 0x89E2A6U, 0x89FA4CU, 0x8A0962U, 0x8A1188U, + 0x8A205CU, 0x8A38B6U, 0x8A43F4U, 0x8A5B1EU, 0x8A6ACAU, 0x8A7220U, 0x8A84A4U, 0x8A9C4EU, 0x8AAD9AU, 0x8AB570U, + 0x8ACE32U, 0x8AD6D8U, 0x8AE70CU, 0x8AFFE6U, 0x8B0A04U, 0x8B12EEU, 0x8B233AU, 0x8B3BD0U, 0x8B4092U, 0x8B5878U, + 0x8B69ACU, 0x8B7146U, 0x8B87C2U, 0x8B9F28U, 0x8BAEFCU, 0x8BB616U, 0x8BCD54U, 0x8BD5BEU, 0x8BE46AU, 0x8BFC80U, + 0x8C0236U, 0x8C1ADCU, 0x8C2B08U, 0x8C33E2U, 0x8C48A0U, 0x8C504AU, 0x8C619EU, 0x8C7974U, 0x8C8FF0U, 0x8C971AU, + 0x8CA6CEU, 0x8CBE24U, 0x8CC566U, 0x8CDD8CU, 0x8CEC58U, 0x8CF4B2U, 0x8D0150U, 0x8D19BAU, 0x8D286EU, 0x8D3084U, + 0x8D4BC6U, 0x8D532CU, 0x8D62F8U, 0x8D7A12U, 0x8D8C96U, 0x8D947CU, 0x8DA5A8U, 0x8DBD42U, 0x8DC600U, 0x8DDEEAU, + 0x8DEF3EU, 0x8DF7D4U, 0x8E04FAU, 0x8E1C10U, 0x8E2DC4U, 0x8E352EU, 0x8E4E6CU, 0x8E5686U, 0x8E6752U, 0x8E7FB8U, + 0x8E893CU, 0x8E91D6U, 0x8EA002U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB40U, 0x8EEA94U, 0x8EF27EU, 0x8F079CU, 0x8F1F76U, + 0x8F2EA2U, 0x8F3648U, 0x8F4D0AU, 0x8F55E0U, 0x8F6434U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B0U, 0x8FA364U, 0x8FBB8EU, + 0x8FC0CCU, 0x8FD826U, 0x8FE9F2U, 0x8FF118U, 0x900BC0U, 0x90132AU, 0x9022FEU, 0x903A14U, 0x904156U, 0x9059BCU, + 0x906868U, 0x907082U, 0x908606U, 0x909EECU, 0x90AF38U, 0x90B7D2U, 0x90CC90U, 0x90D47AU, 0x90E5AEU, 0x90FD44U, + 0x9108A6U, 0x91104CU, 0x912198U, 0x913972U, 0x914230U, 0x915ADAU, 0x916B0EU, 0x9173E4U, 0x918560U, 0x919D8AU, + 0x91AC5EU, 0x91B4B4U, 0x91CFF6U, 0x91D71CU, 0x91E6C8U, 0x91FE22U, 0x920D0CU, 0x9215E6U, 0x922432U, 0x923CD8U, + 0x92479AU, 0x925F70U, 0x926EA4U, 0x92764EU, 0x9280CAU, 0x929820U, 0x92A9F4U, 0x92B11EU, 0x92CA5CU, 0x92D2B6U, + 0x92E362U, 0x92FB88U, 0x930E6AU, 0x931680U, 0x932754U, 0x933FBEU, 0x9344FCU, 0x935C16U, 0x936DC2U, 0x937528U, + 0x9383ACU, 0x939B46U, 0x93AA92U, 0x93B278U, 0x93C93AU, 0x93D1D0U, 0x93E004U, 0x93F8EEU, 0x940658U, 0x941EB2U, + 0x942F66U, 0x94378CU, 0x944CCEU, 0x945424U, 0x9465F0U, 0x947D1AU, 0x948B9EU, 0x949374U, 0x94A2A0U, 0x94BA4AU, + 0x94C108U, 0x94D9E2U, 0x94E836U, 0x94F0DCU, 0x95053EU, 0x951DD4U, 0x952C00U, 0x9534EAU, 0x954FA8U, 0x955742U, + 0x956696U, 0x957E7CU, 0x9588F8U, 0x959012U, 0x95A1C6U, 0x95B92CU, 0x95C26EU, 0x95DA84U, 0x95EB50U, 0x95F3BAU, + 0x960094U, 0x96187EU, 0x9629AAU, 0x963140U, 0x964A02U, 0x9652E8U, 0x96633CU, 0x967BD6U, 0x968D52U, 0x9695B8U, + 0x96A46CU, 0x96BC86U, 0x96C7C4U, 0x96DF2EU, 0x96EEFAU, 0x96F610U, 0x9703F2U, 0x971B18U, 0x972ACCU, 0x973226U, + 0x974964U, 0x97518EU, 0x97605AU, 0x9778B0U, 0x978E34U, 0x9796DEU, 0x97A70AU, 0x97BFE0U, 0x97C4A2U, 0x97DC48U, + 0x97ED9CU, 0x97F576U, 0x98081AU, 0x9810F0U, 0x982124U, 0x9839CEU, 0x98428CU, 0x985A66U, 0x986BB2U, 0x987358U, + 0x9885DCU, 0x989D36U, 0x98ACE2U, 0x98B408U, 0x98CF4AU, 0x98D7A0U, 0x98E674U, 0x98FE9EU, 0x990B7CU, 0x991396U, + 0x992242U, 0x993AA8U, 0x9941EAU, 0x995900U, 0x9968D4U, 0x99703EU, 0x9986BAU, 0x999E50U, 0x99AF84U, 0x99B76EU, + 0x99CC2CU, 0x99D4C6U, 0x99E512U, 0x99FDF8U, 0x9A0ED6U, 0x9A163CU, 0x9A27E8U, 0x9A3F02U, 0x9A4440U, 0x9A5CAAU, + 0x9A6D7EU, 0x9A7594U, 0x9A8310U, 0x9A9BFAU, 0x9AAA2EU, 0x9AB2C4U, 0x9AC986U, 0x9AD16CU, 0x9AE0B8U, 0x9AF852U, + 0x9B0DB0U, 0x9B155AU, 0x9B248EU, 0x9B3C64U, 0x9B4726U, 0x9B5FCCU, 0x9B6E18U, 0x9B76F2U, 0x9B8076U, 0x9B989CU, + 0x9BA948U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20AU, 0x9BE3DEU, 0x9BFB34U, 0x9C0582U, 0x9C1D68U, 0x9C2CBCU, 0x9C3456U, + 0x9C4F14U, 0x9C57FEU, 0x9C662AU, 0x9C7EC0U, 0x9C8844U, 0x9C90AEU, 0x9CA17AU, 0x9CB990U, 0x9CC2D2U, 0x9CDA38U, + 0x9CEBECU, 0x9CF306U, 0x9D06E4U, 0x9D1E0EU, 0x9D2FDAU, 0x9D3730U, 0x9D4C72U, 0x9D5498U, 0x9D654CU, 0x9D7DA6U, + 0x9D8B22U, 0x9D93C8U, 0x9DA21CU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95EU, 0x9DE88AU, 0x9DF060U, 0x9E034EU, 0x9E1BA4U, + 0x9E2A70U, 0x9E329AU, 0x9E49D8U, 0x9E5132U, 0x9E60E6U, 0x9E780CU, 0x9E8E88U, 0x9E9662U, 0x9EA7B6U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF4U, 0x9EED20U, 0x9EF5CAU, 0x9F0028U, 0x9F18C2U, 0x9F2916U, 0x9F31FCU, 0x9F4ABEU, 0x9F5254U, + 0x9F6380U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9504U, 0x9FA4D0U, 0x9FBC3AU, 0x9FC778U, 0x9FDF92U, 0x9FEE46U, 0x9FF6ACU, + 0xA0031CU, 0xA01BF6U, 0xA02A22U, 0xA032C8U, 0xA0498AU, 0xA05160U, 0xA060B4U, 0xA0785EU, 0xA08EDAU, 0xA09630U, + 0xA0A7E4U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA6U, 0xA0ED72U, 0xA0F598U, 0xA1007AU, 0xA11890U, 0xA12944U, 0xA131AEU, + 0xA14AECU, 0xA15206U, 0xA163D2U, 0xA17B38U, 0xA18DBCU, 0xA19556U, 0xA1A482U, 0xA1BC68U, 0xA1C72AU, 0xA1DFC0U, + 0xA1EE14U, 0xA1F6FEU, 0xA205D0U, 0xA21D3AU, 0xA22CEEU, 0xA23404U, 0xA24F46U, 0xA257ACU, 0xA26678U, 0xA27E92U, + 0xA28816U, 0xA290FCU, 0xA2A128U, 0xA2B9C2U, 0xA2C280U, 0xA2DA6AU, 0xA2EBBEU, 0xA2F354U, 0xA306B6U, 0xA31E5CU, + 0xA32F88U, 0xA33762U, 0xA34C20U, 0xA354CAU, 0xA3651EU, 0xA37DF4U, 0xA38B70U, 0xA3939AU, 0xA3A24EU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90CU, 0xA3E8D8U, 0xA3F032U, 0xA40E84U, 0xA4166EU, 0xA427BAU, 0xA43F50U, 0xA44412U, 0xA45CF8U, + 0xA46D2CU, 0xA475C6U, 0xA48342U, 0xA49BA8U, 0xA4AA7CU, 0xA4B296U, 0xA4C9D4U, 0xA4D13EU, 0xA4E0EAU, 0xA4F800U, + 0xA50DE2U, 0xA51508U, 0xA524DCU, 0xA53C36U, 0xA54774U, 0xA55F9EU, 0xA56E4AU, 0xA576A0U, 0xA58024U, 0xA598CEU, + 0xA5A91AU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D258U, 0xA5E38CU, 0xA5FB66U, 0xA60848U, 0xA610A2U, 0xA62176U, 0xA6399CU, + 0xA642DEU, 0xA65A34U, 0xA66BE0U, 0xA6730AU, 0xA6858EU, 0xA69D64U, 0xA6ACB0U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F2U, + 0xA6E626U, 0xA6FECCU, 0xA70B2EU, 0xA713C4U, 0xA72210U, 0xA73AFAU, 0xA741B8U, 0xA75952U, 0xA76886U, 0xA7706CU, + 0xA786E8U, 0xA79E02U, 0xA7AFD6U, 0xA7B73CU, 0xA7CC7EU, 0xA7D494U, 0xA7E540U, 0xA7FDAAU, 0xA800C6U, 0xA8182CU, + 0xA829F8U, 0xA83112U, 0xA84A50U, 0xA852BAU, 0xA8636EU, 0xA87B84U, 0xA88D00U, 0xA895EAU, 0xA8A43EU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7CU, 0xA8EEA8U, 0xA8F642U, 0xA903A0U, 0xA91B4AU, 0xA92A9EU, 0xA93274U, 0xA94936U, 0xA951DCU, + 0xA96008U, 0xA978E2U, 0xA98E66U, 0xA9968CU, 0xA9A758U, 0xA9BFB2U, 0xA9C4F0U, 0xA9DC1AU, 0xA9EDCEU, 0xA9F524U, + 0xAA060AU, 0xAA1EE0U, 0xAA2F34U, 0xAA37DEU, 0xAA4C9CU, 0xAA5476U, 0xAA65A2U, 0xAA7D48U, 0xAA8BCCU, 0xAA9326U, + 0xAAA2F2U, 0xAABA18U, 0xAAC15AU, 0xAAD9B0U, 0xAAE864U, 0xAAF08EU, 0xAB056CU, 0xAB1D86U, 0xAB2C52U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5710U, 0xAB66C4U, 0xAB7E2EU, 0xAB88AAU, 0xAB9040U, 0xABA194U, 0xABB97EU, 0xABC23CU, 0xABDAD6U, + 0xABEB02U, 0xABF3E8U, 0xAC0D5EU, 0xAC15B4U, 0xAC2460U, 0xAC3C8AU, 0xAC47C8U, 0xAC5F22U, 0xAC6EF6U, 0xAC761CU, + 0xAC8098U, 0xAC9872U, 0xACA9A6U, 0xACB14CU, 0xACCA0EU, 0xACD2E4U, 0xACE330U, 0xACFBDAU, 0xAD0E38U, 0xAD16D2U, + 0xAD2706U, 0xAD3FECU, 0xAD44AEU, 0xAD5C44U, 0xAD6D90U, 0xAD757AU, 0xAD83FEU, 0xAD9B14U, 0xADAAC0U, 0xADB22AU, + 0xADC968U, 0xADD182U, 0xADE056U, 0xADF8BCU, 0xAE0B92U, 0xAE1378U, 0xAE22ACU, 0xAE3A46U, 0xAE4104U, 0xAE59EEU, + 0xAE683AU, 0xAE70D0U, 0xAE8654U, 0xAE9EBEU, 0xAEAF6AU, 0xAEB780U, 0xAECCC2U, 0xAED428U, 0xAEE5FCU, 0xAEFD16U, + 0xAF08F4U, 0xAF101EU, 0xAF21CAU, 0xAF3920U, 0xAF4262U, 0xAF5A88U, 0xAF6B5CU, 0xAF73B6U, 0xAF8532U, 0xAF9DD8U, + 0xAFAC0CU, 0xAFB4E6U, 0xAFCFA4U, 0xAFD74EU, 0xAFE69AU, 0xAFFE70U, 0xB004A8U, 0xB01C42U, 0xB02D96U, 0xB0357CU, + 0xB04E3EU, 0xB056D4U, 0xB06700U, 0xB07FEAU, 0xB0896EU, 0xB09184U, 0xB0A050U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB12U, + 0xB0EAC6U, 0xB0F22CU, 0xB107CEU, 0xB11F24U, 0xB12EF0U, 0xB1361AU, 0xB14D58U, 0xB155B2U, 0xB16466U, 0xB17C8CU, + 0xB18A08U, 0xB192E2U, 0xB1A336U, 0xB1BBDCU, 0xB1C09EU, 0xB1D874U, 0xB1E9A0U, 0xB1F14AU, 0xB20264U, 0xB21A8EU, + 0xB22B5AU, 0xB233B0U, 0xB248F2U, 0xB25018U, 0xB261CCU, 0xB27926U, 0xB28FA2U, 0xB29748U, 0xB2A69CU, 0xB2BE76U, + 0xB2C534U, 0xB2DDDEU, 0xB2EC0AU, 0xB2F4E0U, 0xB30102U, 0xB319E8U, 0xB3283CU, 0xB330D6U, 0xB34B94U, 0xB3537EU, + 0xB362AAU, 0xB37A40U, 0xB38CC4U, 0xB3942EU, 0xB3A5FAU, 0xB3BD10U, 0xB3C652U, 0xB3DEB8U, 0xB3EF6CU, 0xB3F786U, + 0xB40930U, 0xB411DAU, 0xB4200EU, 0xB438E4U, 0xB443A6U, 0xB45B4CU, 0xB46A98U, 0xB47272U, 0xB484F6U, 0xB49C1CU, + 0xB4ADC8U, 0xB4B522U, 0xB4CE60U, 0xB4D68AU, 0xB4E75EU, 0xB4FFB4U, 0xB50A56U, 0xB512BCU, 0xB52368U, 0xB53B82U, + 0xB540C0U, 0xB5582AU, 0xB569FEU, 0xB57114U, 0xB58790U, 0xB59F7AU, 0xB5AEAEU, 0xB5B644U, 0xB5CD06U, 0xB5D5ECU, + 0xB5E438U, 0xB5FCD2U, 0xB60FFCU, 0xB61716U, 0xB626C2U, 0xB63E28U, 0xB6456AU, 0xB65D80U, 0xB66C54U, 0xB674BEU, + 0xB6823AU, 0xB69AD0U, 0xB6AB04U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D046U, 0xB6E192U, 0xB6F978U, 0xB70C9AU, 0xB71470U, + 0xB725A4U, 0xB73D4EU, 0xB7460CU, 0xB75EE6U, 0xB76F32U, 0xB777D8U, 0xB7815CU, 0xB799B6U, 0xB7A862U, 0xB7B088U, + 0xB7CBCAU, 0xB7D320U, 0xB7E2F4U, 0xB7FA1EU, 0xB80772U, 0xB81F98U, 0xB82E4CU, 0xB836A6U, 0xB84DE4U, 0xB8550EU, + 0xB864DAU, 0xB87C30U, 0xB88AB4U, 0xB8925EU, 0xB8A38AU, 0xB8BB60U, 0xB8C022U, 0xB8D8C8U, 0xB8E91CU, 0xB8F1F6U, + 0xB90414U, 0xB91CFEU, 0xB92D2AU, 0xB935C0U, 0xB94E82U, 0xB95668U, 0xB967BCU, 0xB97F56U, 0xB989D2U, 0xB99138U, + 0xB9A0ECU, 0xB9B806U, 0xB9C344U, 0xB9DBAEU, 0xB9EA7AU, 0xB9F290U, 0xBA01BEU, 0xBA1954U, 0xBA2880U, 0xBA306AU, + 0xBA4B28U, 0xBA53C2U, 0xBA6216U, 0xBA7AFCU, 0xBA8C78U, 0xBA9492U, 0xBAA546U, 0xBABDACU, 0xBAC6EEU, 0xBADE04U, + 0xBAEFD0U, 0xBAF73AU, 0xBB02D8U, 0xBB1A32U, 0xBB2BE6U, 0xBB330CU, 0xBB484EU, 0xBB50A4U, 0xBB6170U, 0xBB799AU, + 0xBB8F1EU, 0xBB97F4U, 0xBBA620U, 0xBBBECAU, 0xBBC588U, 0xBBDD62U, 0xBBECB6U, 0xBBF45CU, 0xBC0AEAU, 0xBC1200U, + 0xBC23D4U, 0xBC3B3EU, 0xBC407CU, 0xBC5896U, 0xBC6942U, 0xBC71A8U, 0xBC872CU, 0xBC9FC6U, 0xBCAE12U, 0xBCB6F8U, + 0xBCCDBAU, 0xBCD550U, 0xBCE484U, 0xBCFC6EU, 0xBD098CU, 0xBD1166U, 0xBD20B2U, 0xBD3858U, 0xBD431AU, 0xBD5BF0U, + 0xBD6A24U, 0xBD72CEU, 0xBD844AU, 0xBD9CA0U, 0xBDAD74U, 0xBDB59EU, 0xBDCEDCU, 0xBDD636U, 0xBDE7E2U, 0xBDFF08U, + 0xBE0C26U, 0xBE14CCU, 0xBE2518U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5AU, 0xBE6F8EU, 0xBE7764U, 0xBE81E0U, 0xBE990AU, + 0xBEA8DEU, 0xBEB034U, 0xBECB76U, 0xBED39CU, 0xBEE248U, 0xBEFAA2U, 0xBF0F40U, 0xBF17AAU, 0xBF267EU, 0xBF3E94U, + 0xBF45D6U, 0xBF5D3CU, 0xBF6CE8U, 0xBF7402U, 0xBF8286U, 0xBF9A6CU, 0xBFABB8U, 0xBFB352U, 0xBFC810U, 0xBFD0FAU, + 0xBFE12EU, 0xBFF9C4U, 0xC00A4EU, 0xC012A4U, 0xC02370U, 0xC03B9AU, 0xC040D8U, 0xC05832U, 0xC069E6U, 0xC0710CU, + 0xC08788U, 0xC09F62U, 0xC0AEB6U, 0xC0B65CU, 0xC0CD1EU, 0xC0D5F4U, 0xC0E420U, 0xC0FCCAU, 0xC10928U, 0xC111C2U, + 0xC12016U, 0xC138FCU, 0xC143BEU, 0xC15B54U, 0xC16A80U, 0xC1726AU, 0xC184EEU, 0xC19C04U, 0xC1ADD0U, 0xC1B53AU, + 0xC1CE78U, 0xC1D692U, 0xC1E746U, 0xC1FFACU, 0xC20C82U, 0xC21468U, 0xC225BCU, 0xC23D56U, 0xC24614U, 0xC25EFEU, + 0xC26F2AU, 0xC277C0U, 0xC28144U, 0xC299AEU, 0xC2A87AU, 0xC2B090U, 0xC2CBD2U, 0xC2D338U, 0xC2E2ECU, 0xC2FA06U, + 0xC30FE4U, 0xC3170EU, 0xC326DAU, 0xC33E30U, 0xC34572U, 0xC35D98U, 0xC36C4CU, 0xC374A6U, 0xC38222U, 0xC39AC8U, + 0xC3AB1CU, 0xC3B3F6U, 0xC3C8B4U, 0xC3D05EU, 0xC3E18AU, 0xC3F960U, 0xC407D6U, 0xC41F3CU, 0xC42EE8U, 0xC43602U, + 0xC44D40U, 0xC455AAU, 0xC4647EU, 0xC47C94U, 0xC48A10U, 0xC492FAU, 0xC4A32EU, 0xC4BBC4U, 0xC4C086U, 0xC4D86CU, + 0xC4E9B8U, 0xC4F152U, 0xC504B0U, 0xC51C5AU, 0xC52D8EU, 0xC53564U, 0xC54E26U, 0xC556CCU, 0xC56718U, 0xC57FF2U, + 0xC58976U, 0xC5919CU, 0xC5A048U, 0xC5B8A2U, 0xC5C3E0U, 0xC5DB0AU, 0xC5EADEU, 0xC5F234U, 0xC6011AU, 0xC619F0U, + 0xC62824U, 0xC630CEU, 0xC64B8CU, 0xC65366U, 0xC662B2U, 0xC67A58U, 0xC68CDCU, 0xC69436U, 0xC6A5E2U, 0xC6BD08U, + 0xC6C64AU, 0xC6DEA0U, 0xC6EF74U, 0xC6F79EU, 0xC7027CU, 0xC71A96U, 0xC72B42U, 0xC733A8U, 0xC748EAU, 0xC75000U, + 0xC761D4U, 0xC7793EU, 0xC78FBAU, 0xC79750U, 0xC7A684U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC6U, 0xC7EC12U, 0xC7F4F8U, + 0xC80994U, 0xC8117EU, 0xC820AAU, 0xC83840U, 0xC84302U, 0xC85BE8U, 0xC86A3CU, 0xC872D6U, 0xC88452U, 0xC89CB8U, + 0xC8AD6CU, 0xC8B586U, 0xC8CEC4U, 0xC8D62EU, 0xC8E7FAU, 0xC8FF10U, 0xC90AF2U, 0xC91218U, 0xC923CCU, 0xC93B26U, + 0xC94064U, 0xC9588EU, 0xC9695AU, 0xC971B0U, 0xC98734U, 0xC99FDEU, 0xC9AE0AU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D548U, + 0xC9E49CU, 0xC9FC76U, 0xCA0F58U, 0xCA17B2U, 0xCA2666U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D24U, 0xCA6CF0U, 0xCA741AU, + 0xCA829EU, 0xCA9A74U, 0xCAABA0U, 0xCAB34AU, 0xCAC808U, 0xCAD0E2U, 0xCAE136U, 0xCAF9DCU, 0xCB0C3EU, 0xCB14D4U, + 0xCB2500U, 0xCB3DEAU, 0xCB46A8U, 0xCB5E42U, 0xCB6F96U, 0xCB777CU, 0xCB81F8U, 0xCB9912U, 0xCBA8C6U, 0xCBB02CU, + 0xCBCB6EU, 0xCBD384U, 0xCBE250U, 0xCBFABAU, 0xCC040CU, 0xCC1CE6U, 0xCC2D32U, 0xCC35D8U, 0xCC4E9AU, 0xCC5670U, + 0xCC67A4U, 0xCC7F4EU, 0xCC89CAU, 0xCC9120U, 0xCCA0F4U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB6U, 0xCCEA62U, 0xCCF288U, + 0xCD076AU, 0xCD1F80U, 0xCD2E54U, 0xCD36BEU, 0xCD4DFCU, 0xCD5516U, 0xCD64C2U, 0xCD7C28U, 0xCD8AACU, 0xCD9246U, + 0xCDA392U, 0xCDBB78U, 0xCDC03AU, 0xCDD8D0U, 0xCDE904U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2AU, 0xCE2BFEU, 0xCE3314U, + 0xCE4856U, 0xCE50BCU, 0xCE6168U, 0xCE7982U, 0xCE8F06U, 0xCE97ECU, 0xCEA638U, 0xCEBED2U, 0xCEC590U, 0xCEDD7AU, + 0xCEECAEU, 0xCEF444U, 0xCF01A6U, 0xCF194CU, 0xCF2898U, 0xCF3072U, 0xCF4B30U, 0xCF53DAU, 0xCF620EU, 0xCF7AE4U, + 0xCF8C60U, 0xCF948AU, 0xCFA55EU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1CU, 0xCFEFC8U, 0xCFF722U, 0xD00DFAU, 0xD01510U, + 0xD024C4U, 0xD03C2EU, 0xD0476CU, 0xD05F86U, 0xD06E52U, 0xD076B8U, 0xD0803CU, 0xD098D6U, 0xD0A902U, 0xD0B1E8U, + 0xD0CAAAU, 0xD0D240U, 0xD0E394U, 0xD0FB7EU, 0xD10E9CU, 0xD11676U, 0xD127A2U, 0xD13F48U, 0xD1440AU, 0xD15CE0U, + 0xD16D34U, 0xD175DEU, 0xD1835AU, 0xD19BB0U, 0xD1AA64U, 0xD1B28EU, 0xD1C9CCU, 0xD1D126U, 0xD1E0F2U, 0xD1F818U, + 0xD20B36U, 0xD213DCU, 0xD22208U, 0xD23AE2U, 0xD241A0U, 0xD2594AU, 0xD2689EU, 0xD27074U, 0xD286F0U, 0xD29E1AU, + 0xD2AFCEU, 0xD2B724U, 0xD2CC66U, 0xD2D48CU, 0xD2E558U, 0xD2FDB2U, 0xD30850U, 0xD310BAU, 0xD3216EU, 0xD33984U, + 0xD342C6U, 0xD35A2CU, 0xD36BF8U, 0xD37312U, 0xD38596U, 0xD39D7CU, 0xD3ACA8U, 0xD3B442U, 0xD3CF00U, 0xD3D7EAU, + 0xD3E63EU, 0xD3FED4U, 0xD40062U, 0xD41888U, 0xD4295CU, 0xD431B6U, 0xD44AF4U, 0xD4521EU, 0xD463CAU, 0xD47B20U, + 0xD48DA4U, 0xD4954EU, 0xD4A49AU, 0xD4BC70U, 0xD4C732U, 0xD4DFD8U, 0xD4EE0CU, 0xD4F6E6U, 0xD50304U, 0xD51BEEU, + 0xD52A3AU, 0xD532D0U, 0xD54992U, 0xD55178U, 0xD560ACU, 0xD57846U, 0xD58EC2U, 0xD59628U, 0xD5A7FCU, 0xD5BF16U, + 0xD5C454U, 0xD5DCBEU, 0xD5ED6AU, 0xD5F580U, 0xD606AEU, 0xD61E44U, 0xD62F90U, 0xD6377AU, 0xD64C38U, 0xD654D2U, + 0xD66506U, 0xD67DECU, 0xD68B68U, 0xD69382U, 0xD6A256U, 0xD6BABCU, 0xD6C1FEU, 0xD6D914U, 0xD6E8C0U, 0xD6F02AU, + 0xD705C8U, 0xD71D22U, 0xD72CF6U, 0xD7341CU, 0xD74F5EU, 0xD757B4U, 0xD76660U, 0xD77E8AU, 0xD7880EU, 0xD790E4U, + 0xD7A130U, 0xD7B9DAU, 0xD7C298U, 0xD7DA72U, 0xD7EBA6U, 0xD7F34CU, 0xD80E20U, 0xD816CAU, 0xD8271EU, 0xD83FF4U, + 0xD844B6U, 0xD85C5CU, 0xD86D88U, 0xD87562U, 0xD883E6U, 0xD89B0CU, 0xD8AAD8U, 0xD8B232U, 0xD8C970U, 0xD8D19AU, + 0xD8E04EU, 0xD8F8A4U, 0xD90D46U, 0xD915ACU, 0xD92478U, 0xD93C92U, 0xD947D0U, 0xD95F3AU, 0xD96EEEU, 0xD97604U, + 0xD98080U, 0xD9986AU, 0xD9A9BEU, 0xD9B154U, 0xD9CA16U, 0xD9D2FCU, 0xD9E328U, 0xD9FBC2U, 0xDA08ECU, 0xDA1006U, + 0xDA21D2U, 0xDA3938U, 0xDA427AU, 0xDA5A90U, 0xDA6B44U, 0xDA73AEU, 0xDA852AU, 0xDA9DC0U, 0xDAAC14U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD756U, 0xDAE682U, 0xDAFE68U, 0xDB0B8AU, 0xDB1360U, 0xDB22B4U, 0xDB3A5EU, 0xDB411CU, 0xDB59F6U, + 0xDB6822U, 0xDB70C8U, 0xDB864CU, 0xDB9EA6U, 0xDBAF72U, 0xDBB798U, 0xDBCCDAU, 0xDBD430U, 0xDBE5E4U, 0xDBFD0EU, + 0xDC03B8U, 0xDC1B52U, 0xDC2A86U, 0xDC326CU, 0xDC492EU, 0xDC51C4U, 0xDC6010U, 0xDC78FAU, 0xDC8E7EU, 0xDC9694U, + 0xDCA740U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC02U, 0xDCEDD6U, 0xDCF53CU, 0xDD00DEU, 0xDD1834U, 0xDD29E0U, 0xDD310AU, + 0xDD4A48U, 0xDD52A2U, 0xDD6376U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F2U, 0xDDA426U, 0xDDBCCCU, 0xDDC78EU, 0xDDDF64U, + 0xDDEEB0U, 0xDDF65AU, 0xDE0574U, 0xDE1D9EU, 0xDE2C4AU, 0xDE34A0U, 0xDE4FE2U, 0xDE5708U, 0xDE66DCU, 0xDE7E36U, + 0xDE88B2U, 0xDE9058U, 0xDEA18CU, 0xDEB966U, 0xDEC224U, 0xDEDACEU, 0xDEEB1AU, 0xDEF3F0U, 0xDF0612U, 0xDF1EF8U, + 0xDF2F2CU, 0xDF37C6U, 0xDF4C84U, 0xDF546EU, 0xDF65BAU, 0xDF7D50U, 0xDF8BD4U, 0xDF933EU, 0xDFA2EAU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A8U, 0xDFE87CU, 0xDFF096U, 0xE00526U, 0xE01DCCU, 0xE02C18U, 0xE034F2U, 0xE04FB0U, 0xE0575AU, + 0xE0668EU, 0xE07E64U, 0xE088E0U, 0xE0900AU, 0xE0A1DEU, 0xE0B934U, 0xE0C276U, 0xE0DA9CU, 0xE0EB48U, 0xE0F3A2U, + 0xE10640U, 0xE11EAAU, 0xE12F7EU, 0xE13794U, 0xE14CD6U, 0xE1543CU, 0xE165E8U, 0xE17D02U, 0xE18B86U, 0xE1936CU, + 0xE1A2B8U, 0xE1BA52U, 0xE1C110U, 0xE1D9FAU, 0xE1E82EU, 0xE1F0C4U, 0xE203EAU, 0xE21B00U, 0xE22AD4U, 0xE2323EU, + 0xE2497CU, 0xE25196U, 0xE26042U, 0xE278A8U, 0xE28E2CU, 0xE296C6U, 0xE2A712U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC50U, + 0xE2ED84U, 0xE2F56EU, 0xE3008CU, 0xE31866U, 0xE329B2U, 0xE33158U, 0xE34A1AU, 0xE352F0U, 0xE36324U, 0xE37BCEU, + 0xE38D4AU, 0xE395A0U, 0xE3A474U, 0xE3BC9EU, 0xE3C7DCU, 0xE3DF36U, 0xE3EEE2U, 0xE3F608U, 0xE408BEU, 0xE41054U, + 0xE42180U, 0xE4396AU, 0xE44228U, 0xE45AC2U, 0xE46B16U, 0xE473FCU, 0xE48578U, 0xE49D92U, 0xE4AC46U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D704U, 0xE4E6D0U, 0xE4FE3AU, 0xE50BD8U, 0xE51332U, 0xE522E6U, 0xE53A0CU, 0xE5414EU, 0xE559A4U, + 0xE56870U, 0xE5709AU, 0xE5861EU, 0xE59EF4U, 0xE5AF20U, 0xE5B7CAU, 0xE5CC88U, 0xE5D462U, 0xE5E5B6U, 0xE5FD5CU, + 0xE60E72U, 0xE61698U, 0xE6274CU, 0xE63FA6U, 0xE644E4U, 0xE65C0EU, 0xE66DDAU, 0xE67530U, 0xE683B4U, 0xE69B5EU, + 0xE6AA8AU, 0xE6B260U, 0xE6C922U, 0xE6D1C8U, 0xE6E01CU, 0xE6F8F6U, 0xE70D14U, 0xE715FEU, 0xE7242AU, 0xE73CC0U, + 0xE74782U, 0xE75F68U, 0xE76EBCU, 0xE77656U, 0xE780D2U, 0xE79838U, 0xE7A9ECU, 0xE7B106U, 0xE7CA44U, 0xE7D2AEU, + 0xE7E37AU, 0xE7FB90U, 0xE806FCU, 0xE81E16U, 0xE82FC2U, 0xE83728U, 0xE84C6AU, 0xE85480U, 0xE86554U, 0xE87DBEU, + 0xE88B3AU, 0xE893D0U, 0xE8A204U, 0xE8BAEEU, 0xE8C1ACU, 0xE8D946U, 0xE8E892U, 0xE8F078U, 0xE9059AU, 0xE91D70U, + 0xE92CA4U, 0xE9344EU, 0xE94F0CU, 0xE957E6U, 0xE96632U, 0xE97ED8U, 0xE9885CU, 0xE990B6U, 0xE9A162U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA20U, 0xE9EBF4U, 0xE9F31EU, 0xEA0030U, 0xEA18DAU, 0xEA290EU, 0xEA31E4U, 0xEA4AA6U, 0xEA524CU, + 0xEA6398U, 0xEA7B72U, 0xEA8DF6U, 0xEA951CU, 0xEAA4C8U, 0xEABC22U, 0xEAC760U, 0xEADF8AU, 0xEAEE5EU, 0xEAF6B4U, + 0xEB0356U, 0xEB1BBCU, 0xEB2A68U, 0xEB3282U, 0xEB49C0U, 0xEB512AU, 0xEB60FEU, 0xEB7814U, 0xEB8E90U, 0xEB967AU, + 0xEBA7AEU, 0xEBBF44U, 0xEBC406U, 0xEBDCECU, 0xEBED38U, 0xEBF5D2U, 0xEC0B64U, 0xEC138EU, 0xEC225AU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5918U, 0xEC68CCU, 0xEC7026U, 0xEC86A2U, 0xEC9E48U, 0xECAF9CU, 0xECB776U, 0xECCC34U, 0xECD4DEU, + 0xECE50AU, 0xECFDE0U, 0xED0802U, 0xED10E8U, 0xED213CU, 0xED39D6U, 0xED4294U, 0xED5A7EU, 0xED6BAAU, 0xED7340U, + 0xED85C4U, 0xED9D2EU, 0xEDACFAU, 0xEDB410U, 0xEDCF52U, 0xEDD7B8U, 0xEDE66CU, 0xEDFE86U, 0xEE0DA8U, 0xEE1542U, + 0xEE2496U, 0xEE3C7CU, 0xEE473EU, 0xEE5FD4U, 0xEE6E00U, 0xEE76EAU, 0xEE806EU, 0xEE9884U, 0xEEA950U, 0xEEB1BAU, + 0xEECAF8U, 0xEED212U, 0xEEE3C6U, 0xEEFB2CU, 0xEF0ECEU, 0xEF1624U, 0xEF27F0U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB2U, + 0xEF6D66U, 0xEF758CU, 0xEF8308U, 0xEF9BE2U, 0xEFAA36U, 0xEFB2DCU, 0xEFC99EU, 0xEFD174U, 0xEFE0A0U, 0xEFF84AU, + 0xF00292U, 0xF01A78U, 0xF02BACU, 0xF03346U, 0xF04804U, 0xF050EEU, 0xF0613AU, 0xF079D0U, 0xF08F54U, 0xF097BEU, + 0xF0A66AU, 0xF0BE80U, 0xF0C5C2U, 0xF0DD28U, 0xF0ECFCU, 0xF0F416U, 0xF101F4U, 0xF1191EU, 0xF128CAU, 0xF13020U, + 0xF14B62U, 0xF15388U, 0xF1625CU, 0xF17AB6U, 0xF18C32U, 0xF194D8U, 0xF1A50CU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4EU, + 0xF1EF9AU, 0xF1F770U, 0xF2045EU, 0xF21CB4U, 0xF22D60U, 0xF2358AU, 0xF24EC8U, 0xF25622U, 0xF267F6U, 0xF27F1CU, + 0xF28998U, 0xF29172U, 0xF2A0A6U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE4U, 0xF2EA30U, 0xF2F2DAU, 0xF30738U, 0xF31FD2U, + 0xF32E06U, 0xF336ECU, 0xF34DAEU, 0xF35544U, 0xF36490U, 0xF37C7AU, 0xF38AFEU, 0xF39214U, 0xF3A3C0U, 0xF3BB2AU, + 0xF3C068U, 0xF3D882U, 0xF3E956U, 0xF3F1BCU, 0xF40F0AU, 0xF417E0U, 0xF42634U, 0xF43EDEU, 0xF4459CU, 0xF45D76U, + 0xF46CA2U, 0xF47448U, 0xF482CCU, 0xF49A26U, 0xF4ABF2U, 0xF4B318U, 0xF4C85AU, 0xF4D0B0U, 0xF4E164U, 0xF4F98EU, + 0xF50C6CU, 0xF51486U, 0xF52552U, 0xF53DB8U, 0xF546FAU, 0xF55E10U, 0xF56FC4U, 0xF5772EU, 0xF581AAU, 0xF59940U, + 0xF5A894U, 0xF5B07EU, 0xF5CB3CU, 0xF5D3D6U, 0xF5E202U, 0xF5FAE8U, 0xF609C6U, 0xF6112CU, 0xF620F8U, 0xF63812U, + 0xF64350U, 0xF65BBAU, 0xF66A6EU, 0xF67284U, 0xF68400U, 0xF69CEAU, 0xF6AD3EU, 0xF6B5D4U, 0xF6CE96U, 0xF6D67CU, + 0xF6E7A8U, 0xF6FF42U, 0xF70AA0U, 0xF7124AU, 0xF7239EU, 0xF73B74U, 0xF74036U, 0xF758DCU, 0xF76908U, 0xF771E2U, + 0xF78766U, 0xF79F8CU, 0xF7AE58U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51AU, 0xF7E4CEU, 0xF7FC24U, 0xF80148U, 0xF819A2U, + 0xF82876U, 0xF8309CU, 0xF84BDEU, 0xF85334U, 0xF862E0U, 0xF87A0AU, 0xF88C8EU, 0xF89464U, 0xF8A5B0U, 0xF8BD5AU, + 0xF8C618U, 0xF8DEF2U, 0xF8EF26U, 0xF8F7CCU, 0xF9022EU, 0xF91AC4U, 0xF92B10U, 0xF933FAU, 0xF948B8U, 0xF95052U, + 0xF96186U, 0xF9796CU, 0xF98FE8U, 0xF99702U, 0xF9A6D6U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD94U, 0xF9EC40U, 0xF9F4AAU, + 0xFA0784U, 0xFA1F6EU, 0xFA2EBAU, 0xFA3650U, 0xFA4D12U, 0xFA55F8U, 0xFA642CU, 0xFA7CC6U, 0xFA8A42U, 0xFA92A8U, + 0xFAA37CU, 0xFABB96U, 0xFAC0D4U, 0xFAD83EU, 0xFAE9EAU, 0xFAF100U, 0xFB04E2U, 0xFB1C08U, 0xFB2DDCU, 0xFB3536U, + 0xFB4E74U, 0xFB569EU, 0xFB674AU, 0xFB7FA0U, 0xFB8924U, 0xFB91CEU, 0xFBA01AU, 0xFBB8F0U, 0xFBC3B2U, 0xFBDB58U, + 0xFBEA8CU, 0xFBF266U, 0xFC0CD0U, 0xFC143AU, 0xFC25EEU, 0xFC3D04U, 0xFC4646U, 0xFC5EACU, 0xFC6F78U, 0xFC7792U, + 0xFC8116U, 0xFC99FCU, 0xFCA828U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36AU, 0xFCE2BEU, 0xFCFA54U, 0xFD0FB6U, 0xFD175CU, + 0xFD2688U, 0xFD3E62U, 0xFD4520U, 0xFD5DCAU, 0xFD6C1EU, 0xFD74F4U, 0xFD8270U, 0xFD9A9AU, 0xFDAB4EU, 0xFDB3A4U, + 0xFDC8E6U, 0xFDD00CU, 0xFDE1D8U, 0xFDF932U, 0xFE0A1CU, 0xFE12F6U, 0xFE2322U, 0xFE3BC8U, 0xFE408AU, 0xFE5860U, + 0xFE69B4U, 0xFE715EU, 0xFE87DAU, 0xFE9F30U, 0xFEAEE4U, 0xFEB60EU, 0xFECD4CU, 0xFED5A6U, 0xFEE472U, 0xFEFC98U, + 0xFF097AU, 0xFF1190U, 0xFF2044U, 0xFF38AEU, 0xFF43ECU, 0xFF5B06U, 0xFF6AD2U, 0xFF7238U, 0xFF84BCU, 0xFF9C56U, + 0xFFAD82U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C0U, 0xFFE714U, 0xFFFFFEU +}; + +static const unsigned int ENCODING_TABLE_24128[] = +{ + 0x000000U, 0x0018EBU, 0x00293EU, 0x0031D5U, 0x004A97U, 0x00527CU, 0x0063A9U, 0x007B42U, 0x008DC6U, 0x00952DU, + 0x00A4F8U, 0x00BC13U, 0x00C751U, 0x00DFBAU, 0x00EE6FU, 0x00F684U, 0x010367U, 0x011B8CU, 0x012A59U, 0x0132B2U, + 0x0149F0U, 0x01511BU, 0x0160CEU, 0x017825U, 0x018EA1U, 0x01964AU, 0x01A79FU, 0x01BF74U, 0x01C436U, 0x01DCDDU, + 0x01ED08U, 0x01F5E3U, 0x0206CDU, 0x021E26U, 0x022FF3U, 0x023718U, 0x024C5AU, 0x0254B1U, 0x026564U, 0x027D8FU, + 0x028B0BU, 0x0293E0U, 0x02A235U, 0x02BADEU, 0x02C19CU, 0x02D977U, 0x02E8A2U, 0x02F049U, 0x0305AAU, 0x031D41U, + 0x032C94U, 0x03347FU, 0x034F3DU, 0x0357D6U, 0x036603U, 0x037EE8U, 0x03886CU, 0x039087U, 0x03A152U, 0x03B9B9U, + 0x03C2FBU, 0x03DA10U, 0x03EBC5U, 0x03F32EU, 0x040D99U, 0x041572U, 0x0424A7U, 0x043C4CU, 0x04470EU, 0x045FE5U, + 0x046E30U, 0x0476DBU, 0x04805FU, 0x0498B4U, 0x04A961U, 0x04B18AU, 0x04CAC8U, 0x04D223U, 0x04E3F6U, 0x04FB1DU, + 0x050EFEU, 0x051615U, 0x0527C0U, 0x053F2BU, 0x054469U, 0x055C82U, 0x056D57U, 0x0575BCU, 0x058338U, 0x059BD3U, + 0x05AA06U, 0x05B2EDU, 0x05C9AFU, 0x05D144U, 0x05E091U, 0x05F87AU, 0x060B54U, 0x0613BFU, 0x06226AU, 0x063A81U, + 0x0641C3U, 0x065928U, 0x0668FDU, 0x067016U, 0x068692U, 0x069E79U, 0x06AFACU, 0x06B747U, 0x06CC05U, 0x06D4EEU, + 0x06E53BU, 0x06FDD0U, 0x070833U, 0x0710D8U, 0x07210DU, 0x0739E6U, 0x0742A4U, 0x075A4FU, 0x076B9AU, 0x077371U, + 0x0785F5U, 0x079D1EU, 0x07ACCBU, 0x07B420U, 0x07CF62U, 0x07D789U, 0x07E65CU, 0x07FEB7U, 0x0803DAU, 0x081B31U, + 0x082AE4U, 0x08320FU, 0x08494DU, 0x0851A6U, 0x086073U, 0x087898U, 0x088E1CU, 0x0896F7U, 0x08A722U, 0x08BFC9U, + 0x08C48BU, 0x08DC60U, 0x08EDB5U, 0x08F55EU, 0x0900BDU, 0x091856U, 0x092983U, 0x093168U, 0x094A2AU, 0x0952C1U, + 0x096314U, 0x097BFFU, 0x098D7BU, 0x099590U, 0x09A445U, 0x09BCAEU, 0x09C7ECU, 0x09DF07U, 0x09EED2U, 0x09F639U, + 0x0A0517U, 0x0A1DFCU, 0x0A2C29U, 0x0A34C2U, 0x0A4F80U, 0x0A576BU, 0x0A66BEU, 0x0A7E55U, 0x0A88D1U, 0x0A903AU, + 0x0AA1EFU, 0x0AB904U, 0x0AC246U, 0x0ADAADU, 0x0AEB78U, 0x0AF393U, 0x0B0670U, 0x0B1E9BU, 0x0B2F4EU, 0x0B37A5U, + 0x0B4CE7U, 0x0B540CU, 0x0B65D9U, 0x0B7D32U, 0x0B8BB6U, 0x0B935DU, 0x0BA288U, 0x0BBA63U, 0x0BC121U, 0x0BD9CAU, + 0x0BE81FU, 0x0BF0F4U, 0x0C0E43U, 0x0C16A8U, 0x0C277DU, 0x0C3F96U, 0x0C44D4U, 0x0C5C3FU, 0x0C6DEAU, 0x0C7501U, + 0x0C8385U, 0x0C9B6EU, 0x0CAABBU, 0x0CB250U, 0x0CC912U, 0x0CD1F9U, 0x0CE02CU, 0x0CF8C7U, 0x0D0D24U, 0x0D15CFU, + 0x0D241AU, 0x0D3CF1U, 0x0D47B3U, 0x0D5F58U, 0x0D6E8DU, 0x0D7666U, 0x0D80E2U, 0x0D9809U, 0x0DA9DCU, 0x0DB137U, + 0x0DCA75U, 0x0DD29EU, 0x0DE34BU, 0x0DFBA0U, 0x0E088EU, 0x0E1065U, 0x0E21B0U, 0x0E395BU, 0x0E4219U, 0x0E5AF2U, + 0x0E6B27U, 0x0E73CCU, 0x0E8548U, 0x0E9DA3U, 0x0EAC76U, 0x0EB49DU, 0x0ECFDFU, 0x0ED734U, 0x0EE6E1U, 0x0EFE0AU, + 0x0F0BE9U, 0x0F1302U, 0x0F22D7U, 0x0F3A3CU, 0x0F417EU, 0x0F5995U, 0x0F6840U, 0x0F70ABU, 0x0F862FU, 0x0F9EC4U, + 0x0FAF11U, 0x0FB7FAU, 0x0FCCB8U, 0x0FD453U, 0x0FE586U, 0x0FFD6DU, 0x1007B4U, 0x101F5FU, 0x102E8AU, 0x103661U, + 0x104D23U, 0x1055C8U, 0x10641DU, 0x107CF6U, 0x108A72U, 0x109299U, 0x10A34CU, 0x10BBA7U, 0x10C0E5U, 0x10D80EU, + 0x10E9DBU, 0x10F130U, 0x1104D3U, 0x111C38U, 0x112DEDU, 0x113506U, 0x114E44U, 0x1156AFU, 0x11677AU, 0x117F91U, + 0x118915U, 0x1191FEU, 0x11A02BU, 0x11B8C0U, 0x11C382U, 0x11DB69U, 0x11EABCU, 0x11F257U, 0x120179U, 0x121992U, + 0x122847U, 0x1230ACU, 0x124BEEU, 0x125305U, 0x1262D0U, 0x127A3BU, 0x128CBFU, 0x129454U, 0x12A581U, 0x12BD6AU, + 0x12C628U, 0x12DEC3U, 0x12EF16U, 0x12F7FDU, 0x13021EU, 0x131AF5U, 0x132B20U, 0x1333CBU, 0x134889U, 0x135062U, + 0x1361B7U, 0x13795CU, 0x138FD8U, 0x139733U, 0x13A6E6U, 0x13BE0DU, 0x13C54FU, 0x13DDA4U, 0x13EC71U, 0x13F49AU, + 0x140A2DU, 0x1412C6U, 0x142313U, 0x143BF8U, 0x1440BAU, 0x145851U, 0x146984U, 0x14716FU, 0x1487EBU, 0x149F00U, + 0x14AED5U, 0x14B63EU, 0x14CD7CU, 0x14D597U, 0x14E442U, 0x14FCA9U, 0x15094AU, 0x1511A1U, 0x152074U, 0x15389FU, + 0x1543DDU, 0x155B36U, 0x156AE3U, 0x157208U, 0x15848CU, 0x159C67U, 0x15ADB2U, 0x15B559U, 0x15CE1BU, 0x15D6F0U, + 0x15E725U, 0x15FFCEU, 0x160CE0U, 0x16140BU, 0x1625DEU, 0x163D35U, 0x164677U, 0x165E9CU, 0x166F49U, 0x1677A2U, + 0x168126U, 0x1699CDU, 0x16A818U, 0x16B0F3U, 0x16CBB1U, 0x16D35AU, 0x16E28FU, 0x16FA64U, 0x170F87U, 0x17176CU, + 0x1726B9U, 0x173E52U, 0x174510U, 0x175DFBU, 0x176C2EU, 0x1774C5U, 0x178241U, 0x179AAAU, 0x17AB7FU, 0x17B394U, + 0x17C8D6U, 0x17D03DU, 0x17E1E8U, 0x17F903U, 0x18046EU, 0x181C85U, 0x182D50U, 0x1835BBU, 0x184EF9U, 0x185612U, + 0x1867C7U, 0x187F2CU, 0x1889A8U, 0x189143U, 0x18A096U, 0x18B87DU, 0x18C33FU, 0x18DBD4U, 0x18EA01U, 0x18F2EAU, + 0x190709U, 0x191FE2U, 0x192E37U, 0x1936DCU, 0x194D9EU, 0x195575U, 0x1964A0U, 0x197C4BU, 0x198ACFU, 0x199224U, + 0x19A3F1U, 0x19BB1AU, 0x19C058U, 0x19D8B3U, 0x19E966U, 0x19F18DU, 0x1A02A3U, 0x1A1A48U, 0x1A2B9DU, 0x1A3376U, + 0x1A4834U, 0x1A50DFU, 0x1A610AU, 0x1A79E1U, 0x1A8F65U, 0x1A978EU, 0x1AA65BU, 0x1ABEB0U, 0x1AC5F2U, 0x1ADD19U, + 0x1AECCCU, 0x1AF427U, 0x1B01C4U, 0x1B192FU, 0x1B28FAU, 0x1B3011U, 0x1B4B53U, 0x1B53B8U, 0x1B626DU, 0x1B7A86U, + 0x1B8C02U, 0x1B94E9U, 0x1BA53CU, 0x1BBDD7U, 0x1BC695U, 0x1BDE7EU, 0x1BEFABU, 0x1BF740U, 0x1C09F7U, 0x1C111CU, + 0x1C20C9U, 0x1C3822U, 0x1C4360U, 0x1C5B8BU, 0x1C6A5EU, 0x1C72B5U, 0x1C8431U, 0x1C9CDAU, 0x1CAD0FU, 0x1CB5E4U, + 0x1CCEA6U, 0x1CD64DU, 0x1CE798U, 0x1CFF73U, 0x1D0A90U, 0x1D127BU, 0x1D23AEU, 0x1D3B45U, 0x1D4007U, 0x1D58ECU, + 0x1D6939U, 0x1D71D2U, 0x1D8756U, 0x1D9FBDU, 0x1DAE68U, 0x1DB683U, 0x1DCDC1U, 0x1DD52AU, 0x1DE4FFU, 0x1DFC14U, + 0x1E0F3AU, 0x1E17D1U, 0x1E2604U, 0x1E3EEFU, 0x1E45ADU, 0x1E5D46U, 0x1E6C93U, 0x1E7478U, 0x1E82FCU, 0x1E9A17U, + 0x1EABC2U, 0x1EB329U, 0x1EC86BU, 0x1ED080U, 0x1EE155U, 0x1EF9BEU, 0x1F0C5DU, 0x1F14B6U, 0x1F2563U, 0x1F3D88U, + 0x1F46CAU, 0x1F5E21U, 0x1F6FF4U, 0x1F771FU, 0x1F819BU, 0x1F9970U, 0x1FA8A5U, 0x1FB04EU, 0x1FCB0CU, 0x1FD3E7U, + 0x1FE232U, 0x1FFAD9U, 0x200F68U, 0x201783U, 0x202656U, 0x203EBDU, 0x2045FFU, 0x205D14U, 0x206CC1U, 0x20742AU, + 0x2082AEU, 0x209A45U, 0x20AB90U, 0x20B37BU, 0x20C839U, 0x20D0D2U, 0x20E107U, 0x20F9ECU, 0x210C0FU, 0x2114E4U, + 0x212531U, 0x213DDAU, 0x214698U, 0x215E73U, 0x216FA6U, 0x21774DU, 0x2181C9U, 0x219922U, 0x21A8F7U, 0x21B01CU, + 0x21CB5EU, 0x21D3B5U, 0x21E260U, 0x21FA8BU, 0x2209A5U, 0x22114EU, 0x22209BU, 0x223870U, 0x224332U, 0x225BD9U, + 0x226A0CU, 0x2272E7U, 0x228463U, 0x229C88U, 0x22AD5DU, 0x22B5B6U, 0x22CEF4U, 0x22D61FU, 0x22E7CAU, 0x22FF21U, + 0x230AC2U, 0x231229U, 0x2323FCU, 0x233B17U, 0x234055U, 0x2358BEU, 0x23696BU, 0x237180U, 0x238704U, 0x239FEFU, + 0x23AE3AU, 0x23B6D1U, 0x23CD93U, 0x23D578U, 0x23E4ADU, 0x23FC46U, 0x2402F1U, 0x241A1AU, 0x242BCFU, 0x243324U, + 0x244866U, 0x24508DU, 0x246158U, 0x2479B3U, 0x248F37U, 0x2497DCU, 0x24A609U, 0x24BEE2U, 0x24C5A0U, 0x24DD4BU, + 0x24EC9EU, 0x24F475U, 0x250196U, 0x25197DU, 0x2528A8U, 0x253043U, 0x254B01U, 0x2553EAU, 0x25623FU, 0x257AD4U, + 0x258C50U, 0x2594BBU, 0x25A56EU, 0x25BD85U, 0x25C6C7U, 0x25DE2CU, 0x25EFF9U, 0x25F712U, 0x26043CU, 0x261CD7U, + 0x262D02U, 0x2635E9U, 0x264EABU, 0x265640U, 0x266795U, 0x267F7EU, 0x2689FAU, 0x269111U, 0x26A0C4U, 0x26B82FU, + 0x26C36DU, 0x26DB86U, 0x26EA53U, 0x26F2B8U, 0x27075BU, 0x271FB0U, 0x272E65U, 0x27368EU, 0x274DCCU, 0x275527U, + 0x2764F2U, 0x277C19U, 0x278A9DU, 0x279276U, 0x27A3A3U, 0x27BB48U, 0x27C00AU, 0x27D8E1U, 0x27E934U, 0x27F1DFU, + 0x280CB2U, 0x281459U, 0x28258CU, 0x283D67U, 0x284625U, 0x285ECEU, 0x286F1BU, 0x2877F0U, 0x288174U, 0x28999FU, + 0x28A84AU, 0x28B0A1U, 0x28CBE3U, 0x28D308U, 0x28E2DDU, 0x28FA36U, 0x290FD5U, 0x29173EU, 0x2926EBU, 0x293E00U, + 0x294542U, 0x295DA9U, 0x296C7CU, 0x297497U, 0x298213U, 0x299AF8U, 0x29AB2DU, 0x29B3C6U, 0x29C884U, 0x29D06FU, + 0x29E1BAU, 0x29F951U, 0x2A0A7FU, 0x2A1294U, 0x2A2341U, 0x2A3BAAU, 0x2A40E8U, 0x2A5803U, 0x2A69D6U, 0x2A713DU, + 0x2A87B9U, 0x2A9F52U, 0x2AAE87U, 0x2AB66CU, 0x2ACD2EU, 0x2AD5C5U, 0x2AE410U, 0x2AFCFBU, 0x2B0918U, 0x2B11F3U, + 0x2B2026U, 0x2B38CDU, 0x2B438FU, 0x2B5B64U, 0x2B6AB1U, 0x2B725AU, 0x2B84DEU, 0x2B9C35U, 0x2BADE0U, 0x2BB50BU, + 0x2BCE49U, 0x2BD6A2U, 0x2BE777U, 0x2BFF9CU, 0x2C012BU, 0x2C19C0U, 0x2C2815U, 0x2C30FEU, 0x2C4BBCU, 0x2C5357U, + 0x2C6282U, 0x2C7A69U, 0x2C8CEDU, 0x2C9406U, 0x2CA5D3U, 0x2CBD38U, 0x2CC67AU, 0x2CDE91U, 0x2CEF44U, 0x2CF7AFU, + 0x2D024CU, 0x2D1AA7U, 0x2D2B72U, 0x2D3399U, 0x2D48DBU, 0x2D5030U, 0x2D61E5U, 0x2D790EU, 0x2D8F8AU, 0x2D9761U, + 0x2DA6B4U, 0x2DBE5FU, 0x2DC51DU, 0x2DDDF6U, 0x2DEC23U, 0x2DF4C8U, 0x2E07E6U, 0x2E1F0DU, 0x2E2ED8U, 0x2E3633U, + 0x2E4D71U, 0x2E559AU, 0x2E644FU, 0x2E7CA4U, 0x2E8A20U, 0x2E92CBU, 0x2EA31EU, 0x2EBBF5U, 0x2EC0B7U, 0x2ED85CU, + 0x2EE989U, 0x2EF162U, 0x2F0481U, 0x2F1C6AU, 0x2F2DBFU, 0x2F3554U, 0x2F4E16U, 0x2F56FDU, 0x2F6728U, 0x2F7FC3U, + 0x2F8947U, 0x2F91ACU, 0x2FA079U, 0x2FB892U, 0x2FC3D0U, 0x2FDB3BU, 0x2FEAEEU, 0x2FF205U, 0x3008DCU, 0x301037U, + 0x3021E2U, 0x303909U, 0x30424BU, 0x305AA0U, 0x306B75U, 0x30739EU, 0x30851AU, 0x309DF1U, 0x30AC24U, 0x30B4CFU, + 0x30CF8DU, 0x30D766U, 0x30E6B3U, 0x30FE58U, 0x310BBBU, 0x311350U, 0x312285U, 0x313A6EU, 0x31412CU, 0x3159C7U, + 0x316812U, 0x3170F9U, 0x31867DU, 0x319E96U, 0x31AF43U, 0x31B7A8U, 0x31CCEAU, 0x31D401U, 0x31E5D4U, 0x31FD3FU, + 0x320E11U, 0x3216FAU, 0x32272FU, 0x323FC4U, 0x324486U, 0x325C6DU, 0x326DB8U, 0x327553U, 0x3283D7U, 0x329B3CU, + 0x32AAE9U, 0x32B202U, 0x32C940U, 0x32D1ABU, 0x32E07EU, 0x32F895U, 0x330D76U, 0x33159DU, 0x332448U, 0x333CA3U, + 0x3347E1U, 0x335F0AU, 0x336EDFU, 0x337634U, 0x3380B0U, 0x33985BU, 0x33A98EU, 0x33B165U, 0x33CA27U, 0x33D2CCU, + 0x33E319U, 0x33FBF2U, 0x340545U, 0x341DAEU, 0x342C7BU, 0x343490U, 0x344FD2U, 0x345739U, 0x3466ECU, 0x347E07U, + 0x348883U, 0x349068U, 0x34A1BDU, 0x34B956U, 0x34C214U, 0x34DAFFU, 0x34EB2AU, 0x34F3C1U, 0x350622U, 0x351EC9U, + 0x352F1CU, 0x3537F7U, 0x354CB5U, 0x35545EU, 0x35658BU, 0x357D60U, 0x358BE4U, 0x35930FU, 0x35A2DAU, 0x35BA31U, + 0x35C173U, 0x35D998U, 0x35E84DU, 0x35F0A6U, 0x360388U, 0x361B63U, 0x362AB6U, 0x36325DU, 0x36491FU, 0x3651F4U, + 0x366021U, 0x3678CAU, 0x368E4EU, 0x3696A5U, 0x36A770U, 0x36BF9BU, 0x36C4D9U, 0x36DC32U, 0x36EDE7U, 0x36F50CU, + 0x3700EFU, 0x371804U, 0x3729D1U, 0x37313AU, 0x374A78U, 0x375293U, 0x376346U, 0x377BADU, 0x378D29U, 0x3795C2U, + 0x37A417U, 0x37BCFCU, 0x37C7BEU, 0x37DF55U, 0x37EE80U, 0x37F66BU, 0x380B06U, 0x3813EDU, 0x382238U, 0x383AD3U, + 0x384191U, 0x38597AU, 0x3868AFU, 0x387044U, 0x3886C0U, 0x389E2BU, 0x38AFFEU, 0x38B715U, 0x38CC57U, 0x38D4BCU, + 0x38E569U, 0x38FD82U, 0x390861U, 0x39108AU, 0x39215FU, 0x3939B4U, 0x3942F6U, 0x395A1DU, 0x396BC8U, 0x397323U, + 0x3985A7U, 0x399D4CU, 0x39AC99U, 0x39B472U, 0x39CF30U, 0x39D7DBU, 0x39E60EU, 0x39FEE5U, 0x3A0DCBU, 0x3A1520U, + 0x3A24F5U, 0x3A3C1EU, 0x3A475CU, 0x3A5FB7U, 0x3A6E62U, 0x3A7689U, 0x3A800DU, 0x3A98E6U, 0x3AA933U, 0x3AB1D8U, + 0x3ACA9AU, 0x3AD271U, 0x3AE3A4U, 0x3AFB4FU, 0x3B0EACU, 0x3B1647U, 0x3B2792U, 0x3B3F79U, 0x3B443BU, 0x3B5CD0U, + 0x3B6D05U, 0x3B75EEU, 0x3B836AU, 0x3B9B81U, 0x3BAA54U, 0x3BB2BFU, 0x3BC9FDU, 0x3BD116U, 0x3BE0C3U, 0x3BF828U, + 0x3C069FU, 0x3C1E74U, 0x3C2FA1U, 0x3C374AU, 0x3C4C08U, 0x3C54E3U, 0x3C6536U, 0x3C7DDDU, 0x3C8B59U, 0x3C93B2U, + 0x3CA267U, 0x3CBA8CU, 0x3CC1CEU, 0x3CD925U, 0x3CE8F0U, 0x3CF01BU, 0x3D05F8U, 0x3D1D13U, 0x3D2CC6U, 0x3D342DU, + 0x3D4F6FU, 0x3D5784U, 0x3D6651U, 0x3D7EBAU, 0x3D883EU, 0x3D90D5U, 0x3DA100U, 0x3DB9EBU, 0x3DC2A9U, 0x3DDA42U, + 0x3DEB97U, 0x3DF37CU, 0x3E0052U, 0x3E18B9U, 0x3E296CU, 0x3E3187U, 0x3E4AC5U, 0x3E522EU, 0x3E63FBU, 0x3E7B10U, + 0x3E8D94U, 0x3E957FU, 0x3EA4AAU, 0x3EBC41U, 0x3EC703U, 0x3EDFE8U, 0x3EEE3DU, 0x3EF6D6U, 0x3F0335U, 0x3F1BDEU, + 0x3F2A0BU, 0x3F32E0U, 0x3F49A2U, 0x3F5149U, 0x3F609CU, 0x3F7877U, 0x3F8EF3U, 0x3F9618U, 0x3FA7CDU, 0x3FBF26U, + 0x3FC464U, 0x3FDC8FU, 0x3FED5AU, 0x3FF5B1U, 0x40063BU, 0x401ED0U, 0x402F05U, 0x4037EEU, 0x404CACU, 0x405447U, + 0x406592U, 0x407D79U, 0x408BFDU, 0x409316U, 0x40A2C3U, 0x40BA28U, 0x40C16AU, 0x40D981U, 0x40E854U, 0x40F0BFU, + 0x41055CU, 0x411DB7U, 0x412C62U, 0x413489U, 0x414FCBU, 0x415720U, 0x4166F5U, 0x417E1EU, 0x41889AU, 0x419071U, + 0x41A1A4U, 0x41B94FU, 0x41C20DU, 0x41DAE6U, 0x41EB33U, 0x41F3D8U, 0x4200F6U, 0x42181DU, 0x4229C8U, 0x423123U, + 0x424A61U, 0x42528AU, 0x42635FU, 0x427BB4U, 0x428D30U, 0x4295DBU, 0x42A40EU, 0x42BCE5U, 0x42C7A7U, 0x42DF4CU, + 0x42EE99U, 0x42F672U, 0x430391U, 0x431B7AU, 0x432AAFU, 0x433244U, 0x434906U, 0x4351EDU, 0x436038U, 0x4378D3U, + 0x438E57U, 0x4396BCU, 0x43A769U, 0x43BF82U, 0x43C4C0U, 0x43DC2BU, 0x43EDFEU, 0x43F515U, 0x440BA2U, 0x441349U, + 0x44229CU, 0x443A77U, 0x444135U, 0x4459DEU, 0x44680BU, 0x4470E0U, 0x448664U, 0x449E8FU, 0x44AF5AU, 0x44B7B1U, + 0x44CCF3U, 0x44D418U, 0x44E5CDU, 0x44FD26U, 0x4508C5U, 0x45102EU, 0x4521FBU, 0x453910U, 0x454252U, 0x455AB9U, + 0x456B6CU, 0x457387U, 0x458503U, 0x459DE8U, 0x45AC3DU, 0x45B4D6U, 0x45CF94U, 0x45D77FU, 0x45E6AAU, 0x45FE41U, + 0x460D6FU, 0x461584U, 0x462451U, 0x463CBAU, 0x4647F8U, 0x465F13U, 0x466EC6U, 0x46762DU, 0x4680A9U, 0x469842U, + 0x46A997U, 0x46B17CU, 0x46CA3EU, 0x46D2D5U, 0x46E300U, 0x46FBEBU, 0x470E08U, 0x4716E3U, 0x472736U, 0x473FDDU, + 0x47449FU, 0x475C74U, 0x476DA1U, 0x47754AU, 0x4783CEU, 0x479B25U, 0x47AAF0U, 0x47B21BU, 0x47C959U, 0x47D1B2U, + 0x47E067U, 0x47F88CU, 0x4805E1U, 0x481D0AU, 0x482CDFU, 0x483434U, 0x484F76U, 0x48579DU, 0x486648U, 0x487EA3U, + 0x488827U, 0x4890CCU, 0x48A119U, 0x48B9F2U, 0x48C2B0U, 0x48DA5BU, 0x48EB8EU, 0x48F365U, 0x490686U, 0x491E6DU, + 0x492FB8U, 0x493753U, 0x494C11U, 0x4954FAU, 0x49652FU, 0x497DC4U, 0x498B40U, 0x4993ABU, 0x49A27EU, 0x49BA95U, + 0x49C1D7U, 0x49D93CU, 0x49E8E9U, 0x49F002U, 0x4A032CU, 0x4A1BC7U, 0x4A2A12U, 0x4A32F9U, 0x4A49BBU, 0x4A5150U, + 0x4A6085U, 0x4A786EU, 0x4A8EEAU, 0x4A9601U, 0x4AA7D4U, 0x4ABF3FU, 0x4AC47DU, 0x4ADC96U, 0x4AED43U, 0x4AF5A8U, + 0x4B004BU, 0x4B18A0U, 0x4B2975U, 0x4B319EU, 0x4B4ADCU, 0x4B5237U, 0x4B63E2U, 0x4B7B09U, 0x4B8D8DU, 0x4B9566U, + 0x4BA4B3U, 0x4BBC58U, 0x4BC71AU, 0x4BDFF1U, 0x4BEE24U, 0x4BF6CFU, 0x4C0878U, 0x4C1093U, 0x4C2146U, 0x4C39ADU, + 0x4C42EFU, 0x4C5A04U, 0x4C6BD1U, 0x4C733AU, 0x4C85BEU, 0x4C9D55U, 0x4CAC80U, 0x4CB46BU, 0x4CCF29U, 0x4CD7C2U, + 0x4CE617U, 0x4CFEFCU, 0x4D0B1FU, 0x4D13F4U, 0x4D2221U, 0x4D3ACAU, 0x4D4188U, 0x4D5963U, 0x4D68B6U, 0x4D705DU, + 0x4D86D9U, 0x4D9E32U, 0x4DAFE7U, 0x4DB70CU, 0x4DCC4EU, 0x4DD4A5U, 0x4DE570U, 0x4DFD9BU, 0x4E0EB5U, 0x4E165EU, + 0x4E278BU, 0x4E3F60U, 0x4E4422U, 0x4E5CC9U, 0x4E6D1CU, 0x4E75F7U, 0x4E8373U, 0x4E9B98U, 0x4EAA4DU, 0x4EB2A6U, + 0x4EC9E4U, 0x4ED10FU, 0x4EE0DAU, 0x4EF831U, 0x4F0DD2U, 0x4F1539U, 0x4F24ECU, 0x4F3C07U, 0x4F4745U, 0x4F5FAEU, + 0x4F6E7BU, 0x4F7690U, 0x4F8014U, 0x4F98FFU, 0x4FA92AU, 0x4FB1C1U, 0x4FCA83U, 0x4FD268U, 0x4FE3BDU, 0x4FFB56U, + 0x50018FU, 0x501964U, 0x5028B1U, 0x50305AU, 0x504B18U, 0x5053F3U, 0x506226U, 0x507ACDU, 0x508C49U, 0x5094A2U, + 0x50A577U, 0x50BD9CU, 0x50C6DEU, 0x50DE35U, 0x50EFE0U, 0x50F70BU, 0x5102E8U, 0x511A03U, 0x512BD6U, 0x51333DU, + 0x51487FU, 0x515094U, 0x516141U, 0x5179AAU, 0x518F2EU, 0x5197C5U, 0x51A610U, 0x51BEFBU, 0x51C5B9U, 0x51DD52U, + 0x51EC87U, 0x51F46CU, 0x520742U, 0x521FA9U, 0x522E7CU, 0x523697U, 0x524DD5U, 0x52553EU, 0x5264EBU, 0x527C00U, + 0x528A84U, 0x52926FU, 0x52A3BAU, 0x52BB51U, 0x52C013U, 0x52D8F8U, 0x52E92DU, 0x52F1C6U, 0x530425U, 0x531CCEU, + 0x532D1BU, 0x5335F0U, 0x534EB2U, 0x535659U, 0x53678CU, 0x537F67U, 0x5389E3U, 0x539108U, 0x53A0DDU, 0x53B836U, + 0x53C374U, 0x53DB9FU, 0x53EA4AU, 0x53F2A1U, 0x540C16U, 0x5414FDU, 0x542528U, 0x543DC3U, 0x544681U, 0x545E6AU, + 0x546FBFU, 0x547754U, 0x5481D0U, 0x54993BU, 0x54A8EEU, 0x54B005U, 0x54CB47U, 0x54D3ACU, 0x54E279U, 0x54FA92U, + 0x550F71U, 0x55179AU, 0x55264FU, 0x553EA4U, 0x5545E6U, 0x555D0DU, 0x556CD8U, 0x557433U, 0x5582B7U, 0x559A5CU, + 0x55AB89U, 0x55B362U, 0x55C820U, 0x55D0CBU, 0x55E11EU, 0x55F9F5U, 0x560ADBU, 0x561230U, 0x5623E5U, 0x563B0EU, + 0x56404CU, 0x5658A7U, 0x566972U, 0x567199U, 0x56871DU, 0x569FF6U, 0x56AE23U, 0x56B6C8U, 0x56CD8AU, 0x56D561U, + 0x56E4B4U, 0x56FC5FU, 0x5709BCU, 0x571157U, 0x572082U, 0x573869U, 0x57432BU, 0x575BC0U, 0x576A15U, 0x5772FEU, + 0x57847AU, 0x579C91U, 0x57AD44U, 0x57B5AFU, 0x57CEEDU, 0x57D606U, 0x57E7D3U, 0x57FF38U, 0x580255U, 0x581ABEU, + 0x582B6BU, 0x583380U, 0x5848C2U, 0x585029U, 0x5861FCU, 0x587917U, 0x588F93U, 0x589778U, 0x58A6ADU, 0x58BE46U, + 0x58C504U, 0x58DDEFU, 0x58EC3AU, 0x58F4D1U, 0x590132U, 0x5919D9U, 0x59280CU, 0x5930E7U, 0x594BA5U, 0x59534EU, + 0x59629BU, 0x597A70U, 0x598CF4U, 0x59941FU, 0x59A5CAU, 0x59BD21U, 0x59C663U, 0x59DE88U, 0x59EF5DU, 0x59F7B6U, + 0x5A0498U, 0x5A1C73U, 0x5A2DA6U, 0x5A354DU, 0x5A4E0FU, 0x5A56E4U, 0x5A6731U, 0x5A7FDAU, 0x5A895EU, 0x5A91B5U, + 0x5AA060U, 0x5AB88BU, 0x5AC3C9U, 0x5ADB22U, 0x5AEAF7U, 0x5AF21CU, 0x5B07FFU, 0x5B1F14U, 0x5B2EC1U, 0x5B362AU, + 0x5B4D68U, 0x5B5583U, 0x5B6456U, 0x5B7CBDU, 0x5B8A39U, 0x5B92D2U, 0x5BA307U, 0x5BBBECU, 0x5BC0AEU, 0x5BD845U, + 0x5BE990U, 0x5BF17BU, 0x5C0FCCU, 0x5C1727U, 0x5C26F2U, 0x5C3E19U, 0x5C455BU, 0x5C5DB0U, 0x5C6C65U, 0x5C748EU, + 0x5C820AU, 0x5C9AE1U, 0x5CAB34U, 0x5CB3DFU, 0x5CC89DU, 0x5CD076U, 0x5CE1A3U, 0x5CF948U, 0x5D0CABU, 0x5D1440U, + 0x5D2595U, 0x5D3D7EU, 0x5D463CU, 0x5D5ED7U, 0x5D6F02U, 0x5D77E9U, 0x5D816DU, 0x5D9986U, 0x5DA853U, 0x5DB0B8U, + 0x5DCBFAU, 0x5DD311U, 0x5DE2C4U, 0x5DFA2FU, 0x5E0901U, 0x5E11EAU, 0x5E203FU, 0x5E38D4U, 0x5E4396U, 0x5E5B7DU, + 0x5E6AA8U, 0x5E7243U, 0x5E84C7U, 0x5E9C2CU, 0x5EADF9U, 0x5EB512U, 0x5ECE50U, 0x5ED6BBU, 0x5EE76EU, 0x5EFF85U, + 0x5F0A66U, 0x5F128DU, 0x5F2358U, 0x5F3BB3U, 0x5F40F1U, 0x5F581AU, 0x5F69CFU, 0x5F7124U, 0x5F87A0U, 0x5F9F4BU, + 0x5FAE9EU, 0x5FB675U, 0x5FCD37U, 0x5FD5DCU, 0x5FE409U, 0x5FFCE2U, 0x600953U, 0x6011B8U, 0x60206DU, 0x603886U, + 0x6043C4U, 0x605B2FU, 0x606AFAU, 0x607211U, 0x608495U, 0x609C7EU, 0x60ADABU, 0x60B540U, 0x60CE02U, 0x60D6E9U, + 0x60E73CU, 0x60FFD7U, 0x610A34U, 0x6112DFU, 0x61230AU, 0x613BE1U, 0x6140A3U, 0x615848U, 0x61699DU, 0x617176U, + 0x6187F2U, 0x619F19U, 0x61AECCU, 0x61B627U, 0x61CD65U, 0x61D58EU, 0x61E45BU, 0x61FCB0U, 0x620F9EU, 0x621775U, + 0x6226A0U, 0x623E4BU, 0x624509U, 0x625DE2U, 0x626C37U, 0x6274DCU, 0x628258U, 0x629AB3U, 0x62AB66U, 0x62B38DU, + 0x62C8CFU, 0x62D024U, 0x62E1F1U, 0x62F91AU, 0x630CF9U, 0x631412U, 0x6325C7U, 0x633D2CU, 0x63466EU, 0x635E85U, + 0x636F50U, 0x6377BBU, 0x63813FU, 0x6399D4U, 0x63A801U, 0x63B0EAU, 0x63CBA8U, 0x63D343U, 0x63E296U, 0x63FA7DU, + 0x6404CAU, 0x641C21U, 0x642DF4U, 0x64351FU, 0x644E5DU, 0x6456B6U, 0x646763U, 0x647F88U, 0x64890CU, 0x6491E7U, + 0x64A032U, 0x64B8D9U, 0x64C39BU, 0x64DB70U, 0x64EAA5U, 0x64F24EU, 0x6507ADU, 0x651F46U, 0x652E93U, 0x653678U, + 0x654D3AU, 0x6555D1U, 0x656404U, 0x657CEFU, 0x658A6BU, 0x659280U, 0x65A355U, 0x65BBBEU, 0x65C0FCU, 0x65D817U, + 0x65E9C2U, 0x65F129U, 0x660207U, 0x661AECU, 0x662B39U, 0x6633D2U, 0x664890U, 0x66507BU, 0x6661AEU, 0x667945U, + 0x668FC1U, 0x66972AU, 0x66A6FFU, 0x66BE14U, 0x66C556U, 0x66DDBDU, 0x66EC68U, 0x66F483U, 0x670160U, 0x67198BU, + 0x67285EU, 0x6730B5U, 0x674BF7U, 0x67531CU, 0x6762C9U, 0x677A22U, 0x678CA6U, 0x67944DU, 0x67A598U, 0x67BD73U, + 0x67C631U, 0x67DEDAU, 0x67EF0FU, 0x67F7E4U, 0x680A89U, 0x681262U, 0x6823B7U, 0x683B5CU, 0x68401EU, 0x6858F5U, + 0x686920U, 0x6871CBU, 0x68874FU, 0x689FA4U, 0x68AE71U, 0x68B69AU, 0x68CDD8U, 0x68D533U, 0x68E4E6U, 0x68FC0DU, + 0x6909EEU, 0x691105U, 0x6920D0U, 0x69383BU, 0x694379U, 0x695B92U, 0x696A47U, 0x6972ACU, 0x698428U, 0x699CC3U, + 0x69AD16U, 0x69B5FDU, 0x69CEBFU, 0x69D654U, 0x69E781U, 0x69FF6AU, 0x6A0C44U, 0x6A14AFU, 0x6A257AU, 0x6A3D91U, + 0x6A46D3U, 0x6A5E38U, 0x6A6FEDU, 0x6A7706U, 0x6A8182U, 0x6A9969U, 0x6AA8BCU, 0x6AB057U, 0x6ACB15U, 0x6AD3FEU, + 0x6AE22BU, 0x6AFAC0U, 0x6B0F23U, 0x6B17C8U, 0x6B261DU, 0x6B3EF6U, 0x6B45B4U, 0x6B5D5FU, 0x6B6C8AU, 0x6B7461U, + 0x6B82E5U, 0x6B9A0EU, 0x6BABDBU, 0x6BB330U, 0x6BC872U, 0x6BD099U, 0x6BE14CU, 0x6BF9A7U, 0x6C0710U, 0x6C1FFBU, + 0x6C2E2EU, 0x6C36C5U, 0x6C4D87U, 0x6C556CU, 0x6C64B9U, 0x6C7C52U, 0x6C8AD6U, 0x6C923DU, 0x6CA3E8U, 0x6CBB03U, + 0x6CC041U, 0x6CD8AAU, 0x6CE97FU, 0x6CF194U, 0x6D0477U, 0x6D1C9CU, 0x6D2D49U, 0x6D35A2U, 0x6D4EE0U, 0x6D560BU, + 0x6D67DEU, 0x6D7F35U, 0x6D89B1U, 0x6D915AU, 0x6DA08FU, 0x6DB864U, 0x6DC326U, 0x6DDBCDU, 0x6DEA18U, 0x6DF2F3U, + 0x6E01DDU, 0x6E1936U, 0x6E28E3U, 0x6E3008U, 0x6E4B4AU, 0x6E53A1U, 0x6E6274U, 0x6E7A9FU, 0x6E8C1BU, 0x6E94F0U, + 0x6EA525U, 0x6EBDCEU, 0x6EC68CU, 0x6EDE67U, 0x6EEFB2U, 0x6EF759U, 0x6F02BAU, 0x6F1A51U, 0x6F2B84U, 0x6F336FU, + 0x6F482DU, 0x6F50C6U, 0x6F6113U, 0x6F79F8U, 0x6F8F7CU, 0x6F9797U, 0x6FA642U, 0x6FBEA9U, 0x6FC5EBU, 0x6FDD00U, + 0x6FECD5U, 0x6FF43EU, 0x700EE7U, 0x70160CU, 0x7027D9U, 0x703F32U, 0x704470U, 0x705C9BU, 0x706D4EU, 0x7075A5U, + 0x708321U, 0x709BCAU, 0x70AA1FU, 0x70B2F4U, 0x70C9B6U, 0x70D15DU, 0x70E088U, 0x70F863U, 0x710D80U, 0x71156BU, + 0x7124BEU, 0x713C55U, 0x714717U, 0x715FFCU, 0x716E29U, 0x7176C2U, 0x718046U, 0x7198ADU, 0x71A978U, 0x71B193U, + 0x71CAD1U, 0x71D23AU, 0x71E3EFU, 0x71FB04U, 0x72082AU, 0x7210C1U, 0x722114U, 0x7239FFU, 0x7242BDU, 0x725A56U, + 0x726B83U, 0x727368U, 0x7285ECU, 0x729D07U, 0x72ACD2U, 0x72B439U, 0x72CF7BU, 0x72D790U, 0x72E645U, 0x72FEAEU, + 0x730B4DU, 0x7313A6U, 0x732273U, 0x733A98U, 0x7341DAU, 0x735931U, 0x7368E4U, 0x73700FU, 0x73868BU, 0x739E60U, + 0x73AFB5U, 0x73B75EU, 0x73CC1CU, 0x73D4F7U, 0x73E522U, 0x73FDC9U, 0x74037EU, 0x741B95U, 0x742A40U, 0x7432ABU, + 0x7449E9U, 0x745102U, 0x7460D7U, 0x74783CU, 0x748EB8U, 0x749653U, 0x74A786U, 0x74BF6DU, 0x74C42FU, 0x74DCC4U, + 0x74ED11U, 0x74F5FAU, 0x750019U, 0x7518F2U, 0x752927U, 0x7531CCU, 0x754A8EU, 0x755265U, 0x7563B0U, 0x757B5BU, + 0x758DDFU, 0x759534U, 0x75A4E1U, 0x75BC0AU, 0x75C748U, 0x75DFA3U, 0x75EE76U, 0x75F69DU, 0x7605B3U, 0x761D58U, + 0x762C8DU, 0x763466U, 0x764F24U, 0x7657CFU, 0x76661AU, 0x767EF1U, 0x768875U, 0x76909EU, 0x76A14BU, 0x76B9A0U, + 0x76C2E2U, 0x76DA09U, 0x76EBDCU, 0x76F337U, 0x7706D4U, 0x771E3FU, 0x772FEAU, 0x773701U, 0x774C43U, 0x7754A8U, + 0x77657DU, 0x777D96U, 0x778B12U, 0x7793F9U, 0x77A22CU, 0x77BAC7U, 0x77C185U, 0x77D96EU, 0x77E8BBU, 0x77F050U, + 0x780D3DU, 0x7815D6U, 0x782403U, 0x783CE8U, 0x7847AAU, 0x785F41U, 0x786E94U, 0x78767FU, 0x7880FBU, 0x789810U, + 0x78A9C5U, 0x78B12EU, 0x78CA6CU, 0x78D287U, 0x78E352U, 0x78FBB9U, 0x790E5AU, 0x7916B1U, 0x792764U, 0x793F8FU, + 0x7944CDU, 0x795C26U, 0x796DF3U, 0x797518U, 0x79839CU, 0x799B77U, 0x79AAA2U, 0x79B249U, 0x79C90BU, 0x79D1E0U, + 0x79E035U, 0x79F8DEU, 0x7A0BF0U, 0x7A131BU, 0x7A22CEU, 0x7A3A25U, 0x7A4167U, 0x7A598CU, 0x7A6859U, 0x7A70B2U, + 0x7A8636U, 0x7A9EDDU, 0x7AAF08U, 0x7AB7E3U, 0x7ACCA1U, 0x7AD44AU, 0x7AE59FU, 0x7AFD74U, 0x7B0897U, 0x7B107CU, + 0x7B21A9U, 0x7B3942U, 0x7B4200U, 0x7B5AEBU, 0x7B6B3EU, 0x7B73D5U, 0x7B8551U, 0x7B9DBAU, 0x7BAC6FU, 0x7BB484U, + 0x7BCFC6U, 0x7BD72DU, 0x7BE6F8U, 0x7BFE13U, 0x7C00A4U, 0x7C184FU, 0x7C299AU, 0x7C3171U, 0x7C4A33U, 0x7C52D8U, + 0x7C630DU, 0x7C7BE6U, 0x7C8D62U, 0x7C9589U, 0x7CA45CU, 0x7CBCB7U, 0x7CC7F5U, 0x7CDF1EU, 0x7CEECBU, 0x7CF620U, + 0x7D03C3U, 0x7D1B28U, 0x7D2AFDU, 0x7D3216U, 0x7D4954U, 0x7D51BFU, 0x7D606AU, 0x7D7881U, 0x7D8E05U, 0x7D96EEU, + 0x7DA73BU, 0x7DBFD0U, 0x7DC492U, 0x7DDC79U, 0x7DEDACU, 0x7DF547U, 0x7E0669U, 0x7E1E82U, 0x7E2F57U, 0x7E37BCU, + 0x7E4CFEU, 0x7E5415U, 0x7E65C0U, 0x7E7D2BU, 0x7E8BAFU, 0x7E9344U, 0x7EA291U, 0x7EBA7AU, 0x7EC138U, 0x7ED9D3U, + 0x7EE806U, 0x7EF0EDU, 0x7F050EU, 0x7F1DE5U, 0x7F2C30U, 0x7F34DBU, 0x7F4F99U, 0x7F5772U, 0x7F66A7U, 0x7F7E4CU, + 0x7F88C8U, 0x7F9023U, 0x7FA1F6U, 0x7FB91DU, 0x7FC25FU, 0x7FDAB4U, 0x7FEB61U, 0x7FF38AU, 0x800C75U, 0x80149EU, + 0x80254BU, 0x803DA0U, 0x8046E2U, 0x805E09U, 0x806FDCU, 0x807737U, 0x8081B3U, 0x809958U, 0x80A88DU, 0x80B066U, + 0x80CB24U, 0x80D3CFU, 0x80E21AU, 0x80FAF1U, 0x810F12U, 0x8117F9U, 0x81262CU, 0x813EC7U, 0x814585U, 0x815D6EU, + 0x816CBBU, 0x817450U, 0x8182D4U, 0x819A3FU, 0x81ABEAU, 0x81B301U, 0x81C843U, 0x81D0A8U, 0x81E17DU, 0x81F996U, + 0x820AB8U, 0x821253U, 0x822386U, 0x823B6DU, 0x82402FU, 0x8258C4U, 0x826911U, 0x8271FAU, 0x82877EU, 0x829F95U, + 0x82AE40U, 0x82B6ABU, 0x82CDE9U, 0x82D502U, 0x82E4D7U, 0x82FC3CU, 0x8309DFU, 0x831134U, 0x8320E1U, 0x83380AU, + 0x834348U, 0x835BA3U, 0x836A76U, 0x83729DU, 0x838419U, 0x839CF2U, 0x83AD27U, 0x83B5CCU, 0x83CE8EU, 0x83D665U, + 0x83E7B0U, 0x83FF5BU, 0x8401ECU, 0x841907U, 0x8428D2U, 0x843039U, 0x844B7BU, 0x845390U, 0x846245U, 0x847AAEU, + 0x848C2AU, 0x8494C1U, 0x84A514U, 0x84BDFFU, 0x84C6BDU, 0x84DE56U, 0x84EF83U, 0x84F768U, 0x85028BU, 0x851A60U, + 0x852BB5U, 0x85335EU, 0x85481CU, 0x8550F7U, 0x856122U, 0x8579C9U, 0x858F4DU, 0x8597A6U, 0x85A673U, 0x85BE98U, + 0x85C5DAU, 0x85DD31U, 0x85ECE4U, 0x85F40FU, 0x860721U, 0x861FCAU, 0x862E1FU, 0x8636F4U, 0x864DB6U, 0x86555DU, + 0x866488U, 0x867C63U, 0x868AE7U, 0x86920CU, 0x86A3D9U, 0x86BB32U, 0x86C070U, 0x86D89BU, 0x86E94EU, 0x86F1A5U, + 0x870446U, 0x871CADU, 0x872D78U, 0x873593U, 0x874ED1U, 0x87563AU, 0x8767EFU, 0x877F04U, 0x878980U, 0x87916BU, + 0x87A0BEU, 0x87B855U, 0x87C317U, 0x87DBFCU, 0x87EA29U, 0x87F2C2U, 0x880FAFU, 0x881744U, 0x882691U, 0x883E7AU, + 0x884538U, 0x885DD3U, 0x886C06U, 0x8874EDU, 0x888269U, 0x889A82U, 0x88AB57U, 0x88B3BCU, 0x88C8FEU, 0x88D015U, + 0x88E1C0U, 0x88F92BU, 0x890CC8U, 0x891423U, 0x8925F6U, 0x893D1DU, 0x89465FU, 0x895EB4U, 0x896F61U, 0x89778AU, + 0x89810EU, 0x8999E5U, 0x89A830U, 0x89B0DBU, 0x89CB99U, 0x89D372U, 0x89E2A7U, 0x89FA4CU, 0x8A0962U, 0x8A1189U, + 0x8A205CU, 0x8A38B7U, 0x8A43F5U, 0x8A5B1EU, 0x8A6ACBU, 0x8A7220U, 0x8A84A4U, 0x8A9C4FU, 0x8AAD9AU, 0x8AB571U, + 0x8ACE33U, 0x8AD6D8U, 0x8AE70DU, 0x8AFFE6U, 0x8B0A05U, 0x8B12EEU, 0x8B233BU, 0x8B3BD0U, 0x8B4092U, 0x8B5879U, + 0x8B69ACU, 0x8B7147U, 0x8B87C3U, 0x8B9F28U, 0x8BAEFDU, 0x8BB616U, 0x8BCD54U, 0x8BD5BFU, 0x8BE46AU, 0x8BFC81U, + 0x8C0236U, 0x8C1ADDU, 0x8C2B08U, 0x8C33E3U, 0x8C48A1U, 0x8C504AU, 0x8C619FU, 0x8C7974U, 0x8C8FF0U, 0x8C971BU, + 0x8CA6CEU, 0x8CBE25U, 0x8CC567U, 0x8CDD8CU, 0x8CEC59U, 0x8CF4B2U, 0x8D0151U, 0x8D19BAU, 0x8D286FU, 0x8D3084U, + 0x8D4BC6U, 0x8D532DU, 0x8D62F8U, 0x8D7A13U, 0x8D8C97U, 0x8D947CU, 0x8DA5A9U, 0x8DBD42U, 0x8DC600U, 0x8DDEEBU, + 0x8DEF3EU, 0x8DF7D5U, 0x8E04FBU, 0x8E1C10U, 0x8E2DC5U, 0x8E352EU, 0x8E4E6CU, 0x8E5687U, 0x8E6752U, 0x8E7FB9U, + 0x8E893DU, 0x8E91D6U, 0x8EA003U, 0x8EB8E8U, 0x8EC3AAU, 0x8EDB41U, 0x8EEA94U, 0x8EF27FU, 0x8F079CU, 0x8F1F77U, + 0x8F2EA2U, 0x8F3649U, 0x8F4D0BU, 0x8F55E0U, 0x8F6435U, 0x8F7CDEU, 0x8F8A5AU, 0x8F92B1U, 0x8FA364U, 0x8FBB8FU, + 0x8FC0CDU, 0x8FD826U, 0x8FE9F3U, 0x8FF118U, 0x900BC1U, 0x90132AU, 0x9022FFU, 0x903A14U, 0x904156U, 0x9059BDU, + 0x906868U, 0x907083U, 0x908607U, 0x909EECU, 0x90AF39U, 0x90B7D2U, 0x90CC90U, 0x90D47BU, 0x90E5AEU, 0x90FD45U, + 0x9108A6U, 0x91104DU, 0x912198U, 0x913973U, 0x914231U, 0x915ADAU, 0x916B0FU, 0x9173E4U, 0x918560U, 0x919D8BU, + 0x91AC5EU, 0x91B4B5U, 0x91CFF7U, 0x91D71CU, 0x91E6C9U, 0x91FE22U, 0x920D0CU, 0x9215E7U, 0x922432U, 0x923CD9U, + 0x92479BU, 0x925F70U, 0x926EA5U, 0x92764EU, 0x9280CAU, 0x929821U, 0x92A9F4U, 0x92B11FU, 0x92CA5DU, 0x92D2B6U, + 0x92E363U, 0x92FB88U, 0x930E6BU, 0x931680U, 0x932755U, 0x933FBEU, 0x9344FCU, 0x935C17U, 0x936DC2U, 0x937529U, + 0x9383ADU, 0x939B46U, 0x93AA93U, 0x93B278U, 0x93C93AU, 0x93D1D1U, 0x93E004U, 0x93F8EFU, 0x940658U, 0x941EB3U, + 0x942F66U, 0x94378DU, 0x944CCFU, 0x945424U, 0x9465F1U, 0x947D1AU, 0x948B9EU, 0x949375U, 0x94A2A0U, 0x94BA4BU, + 0x94C109U, 0x94D9E2U, 0x94E837U, 0x94F0DCU, 0x95053FU, 0x951DD4U, 0x952C01U, 0x9534EAU, 0x954FA8U, 0x955743U, + 0x956696U, 0x957E7DU, 0x9588F9U, 0x959012U, 0x95A1C7U, 0x95B92CU, 0x95C26EU, 0x95DA85U, 0x95EB50U, 0x95F3BBU, + 0x960095U, 0x96187EU, 0x9629ABU, 0x963140U, 0x964A02U, 0x9652E9U, 0x96633CU, 0x967BD7U, 0x968D53U, 0x9695B8U, + 0x96A46DU, 0x96BC86U, 0x96C7C4U, 0x96DF2FU, 0x96EEFAU, 0x96F611U, 0x9703F2U, 0x971B19U, 0x972ACCU, 0x973227U, + 0x974965U, 0x97518EU, 0x97605BU, 0x9778B0U, 0x978E34U, 0x9796DFU, 0x97A70AU, 0x97BFE1U, 0x97C4A3U, 0x97DC48U, + 0x97ED9DU, 0x97F576U, 0x98081BU, 0x9810F0U, 0x982125U, 0x9839CEU, 0x98428CU, 0x985A67U, 0x986BB2U, 0x987359U, + 0x9885DDU, 0x989D36U, 0x98ACE3U, 0x98B408U, 0x98CF4AU, 0x98D7A1U, 0x98E674U, 0x98FE9FU, 0x990B7CU, 0x991397U, + 0x992242U, 0x993AA9U, 0x9941EBU, 0x995900U, 0x9968D5U, 0x99703EU, 0x9986BAU, 0x999E51U, 0x99AF84U, 0x99B76FU, + 0x99CC2DU, 0x99D4C6U, 0x99E513U, 0x99FDF8U, 0x9A0ED6U, 0x9A163DU, 0x9A27E8U, 0x9A3F03U, 0x9A4441U, 0x9A5CAAU, + 0x9A6D7FU, 0x9A7594U, 0x9A8310U, 0x9A9BFBU, 0x9AAA2EU, 0x9AB2C5U, 0x9AC987U, 0x9AD16CU, 0x9AE0B9U, 0x9AF852U, + 0x9B0DB1U, 0x9B155AU, 0x9B248FU, 0x9B3C64U, 0x9B4726U, 0x9B5FCDU, 0x9B6E18U, 0x9B76F3U, 0x9B8077U, 0x9B989CU, + 0x9BA949U, 0x9BB1A2U, 0x9BCAE0U, 0x9BD20BU, 0x9BE3DEU, 0x9BFB35U, 0x9C0582U, 0x9C1D69U, 0x9C2CBCU, 0x9C3457U, + 0x9C4F15U, 0x9C57FEU, 0x9C662BU, 0x9C7EC0U, 0x9C8844U, 0x9C90AFU, 0x9CA17AU, 0x9CB991U, 0x9CC2D3U, 0x9CDA38U, + 0x9CEBEDU, 0x9CF306U, 0x9D06E5U, 0x9D1E0EU, 0x9D2FDBU, 0x9D3730U, 0x9D4C72U, 0x9D5499U, 0x9D654CU, 0x9D7DA7U, + 0x9D8B23U, 0x9D93C8U, 0x9DA21DU, 0x9DBAF6U, 0x9DC1B4U, 0x9DD95FU, 0x9DE88AU, 0x9DF061U, 0x9E034FU, 0x9E1BA4U, + 0x9E2A71U, 0x9E329AU, 0x9E49D8U, 0x9E5133U, 0x9E60E6U, 0x9E780DU, 0x9E8E89U, 0x9E9662U, 0x9EA7B7U, 0x9EBF5CU, + 0x9EC41EU, 0x9EDCF5U, 0x9EED20U, 0x9EF5CBU, 0x9F0028U, 0x9F18C3U, 0x9F2916U, 0x9F31FDU, 0x9F4ABFU, 0x9F5254U, + 0x9F6381U, 0x9F7B6AU, 0x9F8DEEU, 0x9F9505U, 0x9FA4D0U, 0x9FBC3BU, 0x9FC779U, 0x9FDF92U, 0x9FEE47U, 0x9FF6ACU, + 0xA0031DU, 0xA01BF6U, 0xA02A23U, 0xA032C8U, 0xA0498AU, 0xA05161U, 0xA060B4U, 0xA0785FU, 0xA08EDBU, 0xA09630U, + 0xA0A7E5U, 0xA0BF0EU, 0xA0C44CU, 0xA0DCA7U, 0xA0ED72U, 0xA0F599U, 0xA1007AU, 0xA11891U, 0xA12944U, 0xA131AFU, + 0xA14AEDU, 0xA15206U, 0xA163D3U, 0xA17B38U, 0xA18DBCU, 0xA19557U, 0xA1A482U, 0xA1BC69U, 0xA1C72BU, 0xA1DFC0U, + 0xA1EE15U, 0xA1F6FEU, 0xA205D0U, 0xA21D3BU, 0xA22CEEU, 0xA23405U, 0xA24F47U, 0xA257ACU, 0xA26679U, 0xA27E92U, + 0xA28816U, 0xA290FDU, 0xA2A128U, 0xA2B9C3U, 0xA2C281U, 0xA2DA6AU, 0xA2EBBFU, 0xA2F354U, 0xA306B7U, 0xA31E5CU, + 0xA32F89U, 0xA33762U, 0xA34C20U, 0xA354CBU, 0xA3651EU, 0xA37DF5U, 0xA38B71U, 0xA3939AU, 0xA3A24FU, 0xA3BAA4U, + 0xA3C1E6U, 0xA3D90DU, 0xA3E8D8U, 0xA3F033U, 0xA40E84U, 0xA4166FU, 0xA427BAU, 0xA43F51U, 0xA44413U, 0xA45CF8U, + 0xA46D2DU, 0xA475C6U, 0xA48342U, 0xA49BA9U, 0xA4AA7CU, 0xA4B297U, 0xA4C9D5U, 0xA4D13EU, 0xA4E0EBU, 0xA4F800U, + 0xA50DE3U, 0xA51508U, 0xA524DDU, 0xA53C36U, 0xA54774U, 0xA55F9FU, 0xA56E4AU, 0xA576A1U, 0xA58025U, 0xA598CEU, + 0xA5A91BU, 0xA5B1F0U, 0xA5CAB2U, 0xA5D259U, 0xA5E38CU, 0xA5FB67U, 0xA60849U, 0xA610A2U, 0xA62177U, 0xA6399CU, + 0xA642DEU, 0xA65A35U, 0xA66BE0U, 0xA6730BU, 0xA6858FU, 0xA69D64U, 0xA6ACB1U, 0xA6B45AU, 0xA6CF18U, 0xA6D7F3U, + 0xA6E626U, 0xA6FECDU, 0xA70B2EU, 0xA713C5U, 0xA72210U, 0xA73AFBU, 0xA741B9U, 0xA75952U, 0xA76887U, 0xA7706CU, + 0xA786E8U, 0xA79E03U, 0xA7AFD6U, 0xA7B73DU, 0xA7CC7FU, 0xA7D494U, 0xA7E541U, 0xA7FDAAU, 0xA800C7U, 0xA8182CU, + 0xA829F9U, 0xA83112U, 0xA84A50U, 0xA852BBU, 0xA8636EU, 0xA87B85U, 0xA88D01U, 0xA895EAU, 0xA8A43FU, 0xA8BCD4U, + 0xA8C796U, 0xA8DF7DU, 0xA8EEA8U, 0xA8F643U, 0xA903A0U, 0xA91B4BU, 0xA92A9EU, 0xA93275U, 0xA94937U, 0xA951DCU, + 0xA96009U, 0xA978E2U, 0xA98E66U, 0xA9968DU, 0xA9A758U, 0xA9BFB3U, 0xA9C4F1U, 0xA9DC1AU, 0xA9EDCFU, 0xA9F524U, + 0xAA060AU, 0xAA1EE1U, 0xAA2F34U, 0xAA37DFU, 0xAA4C9DU, 0xAA5476U, 0xAA65A3U, 0xAA7D48U, 0xAA8BCCU, 0xAA9327U, + 0xAAA2F2U, 0xAABA19U, 0xAAC15BU, 0xAAD9B0U, 0xAAE865U, 0xAAF08EU, 0xAB056DU, 0xAB1D86U, 0xAB2C53U, 0xAB34B8U, + 0xAB4FFAU, 0xAB5711U, 0xAB66C4U, 0xAB7E2FU, 0xAB88ABU, 0xAB9040U, 0xABA195U, 0xABB97EU, 0xABC23CU, 0xABDAD7U, + 0xABEB02U, 0xABF3E9U, 0xAC0D5EU, 0xAC15B5U, 0xAC2460U, 0xAC3C8BU, 0xAC47C9U, 0xAC5F22U, 0xAC6EF7U, 0xAC761CU, + 0xAC8098U, 0xAC9873U, 0xACA9A6U, 0xACB14DU, 0xACCA0FU, 0xACD2E4U, 0xACE331U, 0xACFBDAU, 0xAD0E39U, 0xAD16D2U, + 0xAD2707U, 0xAD3FECU, 0xAD44AEU, 0xAD5C45U, 0xAD6D90U, 0xAD757BU, 0xAD83FFU, 0xAD9B14U, 0xADAAC1U, 0xADB22AU, + 0xADC968U, 0xADD183U, 0xADE056U, 0xADF8BDU, 0xAE0B93U, 0xAE1378U, 0xAE22ADU, 0xAE3A46U, 0xAE4104U, 0xAE59EFU, + 0xAE683AU, 0xAE70D1U, 0xAE8655U, 0xAE9EBEU, 0xAEAF6BU, 0xAEB780U, 0xAECCC2U, 0xAED429U, 0xAEE5FCU, 0xAEFD17U, + 0xAF08F4U, 0xAF101FU, 0xAF21CAU, 0xAF3921U, 0xAF4263U, 0xAF5A88U, 0xAF6B5DU, 0xAF73B6U, 0xAF8532U, 0xAF9DD9U, + 0xAFAC0CU, 0xAFB4E7U, 0xAFCFA5U, 0xAFD74EU, 0xAFE69BU, 0xAFFE70U, 0xB004A9U, 0xB01C42U, 0xB02D97U, 0xB0357CU, + 0xB04E3EU, 0xB056D5U, 0xB06700U, 0xB07FEBU, 0xB0896FU, 0xB09184U, 0xB0A051U, 0xB0B8BAU, 0xB0C3F8U, 0xB0DB13U, + 0xB0EAC6U, 0xB0F22DU, 0xB107CEU, 0xB11F25U, 0xB12EF0U, 0xB1361BU, 0xB14D59U, 0xB155B2U, 0xB16467U, 0xB17C8CU, + 0xB18A08U, 0xB192E3U, 0xB1A336U, 0xB1BBDDU, 0xB1C09FU, 0xB1D874U, 0xB1E9A1U, 0xB1F14AU, 0xB20264U, 0xB21A8FU, + 0xB22B5AU, 0xB233B1U, 0xB248F3U, 0xB25018U, 0xB261CDU, 0xB27926U, 0xB28FA2U, 0xB29749U, 0xB2A69CU, 0xB2BE77U, + 0xB2C535U, 0xB2DDDEU, 0xB2EC0BU, 0xB2F4E0U, 0xB30103U, 0xB319E8U, 0xB3283DU, 0xB330D6U, 0xB34B94U, 0xB3537FU, + 0xB362AAU, 0xB37A41U, 0xB38CC5U, 0xB3942EU, 0xB3A5FBU, 0xB3BD10U, 0xB3C652U, 0xB3DEB9U, 0xB3EF6CU, 0xB3F787U, + 0xB40930U, 0xB411DBU, 0xB4200EU, 0xB438E5U, 0xB443A7U, 0xB45B4CU, 0xB46A99U, 0xB47272U, 0xB484F6U, 0xB49C1DU, + 0xB4ADC8U, 0xB4B523U, 0xB4CE61U, 0xB4D68AU, 0xB4E75FU, 0xB4FFB4U, 0xB50A57U, 0xB512BCU, 0xB52369U, 0xB53B82U, + 0xB540C0U, 0xB5582BU, 0xB569FEU, 0xB57115U, 0xB58791U, 0xB59F7AU, 0xB5AEAFU, 0xB5B644U, 0xB5CD06U, 0xB5D5EDU, + 0xB5E438U, 0xB5FCD3U, 0xB60FFDU, 0xB61716U, 0xB626C3U, 0xB63E28U, 0xB6456AU, 0xB65D81U, 0xB66C54U, 0xB674BFU, + 0xB6823BU, 0xB69AD0U, 0xB6AB05U, 0xB6B3EEU, 0xB6C8ACU, 0xB6D047U, 0xB6E192U, 0xB6F979U, 0xB70C9AU, 0xB71471U, + 0xB725A4U, 0xB73D4FU, 0xB7460DU, 0xB75EE6U, 0xB76F33U, 0xB777D8U, 0xB7815CU, 0xB799B7U, 0xB7A862U, 0xB7B089U, + 0xB7CBCBU, 0xB7D320U, 0xB7E2F5U, 0xB7FA1EU, 0xB80773U, 0xB81F98U, 0xB82E4DU, 0xB836A6U, 0xB84DE4U, 0xB8550FU, + 0xB864DAU, 0xB87C31U, 0xB88AB5U, 0xB8925EU, 0xB8A38BU, 0xB8BB60U, 0xB8C022U, 0xB8D8C9U, 0xB8E91CU, 0xB8F1F7U, + 0xB90414U, 0xB91CFFU, 0xB92D2AU, 0xB935C1U, 0xB94E83U, 0xB95668U, 0xB967BDU, 0xB97F56U, 0xB989D2U, 0xB99139U, + 0xB9A0ECU, 0xB9B807U, 0xB9C345U, 0xB9DBAEU, 0xB9EA7BU, 0xB9F290U, 0xBA01BEU, 0xBA1955U, 0xBA2880U, 0xBA306BU, + 0xBA4B29U, 0xBA53C2U, 0xBA6217U, 0xBA7AFCU, 0xBA8C78U, 0xBA9493U, 0xBAA546U, 0xBABDADU, 0xBAC6EFU, 0xBADE04U, + 0xBAEFD1U, 0xBAF73AU, 0xBB02D9U, 0xBB1A32U, 0xBB2BE7U, 0xBB330CU, 0xBB484EU, 0xBB50A5U, 0xBB6170U, 0xBB799BU, + 0xBB8F1FU, 0xBB97F4U, 0xBBA621U, 0xBBBECAU, 0xBBC588U, 0xBBDD63U, 0xBBECB6U, 0xBBF45DU, 0xBC0AEAU, 0xBC1201U, + 0xBC23D4U, 0xBC3B3FU, 0xBC407DU, 0xBC5896U, 0xBC6943U, 0xBC71A8U, 0xBC872CU, 0xBC9FC7U, 0xBCAE12U, 0xBCB6F9U, + 0xBCCDBBU, 0xBCD550U, 0xBCE485U, 0xBCFC6EU, 0xBD098DU, 0xBD1166U, 0xBD20B3U, 0xBD3858U, 0xBD431AU, 0xBD5BF1U, + 0xBD6A24U, 0xBD72CFU, 0xBD844BU, 0xBD9CA0U, 0xBDAD75U, 0xBDB59EU, 0xBDCEDCU, 0xBDD637U, 0xBDE7E2U, 0xBDFF09U, + 0xBE0C27U, 0xBE14CCU, 0xBE2519U, 0xBE3DF2U, 0xBE46B0U, 0xBE5E5BU, 0xBE6F8EU, 0xBE7765U, 0xBE81E1U, 0xBE990AU, + 0xBEA8DFU, 0xBEB034U, 0xBECB76U, 0xBED39DU, 0xBEE248U, 0xBEFAA3U, 0xBF0F40U, 0xBF17ABU, 0xBF267EU, 0xBF3E95U, + 0xBF45D7U, 0xBF5D3CU, 0xBF6CE9U, 0xBF7402U, 0xBF8286U, 0xBF9A6DU, 0xBFABB8U, 0xBFB353U, 0xBFC811U, 0xBFD0FAU, + 0xBFE12FU, 0xBFF9C4U, 0xC00A4EU, 0xC012A5U, 0xC02370U, 0xC03B9BU, 0xC040D9U, 0xC05832U, 0xC069E7U, 0xC0710CU, + 0xC08788U, 0xC09F63U, 0xC0AEB6U, 0xC0B65DU, 0xC0CD1FU, 0xC0D5F4U, 0xC0E421U, 0xC0FCCAU, 0xC10929U, 0xC111C2U, + 0xC12017U, 0xC138FCU, 0xC143BEU, 0xC15B55U, 0xC16A80U, 0xC1726BU, 0xC184EFU, 0xC19C04U, 0xC1ADD1U, 0xC1B53AU, + 0xC1CE78U, 0xC1D693U, 0xC1E746U, 0xC1FFADU, 0xC20C83U, 0xC21468U, 0xC225BDU, 0xC23D56U, 0xC24614U, 0xC25EFFU, + 0xC26F2AU, 0xC277C1U, 0xC28145U, 0xC299AEU, 0xC2A87BU, 0xC2B090U, 0xC2CBD2U, 0xC2D339U, 0xC2E2ECU, 0xC2FA07U, + 0xC30FE4U, 0xC3170FU, 0xC326DAU, 0xC33E31U, 0xC34573U, 0xC35D98U, 0xC36C4DU, 0xC374A6U, 0xC38222U, 0xC39AC9U, + 0xC3AB1CU, 0xC3B3F7U, 0xC3C8B5U, 0xC3D05EU, 0xC3E18BU, 0xC3F960U, 0xC407D7U, 0xC41F3CU, 0xC42EE9U, 0xC43602U, + 0xC44D40U, 0xC455ABU, 0xC4647EU, 0xC47C95U, 0xC48A11U, 0xC492FAU, 0xC4A32FU, 0xC4BBC4U, 0xC4C086U, 0xC4D86DU, + 0xC4E9B8U, 0xC4F153U, 0xC504B0U, 0xC51C5BU, 0xC52D8EU, 0xC53565U, 0xC54E27U, 0xC556CCU, 0xC56719U, 0xC57FF2U, + 0xC58976U, 0xC5919DU, 0xC5A048U, 0xC5B8A3U, 0xC5C3E1U, 0xC5DB0AU, 0xC5EADFU, 0xC5F234U, 0xC6011AU, 0xC619F1U, + 0xC62824U, 0xC630CFU, 0xC64B8DU, 0xC65366U, 0xC662B3U, 0xC67A58U, 0xC68CDCU, 0xC69437U, 0xC6A5E2U, 0xC6BD09U, + 0xC6C64BU, 0xC6DEA0U, 0xC6EF75U, 0xC6F79EU, 0xC7027DU, 0xC71A96U, 0xC72B43U, 0xC733A8U, 0xC748EAU, 0xC75001U, + 0xC761D4U, 0xC7793FU, 0xC78FBBU, 0xC79750U, 0xC7A685U, 0xC7BE6EU, 0xC7C52CU, 0xC7DDC7U, 0xC7EC12U, 0xC7F4F9U, + 0xC80994U, 0xC8117FU, 0xC820AAU, 0xC83841U, 0xC84303U, 0xC85BE8U, 0xC86A3DU, 0xC872D6U, 0xC88452U, 0xC89CB9U, + 0xC8AD6CU, 0xC8B587U, 0xC8CEC5U, 0xC8D62EU, 0xC8E7FBU, 0xC8FF10U, 0xC90AF3U, 0xC91218U, 0xC923CDU, 0xC93B26U, + 0xC94064U, 0xC9588FU, 0xC9695AU, 0xC971B1U, 0xC98735U, 0xC99FDEU, 0xC9AE0BU, 0xC9B6E0U, 0xC9CDA2U, 0xC9D549U, + 0xC9E49CU, 0xC9FC77U, 0xCA0F59U, 0xCA17B2U, 0xCA2667U, 0xCA3E8CU, 0xCA45CEU, 0xCA5D25U, 0xCA6CF0U, 0xCA741BU, + 0xCA829FU, 0xCA9A74U, 0xCAABA1U, 0xCAB34AU, 0xCAC808U, 0xCAD0E3U, 0xCAE136U, 0xCAF9DDU, 0xCB0C3EU, 0xCB14D5U, + 0xCB2500U, 0xCB3DEBU, 0xCB46A9U, 0xCB5E42U, 0xCB6F97U, 0xCB777CU, 0xCB81F8U, 0xCB9913U, 0xCBA8C6U, 0xCBB02DU, + 0xCBCB6FU, 0xCBD384U, 0xCBE251U, 0xCBFABAU, 0xCC040DU, 0xCC1CE6U, 0xCC2D33U, 0xCC35D8U, 0xCC4E9AU, 0xCC5671U, + 0xCC67A4U, 0xCC7F4FU, 0xCC89CBU, 0xCC9120U, 0xCCA0F5U, 0xCCB81EU, 0xCCC35CU, 0xCCDBB7U, 0xCCEA62U, 0xCCF289U, + 0xCD076AU, 0xCD1F81U, 0xCD2E54U, 0xCD36BFU, 0xCD4DFDU, 0xCD5516U, 0xCD64C3U, 0xCD7C28U, 0xCD8AACU, 0xCD9247U, + 0xCDA392U, 0xCDBB79U, 0xCDC03BU, 0xCDD8D0U, 0xCDE905U, 0xCDF1EEU, 0xCE02C0U, 0xCE1A2BU, 0xCE2BFEU, 0xCE3315U, + 0xCE4857U, 0xCE50BCU, 0xCE6169U, 0xCE7982U, 0xCE8F06U, 0xCE97EDU, 0xCEA638U, 0xCEBED3U, 0xCEC591U, 0xCEDD7AU, + 0xCEECAFU, 0xCEF444U, 0xCF01A7U, 0xCF194CU, 0xCF2899U, 0xCF3072U, 0xCF4B30U, 0xCF53DBU, 0xCF620EU, 0xCF7AE5U, + 0xCF8C61U, 0xCF948AU, 0xCFA55FU, 0xCFBDB4U, 0xCFC6F6U, 0xCFDE1DU, 0xCFEFC8U, 0xCFF723U, 0xD00DFAU, 0xD01511U, + 0xD024C4U, 0xD03C2FU, 0xD0476DU, 0xD05F86U, 0xD06E53U, 0xD076B8U, 0xD0803CU, 0xD098D7U, 0xD0A902U, 0xD0B1E9U, + 0xD0CAABU, 0xD0D240U, 0xD0E395U, 0xD0FB7EU, 0xD10E9DU, 0xD11676U, 0xD127A3U, 0xD13F48U, 0xD1440AU, 0xD15CE1U, + 0xD16D34U, 0xD175DFU, 0xD1835BU, 0xD19BB0U, 0xD1AA65U, 0xD1B28EU, 0xD1C9CCU, 0xD1D127U, 0xD1E0F2U, 0xD1F819U, + 0xD20B37U, 0xD213DCU, 0xD22209U, 0xD23AE2U, 0xD241A0U, 0xD2594BU, 0xD2689EU, 0xD27075U, 0xD286F1U, 0xD29E1AU, + 0xD2AFCFU, 0xD2B724U, 0xD2CC66U, 0xD2D48DU, 0xD2E558U, 0xD2FDB3U, 0xD30850U, 0xD310BBU, 0xD3216EU, 0xD33985U, + 0xD342C7U, 0xD35A2CU, 0xD36BF9U, 0xD37312U, 0xD38596U, 0xD39D7DU, 0xD3ACA8U, 0xD3B443U, 0xD3CF01U, 0xD3D7EAU, + 0xD3E63FU, 0xD3FED4U, 0xD40063U, 0xD41888U, 0xD4295DU, 0xD431B6U, 0xD44AF4U, 0xD4521FU, 0xD463CAU, 0xD47B21U, + 0xD48DA5U, 0xD4954EU, 0xD4A49BU, 0xD4BC70U, 0xD4C732U, 0xD4DFD9U, 0xD4EE0CU, 0xD4F6E7U, 0xD50304U, 0xD51BEFU, + 0xD52A3AU, 0xD532D1U, 0xD54993U, 0xD55178U, 0xD560ADU, 0xD57846U, 0xD58EC2U, 0xD59629U, 0xD5A7FCU, 0xD5BF17U, + 0xD5C455U, 0xD5DCBEU, 0xD5ED6BU, 0xD5F580U, 0xD606AEU, 0xD61E45U, 0xD62F90U, 0xD6377BU, 0xD64C39U, 0xD654D2U, + 0xD66507U, 0xD67DECU, 0xD68B68U, 0xD69383U, 0xD6A256U, 0xD6BABDU, 0xD6C1FFU, 0xD6D914U, 0xD6E8C1U, 0xD6F02AU, + 0xD705C9U, 0xD71D22U, 0xD72CF7U, 0xD7341CU, 0xD74F5EU, 0xD757B5U, 0xD76660U, 0xD77E8BU, 0xD7880FU, 0xD790E4U, + 0xD7A131U, 0xD7B9DAU, 0xD7C298U, 0xD7DA73U, 0xD7EBA6U, 0xD7F34DU, 0xD80E20U, 0xD816CBU, 0xD8271EU, 0xD83FF5U, + 0xD844B7U, 0xD85C5CU, 0xD86D89U, 0xD87562U, 0xD883E6U, 0xD89B0DU, 0xD8AAD8U, 0xD8B233U, 0xD8C971U, 0xD8D19AU, + 0xD8E04FU, 0xD8F8A4U, 0xD90D47U, 0xD915ACU, 0xD92479U, 0xD93C92U, 0xD947D0U, 0xD95F3BU, 0xD96EEEU, 0xD97605U, + 0xD98081U, 0xD9986AU, 0xD9A9BFU, 0xD9B154U, 0xD9CA16U, 0xD9D2FDU, 0xD9E328U, 0xD9FBC3U, 0xDA08EDU, 0xDA1006U, + 0xDA21D3U, 0xDA3938U, 0xDA427AU, 0xDA5A91U, 0xDA6B44U, 0xDA73AFU, 0xDA852BU, 0xDA9DC0U, 0xDAAC15U, 0xDAB4FEU, + 0xDACFBCU, 0xDAD757U, 0xDAE682U, 0xDAFE69U, 0xDB0B8AU, 0xDB1361U, 0xDB22B4U, 0xDB3A5FU, 0xDB411DU, 0xDB59F6U, + 0xDB6823U, 0xDB70C8U, 0xDB864CU, 0xDB9EA7U, 0xDBAF72U, 0xDBB799U, 0xDBCCDBU, 0xDBD430U, 0xDBE5E5U, 0xDBFD0EU, + 0xDC03B9U, 0xDC1B52U, 0xDC2A87U, 0xDC326CU, 0xDC492EU, 0xDC51C5U, 0xDC6010U, 0xDC78FBU, 0xDC8E7FU, 0xDC9694U, + 0xDCA741U, 0xDCBFAAU, 0xDCC4E8U, 0xDCDC03U, 0xDCEDD6U, 0xDCF53DU, 0xDD00DEU, 0xDD1835U, 0xDD29E0U, 0xDD310BU, + 0xDD4A49U, 0xDD52A2U, 0xDD6377U, 0xDD7B9CU, 0xDD8D18U, 0xDD95F3U, 0xDDA426U, 0xDDBCCDU, 0xDDC78FU, 0xDDDF64U, + 0xDDEEB1U, 0xDDF65AU, 0xDE0574U, 0xDE1D9FU, 0xDE2C4AU, 0xDE34A1U, 0xDE4FE3U, 0xDE5708U, 0xDE66DDU, 0xDE7E36U, + 0xDE88B2U, 0xDE9059U, 0xDEA18CU, 0xDEB967U, 0xDEC225U, 0xDEDACEU, 0xDEEB1BU, 0xDEF3F0U, 0xDF0613U, 0xDF1EF8U, + 0xDF2F2DU, 0xDF37C6U, 0xDF4C84U, 0xDF546FU, 0xDF65BAU, 0xDF7D51U, 0xDF8BD5U, 0xDF933EU, 0xDFA2EBU, 0xDFBA00U, + 0xDFC142U, 0xDFD9A9U, 0xDFE87CU, 0xDFF097U, 0xE00526U, 0xE01DCDU, 0xE02C18U, 0xE034F3U, 0xE04FB1U, 0xE0575AU, + 0xE0668FU, 0xE07E64U, 0xE088E0U, 0xE0900BU, 0xE0A1DEU, 0xE0B935U, 0xE0C277U, 0xE0DA9CU, 0xE0EB49U, 0xE0F3A2U, + 0xE10641U, 0xE11EAAU, 0xE12F7FU, 0xE13794U, 0xE14CD6U, 0xE1543DU, 0xE165E8U, 0xE17D03U, 0xE18B87U, 0xE1936CU, + 0xE1A2B9U, 0xE1BA52U, 0xE1C110U, 0xE1D9FBU, 0xE1E82EU, 0xE1F0C5U, 0xE203EBU, 0xE21B00U, 0xE22AD5U, 0xE2323EU, + 0xE2497CU, 0xE25197U, 0xE26042U, 0xE278A9U, 0xE28E2DU, 0xE296C6U, 0xE2A713U, 0xE2BFF8U, 0xE2C4BAU, 0xE2DC51U, + 0xE2ED84U, 0xE2F56FU, 0xE3008CU, 0xE31867U, 0xE329B2U, 0xE33159U, 0xE34A1BU, 0xE352F0U, 0xE36325U, 0xE37BCEU, + 0xE38D4AU, 0xE395A1U, 0xE3A474U, 0xE3BC9FU, 0xE3C7DDU, 0xE3DF36U, 0xE3EEE3U, 0xE3F608U, 0xE408BFU, 0xE41054U, + 0xE42181U, 0xE4396AU, 0xE44228U, 0xE45AC3U, 0xE46B16U, 0xE473FDU, 0xE48579U, 0xE49D92U, 0xE4AC47U, 0xE4B4ACU, + 0xE4CFEEU, 0xE4D705U, 0xE4E6D0U, 0xE4FE3BU, 0xE50BD8U, 0xE51333U, 0xE522E6U, 0xE53A0DU, 0xE5414FU, 0xE559A4U, + 0xE56871U, 0xE5709AU, 0xE5861EU, 0xE59EF5U, 0xE5AF20U, 0xE5B7CBU, 0xE5CC89U, 0xE5D462U, 0xE5E5B7U, 0xE5FD5CU, + 0xE60E72U, 0xE61699U, 0xE6274CU, 0xE63FA7U, 0xE644E5U, 0xE65C0EU, 0xE66DDBU, 0xE67530U, 0xE683B4U, 0xE69B5FU, + 0xE6AA8AU, 0xE6B261U, 0xE6C923U, 0xE6D1C8U, 0xE6E01DU, 0xE6F8F6U, 0xE70D15U, 0xE715FEU, 0xE7242BU, 0xE73CC0U, + 0xE74782U, 0xE75F69U, 0xE76EBCU, 0xE77657U, 0xE780D3U, 0xE79838U, 0xE7A9EDU, 0xE7B106U, 0xE7CA44U, 0xE7D2AFU, + 0xE7E37AU, 0xE7FB91U, 0xE806FCU, 0xE81E17U, 0xE82FC2U, 0xE83729U, 0xE84C6BU, 0xE85480U, 0xE86555U, 0xE87DBEU, + 0xE88B3AU, 0xE893D1U, 0xE8A204U, 0xE8BAEFU, 0xE8C1ADU, 0xE8D946U, 0xE8E893U, 0xE8F078U, 0xE9059BU, 0xE91D70U, + 0xE92CA5U, 0xE9344EU, 0xE94F0CU, 0xE957E7U, 0xE96632U, 0xE97ED9U, 0xE9885DU, 0xE990B6U, 0xE9A163U, 0xE9B988U, + 0xE9C2CAU, 0xE9DA21U, 0xE9EBF4U, 0xE9F31FU, 0xEA0031U, 0xEA18DAU, 0xEA290FU, 0xEA31E4U, 0xEA4AA6U, 0xEA524DU, + 0xEA6398U, 0xEA7B73U, 0xEA8DF7U, 0xEA951CU, 0xEAA4C9U, 0xEABC22U, 0xEAC760U, 0xEADF8BU, 0xEAEE5EU, 0xEAF6B5U, + 0xEB0356U, 0xEB1BBDU, 0xEB2A68U, 0xEB3283U, 0xEB49C1U, 0xEB512AU, 0xEB60FFU, 0xEB7814U, 0xEB8E90U, 0xEB967BU, + 0xEBA7AEU, 0xEBBF45U, 0xEBC407U, 0xEBDCECU, 0xEBED39U, 0xEBF5D2U, 0xEC0B65U, 0xEC138EU, 0xEC225BU, 0xEC3AB0U, + 0xEC41F2U, 0xEC5919U, 0xEC68CCU, 0xEC7027U, 0xEC86A3U, 0xEC9E48U, 0xECAF9DU, 0xECB776U, 0xECCC34U, 0xECD4DFU, + 0xECE50AU, 0xECFDE1U, 0xED0802U, 0xED10E9U, 0xED213CU, 0xED39D7U, 0xED4295U, 0xED5A7EU, 0xED6BABU, 0xED7340U, + 0xED85C4U, 0xED9D2FU, 0xEDACFAU, 0xEDB411U, 0xEDCF53U, 0xEDD7B8U, 0xEDE66DU, 0xEDFE86U, 0xEE0DA8U, 0xEE1543U, + 0xEE2496U, 0xEE3C7DU, 0xEE473FU, 0xEE5FD4U, 0xEE6E01U, 0xEE76EAU, 0xEE806EU, 0xEE9885U, 0xEEA950U, 0xEEB1BBU, + 0xEECAF9U, 0xEED212U, 0xEEE3C7U, 0xEEFB2CU, 0xEF0ECFU, 0xEF1624U, 0xEF27F1U, 0xEF3F1AU, 0xEF4458U, 0xEF5CB3U, + 0xEF6D66U, 0xEF758DU, 0xEF8309U, 0xEF9BE2U, 0xEFAA37U, 0xEFB2DCU, 0xEFC99EU, 0xEFD175U, 0xEFE0A0U, 0xEFF84BU, + 0xF00292U, 0xF01A79U, 0xF02BACU, 0xF03347U, 0xF04805U, 0xF050EEU, 0xF0613BU, 0xF079D0U, 0xF08F54U, 0xF097BFU, + 0xF0A66AU, 0xF0BE81U, 0xF0C5C3U, 0xF0DD28U, 0xF0ECFDU, 0xF0F416U, 0xF101F5U, 0xF1191EU, 0xF128CBU, 0xF13020U, + 0xF14B62U, 0xF15389U, 0xF1625CU, 0xF17AB7U, 0xF18C33U, 0xF194D8U, 0xF1A50DU, 0xF1BDE6U, 0xF1C6A4U, 0xF1DE4FU, + 0xF1EF9AU, 0xF1F771U, 0xF2045FU, 0xF21CB4U, 0xF22D61U, 0xF2358AU, 0xF24EC8U, 0xF25623U, 0xF267F6U, 0xF27F1DU, + 0xF28999U, 0xF29172U, 0xF2A0A7U, 0xF2B84CU, 0xF2C30EU, 0xF2DBE5U, 0xF2EA30U, 0xF2F2DBU, 0xF30738U, 0xF31FD3U, + 0xF32E06U, 0xF336EDU, 0xF34DAFU, 0xF35544U, 0xF36491U, 0xF37C7AU, 0xF38AFEU, 0xF39215U, 0xF3A3C0U, 0xF3BB2BU, + 0xF3C069U, 0xF3D882U, 0xF3E957U, 0xF3F1BCU, 0xF40F0BU, 0xF417E0U, 0xF42635U, 0xF43EDEU, 0xF4459CU, 0xF45D77U, + 0xF46CA2U, 0xF47449U, 0xF482CDU, 0xF49A26U, 0xF4ABF3U, 0xF4B318U, 0xF4C85AU, 0xF4D0B1U, 0xF4E164U, 0xF4F98FU, + 0xF50C6CU, 0xF51487U, 0xF52552U, 0xF53DB9U, 0xF546FBU, 0xF55E10U, 0xF56FC5U, 0xF5772EU, 0xF581AAU, 0xF59941U, + 0xF5A894U, 0xF5B07FU, 0xF5CB3DU, 0xF5D3D6U, 0xF5E203U, 0xF5FAE8U, 0xF609C6U, 0xF6112DU, 0xF620F8U, 0xF63813U, + 0xF64351U, 0xF65BBAU, 0xF66A6FU, 0xF67284U, 0xF68400U, 0xF69CEBU, 0xF6AD3EU, 0xF6B5D5U, 0xF6CE97U, 0xF6D67CU, + 0xF6E7A9U, 0xF6FF42U, 0xF70AA1U, 0xF7124AU, 0xF7239FU, 0xF73B74U, 0xF74036U, 0xF758DDU, 0xF76908U, 0xF771E3U, + 0xF78767U, 0xF79F8CU, 0xF7AE59U, 0xF7B6B2U, 0xF7CDF0U, 0xF7D51BU, 0xF7E4CEU, 0xF7FC25U, 0xF80148U, 0xF819A3U, + 0xF82876U, 0xF8309DU, 0xF84BDFU, 0xF85334U, 0xF862E1U, 0xF87A0AU, 0xF88C8EU, 0xF89465U, 0xF8A5B0U, 0xF8BD5BU, + 0xF8C619U, 0xF8DEF2U, 0xF8EF27U, 0xF8F7CCU, 0xF9022FU, 0xF91AC4U, 0xF92B11U, 0xF933FAU, 0xF948B8U, 0xF95053U, + 0xF96186U, 0xF9796DU, 0xF98FE9U, 0xF99702U, 0xF9A6D7U, 0xF9BE3CU, 0xF9C57EU, 0xF9DD95U, 0xF9EC40U, 0xF9F4ABU, + 0xFA0785U, 0xFA1F6EU, 0xFA2EBBU, 0xFA3650U, 0xFA4D12U, 0xFA55F9U, 0xFA642CU, 0xFA7CC7U, 0xFA8A43U, 0xFA92A8U, + 0xFAA37DU, 0xFABB96U, 0xFAC0D4U, 0xFAD83FU, 0xFAE9EAU, 0xFAF101U, 0xFB04E2U, 0xFB1C09U, 0xFB2DDCU, 0xFB3537U, + 0xFB4E75U, 0xFB569EU, 0xFB674BU, 0xFB7FA0U, 0xFB8924U, 0xFB91CFU, 0xFBA01AU, 0xFBB8F1U, 0xFBC3B3U, 0xFBDB58U, + 0xFBEA8DU, 0xFBF266U, 0xFC0CD1U, 0xFC143AU, 0xFC25EFU, 0xFC3D04U, 0xFC4646U, 0xFC5EADU, 0xFC6F78U, 0xFC7793U, + 0xFC8117U, 0xFC99FCU, 0xFCA829U, 0xFCB0C2U, 0xFCCB80U, 0xFCD36BU, 0xFCE2BEU, 0xFCFA55U, 0xFD0FB6U, 0xFD175DU, + 0xFD2688U, 0xFD3E63U, 0xFD4521U, 0xFD5DCAU, 0xFD6C1FU, 0xFD74F4U, 0xFD8270U, 0xFD9A9BU, 0xFDAB4EU, 0xFDB3A5U, + 0xFDC8E7U, 0xFDD00CU, 0xFDE1D9U, 0xFDF932U, 0xFE0A1CU, 0xFE12F7U, 0xFE2322U, 0xFE3BC9U, 0xFE408BU, 0xFE5860U, + 0xFE69B5U, 0xFE715EU, 0xFE87DAU, 0xFE9F31U, 0xFEAEE4U, 0xFEB60FU, 0xFECD4DU, 0xFED5A6U, 0xFEE473U, 0xFEFC98U, + 0xFF097BU, 0xFF1190U, 0xFF2045U, 0xFF38AEU, 0xFF43ECU, 0xFF5B07U, 0xFF6AD2U, 0xFF7239U, 0xFF84BDU, 0xFF9C56U, + 0xFFAD83U, 0xFFB568U, 0xFFCE2AU, 0xFFD6C1U, 0xFFE714U, 0xFFFFFFU +}; + +static const unsigned int DECODING_TABLE_23127[] = +{ + 0x000000U, 0x000001U, 0x000002U, 0x000003U, 0x000004U, 0x000005U, 0x000006U, 0x000007U, 0x000008U, 0x000009U, + 0x00000AU, 0x00000BU, 0x00000CU, 0x00000DU, 0x00000EU, 0x024020U, 0x000010U, 0x000011U, 0x000012U, 0x000013U, + 0x000014U, 0x000015U, 0x000016U, 0x412000U, 0x000018U, 0x000019U, 0x00001AU, 0x180800U, 0x00001CU, 0x200300U, + 0x048040U, 0x001480U, 0x000020U, 0x000021U, 0x000022U, 0x000023U, 0x000024U, 0x000025U, 0x000026U, 0x024008U, + 0x000028U, 0x000029U, 0x00002AU, 0x024004U, 0x00002CU, 0x024002U, 0x024001U, 0x024000U, 0x000030U, 0x000031U, + 0x000032U, 0x008180U, 0x000034U, 0x000C40U, 0x301000U, 0x0C0200U, 0x000038U, 0x043000U, 0x400600U, 0x210040U, + 0x090080U, 0x508000U, 0x002900U, 0x024010U, 0x000040U, 0x000041U, 0x000042U, 0x000043U, 0x000044U, 0x000045U, + 0x000046U, 0x280080U, 0x000048U, 0x000049U, 0x00004AU, 0x002500U, 0x00004CU, 0x111000U, 0x048010U, 0x400A00U, + 0x000050U, 0x000051U, 0x000052U, 0x021200U, 0x000054U, 0x000C20U, 0x048008U, 0x104100U, 0x000058U, 0x404080U, + 0x048004U, 0x210020U, 0x048002U, 0x0A2000U, 0x048000U, 0x048001U, 0x000060U, 0x000061U, 0x000062U, 0x540000U, + 0x000064U, 0x000C10U, 0x010300U, 0x00B000U, 0x000068U, 0x088200U, 0x001880U, 0x210010U, 0x602000U, 0x040180U, + 0x180400U, 0x024040U, 0x000070U, 0x000C04U, 0x086000U, 0x210008U, 0x000C01U, 0x000C00U, 0x420080U, 0x000C02U, + 0x120100U, 0x210002U, 0x210001U, 0x210000U, 0x005200U, 0x000C08U, 0x048020U, 0x210004U, 0x000080U, 0x000081U, + 0x000082U, 0x000083U, 0x000084U, 0x000085U, 0x000086U, 0x280040U, 0x000088U, 0x000089U, 0x00008AU, 0x050200U, + 0x00008CU, 0x00A800U, 0x500100U, 0x001410U, 0x000090U, 0x000091U, 0x000092U, 0x008120U, 0x000094U, 0x160000U, + 0x004A00U, 0x001408U, 0x000098U, 0x404040U, 0x222000U, 0x001404U, 0x090020U, 0x001402U, 0x001401U, 0x001400U, + 0x0000A0U, 0x0000A1U, 0x0000A2U, 0x008110U, 0x0000A4U, 0x401200U, 0x042400U, 0x110800U, 0x0000A8U, 0x300400U, + 0x001840U, 0x482000U, 0x090010U, 0x040140U, 0x208200U, 0x024080U, 0x0000B0U, 0x008102U, 0x008101U, 0x008100U, + 0x090008U, 0x206000U, 0x420040U, 0x008104U, 0x090004U, 0x020A00U, 0x144000U, 0x008108U, 0x090000U, 0x090001U, + 0x090002U, 0x001420U, 0x0000C0U, 0x0000C1U, 0x0000C2U, 0x280004U, 0x0000C4U, 0x280002U, 0x280001U, 0x280000U, + 0x0000C8U, 0x404010U, 0x001820U, 0x128000U, 0x020600U, 0x040120U, 0x016000U, 0x280008U, 0x0000D0U, 0x404008U, + 0x110400U, 0x042800U, 0x003100U, 0x018200U, 0x420020U, 0x280010U, 0x404001U, 0x404000U, 0x080300U, 0x404002U, + 0x300800U, 0x404004U, 0x048080U, 0x001440U, 0x0000E0U, 0x032000U, 0x001808U, 0x004600U, 0x10C000U, 0x040108U, + 0x420010U, 0x280020U, 0x001802U, 0x040104U, 0x001800U, 0x001801U, 0x040101U, 0x040100U, 0x001804U, 0x040102U, + 0x240200U, 0x181000U, 0x420004U, 0x008140U, 0x420002U, 0x000C80U, 0x420000U, 0x420001U, 0x00A400U, 0x404020U, + 0x001810U, 0x210080U, 0x090040U, 0x040110U, 0x420008U, 0x102200U, 0x000100U, 0x000101U, 0x000102U, 0x000103U, + 0x000104U, 0x000105U, 0x000106U, 0x041800U, 0x000108U, 0x000109U, 0x00010AU, 0x002440U, 0x00010CU, 0x200210U, + 0x500080U, 0x098000U, 0x000110U, 0x000111U, 0x000112U, 0x0080A0U, 0x000114U, 0x200208U, 0x0A0400U, 0x104040U, + 0x000118U, 0x200204U, 0x015000U, 0x460000U, 0x200201U, 0x200200U, 0x002820U, 0x200202U, 0x000120U, 0x000121U, + 0x000122U, 0x008090U, 0x000124U, 0x182000U, 0x010240U, 0x600400U, 0x000128U, 0x410800U, 0x2C0000U, 0x101200U, + 0x009400U, 0x0400C0U, 0x002810U, 0x024100U, 0x000130U, 0x008082U, 0x008081U, 0x008080U, 0x444000U, 0x031000U, + 0x002808U, 0x008084U, 0x120040U, 0x084400U, 0x002804U, 0x008088U, 0x002802U, 0x200220U, 0x002800U, 0x002801U, + 0x000140U, 0x000141U, 0x000142U, 0x002408U, 0x000144U, 0x428000U, 0x010220U, 0x104010U, 0x000148U, 0x002402U, + 0x002401U, 0x002400U, 0x084800U, 0x0400A0U, 0x221000U, 0x002404U, 0x000150U, 0x0D0000U, 0x600800U, 0x104004U, + 0x003080U, 0x104002U, 0x104001U, 0x104000U, 0x120020U, 0x009800U, 0x080280U, 0x002410U, 0x410400U, 0x200240U, + 0x048100U, 0x104008U, 0x000160U, 0x205000U, 0x010204U, 0x0A0800U, 0x010202U, 0x040088U, 0x010200U, 0x010201U, + 0x120010U, 0x040084U, 0x40C000U, 0x002420U, 0x040081U, 0x040080U, 0x010208U, 0x040082U, 0x120008U, 0x402200U, + 0x041400U, 0x0080C0U, 0x288000U, 0x000D00U, 0x010210U, 0x104020U, 0x120000U, 0x120001U, 0x120002U, 0x210100U, + 0x120004U, 0x040090U, 0x002840U, 0x481000U, 0x000180U, 0x000181U, 0x000182U, 0x008030U, 0x000184U, 0x014400U, + 0x500008U, 0x022200U, 0x000188U, 0x0A1000U, 0x500004U, 0x204800U, 0x500002U, 0x040060U, 0x500000U, 0x500001U, + 0x000190U, 0x008022U, 0x008021U, 0x008020U, 0x003040U, 0x480800U, 0x250000U, 0x008024U, 0x040C00U, 0x112000U, + 0x080240U, 0x008028U, 0x02C000U, 0x200280U, 0x500010U, 0x001500U, 0x0001A0U, 0x008012U, 0x008011U, 0x008010U, + 0x220800U, 0x040048U, 0x085000U, 0x008014U, 0x006200U, 0x040044U, 0x030400U, 0x008018U, 0x040041U, 0x040040U, + 0x500020U, 0x040042U, 0x008003U, 0x008002U, 0x008001U, 0x008000U, 0x100600U, 0x008006U, 0x008005U, 0x008004U, + 0x601000U, 0x00800AU, 0x008009U, 0x008008U, 0x090100U, 0x040050U, 0x002880U, 0x00800CU, 0x0001C0U, 0x100A00U, + 0x064000U, 0x411000U, 0x003010U, 0x040028U, 0x008C00U, 0x280100U, 0x218000U, 0x040024U, 0x080210U, 0x002480U, + 0x040021U, 0x040020U, 0x500040U, 0x040022U, 0x003004U, 0x220400U, 0x080208U, 0x008060U, 0x003000U, 0x003001U, + 0x003002U, 0x104080U, 0x080202U, 0x404100U, 0x080200U, 0x080201U, 0x003008U, 0x040030U, 0x080204U, 0x030800U, + 0x480400U, 0x04000CU, 0x302000U, 0x008050U, 0x040009U, 0x040008U, 0x010280U, 0x04000AU, 0x040005U, 0x040004U, + 0x001900U, 0x040006U, 0x040001U, 0x040000U, 0x040003U, 0x040002U, 0x014800U, 0x008042U, 0x008041U, 0x008040U, + 0x003020U, 0x040018U, 0x420100U, 0x008044U, 0x120080U, 0x040014U, 0x080220U, 0x008048U, 0x040011U, 0x040010U, + 0x204400U, 0x040012U, 0x000200U, 0x000201U, 0x000202U, 0x000203U, 0x000204U, 0x000205U, 0x000206U, 0x108400U, + 0x000208U, 0x000209U, 0x00020AU, 0x050080U, 0x00020CU, 0x200110U, 0x083000U, 0x400840U, 0x000210U, 0x000211U, + 0x000212U, 0x021040U, 0x000214U, 0x200108U, 0x004880U, 0x0C0020U, 0x000218U, 0x200104U, 0x400420U, 0x00E000U, + 0x200101U, 0x200100U, 0x130000U, 0x200102U, 0x000220U, 0x000221U, 0x000222U, 0x202800U, 0x000224U, 0x401080U, + 0x010140U, 0x0C0010U, 0x000228U, 0x088040U, 0x400410U, 0x101100U, 0x140800U, 0x012400U, 0x208080U, 0x024200U, + 0x000230U, 0x114000U, 0x400408U, 0x0C0004U, 0x02A000U, 0x0C0002U, 0x0C0001U, 0x0C0000U, 0x400402U, 0x020880U, + 0x400400U, 0x400401U, 0x005040U, 0x200120U, 0x400404U, 0x0C0008U, 0x000240U, 0x000241U, 0x000242U, 0x021010U, + 0x000244U, 0x046000U, 0x010120U, 0x400808U, 0x000248U, 0x088020U, 0x304000U, 0x400804U, 0x020480U, 0x400802U, + 0x400801U, 0x400800U, 0x000250U, 0x021002U, 0x021001U, 0x021000U, 0x580000U, 0x018080U, 0x202400U, 0x021004U, + 0x012800U, 0x140400U, 0x080180U, 0x021008U, 0x005020U, 0x200140U, 0x048200U, 0x400810U, 0x000260U, 0x088008U, + 0x010104U, 0x004480U, 0x010102U, 0x320000U, 0x010100U, 0x010101U, 0x088001U, 0x088000U, 0x062000U, 0x088002U, + 0x005010U, 0x088004U, 0x010108U, 0x400820U, 0x240080U, 0x402100U, 0x108800U, 0x021020U, 0x005008U, 0x000E00U, + 0x010110U, 0x0C0040U, 0x005004U, 0x088010U, 0x400440U, 0x210200U, 0x005000U, 0x005001U, 0x005002U, 0x102080U, + 0x000280U, 0x000281U, 0x000282U, 0x050008U, 0x000284U, 0x401020U, 0x004810U, 0x022100U, 0x000288U, 0x050002U, + 0x050001U, 0x050000U, 0x020440U, 0x184000U, 0x208020U, 0x050004U, 0x000290U, 0x082400U, 0x004804U, 0x700000U, + 0x004802U, 0x018040U, 0x004800U, 0x004801U, 0x109000U, 0x020820U, 0x080140U, 0x050010U, 0x442000U, 0x200180U, + 0x004808U, 0x001600U, 0x0002A0U, 0x401004U, 0x1A0000U, 0x004440U, 0x401001U, 0x401000U, 0x208008U, 0x401002U, + 0x006100U, 0x020810U, 0x208004U, 0x050020U, 0x208002U, 0x401008U, 0x208000U, 0x208001U, 0x240040U, 0x020808U, + 0x013000U, 0x008300U, 0x100500U, 0x401010U, 0x004820U, 0x0C0080U, 0x020801U, 0x020800U, 0x400480U, 0x020802U, + 0x090200U, 0x020804U, 0x208010U, 0x102040U, 0x0002C0U, 0x100900U, 0x40A000U, 0x004420U, 0x020408U, 0x018010U, + 0x141000U, 0x280200U, 0x020404U, 0x203000U, 0x080110U, 0x050040U, 0x020400U, 0x020401U, 0x020402U, 0x400880U, + 0x240020U, 0x018004U, 0x080108U, 0x021080U, 0x018001U, 0x018000U, 0x004840U, 0x018002U, 0x080102U, 0x404200U, + 0x080100U, 0x080101U, 0x020410U, 0x018008U, 0x080104U, 0x102020U, 0x240010U, 0x004402U, 0x004401U, 0x004400U, + 0x082800U, 0x401040U, 0x010180U, 0x004404U, 0x510000U, 0x088080U, 0x001A00U, 0x004408U, 0x020420U, 0x040300U, + 0x208040U, 0x102010U, 0x240000U, 0x240001U, 0x240002U, 0x004410U, 0x240004U, 0x018020U, 0x420200U, 0x102008U, + 0x240008U, 0x020840U, 0x080120U, 0x102004U, 0x005080U, 0x102002U, 0x102001U, 0x102000U, 0x000300U, 0x000301U, + 0x000302U, 0x484000U, 0x000304U, 0x200018U, 0x010060U, 0x022080U, 0x000308U, 0x200014U, 0x028800U, 0x101020U, + 0x200011U, 0x200010U, 0x044400U, 0x200012U, 0x000310U, 0x20000CU, 0x142000U, 0x010C00U, 0x200009U, 0x200008U, + 0x409000U, 0x20000AU, 0x200005U, 0x200004U, 0x0800C0U, 0x200006U, 0x200001U, 0x200000U, 0x200003U, 0x200002U, + 0x000320U, 0x060400U, 0x010044U, 0x101008U, 0x010042U, 0x00C800U, 0x010040U, 0x010041U, 0x006080U, 0x101002U, + 0x101001U, 0x101000U, 0x4A0000U, 0x200030U, 0x010048U, 0x101004U, 0x081800U, 0x402040U, 0x224000U, 0x008280U, + 0x100480U, 0x200028U, 0x010050U, 0x0C0100U, 0x058000U, 0x200024U, 0x400500U, 0x101010U, 0x200021U, 0x200020U, + 0x002A00U, 0x200022U, 0x000340U, 0x100880U, 0x010024U, 0x248000U, 0x010022U, 0x081400U, 0x010020U, 0x010021U, + 0x441000U, 0x034000U, 0x080090U, 0x002600U, 0x10A000U, 0x200050U, 0x010028U, 0x400900U, 0x00C400U, 0x402020U, + 0x080088U, 0x021100U, 0x060800U, 0x200048U, 0x010030U, 0x104200U, 0x080082U, 0x200044U, 0x080080U, 0x080081U, + 0x200041U, 0x200040U, 0x080084U, 0x200042U, 0x010006U, 0x402010U, 0x010004U, 0x010005U, 0x010002U, 0x010003U, + 0x010000U, 0x010001U, 0x200C00U, 0x088100U, 0x01000CU, 0x101040U, 0x01000AU, 0x040280U, 0x010008U, 0x010009U, + 0x402001U, 0x402000U, 0x010014U, 0x402002U, 0x010012U, 0x402004U, 0x010010U, 0x010011U, 0x120200U, 0x402008U, + 0x0800A0U, 0x044800U, 0x005100U, 0x200060U, 0x010018U, 0x028400U, 0x000380U, 0x100840U, 0x201400U, 0x022004U, + 0x0C8000U, 0x022002U, 0x022001U, 0x022000U, 0x006020U, 0x408400U, 0x080050U, 0x050100U, 0x011800U, 0x200090U, + 0x500200U, 0x022008U, 0x430000U, 0x045000U, 0x080048U, 0x008220U, 0x100420U, 0x200088U, 0x004900U, 0x022010U, + 0x080042U, 0x200084U, 0x080040U, 0x080041U, 0x200081U, 0x200080U, 0x080044U, 0x200082U, 0x006008U, 0x290000U, + 0x440800U, 0x008210U, 0x100410U, 0x401100U, 0x0100C0U, 0x022020U, 0x006000U, 0x006001U, 0x006002U, 0x101080U, + 0x006004U, 0x040240U, 0x208100U, 0x080C00U, 0x100404U, 0x008202U, 0x008201U, 0x008200U, 0x100400U, 0x100401U, + 0x100402U, 0x008204U, 0x006010U, 0x020900U, 0x080060U, 0x008208U, 0x100408U, 0x2000A0U, 0x061000U, 0x414000U, + 0x100801U, 0x100800U, 0x080018U, 0x100802U, 0x604000U, 0x100804U, 0x0100A0U, 0x022040U, 0x080012U, 0x100808U, + 0x080010U, 0x080011U, 0x020500U, 0x040220U, 0x080014U, 0x00D000U, 0x08000AU, 0x100810U, 0x080008U, 0x080009U, + 0x003200U, 0x018100U, 0x08000CU, 0x440400U, 0x080002U, 0x080003U, 0x080000U, 0x080001U, 0x080006U, 0x2000C0U, + 0x080004U, 0x080005U, 0x029000U, 0x100820U, 0x010084U, 0x004500U, 0x010082U, 0x040208U, 0x010080U, 0x010081U, + 0x006040U, 0x040204U, 0x080030U, 0x620000U, 0x040201U, 0x040200U, 0x010088U, 0x040202U, 0x240100U, 0x402080U, + 0x080028U, 0x008240U, 0x100440U, 0x0A4000U, 0x010090U, 0x201800U, 0x080022U, 0x011400U, 0x080020U, 0x080021U, + 0x408800U, 0x040210U, 0x080024U, 0x102100U, 0x000400U, 0x000401U, 0x000402U, 0x000403U, 0x000404U, 0x000405U, + 0x000406U, 0x108200U, 0x000408U, 0x000409U, 0x00040AU, 0x002140U, 0x00040CU, 0x4C0000U, 0x210800U, 0x001090U, + 0x000410U, 0x000411U, 0x000412U, 0x244000U, 0x000414U, 0x000860U, 0x0A0100U, 0x001088U, 0x000418U, 0x038000U, + 0x400220U, 0x001084U, 0x106000U, 0x001082U, 0x001081U, 0x001080U, 0x000420U, 0x000421U, 0x000422U, 0x091000U, + 0x000424U, 0x000850U, 0x042080U, 0x600100U, 0x000428U, 0x300080U, 0x400210U, 0x048800U, 0x009100U, 0x012200U, + 0x180040U, 0x024400U, 0x000430U, 0x000844U, 0x400208U, 0x122000U, 0x000841U, 0x000840U, 0x01C000U, 0x000842U, + 0x400202U, 0x084100U, 0x400200U, 0x400201U, 0x260000U, 0x000848U, 0x400204U, 0x0010A0U, 0x000440U, 0x000441U, + 0x000442U, 0x002108U, 0x000444U, 0x000830U, 0x405000U, 0x070000U, 0x000448U, 0x002102U, 0x002101U, 0x002100U, + 0x020280U, 0x20C000U, 0x180020U, 0x002104U, 0x000450U, 0x000824U, 0x110080U, 0x488000U, 0x000821U, 0x000820U, + 0x202200U, 0x000822U, 0x281000U, 0x140200U, 0x024800U, 0x002110U, 0x410100U, 0x000828U, 0x048400U, 0x0010C0U, + 0x000460U, 0x000814U, 0x228000U, 0x004280U, 0x000811U, 0x000810U, 0x180008U, 0x000812U, 0x054000U, 0x421000U, + 0x180004U, 0x002120U, 0x180002U, 0x000818U, 0x180000U, 0x180001U, 0x000805U, 0x000804U, 0x041100U, 0x000806U, + 0x000801U, 0x000800U, 0x000803U, 0x000802U, 0x00A080U, 0x00080CU, 0x400240U, 0x210400U, 0x000809U, 0x000808U, + 0x180010U, 0x00080AU, 0x000480U, 0x000481U, 0x000482U, 0x420800U, 0x000484U, 0x014100U, 0x042020U, 0x001018U, + 0x000488U, 0x300020U, 0x08C000U, 0x001014U, 0x020240U, 0x001012U, 0x001011U, 0x001010U, 0x000490U, 0x082200U, + 0x110040U, 0x00100CU, 0x608000U, 0x00100AU, 0x001009U, 0x001008U, 0x040900U, 0x001006U, 0x001005U, 0x001004U, + 0x001003U, 0x001002U, 0x001001U, 0x001000U, 0x0004A0U, 0x300008U, 0x042004U, 0x004240U, 0x042002U, 0x0A8000U, + 0x042000U, 0x042001U, 0x300001U, 0x300000U, 0x030100U, 0x300002U, 0x404800U, 0x300004U, 0x042008U, 0x001030U, + 0x025000U, 0x450000U, 0x280800U, 0x008500U, 0x100300U, 0x0008C0U, 0x042010U, 0x001028U, 0x00A040U, 0x300010U, + 0x400280U, 0x001024U, 0x090400U, 0x001022U, 0x001021U, 0x001020U, 0x0004C0U, 0x049000U, 0x110010U, 0x004220U, + 0x020208U, 0x502000U, 0x008900U, 0x280400U, 0x020204U, 0x090800U, 0x640000U, 0x002180U, 0x020200U, 0x020201U, + 0x020202U, 0x001050U, 0x110002U, 0x220100U, 0x110000U, 0x110001U, 0x0C4000U, 0x0008A0U, 0x110004U, 0x001048U, + 0x00A020U, 0x404400U, 0x110008U, 0x001044U, 0x020210U, 0x001042U, 0x001041U, 0x001040U, 0x480100U, 0x004202U, + 0x004201U, 0x004200U, 0x211000U, 0x000890U, 0x042040U, 0x004204U, 0x00A010U, 0x300040U, 0x001C00U, 0x004208U, + 0x020220U, 0x040500U, 0x180080U, 0x418000U, 0x00A008U, 0x000884U, 0x110020U, 0x004210U, 0x000881U, 0x000880U, + 0x420400U, 0x000882U, 0x00A000U, 0x00A001U, 0x00A002U, 0x0E0000U, 0x00A004U, 0x000888U, 0x204100U, 0x001060U, + 0x000500U, 0x000501U, 0x000502U, 0x002048U, 0x000504U, 0x014080U, 0x0A0010U, 0x600020U, 0x000508U, 0x002042U, + 0x002041U, 0x002040U, 0x009020U, 0x120800U, 0x044200U, 0x002044U, 0x000510U, 0x501000U, 0x0A0004U, 0x010A00U, + 0x0A0002U, 0x04A000U, 0x0A0000U, 0x0A0001U, 0x040880U, 0x084020U, 0x308000U, 0x002050U, 0x410040U, 0x200600U, + 0x0A0008U, 0x001180U, 0x000520U, 0x060200U, 0x104800U, 0x600004U, 0x009008U, 0x600002U, 0x600001U, 0x600000U, + 0x009004U, 0x084010U, 0x030080U, 0x002060U, 0x009000U, 0x009001U, 0x009002U, 0x600008U, 0x212000U, 0x084008U, + 0x041040U, 0x008480U, 0x100280U, 0x000940U, 0x0A0020U, 0x600010U, 0x084001U, 0x084000U, 0x400300U, 0x084002U, + 0x009010U, 0x084004U, 0x002C00U, 0x150000U, 0x000540U, 0x00200AU, 0x002009U, 0x002008U, 0x340000U, 0x081200U, + 0x008880U, 0x00200CU, 0x002003U, 0x002002U, 0x002001U, 0x002000U, 0x410010U, 0x002006U, 0x002005U, 0x002004U, + 0x00C200U, 0x220080U, 0x041020U, 0x002018U, 0x410008U, 0x000920U, 0x0A0040U, 0x104400U, 0x410004U, 0x002012U, + 0x002011U, 0x002010U, 0x410000U, 0x410001U, 0x410002U, 0x002014U, 0x480080U, 0x118000U, 0x041010U, 0x002028U, + 0x026000U, 0x000910U, 0x010600U, 0x600040U, 0x200A00U, 0x002022U, 0x002021U, 0x002020U, 0x009040U, 0x040480U, + 0x180100U, 0x002024U, 0x041002U, 0x000904U, 0x041000U, 0x041001U, 0x000901U, 0x000900U, 0x041004U, 0x000902U, + 0x120400U, 0x084040U, 0x041008U, 0x002030U, 0x410020U, 0x000908U, 0x204080U, 0x028200U, 0x000580U, 0x014004U, + 0x201200U, 0x1C0000U, 0x014001U, 0x014000U, 0x008840U, 0x014002U, 0x040810U, 0x408200U, 0x030020U, 0x0020C0U, + 0x282000U, 0x014008U, 0x500400U, 0x001110U, 0x040808U, 0x220040U, 0x406000U, 0x008420U, 0x100220U, 0x014010U, + 0x0A0080U, 0x001108U, 0x040800U, 0x040801U, 0x040802U, 0x001104U, 0x040804U, 0x001102U, 0x001101U, 0x001100U, + 0x480040U, 0x003800U, 0x030008U, 0x008410U, 0x100210U, 0x014020U, 0x042100U, 0x600080U, 0x030002U, 0x300100U, + 0x030000U, 0x030001U, 0x009080U, 0x040440U, 0x030004U, 0x080A00U, 0x100204U, 0x008402U, 0x008401U, 0x008400U, + 0x100200U, 0x100201U, 0x100202U, 0x008404U, 0x040820U, 0x084080U, 0x030010U, 0x008408U, 0x100208U, 0x422000U, + 0x204040U, 0x001120U, 0x480020U, 0x220010U, 0x008804U, 0x002088U, 0x008802U, 0x014040U, 0x008800U, 0x008801U, + 0x105000U, 0x002082U, 0x002081U, 0x002080U, 0x020300U, 0x040420U, 0x008808U, 0x002084U, 0x220001U, 0x220000U, + 0x110100U, 0x220002U, 0x003400U, 0x220004U, 0x008810U, 0x440200U, 0x040840U, 0x220008U, 0x080600U, 0x002090U, + 0x410080U, 0x188000U, 0x204020U, 0x001140U, 0x480000U, 0x480001U, 0x480002U, 0x004300U, 0x480004U, 0x040408U, + 0x008820U, 0x121000U, 0x480008U, 0x040404U, 0x030040U, 0x0020A0U, 0x040401U, 0x040400U, 0x204010U, 0x040402U, + 0x480010U, 0x220020U, 0x041080U, 0x008440U, 0x100240U, 0x000980U, 0x204008U, 0x092000U, 0x00A100U, 0x011200U, + 0x204004U, 0x500800U, 0x204002U, 0x040410U, 0x204000U, 0x204001U, 0x000600U, 0x000601U, 0x000602U, 0x108004U, + 0x000604U, 0x108002U, 0x108001U, 0x108000U, 0x000608U, 0x005800U, 0x400030U, 0x2A0000U, 0x0200C0U, 0x012020U, + 0x044100U, 0x108008U, 0x000610U, 0x082080U, 0x400028U, 0x010900U, 0x051000U, 0x424000U, 0x202040U, 0x108010U, + 0x400022U, 0x140040U, 0x400020U, 0x400021U, 0x088800U, 0x200500U, 0x400024U, 0x001280U, 0x000620U, 0x060100U, + 0x400018U, 0x0040C0U, 0x284000U, 0x012008U, 0x021800U, 0x108020U, 0x400012U, 0x012004U, 0x400010U, 0x400011U, + 0x012001U, 0x012000U, 0x400014U, 0x012002U, 0x40000AU, 0x209000U, 0x400008U, 0x400009U, 0x100180U, 0x000A40U, + 0x40000CU, 0x0C0400U, 0x400002U, 0x400003U, 0x400000U, 0x400001U, 0x400006U, 0x012010U, 0x400004U, 0x400005U, + 0x000640U, 0x610000U, 0x0C0800U, 0x0040A0U, 0x020088U, 0x081100U, 0x202010U, 0x108040U, 0x020084U, 0x140010U, + 0x019000U, 0x002300U, 0x020080U, 0x020081U, 0x020082U, 0x400C00U, 0x00C100U, 0x140008U, 0x202004U, 0x021400U, + 0x202002U, 0x000A20U, 0x202000U, 0x202001U, 0x140001U, 0x140000U, 0x400060U, 0x140002U, 0x020090U, 0x140004U, + 0x202008U, 0x094000U, 0x103000U, 0x004082U, 0x004081U, 0x004080U, 0x448000U, 0x000A10U, 0x010500U, 0x004084U, + 0x200900U, 0x088400U, 0x400050U, 0x004088U, 0x0200A0U, 0x012040U, 0x180200U, 0x241000U, 0x0B0000U, 0x000A04U, + 0x400048U, 0x004090U, 0x000A01U, 0x000A00U, 0x202020U, 0x000A02U, 0x400042U, 0x140020U, 0x400040U, 0x400041U, + 0x005400U, 0x000A08U, 0x400044U, 0x028100U, 0x000680U, 0x082010U, 0x201100U, 0x004060U, 0x020048U, 0x240800U, + 0x490000U, 0x108080U, 0x020044U, 0x408100U, 0x102800U, 0x050400U, 0x020040U, 0x020041U, 0x020042U, 0x001210U, + 0x082001U, 0x082000U, 0x068000U, 0x082002U, 0x100120U, 0x082004U, 0x004C00U, 0x001208U, 0x214000U, 0x082008U, + 0x4000A0U, 0x001204U, 0x020050U, 0x001202U, 0x001201U, 0x001200U, 0x018800U, 0x004042U, 0x004041U, 0x004040U, + 0x100110U, 0x401400U, 0x042200U, 0x004044U, 0x0C1000U, 0x300200U, 0x400090U, 0x004048U, 0x020060U, 0x012080U, + 0x208400U, 0x080900U, 0x100104U, 0x082020U, 0x400088U, 0x004050U, 0x100100U, 0x100101U, 0x100102U, 0x230000U, + 0x400082U, 0x020C00U, 0x400080U, 0x400081U, 0x100108U, 0x04C000U, 0x400084U, 0x001220U, 0x02000CU, 0x004022U, + 0x004021U, 0x004020U, 0x020008U, 0x020009U, 0x02000AU, 0x004024U, 0x020004U, 0x020005U, 0x020006U, 0x004028U, + 0x020000U, 0x020001U, 0x020002U, 0x020003U, 0x401800U, 0x082040U, 0x110200U, 0x004030U, 0x020018U, 0x018400U, + 0x202080U, 0x440100U, 0x020014U, 0x140080U, 0x080500U, 0x208800U, 0x020010U, 0x020011U, 0x020012U, 0x001240U, + 0x004003U, 0x004002U, 0x004001U, 0x004000U, 0x020028U, 0x004006U, 0x004005U, 0x004004U, 0x020024U, 0x00400AU, + 0x004009U, 0x004008U, 0x020020U, 0x020021U, 0x020022U, 0x00400CU, 0x240400U, 0x004012U, 0x004011U, 0x004010U, + 0x100140U, 0x000A80U, 0x089000U, 0x004014U, 0x00A200U, 0x011100U, 0x4000C0U, 0x004018U, 0x020030U, 0x680000U, + 0x050800U, 0x102400U, 0x000700U, 0x060020U, 0x201080U, 0x010810U, 0x402800U, 0x081040U, 0x044008U, 0x108100U, + 0x190000U, 0x408080U, 0x044004U, 0x002240U, 0x044002U, 0x200410U, 0x044000U, 0x044001U, 0x00C040U, 0x010802U, + 0x010801U, 0x010800U, 0x1000A0U, 0x200408U, 0x0A0200U, 0x010804U, 0x023000U, 0x200404U, 0x400120U, 0x010808U, + 0x200401U, 0x200400U, 0x044010U, 0x200402U, 0x060001U, 0x060000U, 0x08A000U, 0x060002U, 0x100090U, 0x060004U, + 0x010440U, 0x600200U, 0x200840U, 0x060008U, 0x400110U, 0x101400U, 0x009200U, 0x012100U, 0x044020U, 0x080880U, + 0x100084U, 0x060010U, 0x400108U, 0x010820U, 0x100080U, 0x100081U, 0x100082U, 0x007000U, 0x400102U, 0x084200U, + 0x400100U, 0x400101U, 0x100088U, 0x200420U, 0x400104U, 0x028040U, 0x00C010U, 0x081004U, 0x520000U, 0x002208U, + 0x081001U, 0x081000U, 0x010420U, 0x081002U, 0x200820U, 0x002202U, 0x002201U, 0x002200U, 0x020180U, 0x081008U, + 0x044040U, 0x002204U, 0x00C000U, 0x00C001U, 0x00C002U, 0x010840U, 0x00C004U, 0x081010U, 0x202100U, 0x440080U, + 0x00C008U, 0x140100U, 0x080480U, 0x002210U, 0x410200U, 0x200440U, 0x101800U, 0x028020U, 0x200808U, 0x060040U, + 0x010404U, 0x004180U, 0x010402U, 0x081020U, 0x010400U, 0x010401U, 0x200800U, 0x200801U, 0x200802U, 0x002220U, + 0x200804U, 0x504000U, 0x010408U, 0x028010U, 0x00C020U, 0x402400U, 0x041200U, 0x380000U, 0x1000C0U, 0x000B00U, + 0x010410U, 0x028008U, 0x200810U, 0x011080U, 0x400140U, 0x028004U, 0x0C2000U, 0x028002U, 0x028001U, 0x028000U, + 0x201002U, 0x408008U, 0x201000U, 0x201001U, 0x100030U, 0x014200U, 0x201004U, 0x022400U, 0x408001U, 0x408000U, + 0x201008U, 0x408002U, 0x020140U, 0x408004U, 0x044080U, 0x080820U, 0x100024U, 0x082100U, 0x201010U, 0x010880U, + 0x100020U, 0x100021U, 0x100022U, 0x440040U, 0x040A00U, 0x408010U, 0x080440U, 0x124000U, 0x100028U, 0x200480U, + 0x01A000U, 0x001300U, 0x100014U, 0x060080U, 0x201020U, 0x004140U, 0x100010U, 0x100011U, 0x100012U, 0x080808U, + 0x006400U, 0x408020U, 0x030200U, 0x080804U, 0x100018U, 0x080802U, 0x080801U, 0x080800U, 0x100004U, 0x100005U, + 0x100006U, 0x008600U, 0x100000U, 0x100001U, 0x100002U, 0x100003U, 0x10000CU, 0x011040U, 0x400180U, 0x242000U, + 0x100008U, 0x100009U, 0x10000AU, 0x080810U, 0x052000U, 0x100C00U, 0x201040U, 0x004120U, 0x020108U, 0x081080U, + 0x008A00U, 0x440010U, 0x020104U, 0x408040U, 0x080410U, 0x002280U, 0x020100U, 0x020101U, 0x020102U, 0x310000U, + 0x00C080U, 0x220200U, 0x080408U, 0x440004U, 0x100060U, 0x440002U, 0x440001U, 0x440000U, 0x080402U, 0x011020U, + 0x080400U, 0x080401U, 0x020110U, 0x006800U, 0x080404U, 0x440008U, 0x480200U, 0x004102U, 0x004101U, 0x004100U, + 0x100050U, 0x20A000U, 0x010480U, 0x004104U, 0x200880U, 0x011010U, 0x148000U, 0x004108U, 0x020120U, 0x040600U, + 0x403000U, 0x080840U, 0x100044U, 0x011008U, 0x022800U, 0x004110U, 0x100040U, 0x100041U, 0x100042U, 0x440020U, + 0x011001U, 0x011000U, 0x080420U, 0x011002U, 0x100048U, 0x011004U, 0x204200U, 0x028080U +}; + +#define X22 0x00400000 /* vector representation of X^{22} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK12 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +static unsigned int get_syndrome_23127(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X22; + + if (pattern >= X11) + { + while (pattern & MASK12) + { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X11) * GENPOL; + } + } + + return pattern; +} + +unsigned int CGolay24128::encode23127(unsigned int data) +{ + return ENCODING_TABLE_23127[data]; +} + +unsigned int CGolay24128::encode24128(unsigned int data) +{ + return ENCODING_TABLE_24128[data]; +} + +unsigned int CGolay24128::decode23127(unsigned int code) +{ + unsigned int syndrome = ::get_syndrome_23127(code); + unsigned int error_pattern = DECODING_TABLE_23127[syndrome]; + + code ^= error_pattern; + + return code >> 11; +} + +unsigned int CGolay24128::decode24128(unsigned int code) +{ + return decode23127(code >> 1); +} + +unsigned int CGolay24128::decode24128(unsigned char* bytes) +{ + assert(bytes != nullptr); + + unsigned int code = bytes[0U]; + code <<= 8; + code |= bytes[1U]; + code <<= 8; + code |= bytes[2U]; + + return decode23127(code >> 1); +} diff --git a/src/Golay24128.h b/src/Golay24128.h new file mode 100644 index 0000000..6c820d1 --- /dev/null +++ b/src/Golay24128.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Golay24128_H +#define Golay24128_H + +class CGolay24128 +{ +public: + static unsigned int encode23127(unsigned int data); + static unsigned int encode24128(unsigned int data); + + static unsigned int decode23127(unsigned int code); + static unsigned int decode24128(unsigned int code); + static unsigned int decode24128(unsigned char* bytes); +}; + +#endif diff --git a/src/Hamming.cpp b/src/Hamming.cpp new file mode 100644 index 0000000..42636f0 --- /dev/null +++ b/src/Hamming.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Hamming.h" + +#include +#include + +// Hamming (15,11,3) check a boolean data array +bool CHamming::decode15113_1(bool* d) +{ + assert(d != nullptr); + + // Calculate the parity it should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6]; + bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9]; + bool c2 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10]; + bool c3 = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10]; + + unsigned char n = 0U; + n |= (c0 != d[11]) ? 0x01U : 0x00U; + n |= (c1 != d[12]) ? 0x02U : 0x00U; + n |= (c2 != d[13]) ? 0x04U : 0x00U; + n |= (c3 != d[14]) ? 0x08U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[11] = !d[11]; + return true; + case 0x02U: + d[12] = !d[12]; + return true; + case 0x04U: + d[13] = !d[13]; + return true; + case 0x08U: + d[14] = !d[14]; + return true; + + // Data bit errors + case 0x0FU: + d[0] = !d[0]; + return true; + case 0x07U: + d[1] = !d[1]; + return true; + case 0x0BU: + d[2] = !d[2]; + return true; + case 0x03U: + d[3] = !d[3]; + return true; + case 0x0DU: + d[4] = !d[4]; + return true; + case 0x05U: + d[5] = !d[5]; + return true; + case 0x09U: + d[6] = !d[6]; + return true; + case 0x0EU: + d[7] = !d[7]; + return true; + case 0x06U: + d[8] = !d[8]; + return true; + case 0x0AU: + d[9] = !d[9]; + return true; + case 0x0CU: + d[10] = !d[10]; + return true; + + // No bit errors + default: + return false; + } +} + +void CHamming::encode15113_1(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this row should have + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[6]; + d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[7] ^ d[8] ^ d[9]; + d[13] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[8] ^ d[10]; + d[14] = d[0] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[9] ^ d[10]; +} + +// Hamming (15,11,3) check a boolean data array +bool CHamming::decode15113_2(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this row should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + + unsigned char n = 0x00U; + n |= (c0 != d[11]) ? 0x01U : 0x00U; + n |= (c1 != d[12]) ? 0x02U : 0x00U; + n |= (c2 != d[13]) ? 0x04U : 0x00U; + n |= (c3 != d[14]) ? 0x08U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[11] = !d[11]; + return true; + case 0x02U: + d[12] = !d[12]; + return true; + case 0x04U: + d[13] = !d[13]; + return true; + case 0x08U: + d[14] = !d[14]; + return true; + + // Data bit errors + case 0x09U: + d[0] = !d[0]; + return true; + case 0x0BU: + d[1] = !d[1]; + return true; + case 0x0FU: + d[2] = !d[2]; + return true; + case 0x07U: + d[3] = !d[3]; + return true; + case 0x0EU: + d[4] = !d[4]; + return true; + case 0x05U: + d[5] = !d[5]; + return true; + case 0x0AU: + d[6] = !d[6]; + return true; + case 0x0DU: + d[7] = !d[7]; + return true; + case 0x03U: + d[8] = !d[8]; + return true; + case 0x06U: + d[9] = !d[9]; + return true; + case 0x0CU: + d[10] = !d[10]; + return true; + + // No bit errors + default: + return false; + } +} + +void CHamming::encode15113_2(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this row should have + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; +} + +// Hamming (13,9,3) check a boolean data array +bool CHamming::decode1393(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6]; + bool c1 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7]; + bool c2 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c3 = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8]; + + unsigned char n = 0x00U; + n |= (c0 != d[9]) ? 0x01U : 0x00U; + n |= (c1 != d[10]) ? 0x02U : 0x00U; + n |= (c2 != d[11]) ? 0x04U : 0x00U; + n |= (c3 != d[12]) ? 0x08U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[9] = !d[9]; + return true; + case 0x02U: + d[10] = !d[10]; + return true; + case 0x04U: + d[11] = !d[11]; + return true; + case 0x08U: + d[12] = !d[12]; + return true; + + // Data bit erros + case 0x0FU: + d[0] = !d[0]; + return true; + case 0x07U: + d[1] = !d[1]; + return true; + case 0x0EU: + d[2] = !d[2]; + return true; + case 0x05U: + d[3] = !d[3]; + return true; + case 0x0AU: + d[4] = !d[4]; + return true; + case 0x0DU: + d[5] = !d[5]; + return true; + case 0x03U: + d[6] = !d[6]; + return true; + case 0x06U: + d[7] = !d[7]; + return true; + case 0x0CU: + d[8] = !d[8]; + return true; + + // No bit errors + default: + return false; + } +} + +void CHamming::encode1393(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + d[9] = d[0] ^ d[1] ^ d[3] ^ d[5] ^ d[6]; + d[10] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7]; + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[0] ^ d[2] ^ d[4] ^ d[5] ^ d[8]; +} + +// Hamming (10,6,3) check a boolean data array +bool CHamming::decode1063(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[5]; + bool c1 = d[0] ^ d[1] ^ d[3] ^ d[5]; + bool c2 = d[0] ^ d[2] ^ d[3] ^ d[4]; + bool c3 = d[1] ^ d[2] ^ d[3] ^ d[4]; + + unsigned char n = 0x00U; + n |= (c0 != d[6]) ? 0x01U : 0x00U; + n |= (c1 != d[7]) ? 0x02U : 0x00U; + n |= (c2 != d[8]) ? 0x04U : 0x00U; + n |= (c3 != d[9]) ? 0x08U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[6] = !d[6]; + return true; + case 0x02U: + d[7] = !d[7]; + return true; + case 0x04U: + d[8] = !d[8]; + return true; + case 0x08U: + d[9] = !d[9]; + return true; + + // Data bit erros + case 0x07U: + d[0] = !d[0]; + return true; + case 0x0BU: + d[1] = !d[1]; + return true; + case 0x0DU: + d[2] = !d[2]; + return true; + case 0x0EU: + d[3] = !d[3]; + return true; + case 0x0CU: + d[4] = !d[4]; + return true; + case 0x03U: + d[5] = !d[5]; + return true; + + // No bit errors + default: + return false; + } +} + +void CHamming::encode1063(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + d[6] = d[0] ^ d[1] ^ d[2] ^ d[5]; + d[7] = d[0] ^ d[1] ^ d[3] ^ d[5]; + d[8] = d[0] ^ d[2] ^ d[3] ^ d[4]; + d[9] = d[1] ^ d[2] ^ d[3] ^ d[4]; +} + +// A Hamming (16,11,4) Check +bool CHamming::decode16114(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + bool c1 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + bool c2 = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + bool c3 = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + bool c4 = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10]; + + // Compare these with the actual bits + unsigned char n = 0x00U; + n |= (c0 != d[11]) ? 0x01U : 0x00U; + n |= (c1 != d[12]) ? 0x02U : 0x00U; + n |= (c2 != d[13]) ? 0x04U : 0x00U; + n |= (c3 != d[14]) ? 0x08U : 0x00U; + n |= (c4 != d[15]) ? 0x10U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[11] = !d[11]; + return true; + case 0x02U: + d[12] = !d[12]; + return true; + case 0x04U: + d[13] = !d[13]; + return true; + case 0x08U: + d[14] = !d[14]; + return true; + case 0x10U: + d[15] = !d[15]; + return true; + + // Data bit errors + case 0x19U: + d[0] = !d[0]; + return true; + case 0x0BU: + d[1] = !d[1]; + return true; + case 0x1FU: + d[2] = !d[2]; + return true; + case 0x07U: + d[3] = !d[3]; + return true; + case 0x0EU: + d[4] = !d[4]; + return true; + case 0x15U: + d[5] = !d[5]; + return true; + case 0x1AU: + d[6] = !d[6]; + return true; + case 0x0DU: + d[7] = !d[7]; + return true; + case 0x13U: + d[8] = !d[8]; + return true; + case 0x16U: + d[9] = !d[9]; + return true; + case 0x1CU: + d[10] = !d[10]; + return true; + + // No bit errors + case 0x00U: + return true; + + // Unrecoverable errors + default: + return false; + } +} + +void CHamming::encode16114(bool* d) +{ + assert(d != nullptr); + + d[11] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[5] ^ d[7] ^ d[8]; + d[12] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[6] ^ d[8] ^ d[9]; + d[13] = d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[7] ^ d[9] ^ d[10]; + d[14] = d[0] ^ d[1] ^ d[2] ^ d[4] ^ d[6] ^ d[7] ^ d[10]; + d[15] = d[0] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[9] ^ d[10]; +} + +// A Hamming (17,12,3) Check +bool CHamming::decode17123(bool* d) +{ + assert(d != nullptr); + + // Calculate the checksum this column should have + bool c0 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9]; + bool c1 = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10]; + bool c2 = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11]; + bool c3 = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10]; + bool c4 = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11]; + + // Compare these with the actual bits + unsigned char n = 0x00U; + n |= (c0 != d[12]) ? 0x01U : 0x00U; + n |= (c1 != d[13]) ? 0x02U : 0x00U; + n |= (c2 != d[14]) ? 0x04U : 0x00U; + n |= (c3 != d[15]) ? 0x08U : 0x00U; + n |= (c4 != d[16]) ? 0x10U : 0x00U; + + switch (n) + { + // Parity bit errors + case 0x01U: + d[12] = !d[12]; + return true; + case 0x02U: + d[13] = !d[13]; + return true; + case 0x04U: + d[14] = !d[14]; + return true; + case 0x08U: + d[15] = !d[15]; + return true; + case 0x10U: + d[16] = !d[16]; + return true; + + // Data bit errors + case 0x1BU: + d[0] = !d[0]; + return true; + case 0x1FU: + d[1] = !d[1]; + return true; + case 0x17U: + d[2] = !d[2]; + return true; + case 0x07U: + d[3] = !d[3]; + return true; + case 0x0EU: + d[4] = !d[4]; + return true; + case 0x1CU: + d[5] = !d[5]; + return true; + case 0x11U: + d[6] = !d[6]; + return true; + case 0x0BU: + d[7] = !d[7]; + return true; + case 0x16U: + d[8] = !d[8]; + return true; + case 0x05U: + d[9] = !d[9]; + return true; + case 0x0AU: + d[10] = !d[10]; + return true; + case 0x14U: + d[11] = !d[11]; + return true; + + // No bit errors + case 0x00U: + return true; + + // Unrecoverable errors + default: + return false; + } +} + +void CHamming::encode17123(bool* d) +{ + assert(d != nullptr); + + d[12] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[6] ^ d[7] ^ d[9]; + d[13] = d[0] ^ d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[7] ^ d[8] ^ d[10]; + d[14] = d[1] ^ d[2] ^ d[3] ^ d[4] ^ d[5] ^ d[8] ^ d[9] ^ d[11]; + d[15] = d[0] ^ d[1] ^ d[4] ^ d[5] ^ d[7] ^ d[10]; + d[16] = d[0] ^ d[1] ^ d[2] ^ d[5] ^ d[6] ^ d[8] ^ d[11]; +} diff --git a/src/Hamming.h b/src/Hamming.h new file mode 100644 index 0000000..069b256 --- /dev/null +++ b/src/Hamming.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef Hamming_H +#define Hamming_H + +class CHamming +{ +public: + static void encode15113_1(bool* d); + static bool decode15113_1(bool* d); + + static void encode15113_2(bool* d); + static bool decode15113_2(bool* d); + + static void encode1393(bool* d); + static bool decode1393(bool* d); + + static void encode1063(bool* d); + static bool decode1063(bool* d); + + static void encode16114(bool* d); + static bool decode16114(bool* d); + + static void encode17123(bool* d); + static bool decode17123(bool* d); +}; + +#endif diff --git a/src/IP.cpp b/src/IP.cpp new file mode 100644 index 0000000..48a3b6f --- /dev/null +++ b/src/IP.cpp @@ -0,0 +1,309 @@ +/* +* Copyright (C) 2020 by Thomas Early N7TAE +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include "cip.h" + +CIp::CIp() : is_set(false) +{ + Clear(); +} + +CIp::CIp(const char *address, int family, int type, uint16_t port) : is_set(true) +{ + Clear(); + if (0 == strncasecmp(address, "none", 4)) + { + is_set = false; + return; + } + struct addrinfo hints, *result; + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = type; + if (0 == getaddrinfo(address, (port ? std::to_string(port).c_str() : nullptr), &hints, &result)) + { + memcpy(&addr, result->ai_addr, result->ai_addrlen); + addr.ss_family = result->ai_family; + freeaddrinfo(result); + } + SetPort(port); +} +CIp::CIp(const int family, const uint16_t port, const char *address) : is_set(true) +{ + Initialize(family, port, address); +} + +void CIp::Initialize(const int family, const uint16_t port, const char *address) +{ + Clear(); + if (0 == strncasecmp(address, "none", 4)) + { + is_set = false; + return; + } + is_set = true; + addr.ss_family = family; + if (AF_INET == family) + { + auto addr4 = (struct sockaddr_in *)&addr; + addr4->sin_port = htons(port); + if (address) + { + if (0 == strncasecmp(address, "loc", 3)) + inet_pton(AF_INET, "127.0.0.1", &(addr4->sin_addr)); + else if (0 == strncasecmp(address, "any", 3)) + inet_pton(AF_INET, "0.0.0.0", &(addr4->sin_addr)); + else + { + if (1 > inet_pton(AF_INET, address, &(addr4->sin_addr))) + { + std::cerr << "Address Initialization Error: '" << address << "' is not a valdid IPV4 address!" << std::endl; + is_set = false; + } + } + } + } + else if (AF_INET6 == family) + { + auto addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_port = htons(port); + if (address) + { + if (0 == strncasecmp(address, "loc", 3)) + inet_pton(AF_INET6, "::1", &(addr6->sin6_addr)); + else if (0 == strncasecmp(address, "any", 3)) + inet_pton(AF_INET6, "::", &(addr6->sin6_addr)); + else + { + if (1 > inet_pton(AF_INET6, address, &(addr6->sin6_addr))) + { + std::cerr << "Address Initialization Error: '" << address << "' is not a valid IPV6 address!" << std::endl; + is_set = false; + } + } + } + } + else + { + std::cerr << "Error: Wrong address family type:" << family << " for [" << (address ? address : "NULL") << "]:" << port << std::endl; + is_set = false; + } +} + +bool CIp::operator==(const CIp &rhs) const // compares ports, addresses and families +{ + // if anything is not equal, then we are done + if (addr.ss_family != rhs.addr.ss_family) + return false; + if (AF_INET == addr.ss_family) + { + auto l = (struct sockaddr_in *)&addr; + auto r = (struct sockaddr_in *)&rhs.addr; + if (l->sin_addr.s_addr == r->sin_addr.s_addr) + return l->sin_port == r->sin_port; + else + return false; + } + else if (AF_INET6 == addr.ss_family) + { + auto l = (struct sockaddr_in6 *)&addr; + auto r = (struct sockaddr_in6 *)&rhs.addr; + if (0 == memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr))) + return l->sin6_port == r->sin6_port; + else + return false; + } + return false; +} + +bool CIp::operator!=(const CIp &rhs) const // compares ports, addresses and families +{ + // if anything is not equal, then we are done + if (addr.ss_family != rhs.addr.ss_family) + return true; + if (AF_INET == addr.ss_family) + { + auto l = (struct sockaddr_in *)&addr; + auto r = (struct sockaddr_in *)&rhs.addr; + if (l->sin_addr.s_addr != r->sin_addr.s_addr) + return true; + else + return l->sin_port != r->sin_port; + } + else if (AF_INET6 == addr.ss_family) + { + auto l = (struct sockaddr_in6 *)&addr; + auto r = (struct sockaddr_in6 *)&rhs.addr; + if (0 != memcmp(&(l->sin6_addr), &(r->sin6_addr), sizeof(struct in6_addr))) + return true; + else + return l->sin6_port != r->sin6_port; + } + return true; +} + +bool CIp::AddressIsZero() const +{ + if (AF_INET == addr.ss_family) + { + auto addr4 = (struct sockaddr_in *)&addr; + return (addr4->sin_addr.s_addr == 0U); + } + else + { + auto addr6 = (struct sockaddr_in6 *)&addr; + for (unsigned int i=0; i<16; i++) + { + if (addr6->sin6_addr.s6_addr[i]) + return false; + } + return true; + } +} + +void CIp::ClearAddress() +{ + if (AF_INET == addr.ss_family) + { + auto addr4 = (struct sockaddr_in *)&addr; + addr4->sin_addr.s_addr = 0U; + strcpy(straddr, "0.0.0.0"); + } + else + { + auto addr6 = (struct sockaddr_in6 *)&addr; + memset(&(addr6->sin6_addr.s6_addr), 0, 16); + strcpy(straddr, "::"); + } +} + +const char *CIp::GetAddress() const +{ + if (straddr[0]) + return straddr; + + if (AF_INET == addr.ss_family) + { + auto addr4 = (struct sockaddr_in *)&addr; + inet_ntop(AF_INET, &(addr4->sin_addr), straddr, INET6_ADDRSTRLEN); + } + else if (AF_INET6 == addr.ss_family) + { + auto addr6 = (struct sockaddr_in6 *)&addr; + inet_ntop(AF_INET6, &(addr6->sin6_addr), straddr, INET6_ADDRSTRLEN); + } + else + { + std::cerr << "CIp::GetAddress: unknown socket family=" << addr.ss_family << std::endl; + } + return straddr; +} + +std::ostream &operator<<(std::ostream &stream, const CIp &Ip) +{ + const char *sz = Ip; + if (AF_INET6 == Ip.GetFamily()) + stream << "[" << sz << "]"; + else + stream << sz; + const uint16_t port = Ip.GetPort(); + if (port) + stream << ":" << port; + return stream; +} + +uint32_t CIp::GetAddr() const +{ + if (AF_INET6 == addr.ss_family) + { + auto addr6 = (struct sockaddr_in6 *)&addr; + // hash the results + auto *a = (const uint32_t *)&(addr6->sin6_addr.s6_addr); + return a[0] ^ a[1] ^ a[2] ^ a[3]; + } + else + { + auto addr4 = (struct sockaddr_in *)&addr; + return addr4->sin_addr.s_addr; + } +} + +int CIp::GetFamily() const +{ + return addr.ss_family; +} + +uint16_t CIp::GetPort() const +{ + if (AF_INET == addr.ss_family) + { + auto addr4 = (struct sockaddr_in *)&addr; + return ntohs(addr4->sin_port); + } + else if (AF_INET6 == addr.ss_family) + { + auto addr6 = (struct sockaddr_in6 *)&addr; + return ntohs(addr6->sin6_port); + } + else + return 0; +} + +void CIp::SetPort(const uint16_t newport) +{ + if (AF_INET == addr.ss_family) + { + auto addr4 = (struct sockaddr_in *)&addr; + addr4->sin_port = htons(newport); + } + else if (AF_INET6 == addr.ss_family) + { + auto addr6 = (struct sockaddr_in6 *)&addr; + addr6->sin6_port = htons(newport); + } +} + +struct sockaddr *CIp::GetPointer() +{ + memset(straddr, 0, INET6_ADDRSTRLEN); // things might change + return (struct sockaddr *)&addr; +} + +const struct sockaddr *CIp::GetCPointer() const +{ + return (const struct sockaddr *)&addr; +} + +size_t CIp::GetSize() const +{ + if (AF_INET == addr.ss_family) + return sizeof(struct sockaddr_in); + else + return sizeof(struct sockaddr_in6); +} + +void CIp::Clear() +{ + memset(&addr, 0, sizeof(struct sockaddr_storage)); + memset(straddr, 0, INET6_ADDRSTRLEN); + is_set = false; +} diff --git a/src/IP.h b/src/IP.h new file mode 100644 index 0000000..5f7976b --- /dev/null +++ b/src/IP.h @@ -0,0 +1,75 @@ +#pragma once + +/* + * Copyright (C) 2020 by Thomas Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class CIp +{ +public: + // constructors + CIp(); + CIp(const char *address, int family = AF_UNSPEC, int type = SOCK_DGRAM, uint16_t port = 0U); + CIp(const int family, const uint16_t port = 0U, const char *address = nullptr); + + // initializer for empty constructor + void Initialize(const int family, const uint16_t port = 0U, const char *address = nullptr); + + // comparison operators + bool operator==(const CIp &rhs) const; + bool operator!=(const CIp &rhs) const; + + // state methods + bool IsSet() const { return is_set; } + bool AddressIsZero() const; + void ClearAddress(); + const char *GetAddress() const; + operator const char *() const { return GetAddress(); } + friend std::ostream &operator<<(std::ostream &stream, const CIp &Ip); + int GetFamily() const; + uint16_t GetPort() const; + size_t GetSize() const; + uint32_t GetAddr() const; + + // modifiers + void SetPort(const uint16_t newport); + + // for i/o + struct sockaddr *GetPointer(); + const struct sockaddr *GetCPointer() const; + + void Clear(); + +private: + struct sockaddr_storage addr; + mutable char straddr[INET6_ADDRSTRLEN]; + bool is_set; +}; + +std::ostream &operator<<(std::ostream &stream, const CIp &Ip); diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..feea1ca --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,85 @@ +// +// main.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Reflector.h" + +#include + + +//////////////////////////////////////////////////////////////////////////////////////// +// global objects + +CReflector g_Reflector; + +//////////////////////////////////////////////////////////////////////////////////////// +// function declaration + +#include "Users.h" + +int main() +{ + const std::string cs(CALLSIGN); + if ((cs.size() != 6) || (cs.compare(0, 3, "XLX") && cs.compare(0, 3, "XRF"))) + { + std::cerr << "Malformed reflector callsign: '" << cs << "', aborting!" << std::endl; + return EXIT_FAILURE; + } + + // remove pidfile + remove(PIDFILE_PATH); + + // splash + std::cout << "Starting " << cs << " " << VERSION_MAJOR << "." << VERSION_MINOR << "." << VERSION_REVISION << std::endl << std::endl; + + // initialize reflector + g_Reflector.SetCallsign(cs.c_str()); + +#ifdef TRANSCODER_IP + g_Reflector.SetTranscoderIp(TRANSCODER_IP, INET6_ADDRSTRLEN); +#endif + + + // and let it run + if ( !g_Reflector.Start() ) + { + std::cout << "Error starting reflector" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Reflector " << g_Reflector.GetCallsign() << "started and listening" << std::endl; + + // write new pid file + std::ofstream ofs(PIDFILE_PATH, std::ofstream::out); + ofs << getpid() << std::endl; + ofs.close(); + + pause(); // wait for any signal + + g_Reflector.Stop(); + std::cout << "Reflector stopped" << std::endl; + + // done + return EXIT_SUCCESS; +} diff --git a/src/Main.h b/src/Main.h new file mode 100644 index 0000000..8d122e1 --- /dev/null +++ b/src/Main.h @@ -0,0 +1,265 @@ +// +// Main.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Eary, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef main_h +#define main_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configure.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// defines + +//// Module configuration +#define DSTAR_IPV4 true +#define DMR_IPV4 true +#define YSF_IPV4 true +#define XLX_IPV4 true + +#define DSTAR_IPV6 true +#define DMR_IPV6 false +#define YSF_IPV6 false +#define XLX_IPV6 false + +// version ----------------------------------------------------- + +#define VERSION_MAJOR 2 +#define VERSION_MINOR 4 +#define VERSION_REVISION 31 + +// global ------------------------------------------------------ + +//#define JSON_MONITOR + +// debug ------------------------------------------------------- + +//#define DEBUG_NO_ERROR_ON_XML_OPEN_FAIL +//#define DEBUG_DUMPFILE + +// protocols --------------------------------------------------- + +#define PROTOCOL_ANY -1 +#define PROTOCOL_NONE 0 +#define PROTOCOL_DEXTRA 1 +#define PROTOCOL_DPLUS 2 +#define PROTOCOL_DCS 3 +#ifndef NO_XLX +#define PROTOCOL_XLX 4 +#define PROTOCOL_DMRPLUS 5 +#define PROTOCOL_DMRMMDVM 6 +#define PROTOCOL_YSF 7 +#endif +#ifndef NO_G3 +#define PROTOCOL_G3 8 +#endif + +// DExtra +#define DEXTRA_PORT 30001 // UDP port +#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds +#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds +#define DEXTRA_RECONNECT_PERIOD 5 // in seconds + +// DPlus +#define DPLUS_PORT 20001 // UDP port +#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds +#define DPLUS_KEEPALIVE_TIMEOUT (DPLUS_KEEPALIVE_PERIOD*10) // in seconds + +// DCS +#define DCS_PORT 30051 // UDP port +#define DCS_KEEPALIVE_PERIOD 1 // in seconds +#define DCS_KEEPALIVE_TIMEOUT (DCS_KEEPALIVE_PERIOD*30) // in seconds + +#ifndef NO_XLX +// XLX +#define XLX_PORT 10002 // UDP port +#define XLX_KEEPALIVE_PERIOD 1 // in seconds +#define XLX_KEEPALIVE_TIMEOUT (XLX_KEEPALIVE_PERIOD*30) // in seconds +#define XLX_RECONNECT_PERIOD 5 // in seconds + +// DMRPlus (dongle) +#define DMRPLUS_PORT 8880 // UDP port +#define DMRPLUS_KEEPALIVE_PERIOD 1 // in seconds +#define DMRPLUS_KEEPALIVE_TIMEOUT (DMRPLUS_KEEPALIVE_PERIOD*10) // in seconds +#define DMRPLUS_REFLECTOR_SLOT DMR_SLOT2 +#define DMRPLUS_REFLECTOR_COLOUR 1 + +// DMRMmdvm +#define DMRMMDVM_PORT 62030 // UDP port +#define DMRMMDVM_KEEPALIVE_PERIOD 10 // in seconds +#define DMRMMDVM_KEEPALIVE_TIMEOUT (DMRMMDVM_KEEPALIVE_PERIOD*10) // in seconds +#define DMRMMDVM_REFLECTOR_SLOT DMR_SLOT2 +#define DMRMMDVM_REFLECTOR_COLOUR 1 + +// YSF +#define YSF_PORT 42000 // UDP port +#define YSF_KEEPALIVE_PERIOD 3 // in seconds +#define YSF_KEEPALIVE_TIMEOUT (YSF_KEEPALIVE_PERIOD*10) // in seconds +#define YSF_DEFAULT_NODE_TX_FREQ 445500000 // in Hz +#define YSF_DEFAULT_NODE_RX_FREQ 445500000 // in Hz +// the following two defines are now in configure.h +// #define YSF_AUTOLINK_ENABLE 0 // 1 = enable, 0 = disable auto-link +// #define YSF_AUTOLINK_MODULE 'B' // module for client to auto-link to +#endif + +#ifndef NO_G3 +// G3 Terminal +#define G3_PRESENCE_PORT 12346 // UDP port +#define G3_CONFIG_PORT 12345 // UDP port +#define G3_DV_PORT 40000 // UDP port +#define G3_KEEPALIVE_PERIOD 10 // in seconds +#define G3_KEEPALIVE_TIMEOUT 3600 // in seconds, 1 hour +#endif + +#ifdef TRANSCODER_IP +// Transcoder server -------------------------------------------- + +#define TRANSCODER_PORT 10100 // UDP port +#define TRANSCODER_KEEPALIVE_PERIOD 5 // in seconds +#define TRANSCODER_KEEPALIVE_TIMEOUT 30 // in seconds +#define TRANSCODER_AMBEPACKET_TIMEOUT 400 // in ms +#endif + +// codec -------------------------------------------------------- + +#define CODEC_NONE 0 +#define CODEC_AMBEPLUS 1 // DStar +#ifndef NO_XLX +#define CODEC_AMBE2PLUS 2 // DMR + + +// DMRid database ----------------------------------------------- + +#define DMRIDDB_USE_RLX_SERVER 1 // 1 = use http, 0 = use local file +#define DMRIDDB_PATH "/usr/local/etc/dmrid.dat" // local file path +#define DMRIDDB_REFRESH_RATE 180 // in minutes + +// Wires-X node database ---------------------------------------- + +#define YSFNODEDB_USE_RLX_SERVER 1 // 1 = use http, 0 = use local file +#define YSFNODEDB_PATH "/usr/local/etc/ysfnode.dat" // local file path +#define YSFNODEDB_REFRESH_RATE 180 // in minutes +#endif + +// xml & json reporting ----------------------------------------- + +#define LASTHEARD_USERS_MAX_SIZE 100 +#define XML_UPDATE_PERIOD 10 // in seconds +#ifdef JSON_MONITOR +#define JSON_UPDATE_PERIOD 10 // in seconds +#define JSON_PORT 10001 +#endif + +// system paths ------------------------------------------------- +#ifdef NO_XLX +#define XML_PATH "/var/log/xrfd.xml" +#define WHITELIST_PATH "/usr/local/etc/xrfd.whitelist" +#define BLACKLIST_PATH "/usr/local/etc/xrfd.blacklist" +#define INTERLINKLIST_PATH "/usr/local/etc/xrfd.interlink" +#define TERMINALOPTIONS_PATH "/usr/local/etc/xrfd.terminal" +#define DEBUGDUMP_PATH "/var/log/xrfd.debug" +#define PIDFILE_PATH "/var/run/xrfd.pid" +#else +#define XML_PATH "/var/log/xlxd.xml" +#define WHITELIST_PATH "/usr/local/etc/xlxd.whitelist" +#define BLACKLIST_PATH "/usr/local/etc/xlxd.blacklist" +#define INTERLINKLIST_PATH "/usr/local/etc/xlxd.interlink" +#define TERMINALOPTIONS_PATH "/usr/local/etc/xlxd.terminal" +#define DEBUGDUMP_PATH "/var/log/xlxd.debug" +#define PIDFILE_PATH "/var/run/xlxd.pid" +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// typedefs + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned int uint; + + +//////////////////////////////////////////////////////////////////////////////////////// +// macros + +#define MIN(a,b) ((a)<(b))?(a):(b) +#define MAX(a,b) ((a)>(b))?(a):(b) +#define MAKEWORD(low, high) ((uint16)(((uint8)(low)) | (((uint16)((uint8)(high))) << 8))) +#define MAKEDWORD(low, high) ((uint32)(((uint16)(low)) | (((uint32)((uint16)(high))) << 16))) +#define LOBYTE(w) ((uint8)(uint16)(w & 0x00FF)) +#define HIBYTE(w) ((uint8)((((uint16)(w)) >> 8) & 0xFF)) +#define LOWORD(dw) ((uint16)(uint32)(dw & 0x0000FFFF)) +#define HIWORD(dw) ((uint16)((((uint32)(dw)) >> 16) & 0xFFFF)) + +//////////////////////////////////////////////////////////////////////////////////////// +// global objects + +class CReflector; +extern CReflector g_Reflector; + +class CGateKeeper; +extern CGateKeeper g_GateKeeper; + +#ifndef NO_XLX +#if (DMRIDDB_USE_RLX_SERVER == 1) +class CDmridDirHttp; +extern CDmridDirHttp g_DmridDir; +#else +class CDmridDirFile; +extern CDmridDirFile g_DmridDir; +#endif + +#if (YSFNODEDB_USE_RLX_SERVER == 1) +class CYsfNodeDirHttp; +extern CYsfNodeDirHttp g_YsfNodeDir; +#else +class CYsfNodeDirFile; +extern CYsfNodeDirFile g_YsfNodeDir; +#endif + +class CTranscoder; +extern CTranscoder g_Transcoder; +#endif + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* main_h */ diff --git a/src/Notification.cpp b/src/Notification.cpp new file mode 100644 index 0000000..f01e325 --- /dev/null +++ b/src/Notification.cpp @@ -0,0 +1,46 @@ +// +// cnotification.cpp +// xlxd +// +// Created by Jean-Luc on 05/12/2015. +// Copyright © 2015 Jean-Luc. All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "cnotification.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CNotification::CNotification() +{ + // init variables + m_iId = NOTIFICATION_NONE; +} + +CNotification::CNotification(int iId) +{ + m_iId = iId; +} + +CNotification::CNotification(int iId, const CCallsign &Callsign) +{ + m_iId = iId; + m_Callsign = Callsign; +} diff --git a/src/Notification.h b/src/Notification.h new file mode 100644 index 0000000..f5db212 --- /dev/null +++ b/src/Notification.h @@ -0,0 +1,64 @@ +// +// cnotification.h +// xlxd +// +// Created by Jean-Luc on 05/12/2015. +// Copyright © 2015 Jean-Luc. All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cnotification_h +#define cnotification_h + +#include "Callsign.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +// Id +#define NOTIFICATION_NONE 0 +#define NOTIFICATION_CLIENTS 1 +#define NOTIFICATION_USERS 2 +#define NOTIFICATION_STREAM_OPEN 3 +#define NOTIFICATION_STREAM_CLOSE 4 +#define NOTIFICATION_PEERS 5 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CNotification +{ +public: + // constructor + CNotification(); + CNotification(int); + CNotification(int, const CCallsign &); + + // get + int GetId(void) const { return m_iId; } + const CCallsign &GetCallsign(void) const { return m_Callsign; } + +protected: + // data + int m_iId; + CCallsign m_Callsign; + +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cnotification_h */ diff --git a/src/Packet.cpp b/src/Packet.cpp new file mode 100644 index 0000000..2adf718 --- /dev/null +++ b/src/Packet.cpp @@ -0,0 +1,134 @@ +// +// cpacket.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 04/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Packet.h" + + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CPacket::CPacket() +{ + m_uiStreamId = 0; + m_uiDstarPacketId = 0; + m_uiDmrPacketId = 0; + m_uiDmrPacketSubid = 0; + m_uiYsfPacketId = 0; + m_uiYsfPacketSubId = 0; + m_uiYsfPacketFrameId = 0; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +}; + +// dstar contrsuctor + +CPacket::CPacket(uint16 sid, uint8 dstarpid) +{ + m_uiStreamId = sid; + m_uiDstarPacketId = dstarpid; + m_uiDmrPacketId = 0xFF; + m_uiDmrPacketSubid = 0xFF; + m_uiYsfPacketId = 0xFF; + m_uiYsfPacketSubId = 0xFF; + m_uiYsfPacketFrameId = 0xFF; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +}; + +// dmr constructor + +CPacket::CPacket(uint16 sid, uint8 dmrpid, uint8 dmrspid) +{ + m_uiStreamId = sid; + m_uiDmrPacketId = dmrpid; + m_uiDmrPacketSubid = dmrspid; + m_uiDstarPacketId = 0xFF; + m_uiYsfPacketId = 0xFF; + m_uiYsfPacketSubId = 0xFF; + m_uiYsfPacketFrameId = 0xFF; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +}; + +// ysf constructor + +CPacket::CPacket(uint16 sid, uint8 ysfpid, uint8 ysfsubpid, uint8 ysffrid) +{ + m_uiStreamId = sid; + m_uiYsfPacketId = ysfpid; + m_uiYsfPacketSubId = ysfsubpid; + m_uiYsfPacketFrameId = ysffrid; + m_uiDstarPacketId = 0xFF; + m_uiDmrPacketId = 0xFF; + m_uiDmrPacketSubid = 0xFF; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +} + +// xlx constructor + +CPacket::CPacket(uint16 sid, uint8 dstarpid, uint8 dmrpid, uint8 dmrsubpid, uint8 ysfpid, uint8 ysfsubpid, uint8 ysffrid) +{ + m_uiStreamId = sid; + m_uiDstarPacketId = dstarpid; + m_uiDmrPacketId = dmrpid; + m_uiDmrPacketSubid = dmrsubpid; + m_uiYsfPacketId = ysfpid; + m_uiYsfPacketSubId = ysfsubpid; + m_uiYsfPacketFrameId = ysffrid; + m_uiModuleId = ' '; + m_uiOriginId = ORIGIN_LOCAL; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// pid conversion + +void CPacket::UpdatePids(uint32 pid) +{ + // called while phusing this packet in a stream queue + // so now packet sequence number is known and undefined pids can be updated + // this is needed as dtsar & dmt pids are different and cannot be + // derived from each other + + // dstar pid needs update ? + if ( m_uiDstarPacketId == 0xFF ) + { + m_uiDstarPacketId = (pid % 21); + } + // dmr pids need update ? + if ( m_uiDmrPacketId == 0xFF ) + { + m_uiDmrPacketId = ((pid / 3) % 6); + m_uiDmrPacketSubid = ((pid % 3) + 1); + } + // ysf pids need update ? + if ( m_uiYsfPacketId == 0xFF ) + { + m_uiYsfPacketId = ((pid / 5) % 8); + m_uiYsfPacketSubId = pid % 5; + m_uiYsfPacketFrameId = ((pid / 5) & 0x7FU) << 1; + } +} diff --git a/src/Packet.h b/src/Packet.h new file mode 100644 index 0000000..8a68277 --- /dev/null +++ b/src/Packet.h @@ -0,0 +1,95 @@ +// +// Packet.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cpacket_h +#define cpacket_h + +//////////////////////////////////////////////////////////////////////////////////////// + +// Origin Id + +#define ORIGIN_LOCAL 0 +#define ORIGIN_PEER 1 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPacket +{ +public: + // constructor + CPacket(); + CPacket(uint16 sid, uint8 dstarpid); + CPacket(uint16 sid, uint8 dmrpid, uint8 dmrsubpid); + CPacket(uint16 sid, uint8 ysfpid, uint8 ysfsubpid, uint8 ysfsubpidmax); + CPacket(uint16 sid, uint8 dstarpid, uint8 dmrpid, uint8 dmrsubpid, uint8 ysfpid, uint8 ysfsubpid, uint8 ysfsubpidmax); + + // destructor + virtual ~CPacket() {} + + // virtual duplication + virtual std::unique_ptr Duplicate(void) const = 0; + + // identity + virtual bool IsDvHeader(void) const { return false; } + virtual bool IsDvFrame(void) const { return false; } + virtual bool IsLastPacket(void) const { return false; } + virtual bool HasTranscodableAmbe(void) const { return false; } + + // get + virtual bool IsValid(void) const { return true; } + uint16 GetStreamId(void) const { return m_uiStreamId; } + uint8 GetPacketId(void) const { return m_uiDstarPacketId; } + uint8 GetDstarPacketId(void) const { return m_uiDstarPacketId; } + uint8 GetDmrPacketId(void) const { return m_uiDmrPacketId; } + uint8 GetDmrPacketSubid(void) const { return m_uiDmrPacketSubid; } + uint8 GetYsfPacketId(void) const { return m_uiYsfPacketId; } + uint8 GetYsfPacketSubId(void) const { return m_uiYsfPacketSubId; } + uint8 GetYsfPacketFrameId(void) const { return m_uiYsfPacketFrameId; } + uint8 GetModuleId(void) const { return m_uiModuleId; } + bool IsLocalOrigin(void) const { return (m_uiOriginId == ORIGIN_LOCAL); } + + // set + void UpdatePids(uint32); + void SetModuleId(uint8 uiId) { m_uiModuleId = uiId; } + void SetLocalOrigin(void) { m_uiOriginId = ORIGIN_LOCAL; } + void SetRemotePeerOrigin(void) { m_uiOriginId = ORIGIN_PEER; } + +protected: + // data + uint16 m_uiStreamId; + uint8 m_uiDstarPacketId; + uint8 m_uiDmrPacketId; + uint8 m_uiDmrPacketSubid; + uint8 m_uiYsfPacketId; + uint8 m_uiYsfPacketSubId; + uint8 m_uiYsfPacketFrameId; + uint8 m_uiModuleId; + uint8 m_uiOriginId; +}; + + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpacket_h */ diff --git a/src/PacketQueue.h b/src/PacketQueue.h new file mode 100644 index 0000000..912b92d --- /dev/null +++ b/src/PacketQueue.h @@ -0,0 +1,67 @@ +// +// PacketQueue.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 02/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cpacketqueue_h +#define cpacketqueue_h + +#include "Packet.h" +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// CPacketQueue + +class CClient; + +class CPacketQueue +{ +public: + // destructor + virtual ~CPacketQueue() {} + + // lock + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + + // pass thru + void pop() { queue.pop(); } + bool empty() const { return queue.empty(); } + std::unique_ptr front() { return std::move(queue.front()); } + void push(std::unique_ptr &packet) { queue.push(std::move(packet)); } + +protected: + // status + bool m_bOpen; + uint16 m_uiStreamId; + std::mutex m_Mutex; + + // owner + CClient *m_Client; + std::queue> queue; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpacketqueue_h */ diff --git a/src/PacketStream.cpp b/src/PacketStream.cpp new file mode 100644 index 0000000..a39b0d2 --- /dev/null +++ b/src/PacketStream.cpp @@ -0,0 +1,148 @@ +// +// cpacketstream.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 06/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "PacketStream.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CPacketStream::CPacketStream() +{ + m_bOpen = false; + m_uiStreamId = 0; + m_uiPacketCntr = 0; + m_OwnerClient = nullptr; +#ifdef TRANSCODER_IP + m_CodecStream = nullptr; +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////// +// open / close + +bool CPacketStream::OpenPacketStream(const CDvHeaderPacket &DvHeader, std::shared_ptrclient) +{ + bool ok = false; + + // not already open? + if ( !m_bOpen ) + { + // update status + m_bOpen = true; + m_uiStreamId = DvHeader.GetStreamId(); + m_uiPacketCntr = 0; + m_DvHeader = DvHeader; + m_OwnerClient = client; + m_LastPacketTime.Now(); +#ifdef TRANSCODER_IP + if (std::string::npos != std::string(TRANSCODED_MODULES).find(DvHeader.GetRpt2Module())) + m_CodecStream = g_Transcoder.GetCodecStream(this, client->GetCodec()); + else + m_CodecStream = g_Transcoder.GetCodecStream(this, CODEC_NONE); +#endif + ok = true; + } + return ok; +} + +void CPacketStream::ClosePacketStream(void) +{ + // update status + m_bOpen = false; + m_uiStreamId = 0; + m_OwnerClient = nullptr; +#ifdef TRANSCODER_IP + g_Transcoder.ReleaseStream(m_CodecStream); + m_CodecStream = nullptr; +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////// +// push & pop + +void CPacketStream::Push(std::unique_ptr Packet) +{ + // update stream dependent packet data + m_LastPacketTime.Now(); + Packet->UpdatePids(m_uiPacketCntr++); + // transcoder avaliable ? +#ifdef TRANSCODER_IP + if ( m_CodecStream != nullptr ) + { + // todo: verify no possibilty of double lock here + m_CodecStream->Lock(); + { + // transcoder ready & frame need transcoding ? + if ( m_CodecStream->IsConnected() && Packet->HasTranscodableAmbe() ) + { + // yes, push packet to trancoder queue + // trancoder will push it after transcoding + // is completed + m_CodecStream->push(Packet); + } + else + { + // no, just bypass tarnscoder + push(Packet); + } + } + m_CodecStream->Unlock(); + } + else +#endif + { + // otherwise, push direct push + push(Packet); + } +} + +bool CPacketStream::IsEmpty(void) const +{ +#ifdef TRANSCODER_IP + bool bEmpty = empty(); + // also check no packets still in Codec stream's queue + if ( bEmpty && (m_CodecStream != nullptr) ) + { + bEmpty = m_CodecStream->IsEmpty(); + } + + // done + return bEmpty; +#else + return empty(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +const CIp *CPacketStream::GetOwnerIp(void) +{ + if ( m_OwnerClient != nullptr ) + { + return &(m_OwnerClient->GetIp()); + } + return nullptr; +} diff --git a/src/PacketStream.h b/src/PacketStream.h new file mode 100644 index 0000000..eaa3ad1 --- /dev/null +++ b/src/PacketStream.h @@ -0,0 +1,78 @@ +// +// PacketStream.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 06/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cpacketstream_h +#define cpacketstream_h + +#include "PacketQueue.h" +#include "Timer.h" +#include "DVHeaderPacket.h" +#include "Transcoder.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +//#define STREAM_TIMEOUT (0.600) +#define STREAM_TIMEOUT (1.600) + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPacketStream : public CPacketQueue +{ +public: + // constructor + CPacketStream(); + + // open / close + bool OpenPacketStream(const CDvHeaderPacket &, std::shared_ptr); + void ClosePacketStream(void); + + // push & pop + void Push(std::unique_ptr packet); + void Tickle(void) { m_LastPacketTime.Now(); } + bool IsEmpty(void) const; + + // get + std::shared_ptr GetOwnerClient(void) { return m_OwnerClient; } + const CIp *GetOwnerIp(void); + bool IsExpired(void) const { return (m_LastPacketTime.DurationSinceNow() > STREAM_TIMEOUT); } + bool IsOpen(void) const { return m_bOpen; } + uint16 GetStreamId(void) const { return m_uiStreamId; } + const CCallsign &GetUserCallsign(void) const { return m_DvHeader.GetMyCallsign(); } + +protected: + // data + bool m_bOpen; + uint16 m_uiStreamId; + uint32 m_uiPacketCntr; + CTimePoint m_LastPacketTime; + CDvHeaderPacket m_DvHeader; + std::shared_ptr m_OwnerClient; +#ifdef TRANSCODER_IP + std::shared_ptr m_CodecStream; +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpacketstream_h */ diff --git a/src/Peer.cpp b/src/Peer.cpp new file mode 100644 index 0000000..a54006f --- /dev/null +++ b/src/Peer.cpp @@ -0,0 +1,151 @@ +// +// cpeer.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "Peer.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CPeer::CPeer() +{ + ::memset(m_ReflectorModules, 0, sizeof(m_ReflectorModules)); + m_ConnectTime = std::time(nullptr); + m_LastHeardTime = std::time(nullptr); +} + +CPeer::CPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) +{ + m_Callsign = callsign; + m_Ip = ip; + ::memset(m_ReflectorModules, 0, sizeof(m_ReflectorModules)); + ::strncpy(m_ReflectorModules, modules, sizeof(m_ReflectorModules)-1); + m_Version = version; + m_LastKeepaliveTime.Now(); + m_ConnectTime = std::time(nullptr); + m_LastHeardTime = std::time(nullptr); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructors + +CPeer::~CPeer() +{ + m_Clients.clear(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CPeer::operator ==(const CPeer &peer) const +{ + if (peer.m_Callsign != m_Callsign) + return false; + if (peer.m_Ip != m_Ip) + return false; + if (! (peer.m_Version == m_Version)) + return false; + auto it1 = cbegin(); + auto it2 = peer.cbegin(); + while (true) + { + if (it1==cend() && it2==peer.cend()) + break; + if (it1==cend() || it2==peer.cend()) + return false; + if (*it1 != *it2) + return false; + it1++; + it2++; + } + return true; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CPeer::IsAMaster(void) const +{ + for ( auto it=cbegin(); it!=cend(); it++ ) + { + if ((*it)->IsAMaster()) + return true; + } + return false; +} + +void CPeer::Alive(void) +{ + m_LastKeepaliveTime.Now(); + for ( auto it=begin(); it!=end(); it++ ) + { + (*it)->Alive(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// reporting + +void CPeer::WriteXml(std::ofstream &xmlFile) +{ + xmlFile << "" << std::endl; + xmlFile << "\t" << m_Callsign << "" << std::endl; + xmlFile << "\t" << m_Ip.GetAddress() << "" << std::endl; + xmlFile << "\t" << m_ReflectorModules << "" << std::endl; + xmlFile << "\t" << GetProtocolName() << "" << std::endl; + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_ConnectTime))) + { + xmlFile << "\t" << mbstr << "" << std::endl; + } + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + xmlFile << "\t" << mbstr << "" << std::endl; + } + xmlFile << "" << std::endl; +} + +void CPeer::GetJsonObject(char *Buffer) +{ + char sz[512]; + char mbstr[100]; + char cs[16]; + + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + m_Callsign.GetCallsignString(cs); + + ::sprintf(sz, "{\"callsign\":\"%s\",\"linkedto\":\"%s\",\"time\":\"%s\"}", + cs, + m_ReflectorModules, + mbstr); + ::strcat(Buffer, sz); + } +} diff --git a/src/Peer.h b/src/Peer.h new file mode 100644 index 0000000..d70bc94 --- /dev/null +++ b/src/Peer.h @@ -0,0 +1,102 @@ +// +// Peer.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cpeer_h +#define cpeer_h + +#include "Version.h" +#include "Timer.h" +#include "IP.h" +#include "Callsign.h" +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPeer +{ +public: + // constructors + CPeer(); + CPeer(const CCallsign &, const CIp &, const char *, const CVersion &); + CPeer(const CPeer &) = delete; + + // destructor + virtual ~CPeer(); + + // operators + bool operator ==(const CPeer &) const; + + // get + const CCallsign &GetCallsign(void) const { return m_Callsign; } + const CIp &GetIp(void) const { return m_Ip; } + char *GetReflectorModules(void) { return m_ReflectorModules; } + + // set + + // identity + virtual int GetProtocol(void) const { return PROTOCOL_NONE; } + virtual int GetProtocolRevision(void) const { return 0; } + virtual const char *GetProtocolName(void) const { return "none"; } + + // status + virtual bool IsAMaster(void) const; + virtual void Alive(void); + virtual bool IsAlive(void) const { return false; } + virtual void Heard(void) { m_LastHeardTime = std::time(nullptr); } + + // clients access + int GetNbClients(void) const { return (int)m_Clients.size(); } + void ClearClients(void) { m_Clients.clear(); } + + // pass-thru + std::list>::iterator begin() { return m_Clients.begin(); } + std::list>::iterator end() { return m_Clients.end(); } + std::list>::const_iterator cbegin() const { return m_Clients.cbegin(); } + std::list>::const_iterator cend() const { return m_Clients.cend(); } + + // reporting + virtual void WriteXml(std::ofstream &); + virtual void GetJsonObject(char *); + +protected: + // data + CCallsign m_Callsign; + CIp m_Ip; + char m_ReflectorModules[27]; + CVersion m_Version; + std::list> m_Clients; + + // status + CTimePoint m_LastKeepaliveTime; + std::time_t m_ConnectTime; + std::time_t m_LastHeardTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpeer_h */ diff --git a/src/PeerCallsignList.cpp b/src/PeerCallsignList.cpp new file mode 100644 index 0000000..5128d5f --- /dev/null +++ b/src/PeerCallsignList.cpp @@ -0,0 +1,93 @@ +// +// cxlxcallsignlist.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "PeerCallsignList.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// file io + +bool CPeerCallsignList::LoadFromFile(const char *filename) +{ + bool ok = false; + char sz[256]; + + // and load + std::ifstream file (filename); + if ( file.is_open() ) + { + Lock(); + + // empty list + m_Callsigns.clear(); + // fill with file content + while ( file.getline(sz, sizeof(sz)).good() ) + { + // remove leading & trailing spaces + char *szt = TrimWhiteSpaces(sz); + + // crack it + if ( (::strlen(szt) > 0) && (szt[0] != '#') ) + { + // 1st token is callsign + if ( (szt = ::strtok(szt, " ,\t")) != nullptr ) + { + CCallsign callsign(szt); + // 2nd token is ip + char *szip; + if ( (szip = ::strtok(nullptr, " ,\t")) != nullptr ) + { + // 3rd token is modules list + if ( (szt = ::strtok(nullptr, " ,\t")) != nullptr ) + { + // and load + m_Callsigns.push_back(CCallsignListItem(callsign, szip, szt)); + } + } + } + } + } + // close file + file.close(); + + // keep file path + m_Filename = filename; + + // update time + GetLastModTime(&m_LastModTime); + + // and done + Unlock(); + ok = true; + std::cout << "Gatekeeper loaded " << m_Callsigns.size() << " lines from " << filename << std::endl; + } + else + { + std::cout << "Gatekeeper cannot find " << filename << std::endl; + } + + return ok; +} diff --git a/src/PeerCallsignList.h b/src/PeerCallsignList.h new file mode 100644 index 0000000..24bf536 --- /dev/null +++ b/src/PeerCallsignList.h @@ -0,0 +1,51 @@ +// +// PeerCallsignList.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cpeercallsignlist_h +#define cpeercallsignlist_h + + +#include "Main.h" +#include "CallsignList.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPeerCallsignList : public CCallsignList +{ +public: + // constructor + CPeerCallsignList() {} + + // destructor + virtual ~CPeerCallsignList() {} + + // file io + bool LoadFromFile(const char *); +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpeercallsignlist_h */ diff --git a/src/Peers.cpp b/src/Peers.cpp new file mode 100644 index 0000000..9aa1d8b --- /dev/null +++ b/src/Peers.cpp @@ -0,0 +1,171 @@ +// +// cpeers.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Reflector.h" +#include "Peers.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CPeers::CPeers() {} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructors + +CPeers::~CPeers() +{ + m_Mutex.lock(); + m_Peers.clear(); + m_Mutex.unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// manage peers + +void CPeers::AddPeer(std::shared_ptr peer) +{ + // first check if peer already exists + for ( auto it=begin(); it!=end(); it++ ) + { + if (*peer == *(*it)) + // if found, just do nothing + // so *peer keep pointing on a valid object + // on function return + { + // delete new one + return; + } + } + + // if not, append to the vector + m_Peers.push_back(peer); + std::cout << "New peer " << peer->GetCallsign() << " at " << peer->GetIp() << " added with protocol " << peer->GetProtocolName() << std::endl; + // and append all peer's client to reflector client list + // it is double lock safe to lock Clients list after Peers list + CClients *clients = g_Reflector.GetClients(); + for ( auto cit=peer->cbegin(); cit!=peer->cend(); cit++ ) + { + clients->AddClient(*cit); + } + g_Reflector.ReleaseClients(); + + // notify + g_Reflector.OnPeersChanged(); +} + +void CPeers::RemovePeer(std::shared_ptr peer) +{ + // look for the client + for ( auto pit=begin(); pit!=end(); /*increment done in body */ ) + { + // compare object pointers + if (( *pit == peer ) && ( !(*pit)->IsAMaster() )) + { + // remove all clients from reflector client list + // it is double lock safe to lock Clients list after Peers list + CClients *clients = g_Reflector.GetClients(); + for ( auto cit=peer->begin(); cit!=peer->end(); cit++ ) + { + // this also delete the client object + clients->RemoveClient(*cit); + } + // so clear it then + (*pit)->ClearClients(); + g_Reflector.ReleaseClients(); + + // remove it + std::cout << "Peer " << (*pit)->GetCallsign() << " at " << (*pit)->GetIp() << " removed" << std::endl; + pit = m_Peers.erase(pit); + // notify + g_Reflector.OnPeersChanged(); + } + else + { + pit++; + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// find peers + +std::shared_ptr CPeers::FindPeer(const CIp &Ip, int Protocol) +{ + for ( auto it=begin(); it!=end(); it++ ) + { + if ( ((*it)->GetIp() == Ip) && ((*it)->GetProtocol() == Protocol)) + { + return *it; + } + } + + return nullptr; +} + +std::shared_ptr CPeers::FindPeer(const CCallsign &Callsign, const CIp &Ip, int Protocol) +{ + for ( auto it=begin(); it!=end(); it++ ) + { + if ( (*it)->GetCallsign().HasSameCallsign(Callsign) && ((*it)->GetIp() == Ip) && ((*it)->GetProtocol() == Protocol) ) + { + return *it; + } + } + + return nullptr; +} + +std::shared_ptr CPeers::FindPeer(const CCallsign &Callsign, int Protocol) +{ + for ( auto it=begin(); it!=end(); it++ ) + { + if ( ((*it)->GetProtocol() == Protocol) && (*it)->GetCallsign().HasSameCallsign(Callsign) ) + { + return *it; + } + } + + return nullptr; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// iterate on peers + +std::shared_ptr CPeers::FindNextPeer(int Protocol, std::list>::iterator &it) +{ + while ( it!=end() ) + { + if ( (*it)->GetProtocol() == Protocol ) + { + return *it++; + } + it++; + } + return nullptr; +} diff --git a/src/Peers.h b/src/Peers.h new file mode 100644 index 0000000..4cbb046 --- /dev/null +++ b/src/Peers.h @@ -0,0 +1,79 @@ +// +// Peers.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cpeers_h +#define cpeers_h + +#include "Peer.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPeers +{ +public: + // constructors + CPeers(); + + // destructors + virtual ~CPeers(); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // manage peers + int GetSize(void) const { return (int)m_Peers.size(); } + void AddPeer(std::shared_ptr); + void RemovePeer(std::shared_ptr); + + // pass-thru + std::list>::iterator begin() { return m_Peers.begin(); } + std::list>::iterator end() { return m_Peers.end(); } + std::list>::const_iterator cbegin() const { return m_Peers.cbegin(); } + std::list>::const_iterator cend() const { return m_Peers.cend(); } + + // find peers + std::shared_ptr FindPeer(const CIp &, int); + std::shared_ptr FindPeer(const CCallsign &, const CIp &, int); + std::shared_ptr FindPeer(const CCallsign &, int); + + // iterate on peers + std::shared_ptr FindNextPeer(int, std::list>::iterator &); + +protected: + // data + std::mutex m_Mutex; + std::list> m_Peers; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cpeers_h */ diff --git a/src/ProtoAddress.cpp b/src/ProtoAddress.cpp new file mode 100644 index 0000000..6d2e612 --- /dev/null +++ b/src/ProtoAddress.cpp @@ -0,0 +1,100 @@ +// +// Copyright © 2020 Thomas A. Eary, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "ProtoAddress.h" + +CProtoAddress::CProtoAddress() +{ +#ifdef LISTEN_IPV4 + v4address[PROTOCOL_ANY] = LISTEN_IPV4; +#endif +#ifdef LISTEN_V4_DPLUS + v4address[PROTOCOL_DPLUS] = LISTEN_V4_DPLUS; +#endif +#ifdef LISTEN_V4_DCS + v4address[PROTOCOL_DCS] = LISTEN_V4_DCS; +#endif +#ifdef LISTEN_V4_DEXTRA + v4address[PROTOCOL_DEXTRA] = LISTEN_V4_DEXTRA; +#endif +#ifdef LISTEN_V4_DMRMMDVM + v4address[PROTOCOL_DMRMMDVM] = LISTEN_V4_DMRMMDVM; +#endif +#ifdef LISTEN_V4_DMRPLUS + v4address[PROTOCOL_DMRPLUS] = LISTEN_V4_DMRPLUS; +#endif +#ifdef LISTEN_V4_YSF + v4address[PROTOCOL_YSF] = LISTEN_V4_YSF; +#endif +#ifdef LISTEN_V4_XLX + v4address[PROTOCOL_XLX] = LISTEN_V4_XLX; +#endif +#ifdef LISTEN_V4_G3 + v4address[PROTOCOL_G3] = LISTEN_V4_G3; +#endif + +#ifdef LISTEN_IPV6 + v6address[PROTOCOL_ANY] = LISTEN_IPV6; +#endif +#ifdef LISTEN_V6_DPLUS + v6address[PROTOCOL_DPLUS] = LISTEN_V6_DPLUS; +#endif +#ifdef LISTEN_V6_DCS + v6address[PROTOCOL_DCS] = LISTEN_V6_DCS; +#endif +#ifdef LISTEN_V6_DEXTRA + v6address[PROTOCOL_DEXTRA] = LISTEN_V6_DEXTRA; +#endif +#ifdef LISTEN_V6_DMRMMDVM + v6address[PROTOCOL_DMRMMDVM] = LISTEN_V6_DMRMMDVM; +#endif +#ifdef LISTEN_V6_DMRPLUS + v6address[PROTOCOL_DMRPLUS] = LISTEN_V6_DMRPLUS; +#endif +#ifdef LISTEN_V6_YSF + v6address[PROTOCOL_YSF] = LISTEN_V6_YSF; +#endif +#ifdef LISTEN_V6_XLX + v6address[PROTOCOL_XLX] = LISTEN_V6_XLX; +#endif +#ifdef LISTEN_V6_G3 + v6address[PROTOCOL_G3] = LISTEN_V6_G3; +#endif +} + +#ifdef LISTEN_IPV4 +std::string CProtoAddress::GetV4Address(int protocol) +{ + if (v4address.end() == v4address.find(protocol)) + return v4address[PROTOCOL_ANY]; + else + return v4address[protocol]; +} +#endif + +#ifdef LISTEN_IPV6 +std::string CProtoAddress::GetV6Address(int protocol) +{ + if (v6address.end() == v6address.find(protocol)) + return v6address[PROTOCOL_ANY]; + else + return v6address[protocol]; +} +#endif diff --git a/src/ProtoAddress.h b/src/ProtoAddress.h new file mode 100644 index 0000000..f4a1d78 --- /dev/null +++ b/src/ProtoAddress.h @@ -0,0 +1,46 @@ +#pragma once + +// +// Copyright © 2020 Thomas A. Eary, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include + +#include "Main.h" + +class CProtoAddress +{ +public: + CProtoAddress(); +#ifdef LISTEN_IPV4 + std::string GetV4Address(int protocol); +#endif +#ifdef LISTEN_IPV6 + std::string GetV6Address(int protocol); +#endif + +private: +#ifdef LISTEN_IPV4 + std::unordered_map v4address; +#endif +#ifdef LISTEN_IPV6 + std::unordered_map v6address; +#endif +}; diff --git a/src/Protocol.cpp b/src/Protocol.cpp new file mode 100644 index 0000000..9b8e4bb --- /dev/null +++ b/src/Protocol.cpp @@ -0,0 +1,389 @@ +// +// cprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DCSProtocol.h" +#include "Clients.h" +#include "Reflector.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CProtocol::CProtocol() : keep_running(true) {} + + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CProtocol::~CProtocol() +{ + // kill threads + Close(); + + // empty queue + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + m_Queue.pop(); + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// initialization + +bool CProtocol::Initialize(const char *type, int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // init reflector apparent callsign + m_ReflectorCallsign = g_Reflector.GetCallsign(); + + // reset stop flag + keep_running = true; + + // update the reflector callsign + if (type) + m_ReflectorCallsign.PatchCallsign(0, (const uint8 *)type, 3); + + // create our sockets +#ifdef LISTEN_IPV4 + if (has_ipv4) + { + const auto s = g_Reflector.m_Address.GetV4Address(ptype); + CIp ip4(AF_INET, port, s.c_str()); + if ( ip4.IsSet() ) + { + if (! m_Socket4.Open(ip4)) + return false; + } + std::cout << "Listening on " << ip4 << std::endl; + } +#endif + +#ifdef LISTEN_IPV6 + if (has_ipv6) + { + CIp ip6(AF_INET6, port, g_Reflector.m_Address.GetV6Address(ptype).c_str()); + if ( ip6.IsSet() ) + { + if (! m_Socket6.Open(ip6)) + { + m_Socket4.Close(); + return false; + } + std::cout << "Listening on " << ip6 << std::endl; + } + } +#endif + + try { + m_Future = std::async(std::launch::async, &CProtocol::Thread, this); + } + catch (const std::exception &e) + { + std::cerr << "Could not start protocol on port " << port << ": " << e.what() << std::endl; + m_Socket4.Close(); + m_Socket6.Close(); + return false; + } + + return true; +} + +void CProtocol::Thread() +{ + while (keep_running) + { + Task(); + } +} + +void CProtocol::Close(void) +{ + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } + m_Socket4.Close(); + m_Socket6.Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +bool CProtocol::EncodeDvPacket(const CPacket &packet, CBuffer *buffer) const +{ + if ( packet.IsDvFrame() ) + { + if ( packet.IsLastPacket() ) + return EncodeDvLastFramePacket((CDvLastFramePacket &)packet, buffer); + else + return EncodeDvFramePacket((CDvFramePacket &)packet, buffer); + } + if ( packet.IsDvHeader() ) + return EncodeDvHeaderPacket((CDvHeaderPacket &)packet, buffer); + + std::cerr << "Can't encode an unknown packet type!" << std::endl; + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CProtocol::OnDvFramePacketIn(std::unique_ptr &Frame, const CIp *Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip); + if ( stream ) + { + //std::cout << "DV frame" << "from " << *Ip << std::endl; + // and push + stream->Lock(); + stream->Push(std::move(Frame)); + stream->Unlock(); + } + // else + // { + // std::cout << "Orphaned Frame with ID " << Frame->GetStreamId() << " on " << *Ip << std::endl; + // } +} + +void CProtocol::OnDvLastFramePacketIn(std::unique_ptr &Frame, const CIp *Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Frame->GetStreamId(), Ip); + if ( stream ) + { + // push + stream->Lock(); + stream->Push(std::move(Frame)); + stream->Unlock(); + + // Don't close yet, this stops the last packet relfection bug that was fixed in upstream by the same change. + // Don't close the stream yet but rely on CheckStreamsTimeout + // mechanism, so the stream will be closed after the queues have + // been sinked out. This avoid last packets to be send back + // to transmitting client (master) + + } + // else + // { + // std::cout << "Orphaned Last Frame with ID " << Frame->GetStreamId() << " on " << *Ip << std::endl; + // } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// stream handle helpers + +CPacketStream *CProtocol::GetStream(uint16 uiStreamId, const CIp *Ip) +{ + for ( auto it=m_Streams.begin(); it!=m_Streams.end(); it++ ) + { + if ( (*it)->GetStreamId() == uiStreamId ) + { + // if Ip not nullptr, also check if IP match + if ( (Ip != nullptr) && ((*it)->GetOwnerIp() != nullptr) ) + { + if ( *Ip == *((*it)->GetOwnerIp()) ) + { + return *it; + } + } + } + } + // done + return nullptr; +} + +void CProtocol::CheckStreamsTimeout(void) +{ + for ( auto it=m_Streams.begin(); it!=m_Streams.end(); ) + { + // time out ? + (*it)->Lock(); + if ( (*it)->IsExpired() ) + { + // yes, close it + (*it)->Unlock(); + g_Reflector.CloseStream(*it); + // and remove it + it = m_Streams.erase(it); + } + else + { + (*it++)->Unlock(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// syntax helper + +bool CProtocol::IsNumber(char c) const +{ + return ((c >= '0') && (c <= '9')); +} + +bool CProtocol::IsLetter(char c) const +{ + return ((c >= 'A') && (c <= 'Z')); +} + +bool CProtocol::IsSpace(char c) const +{ + return (c == ' '); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DestId to Module helper + +char CProtocol::DmrDstIdToModule(uint32 tg) const +{ + return ((char)((tg % 26)-1) + 'A'); +} + +uint32 CProtocol::ModuleToDmrDestId(char m) const +{ + return (uint32)(m - 'A')+1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Receivers + +bool CProtocol::Receive6(CBuffer &buf, CIp &ip, int time_ms) +{ + return m_Socket6.Receive(buf, ip, time_ms); +} + +bool CProtocol::Receive4(CBuffer &buf, CIp &ip, int time_ms) +{ + return m_Socket4.Receive(buf, ip, time_ms); +} + +bool CProtocol::ReceiveDS(CBuffer &buf, CIp &ip, int time_ms) +{ + auto fd4 = m_Socket4.GetSocket(); + auto fd6 = m_Socket6.GetSocket(); + + if (fd4 < 0) + { + if (fd6 < 0) + return false; + return m_Socket6.Receive(buf, ip, time_ms); + } + else if (fd6 < 0) + return m_Socket4.Receive(buf, ip, time_ms); + + fd_set fset; + FD_ZERO(&fset); + FD_SET(fd4, &fset); + FD_SET(fd6, &fset); + int max = (fd4 > fd6) ? fd4 : fd6; + struct timeval tv; + tv.tv_sec = time_ms / 1000; + tv.tv_usec = (time_ms % 1000) * 1000; + + auto rval = select(max+1, &fset, 0, 0, &tv); + if (rval <= 0) + { + if (rval < 0) + std::cerr << "ReceiveDS select error: " << strerror(errno) << std::endl; + return false; + } + + if (FD_ISSET(fd4, &fset)) + return m_Socket4.ReceiveFrom(buf, ip); + else + return m_Socket6.ReceiveFrom(buf, ip); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// dual stack senders + +void CProtocol::Send(const CBuffer &buf, const CIp &Ip) const +{ + switch (Ip.GetFamily()) + { + case AF_INET: + m_Socket4.Send(buf, Ip); + break; + case AF_INET6: + m_Socket6.Send(buf, Ip); + break; + default: + std::cerr << "Wrong family: " << Ip.GetFamily() << std::endl; + break; + } +} + +void CProtocol::Send(const char *buf, const CIp &Ip) const +{ + switch (Ip.GetFamily()) + { + case AF_INET: + m_Socket4.Send(buf, Ip); + break; + case AF_INET6: + m_Socket6.Send(buf, Ip); + break; + default: + std::cerr << "ERROR: wrong family: " << Ip.GetFamily() << std::endl; + break; + } +} + +void CProtocol::Send(const CBuffer &buf, const CIp &Ip, uint16_t port) const +{ + switch (Ip.GetFamily()) + { + case AF_INET: + m_Socket4.Send(buf, Ip, port); + break; + case AF_INET6: + m_Socket6.Send(buf, Ip, port); + break; + default: + std::cerr << "Wrong family: " << Ip.GetFamily() << " on port " << port << std::endl; + break; + } +} + +void CProtocol::Send(const char *buf, const CIp &Ip, uint16_t port) const +{ + switch (Ip.GetFamily()) + { + case AF_INET: + m_Socket4.Send(buf, Ip, port); + break; + case AF_INET6: + m_Socket6.Send(buf, Ip, port); + break; + default: + std::cerr << "Wrong family: " << Ip.GetFamily() << " on port " << port << std::endl; + break; + } +} diff --git a/src/Protocol.h b/src/Protocol.h new file mode 100644 index 0000000..7538d31 --- /dev/null +++ b/src/Protocol.h @@ -0,0 +1,156 @@ +// +// DCSProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cprotocol_h +#define cprotocol_h + +#include "UDPSocket.h" +#include "PacketStream.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +// DMR defines +// slot n' +#define DMR_SLOT1 1 +#define DMR_SLOT2 2 +// call type +#define DMR_GROUP_CALL 0 +#define DMR_PRIVATE_CALL 1 +// frame type +#define DMR_FRAMETYPE_VOICE 0 +#define DMR_FRAMETYPE_VOICESYNC 1 +#define DMR_FRAMETYPE_DATA 2 +#define DMR_FRAMETYPE_DATASYNC 3 +// data type +#define DMR_DT_VOICE_PI_HEADER 0 +#define DMR_DT_VOICE_LC_HEADER 1 +#define DMR_DT_TERMINATOR_WITH_LC 2 +#define DMR_DT_CSBK 3 +#define DMR_DT_DATA_HEADER 6 +#define DMR_DT_RATE_12_DATA 7 +#define DMR_DT_RATE_34_DATA 8 +#define DMR_DT_IDLE 9 +#define DMR_DT_RATE_1_DATA 10 +// CRC masks +#define DMR_VOICE_LC_HEADER_CRC_MASK 0x96 +#define DMR_TERMINATOR_WITH_LC_CRC_MASK 0x99 +#define DMR_PI_HEADER_CRC_MASK 0x69 +#define DMR_DATA_HEADER_CRC_MASK 0xCC +#define DMR_CSBK_CRC_MASK 0xA5 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CProtocol +{ +public: + // constructor + CProtocol(); + + // destructor + virtual ~CProtocol(); + + // initialization + virtual bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + virtual void Close(void); + + // queue + CPacketQueue *GetQueue(void) { m_Queue.Lock(); return &m_Queue; } + void ReleaseQueue(void) { m_Queue.Unlock(); } + + // get + const CCallsign &GetReflectorCallsign(void)const { return m_ReflectorCallsign; } + + // task + void Thread(void); + virtual void Task(void) = 0; + +protected: + // packet encoding helpers + virtual bool EncodeDvPacket(const CPacket &, CBuffer *) const; + virtual bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const { return false; } + virtual bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const { return false; } + virtual bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const { return false; } + + // stream helpers + virtual void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &) {} + virtual void OnDvFramePacketIn(std::unique_ptr &, const CIp * = nullptr); + virtual void OnDvLastFramePacketIn(std::unique_ptr &, const CIp * = nullptr); + + // stream handle helpers + CPacketStream *GetStream(uint16, const CIp * = nullptr); + void CheckStreamsTimeout(void); + + // queue helper + virtual void HandleQueue(void) = 0; + + // keepalive helpers + virtual void HandleKeepalives(void) = 0; + + // syntax helper + bool IsNumber(char) const; + bool IsLetter(char) const; + bool IsSpace(char) const; + + // dmr DstId to Module helper + virtual char DmrDstIdToModule(uint32) const; + virtual uint32 ModuleToDmrDestId(char) const; + + bool Receive6(CBuffer &buf, CIp &Ip, int time_ms); + bool Receive4(CBuffer &buf, CIp &Ip, int time_ms); + bool ReceiveDS(CBuffer &buf, CIp &Ip, int time_ms); + + void Send(const CBuffer &buf, const CIp &Ip) const; + void Send(const char *buf, const CIp &Ip) const; + void Send(const CBuffer &buf, const CIp &Ip, uint16 port) const; + void Send(const char *buf, const CIp &Ip, uint16 port) const; + + // socket + CUdpSocket m_Socket4; + CUdpSocket m_Socket6; + + // streams + std::list m_Streams; + + // queue + CPacketQueue m_Queue; + + // thread + std::atomic keep_running; + std::future m_Future; + + // identity + CCallsign m_ReflectorCallsign; + + // debug + CTimePoint m_DebugTimer; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cprotocol_h */ diff --git a/src/Protocols.cpp b/src/Protocols.cpp new file mode 100644 index 0000000..6769be4 --- /dev/null +++ b/src/Protocols.cpp @@ -0,0 +1,104 @@ +// +// cprotocols.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "DExtraProtocol.h" +#include "DPlusProtocol.h" +#include "cdcsprotocol.h" +#ifndef NO_XLX +#include "ULXProtocol.h" +#include "DMRPlusProtocol.h" +#include "DMMMDVMProtocol.h" +#include "YSFProtocol.h" +#endif +#ifndef NO_G3 +#include "G3Protocol.h" +#endif +#include "Protocols.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CProtocols::~CProtocols() +{ + Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// initialization + +bool CProtocols::Init(void) +{ + m_Mutex.lock(); + { + m_Protocols.emplace_back(std::unique_ptr(new CDextraProtocol)); + if (! m_Protocols.back()->Initialize("XRF", PROTOCOL_DEXTRA, DEXTRA_PORT, DSTAR_IPV4, DSTAR_IPV6)) + return false; + + m_Protocols.emplace_back(std::unique_ptr(new CDplusProtocol)); + if (! m_Protocols.back()->Initialize("REF", PROTOCOL_DPLUS, DPLUS_PORT, DSTAR_IPV4, DSTAR_IPV6)) + return false; + + m_Protocols.emplace_back(std::unique_ptr(new CDcsProtocol)); + if (! m_Protocols.back()->Initialize("DCS", PROTOCOL_DCS, DCS_PORT, DSTAR_IPV4, DSTAR_IPV6)) + return false; + +#ifndef NO_XLX + m_Protocols.emplace_back(std::unique_ptr(new CDmrmmdvmProtocol)); + if (! m_Protocols.back()->Initialize(nullptr, PROTOCOL_DMRMMDVM, DMRMMDVM_PORT, DMR_IPV4, DMR_IPV6)) + return false; + + m_Protocols.emplace_back(std::unique_ptr(new CDmrplusProtocol)); + if (! m_Protocols.back()->Initialize(nullptr, PROTOCOL_DMRPLUS, DMRPLUS_PORT, DMR_IPV4, DMR_IPV6)) + return false; + + m_Protocols.emplace_back(std::unique_ptr(new CYsfProtocol)); + if (! m_Protocols.back()->Initialize("YSF", PROTOCOL_YSF, YSF_PORT, YSF_IPV4, YSF_IPV6)) + return false; + + m_Protocols.emplace_back(std::unique_ptr(new CUlxProtocol)); + if (! m_Protocols.back()->Initialize("XLX", PROTOCOL_XLX, XLX_PORT, DMR_IPV4, DMR_IPV6)) + return false; +#endif + +#ifndef NO_G3 + m_Protocols.emplace_back(std::unique_ptr(new CG3Protocol)); + if (! m_Protocols.back()->Initialize("XLX", PROTOCOL_G3, G3_DV_PORT, DMR_IPV4, DMR_IPV6)) + return false; +#endif + + } + m_Mutex.unlock(); + + // done + return true; +} + +void CProtocols::Close(void) +{ + m_Mutex.lock(); + m_Protocols.clear(); + m_Mutex.unlock(); +} diff --git a/src/Protocols.h b/src/Protocols.h new file mode 100644 index 0000000..889966d --- /dev/null +++ b/src/Protocols.h @@ -0,0 +1,63 @@ +// +// Protocols.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 01/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cprotocols_h +#define cprotocols_h + +#include "DCSProtocol.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CProtocols +{ +public: + // destructors + ~CProtocols(); + + // initialization + bool Init(void); + void Close(void); + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // pass-thru + std::list>::iterator begin() { return m_Protocols.begin(); } + std::list>::iterator end() { return m_Protocols.end(); } + +protected: + // data + std::mutex m_Mutex; + std::list> m_Protocols; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// + +#endif /* cprotocols_h */ diff --git a/src/QR1676.cpp b/src/QR1676.cpp new file mode 100644 index 0000000..fe76c90 --- /dev/null +++ b/src/QR1676.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "QR1676.h" + +#include +#include + +const unsigned int ENCODING_TABLE_1676[] = +{ + 0x0000U, 0x0273U, 0x04E5U, 0x0696U, 0x09C9U, 0x0BBAU, 0x0D2CU, 0x0F5FU, 0x11E2U, 0x1391U, 0x1507U, 0x1774U, + 0x182BU, 0x1A58U, 0x1CCEU, 0x1EBDU, 0x21B7U, 0x23C4U, 0x2552U, 0x2721U, 0x287EU, 0x2A0DU, 0x2C9BU, 0x2EE8U, + 0x3055U, 0x3226U, 0x34B0U, 0x36C3U, 0x399CU, 0x3BEFU, 0x3D79U, 0x3F0AU, 0x411EU, 0x436DU, 0x45FBU, 0x4788U, + 0x48D7U, 0x4AA4U, 0x4C32U, 0x4E41U, 0x50FCU, 0x528FU, 0x5419U, 0x566AU, 0x5935U, 0x5B46U, 0x5DD0U, 0x5FA3U, + 0x60A9U, 0x62DAU, 0x644CU, 0x663FU, 0x6960U, 0x6B13U, 0x6D85U, 0x6FF6U, 0x714BU, 0x7338U, 0x75AEU, 0x77DDU, + 0x7882U, 0x7AF1U, 0x7C67U, 0x7E14U, 0x804FU, 0x823CU, 0x84AAU, 0x86D9U, 0x8986U, 0x8BF5U, 0x8D63U, 0x8F10U, + 0x91ADU, 0x93DEU, 0x9548U, 0x973BU, 0x9864U, 0x9A17U, 0x9C81U, 0x9EF2U, 0xA1F8U, 0xA38BU, 0xA51DU, 0xA76EU, + 0xA831U, 0xAA42U, 0xACD4U, 0xAEA7U, 0xB01AU, 0xB269U, 0xB4FFU, 0xB68CU, 0xB9D3U, 0xBBA0U, 0xBD36U, 0xBF45U, + 0xC151U, 0xC322U, 0xC5B4U, 0xC7C7U, 0xC898U, 0xCAEBU, 0xCC7DU, 0xCE0EU, 0xD0B3U, 0xD2C0U, 0xD456U, 0xD625U, + 0xD97AU, 0xDB09U, 0xDD9FU, 0xDFECU, 0xE0E6U, 0xE295U, 0xE403U, 0xE670U, 0xE92FU, 0xEB5CU, 0xEDCAU, 0xEFB9U, + 0xF104U, 0xF377U, 0xF5E1U, 0xF792U, 0xF8CDU, 0xFABEU, 0xFC28U, 0xFE5BU +}; + +const unsigned int DECODING_TABLE_1576[] = +{ + 0x0000U, 0x0001U, 0x0002U, 0x0003U, 0x0004U, 0x0005U, 0x0006U, 0x4020U, 0x0008U, 0x0009U, 0x000AU, 0x000BU, + 0x000CU, 0x000DU, 0x2081U, 0x2080U, 0x0010U, 0x0011U, 0x0012U, 0x0013U, 0x0014U, 0x0C00U, 0x0016U, 0x0C02U, + 0x0018U, 0x0120U, 0x001AU, 0x0122U, 0x4102U, 0x0124U, 0x4100U, 0x4101U, 0x0020U, 0x0021U, 0x0022U, 0x4004U, + 0x0024U, 0x4002U, 0x4001U, 0x4000U, 0x0028U, 0x0110U, 0x1800U, 0x1801U, 0x002CU, 0x400AU, 0x4009U, 0x4008U, + 0x0030U, 0x0108U, 0x0240U, 0x0241U, 0x0034U, 0x4012U, 0x4011U, 0x4010U, 0x0101U, 0x0100U, 0x0103U, 0x0102U, + 0x0105U, 0x0104U, 0x1401U, 0x1400U, 0x0040U, 0x0041U, 0x0042U, 0x0043U, 0x0044U, 0x0045U, 0x0046U, 0x4060U, + 0x0048U, 0x0049U, 0x0301U, 0x0300U, 0x004CU, 0x1600U, 0x0305U, 0x0304U, 0x0050U, 0x0051U, 0x0220U, 0x0221U, + 0x3000U, 0x4200U, 0x3002U, 0x4202U, 0x0058U, 0x1082U, 0x1081U, 0x1080U, 0x3008U, 0x4208U, 0x2820U, 0x1084U, + 0x0060U, 0x0061U, 0x0210U, 0x0211U, 0x0480U, 0x0481U, 0x4041U, 0x4040U, 0x0068U, 0x2402U, 0x2401U, 0x2400U, + 0x0488U, 0x3100U, 0x2810U, 0x2404U, 0x0202U, 0x0880U, 0x0200U, 0x0201U, 0x0206U, 0x0884U, 0x0204U, 0x0205U, + 0x0141U, 0x0140U, 0x0208U, 0x0209U, 0x2802U, 0x0144U, 0x2800U, 0x2801U, 0x0080U, 0x0081U, 0x0082U, 0x0A00U, + 0x0084U, 0x0085U, 0x2009U, 0x2008U, 0x0088U, 0x0089U, 0x2005U, 0x2004U, 0x2003U, 0x2002U, 0x2001U, 0x2000U, + 0x0090U, 0x0091U, 0x0092U, 0x1048U, 0x0602U, 0x0C80U, 0x0600U, 0x0601U, 0x0098U, 0x1042U, 0x1041U, 0x1040U, + 0x2013U, 0x2012U, 0x2011U, 0x2010U, 0x00A0U, 0x00A1U, 0x00A2U, 0x4084U, 0x0440U, 0x0441U, 0x4081U, 0x4080U, + 0x6000U, 0x1200U, 0x6002U, 0x1202U, 0x6004U, 0x2022U, 0x2021U, 0x2020U, 0x0841U, 0x0840U, 0x2104U, 0x0842U, + 0x2102U, 0x0844U, 0x2100U, 0x2101U, 0x0181U, 0x0180U, 0x0B00U, 0x0182U, 0x5040U, 0x0184U, 0x2108U, 0x2030U, + 0x00C0U, 0x00C1U, 0x4401U, 0x4400U, 0x0420U, 0x0421U, 0x0422U, 0x4404U, 0x0900U, 0x0901U, 0x1011U, 0x1010U, + 0x0904U, 0x2042U, 0x2041U, 0x2040U, 0x0821U, 0x0820U, 0x1009U, 0x1008U, 0x4802U, 0x0824U, 0x4800U, 0x4801U, + 0x1003U, 0x1002U, 0x1001U, 0x1000U, 0x0501U, 0x0500U, 0x1005U, 0x1004U, 0x0404U, 0x0810U, 0x1100U, 0x1101U, + 0x0400U, 0x0401U, 0x0402U, 0x0403U, 0x040CU, 0x0818U, 0x1108U, 0x1030U, 0x0408U, 0x0409U, 0x040AU, 0x2060U, + 0x0801U, 0x0800U, 0x0280U, 0x0802U, 0x0410U, 0x0804U, 0x0412U, 0x0806U, 0x0809U, 0x0808U, 0x1021U, 0x1020U, + 0x5000U, 0x2200U, 0x5002U, 0x2202U +}; + +#define X14 0x00004000 /* vector representation of X^{14} */ +#define X8 0x00000100 /* vector representation of X^{8} */ +#define MASK7 0xffffff00 /* auxiliary vector for testing */ +#define GENPOL 0x00000139 /* generator polinomial, g(x) */ + +unsigned int CQR1676::getSyndrome1576(unsigned int pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ + unsigned int aux = X14; + + if (pattern >= X8) + { + while (pattern & MASK7) + { + while (!(aux & pattern)) + aux = aux >> 1; + + pattern ^= (aux / X8) * GENPOL; + } + } + + return pattern; +} + +// Compute the EMB against a precomputed list of correct words +void CQR1676::encode(unsigned char* data) +{ + assert(data != nullptr); + + unsigned int value = (data[0U] >> 1) & 0x7FU; + unsigned int cksum = ENCODING_TABLE_1676[value]; + + data[0U] = cksum >> 8; + data[1U] = cksum & 0xFFU; +} + +unsigned char CQR1676::decode(const unsigned char* data) +{ + assert(data != nullptr); + + unsigned int code = (data[0U] << 7) + (data[1U] >> 1); + unsigned int syndrome = getSyndrome1576(code); + unsigned int error_pattern = DECODING_TABLE_1576[syndrome]; + + code ^= error_pattern; + + return code >> 7; +} diff --git a/src/QR1676.h b/src/QR1676.h new file mode 100644 index 0000000..31c874d --- /dev/null +++ b/src/QR1676.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef QR1676_H +#define QR1676_H + +class CQR1676 +{ +public: + static void encode(unsigned char* data); + + static unsigned char decode(const unsigned char* data); + +private: + static unsigned int getSyndrome1576(unsigned int pattern); +}; + +#endif diff --git a/src/RS129.cpp b/src/RS129.cpp new file mode 100644 index 0000000..a897d29 --- /dev/null +++ b/src/RS129.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "RS129.h" + +#include +#include +#include + +const unsigned int NPAR = 3U; + +/* Maximum degree of various polynomials. */ +//const unsigned int MAXDEG = NPAR * 2U; + +/* Generator Polynomial */ +const unsigned char POLY[] = {64U, 56U, 14U, 1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U}; + +const unsigned char EXP_TABLE[] = +{ + 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, + 0x4CU, 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, + 0x9DU, 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, + 0x46U, 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, + 0x5FU, 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, + 0xFDU, 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, + 0xD9U, 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, + 0x81U, 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, + 0x85U, 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, + 0xA8U, 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, + 0xE6U, 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, + 0xE3U, 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, + 0x82U, 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, + 0x51U, 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, + 0x12U, 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, + 0x2CU, 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, + 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1DU, 0x3AU, 0x74U, 0xE8U, 0xCDU, 0x87U, 0x13U, 0x26U, 0x4CU, + 0x98U, 0x2DU, 0x5AU, 0xB4U, 0x75U, 0xEAU, 0xC9U, 0x8FU, 0x03U, 0x06U, 0x0CU, 0x18U, 0x30U, 0x60U, 0xC0U, 0x9DU, + 0x27U, 0x4EU, 0x9CU, 0x25U, 0x4AU, 0x94U, 0x35U, 0x6AU, 0xD4U, 0xB5U, 0x77U, 0xEEU, 0xC1U, 0x9FU, 0x23U, 0x46U, + 0x8CU, 0x05U, 0x0AU, 0x14U, 0x28U, 0x50U, 0xA0U, 0x5DU, 0xBAU, 0x69U, 0xD2U, 0xB9U, 0x6FU, 0xDEU, 0xA1U, 0x5FU, + 0xBEU, 0x61U, 0xC2U, 0x99U, 0x2FU, 0x5EU, 0xBCU, 0x65U, 0xCAU, 0x89U, 0x0FU, 0x1EU, 0x3CU, 0x78U, 0xF0U, 0xFDU, + 0xE7U, 0xD3U, 0xBBU, 0x6BU, 0xD6U, 0xB1U, 0x7FU, 0xFEU, 0xE1U, 0xDFU, 0xA3U, 0x5BU, 0xB6U, 0x71U, 0xE2U, 0xD9U, + 0xAFU, 0x43U, 0x86U, 0x11U, 0x22U, 0x44U, 0x88U, 0x0DU, 0x1AU, 0x34U, 0x68U, 0xD0U, 0xBDU, 0x67U, 0xCEU, 0x81U, + 0x1FU, 0x3EU, 0x7CU, 0xF8U, 0xEDU, 0xC7U, 0x93U, 0x3BU, 0x76U, 0xECU, 0xC5U, 0x97U, 0x33U, 0x66U, 0xCCU, 0x85U, + 0x17U, 0x2EU, 0x5CU, 0xB8U, 0x6DU, 0xDAU, 0xA9U, 0x4FU, 0x9EU, 0x21U, 0x42U, 0x84U, 0x15U, 0x2AU, 0x54U, 0xA8U, + 0x4DU, 0x9AU, 0x29U, 0x52U, 0xA4U, 0x55U, 0xAAU, 0x49U, 0x92U, 0x39U, 0x72U, 0xE4U, 0xD5U, 0xB7U, 0x73U, 0xE6U, + 0xD1U, 0xBFU, 0x63U, 0xC6U, 0x91U, 0x3FU, 0x7EU, 0xFCU, 0xE5U, 0xD7U, 0xB3U, 0x7BU, 0xF6U, 0xF1U, 0xFFU, 0xE3U, + 0xDBU, 0xABU, 0x4BU, 0x96U, 0x31U, 0x62U, 0xC4U, 0x95U, 0x37U, 0x6EU, 0xDCU, 0xA5U, 0x57U, 0xAEU, 0x41U, 0x82U, + 0x19U, 0x32U, 0x64U, 0xC8U, 0x8DU, 0x07U, 0x0EU, 0x1CU, 0x38U, 0x70U, 0xE0U, 0xDDU, 0xA7U, 0x53U, 0xA6U, 0x51U, + 0xA2U, 0x59U, 0xB2U, 0x79U, 0xF2U, 0xF9U, 0xEFU, 0xC3U, 0x9BU, 0x2BU, 0x56U, 0xACU, 0x45U, 0x8AU, 0x09U, 0x12U, + 0x24U, 0x48U, 0x90U, 0x3DU, 0x7AU, 0xF4U, 0xF5U, 0xF7U, 0xF3U, 0xFBU, 0xEBU, 0xCBU, 0x8BU, 0x0BU, 0x16U, 0x2CU, + 0x58U, 0xB0U, 0x7DU, 0xFAU, 0xE9U, 0xCFU, 0x83U, 0x1BU, 0x36U, 0x6CU, 0xD8U, 0xADU, 0x47U, 0x8EU, 0x01U, 0x00U +}; + +const unsigned char LOG_TABLE[] = +{ + 0x00U, 0x00U, 0x01U, 0x19U, 0x02U, 0x32U, 0x1AU, 0xC6U, 0x03U, 0xDFU, 0x33U, 0xEEU, 0x1BU, 0x68U, 0xC7U, 0x4BU, + 0x04U, 0x64U, 0xE0U, 0x0EU, 0x34U, 0x8DU, 0xEFU, 0x81U, 0x1CU, 0xC1U, 0x69U, 0xF8U, 0xC8U, 0x08U, 0x4CU, 0x71U, + 0x05U, 0x8AU, 0x65U, 0x2FU, 0xE1U, 0x24U, 0x0FU, 0x21U, 0x35U, 0x93U, 0x8EU, 0xDAU, 0xF0U, 0x12U, 0x82U, 0x45U, + 0x1DU, 0xB5U, 0xC2U, 0x7DU, 0x6AU, 0x27U, 0xF9U, 0xB9U, 0xC9U, 0x9AU, 0x09U, 0x78U, 0x4DU, 0xE4U, 0x72U, 0xA6U, + 0x06U, 0xBFU, 0x8BU, 0x62U, 0x66U, 0xDDU, 0x30U, 0xFDU, 0xE2U, 0x98U, 0x25U, 0xB3U, 0x10U, 0x91U, 0x22U, 0x88U, + 0x36U, 0xD0U, 0x94U, 0xCEU, 0x8FU, 0x96U, 0xDBU, 0xBDU, 0xF1U, 0xD2U, 0x13U, 0x5CU, 0x83U, 0x38U, 0x46U, 0x40U, + 0x1EU, 0x42U, 0xB6U, 0xA3U, 0xC3U, 0x48U, 0x7EU, 0x6EU, 0x6BU, 0x3AU, 0x28U, 0x54U, 0xFAU, 0x85U, 0xBAU, 0x3DU, + 0xCAU, 0x5EU, 0x9BU, 0x9FU, 0x0AU, 0x15U, 0x79U, 0x2BU, 0x4EU, 0xD4U, 0xE5U, 0xACU, 0x73U, 0xF3U, 0xA7U, 0x57U, + 0x07U, 0x70U, 0xC0U, 0xF7U, 0x8CU, 0x80U, 0x63U, 0x0DU, 0x67U, 0x4AU, 0xDEU, 0xEDU, 0x31U, 0xC5U, 0xFEU, 0x18U, + 0xE3U, 0xA5U, 0x99U, 0x77U, 0x26U, 0xB8U, 0xB4U, 0x7CU, 0x11U, 0x44U, 0x92U, 0xD9U, 0x23U, 0x20U, 0x89U, 0x2EU, + 0x37U, 0x3FU, 0xD1U, 0x5BU, 0x95U, 0xBCU, 0xCFU, 0xCDU, 0x90U, 0x87U, 0x97U, 0xB2U, 0xDCU, 0xFCU, 0xBEU, 0x61U, + 0xF2U, 0x56U, 0xD3U, 0xABU, 0x14U, 0x2AU, 0x5DU, 0x9EU, 0x84U, 0x3CU, 0x39U, 0x53U, 0x47U, 0x6DU, 0x41U, 0xA2U, + 0x1FU, 0x2DU, 0x43U, 0xD8U, 0xB7U, 0x7BU, 0xA4U, 0x76U, 0xC4U, 0x17U, 0x49U, 0xECU, 0x7FU, 0x0CU, 0x6FU, 0xF6U, + 0x6CU, 0xA1U, 0x3BU, 0x52U, 0x29U, 0x9DU, 0x55U, 0xAAU, 0xFBU, 0x60U, 0x86U, 0xB1U, 0xBBU, 0xCCU, 0x3EU, 0x5AU, + 0xCBU, 0x59U, 0x5FU, 0xB0U, 0x9CU, 0xA9U, 0xA0U, 0x51U, 0x0BU, 0xF5U, 0x16U, 0xEBU, 0x7AU, 0x75U, 0x2CU, 0xD7U, + 0x4FU, 0xAEU, 0xD5U, 0xE9U, 0xE6U, 0xE7U, 0xADU, 0xE8U, 0x74U, 0xD6U, 0xF4U, 0xEAU, 0xA8U, 0x50U, 0x58U, 0xAFU +}; + +/* multiplication using logarithms */ +static unsigned char gmult(unsigned char a, unsigned char b) +{ + if (a == 0U || b == 0U) + return 0U; + + unsigned int i = LOG_TABLE[a]; + unsigned int j = LOG_TABLE[b]; + + return EXP_TABLE[i + j]; +} + +/* Simulate a LFSR with generator polynomial for n byte RS code. + * Pass in a pointer to the data array, and amount of data. + * + * The parity bytes are deposited into parity. + */ +void CRS129::encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity) +{ + assert(msg != nullptr); + assert(parity != nullptr); + + for (unsigned int i = 0U; i < NPAR + 1U; i++) + parity[i] = 0x00U; + + for (unsigned int i = 0U; i < nbytes; i++) + { + unsigned char dbyte = msg[i] ^ parity[NPAR - 1U]; + + for (int j = NPAR - 1; j > 0; j--) + parity[j] = parity[j - 1] ^ ::gmult(POLY[j], dbyte); + + parity[0] = ::gmult(POLY[0], dbyte); + } +} + +// Reed-Solomon (12,9) check +bool CRS129::check(const unsigned char* in) +{ + assert(in != nullptr); + + unsigned char parity[4U]; + encode(in, 9U, parity); + + return in[9U] == parity[2U] && in[10U] == parity[1U] && in[11U] == parity[0U]; +} diff --git a/src/RS129.h b/src/RS129.h new file mode 100644 index 0000000..60f99bd --- /dev/null +++ b/src/RS129.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2015 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(RS129_H) +#define RS129_H + +class CRS129 +{ +public: + static bool check(const unsigned char* in); + + static void encode(const unsigned char* msg, unsigned int nbytes, unsigned char* parity); +}; + +#endif diff --git a/src/RawSocket.cpp b/src/RawSocket.cpp new file mode 100644 index 0000000..990f5ca --- /dev/null +++ b/src/RawSocket.cpp @@ -0,0 +1,148 @@ +// +// crawsocket.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "RawSocket.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CRawSocket::CRawSocket() +{ + m_Socket = -1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CRawSocket::~CRawSocket() +{ + if ( m_Socket != -1 ) + { + Close(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// open & close + +bool CRawSocket::Open(uint16 uiProto) +{ + bool open = false; + int on = 1; + + // create socket + m_Socket = socket(AF_INET, SOCK_RAW, uiProto); + if ( m_Socket != -1 ) + { + fcntl(m_Socket, F_SETFL, O_NONBLOCK); + open = true; + m_Proto = uiProto; + } + + // done + return open; +} + +void CRawSocket::Close(void) +{ + if ( m_Socket != -1 ) + { + close(m_Socket); + m_Socket = -1; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// read + +int CRawSocket::Receive(uint8_t *Buffer, CIp *Ip, int timeout) +{ + struct sockaddr_in Sin; + fd_set FdSet; + unsigned int uiFromLen = sizeof(struct sockaddr_in); + int iRecvLen = -1; + struct timeval tv; + + // socket valid ? + if ( m_Socket != -1 ) + { + // control socket + FD_ZERO(&FdSet); + FD_SET(m_Socket, &FdSet); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + select(m_Socket + 1, &FdSet, 0, 0, &tv); + + // read + iRecvLen = (int)recvfrom(m_Socket, Buffer, RAW_BUFFER_LENMAX, 0, (struct sockaddr *)&Sin, &uiFromLen); + + // handle + if ( iRecvLen != -1 ) + { + // get IP + memcpy(Ip->GetPointer(), &Sin, sizeof(struct sockaddr_in)); + } + } + + // done + return iRecvLen; +} + +// protocol specific + +// ICMP + +int CRawSocket::IcmpReceive(uint8_t *Buffer, CIp *Ip, int timeout) +{ + int iIcmpType = -1; + int iRecv; + + if (m_Proto == IPPROTO_ICMP) + { + iRecv = Receive(Buffer, Ip, timeout); + + if (iRecv >= (int)(sizeof(struct ip) + sizeof(struct icmp))) + { + struct ip *iph = (struct ip *)Buffer; + int iphdrlen = iph->ip_hl * 4; + struct icmp *icmph = (struct icmp *)((unsigned char *)iph + iphdrlen); + struct ip *remote_iph = (struct ip *)((unsigned char *)icmph + 8); + + iIcmpType = icmph->icmp_type; + + struct sockaddr_in Sin; + bzero(&Sin, sizeof(Sin)); + Sin.sin_family = AF_INET; + Sin.sin_addr.s_addr = remote_iph->ip_dst.s_addr; + + memcpy(Ip->GetPointer(), &Sin, sizeof(struct sockaddr_in)); + + } + } + return iIcmpType; +} diff --git a/src/RawSocket.h b/src/RawSocket.h new file mode 100644 index 0000000..f0bcbc9 --- /dev/null +++ b/src/RawSocket.h @@ -0,0 +1,101 @@ +// +// RawSocket.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +// Description: +// Raw socket access class with protocol specific + + +#ifndef crawsocket_h +#define crawsocket_h + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "IP.h" +#include "Buffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define RAW_BUFFER_LENMAX 65536 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CRawSocket +{ +public: + // constructor + CRawSocket(); + + // destructor + ~CRawSocket(); + + // open & close + bool Open(uint16); + void Close(void); + int GetSocket(void) { return m_Socket; } + + // read + + // if ETH_P_ALL is used, the received data buffer will hold + // the ethernet header (struct ethhdr) followed by the IP header (struct iphdr), + // the protocol header (e.g tcp, udp, icmp) and the data. + // For specific protocols, the data content may vary depending on the protocol + // Returns the number of received bytes in buffer + +protected: + int Receive(uint8_t *, CIp *, int); + + // ICMP receive helper + // parameters: + // buffer - packet receive buffer (starting with ip header) + // ip - remote address (filled in on receive) + // timeout - receive timeout in msec + // return value: + // ICMP type, -1 if nothing was received + +public: + int IcmpReceive(uint8_t *, CIp *, int); + + // write + // no write support - complexity makes it out of scope for now + // to be added if needed + +protected: + // data + int m_Socket; + int m_Proto; + struct sockaddr_in m_SocketAddr; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* crawsocket_h */ diff --git a/src/Reflector.cpp b/src/Reflector.cpp new file mode 100644 index 0000000..3e28d70 --- /dev/null +++ b/src/Reflector.cpp @@ -0,0 +1,729 @@ +// +// creflector.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "GateKeeper.h" +#include "cdmriddirfile.h" +#include "DMRIdDirHttp.h" +#include "Transcoder.h" +#include "YSFNodeDirFile.h" +#include "YSFNodeDirhttp.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CReflector::CReflector() +{ + keep_running = true; +#ifdef DEBUG_DUMPFILE + m_DebugFile.open("/Users/jeanluc/Desktop/xlxdebug.txt"); +#endif +} + +CReflector::CReflector(const CCallsign &callsign) +{ +#ifdef DEBUG_DUMPFILE + m_DebugFile.close(); +#endif + keep_running = true; + m_Callsign = callsign; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CReflector::~CReflector() +{ + keep_running = false; + if ( m_XmlReportFuture.valid() ) + { + m_XmlReportFuture.get(); + } +#ifdef JSON_MONITOR + if ( m_JsonReportFuture.valid() ) + { + m_JsonReportFuture.get(); + } +#endif + for ( int i = 0; i < NB_OF_MODULES; i++ ) + { + if ( m_RouterFuture[i].valid() ) + { + m_RouterFuture[i].get(); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CReflector::Start(void) +{ + // let's go! + keep_running = true; + + // init gate keeper. It can only return true! + g_GateKeeper.Init(); + +#ifndef NO_XLX + // init dmrid directory. No need to check the return value. + g_DmridDir.Init(); + + // init wiresx node directory. Likewise with the return vale. + g_YsfNodeDir.Init(); + +#ifdef TRANSCODER_IP + // init the transcoder + if (! g_Transcoder.Init()) + return false; +#endif +#endif + + // create protocols + if (! m_Protocols.Init()) + { + m_Protocols.Close(); + return false; + } + + // start one thread per reflector module + for ( int i = 0; i < NB_OF_MODULES; i++ ) + { + m_RouterFuture[i] = std::async(std::launch::async, &CReflector::RouterThread, this, &(m_Stream[i])); + } + + // start the reporting threads + m_XmlReportFuture = std::async(std::launch::async, &CReflector::XmlReportThread, this); +#ifdef JSON_MONITOR + m_JsonReportFuture = std::async(std::launch::async, &CReflector::JsonReportThread, this); +#endif + + return true; +} + +void CReflector::Stop(void) +{ + // stop & delete all threads + keep_running = false; + + // stop & delete report threads + if ( m_XmlReportFuture.valid() ) + { + m_XmlReportFuture.get(); + } +#ifdef JSON_MONITOR + if ( m_JsonReportFuture.valid() ) + { + m_JsonReportFuture.get(); + } +#endif + + // stop & delete all router thread + for ( int i = 0; i < NB_OF_MODULES; i++ ) + { + if ( m_RouterFuture[i].valid() ) + { + m_RouterFuture[i].get(); + } + } + + // close protocols + m_Protocols.Close(); + + // close gatekeeper + g_GateKeeper.Close(); + +#ifdef TRANSCODER_IP + // close transcoder + g_Transcoder.Close(); +#endif + +#ifndef NO_XLX + // close databases + g_DmridDir.Close(); + g_YsfNodeDir.Close(); +#endif +} + +//////////////////////////////////////////////////////////////////////////////////////// +// stream opening & closing + +bool CReflector::IsStreaming(char module) +{ + return false; +} + +// clients MUST have bee locked by the caller so we can freely access it within the fuction +CPacketStream *CReflector::OpenStream(std::unique_ptr &DvHeader, std::shared_ptrclient) +{ + // check sid is not zero + if ( 0U == DvHeader->GetStreamId() ) + return nullptr; + + // check if client is valid candidate + if ( ! m_Clients.IsClient(client) || client->IsAMaster() ) + return nullptr; + + // check if no stream with same streamid already open + // to prevent loops + if ( IsStreamOpen(DvHeader) ) + { + std::cerr << "Detected stream loop on module " << DvHeader->GetRpt2Module() << " for client " << client->GetCallsign() << " with sid " << DvHeader->GetStreamId() << std::endl; + return nullptr; + } + + // get the module's queue + char module = DvHeader->GetRpt2Module(); + CPacketStream *stream = GetStream(module); + if ( stream == nullptr ) + return nullptr; + + stream->Lock(); + // is it available ? + if ( stream->OpenPacketStream(*DvHeader, client) ) + { + // stream open, mark client as master + // so that it can't be deleted + client->SetMasterOfModule(module); + + // update last heard time + client->Heard(); + + // report + std::cout << "Opening stream on module " << module << " for client " << client->GetCallsign() << " with sid " << DvHeader->GetStreamId() << " by user " << DvHeader->GetMyCallsign() << std::endl; + + // and push header packet + stream->Push(std::move(DvHeader)); + + // notify + g_Reflector.OnStreamOpen(stream->GetUserCallsign()); + + } + stream->Unlock(); + return stream; +} + +void CReflector::CloseStream(CPacketStream *stream) +{ + if ( stream != nullptr ) + { + // wait queue is empty. this waits forever + bool bEmpty = false; + do + { + stream->Lock(); + // do not use stream->IsEmpty() has this "may" never succeed + // and anyway, the DvLastFramPacket short-circuit the transcoder + // loop queues + bEmpty = stream->empty(); + stream->Unlock(); + if ( !bEmpty ) + CTimePoint::TaskSleepFor(10); + } + while (!bEmpty); + + GetClients(); // lock clients + stream->Lock(); // lock stream + + // get and check the master + std::shared_ptrclient = stream->GetOwnerClient(); + if ( client != nullptr ) + { + // client no longer a master + client->NotAMaster(); + + // notify + OnStreamClose(stream->GetUserCallsign()); + + std::cout << "Closing stream of module " << GetStreamModule(stream) << std::endl; + } + + // release clients + ReleaseClients(); + + // unlock before closing + // to avoid double lock in assiociated + // codecstream close/thread-join + stream->Unlock(); + + // and stop the queue + stream->ClosePacketStream(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// router threads + +void CReflector::RouterThread(CPacketStream *streamIn) +{ + // get our module + uint8 uiModuleId = GetStreamModule(streamIn); + + // get on input queue + std::unique_ptr packet; + + while (keep_running) + { + // any packet in our input queue ? + streamIn->Lock(); + if ( !streamIn->empty() ) + { + // get the packet + packet = streamIn->front(); + streamIn->pop(); + } + else + { + packet = nullptr; + } + streamIn->Unlock(); + + // route it + if ( packet != nullptr ) + { + // set origin + packet->SetModuleId(uiModuleId); + + // iterate on all protocols + m_Protocols.Lock(); + for ( auto it=m_Protocols.begin(); it!=m_Protocols.end(); it++ ) + { + // duplicate packet + auto packetClone = packet->Duplicate(); + + // if packet is header, update RPT2 according to protocol + if ( packetClone->IsDvHeader() ) + { + // get our callsign + CCallsign csRPT = (*it)->GetReflectorCallsign(); + csRPT.SetModule(GetStreamModule(streamIn)); + (dynamic_cast(packetClone.get()))->SetRpt2Callsign(csRPT); + } + + // and push it + CPacketQueue *queue = (*it)->GetQueue(); + queue->push(packetClone); + (*it)->ReleaseQueue(); + } + m_Protocols.Unlock(); + } + else + { + CTimePoint::TaskSleepFor(10); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// report threads + +void CReflector::XmlReportThread() +{ + while (keep_running) + { + // report to xml file + std::ofstream xmlFile; + xmlFile.open(XML_PATH, std::ios::out | std::ios::trunc); + if ( xmlFile.is_open() ) + { + // write xml file + WriteXmlFile(xmlFile); + + // and close file + xmlFile.close(); + } +#ifndef DEBUG_NO_ERROR_ON_XML_OPEN_FAIL + else + { + std::cout << "Failed to open " << XML_PATH << std::endl; + } +#endif + + // and wait a bit + for (int i=0; i< XML_UPDATE_PERIOD && keep_running; i++) + CTimePoint::TaskSleepFor(1000); + } +} + +#ifdef JSON_MONITOR +void CReflector::JsonReportThread() +{ + CUdpSocket Socket; + CBuffer Buffer; + CIp Ip; + bool bOn; + + // init variable + bOn = false; + + // create listening socket + if ( Socket.Open(JSON_PORT) ) + { + // and loop + while (keep_running) + { + // any command ? + if ( Socket.Receive(Buffer, Ip, 50) ) + { + // check verb + if ( Buffer.Compare((uint8 *)"hello", 5) == 0 ) + { + std::cout << "Monitor socket connected with " << Ip << std::endl; + + // connected + bOn = true; + + // announce ourselves + SendJsonReflectorObject(Socket, Ip); + + // dump tables + SendJsonNodesObject(Socket, Ip); + SendJsonStationsObject(Socket, Ip); + } + else if ( Buffer.Compare((uint8 *)"bye", 3) == 0 ) + { + std::cout << "Monitor socket disconnected" << std::endl; + + // diconnected + bOn = false; + } + } + + // any notifications ? + CNotification notification; + m_Notifications.Lock(); + if ( !m_Notifications.empty() ) + { + // get the packet + notification = m_Notifications.front(); + m_Notifications.pop(); + } + m_Notifications.Unlock(); + + // handle it + if ( bOn ) + { + switch ( notification.GetId() ) + { + case NOTIFICATION_CLIENTS: + case NOTIFICATION_PEERS: + //std::cout << "Monitor updating nodes table" << std::endl; + SendJsonNodesObject(Socket, Ip); + break; + case NOTIFICATION_USERS: + //std::cout << "Monitor updating stations table" << std::endl; + SendJsonStationsObject(Socket, Ip); + break; + case NOTIFICATION_STREAM_OPEN: + //std::cout << "Monitor notify station " << notification.GetCallsign() << "going ON air" << std::endl; + SendJsonStationsObject(Socket, Ip); + SendJsonOnairObject(Socket, Ip, notification.GetCallsign()); + break; + case NOTIFICATION_STREAM_CLOSE: + //std::cout << "Monitor notify station " << notification.GetCallsign() << "going OFF air" << std::endl; + SendJsonOffairObject(Socket, Ip, notification.GetCallsign()); + break; + case NOTIFICATION_NONE: + default: + // nothing to do, just sleep a bit + CTimePoint::TaskSleepFor(250); + break; + } + } + } + } + else + { + std::cout << "Error creating monitor socket" << std::endl; + } +} +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// notifications + +void CReflector::OnPeersChanged(void) +{ + CNotification notification(NOTIFICATION_PEERS); + + m_Notifications.Lock(); + m_Notifications.push(notification); + m_Notifications.Unlock(); +} + +void CReflector::OnClientsChanged(void) +{ + CNotification notification(NOTIFICATION_CLIENTS); + + m_Notifications.Lock(); + m_Notifications.push(notification); + m_Notifications.Unlock(); +} + +void CReflector::OnUsersChanged(void) +{ + CNotification notification(NOTIFICATION_USERS); + + m_Notifications.Lock(); + m_Notifications.push(notification); + m_Notifications.Unlock(); +} + +void CReflector::OnStreamOpen(const CCallsign &callsign) +{ + CNotification notification(NOTIFICATION_STREAM_OPEN, callsign); + + m_Notifications.Lock(); + m_Notifications.push(notification); + m_Notifications.Unlock(); +} + +void CReflector::OnStreamClose(const CCallsign &callsign) +{ + CNotification notification(NOTIFICATION_STREAM_CLOSE, callsign); + + m_Notifications.Lock(); + m_Notifications.push(notification); + m_Notifications.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// modules & queues + +int CReflector::GetModuleIndex(char module) const +{ + int i = (int)module - (int)'A'; + if ( (i < 0) || (i >= NB_OF_MODULES) ) + { + i = -1; + } + return i; +} + +CPacketStream *CReflector::GetStream(char module) +{ + int i = GetModuleIndex(module); + if ( i >= 0 ) + { + return &(m_Stream[i]); + } + return nullptr; +} + +bool CReflector::IsStreamOpen(const std::unique_ptr &DvHeader) +{ + for ( unsigned i = 0; i < m_Stream.size(); i++ ) + { + if ( (m_Stream[i].GetStreamId() == DvHeader->GetStreamId()) && (m_Stream[i].IsOpen()) ) + return true; + } + return false; +} + +char CReflector::GetStreamModule(CPacketStream *stream) +{ + for ( unsigned i = 0; i < m_Stream.size(); i++ ) + { + if ( &(m_Stream[i]) == stream ) + return GetModuleLetter(i); + } + return ' '; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// xml helpers + +void CReflector::WriteXmlFile(std::ofstream &xmlFile) +{ + // write header + xmlFile << "" << std::endl; + + // software version + char sz[64]; + ::sprintf(sz, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION); + xmlFile << "" << sz << "" << std::endl; + + // linked peers + xmlFile << "<" << m_Callsign << "linked peers>" << std::endl; + // lock + CPeers *peers = GetPeers(); + // iterate on peers + for ( auto pit=peers->cbegin(); pit!=peers->cend(); pit++ ) + { + (*pit)->WriteXml(xmlFile); + } + // unlock + ReleasePeers(); + xmlFile << "" << std::endl; + + // linked nodes + xmlFile << "<" << m_Callsign << "linked nodes>" << std::endl; + // lock + CClients *clients = GetClients(); + // iterate on clients + for ( auto cit=clients->cbegin(); cit!=clients->cend(); cit++ ) + { + if ( (*cit)->IsNode() ) + { + (*cit)->WriteXml(xmlFile); + } + } + // unlock + ReleaseClients(); + xmlFile << "" << std::endl; + + // last heard users + xmlFile << "<" << m_Callsign << "heard users>" << std::endl; + // lock + CUsers *users = GetUsers(); + // iterate on users + for ( auto it=users->begin(); it!=users->end(); it++ ) + { + (*it).WriteXml(xmlFile); + } + // unlock + ReleaseUsers(); + xmlFile << "" << std::endl; +} + + +#ifdef JSON_MONITOR +//////////////////////////////////////////////////////////////////////////////////////// +// json helpers + +void CReflector::SendJsonReflectorObject(CUdpSocket &Socket, CIp &Ip) +{ + char Buffer[1024]; + char cs[CALLSIGN_LEN+1]; + char mod[8] = "\"A\""; + + // get reflector callsign + m_Callsign.GetCallsign((uint8 *)cs); + cs[CALLSIGN_LEN] = 0; + + // build string + ::sprintf(Buffer, "{\"reflector\":\"%s\",\"modules\":[", cs); + for ( int i = 0; i < NB_OF_MODULES; i++ ) + { + ::strcat(Buffer, mod); + mod[1]++; + if ( i < NB_OF_MODULES-1 ) + { + ::strcat(Buffer, ","); + } + } + ::strcat(Buffer, "]}"); + + // and send + Socket.Send(Buffer, Ip); +} + +#define JSON_NBMAX_NODES 250 + +void CReflector::SendJsonNodesObject(CUdpSocket &Socket, CIp &Ip) +{ + char Buffer[12+(JSON_NBMAX_NODES*94)]; + + // nodes object table + ::sprintf(Buffer, "{\"nodes\":["); + // lock + CClients *clients = GetClients(); + // iterate on clients + for ( auto it=clients->cbegin(); it!=clients->cend(); ) + { + (*it++)->GetJsonObject(Buffer); + if ( it != clients->cend() ) + { + ::strcat(Buffer, ","); + } + } + // unlock + ReleaseClients(); + ::strcat(Buffer, "]}"); + + // and send + //std::cout << Buffer << std::endl; + Socket.Send(Buffer, Ip); +} + +void CReflector::SendJsonStationsObject(CUdpSocket &Socket, CIp &Ip) +{ + char Buffer[15+(LASTHEARD_USERS_MAX_SIZE*94)]; + + // stations object table + ::sprintf(Buffer, "{\"stations\":["); + + // lock + CUsers *users = GetUsers(); + // iterate on users + for ( auto it=users->begin(); it!=users->end(); ) + { + (*it++).GetJsonObject(Buffer); + if ( it != users->end() ) + { + ::strcat(Buffer, ","); + } + } + // unlock + ReleaseUsers(); + + ::strcat(Buffer, "]}"); + + // and send + //std::cout << Buffer << std::endl; + Socket.Send(Buffer, Ip); +} + +void CReflector::SendJsonOnairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign) +{ + char Buffer[128]; + char sz[CALLSIGN_LEN+1]; + + // onair object + Callsign.GetCallsignString(sz); + ::sprintf(Buffer, "{\"onair\":\"%s\"}", sz); + + // and send + //std::cout << Buffer << std::endl; + Socket.Send(Buffer, Ip); +} + +void CReflector::SendJsonOffairObject(CUdpSocket &Socket, CIp &Ip, const CCallsign &Callsign) +{ + char Buffer[128]; + char sz[CALLSIGN_LEN+1]; + + // offair object + Callsign.GetCallsignString(sz); + ::sprintf(Buffer, "{\"offair\":\"%s\"}", sz); + + // and send + //std::cout << Buffer << std::endl; + Socket.Send(Buffer, Ip); +} +#endif diff --git a/src/Reflector.h b/src/Reflector.h new file mode 100644 index 0000000..55a6292 --- /dev/null +++ b/src/Reflector.h @@ -0,0 +1,160 @@ +// +// Reflector.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef creflector_h +#define creflector_h + +#include "ProtoAddress.h" +#include "Users.h" +#include "Clients.h" +#include "Peers.h" +#include "Protocols.h" +#include "PacketStream.h" +#include "NotificationQueue.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +// event defines +#define EVENT_NONE 0 +#define EVENT_CLIENTS 1 +#define EVENT_USERS 2 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CReflector +{ +public: + // constructor + CReflector(); + CReflector(const CCallsign &); + + // destructor + virtual ~CReflector(); + + // settings + void SetCallsign(const CCallsign &callsign) { m_Callsign = callsign; } + const CCallsign &GetCallsign(void) const { return m_Callsign; } + +#ifdef TRANSCODER_IP + void SetTranscoderIp(const char *a, const int n) { memset(m_AmbedIp, 0, n); strncpy(m_AmbedIp, a, n-1); } + const char *GetTranscoderIp(void) const { return m_AmbedIp; } +#endif + + // operation + bool Start(void); + void Stop(void); + + // clients + CClients *GetClients(void) { m_Clients.Lock(); return &m_Clients; } + void ReleaseClients(void) { m_Clients.Unlock(); } + + // peers + CPeers *GetPeers(void) { m_Peers.Lock(); return &m_Peers; } + void ReleasePeers(void) { m_Peers.Unlock(); } + + // stream opening & closing + CPacketStream *OpenStream(std::unique_ptr &, std::shared_ptr); + bool IsStreaming(char); + void CloseStream(CPacketStream *); + + // users + CUsers *GetUsers(void) { m_Users.Lock(); return &m_Users; } + void ReleaseUsers(void) { m_Users.Unlock(); } + + // IP Addresses + CProtoAddress m_Address; + + // get + bool IsValidModule(char c) const { return (GetModuleIndex(c) >= 0); } + int GetModuleIndex(char) const; + char GetModuleLetter(int i) const { return 'A' + (char)i; } + + // notifications + void OnPeersChanged(void); + void OnClientsChanged(void); + void OnUsersChanged(void); + void OnStreamOpen(const CCallsign &); + void OnStreamClose(const CCallsign &); + +protected: + // threads + void RouterThread(CPacketStream *); + void XmlReportThread(void); +#ifdef JSON_MONITOR + void JsonReportThread(void); +#endif + + // streams + CPacketStream *GetStream(char); + bool IsStreamOpen(const std::unique_ptr &); + char GetStreamModule(CPacketStream *); + + // xml helpers + void WriteXmlFile(std::ofstream &); + +#ifdef JSON_MONITOR + // json helpers + void SendJsonReflectorObject(CUdpSocket &, CIp &); + void SendJsonNodesObject(CUdpSocket &, CIp &); + void SendJsonStationsObject(CUdpSocket &, CIp &); + void SendJsonOnairObject(CUdpSocket &, CIp &, const CCallsign &); + void SendJsonOffairObject(CUdpSocket &, CIp &, const CCallsign &); +#endif + +protected: + // identity + CCallsign m_Callsign; +#ifdef TRANSCODER_IP + char m_AmbedIp[INET6_ADDRSTRLEN]; +#endif + + // objects + CUsers m_Users; // sorted list of lastheard stations + CClients m_Clients; // list of linked repeaters/nodes/peers's modules + CPeers m_Peers; // list of linked peers + CProtocols m_Protocols; // list of supported protocol handlers + + // queues + std::array m_Stream; + + // threads + std::atomic keep_running; + std::array, NB_OF_MODULES> m_RouterFuture; + std::future m_XmlReportFuture, m_JsonReportFuture; + + // notifications + CNotificationQueue m_Notifications; + +public: +#ifdef DEBUG_DUMPFILE + std::ofstream m_DebugFile; +#endif +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* creflector_h */ diff --git a/src/Semaphore.cpp b/src/Semaphore.cpp new file mode 100644 index 0000000..fa1f94f --- /dev/null +++ b/src/Semaphore.cpp @@ -0,0 +1,71 @@ +// +// csemaphore.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Semaphore.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CSemaphore::CSemaphore() +{ + // Initialized as locked. + m_Count = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +void CSemaphore::Reset(void) +{ + std::unique_lock lock(m_Mutex); + m_Count = 0; +} + +void CSemaphore::Notify(void) +{ + std::unique_lock lock(m_Mutex); + m_Count++; + m_Condition.notify_one(); +} + +void CSemaphore::Wait(void) +{ + std::unique_lock lock(m_Mutex); + m_Condition.wait(lock, [&] { return m_Count > 0; }); + m_Count--; +} + +bool CSemaphore::WaitFor(uint ms) +{ + std::chrono::milliseconds timespan(ms); + std::unique_lock lock(m_Mutex); + auto ok = m_Condition.wait_for(lock, timespan, [&] { return m_Count > 0; }); + if ( ok ) + { + m_Count--; + } + return ok; + +} diff --git a/src/Semaphore.h b/src/Semaphore.h new file mode 100644 index 0000000..4fa03bd --- /dev/null +++ b/src/Semaphore.h @@ -0,0 +1,56 @@ +// +// Semaphore.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 16/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef csemaphore_h +#define csemaphore_h + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CSemaphore +{ +public: + // constructor + CSemaphore(); + + // destructor + virtual ~CSemaphore() {}; + + // operation + void Reset(void); + void Notify(void); + void Wait(void); + bool WaitFor(uint); + +protected: + // data + std::mutex m_Mutex; + std::condition_variable m_Condition; + size_t m_Count; + +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* csemaphore_h */ diff --git a/src/Timer.h b/src/Timer.h new file mode 100644 index 0000000..08747af --- /dev/null +++ b/src/Timer.h @@ -0,0 +1,38 @@ +// hyvoc - a hybrid vocoder using DVSI hardware and Codec2 software +// Copyright © 2021 Thomas A. Early N7TAE + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +class CTimer +{ +public: + CTimer() { start(); } + ~CTimer() {} + void start() + { + starttime = std::chrono::steady_clock::now(); + } + double time() const + { + std::chrono::duration elapsed(std::chrono::steady_clock::now() - starttime); + return elapsed.count(); + } +private: + std::chrono::steady_clock::time_point starttime; +}; diff --git a/src/Transcoder.cpp b/src/Transcoder.cpp new file mode 100644 index 0000000..b4a8b42 --- /dev/null +++ b/src/Transcoder.cpp @@ -0,0 +1,392 @@ +// +// ctranscoder.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Reflector.h" +#include "Transcoder.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +// status +#define STATUS_IDLE 0 +#define STATUS_LOGGED 1 + +// timeout +#define AMBED_OPENSTREAM_TIMEOUT 200 // in ms + +//////////////////////////////////////////////////////////////////////////////////////// + +CTranscoder g_Transcoder; + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CTranscoder::CTranscoder() +{ + keep_running = true; + m_bConnected = false; + m_LastKeepaliveTime.Now(); + m_LastActivityTime.Now(); + m_bStreamOpened = false; + m_StreamidOpenStream = 0; + m_PortOpenStream = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CTranscoder::~CTranscoder() +{ + Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// initialization + +bool CTranscoder::Init(void) +{ + // create address to the transcoder + auto s = g_Reflector.GetTranscoderIp(); + m_Ip.Initialize(strchr(s, ':') ? AF_INET6 : AF_INET, TRANSCODER_PORT, s); + + // does the user not want to use a transcoder? + if (0 == strncasecmp(s, "none", 4)) + { + std::cout << "Transcoder will not be enabled because the transcoder IP addess is 'none'" << std::endl; + return true; + } + + // now open the transcoder port +#ifdef LISTEN_IPV4 +#ifdef LISTEN_IPV6 + const auto paddr = (AF_INET == m_Ip.GetFamily()) ? g_Reflector.m_Address.GetV4Address(PROTOCOL_ANY) : g_Reflector.m_Address.GetV6Address(PROTOCOL_ANY); +#else + const auto paddr = g_Reflector.m_Address.GetV4Address(PROTOCOL_ANY); +#endif +#else + const auto paddr = g_Reflector.m_address.GetV6Address(PROTOCOL_ANY); +#endif + CIp tc(m_Ip.GetFamily(), TRANSCODER_PORT, paddr.c_str()); + + // create our socket + if (tc.IsSet()) + { + if (! m_Socket.Open(tc)) + { + std::cerr << "Error opening socket on port UDP" << TRANSCODER_PORT << " on ip " << m_Ip << std::endl; + return false; + } + } + else + { + // something bad was specified for the transcoder IP? + std::cerr << "Error initializing transcoder port using " << ((AF_INET == m_Ip.GetFamily()) ? "IPv4" : "IPv6") << " on " << paddr << std::endl; + return false; + } + + // start thread + keep_running = true; + m_Future = std::async(std::launch::async, &CTranscoder::Thread, this); + + return true; +} + +void CTranscoder::Close(void) +{ + // close socket + m_Socket.Close(); + + // close all streams + m_Mutex.lock(); + m_Streams.clear(); + m_Mutex.unlock(); + + // kill threads + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CTranscoder::Thread() +{ + while (keep_running) + { + Task(); + } +} + +void CTranscoder::Task(void) +{ + CBuffer Buffer; + CIp Ip; + uint16 StreamId; + uint16 Port; + + // anything coming in from codec server ? + //if ( (m_Socket.Receive(&Buffer, &Ip, 20) != -1) && (Ip == m_Ip) ) + if ( m_Socket.Receive(Buffer, Ip, 20) ) + { + m_LastActivityTime.Now(); + + // crack packet + if ( IsValidStreamDescrPacket(Buffer, &StreamId, &Port) ) + { + //std::cout << "Transcoder stream " << (int) StreamId << " descr packet " << std::endl; + m_bStreamOpened = true; + m_StreamidOpenStream = StreamId; + m_PortOpenStream = Port; + m_SemaphoreOpenStream.Notify(); + } + else if ( IsValidNoStreamAvailablePacket(Buffer) ) + { + m_bStreamOpened = false; + m_SemaphoreOpenStream.Notify(); + } + else if ( IsValidKeepAlivePacket(Buffer) ) + { + if ( !m_bConnected ) + { + std::cout << "Transcoder connected at " << Ip << std::endl; + } + m_bConnected = true; + } + + } + + // keep client alive + if ( m_LastKeepaliveTime.DurationSinceNow() > TRANSCODER_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// manage streams + +std::shared_ptr CTranscoder::GetCodecStream(CPacketStream *PacketStream, uint8 uiCodecIn) +{ + CBuffer Buffer; + + // do we need transcoding + if ( uiCodecIn != CODEC_NONE ) + { + // are we connected to server + if ( m_bConnected ) + { + // yes, post openstream request + EncodeOpenstreamPacket(&Buffer, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS); + m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT); + + // wait relpy here + if ( m_SemaphoreOpenStream.WaitFor(AMBED_OPENSTREAM_TIMEOUT) ) + { + if ( m_bStreamOpened ) + { + std::cout << "ambed openstream ok" << std::endl; + + // create stream object + auto stream = std::make_shared(PacketStream, m_StreamidOpenStream, uiCodecIn, (uiCodecIn == CODEC_AMBEPLUS) ? CODEC_AMBE2PLUS : CODEC_AMBEPLUS); + + if ( stream ) { + if ( stream->Init(m_PortOpenStream) ) + { + // and append to list + Lock(); + m_Streams.push_back(stream); + Unlock(); + return stream; + } + else + { + // send close packet + EncodeClosestreamPacket(&Buffer, stream->GetStreamId()); + m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT); + stream.reset(); + } + } + else + std::cerr << "ERROR: Unable to make a new CCodecStream" << std::endl; + } + else + { + std::cerr << "ERROR: Ambed openstream failed (no suitable channel available)" << std::endl; + } + } + else + { + std::cout << "ambed openstream timeout" << std::endl; + } + + } + } + return nullptr; +} + +void CTranscoder::ReleaseStream(std::shared_ptr stream) +{ + CBuffer Buffer; + + if ( stream ) + { + // look for the stream + bool found = false; + Lock(); + { + for ( auto it=m_Streams.begin(); it!=m_Streams.end(); it++ ) + { + // compare object pointers + if ( (*it) == stream ) + { + // send close packet + EncodeClosestreamPacket(&Buffer, (*it)->GetStreamId()); + m_Socket.Send(Buffer, m_Ip, TRANSCODER_PORT); + + // display stats + if ( (*it)->GetPingMin() >= 0.0 ) + { + char sz[256]; + sprintf(sz, "ambed stats (ms) : %.1f/%.1f/%.1f", + (*it)->GetPingMin() * 1000.0, + (*it)->GetPingAve() * 1000.0, + (*it)->GetPingMax() * 1000.0); + std::cout << sz << std::endl; + } + if ( (*it)->GetTimeoutPackets() > 0 ) + { + char sz[256]; + sprintf(sz, "ambed %d of %d packets timed out", + (*it)->GetTimeoutPackets(), + (*it)->GetTotalPackets()); + std::cout << sz << std::endl; + } + + // and close it + (*it)->Close(); + m_Streams.erase(it); + break; + } + } + } + Unlock(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CTranscoder::HandleKeepalives(void) +{ + CBuffer keepalive; + + // send keepalive + EncodeKeepAlivePacket(&keepalive); + m_Socket.Send(keepalive, m_Ip, TRANSCODER_PORT); + + // check if still with us + if ( m_bConnected && (m_LastActivityTime.DurationSinceNow() >= TRANSCODER_KEEPALIVE_TIMEOUT) ) + { + // no, disconnect + m_bConnected = false; + std::cout << "Transcoder keepalive timeout" << std::endl; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CTranscoder::IsValidKeepAlivePacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 'A','M','B','E','D','P','O','N','G' }; + + bool valid = false; + if ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + valid = true; + } + return valid; +} + +bool CTranscoder::IsValidStreamDescrPacket(const CBuffer &Buffer, uint16 *Id, uint16 *Port) +{ + uint8 tag[] = { 'A','M','B','E','D','S','T','D' }; + + bool valid = false; + if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + *Id = *(uint16 *)(&Buffer.data()[8]); + *Port = *(uint16 *)(&Buffer.data()[10]); + // uint8 CodecIn = Buffer.data()[12]; + // uint8 CodecOut = Buffer.data()[13]; + valid = true; + } + return valid; +} + +bool CTranscoder::IsValidNoStreamAvailablePacket(const CBuffer&Buffer) +{ + uint8 tag[] = { 'A','M','B','E','D','B','U','S','Y' }; + + return ( (Buffer.size() == 9) && (Buffer.Compare(tag, sizeof(tag)) == 0) ); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CTranscoder::EncodeKeepAlivePacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'A','M','B','E','D','P','I','N','G' }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN); +} + +void CTranscoder::EncodeOpenstreamPacket(CBuffer *Buffer, uint8 uiCodecIn, uint8 uiCodecOut) +{ + uint8 tag[] = { 'A','M','B','E','D','O','S' }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append((uint8 *)(const char *)g_Reflector.GetCallsign(), CALLSIGN_LEN); + Buffer->Append((uint8)uiCodecIn); + Buffer->Append((uint8)uiCodecOut); +} + +void CTranscoder::EncodeClosestreamPacket(CBuffer *Buffer, uint16 uiStreamId) +{ + uint8 tag[] = { 'A','M','B','E','D','C','S' }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append((uint16)uiStreamId); +} diff --git a/src/Transcoder.h b/src/Transcoder.h new file mode 100644 index 0000000..e04e087 --- /dev/null +++ b/src/Transcoder.h @@ -0,0 +1,111 @@ +// +// Transcoder.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/04/2017. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef ctranscoder_h +#define ctranscoder_h + +#include "Semaphore.h" +#include "CodecStream.h" +#include "UDPSocket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CPacketStream; + +class CTranscoder +{ +public: + // constructor + CTranscoder(); + + // destructor + ~CTranscoder(); + + // initialization + bool Init(void); + void Close(void); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // status + bool IsConnected(void) const { return m_bConnected; } + + // manage streams + std::shared_ptr GetCodecStream(CPacketStream *, uint8); + void ReleaseStream(std::shared_ptr); + + // task + void Thread(void); + void Task(void); + +protected: + // keepalive helpers + void HandleKeepalives(void); + + // packet decoding helpers + bool IsValidKeepAlivePacket(const CBuffer &); + bool IsValidStreamDescrPacket(const CBuffer &, uint16 *, uint16 *); + bool IsValidNoStreamAvailablePacket(const CBuffer&); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *); + void EncodeOpenstreamPacket(CBuffer *, uint8, uint8); + void EncodeClosestreamPacket(CBuffer *, uint16); + +protected: + // streams + std::mutex m_Mutex; + std::list> m_Streams; + + // sync objects for Openstream + CSemaphore m_SemaphoreOpenStream; + bool m_bStreamOpened; + uint16 m_StreamidOpenStream; + uint16 m_PortOpenStream; + + // thread + std::atomic keep_running; + std::future m_Future; + + // socket + CIp m_Ip; + CUdpSocket m_Socket; + bool m_bConnected; + + // time + CTimePoint m_LastKeepaliveTime; + CTimePoint m_LastActivityTime; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* ctranscoder_h */ diff --git a/src/UDPMsgSocket.cpp b/src/UDPMsgSocket.cpp new file mode 100644 index 0000000..e821747 --- /dev/null +++ b/src/UDPMsgSocket.cpp @@ -0,0 +1,117 @@ +// +// cudpmsgsocket.cpp +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "UDPMsgSocket.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// open +bool CUdpMsgSocket::Open(const CIp &ip) +{ + bool ret; + int on = 1; + + ret = CUdpSocket::Open(ip); + setsockopt(m_fd, IPPROTO_IP, IP_PKTINFO, (char *)&on, sizeof(on)); + + return ret; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// read + +int CUdpMsgSocket::Receive(CBuffer *Buffer, CIp *Ip, int timeout) +{ + struct sockaddr_in Sin; + fd_set FdSet; + unsigned int uiFromLen = sizeof(struct sockaddr_in); + int iRecvLen = -1; + struct timeval tv; + + struct msghdr Msg; + struct iovec Iov[1]; + + union + { + struct cmsghdr cm; + unsigned char pktinfo_sizer[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)]; + } Control; + + // socket valid ? + if ( m_fd != -1 ) + { + // allocate buffer + Buffer->resize(UDP_MSG_BUFFER_LENMAX); + + //prepare msghdr + bzero(&Msg, sizeof(Msg)); + Iov[0].iov_base = Buffer->data(); + Iov[0].iov_len = UDP_MSG_BUFFER_LENMAX; + + bzero(&Sin, sizeof(Sin)); + Msg.msg_name = &Sin; + Msg.msg_namelen = sizeof(Sin); + Msg.msg_iov = Iov; + Msg.msg_iovlen = 1; + Msg.msg_control = &Control; + Msg.msg_controllen = sizeof(Control); + + // control socket + FD_ZERO(&FdSet); + FD_SET(m_fd, &FdSet); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + select(m_fd + 1, &FdSet, 0, 0, &tv); + + // read + iRecvLen = (int)recvmsg(m_fd, &Msg, 0); + + // handle + if ( iRecvLen != -1 ) + { + // adjust buffer size + Buffer->resize(iRecvLen); + + // get IP + if (AF_INET == m_addr.GetFamily()) + memcpy(Ip->GetPointer(), &Sin, sizeof(struct sockaddr_in)); + + // get local IP + struct cmsghdr *Cmsg; + for (Cmsg = CMSG_FIRSTHDR(&Msg); Cmsg != nullptr; Cmsg = CMSG_NXTHDR(&Msg, Cmsg)) + { + if (Cmsg->cmsg_level == IPPROTO_IP && Cmsg->cmsg_type == IP_PKTINFO) + { + struct in_pktinfo *PktInfo = (struct in_pktinfo *)CMSG_DATA(Cmsg); + m_LocalAddr.s_addr = PktInfo->ipi_spec_dst.s_addr; + } + } + } + } + + // done + return iRecvLen; +} diff --git a/src/UDPMsgSocket.h b/src/UDPMsgSocket.h new file mode 100644 index 0000000..668d817 --- /dev/null +++ b/src/UDPMsgSocket.h @@ -0,0 +1,56 @@ +// +// UDPMsgSocket.h +// xlxd +// +// Created by Marius Petrescu (YO2LOJ) on 22/02/2020. +// Copyright © 2020 Marius Petrescu (YO2LOJ). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +// Description: +// Extension of the CUdpSocket class to allow retrieving +// the local target IP for a G3 Terminal mode config request + +#ifndef cudpmsgsocket_h +#define cudpmsgsocket_h + +#include "UDPSocket.h" + +#define UDP_MSG_BUFFER_LENMAX 1024 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUdpMsgSocket : public CUdpSocket +{ +public: + // open + bool Open(const CIp &ip); + + // read + int Receive(CBuffer *, CIp *, int); + + struct in_addr *GetLocalAddr(void) { return &m_LocalAddr; } + +protected: + // data + struct in_addr m_LocalAddr; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cudpmsgsocket_h */ diff --git a/src/UDPSocket.cpp b/src/UDPSocket.cpp new file mode 100644 index 0000000..3e75450 --- /dev/null +++ b/src/UDPSocket.cpp @@ -0,0 +1,182 @@ +// +// cudpsocket.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "UDPSocket.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CUdpSocket::CUdpSocket() : m_fd(-1) {} + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CUdpSocket::~CUdpSocket() +{ + Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// open & close + +// returns true on error +bool CUdpSocket::Open(const CIp &Ip) +{ + // check for a vaild family + if (AF_UNSPEC == Ip.GetFamily()) + return true; + + // create socket + m_fd = socket(Ip.GetFamily(), SOCK_DGRAM, 0); + if ( m_fd < 0 ) + { + std::cerr << "Unable to open socket on " << Ip << ", " << strerror(errno) << std::endl; + return false; + } + // initialize sockaddr struct + m_addr = Ip; + + int reuse = 1; + if ( 0 > setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))) + { + std::cerr << "Cannot set the UDP socket option on " << m_addr << ", " << strerror(errno) << std::endl; + Close(); + return false; + } + + if (fcntl(m_fd, F_SETFL, O_NONBLOCK)) + { + std::cerr << "fcntl set non-blocking failed on " << m_addr << ", " << strerror(errno) << std::endl; + Close(); + return false; + } + + if ( bind(m_fd, m_addr.GetCPointer(), m_addr.GetSize()) ) + { + std::cerr << "bind failed on " << m_addr << ", " << strerror(errno) << std::endl; + Close(); + return false; + } + + if (0 == m_addr.GetPort()) // get the assigned port for an ephemeral port request + { + CIp a; + socklen_t len = sizeof(struct sockaddr_storage); + if (getsockname(m_fd, a.GetPointer(), &len)) + { + std::cerr << "getsockname error " << m_addr << ", " << strerror(errno) << std::endl; + Close(); + return false; + } + if (a != m_addr) + std::cout << "getsockname didn't return the same address as set: returned " << a << ", should have been " << m_addr << std::endl; + + m_addr.SetPort(a.GetPort()); + } + + // done + return true; +} + +void CUdpSocket::Close(void) +{ + if ( m_fd >= 0 ) + { + close(m_fd); + m_fd = -1; + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// read + +bool CUdpSocket::Receive(CBuffer &Buffer, CIp &Ip, int timeout) +{ + // socket valid ? + if ( 0 > m_fd ) + return false; + + // control socket + fd_set FdSet; + FD_ZERO(&FdSet); + FD_SET(m_fd, &FdSet); + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + + auto rval = select(m_fd + 1, &FdSet, 0, 0, &tv); + if (rval > 0) + return ReceiveFrom(Buffer, Ip); + + if (rval < 0) + std::cerr << "select error on UPD port " << m_addr << ": " << strerror(errno) << std::endl; + + return false; +} + +bool CUdpSocket::ReceiveFrom(CBuffer &Buffer, CIp &ip) +{ + // read + uint8_t buf[UDP_BUFFER_LENMAX]; + unsigned int fromsize = sizeof(struct sockaddr_storage); + auto iRecvLen = recvfrom(m_fd, buf, UDP_BUFFER_LENMAX, 0, ip.GetPointer(), &fromsize); + + if (0 >= iRecvLen) + return false; + + Buffer.Set(buf, iRecvLen); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// write + +void CUdpSocket::Send(const CBuffer &Buffer, const CIp &Ip) const +{ + sendto(m_fd, Buffer.data(), Buffer.size(), 0, Ip.GetCPointer(), Ip.GetSize()); +} + +void CUdpSocket::Send(const char *Buffer, const CIp &Ip) const +{ + sendto(m_fd, Buffer, ::strlen(Buffer), 0, Ip.GetCPointer(), Ip.GetSize()); +} + +void CUdpSocket::Send(const CBuffer &Buffer, const CIp &Ip, uint16_t destport) const +{ + CIp temp(Ip); + temp.SetPort(destport); + sendto(m_fd, Buffer.data(), Buffer.size(), 0, temp.GetCPointer(), temp.GetSize()); +} + +void CUdpSocket::Send(const char *Buffer, const CIp &Ip, uint16_t destport) const +{ + CIp temp(Ip); + temp.SetPort(destport); + sendto(m_fd, Buffer, ::strlen(Buffer), 0, temp.GetCPointer(), temp.GetSize()); +} diff --git a/src/UDPSocket.h b/src/UDPSocket.h new file mode 100644 index 0000000..6490948 --- /dev/null +++ b/src/UDPSocket.h @@ -0,0 +1,83 @@ +// +// UDPSocket.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 31/10/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cudpsocket_h +#define cudpsocket_h + +#include +#include +#include +#include +#include +#include +#include + +#include "IP.h" +#include "Buffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define UDP_BUFFER_LENMAX 1024 + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUdpSocket +{ +public: + // constructor + CUdpSocket(); + + // destructor + ~CUdpSocket(); + + // open & close + bool Open(const CIp &Ip); + void Close(void); + int GetSocket(void) + { + return m_fd; + } + + // read + bool Receive(CBuffer &, CIp &, int); + bool ReceiveFrom(CBuffer &buf, CIp &ip); + + // write + void Send(const CBuffer &, const CIp &) const; + void Send(const char *, const CIp &) const; + void Send(const CBuffer &, const CIp &, uint16_t) const; + void Send(const char *, const CIp &, uint16_t) const; + +protected: + // data + int m_fd; + CIp m_addr; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cudpsocket_h */ diff --git a/src/ULXClient.cpp b/src/ULXClient.cpp new file mode 100644 index 0000000..fda5cf3 --- /dev/null +++ b/src/ULXClient.cpp @@ -0,0 +1,77 @@ +// +// cxlxclient.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 28/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "ULXClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CUlxClient::CUlxClient() +{ + m_ProtRev = XLX_PROTOCOL_REVISION_0; +} + +CUlxClient::CUlxClient(const CCallsign &callsign, const CIp &ip, char reflectorModule, int protRev) + : CClient(callsign, ip, reflectorModule) +{ + m_ProtRev = protRev; +} + +CUlxClient::CUlxClient(const CUlxClient &client) + : CClient(client) +{ + m_ProtRev = client.m_ProtRev; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// identity + +int CUlxClient::GetCodec(void) const +{ + int codec; + + switch ( GetProtocolRevision() ) + { + case XLX_PROTOCOL_REVISION_0: + case XLX_PROTOCOL_REVISION_1: + default: + codec = CODEC_AMBEPLUS; + break; + case XLX_PROTOCOL_REVISION_2: + codec = CODEC_NONE; + break; + } + return codec; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CUlxClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} diff --git a/src/ULXClient.h b/src/ULXClient.h new file mode 100644 index 0000000..ac0f6bb --- /dev/null +++ b/src/ULXClient.h @@ -0,0 +1,72 @@ +// +// ULXClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 28/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cxlxclient_h +#define cxlxclient_h + + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + +#define XLX_PROTOCOL_REVISION_0 0 // AMBE only, original connect mechanism +#define XLX_PROTOCOL_REVISION_1 1 // AMBE only, revised connect mechanism +#define XLX_PROTOCOL_REVISION_2 2 // Transcoded AMBE+AMBE2 interlink + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUlxClient : public CClient +{ +public: + // constructors + CUlxClient(); + CUlxClient(const CCallsign &, const CIp &, char = ' ', int = XLX_PROTOCOL_REVISION_0); + CUlxClient(const CUlxClient &); + + // destructor + virtual ~CUlxClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + int GetProtocolRevision(void) const { return m_ProtRev; } + const char *GetProtocolName(void) const { return "XLX"; } + int GetCodec(void) const; + bool IsPeer(void) const { return true; } + + // status + bool IsAlive(void) const; + + // reporting + void WriteXml(std::ofstream &) {} + +protected: + // data + int m_ProtRev; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cxlxclient_h */ diff --git a/src/ULXPeer.cpp b/src/ULXPeer.cpp new file mode 100644 index 0000000..411efe3 --- /dev/null +++ b/src/ULXPeer.cpp @@ -0,0 +1,80 @@ +// +// cxlxpeer.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "Reflector.h" +#include "ULXPeer.h" +#include "ULXClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + + +CUlxPeer::CUlxPeer() +{ +} + +CUlxPeer::CUlxPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version) + : CPeer(callsign, ip, modules, version) +{ + // get protocol revision + int protrev = GetProtocolRevision(version); + //std::cout << "Adding XLX peer with protocol revision " << protrev << std::endl; + + // and construct all xlx clients + for ( unsigned i = 0; i < ::strlen(modules); i++ ) + { + // create and append to vector + m_Clients.push_back(std::make_shared(callsign, ip, modules[i], protrev)); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CUlxPeer::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < XLX_KEEPALIVE_TIMEOUT); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// revision helper + +int CUlxPeer::GetProtocolRevision(const CVersion &version) +{ + int protrev = XLX_PROTOCOL_REVISION_0; + + if ( version.IsEqualOrHigherTo(CVersion(2,2,0)) ) + { + protrev = XLX_PROTOCOL_REVISION_2; + } + else if ( version.IsEqualOrHigherTo(CVersion(1,4,0)) ) + { + protrev = XLX_PROTOCOL_REVISION_1; + } + return protrev; +} diff --git a/src/ULXPeer.h b/src/ULXPeer.h new file mode 100644 index 0000000..6105819 --- /dev/null +++ b/src/ULXPeer.h @@ -0,0 +1,58 @@ +// +// ULXPeer.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 10/12/2016. +// Copyright © 2016 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cxlxpeer_h +#define cxlxpeer_h + +#include "Peer.h" +#include "ULXClient.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUlxPeer : public CPeer +{ +public: + // constructors + CUlxPeer(); + CUlxPeer(const CCallsign &, const CIp &, const char *, const CVersion &); + CUlxPeer(const CUlxPeer &) = delete; + + // status + bool IsAlive(void) const; + + // identity + int GetProtocol(void) const { return PROTOCOL_XLX; } + const char *GetProtocolName(void) const { return "XLX"; } + + // revision helper + static int GetProtocolRevision(const CVersion &); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cxlxpeer_h */ diff --git a/src/ULXProtocol.cpp b/src/ULXProtocol.cpp new file mode 100644 index 0000000..2a1775f --- /dev/null +++ b/src/ULXProtocol.cpp @@ -0,0 +1,715 @@ +// +// cxlxprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 28/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "ULXPeer.h" +#include "BMPeer.h" +#include "ULXProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CUlxProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // update time + m_LastKeepaliveTime.Now(); + m_LastPeersLinkTime.Now(); + + // done + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CUlxProtocol::Task(void) +{ + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + char Modules[27]; + CVersion Version; + std::unique_ptr Header; + std::unique_ptr Frame; + std::unique_ptr LastFrame; + + // any incoming packet ? +#if XLX_IPV6==true +#if XLX_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvFramePacket(Buffer, Frame) ) + { + OnDvFramePacketIn(Frame, &Ip); + } + else if ( IsValidDvHeaderPacket(Buffer, Header) ) + { + // callsign allowed? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip) ) + { + OnDvHeaderPacketIn(Header, Ip); + } + } + else if ( IsValidDvLastFramePacket(Buffer, LastFrame) ) + { + OnDvLastFramePacketIn(LastFrame, &Ip); + } + else if ( IsValidConnectPacket(Buffer, &Callsign, Modules, &Version) ) + { + std::cout << "XLX (" << Version.GetMajor() << "." << Version.GetMinor() << "." << Version.GetRevision() << ") connect packet for modules " << Modules << " from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_XLX, Modules) ) + { + // acknowledge connecting request + // following is version dependent + switch ( GetConnectingPeerProtocolRevision(Callsign, Version) ) + { + case XLX_PROTOCOL_REVISION_0: + { + // already connected ? + CPeers *peers = g_Reflector.GetPeers(); + if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == nullptr ) + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer, Modules); + Send(Buffer, Ip); + } + g_Reflector.ReleasePeers(); + + } + break; + case XLX_PROTOCOL_REVISION_1: + case XLX_PROTOCOL_REVISION_2: + default: + // acknowledge the request + EncodeConnectAckPacket(&Buffer, Modules); + Send(Buffer, Ip); + break; + } + } + else + { + // deny the request + EncodeConnectNackPacket(&Buffer); + Send(Buffer, Ip); + } + } + else if ( IsValidAckPacket(Buffer, &Callsign, Modules, &Version) ) + { + std::cout << "XLX ack packet for modules " << Modules << " from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_XLX, Modules) ) + { + // already connected ? + CPeers *peers = g_Reflector.GetPeers(); + if ( peers->FindPeer(Callsign, Ip, PROTOCOL_XLX) == nullptr ) + { + // create the new peer + // this also create one client per module + std::shared_ptrpeer = CreateNewPeer(Callsign, Ip, Modules, Version); + + // append the peer to reflector peer list + // this also add all new clients to reflector client list + peers->AddPeer(peer); + } + g_Reflector.ReleasePeers(); + } + } + else if ( IsValidDisconnectPacket(Buffer, &Callsign) ) + { + std::cout << "XLX disconnect packet from " << Callsign << " at " << Ip << std::endl; + + // find peer + CPeers *peers = g_Reflector.GetPeers(); + std::shared_ptrpeer = peers->FindPeer(Ip, PROTOCOL_XLX); + if ( peer != nullptr ) + { + // remove it from reflector peer list + // this also remove all concerned clients from reflector client list + // and delete them + peers->RemovePeer(peer); + } + g_Reflector.ReleasePeers(); + } + else if ( IsValidNackPacket(Buffer, &Callsign) ) + { + std::cout << "XLX nack packet from " << Callsign << " at " << Ip << std::endl; + } + else if ( IsValidKeepAlivePacket(Buffer, &Callsign) ) + { + //std::cout << "XLX keepalive packet from " << Callsign << " at " << Ip << std::endl; + + // find peer + CPeers *peers = g_Reflector.GetPeers(); + std::shared_ptrpeer = peers->FindPeer(Ip, PROTOCOL_XLX); + if ( peer != nullptr ) + { + // keep it alive + peer->Alive(); + } + g_Reflector.ReleasePeers(); + } + else + { + std::string title("Unknown XLX packet from "); + title += Ip.GetAddress(); + Buffer.Dump(title); + } + } + + // handle end of streaming timeout + CheckStreamsTimeout(); + + // handle queue from reflector + HandleQueue(); + + // keep alive + if ( m_LastKeepaliveTime.DurationSinceNow() > XLX_KEEPALIVE_PERIOD ) + { + // handle keep alives + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } + + // peer connections + if ( m_LastPeersLinkTime.DurationSinceNow() > XLX_RECONNECT_PERIOD ) + { + // handle remote peers connections + HandlePeerLinks(); + + // update time + m_LastPeersLinkTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CUlxProtocol::HandleQueue(void) +{ + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // check if origin of packet is local + // if not, do not stream it out as it will cause + // network loop between linked XLX peers + if ( packet->IsLocalOrigin() ) + { + // encode it + CBuffer buffer; + if ( EncodeDvPacket(*packet, &buffer) ) + { + // encode revision dependent version + CBuffer bufferLegacy = buffer; + if ( packet->IsDvFrame() && (bufferLegacy.size() == 45) ) + { + bufferLegacy.resize(27); + } + + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_XLX, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + // this is protocol revision dependent + switch ( client->GetProtocolRevision() ) + { + case XLX_PROTOCOL_REVISION_0: + case XLX_PROTOCOL_REVISION_1: + Send(bufferLegacy, client->GetIp()); + break; + case XLX_PROTOCOL_REVISION_2: + default: +#ifdef TRANSCODER_IP + if ( g_Transcoder.IsConnected() ) + { + Send(buffer, client->GetIp()); + } + else +#endif + { + Send(bufferLegacy, client->GetIp()); + } + break; + } + } + } + g_Reflector.ReleaseClients(); + } + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CUlxProtocol::HandleKeepalives(void) +{ + // DExtra protocol sends and monitors keepalives packets + // event if the client is currently streaming + // so, send keepalives to all + CBuffer keepalive; + EncodeKeepAlivePacket(&keepalive); + + // iterate on peers + CPeers *peers = g_Reflector.GetPeers(); + auto pit = peers->begin(); + std::shared_ptrpeer = nullptr; + while ( (peer = peers->FindNextPeer(PROTOCOL_XLX, pit)) != nullptr ) + { + // send keepalive + Send(keepalive, peer->GetIp()); + + // client busy ? + if ( peer->IsAMaster() ) + { + // yes, just tickle it + peer->Alive(); + } + // otherwise check if still with us + else if ( !peer->IsAlive() ) + { + // no, disconnect + CBuffer disconnect; + EncodeDisconnectPacket(&disconnect); + Send(disconnect, peer->GetIp()); + + // remove it + std::cout << "XLX peer " << peer->GetCallsign() << " keepalive timeout" << std::endl; + peers->RemovePeer(peer); + } + } + g_Reflector.ReleasePeers(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Peers helpers + +void CUlxProtocol::HandlePeerLinks(void) +{ + CBuffer buffer; + + // get the list of peers + CPeerCallsignList *list = g_GateKeeper.GetPeerList(); + CPeers *peers = g_Reflector.GetPeers(); + + // check if all our connected peers are still listed by gatekeeper + // if not, disconnect + auto pit = peers->begin(); + std::shared_ptrpeer = nullptr; + while ( (peer = peers->FindNextPeer(PROTOCOL_XLX, pit)) != nullptr ) + { + if ( list->FindListItem(peer->GetCallsign()) == nullptr ) + { + // send disconnect packet + EncodeDisconnectPacket(&buffer); + Send(buffer, peer->GetIp()); + std::cout << "Sending disconnect packet to XLX peer " << peer->GetCallsign() << std::endl; + // remove client + peers->RemovePeer(peer); + } + } + + // check if all ours peers listed by gatekeeper are connected + // if not, connect or reconnect + for ( auto it=list->begin(); it!=list->end(); it++ ) + { + if ( (*it).GetCallsign().HasSameCallsignWithWildcard(CCallsign("XRF*")) ) + continue; + if ( peers->FindPeer((*it).GetCallsign(), PROTOCOL_XLX) == nullptr ) + { + // resolve again peer's IP in case it's a dynamic IP + (*it).ResolveIp(); + // send connect packet to re-initiate peer link + EncodeConnectPacket(&buffer, (*it).GetModules()); + Send(buffer, (*it).GetIp(), XLX_PORT); + std::cout << "Sending connect packet to XLX peer " << (*it).GetCallsign() << " @ " << (*it).GetIp() << " for modules " << (*it).GetModules() << std::endl; + } + } + + // done + g_Reflector.ReleasePeers(); + g_GateKeeper.ReleasePeerList(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CUlxProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + CCallsign peer; + + // todo: verify Packet.GetModuleId() is in authorized list of XLX of origin + // todo: do the same for DVFrame and DVLAstFrame packets + + // tag packet as remote peer origin + Header->SetRemotePeerOrigin(); + + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + // no stream open yet, open a new one + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_XLX, Header->GetRpt2Module()); + if ( client ) + { + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + // get origin + peer = client->GetCallsign(); + } + // release + g_Reflector.ReleaseClients(); + // update last heard + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2, peer); + g_Reflector.ReleaseUsers(); + } +} + +void CUlxProtocol::OnDvFramePacketIn(std::unique_ptr &DvFrame, const CIp *Ip) +{ + // tag packet as remote peer origin + DvFrame->SetRemotePeerOrigin(); + + // anc call base class + CDextraProtocol::OnDvFramePacketIn(DvFrame, Ip); +} + +void CUlxProtocol::OnDvLastFramePacketIn(std::unique_ptr &DvFrame, const CIp *Ip) +{ + // tag packet as remote peer origin + DvFrame->SetRemotePeerOrigin(); + + // anc call base class + CDextraProtocol::OnDvLastFramePacketIn(DvFrame, Ip); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// packet decoding helpers + +bool CUlxProtocol::IsValidKeepAlivePacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if (Buffer.size() == 9) + { + callsign->SetCallsign(Buffer.data(), 8); + valid = callsign->IsValid(); + } + return valid; +} + + +bool CUlxProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version) +{ + bool valid = false; + if ((Buffer.size() == 39) && (Buffer.data()[0] == 'L') && (Buffer.data()[38] == 0)) + { + callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); + ::strcpy(modules, (const char *)&(Buffer.data()[12])); + valid = callsign->IsValid(); + *version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]); + for ( unsigned i = 0; i < ::strlen(modules); i++ ) + { + valid &= IsLetter(modules[i]); + } + } + return valid; +} + +bool CUlxProtocol::IsValidDisconnectPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if ((Buffer.size() == 10) && (Buffer.data()[0] == 'U') && (Buffer.data()[9] == 0)) + { + callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); + valid = callsign->IsValid(); + } + return valid; +} + +bool CUlxProtocol::IsValidAckPacket(const CBuffer &Buffer, CCallsign *callsign, char *modules, CVersion *version) +{ + bool valid = false; + if ((Buffer.size() == 39) && (Buffer.data()[0] == 'A') && (Buffer.data()[38] == 0)) + { + callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); + ::strcpy(modules, (const char *)&(Buffer.data()[12])); + valid = callsign->IsValid(); + *version = CVersion(Buffer.data()[9], Buffer.data()[10], Buffer.data()[11]); + for ( unsigned i = 0; i < ::strlen(modules); i++ ) + { + valid &= IsLetter(modules[i]); + } + } + return valid; +} + +bool CUlxProtocol::IsValidNackPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + bool valid = false; + if ((Buffer.size() == 10) && (Buffer.data()[0] == 'N') && (Buffer.data()[9] == 0)) + { + callsign->SetCallsign((const uint8 *)&(Buffer.data()[1]), 8); + valid = callsign->IsValid(); + } + return valid; +} + +bool CUlxProtocol::IsValidDvFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + // base class first (protocol revision 1 and lower) + if (CDextraProtocol::IsValidDvFramePacket(Buffer, dvframe)) + return true; + + // otherwise try protocol revision 2 + if ( 45==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && 0==(Buffer.data()[14] & 0x40) ) + { + // create packet + dvframe = std::unique_ptr(new CDvFramePacket( + // sid + *((uint16 *)&(Buffer.data()[12])), + // dstar + Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), + // dmr + Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]))); + + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +bool CUlxProtocol::IsValidDvLastFramePacket(const CBuffer &Buffer, std::unique_ptr &dvframe) +{ + // base class first (protocol revision 1 and lower) + if (CDextraProtocol::IsValidDvLastFramePacket(Buffer, dvframe)) + return true; + + // otherwise try protocol revision 2 + if ( 45==Buffer.size() && 0==Buffer.Compare((uint8 *)"DSVT", 4) && 0x20U==Buffer.data()[4] && 0x20U==Buffer.data()[8] && (Buffer.data()[14] & 0x40U) ) + { + // create packet + dvframe = std::unique_ptr(new CDvLastFramePacket( + // sid + *((uint16 *)&(Buffer.data()[12])), + // dstar + Buffer.data()[14], &(Buffer.data()[15]), &(Buffer.data()[24]), + // dmr + Buffer.data()[27], Buffer.data()[28], &(Buffer.data()[29]), &(Buffer.data()[38]))); + + // check validity of packet + if ( dvframe && dvframe->IsValid() ) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding helpers + +void CUlxProtocol::EncodeKeepAlivePacket(CBuffer *Buffer) +{ + Buffer->Set(GetReflectorCallsign()); +} + +void CUlxProtocol::EncodeConnectPacket(CBuffer *Buffer, const char *Modules) +{ + uint8 tag[] = { 'L' }; + + // tag + Buffer->Set(tag, sizeof(tag)); + // our callsign + Buffer->resize(Buffer->size()+8); + g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1); + // our version + Buffer->Append((uint8)VERSION_MAJOR); + Buffer->Append((uint8)VERSION_MINOR); + Buffer->Append((uint8)VERSION_REVISION); + // the modules we share + Buffer->Append(Modules); + Buffer->resize(39); +} + +void CUlxProtocol::EncodeDisconnectPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'U' }; + + // tag + Buffer->Set(tag, sizeof(tag)); + // our callsign + Buffer->resize(Buffer->size()+8); + g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1); + Buffer->Append((uint8)0); +} + +void CUlxProtocol::EncodeConnectAckPacket(CBuffer *Buffer, const char *Modules) +{ + uint8 tag[] = { 'A' }; + + // tag + Buffer->Set(tag, sizeof(tag)); + // our callsign + Buffer->resize(Buffer->size()+8); + g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1); + // our version + Buffer->Append((uint8)VERSION_MAJOR); + Buffer->Append((uint8)VERSION_MINOR); + Buffer->Append((uint8)VERSION_REVISION); + // the modules we share + Buffer->Append(Modules); + Buffer->resize(39); +} + +void CUlxProtocol::EncodeConnectNackPacket(CBuffer *Buffer) +{ + uint8 tag[] = { 'N' }; + + // tag + Buffer->Set(tag, sizeof(tag)); + // our callsign + Buffer->resize(Buffer->size()+8); + g_Reflector.GetCallsign().GetCallsign(Buffer->data()+1); + Buffer->Append((uint8)0); +} + +bool CUlxProtocol::EncodeDvFramePacket(const CDvFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)(Packet.GetDstarPacketId() % 21)); + Buffer->Append((uint8 *)Packet.GetAmbe(), AMBE_SIZE); + Buffer->Append((uint8 *)Packet.GetDvData(), DVDATA_SIZE); + + Buffer->Append((uint8)Packet.GetDmrPacketId()); + Buffer->Append((uint8)Packet.GetDmrPacketSubid()); + Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE); + Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE); + + return true; + +} + +bool CUlxProtocol::EncodeDvLastFramePacket(const CDvLastFramePacket &Packet, CBuffer *Buffer) const +{ + uint8 tag[] = { 'D','S','V','T',0x20,0x00,0x00,0x00,0x20,0x00,0x01,0x02 }; + uint8 dstarambe[] = { 0x55,0xC8,0x7A,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8 dstardvdata[] = { 0x25,0x1A,0xC6 }; + + Buffer->Set(tag, sizeof(tag)); + Buffer->Append(Packet.GetStreamId()); + Buffer->Append((uint8)((Packet.GetPacketId() % 21) | 0x40)); + Buffer->Append(dstarambe, sizeof(dstarambe)); + Buffer->Append(dstardvdata, sizeof(dstardvdata)); + + + Buffer->Append((uint8)Packet.GetDmrPacketId()); + Buffer->Append((uint8)Packet.GetDmrPacketSubid()); + Buffer->Append((uint8 *)Packet.GetAmbePlus(), AMBEPLUS_SIZE); + Buffer->Append((uint8 *)Packet.GetDvSync(), DVSYNC_SIZE); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// protocol revision helper + +int CUlxProtocol::GetConnectingPeerProtocolRevision(const CCallsign &Callsign, const CVersion &Version) +{ + int protrev; + + // BM ? + if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) ) + { + protrev = CBmPeer::GetProtocolRevision(Version); + } + // otherwise, assume native xlx + else + { + protrev = CUlxPeer::GetProtocolRevision(Version); + } + + // done + return protrev; +} + +std::shared_ptr CUlxProtocol::CreateNewPeer(const CCallsign &Callsign, const CIp &Ip, char *Modules, const CVersion &Version) +{ + // BM ? + if ( Callsign.HasSameCallsignWithWildcard(CCallsign("BM*")) ) + { + return std::make_shared(Callsign, Ip, Modules, Version); + } + else + { + return std::make_shared(Callsign, Ip, Modules, Version); + } +} diff --git a/src/ULXProtocol.h b/src/ULXProtocol.h new file mode 100644 index 0000000..6df699b --- /dev/null +++ b/src/ULXProtocol.h @@ -0,0 +1,91 @@ +// +// ULXProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 28/01/2016. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cxlxprotocol_h +#define cxlxprotocol_h + +#include "Version.h" +#include "Timer.h" +#include "DExtraProtocol.h" +#include "Clients.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CPeer; + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CUlxProtocol : public CDextraProtocol +{ +public: + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandlePeerLinks(void); + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + void OnDvFramePacketIn(std::unique_ptr &, const CIp * = nullptr); + void OnDvLastFramePacketIn(std::unique_ptr &, const CIp * = nullptr); + + // packet decoding helpers + bool IsValidKeepAlivePacket(const CBuffer &, CCallsign *); + bool IsValidConnectPacket(const CBuffer &, CCallsign *, char *, CVersion *); + bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); + bool IsValidAckPacket(const CBuffer &, CCallsign *, char *, CVersion *); + bool IsValidNackPacket(const CBuffer &, CCallsign *); + bool IsValidDvFramePacket(const CBuffer &, std::unique_ptr &); + bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr &); + + // packet encoding helpers + void EncodeKeepAlivePacket(CBuffer *); + void EncodeConnectPacket(CBuffer *, const char *); + void EncodeDisconnectPacket(CBuffer *); + void EncodeConnectAckPacket(CBuffer *, const char *); + void EncodeConnectNackPacket(CBuffer *); + bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const; + bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const; + + // protocol revision helper + int GetConnectingPeerProtocolRevision(const CCallsign &, const CVersion &); + std::shared_ptrCreateNewPeer(const CCallsign &, const CIp &, char *, const CVersion &); + +protected: + // time + CTimePoint m_LastKeepaliveTime; + CTimePoint m_LastPeersLinkTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cxlxprotocol_h */ diff --git a/src/User.cpp b/src/User.cpp new file mode 100644 index 0000000..f1d7c1c --- /dev/null +++ b/src/User.cpp @@ -0,0 +1,109 @@ +// +// cuser.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "User.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CUser::CUser() +{ + m_LastHeardTime = std::time(nullptr); +} + +CUser::CUser(const CCallsign &my, const CCallsign &rpt1, const CCallsign &rpt2, const CCallsign &xlx) +{ + m_My = my; + m_Rpt1 = rpt1; + m_Rpt2 = rpt2; + m_Xlx = xlx; + m_LastHeardTime = std::time(nullptr); +} + +CUser::CUser(const CUser &user) +{ + m_My = user.m_My; + m_Rpt1 = user.m_Rpt1; + m_Rpt2 = user.m_Rpt2; + m_Xlx = user.m_Xlx; + m_LastHeardTime = user.m_LastHeardTime; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operators + +bool CUser::operator ==(const CUser &user) const +{ + return ((user.m_My == m_My) && (user.m_Rpt1 == m_Rpt1) && (user.m_Rpt2 == m_Rpt2) && (user.m_Xlx == m_Xlx)); +} + + +bool CUser::operator <(const CUser &user) const +{ + // smallest is youngest + return (std::difftime(m_LastHeardTime, user.m_LastHeardTime) > 0); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// reporting + +void CUser::WriteXml(std::ofstream &xmlFile) +{ + xmlFile << "" << std::endl; + xmlFile << "\t" << m_My << "" << std::endl; + xmlFile << "\t" << m_Rpt1 << "" << std::endl; + xmlFile << "\t" << m_Rpt2.GetModule() << "" << std::endl; + xmlFile << "\t" << m_Xlx << "" << std::endl; + + char mbstr[100]; + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + xmlFile << "\t" << mbstr << "" << std::endl; + } + xmlFile << "" << std::endl; +} + +void CUser::GetJsonObject(char *Buffer) +{ + char sz[512]; + char mbstr[100]; + char my[16]; + char rpt1[16]; + + if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime))) + { + m_My.GetCallsignString(my); + m_Rpt1.GetCallsignString(rpt1); + + ::sprintf(sz, "{\"callsign\":\"%s\",\"node\":\"%s\",\"module\":\"%c\",\"time\":\"%s\"}", + my, + rpt1, + m_Rpt1.GetModule(), + mbstr); + ::strcat(Buffer, sz); + } +} diff --git a/src/User.h b/src/User.h new file mode 100644 index 0000000..2d432e6 --- /dev/null +++ b/src/User.h @@ -0,0 +1,65 @@ +// +// User.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cuser_h +#define cuser_h + +#include "Callsign.h" +#include "Buffer.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CUser +{ +public: + // constructor + CUser(); + CUser(const CCallsign &, const CCallsign &, const CCallsign &, const CCallsign &); + CUser(const CUser &); + + // destructor + ~CUser() {} + + // operation + void HeardNow(void) { m_LastHeardTime = std::time(nullptr); } + + // operators + bool operator ==(const CUser &) const; + bool operator <(const CUser &) const; + + // reporting + void WriteXml(std::ofstream &); + void GetJsonObject(char *); + +protected: + // data + CCallsign m_My; + CCallsign m_Rpt1; + CCallsign m_Rpt2; + CCallsign m_Xlx; + std::time_t m_LastHeardTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cuser_h */ diff --git a/src/Users.cpp b/src/Users.cpp new file mode 100644 index 0000000..2b26b4e --- /dev/null +++ b/src/Users.cpp @@ -0,0 +1,76 @@ +// +// cusers.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Users.h" +#include "Reflector.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CUsers::CUsers() {} + +//////////////////////////////////////////////////////////////////////////////////////// +// users management + +void CUsers::AddUser(const CUser &user) +{ + // add + m_Users.push_front(user); + + // if list size too big, remove oldest + if ( m_Users.size() >= (LASTHEARD_USERS_MAX_SIZE-1) ) + { + m_Users.resize(m_Users.size()-1); + } + + // notify + g_Reflector.OnUsersChanged(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +void CUsers::Hearing(const CCallsign &my, const CCallsign &rpt1, const CCallsign &rpt2) +{ + Hearing(my, rpt1, rpt2, g_Reflector.GetCallsign()); +} + +void CUsers::Hearing(const CCallsign &my, const CCallsign &rpt1, const CCallsign &rpt2, const CCallsign &xlx) +{ + CUser heard(my, rpt1, rpt2, xlx); + + // first check if this user is already listed. If so, erase him. + for ( auto it=begin(); it!=end(); it++ ) + { + if (*it == heard) + { + m_Users.erase(it); + break; + } + } + + AddUser(heard); +} diff --git a/src/Users.h b/src/Users.h new file mode 100644 index 0000000..b3917b7 --- /dev/null +++ b/src/Users.h @@ -0,0 +1,65 @@ +// +// Users.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 13/11/2015. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cusers_h +#define cusers_h + +#include "User.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CUsers +{ +public: + // constructor + CUsers(); + + // destructor + virtual ~CUsers() {} + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // management + int GetSize(void) const { return (int)m_Users.size(); } + void AddUser(const CUser &); + + // pass-thru + std::list::iterator begin() { return m_Users.begin(); } + std::list::iterator end() { return m_Users.end(); } + + // operation + void Hearing(const CCallsign &, const CCallsign &, const CCallsign &); + void Hearing(const CCallsign &, const CCallsign &, const CCallsign &, const CCallsign &); + +protected: + // data + std::mutex m_Mutex; + std::list m_Users; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cusers_h */ diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 0000000..348f77b --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009,2014,2015,2016 Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "Utils.h" + +#include +#include + +void CUtils::byteToBitsBE(unsigned char byte, bool* bits) +{ + assert(bits != nullptr); + + bits[0U] = (byte & 0x80U) == 0x80U; + bits[1U] = (byte & 0x40U) == 0x40U; + bits[2U] = (byte & 0x20U) == 0x20U; + bits[3U] = (byte & 0x10U) == 0x10U; + bits[4U] = (byte & 0x08U) == 0x08U; + bits[5U] = (byte & 0x04U) == 0x04U; + bits[6U] = (byte & 0x02U) == 0x02U; + bits[7U] = (byte & 0x01U) == 0x01U; +} + +void CUtils::byteToBitsLE(unsigned char byte, bool* bits) +{ + assert(bits != nullptr); + + bits[0U] = (byte & 0x01U) == 0x01U; + bits[1U] = (byte & 0x02U) == 0x02U; + bits[2U] = (byte & 0x04U) == 0x04U; + bits[3U] = (byte & 0x08U) == 0x08U; + bits[4U] = (byte & 0x10U) == 0x10U; + bits[5U] = (byte & 0x20U) == 0x20U; + bits[6U] = (byte & 0x40U) == 0x40U; + bits[7U] = (byte & 0x80U) == 0x80U; +} + +void CUtils::bitsToByteBE(const bool* bits, unsigned char& byte) +{ + assert(bits != nullptr); + + byte = bits[0U] ? 0x80U : 0x00U; + byte |= bits[1U] ? 0x40U : 0x00U; + byte |= bits[2U] ? 0x20U : 0x00U; + byte |= bits[3U] ? 0x10U : 0x00U; + byte |= bits[4U] ? 0x08U : 0x00U; + byte |= bits[5U] ? 0x04U : 0x00U; + byte |= bits[6U] ? 0x02U : 0x00U; + byte |= bits[7U] ? 0x01U : 0x00U; +} + +void CUtils::bitsToByteLE(const bool* bits, unsigned char& byte) +{ + assert(bits != nullptr); + + byte = bits[0U] ? 0x01U : 0x00U; + byte |= bits[1U] ? 0x02U : 0x00U; + byte |= bits[2U] ? 0x04U : 0x00U; + byte |= bits[3U] ? 0x08U : 0x00U; + byte |= bits[4U] ? 0x10U : 0x00U; + byte |= bits[5U] ? 0x20U : 0x00U; + byte |= bits[6U] ? 0x40U : 0x00U; + byte |= bits[7U] ? 0x80U : 0x00U; +} diff --git a/src/Utils.h b/src/Utils.h new file mode 100644 index 0000000..0c1cb8c --- /dev/null +++ b/src/Utils.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009,2014,2015 by Jonathan Naylor, G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef Utils_H +#define Utils_H + +#include + +class CUtils +{ +public: + static void byteToBitsBE(unsigned char byte, bool* bits); + static void byteToBitsLE(unsigned char byte, bool* bits); + + static void bitsToByteBE(const bool* bits, unsigned char& byte); + static void bitsToByteLE(const bool* bits, unsigned char& byte); + +private: +}; + +#endif diff --git a/src/Version.cpp b/src/Version.cpp new file mode 100644 index 0000000..deb4ea5 --- /dev/null +++ b/src/Version.cpp @@ -0,0 +1,79 @@ +// +// cversion.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 05/01/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "Version.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CVersion::CVersion() +{ + m_iMajor = 0; + m_iMinor = 0; + m_iRevision = 0; +} + +CVersion::CVersion(int iMajor, int iMinor, int iRevision) +{ + m_iMajor = iMajor; + m_iMinor = iMinor; + m_iRevision = iRevision; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// comparaison + +bool CVersion::IsEqualOrHigherTo(const CVersion &version) const +{ + if ( m_iMajor > version.m_iMajor ) + { + return true; + } + else if ( m_iMajor == version.m_iMajor ) + { + if ( m_iMinor > version.m_iMinor ) + { + return true; + } + else if ( m_iMinor == version.m_iMinor ) + { + if ( m_iRevision >= version.m_iRevision ) + { + return true; + } + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operator + +bool CVersion::operator ==(const CVersion &Version) const +{ + return ( (Version.m_iMajor == m_iMajor) && + (Version.m_iMinor == m_iMinor) && + (Version.m_iRevision == m_iRevision )) ; +} diff --git a/src/Version.h b/src/Version.h new file mode 100644 index 0000000..2a87e74 --- /dev/null +++ b/src/Version.h @@ -0,0 +1,60 @@ +// +// Version.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 05/01/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cversion_h +#define cversion_h + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CVersion +{ +public: + // constructor + CVersion(); + CVersion(int, int, int); + + // get + int GetMajor(void) const { return m_iMajor; } + int GetMinor(void) const { return m_iMinor; } + int GetRevision(void) const { return m_iRevision; } + + // comparaison + bool IsEqualOrHigherTo(const CVersion &) const; + + // operator + bool operator ==(const CVersion &) const; + +protected: + // data + int m_iMajor; + int m_iMinor; + int m_iRevision; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cversion_h */ diff --git a/src/WiresXCmd.cpp b/src/WiresXCmd.cpp new file mode 100644 index 0000000..d493655 --- /dev/null +++ b/src/WiresXCmd.cpp @@ -0,0 +1,45 @@ +// +// cwiresxcmd.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "WiresXCmd.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CWiresxCmd::CWiresxCmd() +{ + m_iCmd = WIRESX_CMD_UNKNOWN; + m_Time.Now(); +} + +CWiresxCmd::CWiresxCmd(const CIp &Ip, const CCallsign &Callsign, int iCmd, int iArg) +{ + m_Ip = Ip; + m_Callsign = Callsign; + m_iCmd = iCmd; + m_iArg = iArg; + m_Time.Now(); +} diff --git a/src/WiresXCmd.h b/src/WiresXCmd.h new file mode 100644 index 0000000..7db8203 --- /dev/null +++ b/src/WiresXCmd.h @@ -0,0 +1,71 @@ +// +// WiresXCmd.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cwiresxcmd_h +#define cwiresxcmd_h + +#include "Callsign.h" +#include "IP.h" +#include "Timer.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// defines + +// Wires-X commands +#define WIRESX_CMD_UNKNOWN 0 +#define WIRESX_CMD_DX_REQ 1 +#define WIRESX_CMD_ALL_REQ 2 +#define WIRESX_CMD_SEARCH_REQ 3 +#define WIRESX_CMD_CONN_REQ 4 +#define WIRESX_CMD_DISC_REQ 5 + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CWiresxCmd +{ +public: + // constructor + CWiresxCmd(); + CWiresxCmd(const CIp &, const CCallsign &, int, int); + + // get + const CCallsign &GetCallsign(void) const { return m_Callsign; } + const CIp &GetIp(void) const { return m_Ip; } + int GetCmd(void) const { return m_iCmd; } + int GetArg(void) const { return m_iArg; } + const CTimePoint &GetTime(void) const { return m_Time; } + +protected: + // data + CIp m_Ip; + CCallsign m_Callsign; + int m_iCmd; + int m_iArg; + CTimePoint m_Time; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxcmd_h */ diff --git a/src/WiresXCmdHandler.cpp b/src/WiresXCmdHandler.cpp new file mode 100644 index 0000000..9f88ca0 --- /dev/null +++ b/src/WiresXCmdHandler.cpp @@ -0,0 +1,772 @@ +// +// cwiresxcmdhandler.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "CRC.h" +#include "YSFFich.h" +#include "YSFPayload.h" +#include "YSFClient.h" +#include "YSFNodeDirFile.h" +#include "YSFNodeDirhttp.h" +#include "YSFUtils.h" +#include "Reflector.h" +#include "WiresXCmdHandler.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CWiresxCmdHandler::CWiresxCmdHandler() +{ + m_seqNo = 0; + keep_running = true; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// destructor + +CWiresxCmdHandler::~CWiresxCmdHandler() +{ + // kill thread + Close(); + + // empty queue + m_CmdQueue.Lock(); + while ( !m_CmdQueue.empty() ) + { + m_CmdQueue.pop(); + } + m_CmdQueue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// initialization + +bool CWiresxCmdHandler::Init(void) +{ + // fill our wiresx info + m_ReflectorWiresxInfo.SetCallsign(g_Reflector.GetCallsign()); + m_ReflectorWiresxInfo.SetNode(g_Reflector.GetCallsign()); + m_ReflectorWiresxInfo.SetName("Reflector"); + + // reset stop flag + keep_running = true; + + try + { + m_Future = std::async(std::launch::async, &CWiresxCmdHandler::Thread, this); + } + catch(const std::exception &e) + { + std::cerr << "ERROR: could not start WiresX Command Handler: " << e.what() << std::endl; + return false; + } + + + // done + return true; +} + +void CWiresxCmdHandler::Close(void) +{ + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CWiresxCmdHandler::Thread() +{ + while (keep_running) + { + Task(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CWiresxCmdHandler::Task(void) +{ + CWiresxInfo Info; + CWiresxCmd Cmd; + uint32 uiNodeTxFreq; + uint32 uiNodeRxFreq; + char cModule; + bool bCmd; + + // anything to do ? + bCmd = false; + m_CmdQueue.Lock(); + { + // any cmd in our queue ? + if ( !m_CmdQueue.empty() ) + { + // yes, get a command + Cmd = m_CmdQueue.front(); + // check that the command is at least one second old + // so that the so delayed processing of the command + // introduce the delay the radio needs to switch + // from tx to rx + if ( Cmd.GetTime().DurationSinceNow() >= WIRESX_REPLY_DELAY ) + { + m_CmdQueue.pop(); + bCmd = true; + } + } + } + m_CmdQueue.Unlock(); + + + // handle it + if ( bCmd ) + { + // fill our info object + Info = m_ReflectorWiresxInfo; + g_YsfNodeDir.FindFrequencies(Cmd.GetCallsign(), &uiNodeTxFreq, &uiNodeRxFreq); + Info.SetFrequencies(uiNodeTxFreq, uiNodeRxFreq); + + // find our client and the module it's currentlink linked to + cModule = ' '; + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Cmd.GetCallsign(), Cmd.GetIp(), PROTOCOL_YSF); + if ( client ) + { + cModule = client->GetReflectorModule(); + } + g_Reflector.ReleaseClients(); + + // and crack the cmd + switch ( Cmd.GetCmd() ) + { + case WIRESX_CMD_DX_REQ: + std::cout << "Wires-X DX_REQ command from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << std::endl; + // reply + ReplyToWiresxDxReqPacket(Cmd.GetIp(), Info, cModule); + break; + case WIRESX_CMD_ALL_REQ: + case WIRESX_CMD_SEARCH_REQ: + std::cout << "Wires-X ALL_REQ command from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << std::endl; + // reply + ReplyToWiresxAllReqPacket(Cmd.GetIp(), Info, Cmd.GetArg()); + break; + case WIRESX_CMD_CONN_REQ: + if ( (Cmd.GetArg() >= 1) && (Cmd.GetArg() <= NB_OF_MODULES) ) + { + cModule = 'A' + (char)(Cmd.GetArg() - 1); + std::cout << "Wires-X CONN_REQ command to link on module " << cModule << " from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << std::endl; + // acknowledge + ReplyToWiresxConnReqPacket(Cmd.GetIp(), Info, cModule); + // change client's module + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Cmd.GetCallsign(), Cmd.GetIp(), PROTOCOL_YSF); + if ( client ) + { + client->SetReflectorModule(cModule); + } + g_Reflector.ReleaseClients(); + } + else + { + std::cout << "Wires-X CONN_REQ command with illegal argument from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << std::endl; + } + break; + case WIRESX_CMD_DISC_REQ: + std::cout << "Wires-X DISC_REQ command from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << std::endl; + // and reply + ReplyToWiresxDiscReqPacket(Cmd.GetIp(), Info); + // change client's module + { + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Cmd.GetCallsign(), Cmd.GetIp(), PROTOCOL_YSF); + if ( client != nullptr ) + { + client->SetReflectorModule(' '); + } + g_Reflector.ReleaseClients(); + } + break; + case WIRESX_CMD_UNKNOWN: + default: + std::cout << "Wires-X non implemented command from " << Cmd.GetCallsign() << " at " << Cmd.GetIp() << " cmd=" << Cmd.GetCmd() << " arg=" << Cmd.GetArg() << std::endl; + break; + } + } + else + { + // if nothing to do, sleep a bit + CTimePoint::TaskSleepFor(100); + } + +} + +//////////////////////////////////////////////////////////////////////////////////////// +// packet encoding + +bool CWiresxCmdHandler::ReplyToWiresxDxReqPacket(const CIp &Ip, const CWiresxInfo &WiresxInfo, char Module) +{ + uint8 DX_RESP[] = {0x5DU, 0x51U, 0x5FU, 0x26U}; + bool ok; + uint8 data[150U]; + uint8 RoomId; + bool IsLinked; + + // linked module + // module A == 0 + IsLinked = (Module != ' '); + RoomId = (uint8)(Module - 'A'); + + // fill data buffer + ::memset(data, 0x00U, 150U); + ::memset(data, ' ', 128U); + // seq no + data[0U] = m_seqNo; + // command + ::memcpy(data + 1U, DX_RESP, 4U); + // node info + ::memcpy(data + 5U, WiresxInfo.GetId(), 5U); + ::memcpy(data + 10U, WiresxInfo.GetNode(), 10U); + ::memcpy(data + 20U, WiresxInfo.GetName(), 14U); + // linked room + if (!IsLinked) + { + data[34U] = '1'; + data[35U] = '2'; + // no room linked + data[57U] = '0'; + data[58U] = '0'; + data[59U] = '0'; + } + else + { + data[34U] = '1'; + data[35U] = '5'; + // linked room + char item[16U]; + // refl->m_id + ::sprintf(item, "%05d", 4001U + RoomId); + ::memcpy(data + 36U, item, 5U); + // refl->name + ::memset(item, ' ', 16U); + ::memcpy(item, "MODULE", 6U); // K2IE fix for U/C only radios + item[7] = 'A' + RoomId; + ::memcpy(data + 41U, item, 16U); + // refl->count + ::sprintf(item, "%03d", RoomId + 1); + ::memcpy(data + 57U, item, 3U); + // other + ::memset(data + 60U, ' ', 10U); + // refl->m_desc + ::memcpy(data + 70U, "Description ", 14U); + } + + // frequencies + { + unsigned int offset; + char sign; + if (WiresxInfo.GetTxFrequency() >= WiresxInfo.GetRxFrequency()) + { + offset = WiresxInfo.GetTxFrequency() - WiresxInfo.GetRxFrequency(); + sign = '-'; + } + else + { + offset = WiresxInfo.GetRxFrequency() - WiresxInfo.GetTxFrequency(); + sign = '+'; + } + unsigned int freqHz = WiresxInfo.GetTxFrequency() % 1000000U; + //unsigned int freqkHz = (freqHz + 500U) / 1000U; + + char freq[30U]; + ::sprintf(freq, "%05u.%06u%c%03u.%06u", WiresxInfo.GetTxFrequency() / 1000000U, freqHz, sign, offset / 1000000U, offset % 1000000U); + + ::memcpy(data + 84U, freq, 23U); + } + + // EOD & CRC + data[127U] = 0x03U; + data[128U] = CCRC::addCRC(data, 128U); + + // and encode the reply + CBuffer Data; + Data.Set(data, 129U); + ok = EncodeAndSendWiresxPacket(Ip, Data, WiresxInfo); + + // done + m_seqNo++; + return ok; +} + +bool CWiresxCmdHandler::ReplyToWiresxAllReqPacket(const CIp &Ip, const CWiresxInfo &WiresxInfo, int Start) +{ + bool ok = false; + uint8 ALL_RESP[] = {0x5DU, 0x46U, 0x5FU, 0x29U}; + uint8 data[1100U]; + + // fill data buffer + ::memset(data, 0x00U, 1100U); + // seq no + data[0U] = m_seqNo; + // command + ::memcpy(data + 1U, ALL_RESP, 4U); + data[5U] = '2'; + data[6U] = '1'; + // node info + ::memcpy(data + 7U, WiresxInfo.GetId(), 5U); + ::memcpy(data + 12U, WiresxInfo.GetNode(), 10U); + + // number of entries + uint total = NB_OF_MODULES; + uint n = NB_OF_MODULES - Start; + if (n > 20U) + n = 20U; + ::sprintf((char*)(data + 22U), "%03u%03u", n, total); + data[28U] = 0x0DU; + + // entries + uint offset = 29U; + for ( uint i = 0; i < n; i++ ) + { + char item[16U]; + // module A == 0 + int RoomId = i + Start; + + // prepare + ::memset(data + offset, ' ', 50U); + data[offset + 0U] = '5'; + + // refl->m_id + ::sprintf(item, "%05d", 4001U + RoomId); + ::memcpy(data + offset + 1U, item, 5U); + // refl->name + ::memset(item, ' ', 16U); + ::memcpy(item, "MODULE", 6U); // K2IE fix for U/C only radios + item[7] = 'A' + RoomId; + ::memcpy(data + offset + 6U, item, 16U); + // refl->count + ::sprintf(item, "%03d", RoomId + 1); + ::memcpy(data + offset + 22U, item, 3U); + // other + ::memset(data + offset + 25U, ' ', 10U); + // refl->m_desc + ::memcpy(data + offset + 35U, "Description ", 14U); + data[offset + 49U] = 0x0DU; + // next + offset += 50U; + } + + // the following is a workaround for FT2D which + // do not accept less than 20 items frames. + // first send a 'patched' frame + if ( n < 20 ) + { + // FT2D workaround + uint offset2 = offset; + // patch the remaining + uint k = 1029U - offset2; + ::memset(data+offset2, ' ', k); + offset2 += k; + + // EOD + CRC + data[offset2 + 0U] = 0x03U; + data[offset2 + 1U] = CCRC::addCRC(data, offset2 + 1U); + offset2 += 2U; + + // and encode the reply + CBuffer Data; + Data.Set(data, offset2 + 2U); + ok = EncodeAndSendWiresxPacket(Ip, Data, WiresxInfo); + } + + + // and next repeat with normal frame + { + // EOD + CRC + data[offset + 0U] = 0x03U; + data[offset + 1U] = CCRC::addCRC(data, offset + 1U); + offset += 2U; + + // patch the remaining + //uint k = 1031U - offset; + //::memset(data+offset, ' ', k); + //offset += k; + + // and encode the reply + CBuffer Data; + Data.Set(data, offset + 2U); + ok = EncodeAndSendWiresxPacket(Ip, Data, WiresxInfo); + } + + + // done + m_seqNo++; + return ok; +} + +bool CWiresxCmdHandler::ReplyToWiresxConnReqPacket(const CIp &Ip, const CWiresxInfo &WiresxInfo, char Module) +{ + uint8 CONN_RESP[] = {0x5DU, 0x41U, 0x5FU, 0x26U}; + bool ok = false; + uint8 data[110U]; + uint RoomId; + + // linked room + // Module A == 0 + RoomId = (uint8)(Module - 'A'); + + // prepare buffer + ::memset(data, 0x00U, 110U); + ::memset(data, ' ', 90U); + + // seq no + data[0U] = m_seqNo; + // command + ::memcpy(data + 1U, CONN_RESP, 4U); + // node info + ::memcpy(data + 5U, WiresxInfo.GetId(), 5U); + ::memcpy(data + 10U, WiresxInfo.GetNode(), 10U); + ::memcpy(data + 20U, WiresxInfo.GetName(), 14U); + data[34U] = '1'; + data[35U] = '5'; + // entry info + { + char item[16U]; + + // refl->m_id + ::sprintf(item, "%05d", 4001U + RoomId); + ::memcpy(data + 36U, item, 5U); + // refl->name + ::memset(item, ' ', 16U); + ::memcpy(item, "MODULE", 6U); // K2IE fix for U/C only radios + item[7] = 'A' + RoomId; + ::memcpy(data + 41U, item, 16U); + // refl->count + ::sprintf(item, "%03d", RoomId + 1); + ::memcpy(data + 57U, item, 3U); + // refl->m_desc + ::memcpy(data + 70U, "Description ", 14U); + } + data[84U] = '0'; + data[85U] = '0'; + data[86U] = '0'; + data[87U] = '0'; + data[88U] = '0'; + + // EOD + CRC + data[89U] = 0x03U; + data[90U] = CCRC::addCRC(data, 90U); + + // and encode the reply + CBuffer Data; + Data.Set(data, 91U); + ok = EncodeAndSendWiresxPacket(Ip, Data, WiresxInfo); + + // done + m_seqNo++; + return ok; +} + +bool CWiresxCmdHandler::ReplyToWiresxDiscReqPacket(const CIp &Ip, const CWiresxInfo &WiresxInfo) +{ + uint8 DISC_RESP[] = {0x5DU, 0x41U, 0x5FU, 0x26U}; + bool ok = false; + uint8 data[110U]; + + // prepare buffer + ::memset(data, 0x00U, 110U); + ::memset(data, ' ', 90U); + + // seq no + data[0U] = m_seqNo; + // command + ::memcpy(data + 1U, DISC_RESP, 4U); + // node info + ::memcpy(data + 5U, WiresxInfo.GetId(), 5U); + ::memcpy(data + 10U, WiresxInfo.GetNode(), 10U); + ::memcpy(data + 20U, WiresxInfo.GetName(), 14U); + // data + data[34U] = '1'; + data[35U] = '2'; + + data[57U] = '0'; + data[58U] = '0'; + data[59U] = '0'; + + // EOD + CRC + data[89U] = 0x03U; + data[90U] = CCRC::addCRC(data, 90U); + + // and encode the reply + CBuffer Data; + Data.Set(data, 91U); + ok = EncodeAndSendWiresxPacket(Ip, Data, WiresxInfo); + + // done + m_seqNo++; + return ok; +} + + + +//////////////////////////////////////////////////////////////////////////////////////// +///// packet encoding helpers + +bool CWiresxCmdHandler::EncodeAndSendWiresxPacket(const CIp &Ip, const CBuffer &DataOrg, const CWiresxInfo &WiresxInfo) +{ + uint8 DEFAULT_FICH[] = {0x20U, 0x00U, 0x01U, 0x00U}; + uint8 NET_HEADER[] = "YSFD ALL "; + CYSFFICH fich; + CYSFPayload payload; + uint8 buffer[200U]; + + + CBuffer Data(DataOrg); + + // seq no + uint8 seqNo = 0U; + + // calculate bt and adjust length + uint length = (uint)Data.size(); + uint8 bt = 0; + if (length > 260U) + { + bt = 1U; + bt += (length - 260U) / 259U; + length += bt; + } + if (length > 20U) + { + uint blocks = (length - 20U) / 40U; + if ((length % 40U) > 0U) + blocks++; + length = blocks * 40U + 20U; + } + else + { + length = 20U; + } + if ( length > (uint)Data.size() ) + { + Data.Append((uint8)0x20U, (int)(length - (uint)Data.size())); + } + + // ft + uint8 ft = WiresxCalcFt(length, 0U); + + // Write the header + { + //header + ::memcpy(buffer, NET_HEADER, 34U); + ::memcpy(buffer + 4U, WiresxInfo.GetCallsign(), 10U); + ::memcpy(buffer + 14U, WiresxInfo.GetNode(), 10U); + // sync + ::memcpy(buffer + 35U, YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); + // Fich + fich.load(DEFAULT_FICH); + fich.setFI(YSF_FI_HEADER); + fich.setBT(bt); + fich.setFT(ft); + fich.encode(buffer + 35U + 5U); + // payload + payload.writeDataFRModeData1(WiresxInfo.GetCsd1(), buffer + 35U); + payload.writeDataFRModeData2(WiresxInfo.GetCsd2(), buffer + 35U); + // seqno + buffer[34U] = seqNo; + seqNo += 2U; + // and post it + SendPacket(Ip, buffer); + } + + // write the payload + fich.setFI(YSF_FI_COMMUNICATIONS); + uint offset = 0; + for ( uint8 bn = 0; bn <= bt; bn++ ) + { + for ( uint8 fn = 0; fn <= ft; fn++ ) + { + // fich + fich.setFT(ft); + fich.setFN(fn); + fich.setBT(bt); + fich.setBN(bn); + fich.encode(buffer + 35U + 5U); + // seq no + buffer[34U] = seqNo; + seqNo += 2U; + // data + //if ( (bn == 0) && (fn == 0) ) + if ( fn == 0 ) + { + payload.writeDataFRModeData1(WiresxInfo.GetCsd1(), buffer + 35); + payload.writeDataFRModeData2(WiresxInfo.GetCsd2(), buffer + 35); + } + //else if ( (bn == 0) && (fn == 1) ) + else if ( fn == 1 ) + { + if ( bn == 0 ) + { + payload.writeDataFRModeData1(WiresxInfo.GetCsd3(), buffer + 35); + payload.writeDataFRModeData2(Data.data() + offset, buffer + 35); + offset += 20; + } + else + { + uint8 temp[20U]; + temp[0U] = 0x00U; + ::memcpy(temp + 1U, Data.data() + offset, 19U); + payload.writeDataFRModeData2(temp, buffer + 35U); + offset += 19U; + } + } + else + { + payload.writeDataFRModeData1(Data.data() + offset, buffer + 35); + offset += 20; + payload.writeDataFRModeData2(Data.data() + offset, buffer + 35); + offset += 20; + } + // and post it + SendPacket(Ip, buffer); + // and some delay before next packet + CTimePoint::TaskSleepFor(100); + } + } + + // Write the trailer + { + // fich + fich.setFI(YSF_FI_TERMINATOR); + fich.setFN(ft); + fich.setBN(bt); + fich.encode(buffer + 35U + 5U); + // payload + payload.writeDataFRModeData1(WiresxInfo.GetCsd1(), buffer + 35U); + payload.writeDataFRModeData2(WiresxInfo.GetCsd2(), buffer + 35U); + // seq no + buffer[34U] = seqNo | 0x01U; + // and post it + SendPacket(Ip, buffer); + } + + // done + return true; +} + + +uint8 CWiresxCmdHandler::WiresxCalcFt(uint length, uint offset) const +{ + length -= offset; + if (length > 220U) return 7U; + if (length > 180U) return 6U; + if (length > 140U) return 5U; + if (length > 100U) return 4U; + if (length > 60U) return 3U; + if (length > 20U) return 2U; + return 1U; +} + + +void CWiresxCmdHandler::SendPacket(const CIp &Ip, uint8 *Buffer) +{ + //CBuffer packet(Buffer, 155); + //DebugTestDecodePacket(packet); + + // and push in queue + m_PacketQueue.Lock(); + { + m_PacketQueue.push(CWiresxPacket(CBuffer(Buffer, 155), Ip)); + } + m_PacketQueue.Unlock(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// debug + +#ifdef DEBUG_DUMPFILE +bool CWiresxCmdHandler::DebugTestDecodePacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 'Y','S','F','D' }; + static uint8 command[4098]; + static int len; + CYSFFICH Fich; + CYSFPayload payload; + CBuffer dump; + bool valid = false; + + if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // decode YSH fich + if ( Fich.decode(&(Buffer.data()[40])) ) + { + std::cout << (int)Fich.getDT() << "," + << (int)Fich.getFI() << "," + << (int)Fich.getBN() << "," + << (int)Fich.getBT() << "," + << (int)Fich.getFN() << "," + << (int)Fich.getFT() << " : "; + + switch ( Fich.getFI() ) + { + case YSF_FI_HEADER: + len = 0; + ::memset(command, 0x00, sizeof(command)); + std::cout << "Header" << std::endl; + break; + case YSF_FI_TERMINATOR: + std::cout << "Trailer" << std::endl; + std::cout << "length of payload : " << len << std::endl; + dump.Set(command, len); + dump.DebugDump(g_Reflector.m_DebugFile); + dump.DebugDumpAscii(g_Reflector.m_DebugFile); + break; + case YSF_FI_COMMUNICATIONS: + if ( Fich.getDT() == YSF_DT_DATA_FR_MODE ) + { + valid = payload.readDataFRModeData1(&(Buffer.data()[35]), command + len); + len += 20; + valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + len); + len += 20; + std::cout << "decoded ok" << std::endl; + } + break; + } + } + else + { + std::cout << "invalid fich in packet" << std::endl; + } + } + else + { + std::cout << "invalid size packet" << std::endl; + } + return valid; +} +#endif diff --git a/src/WiresXCmdHandler.h b/src/WiresXCmdHandler.h new file mode 100644 index 0000000..6096670 --- /dev/null +++ b/src/WiresXCmdHandler.h @@ -0,0 +1,94 @@ +// +// WiresXCmdHandler.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cwiresxcmdhandler_h +#define cwiresxcmdhandler_h + +#include "WiresXCmdInc.h" +#include "WiresxCmdQueue.h" +#include "WiresxPacketQueue.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +#define WIRESX_REPLY_DELAY (1.000) + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CWiresxCmdHandler +{ +public: + // constructor + CWiresxCmdHandler(); + + // destructor + ~CWiresxCmdHandler(); + + // initialization + bool Init(void); + void Close(void); + + // queues + CWiresxCmdQueue *GetCmdQueue(void) { m_CmdQueue.Lock(); return &m_CmdQueue; } + void ReleaseCmdQueue(void) { m_CmdQueue.Unlock(); } + CWiresxPacketQueue *GetPacketQueue(void) { m_PacketQueue.Lock(); return &m_PacketQueue; } + void ReleasePacketQueue(void) { m_PacketQueue.Unlock(); } + + // get + + // task + void Thread(void); + void Task(void); + +protected: + // packet encoding + bool ReplyToWiresxDxReqPacket(const CIp &, const CWiresxInfo &, char); + bool ReplyToWiresxAllReqPacket(const CIp &, const CWiresxInfo &, int); + bool ReplyToWiresxConnReqPacket(const CIp &, const CWiresxInfo &, char); + bool ReplyToWiresxDiscReqPacket(const CIp &, const CWiresxInfo &); + + // packet encoding helpers + bool EncodeAndSendWiresxPacket(const CIp &, const CBuffer &, const CWiresxInfo &); + uint8 WiresxCalcFt(uint, uint) const; + void SendPacket(const CIp &, uint8 *); + + // debug + bool DebugTestDecodePacket(const CBuffer &); + +protected: + // data + CWiresxInfo m_ReflectorWiresxInfo; + uint8_t m_seqNo; + + // queues + CWiresxCmdQueue m_CmdQueue; + CWiresxPacketQueue m_PacketQueue; + + // thread + std::atomic keep_running; + std::future m_Future; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxcmdhandler_h */ diff --git a/src/WiresXCmdQueue.h b/src/WiresXCmdQueue.h new file mode 100644 index 0000000..a3285d4 --- /dev/null +++ b/src/WiresXCmdQueue.h @@ -0,0 +1,63 @@ +// +// WiresxCmdQueue.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cwiresxcmdqueue_h +#define cwiresxcmdqueue_h + + +#include "WiresXCmd.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CWiresxCmdQueue +{ +public: + // constructor + CWiresxCmdQueue() {} + + // destructor + ~CWiresxCmdQueue() {} + + // lock + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + + // pass thru + CWiresxCmd front() { return queue.front(); } + void pop() { queue.pop(); } + void push(CWiresxCmd cmd) { queue.push(cmd); } + bool empty() const { return queue.empty(); } + +protected: + // status + std::mutex m_Mutex; + std::queue queue; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxcmdqueue_h */ diff --git a/src/WiresXInfo.cpp b/src/WiresXInfo.cpp new file mode 100644 index 0000000..a005dac --- /dev/null +++ b/src/WiresXInfo.cpp @@ -0,0 +1,107 @@ +// +// cwiresxinfo.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/09/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "Callsign.h" +#include "WiresXCmdInc.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor + +CWiresxInfo::CWiresxInfo() +{ + ::memset(m_callsign, ' ', YSF_CALLSIGN_LENGTH); + ::memset(m_node, ' ', YSF_CALLSIGN_LENGTH); + ::memset(m_name, ' ', 14); + ::memset(m_id, ' ', 6); + m_txFrequency = 0U; + m_rxFrequency = 0U; + + ::memset(m_csd1, '*', 20U); + ::memset(m_csd2, ' ', 20U); + ::memset(m_csd3, ' ', 20U); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +///// set + +void CWiresxInfo::SetCallsign(const CCallsign &callsign) +{ + ::memset(m_callsign, ' ', YSF_CALLSIGN_LENGTH); + callsign.GetCallsign(m_callsign); + UpdateCsds(); +} + +void CWiresxInfo::SetNode(const char *node) +{ + ::memset(m_node, ' ', YSF_CALLSIGN_LENGTH); + ::memcpy(m_node, node, MIN(::strlen(node), YSF_CALLSIGN_LENGTH)); + UpdateCsds(); +} + +void CWiresxInfo::SetName(const char *name) +{ + ::memset(m_name, ' ', 14); + ::memcpy(m_name, name, MIN(::strlen(name), 14)); + UpdateId(); +} + +void CWiresxInfo::SetFrequencies(uint txFreq, uint rxFreq) +{ + m_txFrequency = txFreq; + m_rxFrequency = rxFreq; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// updates + +void CWiresxInfo::UpdateCsds(void) +{ + ::memset(m_csd1, '*', 20U); + ::memset(m_csd2, ' ', 20U); + ::memset(m_csd3, ' ', 20U); + ::memcpy(m_csd1 + 10U, m_node, 10U); + ::memcpy(m_csd2 + 0U, m_callsign, 10U); + ::memcpy(m_csd3 + 0U, m_id, 5U); + ::memcpy(m_csd3 + 15U, m_id, 5U); +} + +void CWiresxInfo::UpdateId(void) +{ + uint hash = 0U; + for (uint i = 0U; i < 14; i++) + { + hash += m_name[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + ::sprintf((char *)m_id, "%05u", hash % 100000U); + UpdateCsds(); +} diff --git a/src/WiresXInfo.h b/src/WiresXInfo.h new file mode 100644 index 0000000..27243bb --- /dev/null +++ b/src/WiresXInfo.h @@ -0,0 +1,76 @@ +// +// WiresXCmdInc.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 29/09/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cwiresxinfo_h +#define cwiresxinfo_h + +#include "YSFDefines.h" +#include "Callsign.h" + +class CWiresxInfo +{ +public: + // constructor + CWiresxInfo(); + + // destructor + virtual ~CWiresxInfo() {} + + // get + const uint8 *GetCallsign(void) const { return m_callsign; } + const uint8 *GetNode(void) const { return m_node; } + const uint8 *GetName(void) const { return m_name; } + const uint8 *GetId(void) const { return m_id; } + uint GetTxFrequency(void) const { return m_txFrequency; } + uint GetRxFrequency(void) const { return m_rxFrequency; } + const uint8 *GetCsd1(void) const { return m_csd1; } + const uint8 *GetCsd2(void) const { return m_csd2; } + const uint8 *GetCsd3(void) const { return m_csd3; } + + // set + void SetCallsign(const CCallsign &); + void SetNode(const char *); + void SetName(const char *); + void SetFrequencies(uint, uint); + + // uodates + void UpdateCsds(void); + void UpdateId(void); + +protected: + // data + uint8 m_callsign[YSF_CALLSIGN_LENGTH]; + uint8 m_node[YSF_CALLSIGN_LENGTH]; + uint8 m_name[14]; + uint8 m_id[6]; + uint m_txFrequency; + uint m_rxFrequency; + + uint8 m_csd1[20]; + uint8 m_csd2[20]; + uint8 m_csd3[20]; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxinfo_h */ diff --git a/src/WiresXPacket.h b/src/WiresXPacket.h new file mode 100644 index 0000000..4176adf --- /dev/null +++ b/src/WiresXPacket.h @@ -0,0 +1,55 @@ +// +// WiresxPacket.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cwiresxpacket_h +#define cwiresxpacket_h + +#include "Buffer.h" +#include "IP.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CWiresxPacket +{ +public: + // constructor + CWiresxPacket() {} + CWiresxPacket(const CBuffer &Buffer, const CIp &Ip) { m_Buffer = Buffer; m_Ip = Ip; } + + // destructor + virtual ~CWiresxPacket() {} + + // get + const CBuffer &GetBuffer(void) const { return m_Buffer; } + const CIp &GetIp(void) const { return m_Ip; } + +protected: + // data + CBuffer m_Buffer; + CIp m_Ip; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxpacket_h */ diff --git a/src/WiresXPacketQueue.h b/src/WiresXPacketQueue.h new file mode 100644 index 0000000..8d610b3 --- /dev/null +++ b/src/WiresXPacketQueue.h @@ -0,0 +1,62 @@ +// +// WiresxPacketQueue.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 09/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cwiresxpacketqueue_h +#define cwiresxpacketqueue_h + +#include "WiresxPacket.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CWiresxPacketQueue +{ +public: + // constructor + CWiresxPacketQueue() {} + + // destructor + ~CWiresxPacketQueue() {} + + // lock + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + + // pass thru + CWiresxPacket front() { return queue.front(); } + void pop() { queue.pop(); } + void push(CWiresxPacket packet) { queue.push(packet); } + bool empty() const { return queue.empty(); } + +protected: + // status + std::mutex m_Mutex; + std::queue queue; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cwiresxpacketqueue_h */ diff --git a/src/YSFClient.cpp b/src/YSFClient.cpp new file mode 100644 index 0000000..f9103ac --- /dev/null +++ b/src/YSFClient.cpp @@ -0,0 +1,52 @@ +// +// cysfclient.cpp +// xlxd +//// Created by Jean-Luc on 20/05/2018. +// Created by Jean-Luc Deltombe (LX3JL) on 20/05/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include "YSFClient.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructors + +CYsfClient::CYsfClient() +{ +} + +CYsfClient::CYsfClient(const CCallsign &callsign, const CIp &ip, char reflectorModule) + : CClient(callsign, ip, reflectorModule) +{ +} + +CYsfClient::CYsfClient(const CYsfClient &client) + : CClient(client) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// status + +bool CYsfClient::IsAlive(void) const +{ + return (m_LastKeepaliveTime.DurationSinceNow() < YSF_KEEPALIVE_TIMEOUT); +} diff --git a/src/YSFClient.h b/src/YSFClient.h new file mode 100644 index 0000000..d867d03 --- /dev/null +++ b/src/YSFClient.h @@ -0,0 +1,59 @@ +// +// YSFClient.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/05/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfclient_h +#define cysfclient_h + +#include "Client.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CYsfClient : public CClient +{ +public: + // constructors + CYsfClient(); + CYsfClient(const CCallsign &, const CIp &, char = ' '); + CYsfClient(const CYsfClient &); + + // destructor + virtual ~CYsfClient() {}; + + // identity + int GetProtocol(void) const { return PROTOCOL_YSF; } + const char *GetProtocolName(void) const { return "YSF"; } + int GetCodec(void) const { return CODEC_AMBE2PLUS; } + bool IsNode(void) const { return true; } + + // status + bool IsAlive(void) const; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfclient_h */ diff --git a/src/YSFConvolution.cpp b/src/YSFConvolution.cpp new file mode 100644 index 0000000..1614faf --- /dev/null +++ b/src/YSFConvolution.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009-2016 by Jonathan Naylor G4KLX + * Copyright (C) 2030 Thomas A. Early N7TAE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "YSFConvolution.h" + +#include +#include +#include + +const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +const uint8_t BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 1U, 1U, 1U, 1U}; +const uint8_t BRANCH_TABLE2[] = {0U, 1U, 1U, 0U, 0U, 1U, 1U, 0U}; + +const unsigned int NUM_OF_STATES_D2 = 8U; +const unsigned int NUM_OF_STATES = 16U; +const uint32_t M = 2U; +const unsigned int K = 5U; + +CYSFConvolution::CYSFConvolution() : m_oldMetrics(nullptr), m_newMetrics(nullptr), m_dp(nullptr) {} + +void CYSFConvolution::start() +{ + memset(m_metrics1, 0, NUM_OF_STATES * sizeof(uint16_t)); + memset(m_metrics2, 0, NUM_OF_STATES * sizeof(uint16_t)); + + m_oldMetrics = m_metrics1; + m_newMetrics = m_metrics2; + m_dp = m_decisions; +} + +void CYSFConvolution::decode(uint8_t s0, uint8_t s1) +{ + *m_dp = 0U; + + for (uint8_t i = 0U; i < NUM_OF_STATES_D2; i++) + { + uint8_t j = i * 2U; + + uint16_t metric = (BRANCH_TABLE1[i] ^ s0) + (BRANCH_TABLE2[i] ^ s1); + + uint16_t m0 = m_oldMetrics[i] + metric; + uint16_t m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + (M - metric); + uint8_t decision0 = (m0 >= m1) ? 1U : 0U; + m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0; + + m0 = m_oldMetrics[i] + (M - metric); + m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + metric; + uint8_t decision1 = (m0 >= m1) ? 1U : 0U; + m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0; + + *m_dp |= (uint64_t(decision1) << (j + 1U)) | (uint64_t(decision0) << (j + 0U)); + } + + ++m_dp; + + assert((m_dp - m_decisions) <= 180); + + uint16_t *tmp = m_oldMetrics; + m_oldMetrics = m_newMetrics; + m_newMetrics = tmp; +} + +void CYSFConvolution::chainback(unsigned char* out, unsigned int nBits) +{ + assert(out != nullptr); + + uint32_t state = 0U; + + while (nBits-- > 0) + { + --m_dp; + + uint32_t i = state >> (9 - K); + uint8_t bit = uint8_t(*m_dp >> i) & 1; + state = (bit << 7) | (state >> 1); + + WRITE_BIT1(out, nBits, bit != 0U); + } +} + +void CYSFConvolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const +{ + assert(in != nullptr); + assert(out != nullptr); + assert(nBits > 0U); + + uint8_t d1 = 0U, d2 = 0U, d3 = 0U, d4 = 0U; + uint32_t k = 0U; + for (unsigned int i = 0U; i < nBits; i++) + { + uint8_t d = READ_BIT1(in, i) ? 1U : 0U; + + uint8_t g1 = (d + d3 + d4) & 1; + uint8_t g2 = (d + d1 + d2 + d4) & 1; + + d4 = d3; + d3 = d2; + d2 = d1; + d1 = d; + + WRITE_BIT1(out, k, g1 != 0U); + k++; + + WRITE_BIT1(out, k, g2 != 0U); + k++; + } +} diff --git a/src/YSFConvolution.h b/src/YSFConvolution.h new file mode 100644 index 0000000..5781b95 --- /dev/null +++ b/src/YSFConvolution.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFConvolution_H) +#define YSFConvolution_H + +#include "YSFConvolution.h" + +#include + +class CYSFConvolution +{ +public: + CYSFConvolution(); + + void start(); + void decode(uint8_t s0, uint8_t s1); + void chainback(unsigned char* out, unsigned int nBits); + + void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const; + +private: + uint16_t m_metrics1[16]; + uint16_t m_metrics2[16]; + uint16_t* m_oldMetrics; + uint16_t* m_newMetrics; + uint64_t m_decisions[180]; + uint64_t* m_dp; +}; + +#endif diff --git a/src/YSFDefines.h b/src/YSFDefines.h new file mode 100644 index 0000000..3734984 --- /dev/null +++ b/src/YSFDefines.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFDefines_H) +#define YSFDefines_H + +const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; + +const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; +const unsigned int YSF_SYNC_LENGTH_BYTES = 5U; + +const unsigned int YSF_FICH_LENGTH_BYTES = 25U; + +const unsigned char YSF_SYNC_OK = 0x01U; + +const unsigned int YSF_CALLSIGN_LENGTH = 10U; + +const unsigned char YSF_FI_HEADER = 0x00U; +const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; +const unsigned char YSF_FI_TERMINATOR = 0x02U; +const unsigned char YSF_FI_TEST = 0x03U; + +const unsigned char YSF_DT_VD_MODE1 = 0x00U; +const unsigned char YSF_DT_DATA_FR_MODE = 0x01U; +const unsigned char YSF_DT_VD_MODE2 = 0x02U; +const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U; + +const unsigned char YSF_CM_GROUP = 0x00U; +const unsigned char YSF_CM_INDIVIDUAL = 0x03U; + +const unsigned char YSF_MR_NOT_BUSY = 0x01U; +const unsigned char YSF_MR_BUSY = 0x02U; + +#endif diff --git a/src/YSFFich.cpp b/src/YSFFich.cpp new file mode 100644 index 0000000..19ac5de --- /dev/null +++ b/src/YSFFich.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2009-2016 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "YSFConvolution.h" +#include "YSFDefines.h" +#include "Golay24128.h" +#include "YSFFich.h" +#include "CRC.h" +//#include "Log.h" + +#include +#include +#include + +const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +const unsigned int INTERLEAVE_TABLE[] = +{ + 0U, 40U, 80U, 120U, 160U, + 2U, 42U, 82U, 122U, 162U, + 4U, 44U, 84U, 124U, 164U, + 6U, 46U, 86U, 126U, 166U, + 8U, 48U, 88U, 128U, 168U, + 10U, 50U, 90U, 130U, 170U, + 12U, 52U, 92U, 132U, 172U, + 14U, 54U, 94U, 134U, 174U, + 16U, 56U, 96U, 136U, 176U, + 18U, 58U, 98U, 138U, 178U, + 20U, 60U, 100U, 140U, 180U, + 22U, 62U, 102U, 142U, 182U, + 24U, 64U, 104U, 144U, 184U, + 26U, 66U, 106U, 146U, 186U, + 28U, 68U, 108U, 148U, 188U, + 30U, 70U, 110U, 150U, 190U, + 32U, 72U, 112U, 152U, 192U, + 34U, 74U, 114U, 154U, 194U, + 36U, 76U, 116U, 156U, 196U, + 38U, 78U, 118U, 158U, 198U +}; + +CYSFFICH::CYSFFICH() +{ + ::memset(m_fich, 0U, 6U); +} + +bool CYSFFICH::decode(const unsigned char* bytes) +{ + assert(bytes != nullptr); + + CYSFConvolution viterbi; + viterbi.start(); + + // Deinterleave the FICH and send bits to the Viterbi decoder + for (unsigned int i = 0U; i < 100U; i++) + { + unsigned int n = INTERLEAVE_TABLE[i]; + uint8_t s0 = READ_BIT1(bytes, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(bytes, n) ? 1U : 0U; + + viterbi.decode(s0, s1); + } + + unsigned char output[13U]; + viterbi.chainback(output, 96U); + + unsigned int b0 = CGolay24128::decode24128(output + 0U); + unsigned int b1 = CGolay24128::decode24128(output + 3U); + unsigned int b2 = CGolay24128::decode24128(output + 6U); + unsigned int b3 = CGolay24128::decode24128(output + 9U); + + m_fich[0U] = (b0 >> 4) & 0xFFU; + m_fich[1U] = ((b0 << 4) & 0xF0U) | ((b1 >> 8) & 0x0FU); + m_fich[2U] = (b1 >> 0) & 0xFFU; + m_fich[3U] = (b2 >> 4) & 0xFFU; + m_fich[4U] = ((b2 << 4) & 0xF0U) | ((b3 >> 8) & 0x0FU); + m_fich[5U] = (b3 >> 0) & 0xFFU; + + return CCRC::checkCCITT162(m_fich, 6U); +} + +void CYSFFICH::encode(unsigned char* bytes) +{ + assert(bytes != nullptr); + + CCRC::addCCITT162(m_fich, 6U); + + unsigned int b0 = ((m_fich[0U] << 4) & 0xFF0U) | ((m_fich[1U] >> 4) & 0x00FU); + unsigned int b1 = ((m_fich[1U] << 8) & 0xF00U) | ((m_fich[2U] >> 0) & 0x0FFU); + unsigned int b2 = ((m_fich[3U] << 4) & 0xFF0U) | ((m_fich[4U] >> 4) & 0x00FU); + unsigned int b3 = ((m_fich[4U] << 8) & 0xF00U) | ((m_fich[5U] >> 0) & 0x0FFU); + + unsigned int c0 = CGolay24128::encode24128(b0); + unsigned int c1 = CGolay24128::encode24128(b1); + unsigned int c2 = CGolay24128::encode24128(b2); + unsigned int c3 = CGolay24128::encode24128(b3); + + unsigned char conv[13U]; + conv[0U] = (c0 >> 16) & 0xFFU; + conv[1U] = (c0 >> 8) & 0xFFU; + conv[2U] = (c0 >> 0) & 0xFFU; + conv[3U] = (c1 >> 16) & 0xFFU; + conv[4U] = (c1 >> 8) & 0xFFU; + conv[5U] = (c1 >> 0) & 0xFFU; + conv[6U] = (c2 >> 16) & 0xFFU; + conv[7U] = (c2 >> 8) & 0xFFU; + conv[8U] = (c2 >> 0) & 0xFFU; + conv[9U] = (c3 >> 16) & 0xFFU; + conv[10U] = (c3 >> 8) & 0xFFU; + conv[11U] = (c3 >> 0) & 0xFFU; + conv[12U] = 0x00U; + + CYSFConvolution convolution; + unsigned char convolved[25U]; + convolution.encode(conv, convolved, 100U); + + unsigned int j = 0U; + for (unsigned int i = 0U; i < 100U; i++) + { + unsigned int n = INTERLEAVE_TABLE[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } +} + +unsigned char CYSFFICH::getFI() const +{ + return (m_fich[0U] >> 6) & 0x03U; +} + +unsigned char CYSFFICH::getCS() const +{ + return (m_fich[0U] >> 4) & 0x03U; +} + +unsigned char CYSFFICH::getCM() const +{ + return (m_fich[0U] >> 2) & 0x03U; +} + +unsigned char CYSFFICH::getBN() const +{ + return m_fich[0U] & 0x03U; +} + +unsigned char CYSFFICH::getBT() const +{ + return (m_fich[1U] >> 6) & 0x03U; +} + +unsigned char CYSFFICH::getFN() const +{ + return (m_fich[1U] >> 3) & 0x07U; +} + +unsigned char CYSFFICH::getFT() const +{ + return m_fich[1U] & 0x07U; +} + +unsigned char CYSFFICH::getDT() const +{ + return m_fich[2U] & 0x03U; +} + +unsigned char CYSFFICH::getMR() const +{ + return (m_fich[2U] >> 3) & 0x03U; +} + +bool CYSFFICH::getDev() const +{ + return (m_fich[2U] & 0x40U) == 0x40U; +} + +bool CYSFFICH::getSQL() const +{ + return (m_fich[3U] & 0x80U) == 0x80U; +} + +unsigned char CYSFFICH::getSQ() const +{ + return m_fich[3U] & 0x7FU; +} + +void CYSFFICH::setFI(unsigned char fi) +{ + m_fich[0U] &= 0x3FU; + m_fich[0U] |= (fi << 6) & 0xC0U; +} + +void CYSFFICH::setCS(unsigned char cs) +{ + m_fich[0U] &= 0xCFU; + m_fich[0U] |= (cs << 4) & 0x30U; +} + +void CYSFFICH::setCM(unsigned char cm) +{ + m_fich[0U] &= 0xF3U; + m_fich[0U] |= (cm << 2) & 0x0CU; +} + +void CYSFFICH::setFN(unsigned char fn) +{ + m_fich[1U] &= 0xC7U; + m_fich[1U] |= (fn << 3) & 0x38U; +} + +void CYSFFICH::setFT(unsigned char ft) +{ + m_fich[1U] &= 0xF8U; + m_fich[1U] |= ft & 0x07U; +} + +void CYSFFICH::setMR(unsigned char mr) +{ + m_fich[2U] &= 0xC7U; + m_fich[2U] |= (mr << 3) & 0x38U; +} + +void CYSFFICH::setVoIP(bool on) +{ + if (on) + m_fich[2U] |= 0x04U; + else + m_fich[2U] &= 0xFBU; +} + +void CYSFFICH::setDev(bool on) +{ + if (on) + m_fich[2U] |= 0x40U; + else + m_fich[2U] &= 0xBFU; +} + +void CYSFFICH::setDT(unsigned char dt) +{ + m_fich[2U] &= 0xFCU; + m_fich[2U] |= dt & 0x03U; +} + +void CYSFFICH::setSQL(bool on) +{ + if (on) + m_fich[3U] |= 0x80U; + else + m_fich[3U] &= 0x7FU; +} + +void CYSFFICH::setSQ(unsigned char sq) +{ + m_fich[3U] &= 0x80U; + m_fich[3U] |= sq & 0x7FU; +} + +void CYSFFICH::setBN(unsigned char bn) +{ + m_fich[0U] &= 0xFCU; + m_fich[0U] |= bn & 0x03U; +} + +void CYSFFICH::setBT(unsigned char bt) +{ + m_fich[1U] &= 0x3FU; + m_fich[1U] |= (bt << 6) & 0xC0U; +} + +void CYSFFICH::load(const unsigned char* fich) +{ + assert(fich != nullptr); + + ::memcpy(m_fich, fich, 4U); +} diff --git a/src/YSFFich.h b/src/YSFFich.h new file mode 100644 index 0000000..52d2d01 --- /dev/null +++ b/src/YSFFich.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFFICH_H) +#define YSFFICH_H + +class CYSFFICH +{ +public: + CYSFFICH(); + + bool decode(const unsigned char* bytes); + + void encode(unsigned char* bytes); + + unsigned char getFI() const; + unsigned char getCS() const; + unsigned char getCM() const; + unsigned char getBN() const; + unsigned char getBT() const; + unsigned char getFN() const; + unsigned char getFT() const; + unsigned char getDT() const; + unsigned char getMR() const; + bool getDev() const; + bool getSQL() const; + unsigned char getSQ() const; + + void setFI(unsigned char fi); + void setCS(unsigned char cs); + void setCM(unsigned char cm); + void setFN(unsigned char fn); + void setFT(unsigned char ft); + void setBN(unsigned char bn); + void setBT(unsigned char bt); + void setDT(unsigned char dt); + void setMR(unsigned char mr); + void setVoIP(bool set); + void setDev(bool set); + void setSQL(bool set); + void setSQ(unsigned char sq); + + void load(const unsigned char* fich); + +private: + unsigned char m_fich[6]; +}; + +#endif diff --git a/src/YSFNode.cpp b/src/YSFNode.cpp new file mode 100644 index 0000000..1a21109 --- /dev/null +++ b/src/YSFNode.cpp @@ -0,0 +1,51 @@ +// +// cysfnode.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "cysfnode.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +///// constructor + +CYsfNode::CYsfNode() +{ + m_uiTxFreq = 0; + m_uiRxFreq = 0; +} + +CYsfNode::CYsfNode(uint32 txfreq, uint32 rxfreq) +{ + m_uiTxFreq = txfreq; + m_uiRxFreq = rxfreq; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// get + +bool CYsfNode::IsValid(void) const +{ + return true; +} diff --git a/src/YSFNode.h b/src/YSFNode.h new file mode 100644 index 0000000..4fbb750 --- /dev/null +++ b/src/YSFNode.h @@ -0,0 +1,56 @@ +// +// cysfnode.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfnode_h +#define cysfnode_h + +#include "Main.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CYsfNode +{ +public: + // constructor + CYsfNode(); + CYsfNode(uint32, uint32); + + // get + uint32 GetTxFrequency(void) const { return m_uiTxFreq; } + uint32 GetRxFrequency(void) const { return m_uiRxFreq; } + bool IsValid(void) const; + +protected: + // data + uint32 m_uiTxFreq; + uint32 m_uiRxFreq; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfnode_h */ diff --git a/src/YSFNodeDir.cpp b/src/YSFNodeDir.cpp new file mode 100644 index 0000000..c91e5cd --- /dev/null +++ b/src/YSFNodeDir.cpp @@ -0,0 +1,182 @@ +// +// cysfnodedir.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include "Main.h" +#include "Reflector.h" +#include "YSFNodeDir.h" + + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor & destructor + +CYsfNodeDir::CYsfNodeDir() +{ + keep_running = true; +} + +CYsfNodeDir::~CYsfNodeDir() +{ + // kill threads + Close(); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// init & close + +bool CYsfNodeDir::Init(void) +{ + // load content + Reload(); + + // reset run flag + keep_running = true; + + // start thread; + m_Future = std::async(std::launch::async, &CYsfNodeDir::Thread, this); + + return true; +} + +void CYsfNodeDir::Close(void) +{ + keep_running = false; + if ( m_Future.valid() ) + { + m_Future.get(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// thread + +void CYsfNodeDir::Thread() +{ + while (keep_running) + { + // Wait YSFNODEDB_REFRESH_RATE minutes + for (int i=0; keep_running && (i < 30*YSFNODEDB_REFRESH_RATE); i++) + { + CTimePoint::TaskSleepFor(2000); +#if YSF_DB_SUPPORT==true + if (keep_running && (0 == i % 450)) + { + ReadDb(); // update from the db every 15 minutes + } +#endif + } + // have lists files changed ? + if (keep_running && NeedReload()) + { + Reload(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Reload + +bool CYsfNodeDir::Reload(void) +{ + CBuffer buffer; + bool ok = false; + + if ( LoadContent(&buffer) ) + { + Lock(); + ok = RefreshContent(buffer); + Unlock(); + } + return ok; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// find + +bool CYsfNodeDir::FindFrequencies(const CCallsign &callsign, uint32 *txfreq, uint32 *rxfreq) +{ + auto found = find(callsign); + if ( found != end() ) + { + *txfreq = found->second.GetTxFrequency(); + *rxfreq = found->second.GetRxFrequency(); + return true; + } + else + { + *txfreq = YSF_DEFAULT_NODE_TX_FREQ; + *rxfreq = YSF_DEFAULT_NODE_RX_FREQ; + return false; + } +} + +#if YSF_DB_SUPPORT==true +void CYsfNodeDir::ReadDb() +{ + MYSQL *con = mysql_init(NULL); + if (con) + { + if (mysql_real_connect(con, "localhost", YSF_DB_USER, YSF_DB_PASSWORD, YSF_DB_NAME, 0, NULL, 0)) + { + if (0 == mysql_query(con, "SELECT callsign,txfreq,rxfreq FROM ysfnodes")) + { + MYSQL_RES *result = mysql_store_result(con); + if (result) + { + std::cout << "Adding " << mysql_num_rows(result) << " registered YSF stations from database " << YSF_DB_NAME << std::endl; + MYSQL_ROW row; + + while ((row = mysql_fetch_row(result))) + { + CCallsign cs(row[0]); + CYsfNode node(atoi(row[1]), atoi(row[2])); + m_map[cs] = node; + } + + mysql_free_result(result); + } + else + { + std::cerr << "Could not fetch MySQL rows" << std::endl; + } + } + else + { + std::cerr << "MySQL query failed: " << mysql_error(con) << std::endl; + } + } + else + { + std::cerr << "Could not connect to database " << YSF_DB_NAME << ": " << mysql_error(con) << std::endl; + } + mysql_close(con); + } + else + { + std::cerr << "Could not init mysql." << std::endl;; + } +} +#endif diff --git a/src/YSFNodeDir.h b/src/YSFNodeDir.h new file mode 100644 index 0000000..ebc9fc3 --- /dev/null +++ b/src/YSFNodeDir.h @@ -0,0 +1,105 @@ +// +// YSFNodeDir.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfnodedir_h +#define cysfnodedir_h + +#include +#include +#include +#include +#include "Buffer.h" +#include "Callsign.h" +#include "cysfnode.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// compare function for std::map::find + +struct CYsfNodeDirCallsignCompare +{ + bool operator() (const CCallsign &cs1, const CCallsign &cs2) const + { return cs1.HasLowerCallsign(cs2);} +}; + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +using CsNodeMap = std::map; + +class CYsfNodeDir +{ +public: + // constructor + CYsfNodeDir(); + // destructor + virtual ~CYsfNodeDir(); + + // init & close + virtual bool Init(void); + virtual void Close(void); + + // locks + void Lock(void) { m_Mutex.lock(); } + void Unlock(void) { m_Mutex.unlock(); } + + // refresh + virtual bool LoadContent(CBuffer *) { return false; } + virtual bool RefreshContent(const CBuffer &) { return false; } + + // find + bool FindFrequencies(const CCallsign &, uint32 *, uint32 *); + + // pass-thru + void clear() { m_map.clear(); } + size_t size() { return m_map.size(); } + CsNodeMap::iterator find(const CCallsign &cs) { return m_map.find(cs); } + CsNodeMap::iterator end() { return m_map.end(); } + std::pair insert(const std::pair &pair) { return m_map.insert(pair); } + +protected: + // thread + void Thread(); + + // reload helpers + bool Reload(void); + virtual bool NeedReload(void) { return false; } +#if YSF_DB_SUPPORT==true + void ReadDb(void); +#endif + //bool IsValidDmrid(const char *); + + +protected: + // Lock() + std::mutex m_Mutex; + + // thread + std::atomic keep_running; + std::future m_Future; + CsNodeMap m_map; +}; + + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfnodedir_h */ diff --git a/src/YSFNodeDirFile.cpp b/src/YSFNodeDirFile.cpp new file mode 100644 index 0000000..4253b9f --- /dev/null +++ b/src/YSFNodeDirFile.cpp @@ -0,0 +1,167 @@ +// +// cysfnodedirfile.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include +#include +#include "Main.h" +#include "YSFNodeDirFile.h" + + +#if (YSFNODEDB_USE_RLX_SERVER == 0) +CYsfNodeDirFile g_YsfNodeDir; +#endif + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor & destructor + +CYsfNodeDirFile::CYsfNodeDirFile() +{ + ::memset(&m_LastModTime, 0, sizeof(time_t)); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// init & close + +bool CYsfNodeDirFile::Init(void) +{ + return CYsfNodeDir::Init(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// refresh + +bool CYsfNodeDirFile::NeedReload(void) +{ + bool needReload = false; + + time_t time; + if ( GetLastModTime(&time) ) + { + needReload = time != m_LastModTime; + } + return needReload; +} + +bool CYsfNodeDirFile::LoadContent(CBuffer *buffer) +{ + bool ok = false; + std::ifstream file; + std::streampos size; + + // open file + file.open(YSFNODEDB_PATH, std::ios::in | std::ios::binary | std::ios::ate); + if ( file.is_open() ) + { + // read file + size = file.tellg(); + if ( size > 0 ) + { + // read file into buffer + buffer->resize((int)size+1); + file.seekg (0, std::ios::beg); + file.read((char *)buffer->data(), (int)size); + + // close file + file.close(); + + // update time + GetLastModTime(&m_LastModTime); + + // done + ok = true; + } + } + + // done + return ok; +} + +bool CYsfNodeDirFile::RefreshContent(const CBuffer &buffer) +{ + bool ok = false; + + // clear directory + clear(); + + // scan buffer + if ( buffer.size() > 0 ) + { + // crack it + char *ptr1 = (char *)buffer.data(); + char *ptr2; + + // get next line + while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr ) + { + *ptr2 = 0; + // get items + char *callsign; + char *txfreq; + char *rxfreq; + if ( ((callsign = ::strtok(ptr1, ";")) != nullptr) ) + { + if ( ((txfreq = ::strtok(nullptr, ";")) != nullptr) ) + { + if ( ((rxfreq = ::strtok(nullptr, ";")) != nullptr) ) + { + // new entry + CCallsign cs(callsign); + CYsfNode node(atoi(txfreq), atoi(rxfreq)); + if ( cs.IsValid() && node.IsValid() ) + { + insert(std::pair(cs, node)); + } + } + } + } + // next line + ptr1 = ptr2+1; + } + + // done + ok = true; + } + + + // report + std::cout << "Read " << size() << " YSF nodes from file " << YSFNODEDB_PATH << std::endl; + + // done + return ok; +} + + +bool CYsfNodeDirFile::GetLastModTime(time_t *time) +{ + bool ok = false; + + struct stat fileStat; + if( ::stat(YSFNODEDB_PATH, &fileStat) != -1 ) + { + *time = fileStat.st_mtime; + ok = true; + } + return ok; +} diff --git a/src/YSFNodeDirFile.h b/src/YSFNodeDirFile.h new file mode 100644 index 0000000..dd0e9f6 --- /dev/null +++ b/src/YSFNodeDirFile.h @@ -0,0 +1,59 @@ +// +// YSFNodeDirFile.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfnodedirfile_h +#define cysfnodedirfile_h + +#include "YSFNodeDir.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CYsfNodeDirFile : public CYsfNodeDir +{ +public: + // constructor + CYsfNodeDirFile(); + + // destructor + ~CYsfNodeDirFile() {} + + // init & close + bool Init(void); + + // refresh + bool LoadContent(CBuffer *); + bool RefreshContent(const CBuffer &); + +protected: + // reload helpers + bool NeedReload(void); + bool GetLastModTime(time_t *); + +protected: + // data + time_t m_LastModTime; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfnodedirfile_h */ diff --git a/src/YSFNodeDirHttp.cpp b/src/YSFNodeDirHttp.cpp new file mode 100644 index 0000000..e2abe71 --- /dev/null +++ b/src/YSFNodeDirHttp.cpp @@ -0,0 +1,185 @@ +// +// cysfnodedirhttp.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include +#include "Main.h" +#include "Reflector.h" +#include "YSFNodeDirhttp.h" + +#if (YSFNODEDB_USE_RLX_SERVER == 1) +CYsfNodeDirHttp g_YsfNodeDir; +#endif + + +//////////////////////////////////////////////////////////////////////////////////////// +// refresh + +bool CYsfNodeDirHttp::LoadContent(CBuffer *buffer) +{ + // get file from xlxapi server + return HttpGet("xlxapi.rlx.lu", "api/exportysfrepeaters.php", 80, buffer); +} + +bool CYsfNodeDirHttp::RefreshContent(const CBuffer &buffer) +{ + bool ok = false; + + // clear directory + clear(); + + // scan buffer + if ( buffer.size() > 0 ) + { + // crack it + char *ptr1 = (char *)buffer.data(); + char *ptr2; + + // get next line + while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr ) + { + *ptr2 = 0; + // get items + char *callsign; + char *txfreq; + char *rxfreq; + if ( ((callsign = ::strtok(ptr1, ";")) != nullptr) ) + { + if ( ((txfreq = ::strtok(nullptr, ";")) != nullptr) ) + { + if ( ((rxfreq = ::strtok(nullptr, ";")) != nullptr) ) + { + // new entry + CCallsign cs(callsign); + CYsfNode node(atoi(txfreq), atoi(rxfreq)); + if ( cs.IsValid() && node.IsValid() ) + { + insert(std::pair(cs, node)); + } + } + } + } + // next line + ptr1 = ptr2+1; + } + + // done + ok = true; + } + + // report + std::cout << "Read " << size() << " YSF nodes from xlxapi.rlx.lu database " << std::endl; + + // done + return ok; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// httpd helpers + +#define YSFNODE_HTTPGET_SIZEMAX (256) + +bool CYsfNodeDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer) +{ + bool ok = false; + int sock_id; + + // open socket + if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) + { + // get hostname address + struct sockaddr_in servaddr; + struct hostent *hp; + ::memset(&servaddr,0,sizeof(servaddr)); + if( (hp = gethostbyname(hostname)) != nullptr ) + { + // dns resolved + ::memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length); + servaddr.sin_port = htons(port); + servaddr.sin_family = AF_INET; + + // connect + if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) + { + // send the GET request + char request[YSFNODE_HTTPGET_SIZEMAX]; + ::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: xlxd\r\n\r\n", + filename, (const char *)g_Reflector.GetCallsign()); + ::write(sock_id, request, strlen(request)); + + // config receive timeouts + fd_set read_set; + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + FD_ZERO(&read_set); + FD_SET(sock_id, &read_set); + + // get the reply back + buffer->clear(); + bool done = false; + do + { + char buf[1440]; + ssize_t len = 0; + select(sock_id+1, &read_set, nullptr, nullptr, &timeout); + //if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) ) + //if ( ret >= 0 ) + //{ + usleep(5000); + len = read(sock_id, buf, 1440); + if ( len > 0 ) + { + buffer->Append((uint8 *)buf, (int)len); + ok = true; + } + //} + done = (len <= 0); + + } + while (!done); + buffer->Append((uint8)0); + + // and disconnect + close(sock_id); + } + else + { + std::cout << "Cannot establish connection with host " << hostname << std::endl; + } + } + else + { + std::cout << "Host " << hostname << " not found" << std::endl; + } + + } + else + { + std::cout << "Failed to open wget socket" << std::endl; + } + + // done + return ok; +} diff --git a/src/YSFNodeDirHttp.h b/src/YSFNodeDirHttp.h new file mode 100644 index 0000000..405a3e0 --- /dev/null +++ b/src/YSFNodeDirHttp.h @@ -0,0 +1,53 @@ +// +// YSFNodeDirhttp.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 08/10/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfnodedirhttp_h +#define cysfnodedirhttp_h + + +#include "YSFNodeDir.h" + +//////////////////////////////////////////////////////////////////////////////////////// + +class CYsfNodeDirHttp : public CYsfNodeDir +{ +public: + // constructor + CYsfNodeDirHttp() {} + + // destructor + ~CYsfNodeDirHttp() {} + + // refresh + bool LoadContent(CBuffer *); + bool RefreshContent(const CBuffer &); + +protected: + // reload helpers + bool NeedReload(void) { return true; } + bool HttpGet(const char *, const char *, int, CBuffer *); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfnodedirhttp_h */ diff --git a/src/YSFPayload.cpp b/src/YSFPayload.cpp new file mode 100644 index 0000000..18898f9 --- /dev/null +++ b/src/YSFPayload.cpp @@ -0,0 +1,653 @@ +/* +* Copyright (C) 2016,2017 Jonathan Naylor, G4KLX +* Copyright (C) 2016 Mathias Weyland, HB9FRV +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include "YSFConvolution.h" +#include "YSFPayload.h" +#include "YSFDefines.h" +#include "Utils.h" +#include "CRC.h" + + +#include +#include +#include +#include + +const unsigned int INTERLEAVE_TABLE_9_20[] = +{ + 0U, 40U, 80U, 120U, 160U, 200U, 240U, 280U, 320U, + 2U, 42U, 82U, 122U, 162U, 202U, 242U, 282U, 322U, + 4U, 44U, 84U, 124U, 164U, 204U, 244U, 284U, 324U, + 6U, 46U, 86U, 126U, 166U, 206U, 246U, 286U, 326U, + 8U, 48U, 88U, 128U, 168U, 208U, 248U, 288U, 328U, + 10U, 50U, 90U, 130U, 170U, 210U, 250U, 290U, 330U, + 12U, 52U, 92U, 132U, 172U, 212U, 252U, 292U, 332U, + 14U, 54U, 94U, 134U, 174U, 214U, 254U, 294U, 334U, + 16U, 56U, 96U, 136U, 176U, 216U, 256U, 296U, 336U, + 18U, 58U, 98U, 138U, 178U, 218U, 258U, 298U, 338U, + 20U, 60U, 100U, 140U, 180U, 220U, 260U, 300U, 340U, + 22U, 62U, 102U, 142U, 182U, 222U, 262U, 302U, 342U, + 24U, 64U, 104U, 144U, 184U, 224U, 264U, 304U, 344U, + 26U, 66U, 106U, 146U, 186U, 226U, 266U, 306U, 346U, + 28U, 68U, 108U, 148U, 188U, 228U, 268U, 308U, 348U, + 30U, 70U, 110U, 150U, 190U, 230U, 270U, 310U, 350U, + 32U, 72U, 112U, 152U, 192U, 232U, 272U, 312U, 352U, + 34U, 74U, 114U, 154U, 194U, 234U, 274U, 314U, 354U, + 36U, 76U, 116U, 156U, 196U, 236U, 276U, 316U, 356U, + 38U, 78U, 118U, 158U, 198U, 238U, 278U, 318U, 358U +}; + +const unsigned int INTERLEAVE_TABLE_5_20[] = +{ + 0U, 40U, 80U, 120U, 160U, + 2U, 42U, 82U, 122U, 162U, + 4U, 44U, 84U, 124U, 164U, + 6U, 46U, 86U, 126U, 166U, + 8U, 48U, 88U, 128U, 168U, + 10U, 50U, 90U, 130U, 170U, + 12U, 52U, 92U, 132U, 172U, + 14U, 54U, 94U, 134U, 174U, + 16U, 56U, 96U, 136U, 176U, + 18U, 58U, 98U, 138U, 178U, + 20U, 60U, 100U, 140U, 180U, + 22U, 62U, 102U, 142U, 182U, + 24U, 64U, 104U, 144U, 184U, + 26U, 66U, 106U, 146U, 186U, + 28U, 68U, 108U, 148U, 188U, + 30U, 70U, 110U, 150U, 190U, + 32U, 72U, 112U, 152U, 192U, + 34U, 74U, 114U, 154U, 194U, + 36U, 76U, 116U, 156U, 196U, + 38U, 78U, 118U, 158U, 198U +}; + +// This one differs from the others in that it interleaves bits and not dibits +const unsigned char WHITENING_DATA[] = { + 0x93U, 0xD7U, 0x51U, 0x21U, 0x9CU, 0x2FU, 0x6CU, 0xD0U, 0xEFU, 0x0FU, 0xF8U, 0x3DU, 0xF1U, 0x73U, 0x20U, 0x94U, 0xEDU, 0x1EU, 0x7CU, 0xD8U +}; + +const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +bool CYSFPayload::processHeaderData(unsigned char* data) +{ + assert(data != nullptr); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 9U); + p1 += 18U; + p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool valid1 = CCRC::checkCCITT162(output, 22U); + if (valid1) + { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + if (m_dest == nullptr) + { + m_dest = std::unique_ptr(new unsigned char[YSF_CALLSIGN_LENGTH]); + ::memcpy(m_dest.get(), output, YSF_CALLSIGN_LENGTH); + } + + if (m_source == nullptr) + { + m_source = std::unique_ptr(new unsigned char[YSF_CALLSIGN_LENGTH]); + ::memcpy(m_source.get(), output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); + } + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p1, p2, 9U); + p1 += 18U; + p2 += 9U; + } + } + + p1 = data + 9U; + p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 9U); + p1 += 18U; + p2 += 9U; + } + + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + conv.chainback(output, 176U); + + bool valid2 = CCRC::checkCCITT162(output, 22U); + if (valid2) + { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + if (m_downlink) + ::memcpy(output + 0U, m_downlink.get(), YSF_CALLSIGN_LENGTH); + + if (m_uplink) + ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink.get(), YSF_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data + 9U; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p1, p2, 9U); + p1 += 18U; + p2 += 9U; + } + } + + return valid1; +} + +bool CYSFPayload::readDataFRModeData1(const unsigned char* data, unsigned char* dt) +{ + assert(data != nullptr); + assert(dt != nullptr); + + ::memset(dt, ' ', 20U); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + const unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 9U); + p1 += 18U; + p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) + { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + // CUtils::dump(1U, "FR Mode Data 1", output, 20U); + + ::memcpy(dt, output, 20U); + } + + return ret; +} + +bool CYSFPayload::readDataFRModeData2(const unsigned char* data, unsigned char* dt) +{ + assert(data != nullptr); + assert(dt != nullptr); + + ::memset(dt, ' ', 20U); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + const unsigned char* p1 = data + 9U; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 9U); + p1 += 18U; + p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) + { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + // CUtils::dump(1U, "FR Mode Data 2", output, 20U); + + ::memcpy(dt, output, 20U); + } + + return ret; +} + +void CYSFPayload::writeVDMode2Data(unsigned char* data, const unsigned char* dt) +{ + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dt_tmp[13]; + ::memcpy(dt_tmp, dt, YSF_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < 10U; i++) + dt_tmp[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(dt_tmp, 12U); + dt_tmp[12U] = 0x00U; + + unsigned char convolved[25U]; + CYSFConvolution conv; + conv.start(); + conv.encode(dt_tmp, convolved, 100U); + + unsigned char bytes[25U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 100U; i++) + { + unsigned int n = INTERLEAVE_TABLE_5_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + unsigned char* p1 = data; + unsigned char* p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p1, p2, 5U); + p1 += 18U; + p2 += 5U; + } +} + + +bool CYSFPayload::readVDMode1Data(const unsigned char* data, unsigned char* dt) +{ + assert(data != nullptr); + assert(dt != nullptr); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + const unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 9U); + p1 += 18U; + p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) + { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + // CUtils::dump(1U, "V/D Mode 1 Data", output, 20U); + + ::memcpy(dt, output, 20U); + } + + return ret; +} + + +bool CYSFPayload::readVDMode2Data(const unsigned char* data, unsigned char* dt) +{ + assert(data != nullptr); + assert(dt != nullptr); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[25U]; + + const unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p2, p1, 5U); + p1 += 18U; + p2 += 5U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 100U; i++) + { + unsigned int n = INTERLEAVE_TABLE_5_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[13U]; + conv.chainback(output, 96U); + + bool ret = CCRC::checkCCITT162(output, 12U); + if (ret) + { + for (unsigned int i = 0U; i < 10U; i++) + output[i] ^= WHITENING_DATA[i]; + + // CUtils::dump(1U, "V/D Mode 2 Data", output, YSF_CALLSIGN_LENGTH); + + ::memcpy(dt, output, YSF_CALLSIGN_LENGTH); + } + + return ret; +} + +void CYSFPayload::writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2) +{ + assert(data != nullptr); + assert(csd1 != nullptr); + assert(csd2 != nullptr); + + writeDataFRModeData1(csd1, data); + + writeDataFRModeData2(csd2, data); +} + +void CYSFPayload::writeDataFRModeData1(const unsigned char* dt, unsigned char* data) +{ + assert(dt != nullptr); + assert(data != nullptr); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char output[25U]; + for (unsigned int i = 0U; i < 20U; i++) + output[i] = dt[i] ^ WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + + CYSFConvolution conv; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + unsigned char* p1 = data; + unsigned char* p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p1, p2, 9U); + p1 += 18U; + p2 += 9U; + } +} + +void CYSFPayload::writeDataFRModeData2(const unsigned char* dt, unsigned char* data) +{ + assert(dt != nullptr); + assert(data != nullptr); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char output[25U]; + for (unsigned int i = 0U; i < 20U; i++) + output[i] = dt[i] ^ WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + + CYSFConvolution conv; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) + { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + unsigned char* p1 = data + 9U; + unsigned char* p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) + { + ::memcpy(p1, p2, 9U); + p1 += 18U; + p2 += 9U; + } +} + +void CYSFPayload::setUplink(const std::string& callsign) +{ + m_uplink = std::unique_ptr(new unsigned char[YSF_CALLSIGN_LENGTH]); + + std::string uplink = callsign; + uplink.resize(YSF_CALLSIGN_LENGTH, ' '); + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + m_uplink[i] = uplink.at(i); +} + +void CYSFPayload::setDownlink(const std::string& callsign) +{ + m_downlink = std::unique_ptr(new unsigned char[YSF_CALLSIGN_LENGTH]); + + std::string downlink = callsign; + downlink.resize(YSF_CALLSIGN_LENGTH, ' '); + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + m_downlink[i] = downlink.at(i); +} + +std::string CYSFPayload::getSource() +{ + std::string tmp; + + if (m_dest) + tmp.assign((const char *)m_source.get(), YSF_CALLSIGN_LENGTH); + else + tmp = ""; + + return tmp; +} + +std::string CYSFPayload::getDest() +{ + std::string tmp; + + if (m_dest) + tmp.assign((const char *)m_dest.get(), YSF_CALLSIGN_LENGTH); + else + tmp = ""; + + return tmp; +} + +void CYSFPayload::reset() +{ + m_source.reset(); + m_dest.reset(); +} diff --git a/src/YSFPayload.h b/src/YSFPayload.h new file mode 100644 index 0000000..831343b --- /dev/null +++ b/src/YSFPayload.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2020 Thomas A. Early + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(YSFPayload_H) +#define YSFPayload_H + +#include +#include + +class CYSFPayload +{ +public: + CYSFPayload() : m_uplink(nullptr), m_downlink(nullptr), m_source(nullptr), m_dest(nullptr) {} + bool processHeaderData(unsigned char* bytes); + + void writeVDMode2Data(unsigned char* data, const unsigned char* dt); + bool readVDMode1Data(const unsigned char* data, unsigned char* dt); + bool readVDMode2Data(const unsigned char* data, unsigned char* dt); + + void writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2); + + void writeDataFRModeData1(const unsigned char* dt, unsigned char* data); + void writeDataFRModeData2(const unsigned char* dt, unsigned char* data); + bool readDataFRModeData1(const unsigned char* data, unsigned char* dt); + bool readDataFRModeData2(const unsigned char* data, unsigned char* dt); + + std::string getSource(); + std::string getDest(); + + void setUplink(const std::string& callsign); + void setDownlink(const std::string& callsign); + + void reset(); + +private: + std::unique_ptr m_uplink; + std::unique_ptr m_downlink; + std::unique_ptr m_source; + std::unique_ptr m_dest; +}; + +#endif diff --git a/src/YSFProtocol.cpp b/src/YSFProtocol.cpp new file mode 100644 index 0000000..6723629 --- /dev/null +++ b/src/YSFProtocol.cpp @@ -0,0 +1,1045 @@ +// +// cysfprotocol.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/05/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright © 2020 Thomas A. Early, N7TAE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "CRC.h" +#include "YSFPayload.h" +#include "YSFClient.h" +#include "YSFNodeDirFile.h" +#include "YSFNodeDirhttp.h" +#include "YSFUtils.h" +#include "YSFProtocol.h" +#include "Reflector.h" +#include "GateKeeper.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constructor +CYsfProtocol::CYsfProtocol() +{ + m_seqNo = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// operation + +bool CYsfProtocol::Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6) +{ + // base class + if (! CProtocol::Initialize(type, ptype, port, has_ipv4, has_ipv6)) + return false; + + // init the wiresx cmd handler + if (! m_WiresxCmdHandler.Init()) + return false; + + // update time + m_LastKeepaliveTime.Now(); + + return true; +} + +void CYsfProtocol::Close(void) +{ + // base class + CProtocol::Close(); + + // and close wiresx handler + m_WiresxCmdHandler.Close(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// task + +void CYsfProtocol::Task(void) +{ + int iWiresxCmd; + int iWiresxArg; + CBuffer Buffer; + CIp Ip; + CCallsign Callsign; + CYSFFICH Fich; + CWiresxCmd WiresxCmd; + + std::unique_ptr Header; + std::array, 5> Frames; + std::unique_ptr OneFrame; + std::unique_ptr LastFrame; + + // handle outgoing packets + { + // any packet to go ? + CWiresxPacketQueue *queue = m_WiresxCmdHandler.GetPacketQueue(); + while ( !queue->empty() ) + { + CWiresxPacket packet = queue->front(); + queue->pop(); + Send(packet.GetBuffer(), packet.GetIp()); + } + m_WiresxCmdHandler.ReleasePacketQueue(); + } + + // handle incoming packets +#if YSF_IPV6==true +#if YSF_IPV4==true + if ( ReceiveDS(Buffer, Ip, 20) ) +#else + if ( Receive6(Buffer, Ip, 20) ) +#endif +#else + if ( Receive4(Buffer, Ip, 20) ) +#endif + { + // crack the packet + if ( IsValidDvPacket(Buffer, &Fich) ) + { + if ( IsValidDvFramePacket(Ip, Fich, Buffer, Frames) ) + { + OnDvFramePacketIn(Frames[0], &Ip); + OnDvFramePacketIn(Frames[1], &Ip); + OnDvFramePacketIn(Frames[2], &Ip); + OnDvFramePacketIn(Frames[3], &Ip); + OnDvFramePacketIn(Frames[4], &Ip); + } + else if ( IsValidDvHeaderPacket(Ip, Fich, Buffer, Header, Frames) ) + { + // node linked and callsign muted? + if ( g_GateKeeper.MayTransmit(Header->GetMyCallsign(), Ip, PROTOCOL_YSF, Header->GetRpt2Module()) ) + { + // handle it + OnDvHeaderPacketIn(Header, Ip); + //OnDvFramePacketIn(Frames[0], &Ip); + //OnDvFramePacketIn(Frames[1], &Ip); + } + } + else if ( IsValidDvLastFramePacket(Ip, Fich, Buffer, OneFrame, LastFrame) ) + { + OnDvFramePacketIn(OneFrame, &Ip); + OnDvLastFramePacketIn(LastFrame, &Ip); + } + } + else if ( IsValidConnectPacket(Buffer, &Callsign) ) + { + //std::cout << "YSF keepalive/connect packet from " << Callsign << " at " << Ip << std::endl; + + // callsign authorized? + if ( g_GateKeeper.MayLink(Callsign, Ip, PROTOCOL_YSF) ) + { + // acknowledge the request + EncodeConnectAckPacket(&Buffer); + Send(Buffer, Ip); + + // add client if needed + CClients *clients = g_Reflector.GetClients(); + std::shared_ptrclient = clients->FindClient(Callsign, Ip, PROTOCOL_YSF); + // client already connected ? + if ( client == nullptr ) + { + std::cout << "YSF connect packet from " << Callsign << " at " << Ip << std::endl; + + // create the client + auto newclient = std::make_shared(Callsign, Ip); + + // aautolink, if enabled +#if YSF_AUTOLINK_ENABLE + newclient->SetReflectorModule(YSF_AUTOLINK_MODULE); +#endif + + // and append + clients->AddClient(newclient); + } + else + { + client->Alive(); + } + // and done + g_Reflector.ReleaseClients(); + } + } + else if ( IsValidwirexPacket(Buffer, &Fich, &Callsign, &iWiresxCmd, &iWiresxArg) ) + { + // std::cout << "Got a WiresX command from " << Callsign << " at " << Ip << " cmd=" < YSF_KEEPALIVE_PERIOD ) + { + // + HandleKeepalives(); + + // update time + m_LastKeepaliveTime.Now(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// streams helpers + +void CYsfProtocol::OnDvHeaderPacketIn(std::unique_ptr &Header, const CIp &Ip) +{ + // find the stream + CPacketStream *stream = GetStream(Header->GetStreamId()); + if ( stream ) + { + // stream already open + // skip packet, but tickle the stream + stream->Tickle(); + } + else + { + // no stream open yet, open a new one + CCallsign my(Header->GetMyCallsign()); + CCallsign rpt1(Header->GetRpt1Callsign()); + CCallsign rpt2(Header->GetRpt2Callsign()); + + // find this client + std::shared_ptrclient = g_Reflector.GetClients()->FindClient(Ip, PROTOCOL_YSF); + if ( client ) + { + // get client callsign + rpt1 = client->GetCallsign(); + // get module it's linked to + auto m = client->GetReflectorModule(); + Header->SetRpt2Module(m); + rpt2.SetModule(m); + + // and try to open the stream + if ( (stream = g_Reflector.OpenStream(Header, client)) != nullptr ) + { + // keep the handle + m_Streams.push_back(stream); + } + } + // release + g_Reflector.ReleaseClients(); + + // update last heard + if ( g_Reflector.IsValidModule(rpt2.GetModule()) ) + { + g_Reflector.GetUsers()->Hearing(my, rpt1, rpt2); + g_Reflector.ReleaseUsers(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// queue helper + +void CYsfProtocol::HandleQueue(void) +{ + + m_Queue.Lock(); + while ( !m_Queue.empty() ) + { + // get the packet + auto packet = m_Queue.front(); + m_Queue.pop(); + + // get our sender's id + int iModId = g_Reflector.GetModuleIndex(packet->GetModuleId()); + + // encode + CBuffer buffer; + + // check if it's header + if ( packet->IsDvHeader() ) + { + // update local stream cache + // this relies on queue feeder setting valid module id + m_StreamsCache[iModId].m_dvHeader = CDvHeaderPacket((CDvHeaderPacket &)*packet); + + // encode it + EncodeDvHeaderPacket((CDvHeaderPacket &)*packet.get(), &buffer); + } + // check if it's a last frame + else if ( packet->IsLastPacket() ) + { + // encode it + EncodeDvLastPacket(m_StreamsCache[iModId].m_dvHeader, &buffer); + } + // otherwise, just a regular DV frame + else + { + // update local stream cache or send triplet when needed + uint8 sid = packet->GetYsfPacketSubId(); + if (sid <= 4) + { + //std::cout << (int)sid; + m_StreamsCache[iModId].m_dvFrames[sid] = CDvFramePacket((CDvFramePacket &)*packet); + if ( sid == 4 ) + { + + EncodeDvPacket(m_StreamsCache[iModId].m_dvHeader, m_StreamsCache[iModId].m_dvFrames, &buffer); + } + } + } + + // send it + if ( buffer.size() > 0 ) + { + // and push it to all our clients linked to the module and who are not streaming in + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_YSF, it)) != nullptr ) + { + // is this client busy ? + if ( !client->IsAMaster() && (client->GetReflectorModule() == packet->GetModuleId()) ) + { + // no, send the packet + Send(buffer, client->GetIp()); + + } + } + g_Reflector.ReleaseClients(); + } + } + m_Queue.Unlock(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// keepalive helpers + +void CYsfProtocol::HandleKeepalives(void) +{ + // YSF protocol keepalive request is client tasks + // here, just check that all clients are still alive + // and disconnect them if not + + // iterate on clients + CClients *clients = g_Reflector.GetClients(); + auto it = clients->begin(); + std::shared_ptrclient = nullptr; + while ( (client = clients->FindNextClient(PROTOCOL_YSF, it)) != nullptr ) + { + // is this client busy ? + if ( client->IsAMaster() ) + { + // yes, just tickle it + client->Alive(); + } + // check it's still with us + else if ( !client->IsAlive() ) + { + // no, remove it + std::cout << "YSF client " << client->GetCallsign() << " keepalive timeout" << std::endl; + clients->RemoveClient(client); + } + + } + g_Reflector.ReleaseClients(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DV packet decoding helpers + +bool CYsfProtocol::IsValidConnectPacket(const CBuffer &Buffer, CCallsign *callsign) +{ + uint8 tag[] = { 'Y','S','F','P' }; + + bool valid = false; + if ( (Buffer.size() == 14) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + callsign->SetCallsign(Buffer.data()+4, 8); + callsign->SetModule(YSF_MODULE_ID); + valid = (callsign->IsValid()); + } + return valid; +} + +bool CYsfProtocol::IsValidDvPacket(const CBuffer &Buffer, CYSFFICH *Fich) +{ + uint8 tag[] = { 'Y','S','F','D' }; + + bool valid = false; + + if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // decode YSH fich + if ( Fich->decode(&(Buffer.data()[40])) ) + { + valid = (Fich->getDT() == YSF_DT_VD_MODE2); + } + } + return valid; +} + + +bool CYsfProtocol::IsValidDvHeaderPacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, std::unique_ptr &header, std::array, 5> &frames) +{ + // DV header ? + if ( Fich.getFI() == YSF_FI_HEADER ) + { + // get stream id + uint32 uiStreamId = IpToStreamId(Ip); + + // get header data + CYSFPayload ysfPayload; + if ( ysfPayload.processHeaderData((unsigned char *)&(Buffer.data()[35])) ) + { + // build DVHeader + char sz[YSF_CALLSIGN_LENGTH+1]; + ::memcpy(sz, &(Buffer.data()[14]), YSF_CALLSIGN_LENGTH); + sz[YSF_CALLSIGN_LENGTH] = 0; + CCallsign csMY = CCallsign((const char *)sz); + ::memcpy(sz, &(Buffer.data()[4]), YSF_CALLSIGN_LENGTH); + sz[YSF_CALLSIGN_LENGTH] = 0; + CCallsign rpt1 = CCallsign((const char *)sz); + rpt1.SetModule(YSF_MODULE_ID); + CCallsign rpt2 = m_ReflectorCallsign; + // as YSF protocol does not provide a module-tranlatable + // destid, set module to none and rely on OnDvHeaderPacketIn() + // to later fill it with proper value + rpt2.SetModule(' '); + + // and packet + header = std::unique_ptr(new CDvHeaderPacket(csMY, CCallsign("CQCQCQ"), rpt1, rpt2, uiStreamId, Fich.getFN())); + } + // and 2 DV Frames + { + uint8 uiAmbe[AMBE_SIZE]; + ::memset(uiAmbe, 0x00, sizeof(uiAmbe)); + frames[0] = std::unique_ptr(new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 0, 0)); + frames[1] = std::unique_ptr(new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 1, 0)); + } + + // check validity of packets + if ( header && frames[0] && frames[1] && header->IsValid() && frames[0]->IsValid() && frames[1]->IsValid() ) + return true; + + } + return false; +} + +bool CYsfProtocol::IsValidDvFramePacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, std::array, 5> &frames) +{ + // is it DV frame ? + if ( Fich.getFI() == YSF_FI_COMMUNICATIONS ) + { + // get stream id + uint32 uiStreamId = IpToStreamId(Ip); + + // get DV frames + uint8 ambe0[AMBEPLUS_SIZE]; + uint8 ambe1[AMBEPLUS_SIZE]; + uint8 ambe2[AMBEPLUS_SIZE]; + uint8 ambe3[AMBEPLUS_SIZE]; + uint8 ambe4[AMBEPLUS_SIZE]; + uint8 *ambes[5] = { ambe0, ambe1, ambe2, ambe3, ambe4 }; + CYsfUtils::DecodeVD2Vchs((unsigned char *)&(Buffer.data()[35]), ambes); + + // get DV frames + uint8 fid = Buffer.data()[34]; + frames[0] = std::unique_ptr(new CDvFramePacket(ambe0, uiStreamId, Fich.getFN(), 0, fid)); + frames[1] = std::unique_ptr(new CDvFramePacket(ambe1, uiStreamId, Fich.getFN(), 1, fid)); + frames[2] = std::unique_ptr(new CDvFramePacket(ambe2, uiStreamId, Fich.getFN(), 2, fid)); + frames[3] = std::unique_ptr(new CDvFramePacket(ambe3, uiStreamId, Fich.getFN(), 3, fid)); + frames[4] = std::unique_ptr(new CDvFramePacket(ambe4, uiStreamId, Fich.getFN(), 4, fid)); + + // check validity of packets + if ( frames[0] && frames[0]->IsValid() && frames[1] && frames[1]->IsValid() && frames[2] && frames[2]->IsValid() && frames[3] && frames[3]->IsValid() && frames[4] && frames[4]->IsValid() ) + return true; + } + return false; +} + +bool CYsfProtocol::IsValidDvLastFramePacket(const CIp &Ip, const CYSFFICH &Fich, const CBuffer &Buffer, std::unique_ptr &oneframe, std::unique_ptr &lastframe) +{ + // DV header ? + if ( Fich.getFI() == YSF_FI_TERMINATOR ) + { + // get stream id + uint32 uiStreamId = IpToStreamId(Ip); + + // get DV frames + { + uint8 uiAmbe[AMBE_SIZE]; + ::memset(uiAmbe, 0x00, sizeof(uiAmbe)); + oneframe = std::unique_ptr(new CDvFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 0, 0)); + lastframe = std::unique_ptr(new CDvLastFramePacket(uiAmbe, uiStreamId, Fich.getFN(), 1, 0)); + } + + // check validity of packets + if ( (oneframe && oneframe->IsValid()) && lastframe && lastframe->IsValid() ) + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// DV packet encoding helpers + +void CYsfProtocol::EncodeConnectAckPacket(CBuffer *Buffer) const +{ + uint8 tag[] = { 'Y','S','F','P','R','E','F','L','E','C','T','O','R',0x20 }; + + Buffer->Set(tag, sizeof(tag)); +} + +bool CYsfProtocol::EncodeDvHeaderPacket(const CDvHeaderPacket &Header, CBuffer *Buffer) const +{ + uint8 tag[] = { 'Y','S','F','D' }; + uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; + char sz[YSF_CALLSIGN_LENGTH]; + uint8 fichd[YSF_FICH_LENGTH_BYTES]; + + // tag + Buffer->Set(tag, sizeof(tag)); + // rpt1 + ::memset(sz, ' ', sizeof(sz)); + Header.GetRpt1Callsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // my + ::memset(sz, ' ', sizeof(sz)); + Header.GetMyCallsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // dest + Buffer->Append(dest, 10); + // net frame counter + Buffer->Append((uint8)0x00); + // FS + Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); + // FICH + CYSFFICH fich; + fich.setFI(YSF_FI_HEADER); + fich.setCS(2U); + //fich.setFN(Header.GetYsfPacketId()); + fich.setFN(0U); + fich.setFT(7U); + fich.setDev(0U); + fich.setMR(YSF_MR_BUSY); + fich.setDT(YSF_DT_VD_MODE2); + fich.setSQL(0U); + fich.setSQ(0U); + fich.encode(fichd); + Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); + // payload + unsigned char csd1[20U], csd2[20U]; + ::memset(csd1, '*', YSF_CALLSIGN_LENGTH); + ::memset(csd1 + YSF_CALLSIGN_LENGTH, ' ', YSF_CALLSIGN_LENGTH); + Header.GetMyCallsign().GetCallsignString(sz); + ::memcpy(csd1 + YSF_CALLSIGN_LENGTH, sz, ::strlen(sz)); + ::memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); + CYSFPayload payload; + uint8 temp[120]; + payload.writeHeader(temp, csd1, csd2); + Buffer->Append(temp+30, 120-30); + + // done + return true; +} + +bool CYsfProtocol::EncodeDvPacket(const CDvHeaderPacket &Header, const CDvFramePacket *DvFrames, CBuffer *Buffer) const +{ + uint8 tag[] = { 'Y','S','F','D' }; + uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; + uint8 gps[] = { 0x52,0x22,0x61,0x5F,0x27,0x03,0x5E,0x20,0x20,0x20 }; + char sz[YSF_CALLSIGN_LENGTH]; + uint8 fichd[YSF_FICH_LENGTH_BYTES]; + + // tag + Buffer->Set(tag, sizeof(tag)); + // rpt1 + ::memset(sz, ' ', sizeof(sz)); + Header.GetRpt1Callsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // my + ::memset(sz, ' ', sizeof(sz)); + Header.GetMyCallsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // dest + Buffer->Append(dest, 10); + // net frame counter + Buffer->Append(DvFrames[0].GetYsfPacketFrameId()); + // FS + Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); + // FICH + CYSFFICH fich; + fich.setFI(YSF_FI_COMMUNICATIONS); + fich.setCS(2U); + fich.setFN(DvFrames[0].GetYsfPacketId()); + fich.setFT(6U); + fich.setDev(0U); + fich.setMR(YSF_MR_BUSY); + fich.setDT(YSF_DT_VD_MODE2); + fich.setSQL(0U); + fich.setSQ(0U); + fich.encode(fichd); + Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); + // payload + CYSFPayload payload; + uint8 temp[120]; + ::memset(temp, 0x00, sizeof(temp)); + // DV + for ( int i = 0; i < 5; i++ ) + { + CYsfUtils::EncodeVD2Vch((unsigned char *)DvFrames[i].GetAmbePlus(), temp+35+(18*i)); + } + // data + switch (DvFrames[0].GetYsfPacketId()) + { + case 0: + // Dest + payload.writeVDMode2Data(temp, (const unsigned char*)"**********"); + break; + case 1: + // Src + ::memset(sz, ' ', sizeof(sz)); + Header.GetMyCallsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + payload.writeVDMode2Data(temp, (const unsigned char*)sz); + break; + case 2: + // Down + ::memset(sz, ' ', sizeof(sz)); + Header.GetRpt1Callsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + payload.writeVDMode2Data(temp, (const unsigned char*)sz); + break; + case 5: + // Rem3+4 + // we need to provide a fake radioid for radios + // to display src callsign + payload.writeVDMode2Data(temp, (const unsigned char*)" G0gBJ"); + break; + case 6: + // DT1 + // we need to issue a fake gps string with proper terminator + // and crc for radios to display src callsign + payload.writeVDMode2Data(temp, gps); + break; + default: + payload.writeVDMode2Data(temp, (const unsigned char*)" "); + break; + + } + Buffer->Append(temp+30, 120-30); + + // done + return true; +} + +bool CYsfProtocol::EncodeDvLastPacket(const CDvHeaderPacket &Header, CBuffer *Buffer) const +{ + uint8 tag[] = { 'Y','S','F','D' }; + uint8 dest[] = { 'A','L','L',' ',' ',' ',' ',' ',' ',' ' }; + char sz[YSF_CALLSIGN_LENGTH]; + uint8 fichd[YSF_FICH_LENGTH_BYTES]; + + // tag + Buffer->Set(tag, sizeof(tag)); + // rpt1 + ::memset(sz, ' ', sizeof(sz)); + Header.GetRpt1Callsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // my + ::memset(sz, ' ', sizeof(sz)); + Header.GetMyCallsign().GetCallsignString(sz); + sz[::strlen(sz)] = ' '; + Buffer->Append((uint8 *)sz, YSF_CALLSIGN_LENGTH); + // dest + Buffer->Append(dest, 10); + // net frame counter + Buffer->Append((uint8)0x00); + // FS + Buffer->Append((uint8 *)YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); + // FICH + CYSFFICH fich; + fich.setFI(YSF_FI_TERMINATOR); + fich.setCS(2U); + //fich.setFN(Header.GetYsfPacketId()); + fich.setFN(0U); + fich.setFT(7U); + fich.setDev(0U); + fich.setMR(YSF_MR_BUSY); + fich.setDT(YSF_DT_VD_MODE2); + fich.setSQL(0U); + fich.setSQ(0U); + fich.encode(fichd); + Buffer->Append(fichd, YSF_FICH_LENGTH_BYTES); + // payload + unsigned char csd1[20U], csd2[20U]; + ::memset(csd1, '*', YSF_CALLSIGN_LENGTH); + ::memset(csd1 + YSF_CALLSIGN_LENGTH, ' ', YSF_CALLSIGN_LENGTH); + Header.GetMyCallsign().GetCallsignString(sz); + ::memcpy(csd1 + YSF_CALLSIGN_LENGTH, sz, ::strlen(sz)); + ::memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); + CYSFPayload payload; + uint8 temp[120]; + payload.writeHeader(temp, csd1, csd2); + Buffer->Append(temp+30, 120-30); + + // done + return true; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Wires-X packet decoding helpers + +bool CYsfProtocol::IsValidwirexPacket(const CBuffer &Buffer, CYSFFICH *Fich, CCallsign *Callsign, int *Cmd, int *Arg) +{ + uint8 tag[] = { 'Y','S','F','D' }; + uint8 DX_REQ[] = {0x5DU, 0x71U, 0x5FU}; + uint8 CONN_REQ[] = {0x5DU, 0x23U, 0x5FU}; + uint8 DISC_REQ[] = {0x5DU, 0x2AU, 0x5FU}; + uint8 ALL_REQ[] = {0x5DU, 0x66U, 0x5FU}; + uint8 command[300]; + CYSFPayload payload; + bool valid = false; + + if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // decode YSH fich + if ( Fich->decode(&(Buffer.data()[40])) ) + { + //std::cout << (int)Fich->getDT() << "," + // << (int)Fich->getFI() << "," + // << (int)Fich->getFN() << "," + // << (int)Fich->getFT() + // << std::endl; + valid = (Fich->getDT() == YSF_DT_DATA_FR_MODE); + valid &= (Fich->getFI() == YSF_FI_COMMUNICATIONS); + if ( valid ) + { + // get callsign + Callsign->SetCallsign(&(Buffer.data()[4]), CALLSIGN_LEN, false); + Callsign->SetModule(YSF_MODULE_ID); + // decode payload + if ( Fich->getFN() == 0U ) + { + valid = false; + } + else if ( Fich->getFN() == 1U ) + { + valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + 0U); + } + else + { + valid &= payload.readDataFRModeData1(&(Buffer.data()[35]), command + (Fich->getFN() - 1U) * 20U + 0U); + if ( valid ) + { + valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + (Fich->getFN() - 1U) * 20U + 20U); + } + } + // check crc if end found + if ( Fich->getFN() == Fich->getFT() ) + { + valid = false; + // Find the end marker + for (unsigned int i = Fich->getFN() * 20U; i > 0U; i--) + { + if (command[i] == 0x03U) + { + unsigned char crc = CCRC::addCRC(command, i + 1U); + if (crc == command[i + 1U]) + valid = true; + break; + } + } + } + // and crack the command + if ( valid ) + { + // get argument + char buffer[4U]; + ::memcpy(buffer, command + 5U + 2U, 3U); + buffer[3U] = 0x00U; + *Arg = ::atoi(buffer); + // and decode command + if (::memcmp(command + 1U, DX_REQ, 3U) == 0) + { + *Cmd = WIRESX_CMD_DX_REQ; + *Arg = 0; + } + else if (::memcmp(command + 1U, ALL_REQ, 3U) == 0) + { + // argument is start index of list + if ( *Arg > 0 ) + (*Arg)--; + // check if all or search + if ( ::memcmp(command + 5U, "01", 2) == 0 ) + { + *Cmd = WIRESX_CMD_ALL_REQ; + } + else if ( ::memcmp(command + 5U, "11", 2) == 0 ) + { + *Cmd = WIRESX_CMD_SEARCH_REQ; + } + } + else if (::memcmp(command + 1U, CONN_REQ, 3U) == 0) + { + *Cmd = WIRESX_CMD_CONN_REQ; + } + else if (::memcmp(command + 1U, DISC_REQ, 3U) == 0) + { + *Cmd = WIRESX_CMD_DISC_REQ; + *Arg = 0; + } + else + { + std::cout << "Wires-X unknown command" << std::endl; + *Cmd = WIRESX_CMD_UNKNOWN; + *Arg = 0; + valid = false; + } + } + } + } + } + return valid; +} + +// server status packet decoding helpers + +bool CYsfProtocol::IsValidServerStatusPacket(const CBuffer &Buffer) const +{ + uint8 tag[] = { 'Y','S','F','S' }; + + return ( (Buffer.size() >= 4) && (Buffer.Compare(tag, sizeof(tag)) == 0) ); +} + +// server status packet encoding helpers + +bool CYsfProtocol::EncodeServerStatusPacket(CBuffer *Buffer) const +{ + uint8 tag[] = { 'Y','S','F','S' }; + uint8 description[] = { 'X','L','X',' ','r','e','f','l','e','c','t','o','r',' ' }; + uint8 callsign[16]; + + // tag + Buffer->Set(tag, sizeof(tag)); + // hash + ::memset(callsign, ' ', sizeof(callsign)); + g_Reflector.GetCallsign().GetCallsign(callsign); + char sz[16]; + ::sprintf(sz, "%05u", CalcHash(callsign, 16) % 100000U); + Buffer->Append((uint8 *)sz, 5); + // name + Buffer->Append(callsign, 16); + // desscription + Buffer->Append(description, 14); + // connected clients + CClients *clients = g_Reflector.GetClients(); + int count = MIN(999, clients->GetSize()); + g_Reflector.ReleaseClients(); + ::sprintf(sz, "%03u", count); + Buffer->Append((uint8 *)sz, 3); + + // done + return true; +} + +uint32 CYsfProtocol::CalcHash(const uint8 *buffer, int len) const +{ + uint32 hash = 0U; + + for ( int i = 0; i < len; i++) + { + hash += buffer[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// uiStreamId helpers + + +// uiStreamId helpers +uint32 CYsfProtocol::IpToStreamId(const CIp &ip) const +{ + return ip.GetAddr() ^ (uint32)(MAKEDWORD(ip.GetPort(), ip.GetPort())); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// debug + +#ifdef DEBUG_DUMPFILE +bool CYsfProtocol::DebugTestDecodePacket(const CBuffer &Buffer) +{ + uint8 tag[] = { 'Y','S','F','D' }; + static uint8 command[4098]; + static int len; + CYSFFICH Fich; + CYSFPayload payload; + CBuffer dump; + bool valid = false; + + if ( (Buffer.size() == 155) && (Buffer.Compare(tag, sizeof(tag)) == 0) ) + { + // decode YSH fich + if ( Fich.decode(&(Buffer.data()[40])) ) + { + std::cout << (int)Fich.getDT() << "," + << (int)Fich.getFI() << "," + << (int)Fich.getBN() << "," + << (int)Fich.getBT() << "," + << (int)Fich.getFN() << "," + << (int)Fich.getFT() << " : "; + + switch ( Fich.getFI() ) + { + case YSF_FI_HEADER: + len = 0; + ::memset(command, 0x00, sizeof(command)); + std::cout << "Header" << std::endl; + break; + case YSF_FI_TERMINATOR: + std::cout << "Trailer" << std::endl; + std::cout << "length of payload : " << len << std::endl; + dump.Set(command, len); + dump.DebugDump(g_Reflector.m_DebugFile); + dump.DebugDumpAscii(g_Reflector.m_DebugFile); + break; + case YSF_FI_COMMUNICATIONS: + if ( Fich.getDT() == YSF_DT_DATA_FR_MODE ) + { + valid = payload.readDataFRModeData1(&(Buffer.data()[35]), command + len); + len += 20; + valid &= payload.readDataFRModeData2(&(Buffer.data()[35]), command + len); + len += 20; + std::cout << "decoded ok" << std::endl; + } + break; + } + } + else + { + std::cout << "invalid fich in packet" << std::endl; + } + } + else + { + std::cout << "invalid size packet" << std::endl; + } + return valid; +} +#endif + + +bool CYsfProtocol::DebugDumpHeaderPacket(const CBuffer &Buffer) +{ + bool ok; + CYSFFICH fich; + CYSFPayload payload; + uint8 data[200]; + + :: memset(data, 0, sizeof(data)); + + + ok = IsValidDvPacket(Buffer, &fich); + if ( ok && (fich.getFI() == YSF_FI_HEADER) ) + { + ok &= payload.processHeaderData((unsigned char *)&(Buffer.data()[35])); + } + + std::cout << "HD-" <<(ok ? "ok " : "xx ") << "src: " << payload.getSource() << "dest: " << payload.getDest() << std::endl; + + return ok; +} + +bool CYsfProtocol::DebugDumpDvPacket(const CBuffer &Buffer) +{ + bool ok; + CYSFFICH fich; + CYSFPayload payload; + uint8 data[200]; + + :: memset(data, 0, sizeof(data)); + + ok = IsValidDvPacket(Buffer, &fich); + if ( ok && (fich.getFI() == YSF_FI_COMMUNICATIONS) ) + { + ok &= payload.readVDMode2Data(&(Buffer.data()[35]), data); + } + + std::cout << "DV-" <<(ok ? "ok " : "xx ") << "FN:" << (int)fich.getFN() << " payload: " << (char *)data << std::endl; + + return ok; +} + +bool CYsfProtocol::DebugDumpLastDvPacket(const CBuffer &Buffer) +{ + bool ok; + CYSFFICH fich; + CYSFPayload payload; + uint8 data[200]; + + :: memset(data, 0, sizeof(data)); + + + ok = IsValidDvPacket(Buffer, &fich); + if ( ok && (fich.getFI() == YSF_FI_TERMINATOR) ) + { + ok &= payload.processHeaderData((unsigned char *)&(Buffer.data()[35])); + } + + std::cout << "TC-" <<(ok ? "ok " : "xx ") << "src: " << payload.getSource() << "dest: " << payload.getDest() << std::endl; + + return ok; +} diff --git a/src/YSFProtocol.h b/src/YSFProtocol.h new file mode 100644 index 0000000..03da89e --- /dev/null +++ b/src/YSFProtocol.h @@ -0,0 +1,141 @@ +// +// YSFProtocol.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 20/05/2018. +// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved. +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + + +#ifndef cysfprotocol_h +#define cysfprotocol_h + + +#include "Timer.h" +#include "DCSProtocol.h" +#include "DVHeaderPacket.h" +#include "DVFramePacket.h" +#include "DVLastFramePacket.h" +#include "YSFDefines.h" +#include "YSFFich.h" +#include "WiresXCmdInc.h" +#include "WiresXCmdHandler.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// define + + +// Wires-X commands +#define WIRESX_CMD_UNKNOWN 0 +#define WIRESX_CMD_DX_REQ 1 +#define WIRESX_CMD_ALL_REQ 2 +#define WIRESX_CMD_SEARCH_REQ 3 +#define WIRESX_CMD_CONN_REQ 4 +#define WIRESX_CMD_DISC_REQ 5 + +// YSF Module ID +#define YSF_MODULE_ID 'B' + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CYsfStreamCacheItem +{ +public: + CYsfStreamCacheItem() {} + ~CYsfStreamCacheItem() {} + + CDvHeaderPacket m_dvHeader; + CDvFramePacket m_dvFrames[5]; + + //uint8 m_uiSeqId; +}; + +class CYsfProtocol : public CProtocol +{ +public: + // constructor + CYsfProtocol(); + + // initialization + bool Initialize(const char *type, const int ptype, const uint16 port, const bool has_ipv4, const bool has_ipv6); + void Close(void); + + // task + void Task(void); + +protected: + // queue helper + void HandleQueue(void); + + // keepalive helpers + void HandleKeepalives(void); + + // stream helpers + void OnDvHeaderPacketIn(std::unique_ptr &, const CIp &); + + // DV packet decoding helpers + bool IsValidConnectPacket(const CBuffer &, CCallsign *); + //bool IsValidDisconnectPacket(const CBuffer &, CCallsign *); + bool IsValidDvPacket(const CBuffer &, CYSFFICH *); + bool IsValidDvHeaderPacket(const CIp &, const CYSFFICH &, const CBuffer &, std::unique_ptr &, std::array, 5> &); + bool IsValidDvFramePacket(const CIp &, const CYSFFICH &, const CBuffer &, std::array, 5> &); + bool IsValidDvLastFramePacket(const CIp &, const CYSFFICH &, const CBuffer &, std::unique_ptr &, std::unique_ptr &); + + // DV packet encoding helpers + void EncodeConnectAckPacket(CBuffer *) const; + //void EncodeConnectNackPacket(const CCallsign &, char, CBuffer *); + //void EncodeDisconnectPacket(CBuffer *, std::shared_ptr); + bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const; + bool EncodeDvPacket(const CDvHeaderPacket &, const CDvFramePacket *, CBuffer *) const; + bool EncodeDvLastPacket(const CDvHeaderPacket &, CBuffer *) const; + + // Wires-X packet decoding helpers + bool IsValidwirexPacket(const CBuffer &, CYSFFICH *, CCallsign *, int *, int*); + + // server status packet decoding helpers + bool IsValidServerStatusPacket(const CBuffer &) const; + uint32 CalcHash(const uint8 *, int) const; + + // server status packet encoding helpers + bool EncodeServerStatusPacket(CBuffer *) const; + + // uiStreamId helpers + uint32 IpToStreamId(const CIp &) const; + + // debug + bool DebugTestDecodePacket(const CBuffer &); + bool DebugDumpHeaderPacket(const CBuffer &); + bool DebugDumpDvPacket(const CBuffer &); + bool DebugDumpLastDvPacket(const CBuffer &); + +protected: + // for keep alive + CTimePoint m_LastKeepaliveTime; + + // for queue header caches + std::array m_StreamsCache; + + // for wires-x + CWiresxCmdHandler m_WiresxCmdHandler; + unsigned char m_seqNo; +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfprotocol_h */ diff --git a/src/YSFUtils.cpp b/src/YSFUtils.cpp new file mode 100644 index 0000000..814226f --- /dev/null +++ b/src/YSFUtils.cpp @@ -0,0 +1,666 @@ +// +// cysfutils.cpp +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 14/04/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +// Copyright (C) 2018 by Andy Uribe CA6JAU +// Copyright (C) 2018 by Manuel Sanchez EA7EE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#include "Main.h" +#include +#include "YSFDefines.h" +#include "YSFUtils.h" +#include "Golay24128.h" + +//////////////////////////////////////////////////////////////////////////////////////// +// constants & defines + +const unsigned int PRNG_TABLE[] = +{ + 0x42CC47U, 0x19D6FEU, 0x304729U, 0x6B2CD0U, 0x60BF47U, 0x39650EU, 0x7354F1U, 0xEACF60U, 0x819C9FU, 0xDE25CEU, + 0xD7B745U, 0x8CC8B8U, 0x8D592BU, 0xF71257U, 0xBCA084U, 0xA5B329U, 0xEE6AFAU, 0xF7D9A7U, 0xBCC21CU, 0x4712D9U, + 0x4F2922U, 0x14FA37U, 0x5D43ECU, 0x564115U, 0x299A92U, 0x20A9EBU, 0x7B707DU, 0x3BE3A4U, 0x20D95BU, 0x6B085AU, + 0x5233A5U, 0x99A474U, 0xC0EDCBU, 0xCB5F12U, 0x918455U, 0xF897ECU, 0xE32E3BU, 0xAA7CC2U, 0xB1E7C9U, 0xFC561DU, + 0xA70DE6U, 0x8DBE73U, 0xD4F608U, 0x57658DU, 0x0E5E56U, 0x458DABU, 0x7E15B8U, 0x376645U, 0x2DFD86U, 0x64EC3BU, + 0x3F1F60U, 0x3481B4U, 0x4DA00FU, 0x067BCEU, 0x1B68B1U, 0xD19328U, 0xCA03FFU, 0xA31856U, 0xF8EB81U, 0xF9F2F8U, + 0xA26067U, 0xA91BB6U, 0xF19A59U, 0x9A6148U, 0x8372B6U, 0xC8E86FU, 0x9399DCU, 0x1A0291U, 0x619142U, 0x6DE9FFU, + 0x367A2CU, 0x7D2511U, 0x6484DAU, 0x2F1F0FU, 0x1E6DB4U, 0x55F6E1U, 0x0EA70AU, 0x061C96U, 0xDD0E45U, 0xB4D738U, + 0xAF64ABU, 0xE47F42U, 0xFDBE9DU, 0xB684ACU, 0xFE5773U, 0xC1E4A2U, 0x8AFD0DU, 0x932ED4U, 0xD814E3U, 0x81853AU, + 0x225EECU, 0x7A6945U, 0x31A112U, 0x2AB2EBU, 0x630974U, 0x785AB5U, 0x11E3CEU, 0x4A715BU, 0x402AA0U, 0x199B7DU, + 0x16C05EU, 0x6F5283U, 0xA4FB10U, 0xBFA8ECU, 0xF633B7U, 0xEC4012U, 0xADD8C9U, 0xD6EB1CU, 0xDD3027U, 0x84A1FAU, + 0xCF9E19U, 0xD64C80U, 0xBC4557U, 0xA7B62EU, 0x6E2DA1U, 0x311F50U, 0x38C68EU, 0x63D5BFU, 0x486E60U, 0x10BFE1U, + 0x5BAD1EU, 0x4A4647U, 0x0157F0U, 0x7ACC29U, 0x73BEEAU, 0x2825D7U, 0xA0940CU, 0xFBCFF9U, 0xB05C62U, 0x892426U, + 0xC6B3DDU, 0xDF3840U, 0x9449B3U, 0xCED3BEU, 0xE7804DU, 0xBC3B90U, 0xF5AA0BU, 0xE6D17EU, 0x2D43B5U, 0x345A04U, + 0x5EA9DBU, 0x07A202U, 0x0C7134U, 0x45C9FDU, 0x5EDA0AU, 0x310193U, 0x6830C4U, 0x62AA3DU, 0x3B59B2U, 0xB04043U, + 0xEB975CU, 0x82BCADU, 0x912E62U, 0xD8F7FBU, 0x82C489U, 0x895F54U, 0xF00FE7U, 0xFBBC2AU, 0xA2E771U, 0xE956C4U, + 0xF6CD1FU, 0x3F8FEAU, 0x0534E1U, 0x4C653CU, 0x17FE8FU, 0x1C4C52U, 0x4515A1U, 0x2E86A9U, 0x3FBD56U, 0x756C87U, + 0x6ED218U, 0x279179U, 0x7C0AA6U, 0xD53B17U, 0x8EE0C8U, 0x85F291U, 0xD94B36U, 0x9298EFU, 0xAB8318U, 0xE07301U, + 0xBB68DFU, 0xB2CB7CU, 0xE910A5U, 0xE101D2U, 0x92BB4BU, 0x59E8B4U, 0x407175U, 0x0B026AU, 0x12989BU, 0x792944U, + 0x2376EDU, 0x2EF5BAU, 0x758663U, 0x7C1ED5U, 0x078D0CU, 0x4EF6ABU, 0x5567F2U, 0x9F7C29U, 0xC68E9CU, 0xC51747U, + 0xBC6422U, 0xB7EFB9U, 0xECFD44U, 0xA50497U, 0xAF178AU, 0xD68C69U, 0xD97DB5U, 0x82670EU, 0xCBB45BU, 0x508D90U, + 0x190A25U, 0x63F0FEU, 0x68E3C7U, 0x317A10U, 0x3A09D9U, 0x6B926EU, 0x004237U, 0x1B79C8U, 0x53EA59U, 0x48B3B7U, + 0x811166U, 0xDE4A79U, 0xF5F988U, 0xAC6057U, 0xE733FEU, 0xFF89ADU, 0xB49830U, 0x8F4BC3U, 0xC6F00EU, 0x9DA135U, + 0x942FE0U, 0xC71C3BU, 0x4DC78FU, 0x3476C4U, 0x7F6C39U, 0x66BFAAU, 0x298657U, 0x725504U, 0x5B4E89U, 0x01FE72U, + 0x0835A3U, 0x53269CU, 0x189D4DU, 0x01CDC2U, 0xEA763BU, 0xF3A56DU, 0xB0BCD4U, 0xE80F13U, 0xE355CAU, 0x98C47DU, + 0x91AB24U, 0xCE38DBU, 0x87A35AU, 0x9CD3A5U, 0xD648F4U, 0xAF7B6FU, 0x24A292U, 0x7D3011U, 0x764B6DU, 0x2DDABEU, + 0x44D123U, 0x5E22D8U, 0x1FB09DU, 0x04A926U, 0x4F5AF3U, 0x064128U, 0x3DB105U, 0x70AAD6U, 0xAA392FU, 0xA1C4B8U, + 0xF8C7C0U, 0xD35D0FU, 0x8A2E9EU, 0xC1B761U, 0xDA44F0U, 0x925E8FU, 0x89CF4EU, 0xE8B4D1U, 0xB32728U, 0xB8FE7FU, + 0x61DCC6U, 0x2A4701U, 0x1614D8U, 0x5DADE2U, 0x46BE37U, 0x0F44DCU, 0x54D549U, 0x5D8E32U, 0x263DAFU, 0x2C237CU, + 0x75E291U, 0xBE5982U, 0xA74A7FU, 0xC493A4U, 0xDFA131U, 0x967A5AU, 0xCCCB8EU, 0xC1D835U, 0x9A02ECU, 0xF331BBU, + 0xE8B812U, 0xA3EBC5U, 0xBA507CU, 0x7080ABU, 0x099BC2U, 0x02285DU, 0x59718CU, 0x50C273U, 0x0B1862U, 0x4A1F8CU, + 0x70A655U, 0x3BF5C2U, 0x666FBBU, 0x6DDE68U, 0x3485C5U, 0x9F161EU, 0xC46F4BU, 0x8CFDF0U, 0x97C625U, 0xDE058EU, + 0xC59CD3U, 0xAEAE20U, 0xF775BCU, 0xFC647FU, 0xBD9F02U, 0xE70C91U, 0xCC1468U, 0x11E7B7U, 0x1AFC36U, 0x435B49U, + 0x080398U, 0x139027U, 0x7B63FEU, 0x607AF9U, 0x29E900U, 0x7293D6U, 0x79026FU, 0x00D930U, 0x0BEAF1U, 0xD3614EU, + 0x90119FU, 0x8B8AE4U, 0xC61969U, 0xBD609AU, 0xB4F247U, 0xEFA954U, 0xE518A9U, 0xBC0362U, 0xD7D0D6U, 0xCE7E8DU, + 0x856F18U, 0x1C94E3U, 0x578726U, 0x0D5F1DU, 0x24ECC0U, 0x7FF713U, 0x3E26AAU, 0x251D6DU, 0x6A8F14U, 0x53648BU, + 0x19757AU, 0x40AEB4U, 0xCB9CA5U, 0x90055AU, 0x9956C3U, 0xE2ED34U, 0xAB3C7DU, 0xB126EAU, 0xFA9513U, 0xA3D2C8U, + 0x886BFDU, 0xD9F836U, 0xD2A2E3U, 0x8D1359U, 0x454804U, 0x5EDBF7U, 0x37637AU, 0x2C3089U, 0x67ABD4U, 0x3E8847U, + 0x3551BAU, 0x4D6331U, 0x46B8C4U, 0x1D299FU, 0x54120EU, 0x5FC0E1U, 0x86D93BU, 0xE56A0EU, 0xFBB1D5U, 0xB2B600U, + 0xA94EABU, 0xE05DF6U, 0x9BE605U, 0x90B798U, 0xC92C6BU, 0xC3DE66U, 0x9AC7BDU, 0xD15448U, 0x6A3FD3U, 0x23ADA3U, + 0x78346CU, 0x7147F5U, 0x2BDC02U, 0x0EAD5BU, 0x553FFCU, 0x1EA425U, 0x07D5F2U, 0x4C4ECBU, 0x554C14U, 0x3EB3F5U, + 0xE4A26AU, 0xED799BU, 0xB6CA85U, 0xFFD25CU, 0xC421BFU, 0x8F3A22U, 0x96AB51U, 0xDC518CU, 0x895217U, 0x8289F2U, + 0xF9B8A9U, 0xF0231CU, 0x2BF1C7U, 0x62C80AU, 0x781B39U, 0x1320E5U, 0x4AB156U, 0x41EB8FU, 0x1848E0U, 0x13D771U, + 0x4886AEU, 0x203C5FU, 0x3B6F40U, 0x76F6A1U, 0xE5457EU, 0xAE1EE7U, 0xD7AC10U, 0xDCB549U, 0x8476EFU, 0x8FC536U, + 0xD49DE9U, 0x9D0ED8U, 0xA63513U, 0xEFE4A6U, 0xB4DF7DU, 0x3E0D00U, 0x779693U, 0x4CA75EU, 0x0568ADU, 0x527BB0U, + 0x59C34BU, 0x00109FU, 0x0A0B14U, 0x73FA61U, 0x38E0BAU, 0x23530FU, 0x6A88D4U, 0xB199DDU, 0x98322AU, 0xC260F3U, + 0xCBF944U, 0x908A0DU, 0xDB11F2U, 0xC28163U, 0xADFABDU, 0xBC694CU, 0xF65243U, 0xAD83BAU, 0xA40D6DU, 0x5F7EF4U, + 0x16E787U, 0x0DF44AU, 0x460EF1U, 0x5E1F24U, 0x15CC3FU, 0x6C77CAU, 0x676401U, 0x3C9CBDU, 0x359FEEU, 0x6A0413U, + 0x02F590U, 0x91EE4DU, 0xDA3C3EU, 0xC305A3U, 0x889658U, 0xF14D99U, 0xFA7F86U, 0xA1E677U, 0xE981E8U, 0xF21A10U, + 0xBB4BD7U, 0x80F1CEU, 0xCB6239U, 0x123BE0U, 0x1D885FU, 0x45921EU, 0x6641E1U, 0x3DE870U, 0x74BBAFU, 0x6F00C6U, + 0x261055U, 0x7DCBA8U, 0x57787AU, 0x0E2167U, 0x05B28CU, 0xCC8819U, 0x975BE2U, 0xBC52B7U, 0xE5E52CU, 0xEB37C9U, + 0xB20E12U, 0xF9DD2FU, 0xE8C6FCU, 0x837701U, 0xD8AD82U, 0xD1BE5AU, 0x0B0525U, 0x0244B4U, 0x79FE5BU, 0x322DCAU, + 0x2B3495U, 0x60876CU, 0x79DCFBU, 0x334C12U, 0x4C7745U, 0x45A4DCU, 0x1E3F23U, 0x175FF2U, 0xC4C0D8U, 0xAFF30DU, + 0xB72AF6U, 0xFCB96BU, 0xA5C338U, 0xAE5295U, 0xF54946U, 0xDCBABBU, 0x87A1A8U, 0xCF2165U, 0xD4DA9EU, 0x9FC90BU, + 0x223070U, 0x6922A4U, 0x30B92FU, 0x3348D6U, 0x695B01U, 0x20C038U, 0x1BB2EFU, 0x523B06U, 0x49EC99U, 0x02D7C8U, + 0x5B4777U, 0x713CA6U, 0xA8AF49U, 0xA3B650U, 0xF84586U, 0xB5DF7FU, 0xAE8CF8U, 0xC72581U, 0x9D3652U, 0x9EEDCFU, + 0xC75D34U, 0xCC0671U, 0xB5B5CAU, 0xFEAC1FU, 0x677EA4U, 0x2DC5F9U, 0x26D63AU, 0x7F1F86U, 0x142855U, 0x0DF2A8U, + 0x42E3B3U, 0x195872U, 0x108B8DU, 0x6AB31CU, 0x632063U, 0x307BAAU, 0xFBC83DU, 0xE201C4U, 0xA91393U, 0x90A82AU, + 0xDAF9E4U, 0x816A55U, 0x88D00AU, 0xD383DBU, 0xFA3A64U, 0xA569A5U, 0xEEE2DEU, 0x76D243U, 0x3D0D90U, 0x649E6DU, + 0x47E76EU, 0x1C7491U, 0x156E49U, 0x4E9DDEU, 0x0604B7U, 0x3D3720U, 0x76FDD9U, 0x6FEC06U, 0x2417B7U, 0xFD04F8U, + 0xF29D29U, 0x886F92U, 0xC1744FU, 0xDAC73CU, 0x939EB1U, 0x880C63U, 0xEBE79EU, 0xB2F285U, 0xB86970U, 0xE11ABBU, + 0xEA822EU, 0x311155U, 0x586AC0U, 0x43F92BU, 0x0A81F6U, 0x5412C5U, 0x5D111CU, 0x26E8CBU, 0x2D7B63U, 0x74213CU, + 0x3F90CDU, 0x2E8B52U, 0x645883U, 0xDFE36CU, 0x96F375U, 0xDD0882U, 0xC40B1BU, 0x8FD6CCU, 0xB464A5U, 0xFC7F3EU, + 0xA7AECBU, 0xAA9511U, 0xF10634U, 0xBA5CEFU, 0x83ED32U, 0x483681U, 0x5015DCU, 0x138D3FU, 0x48DEA2U, 0x616571U, + 0x3AF40CU, 0x33AF97U, 0x681D72U, 0x2246E9U, 0x3BD7B9U, 0x506C46U, 0x0D2FDFU, 0x869338U, 0xDDC061U, 0xD45BD6U, + 0xAF6A0FU, 0xE7B8C0U, 0xFC2371U, 0xBF102EU, 0xA6C9DFU, 0xEDDA40U, 0x943089U, 0x9FA1BFU, 0x459A66U, 0x0C4995U, + 0x175108U, 0x7AE243U, 0x6139B6U, 0x2A2A2DU, 0x73D3D8U, 0x79C183U, 0x204A26U, 0x0B3FFDU, 0x5AA420U, 0x111613U, + 0x8A4FDFU, 0xC3DC2CU, 0xF9A7B5U, 0xB034EAU, 0xEBAC5BU, 0xE0CF94U, 0xBD5465U, 0xF605FAU, 0xCFBEA3U, 0x85AC54U, + 0x9E55DDU, 0xD7C62AU, 0x0CDD73U, 0x252FCDU, 0x76361CU, 0x7DF5D3U, 0x3546E2U, 0x6E5B39U, 0x67A98CU, 0x1CB247U, + 0x57231AU, 0x4AD8A9U, 0x01CA74U, 0x191187U, 0xF2208AU, 0xA9AB50U, 0xA0F8A5U, 0xFB403EU, 0xF2D34BU, 0xA9A880U, + 0xCB393DU, 0xD262EEU, 0x99D0B7U, 0xC04B00U, 0xCB1AC9U, 0xB0B176U, 0x39E3A7U, 0x677EF8U, 0x2ECD58U, 0x359687U, + 0x7E277EU, 0x473D69U, 0x0CEEB0U, 0x55D557U, 0x5F04CEU, 0x0C8EBDU, 0x25BD60U, 0x7E64DBU, 0xB7771EU, 0xACCC05U, + 0xE51CF0U, 0xBF2F2AU, 0x90F497U, 0xC9E7D4U, 0xC25F09U, 0x9B9CBAU, 0xD08767U, 0xEB320CU, 0xA36999U, 0x38FB42U, + 0x7180B3U, 0x22112CU, 0x29AA45U, 0x50F9D2U, 0x1B610AU, 0x0202FDU, 0x4899E4U, 0x57080BU, 0x3E72DAU, 0x65E165U, + 0x6CFA34U, 0xB70BEBU, 0xBC104AU, 0xE4E295U, 0x8F7BECU, 0x96787FU, 0xD583B2U, 0x9E9740U, 0x870C5DU, 0xECFFA6U, + 0xF4E433U, 0xBF35F8U, 0xE00F8DU, 0x699C16U, 0x3265EBU, 0x1B6638U, 0x40F515U, 0x0A8DC6U, 0x131E1BU, 0x5845A0U, + 0x21F670U, 0x2A6E1FU, 0x791D8EU, 0x708651U, 0x2AD7E8U, 0xE37CAFU, 0xD8EE56U, 0x97B3C1U, 0x8E0018U, 0xC51B6FU, + 0x9CC9E6U, 0xB67019U, 0xEF23C8U, 0xE498F2U, 0xBF9927U, 0xF643ECU, 0xCD7051U, 0x04E902U, 0x563AFFU, 0x5D006CU, + 0x04D3A1U, 0x0FCA9AU, 0x72794FU, 0x39A2B4U, 0x228231U, 0x6A19EAU, 0x714E96U, 0x18F705U, 0x4324FCU, 0xC83E3BU, + 0x918D02U, 0xDADCD5U, 0xC2470CU, 0xA135B3U, 0xBABCF2U, 0xF30F4DU, 0xA8549EU, 0xA1C543U, 0xDEFF78U, 0xD42CBCU, + 0x0DB747U, 0x46C6D2U, 0x5F5C89U, 0x144F60U, 0x6FA6F7U, 0x66350EU, 0x2C0A59U, 0x35DAE0U, 0x7EC12FU, 0x0D32FEU, + 0x0429C1U, 0x5FB911U, 0xD642AEU, 0x895167U, 0xC3D8B0U, 0xFAAB89U, 0xB1315AU, 0xA8C0A7U, 0xE3DB24U, 0xB84879U, + 0x913382U, 0xCBA317U, 0x82F8FCU, 0x994BA9U, 0x50C213U, 0x4390CEU, 0x282F5DU, 0x713E30U, 0x7FCDE3U, 0x26565EU, + 0x2D0485U, 0x56BDD4U, 0x1FAE7BU, 0x0475AAU, 0x4DD555U, 0x17CE4CU, 0x9C1D9BU, 0xE52473U, 0xEEF7E4U, 0xB7CD1DU, + 0xF45E42U, 0xEF87E3U, 0x87B43CU, 0x986FADU, 0xD16FD2U, 0x8AD403U, 0x8103A8U, 0xD83A75U, 0x33A826U, 0x2BF39BU, + 0x604049U, 0x7B99A4U, 0x328ABFU, 0x49306AU, 0x407191U, 0x1BEA04U, 0x19D96FU, 0x4001F2U, 0x0FB201U, 0x36E9DCU, + 0xFD7ADFU, 0xE64326U, 0xAF91F9U, 0xF51249U, 0xDC2B16U, 0x87F8D7U, 0xCCE668U, 0xC517B1U, 0x9E8C46U, 0x97BF5FU, + 0xED6498U, 0xA67461U, 0x378FF6U, 0x788C8FU, 0x611514U, 0x0AE6F1U, 0x53FC2BU, 0x596F3EU, 0x0216C5U, 0x4B8508U, + 0x507FBBU, 0x396EE6U, 0x22F535U, 0xE99688U, 0xB10F43U, 0xBA1D36U, 0xC3E2ADU, 0xC07178U, 0x9B28C3U, 0xD69A8BU, + 0xCD817CU, 0x8570E5U, 0xFEEB12U, 0xF5E8CBU, 0xAC10C4U, 0x270335U, 0x7ED8EAU, 0x156B5BU, 0x0E7A14U, 0x46A0C5U, + 0x5D937AU, 0x144AA3U, 0x4F79D5U, 0x6CF35CU, 0x31228FU, 0x7A1932U, 0x628E69U, 0xA9D59CU, 0x926517U, 0xDBBEE2U, + 0x80ADB9U, 0x891424U, 0xD246D7U, 0xD8ED1AU, 0xA17C28U, 0xEA27F5U, 0xF3942EU, 0xB8CE8FU, 0xAB5FD0U, 0x466461U, + 0x1CB7BEU, 0x152F6FU, 0x4E1CC0U, 0x05D799U, 0x1CE66EU, 0x773DF7U, 0x7EAB00U, 0x249048U, 0x6D41D7U, 0x765A26U, + 0x1DA9F9U, 0x8431C8U, 0xCF0203U, 0x96C1DEU, 0x90D86DU, 0xCB6A30U, 0xA23193U, 0xB9A24EU, 0xF05B95U, 0xEB48A0U, + 0xA0D27AU, 0xD8A39FU, 0xD33804U, 0x0A9B79U, 0x01C3AAU, 0x5A5437U, 0x132FD4U, 0x28BC0DU, 0x60253AU, 0x3F57E3U, + 0x3CCC7CU, 0x65DD9DU, 0x4E26C2U, 0x172572U, 0xDCDDADU, 0xC64E64U, 0x8F5553U, 0x94A68AU, 0xFDBE7DU, 0xA66DE4U, + 0xADD68BU, 0xF4C75AU, 0xFE0CC1U, 0x873E34U, 0xC8A72FU, 0xDBD0C2U, 0x124B10U, 0x49998DU, 0x40A8FEU, 0x3A3323U, + 0x316088U, 0x68D95DU, 0x235B06U, 0x3A00B3U, 0x51B178U, 0x4AEA89U, 0x025816U, 0x59C36FU, 0xD092B8U, 0x8B2930U, + 0xE43AC7U, 0xF5E2DEU, 0xBEC121U, 0xA71AF0U, 0xED8B7FU, 0x94B40EU, 0x9F66D1U, 0xD45D68U, 0xCD8CBFU, 0x8617F6U, + 0x5F2545U, 0x75FC98U, 0x2EFF62U, 0x674467U, 0x7C959CU, 0x318F09U, 0x0A7CD2U, 0x4967AFU, 0x11D62CU, 0x1A8CD1U, + 0x431F02U, 0x48A69DU, 0xB3E5ECU, 0xFA7623U, 0xE10E9AU, 0xA99948U, 0xB20215U, 0xD971A6U, 0x80E86BU, 0x8BDA90U, + 0xD60185U, 0x9D907EU, 0x8FFBFBU, 0xE66920U, 0x7D705DU, 0x3483CEU, 0x6F9833U, 0x646BF1U, 0x1DF3E8U, 0x17E017U, + 0x4E1BC6U, 0x050A79U, 0x1E8038U, 0x5773E7U, 0x2C685EU, 0xA1BD89U, 0xFB86B0U, 0xF01477U, 0xA16D8EU, 0xCAFE19U, + 0xD365C1U, 0x9815AEU, 0x839E3FU, 0xCBCDC4U, 0x907611U, 0xB9E70AU, 0xE2BDE7U, 0x2B0E34U, 0x301789U, 0x7BE4DAU, + 0x477707U, 0x0C2FACU, 0x558C79U, 0x5E9743U, 0x0D4496U, 0x04786DU, 0x7FABE0U, 0x3730B3U, 0x3C014AU, 0xE7DADDU, + 0xEEE834U, 0x956163U, 0xDCB2FAU, 0xC78905U, 0x8D5BD4U, 0xD0427BU, 0xDBF12BU, 0xA22AB4U, 0xA93B4DU, 0xFA819AU, + 0xB3D2B3U, 0x287B64U, 0x40289DU, 0x5BB206U, 0x100153U, 0x495CB8U, 0x42CF2DU, 0x3BF4D6U, 0x70248BU, 0x6ABF19U, + 0x23CCF4U, 0x3C4527U, 0x75761AU, 0x8EACC1U, 0x853F44U, 0xD44EBFU, 0xDED5EEU, 0x87C751U, 0xEC3E80U, 0xF72D6FU, + 0xBEB676U, 0xE557A1U, 0xEC4D59U, 0xB6BECEU, 0x9DA527U, 0x443078U, 0x0BCAE9U, 0x12D916U, 0x594087U, 0x6033E8U, + 0x22A831U, 0x7948A2U, 0x70535FU, 0x2BC01CU, 0x62BBA1U, 0x592A7BU, 0x92308EU, 0x8AC395U, 0xC15A50U, 0x9809ABU, + 0xB3B336U, 0xECB245U, 0xE54998U, 0xBEDA1BU, 0xF681E6U, 0xED35F5U, 0x8E2E0CU, 0x87FDD3U, 0x5CC453U, 0x1556ACU, + 0x0E85FDU, 0x64AC42U, 0x3D7F8BU, 0x36447CU, 0x6FD665U, 0x640FB2U, 0x3B3C4BU, 0x52A7C4U, 0x48F7B5U, 0x014C2EU, + 0x9A9FFBU, 0xD19601U, 0xA0250CU, 0xAB7FFFU, 0xF2C822U, 0xB8D1B1U, 0xA302CCU, 0xEAB907U, 0xD1E9B2U, 0x987269U, + 0xC3411CU, 0xCC8897U, 0x141A42U, 0x3F61B8U, 0x66F2A1U, 0x2DCB56U, 0x3618DFU, 0x778208U, 0x2CB3F1U, 0x0468EEU, + 0x5F7B1FU, 0x5693D0U, 0x0D8041U, 0x461B3EU, 0xFFECE7U, 0xB4FD50U, 0xA94798U, 0xE314CFU, 0xB88D76U, 0xB17EADU, + 0xCA7508U, 0xC3E553U, 0x989EA6U, 0xDB0D3DU, 0xC396E8U, 0xA8E683U, 0x717D1EU, 0x7A0EEDU, 0x219730U, 0x288422U, + 0x736ECFU, 0x1BFF14U, 0x04A4A1U, 0x4F177AU, 0x56092BU, 0x1DD884U, 0x64635DU, 0xEF70EAU, 0xA589B3U, 0xF49B54U, + 0xFF50CDU, 0xA66312U, 0x8DFA62U, 0xD628FDU, 0x9F131CU, 0x8582C3U, 0xCCF9DAU, 0xF36A29U, 0xB8B2F4U, 0x618157U, + 0x6A020AU, 0x335999U, 0x79E864U, 0x4272BFU, 0x03259AU, 0x189C40U, 0x51CFB5U, 0x0A752EU, 0x216463U, 0x79BF90U, + 0x721C0DU, 0xAB47FEU, 0xE4D727U, 0xFDEC28U, 0x963FD9U, 0x8DA646U, 0xC594B7U, 0x9E4FE8U, 0x977E60U, 0xECA597U, + 0xAF264EU, 0xB61C79U, 0xFDCDA0U, 0x65D64FU, 0x2E61DCU, 0x553881U, 0x5CAA72U, 0x0351FBU, 0x0A400CU, 0x51FB55U, + 0x3BB9CAU, 0x22223AU, 0x6993B5U, 0x30C8C4U, 0x3B5B1BU, 0xE02B82U, 0xC1B075U, 0x9B23BCU, 0xD25A8BU, 0xC9C852U, + 0x82A3A9U, 0xBB303CU, 0xF42977U, 0xADDA82U, 0xA64418U, 0xFC55E5U, 0xB5AEE6U, 0x0EBD3BU, 0x4765C8U, 0x4CD655U, + 0x17DD2EU, 0x562EEBU, 0x6C3770U, 0x25A585U, 0x3E5EDEU, 0x754F6FU, 0x2C94A1U, 0x23A758U, 0x5A3F4FU, 0xD07C96U, + 0x8BC761U, 0xC254E8U, 0xD92C97U, 0xB0BF06U, 0xEBE0D9U, 0xE25138U, 0xB8CAA7U, 0xBB98DEU, 0xE22109U, 0x896291U, + 0x10F172U, 0x5BCB2FU, 0x401A94U, 0x0CA141U, 0x77B2BAU, 0x7E6BBFU, 0x255964U, 0x6E82D9U, 0x77130AU, 0x3C3877U, + 0x04EAF4U, 0x4FD129U, 0x9C40DBU, 0x959BC6U, 0xCEAC2DU, 0xE774FCU, 0xBC6763U, 0xF6DC12U, 0xEB8DCDU, 0xA00664U, + 0xF9F4B3U, 0xD2EF4AU, 0x895E5DU, 0x800584U, 0x5A972BU, 0x132EFBU, 0x287D84U, 0x63E615U, 0x7297CEU, 0x391D23U, + 0x608E30U, 0x6AF5CDU, 0x11641EU, 0x5C5E93U, 0x4789E0U, 0x0E903DU, 0x956386U, 0xFEF053U, 0xB6E879U, 0xAD0BACU, + 0xE41077U, 0xFF83CAU, 0xB47A99U, 0xCD6870U, 0xCE93E7U, 0x96823EU, 0x9D1941U, 0xC4EBD0U, 0x2BF23FU, 0x3031EEU, + 0x790A71U, 0x229909U, 0x2AC1CEU, 0x717677U, 0x5AEDA0U, 0x039C99U, 0x480646U, 0x515587U, 0x1AEC3CU, 0x296F69U, + 0xE13492U, 0xBA8607U, 0xB39FCCU, 0xEC4CB1U, 0xA77723U, 0x9EA7DEU, 0xD51C0DU, 0xCD0F00U, 0x86D4FBU, 0xDDF56EU, + 0xF46F95U, 0x2FBCD4U, 0x268D6BU, 0x7D52B2U, 0x374165U, 0x26F9DCU, 0x4D2A9BU, 0x141163U, 0x1FD2FCU, 0x40CA2DU, + 0x497952U, 0x3322D3U, 0x7AB32CU, 0xE108F5U, 0xAA5AE2U, 0xB3E31BU, 0xF8B098U, 0x812B65U, 0x8B8936U, 0xD0D08AU, + 0xD94341U, 0x8A7894U, 0xE3A9AFU, 0xF8377AU, 0xB74481U, 0x6FDD0CU, 0x64EE5FU, 0x3D35A2U, 0x163731U, 0x5F8ECCU, + 0x045DC7U, 0x0F4616U, 0x57B6E8U, 0x7CAD79U, 0x253E86U, 0x6EC7CFU, 0x7DD478U, 0xB426A1U, 0xCF2D76U, 0xC3BC5FU, + 0x984780U, 0x935571U, 0xCACCEEU, 0x81BBBFU, 0xB82054U, 0xF371C0U, 0xE9CB3BU, 0xA05826U, 0xFB33F5U, 0x52A218U, + 0x09B88BU, 0x424BF6U, 0x53D22DU, 0x198198U, 0x043A53U, 0x6F2A06U, 0x34F1BDU, 0x3DC260U, 0x664982U, 0x6FB81BU, + 0x15A24CU, 0xDE71F5U, 0xC7482AU, 0x8CDFCBU, 0x9505D4U, 0xDE3405U, 0xA5EFFAU, 0xA4FC63U, 0xFE5704U, 0xB387DDU, + 0xA8BC6AU, 0xC32FB2U, 0x5A7EE5U, 0x11C44CU, 0x489797U, 0x420E62U, 0x19BD79U, 0x30E6BCU, 0x6B6407U, 0x225DDAU, + 0x398EA9U, 0x703534U, 0x0A64F7U, 0x09FA0AU, 0xD4C910U, 0xDF10E5U, 0x86833EU, 0xCDB99BU, 0xE67A40U, 0xBE631BU, + 0xB590AEU, 0xEC8B75U, 0xA73BD0U, 0x9CE08BU, 0xD5F35EU, 0x8E0AE5U, 0x061828U, 0x5D835AU, 0x5660C7U, 0x277914U, + 0x68CAE9U, 0x7190E2U, 0x3A0113U, 0x20FECCU, 0x49ED7DU, 0x127522U, 0x1B06ABU, 0x40855CU, 0x8B9E85U, 0x926FB2U, + 0xF8F56AU, 0xE186A5U, 0xAA1F14U, 0xF10CCBU, 0xF0F7BAU, 0x8F6735U, 0x867CECU, 0xDC9F1FU, 0x978402U, 0x8E54F1U, + 0x45EF3CU, 0x7CFC8FU, 0x3705D2U, 0x6C1248U, 0x64C8BDU, 0x3FF976U, 0x566243U, 0x4DA198U, 0x069B45U, 0x1F0AF6U, + 0x5851BBU, 0x00E248U, 0xAB3BD1U, 0xF2090EU, 0xF9926FU, 0xA2C3F1U, 0xEB7800U, 0xD07B9FU, 0x98A1E6U, 0xC31021U, + 0xC84BB8U, 0x91D84FU, 0x9AEC96U, 0x6337A9U, 0x288468U, 0x369FB3U, 0x774E06U, 0x6C645DU, 0x05B7A9U, 0x4E2E22U, + 0x551DFFU, 0x1CC78CU, 0x47D611U, 0x4F2DF2U, 0x343E6FU, 0xBF8514U, 0xE655C1U, 0xAD5E5AU, 0xB4EDBFU, 0xDFB4E4U, + 0xC1265DU, 0x80DD8BU, 0xDBC852U, 0xD25375U, 0x8920ACU, 0xA2BA53U, 0xFB0BC2U, 0x31401DU, 0x28D33CU, 0x63AAE3U, + 0x18381AU, 0x11238DU, 0x4AD2E4U, 0x434933U, 0x195BABU, 0x56A058U, 0x6FB105U, 0x2C5AAEU, 0x35C97BU, 0xFED9A0U, + 0xA52295U, 0x8D314EU, 0xD6ECA3U, 0x9F5E30U, 0x84456DU, 0xCFB6DEU, 0xD6AF03U, 0xBD2CE9U, 0xE556FCU, 0xEEC707U, + 0xB71CD6U, 0x382F59U, 0x43B720U, 0x02E4F7U, 0x195F4EU, 0x51CC99U, 0x0AA550U, 0x013767U, 0x786CBEU, 0x73DD01U, + 0x2AC6D1U, 0x61159EU, 0x7BA92FU, 0x92BAF4U, 0x896109U, 0xC0521AU, 0x9F9AF7U, 0x942924U, 0xC532B9U, 0xEFE3C2U, + 0xA6D807U, 0xFD0ABCU, 0xF69369U, 0xAFA033U, 0x44738EU, 0x5D694DU, 0x17C8F0U, 0x0C93A3U, 0x45207AU, 0x1EF9C5U, + 0x37EB04U, 0x6850FBU, 0x6305EAU, 0x3B9E15U, 0x782DC4U, 0x41774BU, 0x8AF633U, 0xD18DE4U, 0xD81E5DU, 0x83A69AU, + 0x8AF583U, 0xF06E7CU, 0xBB5FADU, 0xA28416U, 0xE99653U, 0xF06D88U, 0x9FEC35U, 0xC4F7E6U, 0x4C059AU, 0x1F1C19U, + 0x56EFC4U, 0x4D743FU, 0x24612AU, 0x3F9BD1U, 0x748814U, 0x2C13AFU, 0x27F276U, 0x5EE861U, 0x553B88U, 0x0E0A5FU, + 0xC791E6U, 0xD8E2B0U, 0x907A69U, 0xABE9C6U, 0xE09217U, 0xB10168U, 0xBA48F9U, 0xE3FA26U, 0x8861CFU, 0x9230D8U, + 0xDB8B21U, 0xC099B2U, 0x09644FU, 0x52F704U, 0x79AC90U, 0x201F6BU, 0x2E17BEU, 0x77C495U, 0x3CFF48U, 0x172E9BU, + 0x4E9426U, 0x0D8775U, 0x145E98U, 0x5E6D03U, 0xC5F6D6U, 0xAC242DU, 0xF70D3CU, 0xFEDED2U, 0xA5C543U, 0xAE74BCU, + 0xD62EE5U, 0x9D9D72U, 0x80029BU, 0xCB534CU, 0x90E175U, 0x19BAAAU, 0x6A3B6BU, 0x6280D4U, 0x39D385U, 0x724B7AU, + 0x6B78E2U, 0x00A321U, 0x19101CU, 0x5248CFU, 0x0ADB30U, 0x01F0A9U, 0x5A21CEU, 0xB73A17U, 0xACC880U, 0xE55179U, + 0xFE42A6U, 0xB4B987U, 0xC5AF58U, 0xCE1688U, 0x97C533U, 0x9CCE76U, 0xC73F8DU, 0x8E2510U, 0xB4B6C3U, 0x7D4FFEU, + 0x665C3DU, 0x2DC7C0U, 0x70B55BU, 0x5B2C2EU, 0x025FF5U, 0x49D470U, 0x53448AU, 0x1A3FD7U, 0x09AC64U, 0x60BDBDU, + 0x3B467AU, 0xB0D043U, 0xE98B9CU, 0xE33A2DU, 0x9A21E2U, 0xD1C3B3U, 0xCA5A0CU, 0x8709DDU, 0xDCB222U, 0xF5A3AAU, + 0xBF79DDU, 0xA44A04U, 0xEDD193U, 0x3E006AU, 0x373B21U, 0x4CF994U, 0x47C04FU, 0x1F53DAU, 0x5488A1U, 0x4DB86CU, + 0x2623DFU, 0x7D7402U, 0x70CF50U, 0x2B9EFDU, 0x232426U, 0xF8A7D3U, 0x91FEC8U, 0x8A4D39U, 0xC117F6U, 0xD0866FU, + 0x9B3D18U, 0xE36EC1U, 0xE8F576U, 0xB3C5BFU, 0xBA1629U, 0xE1BD50U, 0xA8EC8FU, 0x17763EU, 0x5D45F1U, 0x049CA0U, + 0x0F8F1FU, 0x5630C6U, 0x7DE225U, 0x26FB38U, 0x6F08CBU, 0x7D0316U, 0x34B28DU, 0x2F68E9U, 0xC47B72U, 0x9DC287U, + 0x96915CU, 0xCF0B41U, 0x85F8A2U, 0xBAE17FU, 0xF372CCU, 0xE81991U, 0xA1894AU, 0xFAF2EBU, 0xF16134U, 0x89F845U, + 0x0A8ADBU, 0x53153AU, 0x1806E5U, 0x03FF7CU, 0x6A7C0BU, 0x312692U, 0x399775U, 0x628CACU, 0x6D7FB3U, 0x34EE42U, + 0x5FF49DU, 0x56073CU, 0x8D1C67U, 0x87CDBBU, 0xDEE708U, 0xB574D5U, 0xA4ADB6U, 0xEF9E2BU, 0xF605D0U, 0xBD7545U, + 0xE6EE0EU, 0xCE39FBU, 0x950260U, 0xD8929DU, 0x43D9CEU, 0x086A47U, 0x31B3B1U, 0x7AA068U, 0x221ADFU, 0x294B86U, + 0x72F049U, 0x73E3F8U, 0x083927U, 0x418856U, 0x5AC3C9U, 0x105020U, 0xC969B7U, 0xE2BBEEU, 0xBF2019U, 0xB41181U, + 0xEFCA6AU, 0xA6FD3FU, 0xBC27A4U, 0xD53651U, 0xCE9D9AU, 0x854EA7U, 0xDC5E74U, 0xDFE5A9U, 0x26B61AU, 0x6C0D57U, + 0x77DCECU, 0x3EC639U, 0x2575C3U, 0x682CD6U, 0x13AF1DU, 0x1855ECU, 0x404473U, 0x4BDF8AU, 0x12ACDDU, 0xF93754U, + 0xE207A3U, 0xABD87AU, 0xF04B45U, 0xF03284U, 0xABB05BU, 0x80ABEBU, 0xD95AB4U, 0x92C10DU, 0x8FD2CEU, 0xC42833U, + 0xEC3920U, 0x37C2FDU, 0x7C5106U, 0x654883U, 0x2EAAF8U, 0x37B12DU, 0x5C20B6U, 0x065B42U, 0x07C909U, 0x5C12B4U, + 0x152367U, 0x2EB4FAU, 0x65CF19U, 0xFC5F40U, 0xB294FFU, 0xEBA72EU, 0xE03ED1U, 0x9B6CD0U, 0x92D70FU, 0xC944F6U, + 0x801D60U, 0x9AAE19U, 0xF1F4DEU, 0xA85547U, 0xAB4EB8U, 0x729DE9U, 0x792456U, 0x223697U, 0x4BED0CU, 0x55DE71U, + 0x1C03A2U, 0x07910FU, 0x4CAADCU, 0x356BA0U, 0x3E5033U, 0x67C3EEU, 0x2D9B05U, 0xB62810U, 0xFFF3EBU, 0xC4E03EU, + 0x8558A5U, 0xDE0B48U, 0xD5905BU, 0x8D71A2U, 0xA26A75U, 0xFBD8ECU, 0xB08982U, 0xAB1253U, 0xE2A1ECU, 0x79FB3FU, + 0x116E52U, 0x4A15C9U, 0x43861CU, 0x188FE7U, 0x537DF2U, 0x62E619U, 0x29D7C0U, 0x310C57U, 0x7A1F2EU, 0x25E5B8U, + 0xAC7451U, 0xC76F86U, 0xDE9C9FU, 0x959460U, 0xCF27B1U, 0xC6FC1EU, 0xBDEDCFU, 0xF416B0U, 0xEF0429U, 0xA49FEEU, + 0xBDEA17U, 0xFF7104U, 0x06A3F8U, 0x0D8A63U, 0x5219A6U, 0x5B62DDU, 0x00F348U, 0x6969B3U, 0x731A6EU, 0x38816DU, + 0x61D090U, 0x6A6343U, 0x33F9FEU, 0x18B8A5U, 0xC30340U, 0x8B10DAU, 0x98E80BU, 0xD1FB74U, 0xEA20F5U, 0xA5930AU, + 0xFC8E93U, 0xF75CC4U, 0xAF673DU, 0xA4E6BAU, 0xDF3D43U, 0x960F9CU, 0x0DD68DU, 0x44E572U, 0x1F7EB2U, 0x35AD09U, + 0x6C9554U, 0x6746A7U, 0x365D3AU, 0x7DFCF9U, 0x64A6C4U, 0x0B351FU, 0x118CEAU, 0x58DF61U, 0x836434U, 0x8A36CFU, + 0xF1AB5BU, 0xBA18A0U, 0xA343EDU, 0xE8C27EU, 0xF0F887U, 0xBB2B50U, 0xC03A69U, 0xC9C1A6U, 0x9A5317U, 0x9368C8U, + 0x5CB919U, 0x26A226U, 0x2F01EFU, 0x74D919U, 0x3DCA80U, 0x2631D7U, 0x6D223EU, 0x54BAA1U, 0x1E4950U, 0x47520BU, + 0x4CA79EU, 0x97BC75U, 0xBE3EA8U, 0xED479BU, 0xA4D446U, 0xBA4FF5U, 0xF13C39U, 0xE8A46AU, 0x83D7D7U, 0xDA4C0CU, + 0xD1DDF9U, 0x8AA7F2U, 0xC22427U, 0x793DDCU, 0x30CE45U, 0x2B5522U, 0x6007FBU, 0x39BE6CU, 0x32AD95U, 0x42560BU, + 0x4D426AU, 0x16D1B5U, 0x5F3A04U, 0x442BDBU, 0x2DF082U, 0xF6C225U, 0xFE59FCU, 0xA5880FU, 0xAEB312U, 0xF761C9U, + 0x9C582CU, 0x85CBB7U, 0xCE00C3U, 0xD43118U, 0x9DAB9DU, 0xEAF866U, 0xE3437BU, 0x381288U, 0x738955U, 0x6A3BF6U, + 0x2066ABU, 0x19D570U, 0x52DEC1U, 0x090E1EU, 0x00B5FFU, 0x5BE6E1U, 0x727D38U, 0x284CCFU, 0x639656U, 0xFA8531U, + 0xBD3CA8U, 0xD4EF77U, 0xCFC586U, 0x841489U, 0x9C0F78U, 0xD7BCA7U, 0x8E671EU, 0xA5774DU, 0xFE8481U, 0xF79F32U, + 0xAC0AEFU, 0x65F09CU, 0x5FF301U, 0x144ACAU, 0x0D193FU, 0x468224U, 0x13F0D1U, 0x18694AU, 0x63FA87U, 0x2B81F4U, + 0x30106DU, 0x790A9BU, 0xE2E952U, 0x8970CDU, 0xD003BCU, 0xDB9963U, 0x838AD2U, 0x88731DU, 0xD1E064U, 0xBAFFF3U, + 0xA10F2AU, 0xEC049DU, 0xBFD7D4U, 0xB7EE2BU, 0x4C7CBBU, 0x478760U, 0x1E9415U, 0x554D9EU, 0x4C7E6BU, 0x07E4B0U, + 0x3D35ADU, 0x741E4EU, 0x2F8D93U, 0x26FC20U, 0x7D667DU, 0x16B586U, 0x8B8E02U, 0xC91FD9U, 0xD0456CU, 0x9BF237U, + 0xC0EBCEU, 0xE92849U, 0xB29390U, 0xBBC3E7U, 0xE1787EU, 0xAA6B81U, 0x93B040U, 0xD8005FU, 0x411BAEU, 0x0AC870U, + 0x51F1D1U, 0x5D328EU, 0x362837U, 0x6799E0U, 0x6C4239U, 0x37711AU, 0x3EABC7U, 0x45BA3CU, 0x0D01A9U, 0x16D6F2U, + 0xDDCF17U, 0xC46D8CU, 0x8F3670U, 0xF6A723U, 0xFD5CBCU, 0xA74F5DU, 0xEAF582U, 0xF1A43BU, 0x903768U, 0x8B0CC5U, + 0xC0DC16U, 0x9957CBU, 0x1324F0U, 0x4ABD25U, 0x61AECEU, 0x38545AU, 0x73C701U, 0x68FEF4U, 0x212D6FU, 0x5B3382U, + 0x52C2D1U, 0x09494CU, 0x065ABFU, 0xDFA126U, 0x9CB149U, 0xA56A98U, 0xEE5927U, 0xF4C0F6U, 0xBD33B8U, 0xE62901U, + 0xCFB8D6U, 0x94D32FU, 0x9F40B8U, 0xC69AF1U, 0x8CAB0EU, 0x15309FU, 0x7E6360U, 0x21DA31U, 0x2848BAU, 0x733747U, + 0x72A6D4U, 0x08EDA8U, 0x435F7BU, 0x5A4CD6U, 0x119505U, 0x082658U, 0x433DE3U, 0xB8ED26U, 0xB0D6DDU, 0xEB05C8U, + 0xA2BC13U, 0xA9BEEAU, 0xD6656DU, 0xDF5614U, 0x848F82U, 0xC41C5BU, 0xDF26A4U, 0x94F7A5U, 0xADCC5AU, 0x665B8BU, + 0x3F1234U, 0x34A0EDU, 0x6E7BAAU, 0x076813U, 0x1CD1C4U, 0x55833DU, 0x4E1836U, 0x03A9E2U, 0x58F219U, 0x72418CU, + 0x2B09F7U, 0xA89A72U, 0xF1A1A9U, 0xBA7254U, 0x81EA47U, 0xC899BAU, 0xD20279U, 0x9B13C4U, 0xC0E09FU, 0xCB7E4BU, + 0xB25FF0U, 0xF98431U, 0xE4974EU, 0x2E6CD7U, 0x35FC00U, 0x5CE7A9U, 0x07147EU, 0x060D07U, 0x5D9F98U, 0x56E449U, + 0x0E65A6U, 0x659EB7U, 0x7C8D49U, 0x371790U, 0x6C6623U, 0xE5FD6EU, 0x9E6EBDU, 0x921600U, 0xC985D3U, 0x82DAEEU, + 0x9B7B25U, 0xD0E0F0U, 0xE1924BU, 0xAA091EU, 0xF158F5U, 0xF9E369U, 0x22F1BAU, 0x4B28C7U, 0x509B54U, 0x1B80BDU, + 0x024162U, 0x497B53U, 0x01A88CU, 0x3E1B5DU, 0x7502F2U, 0x6CD12BU, 0x27EB1CU, 0x7E7AC5U, 0xDDA113U, 0x8596BAU, + 0xCE5EEDU, 0xD54D14U, 0x9CF68BU, 0x87A54AU, 0xEE1C31U, 0xB58EA4U, 0xBFD55FU, 0xE66482U, 0xE93FA1U, 0x90AD7CU, + 0x5B04EFU, 0x405713U, 0x09CC48U, 0x13BFEDU, 0x522736U, 0x2914E3U, 0x22CFD8U, 0x7B5E05U, 0x3061E6U, 0x29B37FU, + 0x43BAA8U, 0x5849D1U, 0x91D25EU, 0xCEE0AFU, 0xC73971U, 0x9C2A40U, 0xB7919FU, 0xEF401EU, 0xA452E1U, 0xB5B9B8U, + 0xFEA80FU, 0x8533D6U, 0x8C4115U, 0xD7DA28U, 0x5F6BF3U, 0x043006U, 0x4FA39DU, 0x76DBD9U, 0x394C22U, 0x20C7BFU, + 0x6BB64CU, 0x312C41U, 0x187FB2U, 0x43C46FU, 0x0A55F4U, 0x192E81U, 0xD2BC4AU, 0xCBA5FBU, 0xA15624U, 0xF85DFDU, + 0xF38ECBU, 0xBA3602U, 0xA125F5U, 0xCEFE6CU, 0x97CF3BU, 0x9D55C2U, 0xC4A64DU, 0x4FBFBCU, 0x1468A3U, 0x7D4352U, + 0x6ED19DU, 0x270804U, 0x7D3B76U, 0x76A0ABU, 0x0FF018U, 0x0443D5U, 0x5D188EU, 0x16A93BU, 0x0932E0U, 0xC07015U, + 0xFACB1EU, 0xB39AC3U, 0xE80170U, 0xE3B3ADU, 0xBAEA5EU, 0xD17956U, 0xC042A9U, 0x8A9378U, 0x912DE7U, 0xD86E86U, + 0x83F559U, 0x2AC4E8U, 0x711F37U, 0x7A0D6EU, 0x26B4C9U, 0x6D6710U, 0x547CE7U, 0x1F8CFEU, 0x449720U, 0x4D3483U, + 0x16EF5AU, 0x1EFE2DU, 0x6D44B4U, 0xA6174BU, 0xBF8E8AU, 0xF4FD95U, 0xED6764U, 0x86D6BBU, 0xDC8912U, 0xD10A45U, + 0x8A799CU, 0x83E12AU, 0xF872F3U, 0xB10954U, 0xAA980DU, 0x6083D6U, 0x397163U, 0x3AE8B8U, 0x439BDDU, 0x481046U, + 0x1302BBU, 0x5AFB68U, 0x50E875U, 0x297396U, 0x26824AU, 0x7D98F1U, 0x344BA4U, 0xAF726FU, 0xE6F5DAU, 0x9C0F01U, + 0x971C38U, 0xCE85EFU, 0xC5F626U, 0x946D91U, 0xFFBDC8U, 0xE48637U, 0xAC15A6U, 0xB74C48U, 0x7EEE99U, 0x21B586U, + 0x0A0677U, 0x539FA8U, 0x18CC01U, 0x007652U, 0x4B67CFU, 0x70B43CU, 0x390FF1U, 0x625ECAU, 0x6BD01FU, 0x38E3C4U, + 0xB23870U, 0xCB893BU, 0x8093C6U, 0x994055U, 0xD679A8U, 0x8DAAFBU, 0xA4B176U, 0xFE018DU, 0xF7CA5CU, 0xACD963U, + 0xE762B2U, 0xFE323DU, 0x1589C4U, 0x0C5A92U, 0x4F432BU, 0x17F0ECU, 0x1CAA35U, 0x673B82U, 0x6E54DBU, 0x31C724U, + 0x785CA5U, 0x632C5AU, 0x29B70BU, 0x508490U, 0xDB5D6DU, 0x82CFEEU, 0x89B492U, 0xD22541U, 0xBB2EDCU, 0xA1DD27U, + 0xE04F62U, 0xFB56D9U, 0xB0A50CU, 0xF9BED7U, 0xC24EFAU, 0x8F5529U, 0x55C6D0U, 0x5E3B47U, 0x07383FU, 0x2CA2F0U, + 0x75D161U, 0x3E489EU, 0x25BB0FU, 0x6DA170U, 0x7630B1U, 0x174B2EU, 0x4CD8D7U, 0x470180U, 0x9E2339U, 0xD5B8FEU, + 0xE9EB27U, 0xA2521DU, 0xB941C8U, 0xF0BB23U, 0xAB2AB6U, 0xA271CDU, 0xD9C250U, 0xD3DC83U, 0x8A1D6EU, 0x41A67DU, + 0x58B580U, 0x3B6C5BU, 0x205ECEU, 0x6985A5U, 0x333471U, 0x3E27CAU, 0x65FD13U, 0x0CCE44U, 0x1747EDU, 0x5C143AU, + 0x45AF83U, 0x8F7F54U, 0xF6643DU, 0xFDD7A2U, 0xA68E73U, 0xAF3D8CU, 0xF4E79DU, 0xB5E073U, 0x8F59AAU, 0xC40A3DU, + 0x999044U, 0x922197U, 0xCB7A3AU, 0x60E9E1U, 0x3B90B4U, 0x73020FU, 0x6839DAU, 0x21FA71U, 0x3A632CU, 0x5151DFU, + 0x088A43U, 0x039B80U, 0x4260FDU, 0x18F36EU, 0x33EB97U, 0xEE1848U, 0xE503C9U, 0xBCA4B6U, 0xF7FC67U, 0xEC6FD8U, + 0x849C01U, 0x9F8506U, 0xD616FFU, 0x8D6C29U, 0x86FD90U, 0xFF26CFU, 0xF4150EU, 0x2C9EB1U, 0x6FEE60U, 0x74751BU, + 0x39E696U, 0x429F65U, 0x4B0DB8U, 0x1056ABU, 0x1AE756U, 0x43FC9DU, 0x282F29U, 0x318172U, 0x7A90E7U, 0xE36B1CU, + 0xA878D9U, 0xF2A0E2U, 0xDB133FU, 0x8008ECU, 0xC1D955U, 0xDAE292U, 0x9570EBU, 0xAC9B74U, 0xE68A85U, 0xBF514BU, + 0x34635AU, 0x6FFAA5U, 0x66A93CU, 0x1D12CBU, 0x54C382U, 0x4ED915U, 0x056AECU, 0x5C2D37U, 0x779402U, 0x2607C9U, + 0x2D5D1CU, 0x72ECA6U, 0xBAB7FBU, 0xA12408U, 0xC89C85U, 0xD3CF76U, 0x98542BU, 0xC177B8U, 0xCAAE45U, 0xB29CCEU, + 0xB9473BU, 0xE2D660U, 0xABEDF1U, 0xA03F1EU, 0x7926C4U, 0x1A95F1U, 0x044E2AU, 0x4D49FFU, 0x56B154U, 0x1FA209U, + 0x6419FAU, 0x6F4867U, 0x36D394U, 0x3C2199U, 0x653842U, 0x2EABB7U, 0x95C02CU, 0xDC525CU, 0x87CB93U, 0x8EB80AU, + 0xD423FDU, 0xF152A4U, 0xAAC003U, 0xE15BDAU, 0xF82A0DU, 0xB3B134U, 0xAAB3EBU, 0xC14C0AU, 0x1B5D95U, 0x128664U, + 0x49357AU, 0x002DA3U, 0x3BDE40U, 0x70C5DDU, 0x6954AEU, 0x23AE73U, 0x76ADE8U, 0x7D760DU, 0x064756U, 0x0FDCE3U, + 0xD40E38U, 0x9D37F5U, 0x87E4C6U, 0xECDF1AU, 0xB54EA9U, 0xBE1470U, 0xE7B71FU, 0xEC288EU, 0xB77951U, 0xDFC3A0U, + 0xC490BFU, 0x89095EU, 0x1ABA81U, 0x51E118U, 0x2853EFU, 0x234AB6U, 0x7B8910U, 0x703AC9U, 0x2B6216U, 0x62F127U, + 0x59CAECU, 0x101B59U, 0x4B2082U, 0xC1F2FFU, 0x88696CU, 0xB358A1U, 0xFA9752U, 0xAD844FU, 0xA63CB4U, 0xFFEF60U, + 0xF5F4EBU, 0x8C059EU, 0xC71F45U, 0xDCACF0U, 0x95772BU, 0x4E6622U, 0x67CDD5U, 0x3D9F0CU, 0x3406BBU, 0x6F75F2U, + 0x24EE0DU, 0x3D7E9CU, 0x520542U, 0x4396B3U, 0x09ADBCU, 0x527C45U, 0x5BF292U, 0xA0810BU, 0xE91878U, 0xF20BB5U, + 0xB9F10EU, 0xA1E0DBU, 0xEA33C0U, 0x938835U, 0x989BFEU, 0xC36342U, 0xCA6011U, 0x95FBECU, 0xFD0A6FU, 0x6E11B2U, + 0x25C3C1U, 0x3CFA5CU, 0x7769A7U, 0x0EB266U, 0x058079U, 0x5E1988U, 0x167E17U, 0x0DE5EFU, 0x44B428U, 0x7F0E31U, + 0x349DC6U, 0xEDC41FU, 0xE277A0U, 0xBA6DE1U, 0x99BE1EU, 0xC2178FU, 0x8B4450U, 0x90FF39U, 0xD9EFAAU, 0x823457U, + 0xA88785U, 0xF1DE98U, 0xFA4D73U, 0x3377E6U, 0x68A41DU, 0x43AD48U, 0x1A1AD3U, 0x14C836U, 0x4DF1EDU, 0x0622D0U, + 0x173903U, 0x7C88FEU, 0x27527DU, 0x2E41A5U, 0xF4FADAU, 0xFDBB4BU, 0x8601A4U, 0xCDD235U, 0xD4CB6AU, 0x9F7893U, + 0x862304U, 0xCCB3EDU, 0xB388BAU, 0xBA5B23U, 0xE1C0DCU, 0xE8A00DU, 0x3B3F27U, 0x500CF2U, 0x48D509U, 0x034694U, + 0x5A3CC7U, 0x51AD6AU, 0x0AB6B9U, 0x234544U, 0x785E57U, 0x30DE9AU, 0x2B2561U, 0x6036F4U, 0xDDCF8FU, 0x96DD5BU, + 0xCF46D0U, 0xCCB729U, 0x96A4FEU, 0xDF3FC7U, 0xE44D10U, 0xADC4F9U, 0xB61366U, 0xFD2837U, 0xA4B888U, 0x8EC359U, + 0x5750B6U, 0x5C49AFU, 0x07BA79U, 0x4A2080U, 0x517307U, 0x38DA7EU, 0x62C9ADU, 0x611230U, 0x38A2CBU, 0x33F98EU, + 0x4A4A35U, 0x0153E0U, 0x98815BU, 0xD23A06U, 0xD929C5U, 0x80E079U, 0xEBD7AAU, 0xF20D57U, 0xBD1C4CU, 0xE6A78DU, + 0xEF7472U, 0x954CE3U, 0x9CDF9CU, 0xCF8455U, 0x0437C2U, 0x1DFE3BU, 0x56EC6CU, 0x6F57D5U, 0x25061BU, 0x7E95AAU, + 0x772FF5U, 0x2C7C24U, 0x05C59BU, 0x5A965AU, 0x111D21U, 0x892DBCU, 0xC2F26FU, 0x9B6192U, 0xB81891U, 0xE38B6EU, + 0xEA91B6U, 0xB16221U, 0xF9FB48U, 0xC2C8DFU, 0x890226U, 0x9013F9U, 0xDBE848U, 0x02FB07U, 0x0D62D6U, 0x77906DU, + 0x3E8BB0U, 0x2538C3U, 0x6C614EU, 0x77F39CU, 0x141861U, 0x4D0D7AU, 0x47968FU, 0x1EE544U, 0x157DD1U, 0xCEEEAAU, + 0xA7953FU, 0xBC06D4U, 0xF57E09U, 0xABED3AU, 0xA2EEE3U, 0xD91734U, 0xD2849CU, 0x8BDEC3U, 0xC06F32U, 0xD174ADU, + 0x9BA77CU, 0x201C93U, 0x690C8AU, 0x22F77DU, 0x3BF4E4U, 0x702933U, 0x4B9B5AU, 0x0380C1U, 0x585134U, 0x556AEEU, + 0x0EF9CBU, 0x45A310U, 0x7C12CDU, 0xB7C97EU, 0xAFEA23U, 0xEC72C0U, 0xB7215DU, 0x9E9A8EU, 0xC50BF3U, 0xCC5068U, + 0x97E28DU, 0xDDB916U, 0xC42846U, 0xAF93B9U, 0xF2D020U, 0x796CC7U, 0x223F9EU, 0x2BA429U, 0x5095F0U, 0x18473FU, + 0x03DC8EU, 0x40EFD1U, 0x593620U, 0x1225BFU, 0x6BCF76U, 0x605E40U, 0xBA6599U, 0xF3B66AU, 0xE8AEF7U, 0x851DBCU, + 0x9EC649U, 0xD5D5D2U, 0x8C2C27U, 0x863E7CU, 0xDFB5D9U, 0xF4C002U, 0xA55BDFU, 0xEEE9ECU, 0x75B020U, 0x3C23D3U, + 0x06584AU, 0x4FCB15U, 0x1453A4U, 0x1F306BU, 0x42AB9AU, 0x09FA05U, 0x30415CU, 0x7A53ABU, 0x61AA22U, 0x2839D5U, + 0xF3228CU, 0xDAD032U, 0x89C9E3U, 0x820A2CU, 0xCAB91DU, 0x91A4C6U, 0x985673U, 0xE34DB8U, 0xA8DCE5U, 0xB52756U, + 0xFE358BU, 0xE6EE78U, 0x0DDF75U, 0x5654AFU, 0x5F075AU, 0x04BFC1U, 0x0D2CB4U, 0x56577FU, 0x34C6C2U, 0x2D9D11U, + 0x662F48U, 0x3FB4FFU, 0x34E536U, 0x4F4E89U, 0xC61C58U, 0x988107U, 0xD132A7U, 0xCA6978U, 0x81D881U, 0xB8C296U, + 0xF3114FU, 0xAA2AA8U, 0xA0FB31U, 0xF37142U, 0xDA429FU, 0x819B24U, 0x4888E1U, 0x5333FAU, 0x1AE30FU, 0x40D0D5U, + 0x6F0B68U, 0x36182BU, 0x3DA0F6U, 0x646345U, 0x2F7898U, 0x14CDF3U, 0x5C9666U, 0xC704BDU, 0x8E7F4CU, 0xDDEED3U, + 0xD655BAU, 0xAF062DU, 0xE49EF5U, 0xFDFD02U, 0xB7661BU, 0xA8F7F4U, 0xC18D25U, 0x9A1E9AU, 0x9305CBU, 0x48F414U, + 0x43EFB5U, 0x1B1D6AU, 0x708413U, 0x698780U, 0x2A7C4DU, 0x6168BFU, 0x78F3A2U, 0x130059U, 0x0B1BCCU, 0x40CA07U, + 0x1FF072U, 0x9663E9U, 0xCD9A14U, 0xE499C7U, 0xBF0AEAU, 0xF57239U, 0xECE1E4U, 0xA7BA5FU, 0xDE098FU, 0xD591E0U, + 0x86E271U, 0x8F79AEU, 0xD52817U, 0x1C8350U, 0x2711A9U, 0x684C3EU, 0x71FFE7U, 0x3AE490U, 0x633619U, 0x498FE6U, + 0x10DC37U, 0x1B670DU, 0x4066D8U, 0x09BC13U, 0x328FAEU, 0xFB16FDU, 0xA9C500U, 0xA2FF93U, 0xFB2C5EU, 0xF03565U, + 0x8D86B0U, 0xC65D4BU, 0xDD7DCEU, 0x95E615U, 0x8EB169U, 0xE708FAU, 0xBCDB03U, 0x37C1C4U, 0x6E72FDU, 0x25232AU, + 0x3DB8F3U, 0x5ECA4CU, 0x45430DU, 0x0CF0B2U, 0x57AB61U, 0x5E3ABCU, 0x210087U, 0x2BD343U, 0xF248B8U, 0xB9392DU, + 0xA0A376U, 0xEBB09FU, 0x905908U, 0x99CAF1U, 0xD3F5A6U, 0xCA251FU, 0x813ED0U, 0xF2CD01U, 0xFBD63EU, 0xA046EEU, + 0x29BD51U, 0x76AE98U, 0x3C274FU, 0x055476U, 0x4ECEA5U, 0x573F58U, 0x1C24DBU, 0x47B786U, 0x6ECC7DU, 0x345CE8U, + 0x7D0703U, 0x66B456U, 0xAF3DECU, 0xBC6F31U, 0xD7D0A2U, 0x8EC1CFU, 0x80321CU, 0xD9A9A1U, 0xD2FB7AU, 0xA9422BU, + 0xE05184U, 0xFB8A55U, 0xB22AAAU, 0xE831B3U, 0x63E264U, 0x1ADB8CU, 0x11081BU, 0x4832E2U, 0x0BA1BDU, 0x10781CU, + 0x784BC3U, 0x679052U, 0x2E902DU, 0x752BFCU, 0x7EFC57U, 0x27C58AU, 0xCC57D9U, 0xD40C64U, 0x9FBFB6U, 0x84665BU, + 0xCD7540U, 0xB6CF95U, 0xBF8E6EU, 0xE415FBU, 0xE62690U, 0xBFFE0DU, 0xF04DFEU, 0xC91623U, 0x028520U, 0x19BCD9U, + 0x506E06U, 0x0AEDB6U, 0x23D4E9U, 0x780728U, 0x331997U, 0x3AE84EU, 0x6173B9U, 0x6840A0U, 0x129B67U, 0x598B9EU, + 0xC87009U, 0x877370U, 0x9EEAEBU, 0xF5190EU, 0xAC03D4U, 0xA690C1U, 0xFDE93AU, 0xB47AF7U, 0xAF8044U, 0xC69119U, + 0xDD0ACAU, 0x166977U, 0x4EF0BCU, 0x45E2C9U, 0x3C1D52U, 0x3F8E87U, 0x64D73CU, 0x296574U, 0x327E83U, 0x7A8F1AU, + 0x0114EDU, 0x0A1734U, 0x53EF3BU, 0xD8FCCAU, 0x812715U, 0xEA94A4U, 0xF185EBU, 0xB95F3AU, 0xA26C85U, 0xEBB55CU, + 0xB0862AU, 0x930CA3U, 0xCEDD70U, 0x85E6CDU, 0x9D7196U, 0x562A63U, 0x6D9AE8U, 0x24411DU, 0x7F5246U, 0x76EBDBU, + 0x2DB928U, 0x2712E5U, 0x5E83D7U, 0x15D80AU, 0x0C6BD1U, 0x473170U, 0x54A02FU, 0xB99B9EU, 0xE34841U, 0xEAD090U, + 0xB1E33FU, 0xFA2866U, 0xE31991U, 0x88C208U, 0x8154FFU, 0xDB6FB7U, 0x92BE28U, 0x89A5D9U, 0xE25606U, 0x7BCE37U, + 0x30FDFCU, 0x693E21U, 0x6F2792U, 0x3495CFU, 0x5DCE6CU, 0x465DB1U, 0x0FA46AU, 0x14B75FU, 0x5F2D85U, 0x275C60U, + 0x2CC7FBU, 0xF56486U, 0xFE3C55U, 0xA5ABC8U, 0xECD02BU, 0xD743F2U, 0x9FDAC5U, 0xC0A81CU, 0xC33383U, 0x9A2262U, + 0xB1D93DU, 0xE8DA8DU, 0x232252U, 0x39B19BU, 0x70AAACU, 0x6B5975U, 0x024182U, 0x59921BU, 0x522974U, 0x0B38A5U, + 0x01F33EU, 0x78C1CBU, 0x3758D0U, 0x242F3DU, 0xEDB4EFU, 0xB66672U, 0xBF5701U, 0xC5CCDCU, 0xCE9F77U, 0x9726A2U, + 0xDCA4F9U, 0xC5FF4CU, 0xAE4E87U, 0xB51576U, 0xFDA7E9U, 0xA63C90U, 0x2F6D47U, 0x74D6CFU, 0x1BC538U, 0x0A1D21U, + 0x413EDEU, 0x58E50FU, 0x127480U, 0x6B4BF1U, 0x60992EU, 0x2BA297U, 0x327340U, 0x79E809U, 0xA0DABAU, 0x8A0367U, + 0xD1009DU, 0x98BB98U, 0x836A63U, 0xCE70F6U, 0xF5832DU, 0xB69850U, 0xEE29D3U, 0xE5732EU, 0xBCE0FDU, 0xB75962U, + 0x4C1A13U, 0x0589DCU, 0x1EF165U, 0x5666B7U, 0x4DFDEAU, 0x268E59U, 0x7F1794U, 0x74256FU, 0x29FE7AU, 0x626F81U, + 0x700404U, 0x1996DFU, 0x828FA2U, 0xCB7C31U, 0x9067CCU, 0x9B940EU, 0xE20C17U, 0xE81FE8U, 0xB1E439U, 0xFAF586U, + 0xE17FC7U, 0xA88C18U, 0xD397A1U, 0x5E4276U, 0x04794FU, 0x0FEB88U, 0x5E9271U, 0x3501E6U, 0x2C9A3EU, 0x67EA51U, + 0x7C61C0U, 0x34323BU, 0x6F89EEU, 0x4618F5U, 0x1D4218U, 0xD4F1CBU, 0xCFE876U, 0x841B25U, 0xB888F8U, 0xF3D053U, + 0xAA7386U, 0xA168BCU, 0xF2BB69U, 0xFB8792U, 0x80541FU, 0xC8CF4CU, 0xC3FEB5U, 0x182522U, 0x1117CBU, 0x6A9E9CU, + 0x234D05U, 0x3876FAU, 0x72A42BU, 0x2FBD84U, 0x240ED4U, 0x5DD54BU, 0x56C4B2U, 0x057E65U, 0x4C2D4CU, 0xD7849BU, + 0xBFD762U, 0xA44DF9U, 0xEFFEACU, 0xB6A347U, 0xBD30D2U, 0xC40B29U, 0x8FDB74U, 0x9540E6U, 0xDC330BU, 0xC3BAD8U, + 0x8A89E5U, 0x71533EU, 0x7AC0BBU, 0x2BB140U, 0x212A11U, 0x7838AEU, 0x13C17FU, 0x08D290U, 0x414989U, 0x1AA85EU, + 0x13B2A6U, 0x494131U, 0x625AD8U, 0xBBCF87U, 0xF43516U, 0xED26E9U, 0xA6BF78U, 0x9FCC17U, 0xDD57CEU, 0x86B75DU, + 0x8FACA0U, 0xD43FE3U, 0x9D445EU, 0xA6D584U, 0x6DCF71U, 0x753C6AU, 0x3EA5AFU, 0x67F654U, 0x4C4CC9U, 0x134DBAU, + 0x1AB667U, 0x4125E4U, 0x097E19U, 0x12CA0AU, 0x71D1F3U, 0x78022CU, 0xA33BACU, 0xEAA953U, 0xF17A02U, 0x9B53BDU, + 0xC28074U, 0xC9BB83U, 0x90299AU, 0x9BF04DU, 0xC4C3B4U, 0xAD583BU, 0xB7084AU, 0xFEB3D1U, 0x656004U, 0x2E69FEU, + 0x5FDAF3U, 0x548000U, 0x0D37DDU, 0x472E4EU, 0x5CFD33U, 0x1546F8U, 0x2E164DU, 0x678D96U, 0x3CBEE3U, 0x337768U, + 0xEBE5BDU, 0xC09E47U, 0x990D5EU, 0xD234A9U, 0xC9E720U, 0x887DF7U, 0xD34C0EU, 0xFB9711U, 0xA084E0U, 0xA96C2FU, + 0xF27FBEU, 0xB9E4C1U, 0x001318U, 0x4B02AFU, 0x56B867U, 0x1CEB30U, 0x477289U, 0x4E8152U, 0x358AF7U, 0x3C1AACU, + 0x676159U, 0x24F2C2U, 0x3C6917U, 0x57197CU, 0x8E82E1U, 0x85F112U, 0xDE68CFU, 0xD77BDDU, 0x8C9130U, 0xE400EBU, + 0xFB5B5EU, 0xB0E885U, 0xA9F6D4U, 0xE2277BU, 0x9B9CA2U, 0x108F15U, 0x5A764CU, 0x0B64ABU, 0x00AF32U, 0x599CEDU, + 0x72059DU, 0x29D702U, 0x60ECE3U, 0x7A7D3CU, 0x330625U, 0x0C95D6U, 0x474D0BU, 0x9E7EA8U, 0x95FDF5U, 0xCCA666U, + 0x86179BU, 0xBD8D40U, 0xFCDA65U, 0xE763BFU, 0xAE304AU, 0xF58AD1U, 0xDE9B9CU, 0x86406FU, 0x8DE3F2U, 0x54B801U, + 0x1B28D8U, 0x0213D7U, 0x69C026U, 0x7259B9U, 0x3A6B48U, 0x61B017U, 0x68819FU, 0x135A68U, 0x50D9B1U, 0x49E386U, + 0x02325FU, 0x9A29B0U, 0xD19E23U, 0xAAC77EU, 0xA3558DU, 0xFCAE04U, 0xF5BFF3U, 0xAE04AAU, 0xC44635U, 0xDDDDC5U, + 0x966C4AU, 0xCF373BU, 0xC4A4E4U, 0x1FD47DU, 0x3E4F8AU, 0x64DC43U, 0x2DA574U, 0x3637ADU, 0x7D5C56U, 0x44CFC3U, + 0x0BD688U, 0x52257DU, 0x59BBE7U, 0x03AA1AU, 0x4A5119U, 0xF142C4U, 0xB89A37U, 0xB329AAU, 0xE822D1U, 0xA9D114U, + 0x93C88FU, 0xDA5A7AU, 0xC1A121U, 0x8AB090U, 0xD36B5EU, 0xDC58A7U, 0xA5C0B0U, 0x2F8369U, 0x74389EU, 0x3DAB17U, + 0x26D368U, 0x4F40F9U, 0x141F26U, 0x1DAEC7U, 0x473558U, 0x446721U, 0x1DDEF6U, 0x769D6EU, 0xEF0E8DU, 0xA434D0U, + 0xBFE56BU, 0xF35EBEU, 0x884D45U, 0x819440U, 0xDAA69BU, 0x917D26U, 0x88ECF5U, 0xC3C788U, 0xFB150BU, 0xB02ED6U, + 0x63BF24U, 0x6A6439U, 0x3153D2U, 0x188B03U, 0x43989CU, 0x0923EDU, 0x147232U, 0x5FF99BU, 0x060B4CU, 0x2D10B5U, + 0x76A1A2U, 0x7FFA7BU, 0xA568D4U, 0xECD104U, 0xD7827BU, 0x9C19EAU, 0x8D6831U, 0xC6E2DCU, 0x9F71CFU, 0x950A32U, + 0xEE9BE1U, 0xA3A16CU, 0xB8761FU, 0xF16FC2U, 0x6A9C79U, 0x010FACU, 0x491786U, 0x52F453U, 0x1BEF88U, 0x007C35U, + 0x4B8566U, 0x32978FU, 0x316C18U, 0x697DC1U, 0x62E6BEU, 0x3B142FU, 0xD40DC0U, 0xCFCE11U, 0x86F58EU, 0xDD66F6U, + 0xD53E31U, 0x8E8988U, 0xA5125FU, 0xFC6366U, 0xB7F9B9U, 0xAEAA78U, 0xE513C3U, 0xD69096U, 0x1ECB6DU, 0x4579F8U, + 0x4C6033U, 0x13B34EU, 0x5888DCU, 0x615821U, 0x2AE3F2U, 0x32F0FFU, 0x792B04U, 0x220A91U, 0x0B906AU, 0xD0432BU, + 0xD97294U, 0x82AD4DU, 0xC8BE9AU, 0xD90623U, 0xB2D564U, 0xEBEE9CU, 0xE02D03U, 0xBF35D2U, 0xB686ADU, 0xCCDD2CU, + 0x854CD3U, 0x1EF70AU, 0x55A51DU, 0x4C1CE4U, 0x074F67U, 0x7ED49AU, 0x7476C9U, 0x2F2F75U, 0x26BCBEU, 0x75876BU, + 0x1C5650U, 0x07C885U, 0x48BB7EU, 0x9022F3U, 0x9B11A0U, 0xC2CA5DU, 0xE9C8CEU, 0xA07133U, 0xFBA238U, 0xF0B9E9U, + 0xA84917U, 0x835286U, 0xDAC179U, 0x913830U, 0x822B87U, 0x4BD95EU, 0x30D289U, 0x3C43A0U, 0x67B87FU, 0x6CAA8EU, + 0x353311U, 0x7E4440U, 0x47DFABU, 0x0C8E3FU, 0x1634C4U, 0x5FA7D9U, 0x04CC0AU, 0xAD5DE7U, 0xF64774U, 0xBDB409U, + 0xAC2DD2U, 0xE67E67U, 0xFBC5ACU, 0x90D5F9U, 0xCB0E42U, 0xC23D9FU, 0x99B67DU, 0x9047E4U, 0xEA5DB3U, 0x218E0AU, + 0x38B7D5U, 0x732034U, 0x6AFA2BU, 0x21CBFAU, 0x5A1005U, 0x5B039CU, 0x01A8FBU, 0x4C7822U, 0x574395U, 0x3CD04DU, + 0xA5811AU, 0xEE3BB3U, 0xB76868U, 0xBDF19DU, 0xE64286U, 0xCF1943U, 0x949BF8U, 0xDDA225U, 0xC67156U, 0x8FCACBU, + 0xF59B08U, 0xF605F5U, 0x2B36EFU, 0x20EF1AU, 0x797CC1U, 0x324664U, 0x1985BFU, 0x419CE4U, 0x4A6F51U, 0x13748AU, + 0x58C42FU, 0x631F74U, 0x2A0CA1U, 0x71F51AU, 0xF9E7D7U, 0xA27CA5U, 0xA99F38U, 0xD886EBU, 0x973516U, 0x8E6F1DU, + 0xC5FEECU, 0xDF0133U, 0xB61282U, 0xED8ADDU, 0xE4F954U, 0xBF7AA3U, 0x74617AU, 0x6D904DU, 0x070A95U, 0x1E795AU, + 0x55E0EBU, 0x0EF334U, 0x0F0845U, 0x7098CAU, 0x798313U, 0x2360E0U, 0x687BFDU, 0x71AB0EU, 0xBA10C3U, 0x830370U, + 0xC8FA2DU, 0x93EDB7U, 0x9B3742U, 0xC00689U, 0xA99DBCU, 0xB25E67U, 0xF964BAU, 0xE0F509U, 0xA7AE44U, 0xFF1DB7U, + 0x54C42EU, 0x0DF6F1U, 0x066D90U, 0x5D3C0EU, 0x1487FFU, 0x2F8460U, 0x675E19U, 0x3CEFDEU, 0x37B447U, 0x6E27B0U, + 0x651369U, 0x9CC856U, 0xD77B97U, 0xC9604CU, 0x88B1F9U, 0x939BA2U, 0xFA4856U, 0xB1D1DDU, 0xAAE200U, 0xE33873U, + 0xB829EEU, 0xB0D20DU, 0xCBC190U, 0x407AEBU, 0x19AA3EU, 0x52A1A5U, 0x4B1240U, 0x204B1BU, 0x3ED9A2U, 0x7F2274U, + 0x2437ADU, 0x2DAC8AU, 0x76DF53U, 0x5D45ACU, 0x04F43DU, 0xCEBFE2U, 0xD72CC3U, 0x9C551CU, 0xE7C7E5U, 0xEEDC72U, + 0xB52D1BU, 0xBCB6CCU, 0xE6A454U, 0xA95FA7U, 0x904EFAU, 0xD3A551U, 0xCA3684U, 0x01265FU, 0x5ADD6AU, 0x72CEB1U, + 0x29135CU, 0x60A1CFU, 0x7BBA92U, 0x304921U, 0x2950FCU, 0x42D316U, 0x1AA903U, 0x1138F8U, 0x48E329U, 0xC7D0A6U, + 0xBC48DFU, 0xFD1B08U, 0xE6A0B1U, 0xAE3366U, 0xF55AAFU, 0xFEC898U, 0x879341U, 0x8C22FEU, 0xD5392EU, 0x9EEA61U, + 0x8456D0U, 0x6D450BU, 0x769EF6U, 0x3FADE5U, 0x606508U, 0x6BD6DBU, 0x3ACD46U, 0x101C3DU, 0x5927F8U, 0x02F543U, + 0x096C96U, 0x505FCCU, 0xBB8C71U, 0xA296B2U, 0xE8370FU, 0xF36C5CU, 0xBADF85U, 0xE1063AU, 0xC814FBU, 0x97AF04U, + 0x9CFA15U, 0xC461EAU, 0x87D23BU, 0xBE88B4U, 0x7509CCU, 0x2E721BU, 0x27E1A2U, 0x7C5965U, 0x750A7CU, 0x0F9183U, + 0x44A052U, 0x5D7BE9U, 0x1669ACU, 0x0F9277U, 0x6013CAU, 0x3B0819U, 0xB3FA65U, 0xE0E3E6U, 0xA9103BU, 0xB28BC0U, + 0xDB9ED5U, 0xC0642EU, 0x8B77EBU, 0xD3EC50U, 0xD80D89U, 0xA1179EU, 0xAAC477U, 0xF1F5A0U, 0x386E19U, 0x271D4FU, + 0x6F8596U, 0x541639U, 0x1F6DE8U, 0x4EFE97U, 0x45B706U, 0x1C05D9U, 0x779E30U, 0x6DCF27U, 0x2474DEU, 0x3F664DU, + 0xF69BB0U, 0xAD08FBU, 0x86536FU, 0xDFE094U, 0xD1E841U, 0x883B6AU, 0xC300B7U, 0xE8D164U, 0xB16BD9U, 0xF2788AU, + 0xEBA167U, 0xA192FCU, 0x3A0929U, 0x53DBD2U, 0x08F2C3U, 0x01212DU, 0x5A3ABCU, 0x518B43U, 0x29D11AU, 0x62628DU, + 0x7FFD64U, 0x34ACB3U, 0x6F1E8AU, 0xE64555U, 0x95C494U, 0x9D7F2BU, 0xC62C7AU, 0x8DB485U, 0x94871DU, 0xFF5CDEU, + 0xE6EFE3U, 0xADB730U, 0xF524CFU, 0xFE0F56U, 0xA5DE31U, 0x48C5E8U, 0x53377FU, 0x1AAE86U, 0x01BD59U, 0x4B4678U, + 0x3A50A7U, 0x31E977U, 0x683ACCU, 0x633189U, 0x38C072U, 0x71DAEFU, 0x4B493CU, 0x82B001U, 0x99A3C2U, 0xD2383FU, + 0x8F4AA4U, 0xA4D3D1U, 0xFDA00AU, 0xB62B8FU, 0xACBB75U, 0xE5C028U, 0xF6539BU, 0x9F4242U, 0xC4B985U, 0x4F2FBCU, + 0x167463U, 0x1CC5D2U, 0x65DE1DU, 0x2E3C4CU, 0x35A5F3U, 0x78F622U, 0x234DDDU, 0x0A5C55U, 0x408622U, 0x5BB5FBU, + 0x122E6CU, 0xC1FF95U, 0xC8C4DEU, 0xB3066BU, 0xB83FB0U, 0xE0AC25U, 0xAB775EU, 0xB24793U, 0xD9DC20U, 0x828BFDU, + 0x8F30AFU, 0xD46102U, 0xDCDBD9U, 0x07582CU, 0x6E0137U, 0x75B2C6U, 0x3EE809U, 0x2F7990U, 0x64C2E7U, 0x1C913EU, + 0x170A89U, 0x4C3A40U, 0x45E9D6U, 0x1E42AFU, 0x571370U, 0xE889C1U, 0xA2BA0EU, 0xFB635FU, 0xF070E0U, 0xA9CF39U, + 0x821DDAU, 0xD904C7U, 0x90F734U, 0x82FCE9U, 0xCB4D72U, 0xD09716U, 0x3B848DU, 0x623D78U, 0x696EA3U, 0x30F4BEU, + 0x7A075DU, 0x451E80U, 0x0C8D33U, 0x17E66EU, 0x5E76B5U, 0x050D14U, 0x0E9ECBU, 0x7607BAU, 0xF57524U, 0xACEAC5U, + 0xE7F91AU, 0xFC0083U, 0x9583F4U, 0xCED96DU, 0xC6688AU, 0x9D7353U, 0x92804CU, 0xCB11BDU, 0xA00B62U, 0xA9F8C3U, + 0x72E398U, 0x783244U, 0x2118F7U, 0x4A8B2AU, 0x5B5249U, 0x1061D4U, 0x09FA2FU, 0x428ABAU, 0x1911F1U, 0x31C604U, + 0x6AFD9FU, 0x276D62U, 0xBC2631U, 0xF795B8U, 0xCE4C4EU, 0x855F97U, 0xDDE520U, 0xD6B479U, 0x8D0FB6U, 0x8C1C07U, + 0xF7C6D8U, 0xBE77A9U, 0xA53C36U, 0xEFAFDFU, 0x369648U, 0x1D4411U, 0x40DFE6U, 0x4BEE7EU, 0x103595U, 0x5902C0U, + 0x43D85BU, 0x2AC9AEU, 0x316265U, 0x7AB158U, 0x23A18BU, 0x201A56U, 0xD949E5U, 0x93F2A8U, 0x882313U, 0xC139C6U, + 0xDA8A3CU, 0x97D329U, 0xEC50E2U, 0xE7AA13U, 0xBFBB8CU, 0xB42075U, 0xED5322U, 0x06C8ABU, 0x1DF85CU, 0x542785U, + 0x0FB4BAU, 0x0FCD7BU, 0x544FA4U, 0x7F5414U, 0x26A54BU, 0x6D3EF2U, 0x702D31U, 0x3BD7CCU, 0x13C6DFU, 0xC83D02U, + 0x83AEF9U, 0x9AB77CU, 0xD15507U, 0xC84ED2U, 0xA3DF49U, 0xF9A4BDU, 0xF836F6U, 0xA3ED4BU, 0xEADC98U, 0xD14B05U, + 0x9A30E6U, 0x03A0BFU, 0x4D6B00U, 0x1458D1U, 0x1FC12EU, 0x64932FU, 0x6D28F0U, 0x36BB09U, 0x7FE29FU, 0x6551E6U, + 0x0E0B21U, 0x57AAB8U, 0x54B147U, 0x8D6216U, 0x86DBA9U, 0xDDC968U, 0xB412F3U, 0xAA218EU, 0xE3FC5DU, 0xF86EF0U, + 0xB35523U, 0xCA945FU, 0xC1AFCCU, 0x983C11U, 0xD264FAU, 0x49D7EFU, 0x000C14U, 0x3B1FC1U, 0x7AA75AU, 0x21F4B7U, + 0x2A6FA4U, 0x728E5DU, 0x5D958AU, 0x042713U, 0x4F767DU, 0x54EDACU, 0x1D5E13U, 0x8604C0U, 0xEE91ADU, 0xB5EA36U, + 0xBC79E3U, 0xE77018U, 0xAC820DU, 0x9D19E6U, 0xD6283FU, 0xCEF3A8U, 0x85E0D1U, 0xDA1A47U, 0x538BAEU, 0x389079U, + 0x216360U, 0x6A6B9FU, 0x30D84EU, 0x3903E1U, 0x421230U, 0x0BE94FU, 0x10FBD6U, 0x5B6011U, 0x4215E8U, 0x008EFBU, + 0xF95C07U, 0xF2759CU, 0xADE659U, 0xA49D22U, 0xFF0CB7U, 0x96964CU, 0x8CE591U, 0xC77E92U, 0x9E2F6FU, 0x959CBCU, + 0xCC0601U, 0xE7475AU, 0x3CFCBFU, 0x74EF25U, 0x6717F4U, 0x2E048BU, 0x15DF0AU, 0x5A6CF5U, 0x03716CU, 0x08A33BU, + 0x5098C2U, 0x5B1945U, 0x20C2BCU, 0x69F063U, 0xF22972U, 0xBB1A8DU, 0xE0814DU, 0xCA52F6U, 0x936AABU, 0x98B958U, + 0xC9A2C5U, 0x820306U, 0x9B593BU, 0xF4CAE0U, 0xEE7315U, 0xA7209EU, 0x7C9BCBU, 0x75C930U, 0x0E54A4U, 0x45E75FU, + 0x5CBC12U, 0x173D81U, 0x0F0778U, 0x44D4AFU, 0x3FC596U, 0x363E59U, 0x65ACE8U, 0x6C9737U, 0xA346E6U, 0xD95DD9U, + 0xD0FE10U, 0x8B26E6U, 0xC2357FU, 0xD9CE28U, 0x92DDC1U, 0xAB455EU, 0xE1B6AFU, 0xB8ADF4U, 0xB35861U, 0x68438AU, + 0x41C157U, 0x12B864U, 0x5B2BB9U, 0x45B00AU, 0x0EC3C6U, 0x175B95U, 0x7C2828U, 0x25B3F3U, 0x2E2206U, 0x75580DU, + 0x3DDBD8U, 0x86C223U, 0xCF31BAU, 0xD4AADDU, 0x9FF804U, 0xC64193U, 0xCD526AU, 0xBDA9F4U, 0xB2BD95U, 0xE92E4AU, + 0xA0C5FBU, 0xBBD424U, 0xD20F7DU, 0x093DDAU, 0x01A603U, 0x5A77F0U, 0x514CEDU, 0x089E36U, 0x63A7D3U, 0x7A3448U, + 0x31FF3CU, 0x2BCEE7U, 0x625462U, 0x150799U, 0x1CBC84U, 0xC7ED77U, 0x8C76AAU, 0x95C409U, 0xDF9954U, 0xE62A8FU, + 0xAD213EU, 0xF6F1E1U, 0xFF4A00U, 0xA4191EU, 0x8D82C7U, 0xD7B330U, 0x9C69A9U, 0x057ACEU, 0x42C357U, 0x2B1088U, + 0x303A79U, 0x7BEB76U, 0x63F087U, 0x284358U, 0x7198E1U, 0x5A88B2U, 0x017B7EU, 0x0860CDU, 0x53F510U, 0x9A0F63U, + 0xA00CFEU, 0xEBB535U, 0xF2E6C0U, 0xB97DDBU, 0xEC0F2EU, 0xE796B5U, 0x9C0578U, 0xD47E0BU, 0xCFEF92U, 0x86F564U, + 0x1D16ADU, 0x768F32U, 0x2FFC43U, 0x24669CU, 0x7C752DU, 0x778CE2U, 0x2E1F9BU, 0x45000CU, 0x5EF0D5U, 0x13FB62U, + 0x40282BU, 0x4811D4U, 0xB38344U, 0xB8789FU, 0xE16BEAU, 0xAAB261U, 0xB38194U, 0xF81B4FU, 0xC2CA52U, 0x8BE1B1U, + 0xD0726CU, 0xD903DFU, 0x829982U, 0xE94A79U, 0x7471FDU, 0x36E026U, 0x2FBA93U, 0x640DC8U, 0x3F1431U, 0x16D7B6U, + 0x4D6C6FU, 0x443C18U, 0x1E8781U, 0x55947EU, 0x6C4FBFU, 0x27FFA0U, 0xBEE451U, 0xF5378FU, 0xAE0E2EU, 0xA2CD71U, + 0xC9D7C8U, 0x98661FU, 0x93BDC6U, 0xC88EE5U, 0xC15438U, 0xBA45C3U, 0xF2FE56U, 0xE9290DU, 0x2230E8U, 0x3B9273U, + 0x70C98FU, 0x0958DCU, 0x02A343U, 0x58B0A2U, 0x150A7DU, 0x0E5BC4U, 0x6FC897U, 0x74F33AU, 0x3F23E9U, 0x66A834U, + 0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U, + 0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U +}; + +const unsigned int DMR_A_TABLE[] = {0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U, + 48U, 52U, 56U, 60U, 64U, 68U, 1U, 5U, 9U, 13U, 17U, 21U + }; +const unsigned int DMR_B_TABLE[] = {25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, + 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U + }; +const unsigned int DMR_C_TABLE[] = {46U, 50U, 54U, 58U, 62U, 66U, 70U, 3U, 7U, 11U, 15U, 19U, 23U, + 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U + }; + + +const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +const unsigned int INTERLEAVE_TABLE_26_4[] = +{ + 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U, 48U, 52U, 56U, 60U, 64U, 68U, 72U, 76U, 80U, 84U, 88U, 92U, 96U, 100U, + 1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U, 81U, 85U, 89U, 93U, 97U, 101U, + 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U, 46U, 50U, 54U, 58U, 62U, 66U, 70U, 74U, 78U, 82U, 86U, 90U, 94U, 98U, 102U, + 3U, 7U, 11U, 15U, 19U, 23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U, 75U, 79U, 83U, 87U, 91U, 95U, 99U, 103U +}; + +const unsigned char WHITENING_DATA[] = {0x93U, 0xD7U, 0x51U, 0x21U, 0x9CU, 0x2FU, 0x6CU, 0xD0U, 0xEFU, 0x0FU, + 0xF8U, 0x3DU, 0xF1U, 0x73U, 0x20U, 0x94U, 0xEDU, 0x1EU, 0x7CU, 0xD8U + }; + +//////////////////////////////////////////////////////////////////////////////////////// +// macros + +#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +//////////////////////////////////////////////////////////////////////////////////////// +// + +void CYsfUtils::DecodeVD2Vchs(uint8 *data, uint8 **ambe) +{ + int frame = 0; + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned int offset = 40U; // DCH(0) + + // We have a total of 5 VCH sections, iterate through each + for (unsigned int j = 0U; j < 5U; j++, offset += 144U) + { + + unsigned char vch[13U]; + unsigned int dat_a = 0U; + unsigned int dat_b = 0U; + unsigned int dat_c = 0U; + + // Deinterleave + for (unsigned int i = 0U; i < 104U; i++) + { + unsigned int n = INTERLEAVE_TABLE_26_4[i]; + bool s = READ_BIT(data, offset + n); + WRITE_BIT(vch, i, s); + } + + // "Un-whiten" (descramble) + for (unsigned int i = 0U; i < 13U; i++) + vch[i] ^= WHITENING_DATA[i]; + + for (unsigned int i = 0U; i < 12U; i++) + { + dat_a <<= 1U; + if (READ_BIT(vch, 3U*i + 1U)) + dat_a |= 0x01U; + } + + for (unsigned int i = 0U; i < 12U; i++) + { + dat_b <<= 1U; + if (READ_BIT(vch, 3U*(i + 12U) + 1U)) + dat_b |= 0x01U; + } + + for (unsigned int i = 0U; i < 3U; i++) + { + dat_c <<= 1U; + if (READ_BIT(vch, 3U*(i + 24U) + 1U)) + dat_c |= 0x01U; + } + + for (unsigned int i = 0U; i < 22U; i++) + { + dat_c <<= 1U; + if (READ_BIT(vch, i + 81U)) + dat_c |= 0x01U; + } + + // convert to ambe2plus + unsigned char v_dmr[9U]; + + unsigned int a = CGolay24128::encode24128(dat_a); + unsigned int p = PRNG_TABLE[dat_a] >> 1; + unsigned int b = CGolay24128::encode23127(dat_b) >> 1; + b ^= p; + + unsigned int MASK = 0x800000U; + for (unsigned int i = 0U; i < 24U; i++, MASK >>= 1) + { + unsigned int aPos = DMR_A_TABLE[i]; + WRITE_BIT(v_dmr, aPos, a & MASK); + } + + MASK = 0x400000U; + for (unsigned int i = 0U; i < 23U; i++, MASK >>= 1) + { + unsigned int bPos = DMR_B_TABLE[i]; + WRITE_BIT(v_dmr, bPos, b & MASK); + } + + MASK = 0x1000000U; + for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) + { + unsigned int cPos = DMR_C_TABLE[i]; + WRITE_BIT(v_dmr, cPos, dat_c & MASK); + } + + ::memcpy(ambe[frame++], v_dmr, 9); + } +} + +void CYsfUtils::EncodeVD2Vch(uint8 *ambe, uint8 *data) +{ + // convert from ambe2plus + unsigned int a = 0U; + unsigned int MASK = 0x800000U; + for (unsigned int i = 0U; i < 24U; i++, MASK >>= 1) + { + unsigned int aPos = DMR_A_TABLE[i]; + + if (READ_BIT(ambe, aPos)) + a |= MASK; + } + + unsigned int b = 0U; + MASK = 0x400000U; + for (unsigned int i = 0U; i < 23U; i++, MASK >>= 1) + { + unsigned int bPos = DMR_B_TABLE[i]; + + if (READ_BIT(ambe, bPos)) + b |= MASK; + } + + unsigned int dat_c = 0U; + MASK = 0x1000000U; + for (unsigned int i = 0U; i < 25U; i++, MASK >>= 1) + { + unsigned int cPos = DMR_C_TABLE[i]; + + if (READ_BIT(ambe, cPos)) + dat_c |= MASK; + } + + // and to vch + unsigned char vch[13U]; + unsigned char ysfFrame[13U]; + ::memset(vch, 0U, 13U); + ::memset(ysfFrame, 0, 13U); + + unsigned int dat_a = a >> 12; + + // The PRNG + b ^= (PRNG_TABLE[dat_a] >> 1); + + unsigned int dat_b = b >> 11; + + for (unsigned int i = 0U; i < 12U; i++) + { + bool s = (dat_a << (20U + i)) & 0x80000000U; + WRITE_BIT(vch, 3*i + 0U, s); + WRITE_BIT(vch, 3*i + 1U, s); + WRITE_BIT(vch, 3*i + 2U, s); + } + + for (unsigned int i = 0U; i < 12U; i++) + { + bool s = (dat_b << (20U + i)) & 0x80000000U; + WRITE_BIT(vch, 3*(i + 12U) + 0U, s); + WRITE_BIT(vch, 3*(i + 12U) + 1U, s); + WRITE_BIT(vch, 3*(i + 12U) + 2U, s); + } + + for (unsigned int i = 0U; i < 3U; i++) + { + bool s = (dat_c << (7U + i)) & 0x80000000U; + WRITE_BIT(vch, 3*(i + 24U) + 0U, s); + WRITE_BIT(vch, 3*(i + 24U) + 1U, s); + WRITE_BIT(vch, 3*(i + 24U) + 2U, s); + } + + for (unsigned int i = 0U; i < 22U; i++) + { + bool s = (dat_c << (10U + i)) & 0x80000000U; + WRITE_BIT(vch, i + 81U, s); + } + + WRITE_BIT(vch, 103U, 0U); + + // Scramble + for (unsigned int i = 0U; i < 13U; i++) + vch[i] ^= WHITENING_DATA[i]; + + // Interleave + for (unsigned int i = 0U; i < 104U; i++) + { + unsigned int n = INTERLEAVE_TABLE_26_4[i]; + bool s = READ_BIT(vch, i); + WRITE_BIT(ysfFrame, n, s); + } + + ::memcpy(data, ysfFrame, 13U); +} diff --git a/src/YSFUtils.h b/src/YSFUtils.h new file mode 100644 index 0000000..bb714ec --- /dev/null +++ b/src/YSFUtils.h @@ -0,0 +1,43 @@ +// +// YSFUtils.h +// xlxd +// +// Created by Jean-Luc Deltombe (LX3JL) on 14/04/2019. +// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved. +// Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +// Copyright (C) 2018 by Andy Uribe CA6JAU +// Copyright (C) 2018 by Manuel Sanchez EA7EE +// +// ---------------------------------------------------------------------------- +// This file is part of xlxd. +// +// xlxd is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// xlxd is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Foobar. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef cysfutils_h +#define cysfutils_h + +//////////////////////////////////////////////////////////////////////////////////////// +// class + +class CYsfUtils +{ +public: + // operation + static void DecodeVD2Vchs(uint8 *, uint8 **); + static void EncodeVD2Vch(uint8 *, uint8 *); +}; + +//////////////////////////////////////////////////////////////////////////////////////// +#endif /* cysfutils_h */