unstable
Tom Early 4 years ago
parent 68c30237f8
commit b296c10ac5

14
.gitignore vendored

@ -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

@ -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 dont 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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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);
}

@ -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 <https://www.gnu.org/licenses/>.
#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 &) {}
};

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CBmClient>(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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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;
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cbuffer_h
#define cbuffer_h
#include <vector>
#include <fstream>
#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<uint8_t>::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<uint8_t>::size_type len, uint8_t val = 0) { m_data.resize(len, val); }
uint8_t at(std::vector<uint8_t>::size_type i) const { return m_data.at(i); }
protected:
std::vector<uint8_t> m_data;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cbuffer_h */

@ -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 <cstdio>
#include <cassert>
#include <cstring>
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]);
}

@ -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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cnotificationqueue_h
#define cnotificationqueue_h
#include <queue>
#include <mutex>
#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<CNotification> queue;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cnotificationqueue_h */

@ -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 <cstdint>
#include <cstdio>
#include <cassert>
#include <cmath>
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;
}

@ -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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#include <cctype>
#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 == ' ');
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CCallsignListItem>::iterator begin() { return m_Callsigns.begin(); }
std::list<CCallsignListItem>::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<CCallsignListItem> m_Callsigns;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* ccallsignlist_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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 << "<NODE>" << std::endl;
xmlFile << "\t<Callsign>" << m_Callsign << "</Callsign>" << std::endl;
xmlFile << "\t<IP>" << m_Ip.GetAddress() << "</IP>" << std::endl;
xmlFile << "\t<LinkedModule>" << m_ReflectorModule << "</LinkedModule>" << std::endl;
xmlFile << "\t<Protocol>" << GetProtocolName() << "</Protocol>" << std::endl;
char mbstr[100];
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_ConnectTime)))
{
xmlFile << "\t<ConnectTime>" << mbstr << "</ConnectTime>" << std::endl;
}
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime)))
{
xmlFile << "\t<LastHeardTime>" << mbstr << "</LastHeardTime>" << std::endl;
}
xmlFile << "</NODE>" << 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);
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CClient> 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<CClient> 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<CClient> client) const
{
for ( auto it=cbegin(); it!=cend(); it++ )
{
if (*it == client)
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////
// find Clients
std::shared_ptr<CClient> 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<CClient> 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<CClient> 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<CClient> 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<CClient> 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<CClient> 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<CClient> CClients::FindNextClient(int Protocol, std::list<std::shared_ptr<CClient>>::iterator &it)
{
while ( it != end() )
{
if ( (*it)->GetProtocol() == Protocol )
{
return *it++;
}
it++;
}
return nullptr;
}
std::shared_ptr<CClient> CClients::FindNextClient(const CIp &Ip, int Protocol, std::list<std::shared_ptr<CClient>>::iterator &it)
{
while ( it != end() )
{
if ( ((*it)->GetProtocol() == Protocol) && ((*it)->GetIp() == Ip) )
{
return *it++;
}
it++;
}
return nullptr;
}
std::shared_ptr<CClient> CClients::FindNextClient(const CCallsign &Callsign, const CIp &Ip, int Protocol, std::list<std::shared_ptr<CClient>>::iterator &it)
{
while ( it != end() )
{
if ( ((*it)->GetProtocol() == Protocol) && ((*it)->GetIp() == Ip) && (*it)->GetCallsign().HasSameCallsign(Callsign) )
{
return *it++;
}
it++;
}
return nullptr;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CClient>);
void RemoveClient(std::shared_ptr<CClient>);
bool IsClient(std::shared_ptr<CClient>) const;
// pass-thru
std::list<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); }
std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); }
std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); }
std::list<std::shared_ptr<CClient>>::const_iterator cend() const { return m_Clients.cend(); }
// find clients
std::shared_ptr<CClient> FindClient(const CIp &);
std::shared_ptr<CClient> FindClient(const CIp &, int);
std::shared_ptr<CClient> FindClient(const CIp &, int, char);
std::shared_ptr<CClient> FindClient(const CCallsign &, const CIp &, int);
std::shared_ptr<CClient> FindClient(const CCallsign &, char, const CIp &, int);
std::shared_ptr<CClient> FindClient(const CCallsign &, int);
// iterate on clients
std::shared_ptr<CClient> FindNextClient(int, std::list<std::shared_ptr<CClient>>::iterator &);
std::shared_ptr<CClient> FindNextClient(const CIp &, int, std::list<std::shared_ptr<CClient>>::iterator &);
std::shared_ptr<CClient> FindNextClient(const CCallsign &, const CIp &, int, std::list<std::shared_ptr<CClient>>::iterator &);
protected:
// data
std::mutex m_Mutex;
std::list<std::shared_ptr<CClient>> m_Clients;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cclients_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<bool> keep_running;
std::future<void> 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDvHeaderPacket> Header;
std::unique_ptr<CDvFramePacket> 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<CDvLastFramePacket> &)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<CDcsClient>(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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &header, std::unique_ptr<CDvFramePacket> &frame)
{
uint8 tag[] = { '0','0','0','1' };
if ( (Buffer.size() >= 100) && (Buffer.Compare(tag, sizeof(tag)) == 0) )
{
// get the header
header = std::unique_ptr<CDvHeaderPacket>(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<CDvLastFramePacket>(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<CDvFramePacket>(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_ptr<CClient>Client)
{
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_ptr<CClient>Client)
{
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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, 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<CDvHeaderPacket> &, std::unique_ptr<CDvFramePacket> &);
bool IsIgnorePacket(const CBuffer &);
// packet encoding helpers
void EncodeKeepAlivePacket(CBuffer *);
void EncodeKeepAlivePacket(CBuffer *, std::shared_ptr<CClient>);
void EncodeConnectAckPacket(const CCallsign &, char, CBuffer *);
void EncodeConnectNackPacket(const CCallsign &, char, CBuffer *);
void EncodeDisconnectPacket(CBuffer *, std::shared_ptr<CClient>);
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<CDcsStreamCacheItem, NB_OF_MODULES> m_StreamsCache;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdcsprotocol_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDextraClient>(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();
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDvHeaderPacket> Header;
std::unique_ptr<CDvFramePacket> Frame;
std::unique_ptr<CDvLastFramePacket> 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<CDextraPeer>(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<CDextraClient>(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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CPeer>peer = 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_ptr<CPeer>peer = 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_ptr<CPeer>peer = 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<CDvHeaderPacket> &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_ptr<CClient>client = 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<CDvHeaderPacket> &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<CDvHeaderPacket>(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<CDvFramePacket> &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<CDvFramePacket>(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<CDvLastFramePacket> &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<CDvLastFramePacket>(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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, 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<CDvHeaderPacket> &);
bool IsValidDvFramePacket( const CBuffer &, std::unique_ptr<CDvFramePacket> &);
bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr<CDvLastFramePacket> &);
// 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cdmriddir_h
#define cdmriddir_h
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#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 <uint32, CCallsign> m_CallsignMap;
std::map <CCallsign, uint32, CDmridDirCallsignCompare> m_DmridMap;
// Lock()
std::mutex m_Mutex;
// thread
std::atomic<bool> keep_running;
std::future<void> m_Future;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdmriddir_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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<uint32,CCallsign>(ui, cs));
m_DmridMap.insert(std::pair<CCallsign,uint32>(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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

File diff suppressed because it is too large Load Diff

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, 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<CDvHeaderPacket> &, uint8 *, uint8 *);
bool IsValidDvFramePacket(const CBuffer &, std::array<std::unique_ptr<CDvFramePacket>, 3> &);
bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr<CDvLastFramePacket> &);
// packet encoding helpers
void EncodeKeepAlivePacket(CBuffer *, std::shared_ptr<CClient>);
void EncodeAckPacket(CBuffer *, const CCallsign &);
void EncodeConnectAckPacket(CBuffer *, const CCallsign &, uint32);
void EncodeNackPacket(CBuffer *, const CCallsign &);
void EncodeClosePacket(CBuffer *, std::shared_ptr<CClient>);
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<CDmrmmdvmStreamCacheItem, NB_OF_MODULES> m_StreamsCache;
// for authentication
uint32 m_uiAuthSeed;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdmrmmdvmprotocol_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDvHeaderPacket> Header;
std::array<std::unique_ptr<CDvFramePacket>, 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_ptr<CClient>client = 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<CDmrplusClient>(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_ptr<CClient>client = 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<CDvHeaderPacket> &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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &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<CDvHeaderPacket>(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<std::unique_ptr<CDvFramePacket>, 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<CDvFramePacket>(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 1));
// frame2
memcpy(dmrambe, &dmr3ambe[9], 9);
frames[1] = std::unique_ptr<CDvFramePacket>(new CDvFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 2));
// frame3
memcpy(dmrambe, &dmr3ambe[18], 9);
if ( uiPacketType == 3 )
{
frames[2] = std::unique_ptr<CDvFramePacket>(new CDvLastFramePacket(dmrambe, dmrsync, uiStreamId, uiVoiceSeq, 3));
}
else
{
frames[2] = std::unique_ptr<CDvFramePacket>(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()));
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, 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<CDvHeaderPacket> &);
bool IsValidDvFramePacket(const CIp &, const CBuffer &, std::array<std::unique_ptr<CDvFramePacket>, 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<CDmrplusStreamCacheItem, NB_OF_MODULES> m_StreamsCache;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdmrplusprotocol_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDvHeaderPacket> Header;
std::unique_ptr<CDvFramePacket> Frame;
std::unique_ptr<CDvLastFramePacket> 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<CDplusClient>(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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &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<CDvHeaderPacket>(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<CDvFramePacket> &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<CDvFramePacket>(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<CDvLastFramePacket> &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<CDvLastFramePacket>(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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, 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<CDvHeaderPacket> &);
bool IsValidDvFramePacket(const CBuffer &, std::unique_ptr<CDvFramePacket> &);
bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr<CDvLastFramePacket> &);
// 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<CDPlusStreamCacheItem, NB_OF_MODULES> m_StreamsCache;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdplusprotocol_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CPacket> CDvFramePacket::Duplicate(void) const
{
return std::unique_ptr<CPacket>(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
);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#include <cstdio>
#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<CPacket> CDvHeaderPacket::Duplicate(void) const
{
return std::unique_ptr<CPacket>(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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> CDvLastFramePacket::Duplicate(void) const
{
return std::unique_ptr<CPacket>(new CDvLastFramePacket(*this));
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> Duplicate(void) const;
// identity
bool IsLastPacket(void) const { return true; }
bool HasTranscodableAmbe(void) const { return false; }
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cdvlastframepacket_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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);
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#include <sys/stat.h>
#include "G3Client.h"
#include "G3Protocol.h"
#include "Reflector.h"
#include "GateKeeper.h"
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
////////////////////////////////////////////////////////////////////////////////////////
// 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_ptr<CClient>extant = 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<CG3Client>(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<CG3Client>(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_ptr<CClient>client = 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<CDvHeaderPacket> Header;
std::unique_ptr<CDvFramePacket> Frame;
std::unique_ptr<CDvLastFramePacket> 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_ptr<CClient>client = 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_ptr<CClient>client = 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_ptr<CClient>client = 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<CDvHeaderPacket> &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_ptr<CClient>client = 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<CDvHeaderPacket> &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<CDvHeaderPacket>(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<CDvFramePacket> &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<CDvFramePacket>(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<CDvLastFramePacket> &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<CDvLastFramePacket>(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_ptr<CClient>client = 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;
}
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef cg3protocol_h
#define cg3protocol_h
#include <string>
#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<CDvHeaderPacket> &, const CIp &);
// packet decoding helpers
bool IsValidDvHeaderPacket(const CBuffer &, std::unique_ptr<CDvHeaderPacket> &);
bool IsValidDvFramePacket(const CBuffer &, std::unique_ptr<CDvFramePacket> &);
bool IsValidDvLastFramePacket(const CBuffer &, std::unique_ptr<CDvLastFramePacket> &);
// packet encoding helpers
bool EncodeDvHeaderPacket(const CDvHeaderPacket &, CBuffer *) const;
bool EncodeDvFramePacket(const CDvFramePacket &, CBuffer *) const;
bool EncodeDvLastFramePacket(const CDvLastFramePacket &, CBuffer *) const;
protected:
std::future<void> 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CCallsignList &>(m_NodeWhiteList).Lock();
if ( !m_NodeWhiteList.empty() )
{
ok = m_NodeWhiteList.IsCallsignListedWithWildcard(callsign, module);
}
const_cast<CCallsignList &>(m_NodeWhiteList).Unlock();
// then check if not blacklisted
const_cast<CCallsignList &>(m_NodeBlackList).Lock();
ok &= !m_NodeBlackList.IsCallsignListedWithWildcard(callsign);
const_cast<CCallsignList &>(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<CPeerCallsignList &>(m_PeerList).Lock();
if ( !m_PeerList.empty() )
{
ok = m_PeerList.IsCallsignListed(callsign, module);
}
const_cast<CPeerCallsignList &>(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<CPeerCallsignList &>(m_PeerList).Lock();
if ( !m_PeerList.empty() )
{
ok = m_PeerList.IsCallsignListed(callsign, modules);
}
const_cast<CPeerCallsignList &>(m_PeerList).Unlock();
}
// done
return ok;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<bool> keep_running;
std::future<void> m_Future;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cgatekeeper_h */

@ -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 <cstdio>
#include <cassert>
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;
}

@ -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

File diff suppressed because it is too large Load Diff

@ -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

@ -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 <cstdio>
#include <cassert>
// 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];
}

@ -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

@ -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 <cstdint>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#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;
}

@ -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 <iostream>
#include <cstring>
#include <chrono>
#include <thread>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
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);

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include "Reflector.h"
#include <sys/stat.h>
////////////////////////////////////////////////////////////////////////////////////////
// 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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef main_h
#define main_h
#include <atomic>
#include <vector>
#include <array>
#include <list>
#include <map>
#include <queue>
#include <chrono>
#include <future>
#include <mutex>
#include <memory>
#include <atomic>
#include <condition_variable>
#include <ctime>
#include <cctype>
#include <iostream>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <arpa/inet.h>
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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;
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> 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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPacket> front() { return std::move(queue.front()); }
void push(std::unique_ptr<CPacket> &packet) { queue.push(std::move(packet)); }
protected:
// status
bool m_bOpen;
uint16 m_uiStreamId;
std::mutex m_Mutex;
// owner
CClient *m_Client;
std::queue<std::unique_ptr<CPacket>> queue;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cpacketqueue_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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_ptr<CClient>client)
{
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<CPacket> 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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CClient>);
void ClosePacketStream(void);
// push & pop
void Push(std::unique_ptr<CPacket> packet);
void Tickle(void) { m_LastPacketTime.Now(); }
bool IsEmpty(void) const;
// get
std::shared_ptr<CClient> 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<CClient> m_OwnerClient;
#ifdef TRANSCODER_IP
std::shared_ptr<CCodecStream> m_CodecStream;
#endif
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cpacketstream_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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 << "<PEER>" << std::endl;
xmlFile << "\t<Callsign>" << m_Callsign << "</Callsign>" << std::endl;
xmlFile << "\t<IP>" << m_Ip.GetAddress() << "</IP>" << std::endl;
xmlFile << "\t<LinkedModule>" << m_ReflectorModules << "</LinkedModule>" << std::endl;
xmlFile << "\t<Protocol>" << GetProtocolName() << "</Protocol>" << std::endl;
char mbstr[100];
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_ConnectTime)))
{
xmlFile << "\t<ConnectTime>" << mbstr << "</ConnectTime>" << std::endl;
}
if (std::strftime(mbstr, sizeof(mbstr), "%A %c", std::localtime(&m_LastHeardTime)))
{
xmlFile << "\t<LastHeardTime>" << mbstr << "</LastHeardTime>" << std::endl;
}
xmlFile << "</PEER>" << 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);
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<std::shared_ptr<CClient>>::iterator begin() { return m_Clients.begin(); }
std::list<std::shared_ptr<CClient>>::iterator end() { return m_Clients.end(); }
std::list<std::shared_ptr<CClient>>::const_iterator cbegin() const { return m_Clients.cbegin(); }
std::list<std::shared_ptr<CClient>>::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<std::shared_ptr<CClient>> m_Clients;
// status
CTimePoint m_LastKeepaliveTime;
std::time_t m_ConnectTime;
std::time_t m_LastHeardTime;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cpeer_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <string.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPeer> 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<CPeer> 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<CPeer> 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<CPeer> 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<CPeer> 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<CPeer> CPeers::FindNextPeer(int Protocol, std::list<std::shared_ptr<CPeer>>::iterator &it)
{
while ( it!=end() )
{
if ( (*it)->GetProtocol() == Protocol )
{
return *it++;
}
it++;
}
return nullptr;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CPeer>);
void RemovePeer(std::shared_ptr<CPeer>);
// pass-thru
std::list<std::shared_ptr<CPeer>>::iterator begin() { return m_Peers.begin(); }
std::list<std::shared_ptr<CPeer>>::iterator end() { return m_Peers.end(); }
std::list<std::shared_ptr<CPeer>>::const_iterator cbegin() const { return m_Peers.cbegin(); }
std::list<std::shared_ptr<CPeer>>::const_iterator cend() const { return m_Peers.cend(); }
// find peers
std::shared_ptr<CPeer> FindPeer(const CIp &, int);
std::shared_ptr<CPeer> FindPeer(const CCallsign &, const CIp &, int);
std::shared_ptr<CPeer> FindPeer(const CCallsign &, int);
// iterate on peers
std::shared_ptr<CPeer> FindNextPeer(int, std::list<std::shared_ptr<CPeer>>::iterator &);
protected:
// data
std::mutex m_Mutex;
std::list<std::shared_ptr<CPeer>> m_Peers;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cpeers_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <unordered_map>
#include <string>
#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<int, std::string> v4address;
#endif
#ifdef LISTEN_IPV6
std::unordered_map<int, std::string> v6address;
#endif
};

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvFramePacket> &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<CDvLastFramePacket> &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;
}
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, const CIp &) {}
virtual void OnDvFramePacketIn(std::unique_ptr<CDvFramePacket> &, const CIp * = nullptr);
virtual void OnDvLastFramePacketIn(std::unique_ptr<CDvLastFramePacket> &, 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<CPacketStream *> m_Streams;
// queue
CPacketQueue m_Queue;
// thread
std::atomic<bool> keep_running;
std::future<void> m_Future;
// identity
CCallsign m_ReflectorCallsign;
// debug
CTimePoint m_DebugTimer;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cprotocol_h */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDextraProtocol>(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<CDplusProtocol>(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<CDcsProtocol>(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<CDmrmmdvmProtocol>(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<CDmrplusProtocol>(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<CYsfProtocol>(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<CUlxProtocol>(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<CG3Protocol>(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();
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<std::unique_ptr<CProtocol>>::iterator begin() { return m_Protocols.begin(); }
std::list<std::unique_ptr<CProtocol>>::iterator end() { return m_Protocols.end(); }
protected:
// data
std::mutex m_Mutex;
std::list<std::unique_ptr<CProtocol>> m_Protocols;
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* cprotocols_h */

@ -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 <cstdio>
#include <cassert>
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;
}

@ -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

@ -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 <cstdio>
#include <cassert>
#include <cstring>
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];
}

@ -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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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;
}

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
// Description:
// Raw socket access class with protocol specific
#ifndef crawsocket_h
#define crawsocket_h
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#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 */

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include "Main.h"
#include <string.h>
#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<CDvHeaderPacket> &DvHeader, std::shared_ptr<CClient>client)
{
// 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_ptr<CClient>client = 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<CPacket> 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<CDvHeaderPacket *>(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<CDvHeaderPacket> &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 << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
// software version
char sz[64];
::sprintf(sz, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
xmlFile << "<Version>" << sz << "</Version>" << 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 << "</" << m_Callsign << "linked peers>" << 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 << "</" << m_Callsign << "linked nodes>" << 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 << "</" << m_Callsign << "heard users>" << 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

@ -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 <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#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<CDvHeaderPacket> &, std::shared_ptr<CClient>);
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<CDvHeaderPacket> &);
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<CPacketStream, NB_OF_MODULES> m_Stream;
// threads
std::atomic<bool> keep_running;
std::array<std::future<void>, NB_OF_MODULES> m_RouterFuture;
std::future<void> m_XmlReportFuture, m_JsonReportFuture;
// notifications
CNotificationQueue m_Notifications;
public:
#ifdef DEBUG_DUMPFILE
std::ofstream m_DebugFile;
#endif
};
////////////////////////////////////////////////////////////////////////////////////////
#endif /* creflector_h */

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.