Merge branch 'master' into fix/FastData

This commit is contained in:
Jonathan Naylor 2026-02-26 14:13:51 +00:00 committed by GitHub
commit 03efef08d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
247 changed files with 6676 additions and 13583 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@ Release
x64
MMDVMHost
RemoteCommand
Images
*.o
*.opendb
*.bak

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010,2014,2016,2018,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2010,2014,2016,2018,2021,2023,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016 Mathias Weyland, HB9FRV
*
* This program is free software; you can redistribute it and/or modify
@ -442,20 +442,24 @@ const unsigned int PRNG_TABLE[] = {
0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U,
0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U};
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN)
const unsigned int DMR_A_TABLE[] = { 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U,
48U, 52U, 56U, 60U, 64U, 68U, 1U, 5U, 9U, 13U, 17U, 21U};
const unsigned int DMR_B_TABLE[] = {25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U,
2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U};
const unsigned int DMR_C_TABLE[] = {46U, 50U, 54U, 58U, 62U, 66U, 70U, 3U, 7U, 11U, 15U, 19U,
23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U};
#endif
#if defined(USE_DSTAR)
const unsigned int DSTAR_A_TABLE[] = {0U, 6U, 12U, 18U, 24U, 30U, 36U, 42U, 48U, 54U, 60U, 66U,
1U, 7U, 13U, 19U, 25U, 31U, 37U, 43U, 49U, 55U, 61U, 67U};
const unsigned int DSTAR_B_TABLE[] = {2U, 8U, 14U, 20U, 26U, 32U, 38U, 44U, 50U, 56U, 62U, 68U,
3U, 9U, 15U, 21U, 27U, 33U, 39U, 45U, 51U, 57U, 63U, 69U};
const unsigned int DSTAR_C_TABLE[] = {4U, 10U, 16U, 22U, 28U, 34U, 40U, 46U, 52U, 58U, 64U, 70U,
5U, 11U, 17U, 23U, 29U, 35U, 41U, 47U, 53U, 59U, 65U, 71U};
#endif
#if defined(USE_YSF) || defined(USE_P25)
const unsigned int IMBE_INTERLEAVE[] = {
0, 7, 12, 19, 24, 31, 36, 43, 48, 55, 60, 67, 72, 79, 84, 91, 96, 103, 108, 115, 120, 127, 132, 139,
1, 6, 13, 18, 25, 30, 37, 42, 49, 54, 61, 66, 73, 78, 85, 90, 97, 102, 109, 114, 121, 126, 133, 138,
@ -464,6 +468,7 @@ const unsigned int IMBE_INTERLEAVE[] = {
4, 11, 16, 23, 28, 35, 40, 47, 52, 59, 64, 71, 76, 83, 88, 95, 100, 107, 112, 119, 124, 131, 136, 143,
5, 10, 17, 22, 29, 34, 41, 46, 53, 58, 65, 70, 77, 82, 89, 94, 101, 106, 113, 118, 125, 130, 137, 142
};
#endif
CAMBEFEC::CAMBEFEC()
{
@ -473,6 +478,7 @@ CAMBEFEC::~CAMBEFEC()
{
}
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN)
unsigned int CAMBEFEC::regenerateDMR(unsigned char* bytes) const
{
assert(bytes != nullptr);
@ -573,7 +579,9 @@ unsigned int CAMBEFEC::regenerateDMR(unsigned char* bytes) const
return errors;
}
#endif
#if defined(USE_DSTAR)
unsigned int CAMBEFEC::regenerateDStar(unsigned char* bytes) const
{
assert(bytes != nullptr);
@ -605,7 +613,9 @@ unsigned int CAMBEFEC::regenerateDStar(unsigned char* bytes) const
return errors;
}
#endif
#if defined(USE_YSF) || defined(USE_NXDN)
unsigned int CAMBEFEC::regenerateYSFDN(unsigned char* bytes) const
{
assert(bytes != nullptr);
@ -656,7 +666,9 @@ unsigned int CAMBEFEC::regenerateYSFDN(unsigned char* bytes) const
return errors;
}
#endif
#if defined(USE_YSF) || defined(USE_P25)
unsigned int CAMBEFEC::regenerateIMBE(unsigned char* bytes) const
{
assert(bytes != nullptr);
@ -790,7 +802,9 @@ unsigned int CAMBEFEC::regenerateIMBE(unsigned char* bytes) const
return errors;
}
#endif
#if defined(USE_DSTAR)
unsigned int CAMBEFEC::regenerateDStar(unsigned int& a, unsigned int& b) const
{
unsigned int orig_a = a;
@ -823,7 +837,9 @@ unsigned int CAMBEFEC::regenerateDStar(unsigned int& a, unsigned int& b) const
return errsA + errsB;
}
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN)
unsigned int CAMBEFEC::regenerateDMR(unsigned int& a, unsigned int& b, unsigned int& c) const
{
unsigned int orig_a = a;
@ -866,3 +882,4 @@ unsigned int CAMBEFEC::regenerateDMR(unsigned int& a, unsigned int& b, unsigned
return errsA + errsB;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010,2014,2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2010,2014,2016,2018,2023 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
@ -19,22 +19,36 @@
#if !defined(AMBEFEC_H)
#define AMBEFEC_H
#include "Defines.h"
class CAMBEFEC {
public:
CAMBEFEC();
~CAMBEFEC();
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN)
unsigned int regenerateDMR(unsigned char* bytes) const;
#endif
#if defined(USE_DSTAR)
unsigned int regenerateDStar(unsigned char* bytes) const;
#endif
#if defined(USE_YSF) || defined(USE_NXDN)
unsigned int regenerateYSFDN(unsigned char* bytes) const;
#endif
#if defined(USE_YSF) || defined(USE_P25)
unsigned int regenerateIMBE(unsigned char* bytes) const;
#endif
private:
#if defined(USE_DSTAR)
unsigned int regenerateDStar(unsigned int& a, unsigned int& b) const;
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN)
unsigned int regenerateDMR(unsigned int& a, unsigned int& b,unsigned int& c) const;
#endif
};
#endif

View file

@ -71,6 +71,8 @@
#include "BCH.h"
#if defined(USE_P25)
#include <cmath>
#include <cstdio>
#include <cassert>
@ -137,3 +139,6 @@ void CBCH::encode(unsigned char* nid)
WRITE_BIT(nid, i + 16U, b);
}
}
#endif

9
BCH.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2023 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
@ -19,6 +19,10 @@
#if !defined(BCH_H)
#define BCH_H
#include "Defines.h"
#if defined(USE_P25)
class CBCH {
public:
CBCH();
@ -31,3 +35,6 @@ private:
};
#endif
#endif

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2012 by Ian Wraith
* Copyright (C) 2015,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2023,2025 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
@ -19,6 +19,8 @@
#include "BPTC19696.h"
#if defined(USE_DMR)
#include "Hamming.h"
#include "Utils.h"
@ -345,3 +347,6 @@ void CBPTC19696::encodeExtractBinary(unsigned char* data)
CUtils::bitsToByteBE(m_rawData + 180U, data[31U]);
CUtils::bitsToByteBE(m_rawData + 188U, data[32U]);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2023 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
@ -19,6 +19,10 @@
#if !defined(BPTC19696_H)
#define BPTC19696_H
#include "Defines.h"
#if defined(USE_DMR)
class CBPTC19696
{
public:
@ -45,3 +49,6 @@ private:
};
#endif
#endif

View file

@ -1,151 +0,0 @@
/*
* Copyright (C) 2016,2018,2020,2021,2025 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 "CASTInfo.h"
static bool networkInfoInitialized = false;
static unsigned char passCounter = 0;
CCASTInfo::CCASTInfo(CModem* modem) :
CDisplay(),
m_modem(modem),
m_ipaddress()
{
}
CCASTInfo::~CCASTInfo()
{
}
bool CCASTInfo::open()
{
return true;
}
void CCASTInfo::setIdleInt()
{
unsigned char info[100U];
CNetworkInfo* m_network;
passCounter++;
if (passCounter > 253U)
networkInfoInitialized = false;
if (!networkInfoInitialized) {
//LogMessage("Initialize CNetworkInfo");
info[0]=0;
m_network = new CNetworkInfo;
m_network->getNetworkInterface(info);
m_ipaddress = (char*)info;
delete m_network;
if (m_modem != nullptr)
m_modem->writeIPInfo(m_ipaddress);
networkInfoInitialized = true;
passCounter = 0;
}
}
void CCASTInfo::setErrorInt(const char* text)
{
}
void CCASTInfo::setLockoutInt()
{
}
void CCASTInfo::setQuitInt()
{
}
void CCASTInfo::setFMInt()
{
}
void CCASTInfo::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
if (m_modem != nullptr)
m_modem->writeDStarInfo(my1, my2, your, type, reflector);
}
void CCASTInfo::clearDStarInt()
{
}
void CCASTInfo::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
if (m_modem != nullptr)
m_modem->writeDMRInfo(slotNo, src, group, dst, type);
}
void CCASTInfo::clearDMRInt(unsigned int slotNo)
{
}
void CCASTInfo::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin)
{
if (m_modem != nullptr)
m_modem->writeYSFInfo(source, dest, dgid, type, origin);
}
void CCASTInfo::clearFusionInt()
{
}
void CCASTInfo::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
if (m_modem != nullptr)
m_modem->writeP25Info(source, group, dest, type);
}
void CCASTInfo::clearP25Int()
{
}
void CCASTInfo::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type)
{
if (m_modem != nullptr)
m_modem->writeNXDNInfo(source, group, dest, type);
}
void CCASTInfo::clearNXDNInt()
{
}
void CCASTInfo::writePOCSAGInt(uint32_t ric, const std::string& message)
{
if (m_modem != nullptr)
m_modem->writePOCSAGInfo(ric, message);
}
void CCASTInfo::clearPOCSAGInt()
{
}
void CCASTInfo::writeCWInt()
{
}
void CCASTInfo::clearCWInt()
{
}
void CCASTInfo::close()
{
}

View file

@ -1,72 +0,0 @@
/*
* Copyright (C) 2016,2018,2020,2021,2025 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(CASTINFO_H)
#define CASTINFO_H
#include "Display.h"
#include <string>
#include "NetworkInfo.h"
#include "Modem.h"
class CCASTInfo : public CDisplay
{
public:
CCASTInfo(CModem* modem);
virtual ~CCASTInfo();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void setFMInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearP25Int();
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();
private:
CModem* m_modem;
std::string m_ipaddress;
};
#endif

25
CRC.cpp
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023,2025 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
@ -26,6 +26,7 @@
#include <cassert>
#include <cmath>
#if defined(USE_DMR)
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,
@ -48,8 +49,10 @@ const uint8_t CRC8_TABLE[] = {
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 };
0xFA, 0xFD, 0xF4, 0xF3, 0x01};
#endif
#if defined(USE_DSTAR)
const uint16_t CCITT16_TABLE1[] = {
0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU,
0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U,
@ -82,8 +85,10 @@ const uint16_t CCITT16_TABLE1[] = {
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 };
0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U};
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25)
const uint16_t CCITT16_TABLE2[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
@ -116,9 +121,10 @@ const uint16_t CCITT16_TABLE2[] = {
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 };
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0};
#endif
#if defined(USE_DMR)
bool CCRC::checkFiveBit(bool* in, unsigned int tcrc)
{
assert(in != nullptr);
@ -144,7 +150,9 @@ void CCRC::encodeFiveBit(const bool* in, unsigned int& tcrc)
tcrc = total;
}
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25)
void CCRC::addCCITT162(unsigned char *in, unsigned int length)
{
assert(in != nullptr);
@ -185,7 +193,9 @@ bool CCRC::checkCCITT162(const unsigned char *in, unsigned int length)
return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U];
}
#endif
#if defined(USE_DSTAR)
void CCRC::addCCITT161(unsigned char *in, unsigned int length)
{
assert(in != nullptr);
@ -226,7 +236,9 @@ bool CCRC::checkCCITT161(const unsigned char *in, unsigned int length)
return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U];
}
#endif
#if defined(USE_DMR)
unsigned char CCRC::crc8(const unsigned char *in, unsigned int length)
{
assert(in != nullptr);
@ -238,3 +250,6 @@ unsigned char CCRC::crc8(const unsigned char *in, unsigned int length)
return crc;
}
#endif

20
CRC.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023 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
@ -19,19 +19,35 @@
#if !defined(CRC_H)
#define CRC_H
#include "Defines.h"
class CCRC
{
public:
#if defined(USE_DMR)
static bool checkFiveBit(bool* in, unsigned int tcrc);
static void encodeFiveBit(const bool* in, unsigned int& tcrc);
#endif
#if defined(USE_DSTAR)
static void addCCITT161(unsigned char* in, unsigned int length);
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25)
static void addCCITT162(unsigned char* in, unsigned int length);
#endif
#if defined(USE_DSTAR)
static bool checkCCITT161(const unsigned char* in, unsigned int length);
static bool checkCCITT162(const unsigned char* in, unsigned int length);
#endif
#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25)
static bool checkCCITT162(const unsigned char* in, unsigned int length);
#endif
#if defined(USE_DMR)
static unsigned char crc8(const unsigned char* in, unsigned int length);
#endif
};
#endif

682
Conf.cpp

File diff suppressed because it is too large Load diff

1117
Conf.h

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2023 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
@ -19,6 +19,8 @@
#include "DMRAccessControl.h"
#include "Log.h"
#if defined(USE_DMR)
#include <algorithm>
#include <vector>
#include <cstring>
@ -97,3 +99,6 @@ bool CDMRAccessControl::validateTGId(unsigned int slotNo, bool group, unsigned i
return std::find(m_slot2TGWhiteList.begin(), m_slot2TGWhiteList.end(), id) != m_slot2TGWhiteList.end();
}
}
#endif

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2016 by Simon Rune G7RZU
* Copyright (C) 2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2023 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
@ -18,6 +18,10 @@
#if !defined(DMRAccessControl_H)
#define DMRAccessControl_H
#include "Defines.h"
#if defined(USE_DMR)
#include <vector>
class CDMRAccessControl {
@ -42,3 +46,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2020,2021,2022,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2020,2021,2022,2023,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2019 by Patrick Maier DK5MP
*
* This program is free software; you can redistribute it and/or modify
@ -22,6 +22,8 @@
#include "Utils.h"
#include "CRC.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -246,3 +248,6 @@ void CDMRCSBK::setCBF(unsigned char cbf)
{
m_CBF = m_data[3U] = cbf;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2020,2021,2022,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2020,2021,2022,2023,2025 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
@ -20,6 +20,9 @@
#define DMRCSBK_H
#include "DMRDefines.h"
#include "Defines.h"
#if defined(USE_DMR)
enum class CSBKO : unsigned char {
NONE = 0x00,
@ -81,3 +84,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021,2025 Jonathan Naylor, G4KLX
* Copyright (C) 2015-2021,2023,2025 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
@ -13,15 +13,16 @@
#include "DMRControl.h"
#include "DMRAccessControl.h"
#include "Defines.h"
#include "DMRCSBK.h"
#include "Log.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
#include <algorithm>
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect) :
CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect) :
m_colorCode(colorCode),
m_modem(modem),
m_network(network),
@ -31,14 +32,13 @@ m_lookup(lookup)
{
assert(id != 0U);
assert(modem != nullptr);
assert(display != nullptr);
assert(lookup != nullptr);
assert(rssi != nullptr);
// Load black and white lists to DMRAccessControl
CDMRAccessControl::init(blacklist, whitelist, slot1TGWhitelist, slot2TGWhitelist, selfOnly, prefixes, id);
CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, display, duplex, m_lookup, rssi, jitter, ovcm, protect);
CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_lookup, rssi, jitter, ovcm, protect);
}
CDMRControl::~CDMRControl()
@ -136,3 +136,6 @@ void CDMRControl::enable(bool enabled)
m_slot1.enable(enabled);
m_slot2.enable(enabled);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2021,2023,2025 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
@ -22,16 +22,19 @@
#include "RSSIInterpolator.h"
#include "DMRNetwork.h"
#include "DMRLookup.h"
#include "Display.h"
#include "DMRSlot.h"
#include "DMRData.h"
#include "Modem.h"
#include "Defines.h"
#if defined(USE_DMR)
#include <vector>
class CDMRControl {
public:
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect);
CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector<unsigned int>& prefixes, const std::vector<unsigned int>& blacklist, const std::vector<unsigned int>& whitelist, const std::vector<unsigned int>& slot1TGWhitelist, const std::vector<unsigned int>& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect);
~CDMRControl();
bool processWakeup(const unsigned char* data);
@ -51,10 +54,13 @@ public:
private:
unsigned int m_colorCode;
CModem* m_modem;
IDMRNetwork* m_network;
CDMRNetwork* m_network;
CDMRSlot m_slot1;
CDMRSlot m_slot2;
CDMRLookup* m_lookup;
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2025 Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017,2023,2025 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
@ -16,11 +16,12 @@
#include "Utils.h"
#include "Log.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cstring>
#include <cassert>
CDMRData::CDMRData(const CDMRData& data) :
m_slotNo(data.m_slotNo),
m_data(nullptr),
@ -183,3 +184,6 @@ void CDMRData::setData(const unsigned char* buffer)
::memcpy(m_data, buffer, DMR_FRAME_LENGTH_BYTES);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor, G4KLX
* Copyright (C) 2015,2016,2017,2023 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
@ -15,6 +15,9 @@
#define DMRData_H
#include "DMRDefines.h"
#include "Defines.h"
#if defined(USE_DMR)
class CDMRData {
public:
@ -68,3 +71,6 @@ private:
};
#endif
#endif

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2012 by Ian Wraith
* Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2023,2025 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
@ -25,6 +25,8 @@
#include "CRC.h"
#include "Log.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -173,3 +175,6 @@ CDMRDataHeader& CDMRDataHeader::operator=(const CDMRDataHeader& header)
return *this;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2023 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
@ -19,6 +19,10 @@
#ifndef DMRDataHeader_H
#define DMRDataHeader_H
#include "Defines.h"
#if defined(USE_DMR)
class CDMRDataHeader
{
public:
@ -52,3 +56,5 @@ private:
#endif
#endif

View file

@ -1,644 +0,0 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021,2024,2025 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 "DMRDirectNetwork.h"
#include "SHA256.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRDirectNetwork::CDMRDirectNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, const std::string& password, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) :
m_address(address),
m_port(port),
m_addr(),
m_addrLen(0U),
m_id(nullptr),
m_password(password),
m_duplex(duplex),
m_version(version),
m_debug(debug),
m_socket(localAddress, localPort),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_hwType(hwType),
m_status(STATUS::WAITING_CONNECT),
m_retryTimer(1000U, 10U),
m_timeoutTimer(1000U, 60U),
m_buffer(nullptr),
m_streamId(nullptr),
m_salt(nullptr),
m_rxData(1000U, "DMR Network"),
m_options(),
m_random(),
m_callsign(),
m_rxFrequency(0U),
m_txFrequency(0U),
m_power(0U),
m_colorCode(0U),
m_latitude(0.0F),
m_longitude(0.0F),
m_height(0),
m_location(),
m_description(),
m_url(),
m_beacon(false)
{
assert(!address.empty());
assert(port > 0U);
assert(id > 1000U);
assert(!password.empty());
if (CUDPSocket::lookup(m_address, m_port, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
m_buffer = new unsigned char[BUFFER_LENGTH];
m_salt = new unsigned char[sizeof(uint32_t)];
m_id = new uint8_t[4U];
m_streamId = new uint32_t[2U];
m_id[0U] = id >> 24;
m_id[1U] = id >> 16;
m_id[2U] = id >> 8;
m_id[3U] = id >> 0;
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
m_streamId[0U] = dist(m_random);
m_streamId[1U] = dist(m_random);
}
CDMRDirectNetwork::~CDMRDirectNetwork()
{
delete[] m_streamId;
delete[] m_buffer;
delete[] m_salt;
delete[] m_id;
}
void CDMRDirectNetwork::setOptions(const std::string& options)
{
m_options = options;
}
void CDMRDirectNetwork::setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url)
{
m_callsign = callsign;
m_rxFrequency = rxFrequency;
m_txFrequency = txFrequency;
m_power = power;
m_colorCode = colorCode;
m_latitude = latitude;
m_longitude = longitude;
m_height = height;
m_location = location;
m_description = description;
m_url = url;
}
bool CDMRDirectNetwork::open()
{
if (m_addrLen == 0U) {
LogError("DMR, Could not lookup the address of the DMR Network");
return false;
}
LogMessage("DMR, Opening DMR Network");
m_status = STATUS::WAITING_CONNECT;
m_timeoutTimer.stop();
m_retryTimer.start();
return m_socket.open(m_addr);
}
void CDMRDirectNetwork::enable(bool enabled)
{
if (!enabled && m_enabled)
m_rxData.clear();
m_enabled = enabled;
}
bool CDMRDirectNetwork::read(CDMRData& data)
{
if (m_status != STATUS::RUNNING)
return false;
if (m_rxData.isEmpty())
return false;
unsigned char length = 0U;
m_rxData.getData(&length, 1U);
m_rxData.getData(m_buffer, length);
// Is this a data packet?
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
return false;
unsigned char seqNo = m_buffer[4U];
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0);
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
// DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
return false;
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP;
data.setSeqNo(seqNo);
data.setSlotNo(slotNo);
data.setSrcId(srcId);
data.setDstId(dstId);
data.setFLCO(flco);
bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U;
bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U;
if (dataSync) {
unsigned char dataType = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(dataType);
data.setN(0U);
} else if (voiceSync) {
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE_SYNC);
data.setN(0U);
} else {
unsigned char n = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE);
data.setN(n);
}
return true;
}
bool CDMRDirectNetwork::write(const CDMRData& data)
{
if (m_status != STATUS::RUNNING)
return false;
unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH);
buffer[0U] = 'D';
buffer[1U] = 'M';
buffer[2U] = 'R';
buffer[3U] = 'D';
unsigned int srcId = data.getSrcId();
buffer[5U] = srcId >> 16;
buffer[6U] = srcId >> 8;
buffer[7U] = srcId >> 0;
unsigned int dstId = data.getDstId();
buffer[8U] = dstId >> 16;
buffer[9U] = dstId >> 8;
buffer[10U] = dstId >> 0;
::memcpy(buffer + 11U, m_id, 4U);
unsigned int slotNo = data.getSlotNo();
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
buffer[15U] = slotNo == 1U ? 0x00U : 0x80U;
FLCO flco = data.getFLCO();
buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U;
unsigned int slotIndex = slotNo - 1U;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
unsigned char dataType = data.getDataType();
if (dataType == DT_VOICE_SYNC) {
buffer[15U] |= 0x10U;
} else if (dataType == DT_VOICE) {
buffer[15U] |= data.getN();
} else {
if (dataType == DT_VOICE_LC_HEADER)
m_streamId[slotIndex] = dist(m_random);
if (dataType == DT_CSBK || dataType == DT_DATA_HEADER)
m_streamId[slotIndex] = dist(m_random);
buffer[15U] |= (0x20U | dataType);
}
buffer[4U] = data.getSeqNo();
::memcpy(buffer + 16U, m_streamId + slotIndex, 4U);
data.getData(buffer + 20U);
buffer[53U] = data.getBER();
buffer[54U] = data.getRSSI();
write(buffer, HOMEBREW_DATA_PACKET_LENGTH);
return true;
}
bool CDMRDirectNetwork::writeRadioPosition(unsigned int id, const unsigned char* data)
{
if (m_status != STATUS::RUNNING)
return false;
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRG", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
::memcpy(buffer + 7U, data + 2U, 7U);
return write(buffer, 14U);
}
bool CDMRDirectNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
{
if (m_status != STATUS::RUNNING)
return false;
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRA", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
buffer[7U] = type;
::memcpy(buffer + 8U, data + 2U, 7U);
return write(buffer, 15U);
}
bool CDMRDirectNetwork::isConnected() const
{
return (m_status == STATUS::RUNNING);
}
void CDMRDirectNetwork::close(bool sayGoodbye)
{
LogMessage("DMR, Closing DMR Network");
if (sayGoodbye && (m_status == STATUS::RUNNING)) {
unsigned char buffer[9U];
::memcpy(buffer + 0U, "RPTCL", 5U);
::memcpy(buffer + 5U, m_id, 4U);
write(buffer, 9U);
}
m_socket.close();
m_retryTimer.stop();
m_timeoutTimer.stop();
}
void CDMRDirectNetwork::clock(unsigned int ms)
{
m_retryTimer.clock(ms);
if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) {
switch (m_status) {
case STATUS::WAITING_CONNECT:
writeLogin();
m_status = STATUS::WAITING_LOGIN;
break;
case STATUS::WAITING_LOGIN:
writeLogin();
break;
case STATUS::WAITING_AUTHORISATION:
writeAuthorisation();
break;
case STATUS::WAITING_OPTIONS:
writeOptions();
break;
case STATUS::WAITING_CONFIG:
writeConfig();
break;
case STATUS::RUNNING:
writePing();
break;
default:
break;
}
m_retryTimer.start();
}
sockaddr_storage address;
unsigned int addrlen;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrlen);
if (length < 0) {
LogError("DMR, Socket has failed, retrying connection to the master");
close(false);
open();
return;
}
if (length > 0) {
if (!CUDPSocket::match(m_addr, address)) {
LogMessage("DMR, packet received from an invalid source");
return;
}
if (m_debug)
CUtils::dump(1U, "DMR, Network Received", m_buffer, length);
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
if (m_enabled) {
unsigned char len = length;
m_rxData.addData(&len, 1U);
m_rxData.addData(m_buffer, len);
}
} else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) {
if (m_status == STATUS::RUNNING) {
LogWarning("DMR, Login to the master has failed, retrying login ...");
m_status = STATUS::WAITING_LOGIN;
m_timeoutTimer.start();
m_retryTimer.start();
} else {
/* Once the modem death spiral has been prevented in Modem.cpp
the Network sometimes times out and reaches here.
We want it to reconnect so... */
LogError("DMR, Login to the master has failed, retrying network ...");
close(false);
open();
return;
}
} else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) {
switch (m_status) {
case STATUS::WAITING_LOGIN:
LogDebug("DMR, Sending authorisation");
::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t));
writeAuthorisation();
m_status = STATUS::WAITING_AUTHORISATION;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case STATUS::WAITING_AUTHORISATION:
LogDebug("DMR, Sending configuration");
writeConfig();
m_status = STATUS::WAITING_CONFIG;
m_timeoutTimer.start();
m_retryTimer.start();
break;
case STATUS::WAITING_CONFIG:
if (m_options.empty()) {
LogMessage("DMR, Logged into the master successfully");
m_status = STATUS::RUNNING;
} else {
LogDebug("DMR, Sending options");
writeOptions();
m_status = STATUS::WAITING_OPTIONS;
}
m_timeoutTimer.start();
m_retryTimer.start();
break;
case STATUS::WAITING_OPTIONS:
LogMessage("DMR, Logged into the master successfully");
m_status = STATUS::RUNNING;
m_timeoutTimer.start();
m_retryTimer.start();
break;
default:
break;
}
} else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) {
LogError("DMR, Master is closing down");
close(false);
open();
} else if (::memcmp(m_buffer, "MSTPONG", 7U) == 0) {
m_timeoutTimer.start();
} else if (::memcmp(m_buffer, "RPTSBKN", 7U) == 0) {
m_beacon = true;
} else {
CUtils::dump("DMR, Unknown packet from the master", m_buffer, length);
}
}
m_timeoutTimer.clock(ms);
if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) {
LogError("DMR, Connection to the master has timed out, retrying connection");
close(false);
open();
}
}
bool CDMRDirectNetwork::writeLogin()
{
unsigned char buffer[8U];
::memcpy(buffer + 0U, "RPTL", 4U);
::memcpy(buffer + 4U, m_id, 4U);
return write(buffer, 8U);
}
bool CDMRDirectNetwork::writeAuthorisation()
{
size_t size = m_password.size();
unsigned char* in = new unsigned char[size + sizeof(uint32_t)];
::memcpy(in, m_salt, sizeof(uint32_t));
for (size_t i = 0U; i < size; i++)
in[i + sizeof(uint32_t)] = m_password.at(i);
unsigned char out[40U];
::memcpy(out + 0U, "RPTK", 4U);
::memcpy(out + 4U, m_id, 4U);
CSHA256 sha256;
sha256.buffer(in, (unsigned int)(size + sizeof(uint32_t)), out + 8U);
delete[] in;
return write(out, 40U);
}
bool CDMRDirectNetwork::writeOptions()
{
char buffer[300U];
::memcpy(buffer + 0U, "RPTO", 4U);
::memcpy(buffer + 4U, m_id, 4U);
::strcpy(buffer + 8U, m_options.c_str());
return write((unsigned char*)buffer, (unsigned int)m_options.length() + 8U);
}
bool CDMRDirectNetwork::writeConfig()
{
const char* software;
char slots = '0';
if (m_duplex) {
if (m_slot1 && m_slot2)
slots = '3';
else if (m_slot1 && !m_slot2)
slots = '1';
else if (!m_slot1 && m_slot2)
slots = '2';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
default:
software = "MMDVM_Unknown";
break;
}
} else {
slots = '4';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM_DMO";
break;
case HW_TYPE::DVMEGA:
software = "MMDVM_DVMega";
break;
case HW_TYPE::MMDVM_ZUMSPOT:
software = "MMDVM_ZUMspot";
break;
case HW_TYPE::MMDVM_HS_HAT:
software = "MMDVM_MMDVM_HS_Hat";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
case HW_TYPE::NANO_DV:
software = "MMDVM_Nano_DV";
break;
case HW_TYPE::D2RG_MMDVM_HS:
software = "MMDVM_D2RG_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
default:
software = "MMDVM_Unknown";
break;
}
}
char buffer[400U];
::memcpy(buffer + 0U, "RPTC", 4U);
::memcpy(buffer + 4U, m_id, 4U);
char latitude[20U];
::sprintf(latitude, "%08f", m_latitude);
char longitude[20U];
::sprintf(longitude, "%09f", m_longitude);
unsigned int power = m_power;
if (power > 99U)
power = 99U;
int height = m_height;
if (height > 999)
height = 999;
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(),
m_description.c_str(), slots, m_url.c_str(), m_version, software);
return write((unsigned char*)buffer, 302U);
}
bool CDMRDirectNetwork::writePing()
{
unsigned char buffer[11U];
::memcpy(buffer + 0U, "RPTPING", 7U);
::memcpy(buffer + 7U, m_id, 4U);
return write(buffer, 11U);
}
bool CDMRDirectNetwork::wantsBeacon()
{
bool beacon = m_beacon;
m_beacon = false;
return beacon;
}
bool CDMRDirectNetwork::write(const unsigned char* data, unsigned int length)
{
assert(data != nullptr);
assert(length > 0U);
if (m_debug)
CUtils::dump(1U, "DMR Network Transmitted", data, length);
bool ret = m_socket.write(data, length, m_addr, m_addrLen);
if (!ret) {
LogError("DMR, Socket has failed when writing data to the master, retrying connection");
close(false);
open();
return false;
}
return true;
}

View file

@ -1,121 +0,0 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 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(DMRDirectNetwork_H)
#define DMRDirectNetwork_H
#include "DMRNetwork.h"
#include "UDPSocket.h"
#include "Timer.h"
#include "RingBuffer.h"
#include "DMRData.h"
#include <string>
#include <cstdint>
#include <random>
class CDMRDirectNetwork : public IDMRNetwork
{
public:
CDMRDirectNetwork(const std::string& remoteAddress, unsigned short remotePort, const std::string& localAddress, unsigned short localPort, unsigned int id, const std::string& password, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug);
virtual ~CDMRDirectNetwork();
virtual void setOptions(const std::string& options);
virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url);
virtual bool open();
virtual void enable(bool enabled);
virtual bool read(CDMRData& data);
virtual bool write(const CDMRData& data);
virtual bool writeRadioPosition(unsigned int id, const unsigned char* data);
virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data);
virtual bool wantsBeacon();
virtual void clock(unsigned int ms);
virtual bool isConnected() const;
virtual void close(bool sayGoodbye);
private:
std::string m_address;
unsigned short m_port;
sockaddr_storage m_addr;
unsigned int m_addrLen;
uint8_t* m_id;
std::string m_password;
bool m_duplex;
const char* m_version;
bool m_debug;
CUDPSocket m_socket;
bool m_enabled;
bool m_slot1;
bool m_slot2;
HW_TYPE m_hwType;
enum class STATUS {
WAITING_CONNECT,
WAITING_LOGIN,
WAITING_AUTHORISATION,
WAITING_CONFIG,
WAITING_OPTIONS,
RUNNING
};
STATUS m_status;
CTimer m_retryTimer;
CTimer m_timeoutTimer;
unsigned char* m_buffer;
uint32_t* m_streamId;
unsigned char* m_salt;
CRingBuffer<unsigned char> m_rxData;
std::string m_options;
std::mt19937 m_random;
std::string m_callsign;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
unsigned int m_power;
unsigned int m_colorCode;
float m_latitude;
float m_longitude;
int m_height;
std::string m_location;
std::string m_description;
std::string m_url;
bool m_beacon;
bool writeLogin();
bool writeAuthorisation();
bool writeOptions();
bool writeConfig();
bool writePing();
bool write(const unsigned char* data, unsigned int length);
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023,2025 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
@ -17,9 +17,10 @@
*/
#include "DMREMB.h"
#include "QR1676.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -98,3 +99,6 @@ void CDMREMB::setLCSS(unsigned char lcss)
{
m_LCSS = lcss;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023 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
@ -19,6 +19,10 @@
#if !defined(DMREMB_H)
#define DMREMB_H
#include "Defines.h"
#if defined(USE_DMR)
class CDMREMB
{
public:
@ -44,3 +48,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2023,2025 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
@ -17,11 +17,12 @@
*/
#include "DMREmbeddedData.h"
#include "Hamming.h"
#include "Utils.h"
#include "CRC.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -320,3 +321,6 @@ bool CDMREmbeddedData::getRawData(unsigned char* data) const
return true;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2023,2025 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
@ -20,8 +20,11 @@
#define DMREmbeddedData_H
#include "DMRDefines.h"
#include "Defines.h"
#include "DMRLC.h"
#if defined(USE_DMR)
enum class LC_STATE {
NONE,
FIRST,
@ -61,3 +64,5 @@ private:
};
#endif
#endif

View file

@ -1,6 +1,6 @@
/*
* Copyright (C) 2012 by Ian Wraith
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2012 by Ian Wraith
* Copyright (C) 2015,2016,2023,2025 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
@ -18,12 +18,13 @@
*/
#include "DMRFullLC.h"
#include "DMRDefines.h"
#include "RS129.h"
#include "Utils.h"
#include "Log.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -97,3 +98,6 @@ void CDMRFullLC::encode(const CDMRLC& lc, unsigned char* data, unsigned char typ
m_bptc.encode(lcData, data);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023 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
@ -21,8 +21,10 @@
#include "DMRLC.h"
#include "DMRSlotType.h"
#include "BPTC19696.h"
#include "Defines.h"
#if defined(USE_DMR)
class CDMRFullLC
{
@ -40,3 +42,5 @@ private:
#endif
#endif

View file

@ -1,450 +0,0 @@
/*
* Copyright (C) 2015-2021,2025 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 "DMRGatewayNetwork.h"
#include "Utils.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRGatewayNetwork::CDMRGatewayNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) :
m_addressStr(address),
m_addr(),
m_addrLen(0U),
m_port(port),
m_id(nullptr),
m_duplex(duplex),
m_version(version),
m_debug(debug),
m_socket(localAddress, localPort),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_hwType(hwType),
m_buffer(nullptr),
m_streamId(nullptr),
m_rxData(1000U, "DMR Network"),
m_beacon(false),
m_random(),
m_callsign(),
m_rxFrequency(0U),
m_txFrequency(0U),
m_power(0U),
m_colorCode(0U),
m_pingTimer(1000U, 10U)
{
assert(!address.empty());
assert(port > 0U);
assert(id > 1000U);
if (CUDPSocket::lookup(m_addressStr, m_port, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
m_buffer = new unsigned char[BUFFER_LENGTH];
m_id = new uint8_t[4U];
m_streamId = new uint32_t[2U];
m_id[0U] = id >> 24;
m_id[1U] = id >> 16;
m_id[2U] = id >> 8;
m_id[3U] = id >> 0;
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
m_streamId[0U] = dist(m_random);
m_streamId[1U] = dist(m_random);
}
CDMRGatewayNetwork::~CDMRGatewayNetwork()
{
delete[] m_buffer;
delete[] m_streamId;
delete[] m_id;
}
void CDMRGatewayNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url)
{
m_callsign = callsign;
m_rxFrequency = rxFrequency;
m_txFrequency = txFrequency;
m_power = power;
m_colorCode = colorCode;
}
void CDMRGatewayNetwork::setOptions(const std::string& options)
{
}
bool CDMRGatewayNetwork::open()
{
if (m_addrLen == 0U) {
LogError("DMR, Unable to resolve the address of the DMR Network");
return false;
}
LogMessage("DMR, Opening DMR Network");
bool ret = m_socket.open(m_addr);
if (ret)
m_pingTimer.start();
return ret;
}
void CDMRGatewayNetwork::enable(bool enabled)
{
if (!enabled && m_enabled)
m_rxData.clear();
m_enabled = enabled;
}
bool CDMRGatewayNetwork::read(CDMRData& data)
{
if (m_rxData.isEmpty())
return false;
unsigned char length = 0U;
m_rxData.getData(&length, 1U);
m_rxData.getData(m_buffer, length);
// Is this a data packet?
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
return false;
unsigned char seqNo = m_buffer[4U];
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0);
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
// DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
return false;
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP;
data.setSeqNo(seqNo);
data.setSlotNo(slotNo);
data.setSrcId(srcId);
data.setDstId(dstId);
data.setFLCO(flco);
bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U;
bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U;
if (dataSync) {
unsigned char dataType = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(dataType);
data.setN(0U);
} else if (voiceSync) {
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE_SYNC);
data.setN(0U);
} else {
unsigned char n = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE);
data.setN(n);
}
return true;
}
bool CDMRGatewayNetwork::write(const CDMRData& data)
{
unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH);
buffer[0U] = 'D';
buffer[1U] = 'M';
buffer[2U] = 'R';
buffer[3U] = 'D';
unsigned int srcId = data.getSrcId();
buffer[5U] = srcId >> 16;
buffer[6U] = srcId >> 8;
buffer[7U] = srcId >> 0;
unsigned int dstId = data.getDstId();
buffer[8U] = dstId >> 16;
buffer[9U] = dstId >> 8;
buffer[10U] = dstId >> 0;
::memcpy(buffer + 11U, m_id, 4U);
unsigned int slotNo = data.getSlotNo();
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
buffer[15U] = slotNo == 1U ? 0x00U : 0x80U;
FLCO flco = data.getFLCO();
buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U;
unsigned int slotIndex = slotNo - 1U;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
unsigned char dataType = data.getDataType();
if (dataType == DT_VOICE_SYNC) {
buffer[15U] |= 0x10U;
} else if (dataType == DT_VOICE) {
buffer[15U] |= data.getN();
} else {
if (dataType == DT_VOICE_LC_HEADER)
m_streamId[slotIndex] = dist(m_random);
if (dataType == DT_CSBK || dataType == DT_DATA_HEADER)
m_streamId[slotIndex] = dist(m_random);
buffer[15U] |= (0x20U | dataType);
}
buffer[4U] = data.getSeqNo();
::memcpy(buffer + 16U, m_streamId + slotIndex, 4U);
data.getData(buffer + 20U);
buffer[53U] = data.getBER();
buffer[54U] = data.getRSSI();
write(buffer, HOMEBREW_DATA_PACKET_LENGTH);
return true;
}
bool CDMRGatewayNetwork::writeRadioPosition(unsigned int id, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRG", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
::memcpy(buffer + 7U, data + 2U, 7U);
return write(buffer, 14U);
}
bool CDMRGatewayNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRA", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
buffer[7U] = type;
::memcpy(buffer + 8U, data + 2U, 7U);
return write(buffer, 15U);
}
bool CDMRGatewayNetwork::isConnected() const
{
return (m_addrLen != 0);
}
void CDMRGatewayNetwork::close(bool sayGoodbye)
{
LogMessage("DMR, Closing DMR Network");
m_socket.close();
}
void CDMRGatewayNetwork::clock(unsigned int ms)
{
m_pingTimer.clock(ms);
if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) {
writeConfig();
m_pingTimer.start();
}
sockaddr_storage address;
unsigned int addrLen;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrLen);
if (length <= 0)
return;
if (!CUDPSocket::match(m_addr, address)) {
LogMessage("DMR, packet received from an invalid source");
return;
}
if (m_debug)
CUtils::dump(1U, "DMR Network Received", m_buffer, length);
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
if (m_enabled) {
unsigned char len = length;
m_rxData.addData(&len, 1U);
m_rxData.addData(m_buffer, len);
}
} else if (::memcmp(m_buffer, "DMRP", 4U) == 0) {
;
} else if (::memcmp(m_buffer, "DMRB", 4U) == 0) {
m_beacon = true;
} else {
CUtils::dump("DMR, unknown packet from the DMR Network", m_buffer, length);
}
}
bool CDMRGatewayNetwork::writeConfig()
{
const char* software;
char slots = '0';
if (m_duplex) {
if (m_slot1 && m_slot2)
slots = '3';
else if (m_slot1 && !m_slot2)
slots = '1';
else if (!m_slot1 && m_slot2)
slots = '2';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
default:
software = "MMDVM_Unknown";
break;
}
} else {
slots = '4';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM_DMO";
break;
case HW_TYPE::DVMEGA:
software = "MMDVM_DVMega";
break;
case HW_TYPE::MMDVM_ZUMSPOT:
software = "MMDVM_ZUMspot";
break;
case HW_TYPE::MMDVM_HS_HAT:
software = "MMDVM_MMDVM_HS_Hat";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
case HW_TYPE::NANO_DV:
software = "MMDVM_Nano_DV";
break;
case HW_TYPE::D2RG_MMDVM_HS:
software = "MMDVM_D2RG_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
case HW_TYPE::OPENGD77_HS:
software = "MMDVM_OpenGD77_HS";
break;
case HW_TYPE::SKYBRIDGE:
software = "MMDVM_SkyBridge";
break;
default:
software = "MMDVM_Unknown";
break;
}
}
unsigned int power = m_power;
if (power > 99U)
power = 99U;
char buffer[150U];
::memcpy(buffer + 0U, "DMRC", 4U);
::memcpy(buffer + 4U, m_id, 4U);
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%c%-40.40s%-40.40s",
m_callsign.c_str(), m_rxFrequency, m_txFrequency, power, m_colorCode, slots, m_version,
software);
return write((unsigned char*)buffer, 119U);
}
bool CDMRGatewayNetwork::wantsBeacon()
{
bool beacon = m_beacon;
m_beacon = false;
return beacon;
}
bool CDMRGatewayNetwork::write(const unsigned char* data, unsigned int length)
{
assert(data != nullptr);
assert(length > 0U);
if (m_debug)
CUtils::dump(1U, "DMR Network Transmitted", data, length);
bool ret = m_socket.write(data, length, m_addr, m_addrLen);
if (!ret) {
LogError("DMR, socket error when writing to the DMR Network");
return false;
}
return true;
}

View file

@ -1,94 +0,0 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021 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(DMRGatewayNetwork_H)
#define DMRGatewayNetwork_H
#include "DMRNetwork.h"
#include "UDPSocket.h"
#include "Timer.h"
#include "RingBuffer.h"
#include "DMRData.h"
#include "Defines.h"
#include <string>
#include <cstdint>
#include <random>
class CDMRGatewayNetwork : public IDMRNetwork
{
public:
CDMRGatewayNetwork(const std::string& remoteAddress, unsigned short remotePort, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug);
virtual ~CDMRGatewayNetwork();
virtual void setOptions(const std::string& options);
virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url);
virtual bool open();
virtual void enable(bool enabled);
virtual bool read(CDMRData& data);
virtual bool write(const CDMRData& data);
virtual bool writeRadioPosition(unsigned int id, const unsigned char* data);
virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data);
virtual bool wantsBeacon();
virtual void clock(unsigned int ms);
virtual bool isConnected() const;
virtual void close(bool sayGoodbye);
private:
std::string m_addressStr;
sockaddr_storage m_addr;
unsigned int m_addrLen;
unsigned short m_port;
uint8_t* m_id;
bool m_duplex;
const char* m_version;
bool m_debug;
CUDPSocket m_socket;
bool m_enabled;
bool m_slot1;
bool m_slot2;
HW_TYPE m_hwType;
unsigned char* m_buffer;
uint32_t* m_streamId;
CRingBuffer<unsigned char> m_rxData;
bool m_beacon;
std::mt19937 m_random;
std::string m_callsign;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
unsigned int m_power;
unsigned int m_colorCode;
CTimer m_pingTimer;
bool writeConfig();
bool write(const unsigned char* data, unsigned int length);
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2019,2021,2022,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2019,2021,2022,2023,2025 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
@ -17,9 +17,10 @@
*/
#include "DMRLC.h"
#include "Utils.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -216,3 +217,6 @@ void CDMRLC::setDstId(unsigned int id)
{
m_dstId = id;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2019,2021,2022 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2019,2021,2022,2023 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
@ -20,6 +20,9 @@
#define DMRLC_H
#include "DMRDefines.h"
#include "Defines.h"
#if defined(USE_DMR)
class CDMRLC
{
@ -63,3 +66,5 @@ private:
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2017,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2023,2025 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
@ -20,6 +20,8 @@
#include "Timer.h"
#include "Log.h"
#if defined(USE_DMR) || defined(USE_P25)
#include <cstdio>
#include <cstdlib>
#include <cstring>
@ -134,3 +136,6 @@ bool CDMRLookup::exists(unsigned int id)
{
return m_table.lookup(id, nullptr);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2017,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2021,2023 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
@ -21,6 +21,9 @@
#include "Thread.h"
#include "UserDB.h"
#include "Defines.h"
#if defined(USE_DMR) || defined(USE_P25)
#include <string>
@ -51,3 +54,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2021,2023 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
@ -17,7 +17,434 @@
*/
#include "DMRNetwork.h"
#include "Utils.h"
#include "Log.h"
IDMRNetwork::~IDMRNetwork()
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
const unsigned int BUFFER_LENGTH = 500U;
const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U;
CDMRNetwork::CDMRNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) :
m_addressStr(address),
m_addr(),
m_addrLen(0U),
m_port(port),
m_id(nullptr),
m_duplex(duplex),
m_version(version),
m_debug(debug),
m_socket(localAddress, localPort),
m_enabled(false),
m_slot1(slot1),
m_slot2(slot2),
m_hwType(hwType),
m_buffer(nullptr),
m_streamId(nullptr),
m_rxData(1000U, "DMR Network"),
m_beacon(false),
m_random(),
m_callsign(),
m_rxFrequency(0U),
m_txFrequency(0U),
m_power(0U),
m_colorCode(0U),
m_pingTimer(1000U, 10U)
{
assert(!address.empty());
assert(port > 0U);
assert(id > 1000U);
if (CUDPSocket::lookup(m_addressStr, m_port, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
m_buffer = new unsigned char[BUFFER_LENGTH];
m_id = new uint8_t[4U];
m_streamId = new uint32_t[2U];
m_id[0U] = id >> 24;
m_id[1U] = id >> 16;
m_id[2U] = id >> 8;
m_id[3U] = id >> 0;
std::random_device rd;
std::mt19937 mt(rd());
m_random = mt;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
m_streamId[0U] = dist(m_random);
m_streamId[1U] = dist(m_random);
}
CDMRNetwork::~CDMRNetwork()
{
delete[] m_buffer;
delete[] m_streamId;
delete[] m_id;
}
void CDMRNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode)
{
m_callsign = callsign;
m_rxFrequency = rxFrequency;
m_txFrequency = txFrequency;
m_power = power;
m_colorCode = colorCode;
}
bool CDMRNetwork::open()
{
if (m_addrLen == 0U) {
LogError("Unable to resolve the address of the DMR Network");
return false;
}
LogMessage("DMR, Opening DMR Network");
bool ret = m_socket.open(m_addr);
if (ret)
m_pingTimer.start();
return ret;
}
void CDMRNetwork::enable(bool enabled)
{
if (!enabled && m_enabled)
m_rxData.clear();
m_enabled = enabled;
}
bool CDMRNetwork::read(CDMRData& data)
{
if (m_rxData.isEmpty())
return false;
unsigned char length = 0U;
m_rxData.getData(&length, 1U);
m_rxData.getData(m_buffer, length);
// Is this a data packet?
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
return false;
unsigned char seqNo = m_buffer[4U];
unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0);
unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0);
unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U;
// DMO mode slot disabling
if (slotNo == 1U && !m_duplex)
return false;
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP;
data.setSeqNo(seqNo);
data.setSlotNo(slotNo);
data.setSrcId(srcId);
data.setDstId(dstId);
data.setFLCO(flco);
bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U;
bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U;
if (dataSync) {
unsigned char dataType = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(dataType);
data.setN(0U);
} else if (voiceSync) {
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE_SYNC);
data.setN(0U);
} else {
unsigned char n = m_buffer[15U] & 0x0FU;
data.setData(m_buffer + 20U);
data.setDataType(DT_VOICE);
data.setN(n);
}
return true;
}
bool CDMRNetwork::write(const CDMRData& data)
{
unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH];
::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH);
buffer[0U] = 'D';
buffer[1U] = 'M';
buffer[2U] = 'R';
buffer[3U] = 'D';
unsigned int srcId = data.getSrcId();
buffer[5U] = srcId >> 16;
buffer[6U] = srcId >> 8;
buffer[7U] = srcId >> 0;
unsigned int dstId = data.getDstId();
buffer[8U] = dstId >> 16;
buffer[9U] = dstId >> 8;
buffer[10U] = dstId >> 0;
::memcpy(buffer + 11U, m_id, 4U);
unsigned int slotNo = data.getSlotNo();
// Individual slot disabling
if (slotNo == 1U && !m_slot1)
return false;
if (slotNo == 2U && !m_slot2)
return false;
buffer[15U] = slotNo == 1U ? 0x00U : 0x80U;
FLCO flco = data.getFLCO();
buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U;
unsigned int slotIndex = slotNo - 1U;
std::uniform_int_distribution<uint32_t> dist(0x00000001, 0xfffffffe);
unsigned char dataType = data.getDataType();
if (dataType == DT_VOICE_SYNC) {
buffer[15U] |= 0x10U;
} else if (dataType == DT_VOICE) {
buffer[15U] |= data.getN();
} else {
if (dataType == DT_VOICE_LC_HEADER)
m_streamId[slotIndex] = dist(m_random);
if (dataType == DT_CSBK || dataType == DT_DATA_HEADER)
m_streamId[slotIndex] = dist(m_random);
buffer[15U] |= (0x20U | dataType);
}
buffer[4U] = data.getSeqNo();
::memcpy(buffer + 16U, m_streamId + slotIndex, 4U);
data.getData(buffer + 20U);
buffer[53U] = data.getBER();
buffer[54U] = data.getRSSI();
write(buffer, HOMEBREW_DATA_PACKET_LENGTH);
return true;
}
bool CDMRNetwork::writeRadioPosition(unsigned int id, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRG", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
::memcpy(buffer + 7U, data + 2U, 7U);
return write(buffer, 14U);
}
bool CDMRNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data)
{
unsigned char buffer[20U];
::memcpy(buffer + 0U, "DMRA", 4U);
buffer[4U] = id >> 16;
buffer[5U] = id >> 8;
buffer[6U] = id >> 0;
buffer[7U] = type;
::memcpy(buffer + 8U, data + 2U, 7U);
return write(buffer, 15U);
}
bool CDMRNetwork::isConnected() const
{
return (m_addrLen != 0);
}
void CDMRNetwork::close(bool sayGoodbye)
{
LogMessage("DMR, Closing DMR Network");
m_socket.close();
}
void CDMRNetwork::clock(unsigned int ms)
{
m_pingTimer.clock(ms);
if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) {
writeConfig();
m_pingTimer.start();
}
sockaddr_storage address;
unsigned int addrLen;
int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrLen);
if (length <= 0)
return;
if (!CUDPSocket::match(m_addr, address)) {
LogMessage("DMR, packet received from an invalid source");
return;
}
if (m_debug)
CUtils::dump(1U, "DMR Network Received", m_buffer, length);
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
if (m_enabled) {
unsigned char len = length;
m_rxData.addData(&len, 1U);
m_rxData.addData(m_buffer, len);
}
} else if (::memcmp(m_buffer, "DMRP", 4U) == 0) {
;
} else if (::memcmp(m_buffer, "DMRB", 4U) == 0) {
m_beacon = true;
} else {
CUtils::dump("DMR, unknown packet from the DMR Network", m_buffer, length);
}
}
bool CDMRNetwork::writeConfig()
{
const char* software;
char slots = '0';
if (m_duplex) {
if (m_slot1 && m_slot2)
slots = '3';
else if (m_slot1 && !m_slot2)
slots = '1';
else if (!m_slot1 && m_slot2)
slots = '2';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
default:
software = "MMDVM_Unknown";
break;
}
} else {
slots = '4';
switch (m_hwType) {
case HW_TYPE::MMDVM:
software = "MMDVM_DMO";
break;
case HW_TYPE::DVMEGA:
software = "MMDVM_DVMega";
break;
case HW_TYPE::MMDVM_ZUMSPOT:
software = "MMDVM_ZUMspot";
break;
case HW_TYPE::MMDVM_HS_HAT:
software = "MMDVM_MMDVM_HS_Hat";
break;
case HW_TYPE::MMDVM_HS_DUAL_HAT:
software = "MMDVM_MMDVM_HS_Dual_Hat";
break;
case HW_TYPE::NANO_HOTSPOT:
software = "MMDVM_Nano_hotSPOT";
break;
case HW_TYPE::NANO_DV:
software = "MMDVM_Nano_DV";
break;
case HW_TYPE::D2RG_MMDVM_HS:
software = "MMDVM_D2RG_MMDVM_HS";
break;
case HW_TYPE::MMDVM_HS:
software = "MMDVM_MMDVM_HS";
break;
case HW_TYPE::OPENGD77_HS:
software = "MMDVM_OpenGD77_HS";
break;
case HW_TYPE::SKYBRIDGE:
software = "MMDVM_SkyBridge";
break;
default:
software = "MMDVM_Unknown";
break;
}
}
unsigned int power = m_power;
if (power > 99U)
power = 99U;
char buffer[150U];
::memcpy(buffer + 0U, "DMRC", 4U);
::memcpy(buffer + 4U, m_id, 4U);
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%c%-40.40s%-40.40s",
m_callsign.c_str(), m_rxFrequency, m_txFrequency, power, m_colorCode, slots, m_version,
software);
return write((unsigned char*)buffer, 119U);
}
bool CDMRNetwork::wantsBeacon()
{
bool beacon = m_beacon;
m_beacon = false;
return beacon;
}
bool CDMRNetwork::write(const unsigned char* data, unsigned int length)
{
assert(data != nullptr);
assert(length > 0U);
if (m_debug)
CUtils::dump(1U, "DMR Network Transmitted", data, length);
bool ret = m_socket.write(data, length, m_addr, m_addrLen);
if (!ret) {
LogError("DMR, socket error when writing to the DMR Network");
return false;
}
return true;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2023 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
@ -19,40 +19,78 @@
#if !defined(DMRNetwork_H)
#define DMRNetwork_H
#include "UDPSocket.h"
#include "Timer.h"
#include "RingBuffer.h"
#include "DMRData.h"
#include "Defines.h"
#if defined(USE_DMR)
#include <string>
#include <cstdint>
#include <random>
class IDMRNetwork
class CDMRNetwork
{
public:
virtual ~IDMRNetwork() = 0;
CDMRNetwork(const std::string& gatewayAddress, unsigned short gatewayPort, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug);
~CDMRNetwork();
virtual void setOptions(const std::string& options) = 0;
void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode);
virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url) = 0;
bool open();
virtual bool open() = 0;
void enable(bool enabled);
virtual void enable(bool enabled) = 0;
bool read(CDMRData& data);
virtual bool read(CDMRData& data) = 0;
bool write(const CDMRData& data);
virtual bool write(const CDMRData& data) = 0;
bool writeRadioPosition(unsigned int id, const unsigned char* data);
virtual bool writeRadioPosition(unsigned int id, const unsigned char* data) = 0;
bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data);
virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data) = 0;
bool wantsBeacon();
virtual bool wantsBeacon() = 0;
void clock(unsigned int ms);
virtual void clock(unsigned int ms) = 0;
bool isConnected() const;
virtual bool isConnected() const = 0;
virtual void close(bool sayGoodbye) = 0;
void close(bool sayGoodbye);
private:
std::string m_addressStr;
sockaddr_storage m_addr;
unsigned int m_addrLen;
unsigned short m_port;
uint8_t* m_id;
bool m_duplex;
const char* m_version;
bool m_debug;
CUDPSocket m_socket;
bool m_enabled;
bool m_slot1;
bool m_slot2;
HW_TYPE m_hwType;
unsigned char* m_buffer;
uint32_t* m_streamId;
CRingBuffer<unsigned char> m_rxData;
bool m_beacon;
std::mt19937 m_random;
std::string m_callsign;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
unsigned int m_power;
unsigned int m_colorCode;
CTimer m_pingTimer;
bool writeConfig();
bool write(const unsigned char* data, unsigned int length);
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023,2025 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
@ -17,10 +17,11 @@
*/
#include "DMRShortLC.h"
#include "Hamming.h"
#include "Utils.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -226,3 +227,6 @@ void CDMRShortLC::encodeExtractBinary(unsigned char* data)
CUtils::bitsToByteBE(m_rawData + 56U, data[7U]);
CUtils::bitsToByteBE(m_rawData + 64U, data[8U]);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023 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
@ -19,6 +19,10 @@
#if !defined(DMRSHORTLC_H)
#define DMRSHORTLC_H
#include "Defines.h"
#if defined(USE_DMR)
class CDMRShortLC
{
public:
@ -45,3 +49,6 @@ private:
};
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -29,14 +29,17 @@
#include "AMBEFEC.h"
#include "DMRSlot.h"
#include "DMRData.h"
#include "Display.h"
#include "Defines.h"
#include "Timer.h"
#include "Modem.h"
#include "DMRLC.h"
#if defined(USE_DMR)
#include <vector>
#include <nlohmann/json.hpp>
enum class ACTIVITY_TYPE {
NONE,
VOICE,
@ -62,7 +65,7 @@ public:
void enable(bool enabled);
static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect);
static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect);
private:
unsigned int m_slotNo;
@ -105,13 +108,16 @@ private:
bool m_netTimeout;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
int m_rssi;
int m_maxRSSI;
int m_minRSSI;
int m_aveRSSI;
unsigned int m_rssiCountTotal;
int m_rssiAccum;
unsigned int m_rssiCount;
unsigned int m_bitErrsAccum;
unsigned int m_bitsCount;
bool m_enabled;
FILE* m_fp;
static unsigned int m_colorCode;
@ -119,8 +125,7 @@ private:
static bool m_dumpTAData;
static CModem* m_modem;
static IDMRNetwork* m_network;
static CDisplay* m_display;
static CDMRNetwork* m_network;
static bool m_duplex;
static CDMRLookup* m_lookup;
static unsigned int m_hangCount;
@ -151,14 +156,33 @@ private:
void writeEndRF(bool writeEnd = false);
void writeEndNet(bool writeEnd = false);
bool openFile();
bool writeFile(const unsigned char* data);
void closeFile();
bool insertSilence(const unsigned char* data, unsigned char seqNo);
void insertSilence(unsigned int count);
static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO::GROUP, ACTIVITY_TYPE type = ACTIVITY_TYPE::NONE);
void writeJSONRSSI();
void writeJSONBER();
void writeJSONText(const unsigned char* text);
void writeJSONRF(const char* action);
void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId);
void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames);
void writeJSONRF(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId);
void writeJSONRF(const char* action, float duration, float ber);
void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI);
void writeJSONNet(const char* action);
void writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId);
void writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames);
void writeJSONNet(const char* action, float duration, float loss, float ber);
void writeJSONNet(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId);
void writeJSON(nlohmann::json& json, const char* action);
void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId);
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023,2025 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
@ -17,9 +17,10 @@
*/
#include "DMRSlotType.h"
#include "Golay2087.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -90,3 +91,6 @@ void CDMRSlotType::setDataType(unsigned char type)
{
m_dataType = type;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023 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
@ -19,6 +19,10 @@
#if !defined(DMRSLOTTYPE_H)
#define DMRSLOTTYPE_H
#include "Defines.h"
#if defined(USE_DMR)
class CDMRSlotType
{
public:
@ -40,3 +44,6 @@ private:
};
#endif
#endif

View file

@ -15,6 +15,8 @@
#include "DMRTA.h"
#include "Log.h"
#if defined(USE_DMR)
#include <cstring>
#include <cassert>
@ -134,3 +136,5 @@ bool CDMRTA::decodeTA()
return taLen >= taSize;
}
#endif

View file

@ -15,6 +15,10 @@
#ifndef DMRTA_H
#define DMRTA_H
#include "Defines.h"
#if defined(USE_DMR)
class CDMRTA {
public:
CDMRTA(unsigned int slotNo);
@ -36,3 +40,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2019,2021,2023,2025 Jonathan Naylor, G4KLX
* Copyright (C) 2015-2019,2021,2023,2025,2026 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
@ -15,7 +15,8 @@
#include "Utils.h"
#include "Sync.h"
#include "Log.h"
#include "SMeter.h"
#if defined(USE_DSTAR)
#include <cstdio>
#include <cassert>
@ -25,6 +26,9 @@
const unsigned int MAX_SYNC_BIT_ERRORS = 2U;
const unsigned int RSSI_COUNT = 3U * 21U; // 3 * 420ms = 1260ms
const unsigned int BER_COUNT = 50U * 48U; // 50 * 20ms = 1000ms
bool CallsignCompare(const std::string& arg, const unsigned char* my)
{
for (unsigned int i = 0U; i < (DSTAR_LONG_CALLSIGN_LENGTH - 1U); i++) {
@ -35,9 +39,7 @@ bool CallsignCompare(const std::string& arg, const unsigned char* my)
return true;
}
// #define DUMP_DSTAR
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector<std::string>& blackList, const std::vector<std::string>& whiteList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) :
CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector<std::string>& blackList, const std::vector<std::string>& whiteList, CDStarNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) :
m_callsign(nullptr),
m_gateway(nullptr),
m_selfOnly(selfOnly),
@ -48,7 +50,6 @@ m_remoteGateway(remoteGateway),
m_blackList(blackList),
m_whiteList(whiteList),
m_network(network),
m_display(display),
m_duplex(duplex),
m_queue(5000U, "D-Star Control"),
m_rfHeader(),
@ -75,14 +76,15 @@ m_fec(),
m_rfBits(1U),
m_netBits(1U),
m_rfErrs(0U),
m_netErrs(0U),
m_lastFrame(nullptr),
m_lastFrameValid(false),
m_rssiMapper(rssiMapper),
m_rssi(0U),
m_maxRSSI(0U),
m_minRSSI(0U),
m_aveRSSI(0U),
m_rssi(0),
m_maxRSSI(0),
m_minRSSI(0),
m_aveRSSI(0),
m_rssiCountTotal(0U),
m_rssiAccum(0),
m_rssiCount(0U),
m_enabled(true),
m_fp(nullptr),
@ -91,9 +93,11 @@ m_RFdataLookBackLen(0U),
m_RFdataLookBackIndex(0U),
m_NetdataLookBack(nullptr),
m_NetdataLookBackLen(0U),
m_NetdataLookBackIndex(0U)
m_NetdataLookBackIndex(0U),
m_bitErrsAccum(0U),
m_bitsCount(0U),
m_enabled(true)
{
assert(display != nullptr);
assert(rssiMapper != nullptr);
m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH];
@ -143,10 +147,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfHeader.getMyCall2(my2);
m_rfHeader.getYourCall(your);
if (m_rssi != 0U)
LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
if (m_rssi != 0) {
LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal));
writeJSONRF("lost", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal));
} else {
LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeJSONRF("lost", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
}
writeEndRF();
return false;
}
@ -177,20 +184,20 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
raw |= (data[43U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
if (rssi != 0)
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
int m_rssi = m_rssiMapper->interpolate(raw);
if (m_rssi != 0)
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
if (m_rssi < m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
if (m_rssi > m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
m_rssiCountTotal++;
m_rssiAccum += m_rssi;
m_rssiCountTotal++;
}
// Have we got RSSI bytes on the end of D-Star data?
@ -200,20 +207,20 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
raw |= (data[14U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
if (rssi != 0)
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
int m_rssi = m_rssiMapper->interpolate(raw);
if (m_rssi != 0)
LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi);
// RSSI is always reported as positive
m_rssi = (rssi >= 0) ? rssi : -rssi;
if (m_rssi > m_minRSSI)
if (m_rssi < m_minRSSI)
m_minRSSI = m_rssi;
if (m_rssi < m_maxRSSI)
if (m_rssi > m_maxRSSI)
m_maxRSSI = m_rssi;
m_aveRSSI += m_rssi;
m_rssiCount++;
m_rssiCountTotal++;
m_rssiAccum += m_rssi;
m_rssiCountTotal++;
}
if (type == TAG_HEADER) {
@ -223,35 +230,6 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
header.getMyCall1(my1);
// Is this a transmission destined for a repeater?
if (!header.isRepeater()) {
LogMessage("D-Star, non repeater RF header received from %8.8s", my1);
m_rfState = RPT_RF_STATE::INVALID;
return true;
}
unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall1(callsign);
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1);
m_rfState = RPT_RF_STATE::INVALID;
return true;
}
if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
return true;
}
if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
return true;
}
unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall2(gateway);
@ -261,6 +239,39 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
header.getYourCall(your);
unsigned char rpt1[DSTAR_LONG_CALLSIGN_LENGTH];
header.getRPTCall1(rpt1);
// Is this a transmission destined for a repeater?
if (!header.isRepeater()) {
LogMessage("D-Star, non repeater RF header received from %8.8s", my1);
m_rfState = RPT_RF_STATE::INVALID;
writeJSONRF("invalid", my1, my2, your);
return true;
}
// Is it for us?
if (::memcmp(rpt1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", rpt1, my1);
m_rfState = RPT_RF_STATE::INVALID;
writeJSONRF("invalid", my1, my2, your);
return true;
}
if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
writeJSONRF("rejected", my1, my2, your);
return true;
}
if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
writeJSONRF("rejected", my1, my2, your);
return true;
}
m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0;
// Only start the timeout if not already running
@ -280,6 +291,9 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCountTotal = 1U;
m_rssiAccum = m_rssi;
m_rssiCount = 1U;
m_rfSlowData.start();
@ -306,12 +320,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfState = RPT_RF_STATE::AUDIO;
if (m_netState == RPT_NET_STATE::IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
}
if (m_netState == RPT_NET_STATE::IDLE)
writeJSONRSSI();
LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your);
writeJSONRF("start", my1, my2, your);
} else if (type == TAG_EOT) {
if (m_rfState == RPT_RF_STATE::REJECTED) {
m_rfState = RPT_RF_STATE::LISTENING;
@ -341,10 +354,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfHeader.getMyCall2(my2);
m_rfHeader.getYourCall(your);
if (m_rssi != 0U)
LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount);
else
if (m_rssi != 0) {
LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal));
writeJSONRF("end", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal));
} else {
LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
writeJSONRF("end", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits));
}
writeEndRF();
}
@ -429,13 +445,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
}
if (m_rfState == RPT_RF_STATE::DATA) {
// Send the RSSI data to the display
if (m_rfN == 0U)
m_display->writeDStarRSSI(m_rssi);
writeJSONRSSI();
LogDebug("D-Star, RF fast data sequence no. %u", m_rfN);
m_rfBits += 48U;
m_bitsCount += 48U;
m_rfBits += 48U;
m_rfFrames++;
if (m_net)
@ -455,9 +471,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfN = (m_rfN + 1U) % 21U;
} else if (m_rfState == RPT_RF_STATE::AUDIO) {
// Send the RSSI data to the display
if (m_rfN == 0U)
m_display->writeDStarRSSI(m_rssi);
writeJSONRSSI();
unsigned int errors = 0U;
if (::memcmp(data + 1U, DSTAR_NULL_AMBE_DATA_BYTES_SCRAMBLED, DSTAR_VOICE_FRAME_LENGTH_BYTES) == 0) {
@ -465,11 +480,14 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
} else {
errors = m_fec.regenerateDStar(data + 1U);
LogDebug("D-Star, RF audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_display->writeDStarBER(float(errors) / 0.48F);
m_bitErrsAccum += errors;
m_rfErrs += errors;
writeJSONBER();
}
m_rfErrs += errors;
m_rfBits += 48U;
m_bitsCount += 48U;
m_rfBits += 48U;
m_rfFrames++;
if (m_rfN != 0U) {
@ -506,44 +524,48 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getMyCall1(my1);
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
m_rfHeader.getMyCall2(my2);
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getYourCall(your);
unsigned char rpt1[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getRPTCall1(rpt1);
// Is this a transmission destined for a repeater?
if (!m_rfHeader.isRepeater()) {
LogMessage("D-Star, non repeater RF header received from %8.8s", my1);
m_rfState = RPT_RF_STATE::INVALID;
writeJSONRF("invalid", my1, my2, your);
return true;
}
unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getRPTCall1(callsign);
// Is it for us?
if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1);
if (::memcmp(rpt1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) {
LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", rpt1, my1);
m_rfState = RPT_RF_STATE::INVALID;
writeJSONRF("invalid", my1, my2, your);
return true;
}
if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
writeJSONRF("rejected", my1, my2, your);
return true;
}
if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) {
LogMessage("D-Star, invalid access attempt from %8.8s", my1);
m_rfState = RPT_RF_STATE::REJECTED;
writeJSONRF("rejected", my1, my2, your);
return true;
}
unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getRPTCall2(gateway);
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
m_rfHeader.getMyCall2(my2);
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
m_rfHeader.getYourCall(your);
m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0;
// Only reset the timeout if the timeout is not running
@ -564,7 +586,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_minRSSI = m_rssi;
m_maxRSSI = m_rssi;
m_aveRSSI = m_rssi;
m_rssiCount = 1U;
m_rssiCountTotal = 1U;
if (m_duplex) {
unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U];
@ -600,10 +622,12 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
} else {
errors = m_fec.regenerateDStar(data + 1U);
LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F);
m_bitErrsAccum += errors;
m_rfErrs += errors;
}
m_rfErrs += errors;
m_rfBits += 48U;
m_bitsCount += 48U;
m_rfBits += 48U;
if (m_net)
writeNetworkDataRF(data, errors, false);
@ -615,15 +639,15 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len)
m_rfState = RPT_RF_STATE::AUDIO;
m_rfN = (m_rfN + 1U) % 21U;
if (m_netState == RPT_NET_STATE::IDLE) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " ");
m_display->writeDStarRSSI(m_rssi);
m_display->writeDStarBER(float(errors) / 0.48F);
writeJSONRSSI();
writeJSONBER();
}
LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your);
m_rfN = (m_rfN + 1U) % 21U;
writeJSONRF("late_entry", my1, my2, your);
}
} else {
CUtils::dump("D-Star, unknown data from modem", data, DSTAR_FRAME_LENGTH_BYTES + 1U);
@ -654,8 +678,6 @@ void CDStarControl::writeEndRF()
m_rfTimeoutTimer.stop();
if (m_netState == RPT_NET_STATE::IDLE) {
m_display->clearDStar();
m_ackTimer.start();
if (m_network != nullptr)
@ -669,18 +691,12 @@ void CDStarControl::writeEndNet()
m_lastFrameValid = false;
m_display->clearDStar();
m_netTimeoutTimer.stop();
m_networkWatchdog.stop();
m_packetTimer.stop();
if (m_network != nullptr)
m_network->reset();
#if defined(DUMP_DSTAR)
closeFile();
#endif
}
void CDStarControl::writeNetwork()
@ -733,7 +749,6 @@ void CDStarControl::writeNetwork()
m_netN = 20U;
m_netBits = 1U;
m_netErrs = 0U;
if (m_remoteGateway) {
header.setRepeater(true);
@ -744,22 +759,18 @@ void CDStarControl::writeNetwork()
writeQueueHeaderNet(data);
#if defined(DUMP_DSTAR)
openFile();
writeFile(data + 1U, length - 1U);
#endif
m_netState = RPT_NET_STATE::AUDIO;
LINK_STATUS status = LINK_STATUS::NONE;
unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH];
m_network->getStatus(status, reflector);
if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) reflector);
LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s via %8.8s", my1, my2, your, reflector);
writeJSONNet("start", my1, my2, your, reflector);
} else {
m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) " ");
LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s", my1, my2, your);
}
writeJSONNet("start", my1, my2, your);
}
m_elapsed.start();
} else if (type == TAG_EOT) {
@ -770,10 +781,6 @@ void CDStarControl::writeNetwork()
data[1U] = TAG_EOT;
#if defined(DUMP_DSTAR)
writeFile(data + 1U, length - 1U);
closeFile();
#endif
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
@ -783,7 +790,8 @@ void CDStarControl::writeNetwork()
// We've received the header and EOT haven't we?
m_netFrames += 2U;
LogMessage("D-Star, received network end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, %u%% packet loss, BER: %.1f%%", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits));
LogMessage("D-Star, received network end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, %u%% packet loss", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames);
writeJSONNet("end", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames));
writeEndNet();
} else if (type == TAG_DATA) {
@ -853,7 +861,7 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length)
if (m_netState == RPT_NET_STATE::DATA) {
unsigned char type = m_netSlowData.getType(true);
// as per specs chapter 6.1.3, this could be any value bewtween 0x81 and 0x9C as one fast data block can only contain 28 bytes 0x80 + 28d = 0x9C
if(((type & DSTAR_SLOW_DATA_TYPE_FASTDATA_MASK) == DSTAR_SLOW_DATA_TYPE_FASTDATA) && type > 0x9CU) {
if (((type & DSTAR_SLOW_DATA_TYPE_FASTDATA_MASK) == DSTAR_SLOW_DATA_TYPE_FASTDATA) && type > 0x9CU) {
LogMessage("Invalid fast data type 0x%02X received, returning to audio mode (Net)", type);
m_netState = RPT_NET_STATE::AUDIO;
}
@ -890,9 +898,6 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length)
m_packetTimer.start();
m_netFrames++;
#if defined(DUMP_DSTAR)
writeFile(data + 1U, length - 1U);
#endif
writeQueueDataNet(data + 1U);
}
@ -918,10 +923,9 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length)
m_packetTimer.start();
m_netFrames++;
#if defined(DUMP_DSTAR)
writeFile(data + 1U, length - 1U);
#endif
writeQueueDataNet(data + 1U);
} else {
CUtils::dump("D-Star, unknown data from network", data, DSTAR_FRAME_LENGTH_BYTES + 1U);
}
}
@ -956,12 +960,17 @@ void CDStarControl::clock()
if (m_networkWatchdog.hasExpired()) {
// We're received the header haven't we?
unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH];
unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH];
unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH];
m_netHeader.getMyCall1(my1);
m_netHeader.getMyCall2(my2);
m_netHeader.getYourCall(your);
m_netFrames += 1U;
LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits));
LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames);
writeJSONNet("lost", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames));
writeEndNet();
#if defined(DUMP_DSTAR)
closeFile();
#endif
}
}
@ -1141,46 +1150,6 @@ void CDStarControl::writeNetworkDataRF(const unsigned char* data, unsigned int e
m_network->writeData(data + 1U, DSTAR_FRAME_LENGTH_BYTES, errors, end, m_netState != RPT_NET_STATE::IDLE);
}
bool CDStarControl::openFile()
{
if (m_fp != nullptr)
return true;
time_t t;
::time(&t);
struct tm* tm = ::localtime(&t);
char name[100U];
::sprintf(name, "DStar_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
m_fp = ::fopen(name, "wb");
if (m_fp == nullptr)
return false;
::fwrite("DSTAR", 1U, 4U, m_fp);
return true;
}
bool CDStarControl::writeFile(const unsigned char* data, unsigned int length)
{
if (m_fp == nullptr)
return false;
::fwrite(data, 1U, length, m_fp);
return true;
}
void CDStarControl::closeFile()
{
if (m_fp != nullptr) {
::fclose(m_fp);
m_fp = nullptr;
}
}
bool CDStarControl::insertSilence(const unsigned char* data, unsigned char seqNo)
{
assert(data != nullptr);
@ -1289,18 +1258,32 @@ void CDStarControl::sendAck()
if ((m_ackMessage == DSTAR_ACK::RSSI) && (m_rssi != 0U)) {
if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) {
CUtils::removeChar(reflector, ' ');//remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm
::sprintf(text, "%-8.8s %.1f%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount);
::sprintf(text, "%-8.8s %.1f%% %ddBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal));
} else {
::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount);
::sprintf(text, "BER:%.1f%% %ddBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal));
}
} else if ((m_ackMessage == DSTAR_ACK::SMETER) && (m_rssi != 0U)) {
unsigned int signal, plus;
const int RSSI_S1 = -141;
const int RSSI_S9 = -93;
int signal = 0;
int plus = 0;
int rssi = m_aveRSSI / int(m_rssiCountTotal);
if (rssi < RSSI_S1) {
plus = RSSI_S1 - rssi;
} else if (rssi < RSSI_S9 && rssi >= RSSI_S1) {
signal = ((rssi - RSSI_S1) / 6) + 1;
plus = (rssi - RSSI_S1) % 6;
} else {
plus = rssi - RSSI_S9;
}
char signalText[15U];
CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus);
if (plus != 0U)
::sprintf(signalText, "S%u+%02u", signal, plus);
if (plus != 0)
::sprintf(signalText, "S%d+%02d", signal, plus);
else
::sprintf(signalText, "S%u", signal);
::sprintf(signalText, "S%d", signal);
if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK))
::sprintf(text, "%-8.8s %.1f%% %s ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), signalText);
@ -1354,18 +1337,32 @@ void CDStarControl::sendError()
if ((m_ackMessage == DSTAR_ACK::RSSI) && (m_rssi != 0U)) {
if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) {
CUtils::removeChar(reflector, ' ');//remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm
::sprintf(text, "%-8.8s %.1f%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount);
::sprintf(text, "%-8.8s %.1f%% %ddBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal));
} else {
::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount);
::sprintf(text, "BER:%.1f%% %ddBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal));
}
} else if ((m_ackMessage == DSTAR_ACK::SMETER) && (m_rssi != 0U)) {
unsigned int signal, plus;
const int RSSI_S1 = -141;
const int RSSI_S9 = -93;
int signal = 0;
int plus = 0;
int rssi = m_aveRSSI / int(m_rssiCountTotal);
if (rssi < RSSI_S1) {
plus = RSSI_S1 - rssi;
} else if (rssi < RSSI_S9 && rssi >= RSSI_S1) {
signal = ((rssi - RSSI_S1) / 6) + 1;
plus = (rssi - RSSI_S1) % 6;
} else {
plus = rssi - RSSI_S9;
}
char signalText[15U];
CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus);
if (plus != 0U)
::sprintf(signalText, "S%u+%02u", signal, plus);
::sprintf(signalText, "S%d+%02d", signal, plus);
else
::sprintf(signalText, "S%u", signal);
::sprintf(signalText, "S%d", signal);
if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK))
::sprintf(text, "%-8.8s %.1f%% %s ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), signalText);
@ -1439,3 +1436,171 @@ void CDStarControl::enable(bool enabled)
m_enabled = enabled;
}
void CDStarControl::writeJSONRSSI()
{
if (m_rssi == 0)
return;
if (m_rssiCountTotal >= RSSI_COUNT) {
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["mode"] = "D-Star";
json["value"] = m_rssiAccum / int(m_rssiCountTotal);
WriteJSON("RSSI", json);
m_rssiAccum = 0;
m_rssiCountTotal = 0U;
}
}
void CDStarControl::writeJSONBER()
{
if (m_bitsCount >= BER_COUNT) {
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["mode"] = "D-Star";
json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount);
WriteJSON("BER", json);
m_bitErrsAccum = 0U;
m_bitsCount = 1U;
}
}
void CDStarControl::writeJSONText(const unsigned char* text)
{
assert(text != nullptr);
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["mode"] = "D-Star";
json["value"] = std::string((char*)text);
WriteJSON("Text", json);
}
void CDStarControl::writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your)
{
assert(action != nullptr);
assert(my1 != nullptr);
assert(my2 != nullptr);
assert(your != nullptr);
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["source_cs"] = convertBuffer(my1, DSTAR_LONG_CALLSIGN_LENGTH);
json["source_ext"] = convertBuffer(my2, DSTAR_SHORT_CALLSIGN_LENGTH);
json["destination_cs"] = convertBuffer(your, DSTAR_LONG_CALLSIGN_LENGTH);
json["source"] = "rf";
json["action"] = action;
WriteJSON("D-Star", json);
}
void CDStarControl::writeJSONRF(const char* action, float duration, float ber)
{
assert(action != nullptr);
nlohmann::json json;
writeJSONRF(json, action, duration, ber);
WriteJSON("D-Star", json);
}
void CDStarControl::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI)
{
assert(action != nullptr);
nlohmann::json json;
writeJSONRF(json, action, duration, ber);
nlohmann::json rssi;
rssi["min"] = minRSSI;
rssi["max"] = maxRSSI;
rssi["ave"] = aveRSSI;
json["rssi"] = rssi;
WriteJSON("D-Star", json);
}
void CDStarControl::writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector)
{
assert(action != nullptr);
assert(my1 != nullptr);
assert(my2 != nullptr);
assert(your != nullptr);
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["source_cs"] = convertBuffer(my1, DSTAR_LONG_CALLSIGN_LENGTH);
json["source_ext"] = convertBuffer(my2, DSTAR_SHORT_CALLSIGN_LENGTH);
json["destination_cs"] = convertBuffer(your, DSTAR_LONG_CALLSIGN_LENGTH);
json["source"] = "network";
json["action"] = action;
if (reflector != nullptr)
json["reflector"] = convertBuffer(reflector, DSTAR_LONG_CALLSIGN_LENGTH);
WriteJSON("D-Star", json);
}
void CDStarControl::writeJSONNet(const char* action, float duration, float loss)
{
assert(action != nullptr);
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["duration"] = duration;
json["loss"] = loss;
json["action"] = action;
WriteJSON("D-Star", json);
}
void CDStarControl::writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber)
{
assert(action != nullptr);
json["timestamp"] = CUtils::createTimestamp();
json["duration"] = duration;
json["ber"] = ber;
json["action"] = action;
}
std::string CDStarControl::convertBuffer(const unsigned char* buffer, unsigned int length) const
{
assert(buffer != nullptr);
std::string callsign((char*)buffer, length);
size_t pos = callsign.find_first_of(' ');
if (pos != std::string::npos)
callsign = callsign.substr(0U, pos);
return callsign;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2019,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2019,2023,2025 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
@ -27,17 +27,20 @@
#include "RingBuffer.h"
#include "StopWatch.h"
#include "AMBEFEC.h"
#include "Display.h"
#include "Defines.h"
#include "Timer.h"
#include "Modem.h"
#if defined(USE_DSTAR)
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
class CDStarControl {
public:
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector<std::string>& blackList, const std::vector<std::string>& whiteList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper);
CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector<std::string>& blackList, const std::vector<std::string>& whiteList, CDStarNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper);
~CDStarControl();
bool writeModem(unsigned char* data, unsigned int len);
@ -61,7 +64,6 @@ private:
std::vector<std::string> m_blackList;
std::vector<std::string> m_whiteList;
CDStarNetwork* m_network;
CDisplay* m_display;
bool m_duplex;
CRingBuffer<unsigned char> m_queue;
CDStarHeader m_rfHeader;
@ -88,15 +90,18 @@ private:
unsigned int m_rfBits;
unsigned int m_netBits;
unsigned int m_rfErrs;
unsigned int m_netErrs;
unsigned char* m_lastFrame;
bool m_lastFrameValid;
CRSSIInterpolator* m_rssiMapper;
unsigned char m_rssi;
unsigned char m_maxRSSI;
unsigned char m_minRSSI;
unsigned int m_aveRSSI;
int m_rssi;
int m_maxRSSI;
int m_minRSSI;
int m_aveRSSI;
unsigned int m_rssiCountTotal;
int m_rssiAccum;
unsigned int m_rssiCount;
unsigned int m_bitErrsAccum;
unsigned int m_bitsCount;
bool m_enabled;
FILE* m_fp;
unsigned char * m_RFdataLookBack;
@ -121,9 +126,19 @@ private:
void writeEndRF();
void writeEndNet();
bool openFile();
bool writeFile(const unsigned char* data, unsigned int length);
void closeFile();
void writeJSONRSSI();
void writeJSONBER();
void writeJSONText(const unsigned char* text);
void writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your);
void writeJSONRF(const char* action, float duration, float ber);
void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI);
void writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector = nullptr);
void writeJSONNet(const char* action, float duration, float loss);
void writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber);
std::string convertBuffer(const unsigned char* buffer, unsigned int length) const;
bool insertSilence(const unsigned char* data, unsigned char seqNo);
void insertSilence(unsigned int count);
@ -135,3 +150,5 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2023,2025 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
@ -20,6 +20,8 @@
#include "DStarHeader.h"
#include "CRC.h"
#if defined(USE_DSTAR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -157,3 +159,6 @@ void CDStarHeader::get(unsigned char* header) const
CCRC::addCCITT161(header, DSTAR_HEADER_LENGTH_BYTES);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2023,2026 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
@ -19,6 +19,10 @@
#ifndef DStarHeader_H
#define DStarHeader_H
#include "Defines.h"
#if defined(USE_DSTAR)
#include "DStarDefines.h"
class CDStarHeader {
@ -58,3 +62,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2014,2016,2019,2020,2021,2024,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2014,2016,2019,2020,2021,2023,2024,2025 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
@ -23,6 +23,8 @@
#include "Utils.h"
#include "Log.h"
#if defined(USE_DSTAR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -346,3 +348,6 @@ void CDStarNetwork::getStatus(LINK_STATUS& status, unsigned char* reflector)
::memcpy(reflector, m_linkReflector, DSTAR_LONG_CALLSIGN_LENGTH);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2014,2016,2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2009-2014,2016,2020,2021,2023 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
@ -22,8 +22,11 @@
#include "DStarDefines.h"
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "Defines.h"
#include "Timer.h"
#if defined(USE_DSTAR)
#include <cstdint>
#include <string>
#include <random>
@ -73,3 +76,6 @@ private:
};
#endif
#endif

View file

@ -22,6 +22,8 @@
#include "CRC.h"
#include "Log.h"
#if defined(USE_DSTAR)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -246,3 +248,6 @@ bool CDStarSlowData::isComplete() const
{
return m_complete;
}
#endif

View file

@ -20,6 +20,9 @@
#define DStarSlowData_H
#include "DStarHeader.h"
#include "Defines.h"
#if defined(USE_DSTAR)
class CDStarSlowData {
public:
@ -61,3 +64,6 @@ private:
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2017,2018,2020,2021,2023,2025 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
@ -19,6 +19,15 @@
#if !defined(Defines_H)
#define Defines_H
// Define the wanted modes to compile into the host here
#define USE_DSTAR
#define USE_DMR
#define USE_YSF
#define USE_P25
#define USE_NXDN
#define USE_POCSAG
#define USE_FM
const unsigned char MODE_IDLE = 0U;
const unsigned char MODE_DSTAR = 1U;
const unsigned char MODE_DMR = 2U;
@ -38,6 +47,7 @@ const unsigned char TAG_HEADER = 0x00U;
const unsigned char TAG_DATA = 0x01U;
const unsigned char TAG_LOST = 0x02U;
const unsigned char TAG_EOT = 0x03U;
const unsigned char TAG_RSSI = 0x04U;
const unsigned int DSTAR_MODEM_DATA_LEN = 220U;

View file

@ -1,684 +0,0 @@
/*
* Copyright (C) 2016,2017,2018,2020,2021,2023,2024,2025 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 "Display.h"
#include "Defines.h"
#include "UARTController.h"
#include "ModemSerialPort.h"
#include "NullDisplay.h"
#include "TFTSurenoo.h"
#include "UDPSocket.h"
#include "LCDproc.h"
#include "Nextion.h"
#include "CASTInfo.h"
#include "Conf.h"
#include "Modem.h"
#include "Log.h"
#if defined(HD44780)
#include "HD44780.h"
#endif
#if defined(OLED)
#include "OLED.h"
#endif
#include <cstdio>
#include <cassert>
#include <cstring>
CDisplay::CDisplay() :
m_timer1(3000U, 3U),
m_timer2(3000U, 3U),
m_mode1(MODE_IDLE),
m_mode2(MODE_IDLE)
{
}
CDisplay::~CDisplay()
{
}
void CDisplay::setIdle()
{
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_IDLE;
m_mode2 = MODE_IDLE;
setIdleInt();
}
void CDisplay::setLockout()
{
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_IDLE;
m_mode2 = MODE_IDLE;
setLockoutInt();
}
void CDisplay::setError(const char* text)
{
assert(text != nullptr);
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_IDLE;
m_mode2 = MODE_IDLE;
setErrorInt(text);
}
void CDisplay::setQuit()
{
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_QUIT;
m_mode2 = MODE_QUIT;
setQuitInt();
}
void CDisplay::setFM()
{
m_timer1.stop();
m_timer2.stop();
m_mode1 = MODE_FM;
m_mode2 = MODE_FM;
setFMInt();
}
void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != nullptr);
assert(my2 != nullptr);
assert(your != nullptr);
assert(type != nullptr);
assert(reflector != nullptr);
m_timer1.start();
m_mode1 = MODE_IDLE;
writeDStarInt(my1, my2, your, type, reflector);
}
void CDisplay::writeDStarRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeDStarRSSIInt(rssi);
}
void CDisplay::writeDStarBER(float ber)
{
writeDStarBERInt(ber);
}
void CDisplay::clearDStar()
{
if (m_timer1.hasExpired()) {
clearDStarInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_DSTAR;
}
}
void CDisplay::writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != nullptr);
if (slotNo == 1U) {
m_timer1.start();
m_mode1 = MODE_IDLE;
} else {
m_timer2.start();
m_mode2 = MODE_IDLE;
}
writeDMRInt(slotNo, src, group, dst, type);
}
void CDisplay::writeDMR(unsigned int slotNo, const class CUserDBentry& src, bool group, const std::string& dst, const char* type)
{
assert(type != nullptr);
if (slotNo == 1U) {
m_timer1.start();
m_mode1 = MODE_IDLE;
} else {
m_timer2.start();
m_mode2 = MODE_IDLE;
}
if (int err = writeDMRIntEx(slotNo, src, group, dst, type)) {
std::string src_str = src.get(keyCALLSIGN);
if (err < 0 && !src.get(keyFIRST_NAME).empty()) {
// emulate the result of old CDMRLookup::findWithName()
// (it returned callsign and firstname)
src_str += " " + src.get(keyFIRST_NAME);
}
writeDMRInt(slotNo, src_str, group, dst, type);
}
}
void CDisplay::writeDMRRSSI(unsigned int slotNo, unsigned char rssi)
{
if (rssi != 0U)
writeDMRRSSIInt(slotNo, rssi);
}
void CDisplay::writeDMRTA(unsigned int slotNo, const unsigned char* talkerAlias, const char* type)
{
if (::strcmp(type, " ") == 0) {
writeDMRTAInt(slotNo, (unsigned char*)"", type);
return;
}
if (::strlen((char*)talkerAlias) >= 4U)
writeDMRTAInt(slotNo, (unsigned char*)talkerAlias, type);
}
void CDisplay::writeDMRBER(unsigned int slotNo, float ber)
{
writeDMRBERInt(slotNo, ber);
}
void CDisplay::clearDMR(unsigned int slotNo)
{
if (slotNo == 1U) {
if (m_timer1.hasExpired()) {
clearDMRInt(slotNo);
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_DMR;
}
} else {
if (m_timer2.hasExpired()) {
clearDMRInt(slotNo);
m_timer2.stop();
m_mode2 = MODE_IDLE;
} else {
m_mode2 = MODE_DMR;
}
}
}
void CDisplay::writeFusion(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin)
{
assert(source != nullptr);
assert(dest != nullptr);
assert(type != nullptr);
assert(origin != nullptr);
m_timer1.start();
m_mode1 = MODE_IDLE;
writeFusionInt(source, dest, dgid, type, origin);
}
void CDisplay::writeFusionRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeFusionRSSIInt(rssi);
}
void CDisplay::writeFusionBER(float ber)
{
writeFusionBERInt(ber);
}
void CDisplay::clearFusion()
{
if (m_timer1.hasExpired()) {
clearFusionInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_YSF;
}
}
void CDisplay::writeP25(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != nullptr);
assert(type != nullptr);
m_timer1.start();
m_mode1 = MODE_IDLE;
writeP25Int(source, group, dest, type);
}
void CDisplay::writeP25RSSI(unsigned char rssi)
{
if (rssi != 0U)
writeP25RSSIInt(rssi);
}
void CDisplay::writeP25BER(float ber)
{
writeP25BERInt(ber);
}
void CDisplay::clearP25()
{
if (m_timer1.hasExpired()) {
clearP25Int();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_P25;
}
}
void CDisplay::writeNXDN(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != nullptr);
assert(type != nullptr);
m_timer1.start();
m_mode1 = MODE_IDLE;
writeNXDNInt(source, group, dest, type);
}
void CDisplay::writeNXDN(const class CUserDBentry& source, bool group, unsigned int dest, const char* type)
{
assert(type != nullptr);
m_timer1.start();
m_mode1 = MODE_IDLE;
if (writeNXDNIntEx(source, group, dest, type))
writeNXDNInt(source.get(keyCALLSIGN).c_str(), group, dest, type);
}
void CDisplay::writeNXDNRSSI(unsigned char rssi)
{
if (rssi != 0U)
writeNXDNRSSIInt(rssi);
}
void CDisplay::writeNXDNBER(float ber)
{
writeNXDNBERInt(ber);
}
void CDisplay::clearNXDN()
{
if (m_timer1.hasExpired()) {
clearNXDNInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_NXDN;
}
}
void CDisplay::writePOCSAG(uint32_t ric, const std::string& message)
{
m_timer1.start();
m_mode1 = MODE_POCSAG;
writePOCSAGInt(ric, message);
}
void CDisplay::clearPOCSAG()
{
if (m_timer1.hasExpired()) {
clearPOCSAGInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_POCSAG;
}
}
void CDisplay::writeCW()
{
m_timer1.start();
m_mode1 = MODE_CW;
writeCWInt();
}
void CDisplay::clock(unsigned int ms)
{
m_timer1.clock(ms);
if (m_timer1.isRunning() && m_timer1.hasExpired()) {
switch (m_mode1) {
case MODE_DSTAR:
clearDStarInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_DMR:
clearDMRInt(1U);
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_YSF:
clearFusionInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_P25:
clearP25Int();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_NXDN:
clearNXDNInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_POCSAG:
clearPOCSAGInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_CW:
clearCWInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
default:
break;
}
}
// Timer/mode 2 are only used for DMR
m_timer2.clock(ms);
if (m_timer2.isRunning() && m_timer2.hasExpired()) {
if (m_mode2 == MODE_DMR) {
clearDMRInt(2U);
m_mode2 = MODE_IDLE;
m_timer2.stop();
}
}
clockInt(ms);
}
void CDisplay::clockInt(unsigned int ms)
{
}
void CDisplay::writeDStarRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeDStarBERInt(float ber)
{
}
int CDisplay::writeDMRIntEx(unsigned int slotNo, const class CUserDBentry& src, bool group, const std::string& dst, const char* type)
{
/*
* return value:
* < 0 error condition (i.e. not supported)
* -> call writeXXXXInt() to display
* = 0 no error, writeXXXXIntEx() displayed whole status
* = 1 no error, writeXXXXIntEx() displayed partial status
* -> call writeXXXXInt() to display remain part
* > 1 reserved for future use
*/
return -1; // not supported
}
void CDisplay::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
}
void CDisplay::writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type)
{
}
void CDisplay::writeDMRBERInt(unsigned int slotNo, float ber)
{
}
void CDisplay::writeFusionRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeFusionBERInt(float ber)
{
}
void CDisplay::writeP25RSSIInt(unsigned char rssi)
{
}
void CDisplay::writeP25BERInt(float ber)
{
}
void CDisplay::writeNXDNRSSIInt(unsigned char rssi)
{
}
void CDisplay::writeNXDNBERInt(float ber)
{
}
int CDisplay::writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type)
{
/* return value definition is same as writeDMRIntEx() */
return -1; // not supported
}
/* Factory method extracted from MMDVMHost.cpp - BG5HHP */
CDisplay* CDisplay::createDisplay(const CConf& conf, CModem* modem)
{
CDisplay *display = nullptr;
std::string type = conf.getDisplay();
unsigned int dmrid = conf.getDMRId();
LogInfo("Display Parameters");
LogInfo(" Type: %s", type.c_str());
if (type == "TFT Surenoo") {
std::string port = conf.getTFTSerialPort();
unsigned int brightness = conf.getTFTSerialBrightness();
unsigned int screenLayout = conf.getTFTSerialScreenLayout();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
LogInfo(" Screen Layout: %u", screenLayout);
ISerialPort* serial = nullptr;
if (port == "modem")
serial = new IModemSerialPort(modem);
else
serial = new CUARTController(port, 115200U);
display = new CTFTSurenoo(conf.getCallsign(), dmrid, serial, brightness, conf.getDuplex(), screenLayout);
} else if (type == "Nextion") {
std::string port = conf.getNextionPort();
unsigned int brightness = conf.getNextionBrightness();
bool displayClock = conf.getNextionDisplayClock();
bool utc = conf.getNextionUTC();
unsigned int idleBrightness = conf.getNextionIdleBrightness();
unsigned int screenLayout = conf.getNextionScreenLayout();
unsigned int txFrequency = conf.getTXFrequency();
unsigned int rxFrequency = conf.getRXFrequency();
bool displayTempInF = conf.getNextionTempInFahrenheit();
LogInfo(" Port: %s", port.c_str());
LogInfo(" Brightness: %u", brightness);
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
LogInfo(" Idle Brightness: %u", idleBrightness);
LogInfo(" Temperature in Fahrenheit: %s ", displayTempInF ? "yes" : "no");
switch (screenLayout) {
case 0U:
LogInfo(" Screen Layout: G4KLX (Default)");
break;
case 2U:
LogInfo(" Screen Layout: ON7LDS");
break;
case 3U:
LogInfo(" Screen Layout: DIY by ON7LDS");
break;
case 4U:
LogInfo(" Screen Layout: DIY by ON7LDS (High speed)");
break;
default:
LogInfo(" Screen Layout: %u (Unknown)", screenLayout);
break;
}
if (port == "modem") {
CUDPSocket* socket = nullptr;
struct sockaddr_storage addr;
unsigned int addrLength = 0U;
bool nextionOutput = conf.getNextionOutput();
if (nextionOutput) {
unsigned short nextionUDPPort = conf.getNextionUDPPort();
LogInfo(" Output Port: %u", nextionUDPPort);
CUDPSocket::lookup("127.0.0.1", nextionUDPPort, addr, addrLength);
if (addrLength > 0U) {
socket = new CUDPSocket("127.0.0.1", nextionUDPPort - 1U);
bool ret = socket->open(addr);
if (!ret) {
delete socket;
socket = nullptr;
}
}
}
if (socket == nullptr) {
ISerialPort* serial = new IModemSerialPort(modem);
display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF);
} else {
ISerialPort* serial = new IModemSerialPort(modem);
display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF, socket, addr, addrLength);
}
} else {
unsigned int baudrate = 9600U;
if (screenLayout == 4U)
baudrate = 115200U;
LogInfo(" Display baudrate: %u ", baudrate);
ISerialPort* serial = new CUARTController(port, baudrate);
display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF);
}
} else if (type == "LCDproc") {
std::string address = conf.getLCDprocAddress();
unsigned int port = conf.getLCDprocPort();
unsigned int localPort = conf.getLCDprocLocalPort();
bool displayClock = conf.getLCDprocDisplayClock();
bool utc = conf.getLCDprocUTC();
bool dimOnIdle = conf.getLCDprocDimOnIdle();
LogInfo(" Address: %s", address.c_str());
LogInfo(" Port: %u", port);
if (localPort == 0 )
LogInfo(" Local Port: random");
else
LogInfo(" Local Port: %u", localPort);
LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no");
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
display = new CLCDproc(address.c_str(), port, localPort, conf.getCallsign(), dmrid, displayClock, utc, conf.getDuplex(), dimOnIdle);
#if defined(HD44780)
} else if (type == "HD44780") {
unsigned int rows = conf.getHD44780Rows();
unsigned int columns = conf.getHD44780Columns();
std::vector<unsigned int> pins = conf.getHD44780Pins();
unsigned int i2cAddress = conf.getHD44780i2cAddress();
bool pwm = conf.getHD44780PWM();
unsigned int pwmPin = conf.getHD44780PWMPin();
unsigned int pwmBright = conf.getHD44780PWMBright();
unsigned int pwmDim = conf.getHD44780PWMDim();
bool displayClock = conf.getHD44780DisplayClock();
bool utc = conf.getHD44780UTC();
if (pins.size() == 6U) {
LogInfo(" Rows: %u", rows);
LogInfo(" Columns: %u", columns);
#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY)
LogInfo(" Device Address: %#x", i2cAddress);
#else
LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U));
#endif
LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no");
if (pwm) {
LogInfo(" PWM Pin: %u", pwmPin);
LogInfo(" PWM Bright: %u", pwmBright);
LogInfo(" PWM Dim: %u", pwmDim);
}
LogInfo(" Clock Display: %s", displayClock ? "yes" : "no");
if (displayClock)
LogInfo(" Display UTC: %s", utc ? "yes" : "no");
display = new CHD44780(rows, columns, conf.getCallsign(), dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, conf.getDuplex());
}
#endif
#if defined(OLED)
} else if (type == "OLED") {
unsigned char type = conf.getOLEDType();
unsigned char brightness = conf.getOLEDBrightness();
bool invert = conf.getOLEDInvert();
bool scroll = conf.getOLEDScroll();
bool rotate = conf.getOLEDRotate();
bool logosaver = conf.getOLEDLogoScreensaver();
display = new COLED(type, brightness, invert, scroll, rotate, logosaver, conf.getDuplex());
#endif
} else if (type == "CAST") {
display = new CCASTInfo(modem);
} else {
LogWarning("No valid display found, disabling");
display = new CNullDisplay;
}
bool ret = display->open();
if (!ret) {
delete display;
display = new CNullDisplay;
}
return display;
}

136
Display.h
View file

@ -1,136 +0,0 @@
/*
* Copyright (C) 2016,2017,2018,2020,2021,2023,2025 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(DISPLAY_H)
#define DISPLAY_H
#include "Timer.h"
#include "UserDBentry.h"
#include "Modem.h"
#include <string>
#include <cstdint>
class CConf;
class CModem;
class CDisplay
{
public:
CDisplay();
virtual ~CDisplay() = 0;
virtual bool open() = 0;
void setIdle();
void setLockout();
void setError(const char* text);
void setQuit();
void setFM();
void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
void writeDStarRSSI(unsigned char rssi);
void writeDStarBER(float ber);
void clearDStar();
void writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
void writeDMR(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type);
void writeDMRRSSI(unsigned int slotNo, unsigned char rssi);
void writeDMRBER(unsigned int slotNo, float ber);
void writeDMRTA(unsigned int slotNo, const unsigned char* talkerAlias, const char* type);
void clearDMR(unsigned int slotNo);
void writeFusion(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin);
void writeFusionRSSI(unsigned char rssi);
void writeFusionBER(float ber);
void clearFusion();
void writeP25(const char* source, bool group, unsigned int dest, const char* type);
void writeP25RSSI(unsigned char rssi);
void writeP25BER(float ber);
void clearP25();
void writeNXDN(const char* source, bool group, unsigned int dest, const char* type);
void writeNXDN(const CUserDBentry& source, bool group, unsigned int dest, const char* type);
void writeNXDNRSSI(unsigned char rssi);
void writeNXDNBER(float ber);
void clearNXDN();
void writePOCSAG(uint32_t ric, const std::string& message);
void clearPOCSAG();
void writeCW();
virtual void close() = 0;
void clock(unsigned int ms);
static CDisplay* createDisplay(const CConf& conf, CModem* modem);
protected:
virtual void setIdleInt() = 0;
virtual void setLockoutInt() = 0;
virtual void setErrorInt(const char* text) = 0;
virtual void setQuitInt() = 0;
virtual void setFMInt() = 0;
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) = 0;
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void writeDStarBERInt(float ber);
virtual void clearDStarInt() = 0;
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0;
virtual int writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type);
virtual void writeDMRBERInt(unsigned int slotNo, float ber);
virtual void clearDMRInt(unsigned int slotNo) = 0;
virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) = 0;
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void writeFusionBERInt(float ber);
virtual void clearFusionInt() = 0;
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type) = 0;
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void writeP25BERInt(float ber);
virtual void clearP25Int() = 0;
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) = 0;
virtual int writeNXDNIntEx(const CUserDBentry& source, bool group, unsigned int dest, const char* type);
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void writeNXDNBERInt(float ber);
virtual void clearNXDNInt() = 0;
virtual void writePOCSAGInt(uint32_t ric, const std::string& message) = 0;
virtual void clearPOCSAGInt() = 0;
virtual void writeCWInt() = 0;
virtual void clearCWInt() = 0;
virtual void clockInt(unsigned int ms);
private:
CTimer m_timer1;
CTimer m_timer2;
unsigned char m_mode1;
unsigned char m_mode2;
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021,2023,2025 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
@ -17,13 +17,12 @@
*/
#include "FMControl.h"
#include "Utils.h"
#if defined(USE_FM)
#include <string>
#if defined(DUMP_RF_AUDIO)
#include <cstdio>
#endif
#define SWAP_BYTES_16(a) (((a >> 8) & 0x00FFU) | ((a << 8) & 0xFF00U))
const float DEEMPHASIS_GAIN_DB = 8.0F; // Audio gain adjustment
@ -31,12 +30,26 @@ const float PREEMPHASIS_GAIN_DB = 0.0F; // Audio gain adjustment
const float FILTER_GAIN_DB = 2.0F; // Audio gain adjustment
const unsigned int FM_MASK = 0x00000FFFU;
CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn) :
const unsigned char FS_LISTENING = 0U;
const unsigned char FS_KERCHUNK_RF = 1U;
const unsigned char FS_RELAYING_RF = 2U;
const unsigned char FS_RELAYING_WAIT_RF = 3U;
const unsigned char FS_TIMEOUT_RF = 4U;
const unsigned char FS_TIMEOUT_WAIT_RF = 5U;
const unsigned char FS_KERCHUNK_EXT = 6U;
const unsigned char FS_RELAYING_EXT = 7U;
const unsigned char FS_RELAYING_WAIT_EXT = 8U;
const unsigned char FS_TIMEOUT_EXT = 9U;
const unsigned char FS_TIMEOUT_WAIT_EXT = 10U;
const unsigned char FS_HANG = 11U;
CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper) :
m_network(network),
m_txAudioGain(txAudioGain),
m_rxAudioGain(rxAudioGain),
m_preEmphasisOn(preEmphasisOn),
m_deEmphasisOn(deEmphasisOn),
m_rssiMapper(rssiMapper),
m_enabled(false),
m_incomingRFAudio(1600U, "Incoming RF FM Audio"),
m_preEmphasis(nullptr),
@ -45,26 +58,27 @@ m_filterStage1(nullptr),
m_filterStage2(nullptr),
m_filterStage3(nullptr)
{
assert(txAudioGain > 0.0F);
assert(rxAudioGain > 0.0F);
assert(txAudioGain > 0.0F);
assert(rxAudioGain > 0.0F);
assert(rssiMapper != nullptr);
m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB);
m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB);
m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB);
m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB);
// Chebyshev type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000
m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB);
m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB);
m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB);
// Chebyshev type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000
m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB);
m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB);
m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB);
}
CFMControl::~CFMControl()
{
delete m_preEmphasis;
delete m_deEmphasis;
delete m_preEmphasis;
delete m_deEmphasis;
delete m_filterStage1;
delete m_filterStage2;
delete m_filterStage3;
delete m_filterStage1;
delete m_filterStage2;
delete m_filterStage3;
}
bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
@ -72,121 +86,174 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
assert(data != nullptr);
assert(length > 0U);
if (m_network == nullptr)
return true;
if (data[0U] == TAG_HEADER) {
switch (data[1U]) {
case FS_LISTENING: writeJSON("listening"); break;
case FS_KERCHUNK_RF: writeJSON("kerchunk_rf"); break;
case FS_RELAYING_RF: writeJSON("relaying_rf"); break;
case FS_RELAYING_WAIT_RF: writeJSON("relaying_wait_rf"); break;
case FS_TIMEOUT_RF: writeJSON("timeout_rf"); break;
case FS_TIMEOUT_WAIT_RF: writeJSON("timeout_wait_rf"); break;
case FS_KERCHUNK_EXT: writeJSON("kerchunk_ext"); break;
case FS_RELAYING_EXT: writeJSON("relaying_ext"); break;
case FS_RELAYING_WAIT_EXT: writeJSON("relaying_wait_ext"); break;
case FS_TIMEOUT_EXT: writeJSON("timeout_ext"); break;
case FS_TIMEOUT_WAIT_EXT: writeJSON("timeout_wait_ext"); break;
case FS_HANG: writeJSON("hang"); break;
default: writeJSON("unknown"); break;
}
if (data[0U] == TAG_HEADER)
return true;
return true;
}
if (data[0U] == TAG_EOT)
return m_network->writeEnd();
if (data[0U] == TAG_RSSI) {
uint16_t raw = 0U;
raw |= (data[0U] << 8) & 0xFF00U;
raw |= (data[1U] << 0) & 0x00FFU;
if (data[0U] != TAG_DATA)
return false;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
if (rssi != 0) {
LogDebug("FM, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
writeJSONRSSI(rssi);
}
m_incomingRFAudio.addData(data + 1U, length - 1U);
unsigned int bufferLength = m_incomingRFAudio.dataSize();
if (bufferLength > 240U) // 160 samples 12-bit
bufferLength = 240U; // 160 samples 12-bit
return true;
}
if (bufferLength >= 3U) {
bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3
unsigned char bufferData[240U]; // 160 samples 12-bit
m_incomingRFAudio.getData(bufferData, bufferLength);
if (m_network == nullptr)
return true;
unsigned int pack = 0U;
unsigned char* packPointer = (unsigned char*)&pack;
float out[160U]; // 160 samples 12-bit
unsigned int nOut = 0U;
short unpackedSamples[2U];
if (data[0U] == TAG_EOT)
return m_network->writeEnd();
for (unsigned int i = 0U; i < bufferLength; i += 3U) {
// Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed
packPointer[0U] = bufferData[i + 0U];
packPointer[1U] = bufferData[i + 1U];
packPointer[2U] = bufferData[i + 2U];
if (data[0U] != TAG_DATA)
return false;
unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048);
unpackedSamples[0U] = short(int(pack >> 12 & FM_MASK) - 2048); //
m_incomingRFAudio.addData(data + 1U, length - 1U);
unsigned int bufferLength = m_incomingRFAudio.dataSize();
// Process unpacked sample pair
for (unsigned char j = 0U; j < 2U; j++) {
// Convert to float (-1.0 to +1.0)
float sampleFloat = (float(unpackedSamples[j]) * m_rxAudioGain) / 2048.0F;
if (bufferLength > 240U) // 160 samples 12-bit
bufferLength = 240U; // 160 samples 12-bit
// De-emphasise and remove CTCSS
if (m_deEmphasisOn)
sampleFloat = m_deEmphasis->filter(sampleFloat);
if (bufferLength >= 3U) {
bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3
out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat)));
}
}
unsigned char bufferData[240U]; // 160 samples 12-bit
m_incomingRFAudio.getData(bufferData, bufferLength);
#if defined(DUMP_RF_AUDIO)
FILE * audiofile = fopen("./audiodump.bin", "ab");
if (audiofile != nullptr) {
fwrite(out, sizeof(float), nOut, audiofile);
fclose(audiofile);
}
#endif
return m_network->writeData(out, nOut);
}
unsigned int pack = 0U;
unsigned char* packPointer = (unsigned char*)&pack;
return true;
float out[160U]; // 160 samples 12-bit
unsigned int nOut = 0U;
for (unsigned int i = 0U; i < bufferLength; i += 3U) {
// Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed
packPointer[0U] = bufferData[i + 0U];
packPointer[1U] = bufferData[i + 1U];
packPointer[2U] = bufferData[i + 2U];
short unpackedSamples[2U];
unpackedSamples[1U] = short(int((pack >> 0) & FM_MASK) - 2048);
unpackedSamples[0U] = short(int((pack >> 12) & FM_MASK) - 2048); //
// Process unpacked sample pair
for (unsigned char j = 0U; j < 2U; j++) {
// Convert to float (-1.0 to +1.0)
float sampleFloat = (float(unpackedSamples[j]) * m_rxAudioGain) / 2048.0F;
// De-emphasise and remove CTCSS
if (m_deEmphasisOn)
sampleFloat = m_deEmphasis->filter(sampleFloat);
out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat)));
}
}
return m_network->writeData(out, nOut);
}
return true;
}
unsigned int CFMControl::readModem(unsigned char* data, unsigned int space)
{
assert(data != nullptr);
assert(space > 0U);
assert(data != nullptr);
assert(space > 0U);
if (m_network == nullptr)
return 0U;
if (m_network == nullptr)
return 0U;
if (space > 240U) // 160 samples 12-bit
space = 240U; // 160 samples 12-bit
if (space > 240U) // 160 samples 12-bit
space = 240U; // 160 samples 12-bit
float netData[160U]; // Modem can handle up to 160 samples at a time
unsigned int length = m_network->readData(netData, 160U); // 160 samples 12-bit
if (length == 0U)
return 0U;
float netData[160U]; // Modem can handle up to 160 samples at a time
unsigned int length = m_network->readData(netData, 160U); // 160 samples 12-bit
if (length == 0U)
return 0U;
unsigned int pack = 0U;
unsigned char* packPointer = (unsigned char*)&pack;
unsigned int nData = 0U;
unsigned int pack = 0U;
unsigned char* packPointer = (unsigned char*)&pack;
unsigned int nData = 0U;
for (unsigned int i = 0; i < length; i++) {
float sampleFloat = netData[i] * m_txAudioGain;
for (unsigned int i = 0; i < length; i++) {
float sampleFloat = netData[i] * m_txAudioGain;
// Pre-emphasis
if (m_preEmphasisOn)
sampleFloat = m_preEmphasis->filter(sampleFloat);
// Pre-emphasis
if (m_preEmphasisOn)
sampleFloat = m_preEmphasis->filter(sampleFloat);
// Convert float to 12-bit samples (0 to 4095)
unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F);
// Convert float to 12-bit samples (0 to 4095)
unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F);
// Pack 2 samples into 3 bytes
if ((i & 1U) == 0) {
pack = 0U;
pack = sample12bit << 12;
} else {
pack |= sample12bit;
// Pack 2 samples into 3 bytes
if ((i & 1U) == 0U) {
pack = 0U;
pack = sample12bit << 12;
} else {
pack |= sample12bit;
data[nData++] = packPointer[0U];
data[nData++] = packPointer[1U];
data[nData++] = packPointer[2U];
}
}
data[nData++] = packPointer[0U];
data[nData++] = packPointer[1U];
data[nData++] = packPointer[2U];
}
}
return nData;
return nData;
}
void CFMControl::clock(unsigned int ms)
{
// May not be needed
// May not be needed
}
void CFMControl::enable(bool enabled)
{
// May not be needed
// May not be needed
}
void CFMControl::writeJSON(const char* state)
{
assert(state != nullptr);
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["state"] = state;
WriteJSON("FM", json);
}
void CFMControl::writeJSONRSSI(int rssi)
{
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["mode"] = "FM";
json["value"] = rssi;
WriteJSON("RSSI", json);
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2020,2021 by Jonathan Naylor G4KLX
* Copyright (C) 2020,2021,2023 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
@ -22,6 +22,11 @@
#include "FMNetwork.h"
#include "Defines.h"
#include "IIRDirectForm1Filter.h"
#include "RSSIInterpolator.h"
#if defined(USE_FM)
#include <nlohmann/json.hpp>
// Uncomment this to dump audio to a raw audio file
// The file will be written in same folder as executable
@ -30,7 +35,7 @@
class CFMControl {
public:
CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn);
CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper);
~CFMControl();
bool writeModem(const unsigned char* data, unsigned int length);
@ -42,18 +47,25 @@ public:
void enable(bool enabled);
private:
CFMNetwork* m_network;
float m_txAudioGain;
float m_rxAudioGain;
bool m_preEmphasisOn;
bool m_deEmphasisOn;
bool m_enabled;
CFMNetwork* m_network;
float m_txAudioGain;
float m_rxAudioGain;
bool m_preEmphasisOn;
bool m_deEmphasisOn;
CRSSIInterpolator* m_rssiMapper;
bool m_enabled;
CRingBuffer<unsigned char> m_incomingRFAudio;
CIIRDirectForm1Filter* m_preEmphasis;
CIIRDirectForm1Filter* m_deEmphasis;
CIIRDirectForm1Filter* m_filterStage1;
CIIRDirectForm1Filter* m_filterStage2;
CIIRDirectForm1Filter* m_filterStage3;
void writeJSON(const char* state);
void writeJSONRSSI(int rssi);
};
#endif
#endif

View file

@ -17,10 +17,11 @@
*/
#include "FMNetwork.h"
#include "Defines.h"
#include "Utils.h"
#include "Log.h"
#if defined(USE_FM)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -29,56 +30,34 @@
#include <sys/stat.h>
#include <fcntl.h>
const unsigned int MMDVM_SAMPLERATE = 8000U;
const unsigned int BUFFER_LENGTH = 1500U;
CFMNetwork::CFMNetwork(const std::string& callsign, const std::string& protocol, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, unsigned int sampleRate, const std::string& squelchFile, bool debug) :
CFMNetwork::CFMNetwork(const std::string& callsign, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug) :
m_callsign(callsign),
m_protocol(FM_NETWORK_PROTOCOL::USRP),
m_socket(localAddress, localPort),
m_addr(),
m_addrLen(0U),
m_sampleRate(sampleRate),
m_squelchFile(squelchFile),
m_debug(debug),
m_enabled(false),
m_buffer(2000U, "FM Network"),
m_seqNo(0U),
#if defined(HAS_SRC)
m_resampler(nullptr),
#endif
m_error(0),
m_fp(nullptr)
m_timer(1000U, 5U)
{
assert(!callsign.empty());
assert(gatewayPort > 0U);
assert(!gatewayAddress.empty());
assert(sampleRate > 0U);
if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0)
m_addrLen = 0U;
// Remove any trailing letters in the callsign
// Remove any trailing spaces/letters from the callsign
size_t pos = callsign.find_first_of(' ');
if (pos != std::string::npos)
m_callsign = callsign.substr(0U, pos);
if (protocol == "RAW")
m_protocol = FM_NETWORK_PROTOCOL::RAW;
else
m_protocol = FM_NETWORK_PROTOCOL::USRP;
#if defined(HAS_SRC)
m_resampler = ::src_new(SRC_SINC_FASTEST, 1, &m_error);
#endif
}
CFMNetwork::~CFMNetwork()
{
#if defined(HAS_SRC)
::src_delete(m_resampler);
#endif
}
bool CFMNetwork::open()
@ -90,28 +69,13 @@ bool CFMNetwork::open()
LogMessage("Opening FM network connection");
if (m_protocol == FM_NETWORK_PROTOCOL::RAW) {
if (!m_squelchFile.empty()) {
m_fp = ::fopen(m_squelchFile.c_str(), "wb");
if (m_fp == nullptr) {
#if !defined(_WIN32) && !defined(_WIN64)
LogError("Cannot open the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno);
#else
LogError("Cannot open the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError());
#endif
return false;
}
}
}
#if !defined(HAS_SRC)
if ((m_protocol == FM_NETWORK_PROTOCOL::RAW) && (m_sampleRate != MMDVM_SAMPLERATE)) {
LogError("The resampler needed for non-native sample rates has not been included");
bool ret = m_socket.open(m_addr);
if (!ret)
return false;
}
#endif
return m_socket.open(m_addr);
m_timer.start();
return true;
}
bool CFMNetwork::writeData(const float* data, unsigned int nSamples)
@ -119,72 +83,23 @@ bool CFMNetwork::writeData(const float* data, unsigned int nSamples)
assert(data != nullptr);
assert(nSamples > 0U);
if (m_protocol == FM_NETWORK_PROTOCOL::USRP)
return writeUSRPData(data, nSamples);
else if (m_protocol == FM_NETWORK_PROTOCOL::RAW)
return writeRawData(data, nSamples);
else
return false;
}
bool CFMNetwork::writeUSRPData(const float* data, unsigned int nSamples)
{
assert(data != nullptr);
assert(nSamples > 0U);
if (m_seqNo == 0U) {
bool ret = writeUSRPStart();
bool ret = writeStart();
if (!ret)
return false;
}
unsigned char buffer[500U];
::memset(buffer, 0x00U, 500U);
assert(data != nullptr);
assert(nSamples > 0U);
uint8_t buffer[BUFFER_LENGTH];
::memset(buffer, 0x00U, BUFFER_LENGTH);
unsigned int length = 0U;
buffer[length++] = 'U';
buffer[length++] = 'S';
buffer[length++] = 'R';
buffer[length++] = 'P';
// Sequence number
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// PTT on
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x01U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Type, 0 for audio
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 'F';
buffer[length++] = 'M';
buffer[length++] = 'D';
for (unsigned int i = 0U; i < nSamples; i++) {
short val = short(data[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
@ -193,170 +108,73 @@ bool CFMNetwork::writeUSRPData(const float* data, unsigned int nSamples)
buffer[length++] = (val >> 8) & 0xFFU;
}
m_seqNo++;
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, length);
m_seqNo++;
return m_socket.write(buffer, length, m_addr, m_addrLen);
}
bool CFMNetwork::writeRawData(const float* in, unsigned int nIn)
bool CFMNetwork::writeStart()
{
assert(in != nullptr);
assert(nIn > 0U);
if (m_seqNo == 0U) {
bool ret = writeRawStart();
if (!ret)
return false;
}
unsigned char buffer[2000U];
uint8_t buffer[5U];
unsigned int length = 0U;
#if defined(HAS_SRC)
if (m_sampleRate != MMDVM_SAMPLERATE) {
unsigned int nOut = (nIn * m_sampleRate) / MMDVM_SAMPLERATE;
buffer[length++] = 'F';
buffer[length++] = 'M';
buffer[length++] = 'S';
float out[1000U];
for (unsigned int i = 0U; i < m_callsign.size(); i++)
buffer[length++] = m_callsign.at(i);
SRC_DATA data;
data.data_in = in;
data.data_out = out;
data.input_frames = nIn;
data.output_frames = nOut;
data.end_of_input = 0;
data.src_ratio = float(m_sampleRate) / float(MMDVM_SAMPLERATE);
int ret = ::src_process(m_resampler, &data);
if (ret != 0) {
LogError("Error from the write resampler - %d - %s", ret, ::src_strerror(ret));
return false;
}
for (unsigned int i = 0U; i < nOut; i++) {
short val = short(out[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
buffer[length++] = (val >> 0) & 0xFFU;
buffer[length++] = (val >> 8) & 0xFFU;
}
} else {
#endif
for (unsigned int i = 0U; i < nIn; i++) {
short val = short(in[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE
buffer[length++] = (val >> 0) & 0xFFU;
buffer[length++] = (val >> 8) & 0xFFU;
}
#if defined(HAS_SRC)
}
#endif
buffer[length++] = '\0';
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, length);
m_seqNo++;
return m_socket.write(buffer, length, m_addr, m_addrLen);
}
bool CFMNetwork::writeEnd()
{
if (m_protocol == FM_NETWORK_PROTOCOL::USRP)
return writeUSRPEnd();
else
return writeRawEnd();
}
uint8_t buffer[5U];
bool CFMNetwork::writeUSRPEnd()
{
unsigned char buffer[500U];
::memset(buffer, 0x00U, 500U);
unsigned int length = 0U;
buffer[length++] = 'U';
buffer[length++] = 'S';
buffer[length++] = 'R';
buffer[length++] = 'P';
// Sequence number
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// PTT off
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Type, 0 for audio
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
length += 320U;
buffer[0U] = 'F';
buffer[1U] = 'M';
buffer[2U] = 'E';
m_seqNo = 0U;
if (length > 0U) {
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, length);
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, 3U);
return m_socket.write(buffer, length, m_addr, m_addrLen);
} else {
return true;
}
return m_socket.write(buffer, 3U, m_addr, m_addrLen);
}
bool CFMNetwork::writeRawEnd()
bool CFMNetwork::writePing()
{
m_seqNo = 0U;
uint8_t buffer[5U];
if (m_fp != nullptr) {
size_t n = ::fwrite("Z", 1, 1, m_fp);
if (n != 1) {
#if !defined(_WIN32) && !defined(_WIN64)
LogError("Cannot write to the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno);
#else
LogError("Cannot write to the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError());
#endif
return false;
}
buffer[0U] = 'F';
buffer[1U] = 'M';
buffer[2U] = 'P';
::fflush(m_fp);
}
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, 3U);
return true;
return m_socket.write(buffer, 3U, m_addr, m_addrLen);
}
void CFMNetwork::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_timer.isRunning() && m_timer.hasExpired()) {
writePing();
m_timer.start();
}
unsigned char buffer[BUFFER_LENGTH];
sockaddr_storage addr;
@ -366,43 +184,28 @@ void CFMNetwork::clock(unsigned int ms)
return;
// Check if the data is for us
if (m_protocol == FM_NETWORK_PROTOCOL::USRP) {
if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_AND_PORT)) {
LogMessage("FM packet received from an invalid source");
return;
}
} else {
if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_ONLY)) {
LogMessage("FM packet received from an invalid source");
return;
}
if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_AND_PORT)) {
LogMessage("FM packet received from an invalid source");
return;
}
if (!m_enabled)
return;
// Invalid packet type?
if (::memcmp(buffer, "FM", 2U) != 0)
return;
if (::memcmp(buffer, "FMP", 3U) == 0)
return;
if (m_debug)
CUtils::dump(1U, "FM Network Data Received", buffer, length);
if (m_protocol == FM_NETWORK_PROTOCOL::USRP) {
// Invalid packet type?
if (::memcmp(buffer, "USRP", 4U) != 0)
return;
if (::memcmp(buffer, "FMD", 3U) != 0)
return;
if (length < 32)
return;
// The type is a big-endian 4-byte integer
unsigned int type = (buffer[20U] << 24) +
(buffer[21U] << 16) +
(buffer[22U] << 8) +
(buffer[23U] << 0);
if (type == 0U)
m_buffer.addData(buffer + 32U, length - 32U);
} else if (m_protocol == FM_NETWORK_PROTOCOL::RAW) {
m_buffer.addData(buffer, length);
}
m_buffer.addData(buffer + 3U, length - 3U);
}
unsigned int CFMNetwork::readData(float* out, unsigned int nOut)
@ -414,53 +217,16 @@ unsigned int CFMNetwork::readData(float* out, unsigned int nOut)
if (bytes == 0U)
return 0U;
#if defined(HAS_SRC)
if ((m_protocol == FM_NETWORK_PROTOCOL::RAW) && (m_sampleRate != MMDVM_SAMPLERATE)) {
unsigned int nIn = (nOut * m_sampleRate) / MMDVM_SAMPLERATE;
if (bytes < nOut)
nOut = bytes;
if (bytes < nIn) {
nIn = bytes;
nOut = (nIn * MMDVM_SAMPLERATE) / m_sampleRate;
}
unsigned char buffer[BUFFER_LENGTH];
m_buffer.getData(buffer, nOut * sizeof(unsigned short));
unsigned char buffer[2000U];
m_buffer.getData(buffer, nIn * sizeof(unsigned short));
float in[1000U];
for (unsigned int i = 0U; i < nIn; i++) {
short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8);
in[i] = float(val) / 65536.0F;
}
SRC_DATA data;
data.data_in = in;
data.data_out = out;
data.input_frames = nIn;
data.output_frames = nOut;
data.end_of_input = 0;
data.src_ratio = float(MMDVM_SAMPLERATE) / float(m_sampleRate);
int ret = ::src_process(m_resampler, &data);
if (ret != 0) {
LogError("Error from the read resampler - %d - %s", ret, ::src_strerror(ret));
return false;
}
} else {
#endif
if (bytes < nOut)
nOut = bytes;
unsigned char buffer[1500U];
m_buffer.getData(buffer, nOut * sizeof(unsigned short));
for (unsigned int i = 0U; i < nOut; i++) {
short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8);
out[i] = float(val) / 65536.0F;
}
#if defined(HAS_SRC)
for (unsigned int i = 0U; i < nOut; i++) {
short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8);
out[i] = float(val) / 65536.0F;
}
#endif
return nOut;
}
@ -474,11 +240,6 @@ void CFMNetwork::close()
{
m_socket.close();
if (m_fp != nullptr) {
::fclose(m_fp);
m_fp = nullptr;
}
LogMessage("Closing FM network connection");
}
@ -492,119 +253,5 @@ void CFMNetwork::enable(bool enabled)
m_enabled = enabled;
}
bool CFMNetwork::writeUSRPStart()
{
unsigned char buffer[500U];
::memset(buffer, 0x00U, 500U);
unsigned int length = 0U;
buffer[length++] = 'U';
buffer[length++] = 'S';
buffer[length++] = 'R';
buffer[length++] = 'P';
// Sequence number
buffer[length++] = (m_seqNo >> 24) & 0xFFU;
buffer[length++] = (m_seqNo >> 16) & 0xFFU;
buffer[length++] = (m_seqNo >> 8) & 0xFFU;
buffer[length++] = (m_seqNo >> 0) & 0xFFU;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// PTT off
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Type, 2 for metadata
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x02U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// TLV TAG for Metadata
buffer[length++] = 0x08U;
// TLV Length
buffer[length++] = 3U + 4U + 3U + 1U + 1U + (unsigned char)m_callsign.size() + 1U;
// DMR Id
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Rpt Id
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Talk Group
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
buffer[length++] = 0x00U;
// Time Slot
buffer[length++] = 0x00U;
// Color Code
buffer[length++] = 0x00U;
// Callsign
for (std::string::const_iterator it = m_callsign.cbegin(); it != m_callsign.cend(); ++it)
buffer[length++] = *it;
// End of Metadata
buffer[length++] = 0x00U;
length = 70U;
if (length > 0U) {
if (m_debug)
CUtils::dump(1U, "FM Network Data Sent", buffer, length);
return m_socket.write(buffer, length, m_addr, m_addrLen);
} else {
return true;
}
}
bool CFMNetwork::writeRawStart()
{
if (m_fp != nullptr) {
size_t n = ::fwrite("O", 1, 1, m_fp);
if (n != 1) {
#if !defined(_WIN32) && !defined(_WIN64)
LogError("Cannot write to the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno);
#else
LogError("Cannot write to the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError());
#endif
return false;
}
::fflush(m_fp);
}
return true;
}

View file

@ -21,22 +21,18 @@
#include "RingBuffer.h"
#include "UDPSocket.h"
#include "Defines.h"
#include "Timer.h"
#if defined(HAS_SRC)
#include <samplerate.h>
#endif
#if defined(USE_FM)
#include <cstdint>
#include <string>
enum class FM_NETWORK_PROTOCOL {
USRP,
RAW
};
class CFMNetwork {
public:
CFMNetwork(const std::string& callsign, const std::string& protocol, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, unsigned int sampleRate, const std::string& squelchFile, bool debug);
CFMNetwork(const std::string& callsign, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug);
~CFMNetwork();
bool open();
@ -57,31 +53,20 @@ public:
private:
std::string m_callsign;
FM_NETWORK_PROTOCOL m_protocol;
CUDPSocket m_socket;
sockaddr_storage m_addr;
unsigned int m_addrLen;
unsigned int m_sampleRate;
std::string m_squelchFile;
bool m_debug;
bool m_enabled;
CRingBuffer<unsigned char> m_buffer;
unsigned int m_seqNo;
#if defined(HAS_SRC)
SRC_STATE* m_resampler;
#endif
int m_error;
FILE* m_fp;
CTimer m_timer;
bool writeUSRPStart();
bool writeRawStart();
bool writeUSRPData(const float* data, unsigned int nSamples);
bool writeRawData(const float* in, unsigned int nIn);
bool writeUSRPEnd();
bool writeRawEnd();
bool writeStart();
bool writePing();
};
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2023,2025 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
@ -18,6 +18,8 @@
#include "Golay2087.h"
#if defined(USE_DMR)
#include <cstdio>
#include <cassert>
@ -260,3 +262,6 @@ void CGolay2087::encode(unsigned char* data)
data[1U] = cksum & 0xFFU;
data[2U] = cksum >> 8;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2023 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
@ -19,6 +19,10 @@
#ifndef Golay2087_H
#define Golay2087_H
#include "Defines.h"
#if defined(USE_DMR)
class CGolay2087 {
public:
static void encode(unsigned char* data);
@ -30,3 +34,6 @@ private:
};
#endif
#endif

File diff suppressed because it is too large Load diff

174
HD44780.h
View file

@ -1,174 +0,0 @@
/*
* Copyright (C) 2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX & Tony Corbett G0WFV
*
* 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(HD44780_H)
#define HD44780_H
#include "Display.h"
#include "Timer.h"
#include <string>
#include <vector>
#include <mcp23017.h>
#include <pcf8574.h>
enum ADAFRUIT_COLOUR {
AC_OFF,
AC_WHITE,
AC_RED,
AC_GREEN,
AC_BLUE,
AC_PURPLE,
AC_YELLOW,
AC_ICE
};
// Defines for the Adafruit Pi LCD interface board
#ifdef ADAFRUIT_DISPLAY
#define AF_BASE 100
/* Not yet used defines (for possible future use)
*
* #define AF_SELECT (AF_BASE + 0)
* #define AF_RIGHT (AF_BASE + 1)
* #define AF_DOWN (AF_BASE + 2)
* #define AF_UP (AF_BASE + 3)
* #define AF_LEFT (AF_BASE + 4)
*/
#define AF_RED (AF_BASE + 6)
#define AF_GREEN (AF_BASE + 7)
#define AF_BLUE (AF_BASE + 8)
#define AF_D3 (AF_BASE + 9)
#define AF_D2 (AF_BASE + 10)
#define AF_D1 (AF_BASE + 11)
#define AF_D0 (AF_BASE + 12)
#define AF_E (AF_BASE + 13)
#define AF_RW (AF_BASE + 14)
#define AF_RS (AF_BASE + 15)
#define AF_ON LOW
#define AF_OFF HIGH
// #define MCP23017 0x20
#endif
// Define for HD44780 connected via a PCF8574 GPIO extender
#ifdef PCF8574_DISPLAY
#define AF_BASE 100
#define AF_RS (AF_BASE + 0)
#define AF_RW (AF_BASE + 1)
#define AF_E (AF_BASE + 2)
#define AF_BL (AF_BASE + 3)
#define AF_D0 (AF_BASE + 4)
#define AF_D1 (AF_BASE + 5)
#define AF_D2 (AF_BASE + 6)
#define AF_D3 (AF_BASE + 7)
// #define PCF8574 0x27
#endif
class CHD44780 : public CDisplay
{
public:
CHD44780(unsigned int rows, unsigned int cols, const std::string& callsign, unsigned int dmrid, const std::vector<unsigned int>& pins, unsigned int i2cAddress, bool pwm, unsigned int pwmPin, unsigned int pwmBright, unsigned int pwmDim, bool displayClock, bool utc, bool duplex);
virtual ~CHD44780();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void setFMInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
unsigned int m_rows;
unsigned int m_cols;
std::string m_callsign;
unsigned int m_dmrid;
unsigned int m_rb;
unsigned int m_strb;
unsigned int m_d0;
unsigned int m_d1;
unsigned int m_d2;
unsigned int m_d3;
unsigned int m_i2cAddress;
bool m_pwm;
unsigned int m_pwmPin;
unsigned int m_pwmBright;
unsigned int m_pwmDim;
bool m_displayClock;
bool m_utc;
bool m_duplex;
int m_fd;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
/*
CTimer m_dmrScrollTimer1;
CTimer m_dmrScrollTimer2;
CTimer m_dstarScrollTimer;
*/
#ifdef ADAFRUIT_DISPLAY
void adafruitLCDSetup();
void adafruitLCDColour(ADAFRUIT_COLOUR colour);
#endif
#ifdef PCF8574_DISPLAY
void pcf8574LCDSetup();
#endif
};
#endif

View file

@ -1,107 +0,0 @@
IDLE SCREEN LAYOUTS
-------------------
16 x 2
------
With clock Without clock
---------- -------------
0 1 0 1
0123456789012345 0123456789012345
+----------------+ +----------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1|MMDVM HH:MM:SS| 1|MMDVM Idle|
+----------------+ +----------------+
40 x 2
------
With clock Without clock
---------- -------------
0 1 2 3 0 1 2 3
0123456789012345678901234567890123456789 0123456789012345678901234567890123456789
+----------------------------------------+ +----------------------------------------+
0|AAAAAA DD/MM/YY NNNNNNN| 0|AAAAAA NNNNNNN|
1|MMDVM HH:MM:SS Idle| 1|MMDVM Idle|
+----------------------------------------+ +----------------------------------------+
16 x 4
------
With clock Without clock
---------- -------------
0 1 0 1
0123456789012345 0123456789012345
+----------------+ +----------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1| DD/MM/YY | 1| |
2| HH:MM:SS | 2| |
3|MMDVM Idle| 3|MMDVM Idle|
+----------------+ +----------------+
20 x 4
------
With clock Without clock
---------- -------------
0 1 0 1
01234567890123456479 01234567890123456789
+--------------------+ +--------------------+
0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN|
1| DD/MM/YY | 1| |
2| HH:MM:SS | 2| |
3|MMDVM Idle| 3|MMDVM Idle|
+--------------------+ +--------------------+
D-STAR LAYOUTS
-------------
16 x 2
------
0 1
0123456789012345
+----------------+
0|F AAAAAAAA/AAAAX|
1|T AAAAAAAA |
+----------------+
40 x 2
------
0 1 2 3
0123456789012345678901234567890123456789
+----------------------------------------+
0|F AAAAAAAA/AAAA X|
1|T AAAAAAAA via AAAAAAAA |
+----------------------------------------+
16 x 4
------
0 1
0123456789012345
+----------------+
0|D-Star |
1|F AAAAAAAA/AAAAX|
2|T AAAAAAAA |
3|via AAAAAAAA |
+----------------+
20 x 4
------
0 1
01234567890123456479
+--------------------+
0|D-Star |
1|F AAAAAAAA/AAAA X|
2|T AAAAAAAA |
3|via AAAAAAAA |
+--------------------+

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA
*
* This program is free software; you can redistribute it and/or modify
@ -18,7 +18,9 @@
*/
#include "IIRDirectForm1Filter.h"
#include "math.h"
#include <cmath>
#if defined(USE_FM)
CIIRDirectForm1Filter::CIIRDirectForm1Filter(float b0, float b1, float b2, float , float a1, float a2, float addtionalGaindB) :
m_x2(0.0F),
@ -58,3 +60,6 @@ void CIIRDirectForm1Filter::reset()
m_y1 = 0.0f;
m_y2 = 0.0f;
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX
* Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA
*
* This program is free software; you can redistribute it and/or modify
@ -20,6 +20,10 @@
#if !defined(IIRDIRECTFORM1FILTER_H)
#define IIRDIRECTFORM1FILTER_H
#include "Defines.h"
#if defined(USE_FM)
class CIIRDirectForm1Filter
{
public:
@ -46,5 +50,7 @@ private:
float m_additionalGainLin;
};
#endif
#endif
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 659 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View file

@ -1,870 +0,0 @@
/*
* Copyright (C) 2016,2017,2018 by Tony Corbett G0WFV
* Copyright (C) 2018,2020,2024,2025 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.
*/
/*
* Some LCD displays include additional LEDs for status.
* If they exist, the LDCproc server will use the output command.
* If the LEDs do not exist, the command is ignored.
* to control these LEDs Below are the values for the Crystalfontz CFA-635.
* N4IRS
* LED 1 (DMR)
* Green 1 0000 0001
* Red 16 0001 0000
* Yellow 17 0001 0001
* LED 2 (P25)
* Green 2 0000 0010
* Red 32 0010 0000
* Yellow 34 0010 0010
* LED 3 (Fusion)
* Green 4 0000 0100
* Red 64 0100 0000
* Yellow 68 1000 0100
* LED 4 (D-Star)
* Green 8 0000 1000
* Red 128 1000 0000
* Yellow 136 1000 1000
* LED 5 (NXDN)
* Green 16 0001 0000
* Red 255 1111 1111
* Yellow 255 1111 1111
*/
#include "LCDproc.h"
#include "Log.h"
#include <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <clocale>
#include <ctime>
#if !defined(_WIN32) && !defined(_WIN64)
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <stdarg.h>
#else
#include <ws2tcpip.h>
#include <WinSock2.h>
#endif
#define BUFFER_MAX_LEN 128
#if defined(_WIN32) || defined(_WIN64)
SOCKET m_socketfd;
#else
int m_socketfd;
#endif
char m_buffer[BUFFER_MAX_LEN];
fd_set m_readfds, m_writefds;
struct timeval m_timeout;
int m_recvsize;
unsigned int m_rows(0);
unsigned int m_cols(0);
bool m_screensDefined(false);
bool m_connected(false);
char m_displayBuffer1[BUFFER_MAX_LEN];
char m_displayBuffer2[BUFFER_MAX_LEN];
const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms
const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms
const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms
const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms
const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms
CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned short localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) :
CDisplay(),
m_address(address),
m_port(port),
m_localPort(localPort),
m_callsign(callsign),
m_dmrid(dmrid),
m_displayClock(displayClock),
m_utc(utc),
m_duplex(duplex),
//m_duplex(true), // uncomment to force duplex display for testing!
m_dimOnIdle(dimOnIdle),
m_dmr(false),
m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms
m_rssiCount1(0U),
m_rssiCount2(0U)
{
}
CLCDproc::~CLCDproc()
{
}
bool CLCDproc::open()
{
int err;
unsigned int addrlen;
std::string port, localPort;
struct sockaddr_storage serverAddress, clientAddress;
struct addrinfo hints, *res;
port = std::to_string(m_port);
localPort = std::to_string(m_localPort);
memset(&hints, 0, sizeof(hints));
/* Lookup the hostname address */
hints.ai_flags = AI_NUMERICSERV;
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(m_address.c_str(), port.c_str(), &hints, &res);
if (err) {
LogError("LCDproc, cannot lookup server");
return false;
}
memcpy(&serverAddress, res->ai_addr, addrlen = (unsigned int)res->ai_addrlen);
freeaddrinfo(res);
/* Lookup the client address (random port - need to specify manual port from ini file) */
hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
hints.ai_family = serverAddress.ss_family;
err = getaddrinfo(nullptr, localPort.c_str(), &hints, &res);
if (err) {
LogError("LCDproc, cannot lookup client");
return false;
}
memcpy(&clientAddress, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
/* Create TCP socket */
m_socketfd = socket(clientAddress.ss_family, SOCK_STREAM, 0);
#if defined(_WIN32) || defined(_WIN64)
if (m_socketfd == INVALID_SOCKET) {
#else
if (m_socketfd == -1) {
#endif
LogError("LCDproc, failed to create socket");
return false;
}
/* Bind the address to the socket */
if (bind(m_socketfd, (struct sockaddr *)&clientAddress, addrlen) == -1) {
LogError("LCDproc, error whilst binding address");
return false;
}
/* Connect to server */
if (connect(m_socketfd, (struct sockaddr *)&serverAddress, addrlen) == -1) {
LogError("LCDproc, cannot connect to server");
return false;
}
socketPrintf(m_socketfd, "hello"); // Login to the LCD server
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
return true;
}
void CLCDproc::setIdleInt()
{
m_clockDisplayTimer.start(); // Start the clock display in IDLE only
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setErrorInt(const char* text)
{
assert(text != nullptr);
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Error", m_cols - 4, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setLockoutInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Lockout", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
// LED 4 Green 8 Red 128 Yellow 136
void CLCDproc::setQuitInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u Stopped", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::setFMInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_screensDefined) {
socketPrintf(m_socketfd, "screen_set DStar -priority hidden");
socketPrintf(m_socketfd, "screen_set DMR -priority hidden");
socketPrintf(m_socketfd, "screen_set YSF -priority hidden");
socketPrintf(m_socketfd, "screen_set P25 -priority hidden");
socketPrintf(m_socketfd, "screen_set NXDN -priority hidden");
socketPrintf(m_socketfd, "widget_set Status Status %u %u FM", m_cols - 6, m_rows);
socketPrintf(m_socketfd, "output 0"); // Clear all LEDs
}
m_dmr = false;
}
void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector)
{
assert(my1 != nullptr);
assert(my2 != nullptr);
assert(your != nullptr);
assert(type != nullptr);
assert(reflector != nullptr);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DStar -priority foreground");
socketPrintf(m_socketfd, "widget_set DStar Mode 1 1 \"D-Star\"");
::sprintf(m_displayBuffer1, "%.8s", your);
char *p = m_displayBuffer1;
for (; *p; ++p) {
if (*p == ' ')
*p = '_';
}
if (strcmp(reflector, " ") != 0)
sprintf(m_displayBuffer2, " via %.8s", reflector);
else
memset(m_displayBuffer2, 0, BUFFER_MAX_LEN);
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s to %s%s\"", m_cols - 1, my1, my2, m_displayBuffer1, m_displayBuffer2);
} else {
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s\"", m_cols - 1, my1, my2);
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, m_displayBuffer1, m_displayBuffer2);
socketPrintf(m_socketfd, "output 128"); // Set LED4 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeDStarRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= DSTAR_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearDStarInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 8"); // Set LED4 color green
}
// LED 1 Green 1 Red 16 Yellow 17
void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type)
{
assert(type != nullptr);
if (!m_dmr) {
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set DMR -priority foreground");
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
else
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u \"\"", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u \"\"", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 %u %u %u h 3 \"\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
}
}
if (m_duplex) {
if (m_rows > 2U)
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (slotNo == 1U)
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2, m_cols - 1, m_rows / 2, src.c_str(), group ? "TG" : "", dst.c_str());
else
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s > %s%s\"", m_cols - 1, src.c_str(), group ? "TG" : "", dst.c_str());
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s >\"", m_cols - 1, src.c_str());
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, group ? "TG" : "", dst.c_str());
}
}
socketPrintf(m_socketfd, "output 16"); // Set LED1 color red
m_dmr = true;
m_rssiCount1 = 0U;
m_rssiCount2 = 0U;
}
void CLCDproc::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi)
{
if (m_rows > 2) {
if (slotNo == 1U) {
if (m_rssiCount1 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u -%3udBm", 1, 4, rssi);
m_rssiCount1++;
if (m_rssiCount1 >= DMR_RSSI_COUNT)
m_rssiCount1 = 0U;
} else {
if (m_rssiCount2 == 0U)
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u -%3udBm", (m_cols / 2) + 1, 4, rssi);
m_rssiCount2++;
if (m_rssiCount2 >= DMR_RSSI_COUNT)
m_rssiCount2 = 0U;
}
}
}
void CLCDproc::clearDMRInt(unsigned int slotNo)
{
m_clockDisplayTimer.stop(); // Stop the clock display
if (m_duplex) {
if (slotNo == 1U) {
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u %*.s", 1, 4, m_cols / 2, " ");
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
} else {
socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " ");
}
socketPrintf(m_socketfd, "output 1"); // Set LED1 color green
}
// LED 3 Green 4 Red 64 Yellow 68
void CLCDproc::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin)
{
assert(source != nullptr);
assert(dest != nullptr);
assert(type != nullptr);
assert(origin != nullptr);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set YSF -priority foreground");
socketPrintf(m_socketfd, "widget_set YSF Mode 1 1 \"System Fusion\"");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s > DG-ID %u\"", source, dgid);
} else {
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"DG-ID %u\"", dgid);
socketPrintf(m_socketfd, "output 64"); // Set LED3 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeFusionRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U)
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
m_rssiCount1++;
if (m_rssiCount1 >= YSF_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearFusionInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 4"); // Set LED3 color green
}
// LED 2 Green 2 Red 32 Yellow 34
void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != nullptr);
assert(type != nullptr);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set P25 -priority foreground");
socketPrintf(m_socketfd, "widget_set P25 Mode 1 1 P25");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest);
} else {
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest);
socketPrintf(m_socketfd, "output 32"); // Set LED2 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeP25RSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= P25_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearP25Int()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 2"); // Set LED2 color green
}
// LED 5 Green 16 Red 255 Yellow 255
void CLCDproc::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type)
{
assert(source != nullptr);
assert(type != nullptr);
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "screen_set NXDN -priority foreground");
socketPrintf(m_socketfd, "widget_set NXDN Mode 1 1 NXDN");
if (m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest);
} else {
socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"%.10s >\"", source);
socketPrintf(m_socketfd, "widget_set NXDN Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest);
socketPrintf(m_socketfd, "output 255"); // Set LED5 color red
}
m_dmr = false;
m_rssiCount1 = 0U;
}
void CLCDproc::writeNXDNRSSIInt(unsigned char rssi)
{
if (m_rssiCount1 == 0U) {
socketPrintf(m_socketfd, "widget_set NXDN Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi);
}
m_rssiCount1++;
if (m_rssiCount1 >= NXDN_RSSI_COUNT)
m_rssiCount1 = 0U;
}
void CLCDproc::clearNXDNInt()
{
m_clockDisplayTimer.stop(); // Stop the clock display
socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set NXDN Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set NXDN Line4 1 4 15 4 h 3 \"\"");
socketPrintf(m_socketfd, "output 16"); // Set LED5 color green
}
void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message)
{
}
void CLCDproc::clearPOCSAGInt()
{
}
void CLCDproc::writeCWInt()
{
}
void CLCDproc::clearCWInt()
{
}
void CLCDproc::clockInt(unsigned int ms)
{
m_clockDisplayTimer.clock(ms);
// Idle clock display
if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) {
time_t currentTime;
struct tm *Time;
time(&currentTime);
if (m_utc)
Time = gmtime(&currentTime);
else
Time = localtime(&currentTime);
setlocale(LC_TIME, "");
strftime(m_displayBuffer1, 128, "%X", Time); // Time
strftime(m_displayBuffer2, 128, "%x", Time); // Date
if (m_cols < 26U && m_rows == 2U) {
socketPrintf(m_socketfd, "widget_set Status Time %u 2 \"%s%s\"", m_cols - 9, strlen(m_displayBuffer1) > 8 ? "" : " ", m_displayBuffer1);
} else {
socketPrintf(m_socketfd, "widget_set Status Time %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2, m_displayBuffer1);
socketPrintf(m_socketfd, "widget_set Status Date %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2 + 1, m_displayBuffer2);
}
m_clockDisplayTimer.start();
}
// We must set all this information on each select we do
FD_ZERO(&m_readfds); // empty readfds
// Then we put all the descriptors we want to wait for in a mask = m_readfds
FD_SET(m_socketfd, &m_readfds);
// Timeout, we will stop waiting for information
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
/* The first parameter is the biggest descriptor + 1. The first one was 0, so
* every other descriptor will be bigger
*
* readfds = &m_readfds
* writefds = we are not waiting for writefds
* exceptfds = we are not waiting for exception fds
*/
if (select(int(m_socketfd) + 1, &m_readfds, nullptr, nullptr, &m_timeout) == -1) {
LogError("LCDproc, error on select");
return;
}
// If something was received from the server...
if (FD_ISSET(m_socketfd, &m_readfds)) {
m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0);
if (m_recvsize == -1) {
LogError("LCDproc, cannot receive information");
return;
}
m_buffer[m_recvsize] = '\0';
char *argv[256];
size_t len = strlen(m_buffer);
// Now split the string into tokens...
int argc = 0;
int newtoken = 1;
for (size_t i = 0U; i < len; i++) {
switch (m_buffer[i]) {
case ' ':
newtoken = 1;
m_buffer[i] = 0;
break;
default: /* regular chars, keep tokenizing */
if (newtoken)
argv[argc++] = m_buffer + i;
newtoken = 0;
break;
case '\0':
case '\n':
m_buffer[i] = 0;
if (argc > 0) {
if (0 == strcmp(argv[0], "listen")) {
LogDebug("LCDproc, the %s screen is displayed", argv[1]);
} else if (0 == strcmp(argv[0], "ignore")) {
LogDebug("LCDproc, the %s screen is hidden", argv[1]);
} else if (0 == strcmp(argv[0], "key")) {
LogDebug("LCDproc, Key %s", argv[1]);
} else if (0 == strcmp(argv[0], "menu")) {
} else if (0 == strcmp(argv[0], "connect")) {
// connect LCDproc 0.5.7 protocol 0.3 lcd wid 16 hgt 2 cellwid 5 cellhgt 8
int a;
for (a = 1; a < argc; a++) {
if (0 == strcmp(argv[a], "wid"))
m_cols = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "hgt"))
m_rows = atoi(argv[++a]);
else if (0 == strcmp(argv[a], "cellwid")) {
//lcd_cellwid = atoi(argv[++a]);
} else if (0 == strcmp(argv[a], "cellhgt")) {
//lcd_cellhgt = atoi(argv[++a]);
}
}
m_connected = true;
socketPrintf(m_socketfd, "client_set -name MMDVMHost");
} else if (0 == strcmp(argv[0], "bye")) {
//close the socket- todo
} else if (0 == strcmp(argv[0], "success")) {
//LogDebug("LCDproc, command successful");
} else if (0 == strcmp(argv[0], "huh?")) {
sprintf(m_displayBuffer1, "LCDproc, command failed:");
sprintf(m_displayBuffer2, " ");
int j;
for (j = 1; j < argc; j++) {
strcat(m_displayBuffer1, m_displayBuffer2);
strcat(m_displayBuffer1, argv[j]);
}
LogDebug("%s", m_displayBuffer1);
}
}
/* Restart tokenizing */
argc = 0;
newtoken = 1;
break;
} /* switch( m_buffer[i] ) */
}
}
if (!m_screensDefined && m_connected)
defineScreens();
}
void CLCDproc::close()
{
}
#if defined(_WIN32) || defined(_WIN64)
int CLCDproc::socketPrintf(SOCKET fd, const char* format, ...)
#else
int CLCDproc::socketPrintf(int fd, const char *format, ...)
#endif
{
char buf[BUFFER_MAX_LEN];
va_list ap;
va_start(ap, format);
int size = vsnprintf(buf, BUFFER_MAX_LEN, format, ap);
va_end(ap);
if (size < 0) {
LogError("LCDproc, socketPrintf: vsnprintf failed");
return -1;
}
if (size > BUFFER_MAX_LEN)
LogWarning("LCDproc, socketPrintf: vsnprintf truncated message");
FD_ZERO(&m_writefds); // empty writefds
FD_SET(m_socketfd, &m_writefds);
m_timeout.tv_sec = 0;
m_timeout.tv_usec = 0;
if (select(int(m_socketfd) + 1, nullptr, &m_writefds, nullptr, &m_timeout) == -1)
LogError("LCDproc, error on select");
if (FD_ISSET(m_socketfd, &m_writefds)) {
if (send(m_socketfd, buf, int(strlen(buf) + 1U), 0) == -1) {
LogError("LCDproc, cannot send data");
return -1;
}
}
return 0;
}
void CLCDproc::defineScreens()
{
// The Status Screen
socketPrintf(m_socketfd, "screen_add Status");
socketPrintf(m_socketfd, "screen_set Status -name Status -heartbeat on -priority info -backlight %s", m_dimOnIdle ? "off" : "on");
socketPrintf(m_socketfd, "widget_add Status Callsign string");
socketPrintf(m_socketfd, "widget_add Status DMRNumber string");
socketPrintf(m_socketfd, "widget_add Status Title string");
socketPrintf(m_socketfd, "widget_add Status Status string");
socketPrintf(m_socketfd, "widget_add Status Time string");
socketPrintf(m_socketfd, "widget_add Status Date string");
socketPrintf(m_socketfd, "widget_set Status Callsign 1 1 %s", m_callsign.c_str());
socketPrintf(m_socketfd, "widget_set Status DMRNumber %u 1 %u", m_cols - 7, m_dmrid);
socketPrintf(m_socketfd, "widget_set Status Title 1 %u MMDVM", m_rows);
socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows);
// The DStar Screen
socketPrintf(m_socketfd, "screen_add DStar");
socketPrintf(m_socketfd, "screen_set DStar -name DStar -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DStar Mode string");
socketPrintf(m_socketfd, "widget_add DStar Line2 scroller");
socketPrintf(m_socketfd, "widget_add DStar Line3 scroller");
socketPrintf(m_socketfd, "widget_add DStar Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\"");
socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\"");
*/
// The DMR Screen
socketPrintf(m_socketfd, "screen_add DMR");
socketPrintf(m_socketfd, "screen_set DMR -name DMR -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add DMR Mode string");
socketPrintf(m_socketfd, "widget_add DMR Slot1_ string");
socketPrintf(m_socketfd, "widget_add DMR Slot2_ string");
socketPrintf(m_socketfd, "widget_add DMR Slot1 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot2 scroller");
socketPrintf(m_socketfd, "widget_add DMR Slot1RSSI string");
socketPrintf(m_socketfd, "widget_add DMR Slot2RSSI string");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u 1", m_rows / 2);
socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u 2", m_rows / 2 + 1);
socketPrintf(m_socketfd, "widget_set DMR Slot1 3 1 15 1 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set DMR Slot2 3 2 15 2 h 3 \"Listening\"");
*/
// The YSF Screen
socketPrintf(m_socketfd, "screen_add YSF");
socketPrintf(m_socketfd, "screen_set YSF -name YSF -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add YSF Mode string");
socketPrintf(m_socketfd, "widget_add YSF Line2 scroller");
socketPrintf(m_socketfd, "widget_add YSF Line3 scroller");
socketPrintf(m_socketfd, "widget_add YSF Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set YSF Line2 2 1 15 1 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set YSF Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set YSF Line4 4 2 15 2 h 3 \" \"");
*/
// The P25 Screen
socketPrintf(m_socketfd, "screen_add P25");
socketPrintf(m_socketfd, "screen_set P25 -name P25 -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add P25 Mode string");
socketPrintf(m_socketfd, "widget_add P25 Line2 scroller");
socketPrintf(m_socketfd, "widget_add P25 Line3 scroller");
socketPrintf(m_socketfd, "widget_add P25 Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set P25 Line3 2 1 15 1 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set P25 Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set P25 Line4 4 2 15 2 h 3 \" \"");
*/
// The NXDN Screen
socketPrintf(m_socketfd, "screen_add NXDN");
socketPrintf(m_socketfd, "screen_set NXDN -name NXDN -heartbeat on -priority hidden -backlight on");
socketPrintf(m_socketfd, "widget_add NXDN Mode string");
socketPrintf(m_socketfd, "widget_add NXDN Line2 scroller");
socketPrintf(m_socketfd, "widget_add NXDN Line3 scroller");
socketPrintf(m_socketfd, "widget_add NXDN Line4 scroller");
/* Do we need to pre-populate the values??
socketPrintf(m_socketfd, "widget_set NXDN Line3 2 1 15 1 h 3 \"Listening\"");
socketPrintf(m_socketfd, "widget_set NXDN Line3 3 1 15 1 h 3 \" \"");
socketPrintf(m_socketfd, "widget_set NXDN Line4 4 2 15 2 h 3 \" \"");
*/
m_screensDefined = true;
}

101
LCDproc.h
View file

@ -1,101 +0,0 @@
/*
* Copyright (C) 2016,2017 by Tony Corbett G0WFV
* Copyright (C) 2018,2020,2025 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(LCDproc_H)
#define LCDproc_H
#include "Display.h"
#include "Timer.h"
#include <string>
#if defined(_WIN32) || defined(_WIN64)
#include <ws2tcpip.h>
#include <Winsock2.h>
#endif
class CLCDproc : public CDisplay
{
public:
CLCDproc(std::string address, unsigned int port, unsigned short localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle);
virtual ~CLCDproc();
virtual bool open();
virtual void close();
protected:
virtual void setIdleInt();
virtual void setErrorInt(const char* text);
virtual void setLockoutInt();
virtual void setQuitInt();
virtual void setFMInt();
virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
virtual void writeDStarRSSIInt(unsigned char rssi);
virtual void clearDStarInt();
virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi);
virtual void clearDMRInt(unsigned int slotNo);
virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin);
virtual void writeFusionRSSIInt(unsigned char rssi);
virtual void clearFusionInt();
virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeP25RSSIInt(unsigned char rssi);
virtual void clearP25Int();
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();
virtual void clockInt(unsigned int ms);
private:
std::string m_address;
unsigned int m_port;
unsigned short m_localPort;
std::string m_callsign;
unsigned int m_dmrid;
bool m_displayClock;
bool m_utc;
bool m_duplex;
bool m_dimOnIdle;
bool m_dmr;
CTimer m_clockDisplayTimer;
unsigned int m_rssiCount1;
unsigned int m_rssiCount2;
#if defined(_WIN32) || defined(_WIN64)
int socketPrintf(SOCKET fd, const char* format, ...);
#else
int socketPrintf(int fd, const char *format, ...);
#endif
void defineScreens();
};
#endif

135
Log.cpp
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2020,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2020,2022,2023,2025 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
@ -17,6 +17,7 @@
*/
#include "Log.h"
#include "MQTTConnection.h"
#if defined(_WIN32) || defined(_WIN64)
#include <Windows.h>
@ -32,117 +33,27 @@
#include <cassert>
#include <cstring>
static unsigned int m_fileLevel = 2U;
static std::string m_filePath;
static std::string m_fileRoot;
static bool m_fileRotate = true;
CMQTTConnection* m_mqtt = nullptr;
static FILE* m_fpLog = nullptr;
static bool m_daemon = false;
static unsigned int m_mqttLevel = 2U;
static unsigned int m_displayLevel = 2U;
static struct tm m_tm;
static char LEVELS[] = " DMIWEF";
static bool logOpenRotate()
void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel)
{
bool status = false;
if (m_fileLevel == 0U)
return true;
time_t now;
::time(&now);
struct tm* tm = ::gmtime(&now);
if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) {
if (m_fpLog != nullptr)
return true;
} else {
if (m_fpLog != nullptr)
::fclose(m_fpLog);
}
char filename[200U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#else
::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday);
#endif
if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) {
status = true;
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon)
dup2(fileno(m_fpLog), fileno(stderr));
#endif
}
m_tm = *tm;
return status;
}
static bool logOpenNoRotate()
{
bool status = false;
if (m_fileLevel == 0U)
return true;
if (m_fpLog != nullptr)
return true;
char filename[200U];
#if defined(_WIN32) || defined(_WIN64)
::sprintf(filename, "%s\\%s.log", m_filePath.c_str(), m_fileRoot.c_str());
#else
::sprintf(filename, "%s/%s.log", m_filePath.c_str(), m_fileRoot.c_str());
#endif
if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) {
status = true;
#if !defined(_WIN32) && !defined(_WIN64)
if (m_daemon)
dup2(fileno(m_fpLog), fileno(stderr));
#endif
}
return status;
}
bool LogOpen()
{
if (m_fileRotate)
return logOpenRotate();
else
return logOpenNoRotate();
}
bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate)
{
m_filePath = filePath;
m_fileRoot = fileRoot;
m_fileLevel = fileLevel;
m_mqttLevel = mqttLevel;
m_displayLevel = displayLevel;
m_daemon = daemon;
m_fileRotate = rotate;
if (m_daemon)
m_displayLevel = 0U;
return ::LogOpen();
}
void LogFinalise()
{
if (m_fpLog != nullptr)
::fclose(m_fpLog);
if (m_mqtt != nullptr) {
m_mqtt->close();
delete m_mqtt;
m_mqtt = nullptr;
}
}
void Log(unsigned int level, const char* fmt, ...)
@ -171,22 +82,26 @@ void Log(unsigned int level, const char* fmt, ...)
va_end(vl);
if (level >= m_fileLevel && m_fileLevel != 0U) {
bool ret = ::LogOpen();
if (!ret)
return;
::fprintf(m_fpLog, "%s\n", buffer);
::fflush(m_fpLog);
}
if (m_mqtt != nullptr && level >= m_mqttLevel && m_mqttLevel != 0U)
m_mqtt->publish("log", buffer);
if (level >= m_displayLevel && m_displayLevel != 0U) {
::fprintf(stdout, "%s\n", buffer);
::fflush(stdout);
}
if (level == 6U) { // Fatal
::fclose(m_fpLog);
if (level == 6U) // Fatal
exit(1);
}
void WriteJSON(const std::string& topLevel, nlohmann::json& json)
{
if (m_mqtt != nullptr) {
nlohmann::json top;
top[topLevel] = json;
m_mqtt->publish("json", top.dump());
}
}

8
Log.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2020,2022,2023 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
@ -21,6 +21,8 @@
#include <string>
#include <nlohmann/json.hpp>
#define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__)
#define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__)
#define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__)
@ -30,7 +32,9 @@
extern void Log(unsigned int level, const char* fmt, ...);
extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate);
extern void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel);
extern void LogFinalise();
extern void WriteJSON(const std::string& topLevel, nlohmann::json& json);
#endif

102
MMDVM.ini
View file

@ -6,7 +6,6 @@ Duplex=1
# ModeHang=10
RFModeHang=10
NetModeHang=3
Display=None
Daemon=0
[Info]
@ -23,11 +22,17 @@ URL=www.google.co.uk
[Log]
# Logging levels, 0=No logging
MQTTLevel=1
DisplayLevel=1
FileLevel=1
FilePath=.
FileRoot=MMDVM
FileRotate=1
[MQTT]
Host=127.0.0.1
Port=1883
Auth=0
Username=mmdvm
Password=mmdvm
Keepalive=60
Name=mmdvm
[CW Id]
Enable=1
@ -44,7 +49,7 @@ Time=24
[Modem]
# Valid values are "null", "uart", "udp", and (on Linux) "i2c"
Protocol=uart
Protocol=null
# The port and speed used for a UART connection
# UARTPort=\\.\COM4
# UARTPort=/dev/ttyACM0
@ -197,7 +202,7 @@ ExtAudioBoost=1
# ModeHang=10
[D-Star Network]
Enable=1
Enable=0
LocalAddress=127.0.0.1
LocalPort=20011
GatewayAddress=127.0.0.1
@ -206,24 +211,19 @@ GatewayPort=20010
Debug=0
[DMR Network]
Enable=1
# Type may be either 'Direct' or 'Gateway'. When Direct you must provide the Master's
# address as well as the Password, and for DMR+, Options also.
Type=Gateway
Enable=0
LocalAddress=127.0.0.1
LocalPort=62032
RemoteAddress=127.0.0.1
RemotePort=62031
# Password=P@ssw0rd1234
GatewayAddress=127.0.0.1
GatewayPort=62031
Jitter=360
Slot1=1
Slot2=1
# Options=
# ModeHang=3
Debug=0
[System Fusion Network]
Enable=1
Enable=0
LocalAddress=127.0.0.1
LocalPort=3200
GatewayAddress=127.0.0.1
@ -232,7 +232,7 @@ GatewayPort=4200
Debug=0
[P25 Network]
Enable=1
Enable=0
LocalAddress=127.0.0.1
LocalPort=32010
GatewayAddress=127.0.0.1
@ -241,7 +241,7 @@ GatewayPort=42020
Debug=0
[NXDN Network]
Enable=1
Enable=0
Protocol=Icom
LocalAddress=127.0.0.1
LocalPort=14021
@ -251,7 +251,7 @@ GatewayPort=14020
Debug=0
[POCSAG Network]
Enable=1
Enable=0
LocalAddress=127.0.0.1
LocalPort=3800
GatewayAddress=127.0.0.1
@ -261,12 +261,6 @@ Debug=0
[FM Network]
Enable=1
# Protocol may be USRP or RAW
Protocol=USRP
# SampleRate is only used in RAW mode
SampleRate=48000
# The squelch file is optional and only used in RAW mode
SquelchFile=/tmp/sql
LocalAddress=127.0.0.1
LocalPort=3810
GatewayAddress=127.0.0.1
@ -278,67 +272,9 @@ RXAudioGain=1.0
# ModeHang=3
Debug=0
[TFT Serial]
# Port=modem
Port=/dev/ttyAMA0
Brightness=50
ScreenLayout=0
[HD44780]
Rows=2
Columns=16
# For basic HD44780 displays (4-bit connection)
# rs, strb, d0, d1, d2, d3
Pins=11,10,0,1,2,3
# Device address for I2C
I2CAddress=0x20
# PWM backlight
PWM=0
PWMPin=21
PWMBright=100
PWMDim=16
DisplayClock=1
UTC=0
[Nextion]
# Port=modem
Port=/dev/ttyAMA0
Brightness=50
DisplayClock=1
UTC=0
#Screen Layout: 0=G4KLX 2=ON7LDS
ScreenLayout=2
IdleBrightness=20
# Output data from the Nextion
NextionOutput=0
NextionPort=6759
[OLED]
Type=3
Brightness=0
Invert=0
Scroll=1
Rotate=0
Cast=0
LogoScreensaver=1
[LCDproc]
Address=localhost
Port=13666
#LocalPort=13667
DimOnIdle=0
DisplayClock=1
UTC=0
[Lock File]
Enable=0
File=/tmp/MMDVM_Active.lck
[Remote Control]
Enable=0
Address=127.0.0.1
Port=7642

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
/*
* Copyright (C) 2015-2021,2023 by Jonathan Naylor G4KLX
* Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX
*
* This program is free software; you can redistribute it and/or modify
@ -36,99 +37,181 @@
#include "FMNetwork.h"
#include "DMRLookup.h"
#include "FMControl.h"
#include "Display.h"
#include "Defines.h"
#include "Timer.h"
#include "Modem.h"
#include "Conf.h"
#include <string>
class CMMDVMHost
{
public:
CMMDVMHost(const std::string& confFile);
~CMMDVMHost();
CMMDVMHost(const std::string& confFile);
~CMMDVMHost();
int run();
int run();
void buildNetworkStatusString(std::string &str);
void buildNetworkHostsString(std::string &str);
void buildNetworkStatusString(std::string &str);
void buildNetworkHostsString(std::string &str);
private:
CConf m_conf;
CModem* m_modem;
CDStarControl* m_dstar;
CDMRControl* m_dmr;
CYSFControl* m_ysf;
CP25Control* m_p25;
CNXDNControl* m_nxdn;
CPOCSAGControl* m_pocsag;
CFMControl* m_fm;
CDStarNetwork* m_dstarNetwork;
IDMRNetwork* m_dmrNetwork;
CYSFNetwork* m_ysfNetwork;
CP25Network* m_p25Network;
INXDNNetwork* m_nxdnNetwork;
CPOCSAGNetwork* m_pocsagNetwork;
CFMNetwork* m_fmNetwork;
CDisplay* m_display;
unsigned char m_mode;
unsigned int m_dstarRFModeHang;
unsigned int m_dmrRFModeHang;
unsigned int m_ysfRFModeHang;
unsigned int m_p25RFModeHang;
unsigned int m_nxdnRFModeHang;
unsigned int m_fmRFModeHang;
unsigned int m_dstarNetModeHang;
unsigned int m_dmrNetModeHang;
unsigned int m_ysfNetModeHang;
unsigned int m_p25NetModeHang;
unsigned int m_nxdnNetModeHang;
unsigned int m_pocsagNetModeHang;
unsigned int m_fmNetModeHang;
CTimer m_modeTimer;
CTimer m_dmrTXTimer;
CTimer m_cwIdTimer;
bool m_duplex;
unsigned int m_timeout;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
bool m_nxdnEnabled;
bool m_pocsagEnabled;
bool m_fmEnabled;
unsigned int m_cwIdTime;
CDMRLookup* m_dmrLookup;
CNXDNLookup* m_nxdnLookup;
std::string m_callsign;
unsigned int m_id;
std::string m_cwCallsign;
bool m_lockFileEnabled;
std::string m_lockFileName;
CRemoteControl* m_remoteControl;
bool m_fixedMode;
CConf m_conf;
CModem* m_modem;
#if defined(USE_DSTAR)
CDStarControl* m_dstar;
#endif
#if defined(USE_DMR)
CDMRControl* m_dmr;
#endif
#if defined(USE_YSF)
CYSFControl* m_ysf;
#endif
#if defined(USE_P25)
CP25Control* m_p25;
#endif
#if defined(USE_NXDN)
CNXDNControl* m_nxdn;
#endif
#if defined(USE_POCSAG)
CPOCSAGControl* m_pocsag;
#endif
#if defined(USE_FM)
CFMControl* m_fm;
#endif
#if defined(USE_DSTAR)
CDStarNetwork* m_dstarNetwork;
#endif
#if defined(USE_DMR)
CDMRNetwork* m_dmrNetwork;
#endif
#if defined(USE_YSF)
CYSFNetwork* m_ysfNetwork;
#endif
#if defined(USE_P25)
CP25Network* m_p25Network;
#endif
#if defined(USE_NXDN)
INXDNNetwork* m_nxdnNetwork;
#endif
#if defined(USE_POCSAG)
CPOCSAGNetwork* m_pocsagNetwork;
#endif
#if defined(USE_FM)
CFMNetwork* m_fmNetwork;
#endif
unsigned char m_mode;
#if defined(USE_DSTAR)
unsigned int m_dstarRFModeHang;
#endif
#if defined(USE_DMR)
unsigned int m_dmrRFModeHang;
#endif
#if defined(USE_YSF)
unsigned int m_ysfRFModeHang;
#endif
#if defined(USE_P25)
unsigned int m_p25RFModeHang;
#endif
#if defined(USE_NXDN)
unsigned int m_nxdnRFModeHang;
#endif
#if defined(USE_FM)
unsigned int m_fmRFModeHang;
#endif
#if defined(USE_DSTAR)
unsigned int m_dstarNetModeHang;
#endif
#if defined(USE_DMR)
unsigned int m_dmrNetModeHang;
#endif
#if defined(USE_YSF)
unsigned int m_ysfNetModeHang;
#endif
#if defined(USE_P25)
unsigned int m_p25NetModeHang;
#endif
#if defined(USE_NXDN)
unsigned int m_nxdnNetModeHang;
#endif
#if defined(USE_POCSAG)
unsigned int m_pocsagNetModeHang;
#endif
#if defined(USE_FM)
unsigned int m_fmNetModeHang;
#endif
CTimer m_modeTimer;
#if defined(USE_DMR)
CTimer m_dmrTXTimer;
#endif
CTimer m_cwIdTimer;
bool m_duplex;
unsigned int m_timeout;
bool m_dstarEnabled;
bool m_dmrEnabled;
bool m_ysfEnabled;
bool m_p25Enabled;
bool m_nxdnEnabled;
bool m_pocsagEnabled;
bool m_fmEnabled;
unsigned int m_cwIdTime;
#if defined(USE_DMR) || defined(USE_P25)
CDMRLookup* m_dmrLookup;
#endif
#if defined(USE_NXDN)
CNXDNLookup* m_nxdnLookup;
#endif
std::string m_callsign;
unsigned int m_id;
std::string m_cwCallsign;
bool m_lockFileEnabled;
std::string m_lockFileName;
CRemoteControl* m_remoteControl;
bool m_fixedMode;
void readParams();
bool createModem();
bool createDStarNetwork();
bool createDMRNetwork();
bool createYSFNetwork();
bool createP25Network();
bool createNXDNNetwork();
bool createPOCSAGNetwork();
bool createFMNetwork();
CTimer m_serialTimer;
unsigned char* m_serialBuffer;
unsigned int m_serialStart;
unsigned int m_serialLength;
void remoteControl();
void processModeCommand(unsigned char mode, unsigned int timeout);
void readParams();
bool createModem();
#if defined(USE_DSTAR)
bool createDStarNetwork();
#endif
#if defined(USE_DMR)
bool createDMRNetwork();
#endif
#if defined(USE_YSF)
bool createYSFNetwork();
#endif
#if defined(USE_P25)
bool createP25Network();
#endif
#if defined(USE_NXDN)
bool createNXDNNetwork();
#endif
#if defined(USE_POCSAG)
bool createPOCSAGNetwork();
#endif
#if defined(USE_FM)
bool createFMNetwork();
#endif
void writeSerial(const unsigned char* message, unsigned int length);
void remoteControl(const std::string& commandString);
void processModeCommand(unsigned char mode, unsigned int timeout);
void processEnableCommand(bool& mode, bool enabled);
void setMode(unsigned char mode);
void enableModemMode(bool& mode, bool enabled);
void processEnableModeCommand(unsigned char mode, bool hasController, bool& modeEnabled, bool enableMode);
void setMode(unsigned char mode);
void createLockFile(const char* mode) const;
void removeLockFile() const;
void createLockFile(const char* mode) const;
void removeLockFile() const;
void writeJSONMode(const std::string& mode);
void writeJSONMessage(const std::string& message);
static void onDisplay(const unsigned char* message, unsigned int length);
static void onCommand(const unsigned char* command, unsigned int length);
};
#endif

View file

@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.28307.271
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMDVMHost", "MMDVMHost.vcxproj", "{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RemoteCommand", "RemoteCommand.vcxproj", "{5A61AB93-58BB-413A-BBD9-A26284CB37AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -23,14 +21,6 @@ Global
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.Build.0 = Release|x64
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.ActiveCfg = Release|Win32
{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.Build.0 = Release|Win32
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x64.ActiveCfg = Debug|x64
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x64.Build.0 = Debug|x64
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x86.ActiveCfg = Debug|Win32
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x86.Build.0 = Debug|Win32
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x64.ActiveCfg = Release|x64
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x64.Build.0 = Release|x64
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x86.ActiveCfg = Release|Win32
{5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -87,15 +87,14 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -104,15 +103,14 @@
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>"$(ProjectDir)prebuild.cmd" $(ProjectDir)</Command>
@ -129,17 +127,16 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -150,39 +147,34 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AMBEFEC.h" />
<ClInclude Include="BCH.h" />
<ClInclude Include="BPTC19696.h" />
<ClInclude Include="CASTInfo.h" />
<ClInclude Include="Conf.h" />
<ClInclude Include="CRC.h" />
<ClInclude Include="Defines.h" />
<ClInclude Include="Display.h" />
<ClInclude Include="DMRAccessControl.h" />
<ClInclude Include="DMRControl.h" />
<ClInclude Include="DMRCSBK.h" />
<ClInclude Include="DMRData.h" />
<ClInclude Include="DMRDataHeader.h" />
<ClInclude Include="DMRDefines.h" />
<ClInclude Include="DMRDirectNetwork.h" />
<ClInclude Include="DMREMB.h" />
<ClInclude Include="DMREmbeddedData.h" />
<ClInclude Include="DMRFullLC.h" />
<ClInclude Include="DMRGatewayNetwork.h" />
<ClInclude Include="DMRLC.h" />
<ClInclude Include="DMRNetwork.h" />
<ClInclude Include="DMRShortLC.h" />
@ -202,16 +194,12 @@
<ClInclude Include="Hamming.h" />
<ClInclude Include="DMRLookup.h" />
<ClInclude Include="IIRDirectForm1Filter.h" />
<ClInclude Include="LCDproc.h" />
<ClInclude Include="Log.h" />
<ClInclude Include="MMDVMHost.h" />
<ClInclude Include="ModemPort.h" />
<ClInclude Include="ModemSerialPort.h" />
<ClInclude Include="MQTTConnection.h" />
<ClInclude Include="Mutex.h" />
<ClInclude Include="NetworkInfo.h" />
<ClInclude Include="Nextion.h" />
<ClInclude Include="NullController.h" />
<ClInclude Include="NullDisplay.h" />
<ClInclude Include="NXDNAudio.h" />
<ClInclude Include="NXDNControl.h" />
<ClInclude Include="NXDNConvolution.h" />
@ -237,7 +225,6 @@
<ClInclude Include="POCSAGControl.h" />
<ClInclude Include="POCSAGDefines.h" />
<ClInclude Include="POCSAGNetwork.h" />
<ClInclude Include="PseudoTTYController.h" />
<ClInclude Include="QR1676.h" />
<ClInclude Include="RemoteControl.h" />
<ClInclude Include="RingBuffer.h" />
@ -249,11 +236,8 @@
<ClInclude Include="UARTController.h" />
<ClInclude Include="Modem.h" />
<ClInclude Include="SerialPort.h" />
<ClInclude Include="SHA256.h" />
<ClInclude Include="SMeter.h" />
<ClInclude Include="StopWatch.h" />
<ClInclude Include="Sync.h" />
<ClInclude Include="TFTSurenoo.h" />
<ClInclude Include="Thread.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="UDPController.h" />
@ -273,20 +257,16 @@
<ClCompile Include="AMBEFEC.cpp" />
<ClCompile Include="BCH.cpp" />
<ClCompile Include="BPTC19696.cpp" />
<ClCompile Include="CASTInfo.cpp" />
<ClCompile Include="Conf.cpp" />
<ClCompile Include="CRC.cpp" />
<ClCompile Include="Display.cpp" />
<ClCompile Include="DMRAccessControl.cpp" />
<ClCompile Include="DMRControl.cpp" />
<ClCompile Include="DMRCSBK.cpp" />
<ClCompile Include="DMRData.cpp" />
<ClCompile Include="DMRDataHeader.cpp" />
<ClCompile Include="DMRDirectNetwork.cpp" />
<ClCompile Include="DMREMB.cpp" />
<ClCompile Include="DMREmbeddedData.cpp" />
<ClCompile Include="DMRFullLC.cpp" />
<ClCompile Include="DMRGatewayNetwork.cpp" />
<ClCompile Include="DMRLC.cpp" />
<ClCompile Include="DMRLookup.cpp" />
<ClCompile Include="DMRNetwork.cpp" />
@ -305,16 +285,12 @@
<ClCompile Include="Golay24128.cpp" />
<ClCompile Include="Hamming.cpp" />
<ClCompile Include="IIRDirectForm1Filter.cpp" />
<ClCompile Include="LCDproc.cpp" />
<ClCompile Include="Log.cpp" />
<ClCompile Include="MMDVMHost.cpp" />
<ClCompile Include="ModemPort.cpp" />
<ClCompile Include="ModemSerialPort.cpp" />
<ClCompile Include="MQTTConnection.cpp" />
<ClCompile Include="Mutex.cpp" />
<ClCompile Include="NetworkInfo.cpp" />
<ClCompile Include="Nextion.cpp" />
<ClCompile Include="NullController.cpp" />
<ClCompile Include="NullDisplay.cpp" />
<ClCompile Include="NXDNAudio.cpp" />
<ClCompile Include="NXDNControl.cpp" />
<ClCompile Include="NXDNConvolution.cpp" />
@ -338,7 +314,6 @@
<ClCompile Include="P25Utils.cpp" />
<ClCompile Include="POCSAGControl.cpp" />
<ClCompile Include="POCSAGNetwork.cpp" />
<ClCompile Include="PseudoTTYController.cpp" />
<ClCompile Include="QR1676.cpp" />
<ClCompile Include="RemoteControl.cpp" />
<ClCompile Include="RS129.cpp" />
@ -347,11 +322,8 @@
<ClCompile Include="UARTController.cpp" />
<ClCompile Include="Modem.cpp" />
<ClCompile Include="SerialPort.cpp" />
<ClCompile Include="SHA256.cpp" />
<ClCompile Include="SMeter.cpp" />
<ClCompile Include="StopWatch.cpp" />
<ClCompile Include="Sync.cpp" />
<ClCompile Include="TFTSurenoo.cpp" />
<ClCompile Include="Thread.cpp" />
<ClCompile Include="Timer.cpp" />
<ClCompile Include="UDPController.cpp" />

View file

@ -23,9 +23,6 @@
<ClInclude Include="Defines.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Display.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRControl.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -56,9 +53,6 @@
<ClInclude Include="MMDVMHost.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NullDisplay.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="QR1676.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -143,9 +137,6 @@
<ClInclude Include="YSFPayload.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Nextion.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRLookup.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -194,21 +185,12 @@
<ClInclude Include="SerialPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ModemSerialPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Mutex.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LCDproc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RSSIInterpolator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="NetworkInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="P25Trellis.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -260,12 +242,6 @@
<ClInclude Include="DMRTA.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CASTInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="TFTSurenoo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UserDB.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -287,18 +263,6 @@
<ClInclude Include="IIRDirectForm1Filter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PseudoTTYController.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRDirectNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DMRGatewayNetwork.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SHA256.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UARTController.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -314,7 +278,7 @@
<ClInclude Include="ModemPort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SMeter.h">
<ClInclude Include="MQTTConnection.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RS634717.h">
@ -334,9 +298,6 @@
<ClCompile Include="CRC.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Display.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRControl.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -361,9 +322,6 @@
<ClCompile Include="MMDVMHost.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NullDisplay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="QR1676.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -439,9 +397,6 @@
<ClCompile Include="YSFPayload.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Nextion.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRLookup.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -487,21 +442,12 @@
<ClCompile Include="SerialPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ModemSerialPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Mutex.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LCDproc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RSSIInterpolator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="NetworkInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="P25Trellis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -547,12 +493,6 @@
<ClCompile Include="DMRTA.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CASTInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="TFTSurenoo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UserDB.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -574,18 +514,6 @@
<ClCompile Include="IIRDirectForm1Filter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PseudoTTYController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRDirectNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DMRGatewayNetwork.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SHA256.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UARTController.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -601,7 +529,7 @@
<ClCompile Include="ModemPort.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SMeter.cpp">
<ClCompile Include="MQTTConnection.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RS634717.cpp">

223
MQTTConnection.cpp Normal file
View file

@ -0,0 +1,223 @@
/*
* Copyright (C) 2022,2023,2025 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 "MQTTConnection.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <ctime>
CMQTTConnection::CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>& subs, unsigned int keepalive, MQTT_QOS qos) :
m_host(host),
m_port(port),
m_name(name),
m_authEnabled(authEnabled),
m_username(username),
m_password(password),
m_subs(subs),
m_keepalive(keepalive),
m_qos(qos),
m_mosq(nullptr),
m_connected(false)
{
assert(!host.empty());
assert(port > 0U);
assert(!name.empty());
assert(keepalive >= 5U);
::mosquitto_lib_init();
}
CMQTTConnection::~CMQTTConnection()
{
::mosquitto_lib_cleanup();
}
bool CMQTTConnection::open()
{
char name[50U];
::sprintf(name, "MMDVMHost.%ld", ::time(nullptr));
::fprintf(stdout, "MMDVMHost (%s) connecting to MQTT as %s\n", m_name.c_str(), name);
m_mosq = ::mosquitto_new(name, true, this);
if (m_mosq == nullptr) {
::fprintf(stderr, "MQTT Error newing: Out of memory.\n");
return false;
}
if (m_authEnabled)
::mosquitto_username_pw_set(m_mosq, m_username.c_str(), m_password.c_str());
::mosquitto_connect_callback_set(m_mosq, onConnect);
::mosquitto_subscribe_callback_set(m_mosq, onSubscribe);
::mosquitto_message_callback_set(m_mosq, onMessage);
::mosquitto_disconnect_callback_set(m_mosq, onDisconnect);
int rc = ::mosquitto_connect(m_mosq, m_host.c_str(), m_port, m_keepalive);
if (rc != MOSQ_ERR_SUCCESS) {
::mosquitto_destroy(m_mosq);
m_mosq = nullptr;
::fprintf(stderr, "MQTT Error connecting: %s\n", ::mosquitto_strerror(rc));
return false;
}
rc = ::mosquitto_loop_start(m_mosq);
if (rc != MOSQ_ERR_SUCCESS) {
::mosquitto_disconnect(m_mosq);
::mosquitto_destroy(m_mosq);
m_mosq = nullptr;
::fprintf(stderr, "MQTT Error loop starting: %s\n", ::mosquitto_strerror(rc));
return false;
}
return true;
}
bool CMQTTConnection::publish(const char* topic, const char* text)
{
assert(topic != nullptr);
assert(text != nullptr);
return publish(topic, (unsigned char*)text, (unsigned int)::strlen(text));
}
bool CMQTTConnection::publish(const char* topic, const std::string& text)
{
assert(topic != nullptr);
return publish(topic, (unsigned char*)text.c_str(), (unsigned int)text.size());
}
bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len)
{
assert(topic != nullptr);
assert(data != nullptr);
if (!m_connected)
return false;
if (::strchr(topic, '/') == nullptr) {
char topicEx[100U];
::sprintf(topicEx, "%s/%s", m_name.c_str(), topic);
int rc = ::mosquitto_publish(m_mosq, nullptr, topicEx, len, data, static_cast<int>(m_qos), false);
if (rc != MOSQ_ERR_SUCCESS) {
::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc));
return false;
}
} else {
int rc = ::mosquitto_publish(m_mosq, nullptr, topic, len, data, static_cast<int>(m_qos), false);
if (rc != MOSQ_ERR_SUCCESS) {
::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc));
return false;
}
}
return true;
}
void CMQTTConnection::close()
{
if (m_mosq != nullptr) {
::mosquitto_disconnect(m_mosq);
::mosquitto_destroy(m_mosq);
m_mosq = nullptr;
}
}
void CMQTTConnection::onConnect(mosquitto* mosq, void* obj, int rc)
{
assert(mosq != nullptr);
assert(obj != nullptr);
::fprintf(stdout, "MQTT: on_connect: %s\n", ::mosquitto_connack_string(rc));
if (rc != 0) {
::mosquitto_disconnect(mosq);
return;
}
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
p->m_connected = true;
for (std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) {
std::string topic = (*it).first;
if (topic.find_first_of('/') == std::string::npos) {
char topicEx[100U];
::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str());
rc = ::mosquitto_subscribe(mosq, nullptr, topicEx, static_cast<int>(p->m_qos));
if (rc != MOSQ_ERR_SUCCESS) {
::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topicEx, ::mosquitto_strerror(rc));
::mosquitto_disconnect(mosq);
}
} else {
rc = ::mosquitto_subscribe(mosq, nullptr, topic.c_str(), static_cast<int>(p->m_qos));
if (rc != MOSQ_ERR_SUCCESS) {
::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topic.c_str(), ::mosquitto_strerror(rc));
::mosquitto_disconnect(mosq);
}
}
}
}
void CMQTTConnection::onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS)
{
assert(mosq != nullptr);
assert(obj != nullptr);
assert(grantedQOS != nullptr);
for (int i = 0; i < qosCount; i++)
::fprintf(stdout, "MQTT: on_subscribe: %d:%d\n", i, grantedQOS[i]);
}
void CMQTTConnection::onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message)
{
assert(mosq != nullptr);
assert(obj != nullptr);
assert(message != nullptr);
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
for (std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) {
std::string topic = (*it).first;
char topicEx[100U];
::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str());
if (::strcmp(topicEx, message->topic) == 0) {
(*it).second((unsigned char*)message->payload, message->payloadlen);
break;
}
}
}
void CMQTTConnection::onDisconnect(mosquitto* mosq, void* obj, int rc)
{
assert(mosq != nullptr);
assert(obj != nullptr);
::fprintf(stdout, "MQTT: on_disconnect: %s\n", ::mosquitto_reason_string(rc));
CMQTTConnection* p = static_cast<CMQTTConnection*>(obj);
p->m_connected = false;
}

66
MQTTConnection.h Normal file
View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2022,2023,2025 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(MQTTPUBLISHER_H)
#define MQTTPUBLISHER_H
#include <mosquitto.h>
#include <vector>
#include <string>
enum class MQTT_QOS : int {
AT_MODE_ONCE = 0,
AT_LEAST_ONCE = 1,
EXACTLY_ONCE = 2
};
class CMQTTConnection {
public:
CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>>& subs, unsigned int keepalive, MQTT_QOS qos = MQTT_QOS::EXACTLY_ONCE);
~CMQTTConnection();
bool open();
bool publish(const char* topic, const char* text);
bool publish(const char* topic, const std::string& text);
bool publish(const char* topic, const unsigned char* data, unsigned int len);
void close();
private:
std::string m_host;
unsigned short m_port;
std::string m_name;
bool m_authEnabled;
std::string m_username;
std::string m_password;
std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>> m_subs;
unsigned int m_keepalive;
MQTT_QOS m_qos;
mosquitto* m_mosq;
bool m_connected;
static void onConnect(mosquitto* mosq, void* obj, int rc);
static void onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS);
static void onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message);
static void onDisconnect(mosquitto* mosq, void* obj, int rc);
};
#endif

View file

@ -1,39 +1,32 @@
# This makefile is for all platforms, but doesn't include support for the HD44780, OLED, or PCF8574 displays on the Raspberry Pi.
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
# This makefile is for all platforms.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -I/usr/local/include
LIBS = -lpthread -lutil
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -I/usr/local/include
LIBS = -lpthread -lutil -lsamplerate -lmosquitto
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \
Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \
NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \
Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o DMRAccessControl.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o \
DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o \
DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o Log.o \
MMDVMHost.o MQTTConnection.o Modem.o ModemPort.o Mutex.o NullController.o NXDNAudio.o \
NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o StopWatch.o Sync.o Thread.o Timer.o UARTController.o \
UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
all: MMDVMHost
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(LDFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(LDFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@ -58,7 +51,7 @@ uninstall-service:
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
$(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:

View file

@ -1,71 +0,0 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
# Support for the Adafruit i2c 16 x 2 RGB LCD Pi Plate
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DADAFRUIT_DISPLAY -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \
MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \
NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \
POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \
TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \
YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@useradd --user-group -M --system mmdvm --shell /bin/false || true
@usermod --groups dialout --append mmdvm || true
@mkdir /var/log/mmdvm || true
@chown mmdvm:mmdvm /var/log/mmdvm
@cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/
@systemctl enable mmdvmhost.service
/etc/MMDVM.ini:
@cp -n MMDVM.ini /etc/MMDVM.ini
@sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini
@sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini
@chown mmdvm:mmdvm /etc/MMDVM.ini
.PHONY uninstall-service:
uninstall-service:
@systemctl stop mmdvmhost.service || true
@systemctl disable mmdvmhost.service || true
@rm -f /usr/local/bin/MMDVMHost || true
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

View file

@ -1,70 +0,0 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \
MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \
NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \
POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \
TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \
YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@useradd --user-group -M --system mmdvm --shell /bin/false || true
@usermod --groups dialout --append mmdvm || true
@mkdir /var/log/mmdvm || true
@chown mmdvm:mmdvm /var/log/mmdvm
@cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/
@systemctl enable mmdvmhost.service
/etc/MMDVM.ini:
@cp -n MMDVM.ini /etc/MMDVM.ini
@sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini
@sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini
@chown mmdvm:mmdvm /etc/MMDVM.ini
.PHONY uninstall-service:
uninstall-service:
@systemctl stop mmdvmhost.service || true
@systemctl disable mmdvmhost.service || true
@rm -f /usr/local/bin/MMDVMHost || true
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

View file

@ -1,69 +0,0 @@
# This makefile is for use with the Raspberry Pi. The wiringpi library is needed.
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DRASPBERRY_PI -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \
Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \
NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o \
POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \
Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@useradd --user-group -M --system mmdvm --shell /bin/false || true
@usermod --groups dialout --append mmdvm || true
@mkdir /var/log/mmdvm || true
@chown mmdvm:mmdvm /var/log/mmdvm
@cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/
@systemctl enable mmdvmhost.service
/etc/MMDVM.ini:
@cp -n MMDVM.ini /etc/MMDVM.ini
@sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini
@sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini
@chown mmdvm:mmdvm /etc/MMDVM.ini
.PHONY uninstall-service:
uninstall-service:
@systemctl stop mmdvmhost.service || true
@systemctl disable mmdvmhost.service || true
@rm -f /usr/local/bin/MMDVMHost || true
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

View file

@ -1,75 +0,0 @@
# This makefile is for use with the Raspberry Pi when using an OLED display. The wiringpi library is not needed.
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DOLED -I/usr/local/include
LIBS = -lArduiPi_OLED -lpthread -lutil
# If you use NetBSD, add following CFLAGS
#CFLAGS += -L/usr/local/lib -Wl,-rpath=/usr/local/lib
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \
Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \
NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \
NXDNUDCH.o OLED.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \
POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \
TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \
YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@useradd --user-group -M --system mmdvm --shell /bin/false || true
@usermod --groups dialout --append mmdvm || true
@mkdir /var/log/mmdvm || true
@chown mmdvm:mmdvm /var/log/mmdvm
@cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/
@systemctl enable mmdvmhost.service
/etc/MMDVM.ini:
@cp -n MMDVM.ini /etc/MMDVM.ini
@sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini
@sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini
@chown mmdvm:mmdvm /etc/MMDVM.ini
.PHONY uninstall-service:
uninstall-service:
@systemctl stop mmdvmhost.service || true
@systemctl disable mmdvmhost.service || true
@rm -f /usr/local/bin/MMDVMHost || true
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

View file

@ -1,71 +0,0 @@
# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed.
# Support for the HD44780 connected via a PCF8574 8-bit GPIO expander IC
# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line.
CC = cc
CXX = c++
CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DPCF8574_DISPLAY -I/usr/local/include
LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil
LDFLAGS = -g -L/usr/local/lib
OBJECTS = \
AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \
DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \
DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \
Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \
MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \
NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \
POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \
TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \
YSFNetwork.o YSFPayload.o
all: MMDVMHost RemoteCommand
MMDVMHost: GitVersion.h $(OBJECTS)
$(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost
RemoteCommand: Log.o RemoteCommand.o UDPSocket.o
$(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand
%.o: %.cpp
$(CXX) $(CFLAGS) -c -o $@ $<
.PHONY install:
install: all
install -m 755 MMDVMHost /usr/local/bin/
install -m 755 RemoteCommand /usr/local/bin/
.PHONY install-service:
install-service: install /etc/MMDVM.ini
@useradd --user-group -M --system mmdvm --shell /bin/false || true
@usermod --groups dialout --append mmdvm || true
@mkdir /var/log/mmdvm || true
@chown mmdvm:mmdvm /var/log/mmdvm
@cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/
@systemctl enable mmdvmhost.service
/etc/MMDVM.ini:
@cp -n MMDVM.ini /etc/MMDVM.ini
@sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini
@sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini
@chown mmdvm:mmdvm /etc/MMDVM.ini
.PHONY uninstall-service:
uninstall-service:
@systemctl stop mmdvmhost.service || true
@systemctl disable mmdvmhost.service || true
@rm -f /usr/local/bin/MMDVMHost || true
@rm -f /lib/systemd/system/mmdvmhost.service || true
clean:
$(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h
# Export the current git version if the index file exists, else 000...
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

511
Modem.cpp

File diff suppressed because it is too large Load diff

148
Modem.h
View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2018,2020,2021,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2011-2018,2020,2021,2023,2025 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
@ -49,16 +49,27 @@ public:
void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency);
void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled);
void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel);
#if defined(USE_DMR)
void setDMRParams(unsigned int colorCode);
#endif
#if defined(USE_YSF)
void setYSFParams(bool loDev, unsigned int txHang);
#endif
#if defined(USE_P25)
void setP25Params(unsigned int txHang);
#endif
#if defined(USE_NXDN)
void setNXDNParams(unsigned int txHang);
#endif
void setTransparentDataParams(unsigned int sendFrameType);
#if defined(USE_FM)
void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch);
void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel);
void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool linkMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel);
void setFMExtParams(const std::string& ack, unsigned int audioBoost);
#endif
bool open();
@ -72,22 +83,48 @@ public:
unsigned int getVersion() const;
#if defined(USE_DSTAR)
unsigned int readDStarData(unsigned char* data);
#endif
#if defined(USE_DMR)
unsigned int readDMRData1(unsigned char* data);
unsigned int readDMRData2(unsigned char* data);
#endif
#if defined(USE_YSF)
unsigned int readYSFData(unsigned char* data);
#endif
#if defined(USE_P25)
unsigned int readP25Data(unsigned char* data);
#endif
#if defined(USE_NXDN)
unsigned int readNXDNData(unsigned char* data);
#endif
#if defined(USE_FM)
unsigned int readFMData(unsigned char* data);
#endif
#if defined(USE_DSTAR)
bool hasDStarSpace() const;
#endif
#if defined(USE_DMR)
bool hasDMRSpace1() const;
bool hasDMRSpace2() const;
#endif
#if defined(USE_YSF)
bool hasYSFSpace() const;
#endif
#if defined(USE_P25)
bool hasP25Space() const;
#endif
#if defined(USE_NXDN)
bool hasNXDNSpace() const;
#endif
#if defined(USE_POCSAG)
bool hasPOCSAGSpace() const;
#endif
#if defined(USE_FM)
unsigned int getFMSpace() const;
#endif
bool hasTX() const;
bool hasCD() const;
@ -96,32 +133,60 @@ public:
bool hasError() const;
bool writeConfig();
#if defined(USE_DSTAR)
bool writeDStarData(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_DMR)
bool writeDMRData1(const unsigned char* data, unsigned int length);
bool writeDMRData2(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_YSF)
bool writeYSFData(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_P25)
bool writeP25Data(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_NXDN)
bool writeNXDNData(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_POCSAG)
bool writePOCSAGData(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_FM)
bool writeFMData(const unsigned char* data, unsigned int length);
#endif
#if defined(USE_DSTAR)
bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector);
#endif
#if defined(USE_DMR)
bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type);
#endif
#if defined(USE_YSF)
bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin);
#endif
#if defined(USE_P25)
bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type);
#endif
#if defined(USE_NXDN)
bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type);
#endif
#if defined(USE_POCSAG)
bool writePOCSAGInfo(unsigned int ric, const std::string& message);
#endif
bool writeIPInfo(const std::string& address);
#if defined(USE_DMR)
bool writeDMRStart(bool tx);
bool writeDMRShortLC(const unsigned char* lc);
bool writeDMRAbort(unsigned int slotNo);
#endif
bool writeTransparentData(const unsigned char* data, unsigned int length);
unsigned int readTransparentData(unsigned char* data);
bool writeSerial(const unsigned char* data, unsigned int length);
unsigned int readSerial(unsigned char* data, unsigned int length);
bool writeSerialData(const unsigned char* data, unsigned int length);
unsigned int readSerialData(unsigned char* data);
unsigned char getMode() const;
bool setMode(unsigned char mode);
@ -136,40 +201,80 @@ public:
private:
unsigned int m_protocolVersion;
#if defined(USE_DMR)
unsigned int m_dmrColorCode;
#endif
#if defined(USE_YSF)
bool m_ysfLoDev;
unsigned int m_ysfTXHang;
#endif
#if defined(USE_P25)
unsigned int m_p25TXHang;
#endif
#if defined(USE_NXDN)
unsigned int m_nxdnTXHang;
#endif
bool m_duplex;
bool m_rxInvert;
bool m_txInvert;
bool m_pttInvert;
unsigned int m_txDelay;
#if defined(USE_DMR)
unsigned int m_dmrDelay;
#endif
float m_rxLevel;
float m_cwIdTXLevel;
#if defined(USE_DSTAR)
float m_dstarTXLevel;
#endif
#if defined(USE_DMR)
float m_dmrTXLevel;
#endif
#if defined(USE_YSF)
float m_ysfTXLevel;
#endif
#if defined(USE_P25)
float m_p25TXLevel;
#endif
#if defined(USE_NXDN)
float m_nxdnTXLevel;
#endif
#if defined(USE_POCSAG)
float m_pocsagTXLevel;
#endif
#if defined(USE_FM)
float m_fmTXLevel;
#endif
float m_rfLevel;
bool m_useCOSAsLockout;
bool m_trace;
bool m_debug;
unsigned int m_rxFrequency;
unsigned int m_txFrequency;
#if defined(USE_POCSAG)
unsigned int m_pocsagFrequency;
#endif
#if defined(USE_DSTAR)
bool m_dstarEnabled;
#endif
#if defined(USE_DMR)
bool m_dmrEnabled;
#endif
#if defined(USE_YSF)
bool m_ysfEnabled;
#endif
#if defined(USE_P25)
bool m_p25Enabled;
#endif
#if defined(USE_NXDN)
bool m_nxdnEnabled;
#endif
#if defined(USE_POCSAG)
bool m_pocsagEnabled;
#endif
#if defined(USE_FM)
bool m_fmEnabled;
#endif
int m_rxDCOffset;
int m_txDCOffset;
IModemPort* m_port;
@ -178,21 +283,36 @@ private:
unsigned int m_offset;
SERIAL_STATE m_state;
unsigned char m_type;
#if defined(USE_DSTAR)
CRingBuffer<unsigned char> m_rxDStarData;
CRingBuffer<unsigned char> m_txDStarData;
#endif
#if defined(USE_DMR)
CRingBuffer<unsigned char> m_rxDMRData1;
CRingBuffer<unsigned char> m_rxDMRData2;
CRingBuffer<unsigned char> m_txDMRData1;
CRingBuffer<unsigned char> m_txDMRData2;
#endif
#if defined(USE_YSF)
CRingBuffer<unsigned char> m_rxYSFData;
CRingBuffer<unsigned char> m_txYSFData;
#endif
#if defined(USE_P25)
CRingBuffer<unsigned char> m_rxP25Data;
CRingBuffer<unsigned char> m_txP25Data;
#endif
#if defined(USE_NXDN)
CRingBuffer<unsigned char> m_rxNXDNData;
CRingBuffer<unsigned char> m_txNXDNData;
#endif
#if defined(USE_POCSAG)
CRingBuffer<unsigned char> m_txPOCSAGData;
#endif
#if defined(USE_FM)
CRingBuffer<unsigned char> m_rxFMData;
CRingBuffer<unsigned char> m_txFMData;
#endif
CRingBuffer<unsigned char> m_rxSerialData;
CRingBuffer<unsigned char> m_txSerialData;
CRingBuffer<unsigned char> m_rxTransparentData;
@ -201,21 +321,35 @@ private:
CTimer m_statusTimer;
CTimer m_inactivityTimer;
CTimer m_playoutTimer;
#if defined(USE_DSTAR)
unsigned int m_dstarSpace;
#endif
#if defined(USE_DMR)
unsigned int m_dmrSpace1;
unsigned int m_dmrSpace2;
#endif
#if defined(USE_YSF)
unsigned int m_ysfSpace;
#endif
#if defined(USE_P25)
unsigned int m_p25Space;
#endif
#if defined(USE_NXDN)
unsigned int m_nxdnSpace;
#endif
#if defined(USE_POCSAG)
unsigned int m_pocsagSpace;
#endif
#if defined(USE_FM)
unsigned int m_fmSpace;
#endif
bool m_tx;
bool m_cd;
bool m_lockout;
bool m_error;
unsigned char m_mode;
HW_TYPE m_hwType;
#if defined(USE_FM)
std::string m_fmCallsign;
unsigned int m_fmCallsignSpeed;
unsigned int m_fmCallsignFrequency;
@ -251,6 +385,7 @@ private:
unsigned int m_fmExtAudioBoost;
float m_fmMaxDevLevel;
bool m_fmExtEnable;
#endif
unsigned char m_capabilities1;
unsigned char m_capabilities2;
@ -259,11 +394,12 @@ private:
bool setConfig1();
bool setConfig2();
bool setFrequency();
#if defined(USE_FM)
bool setFMCallsignParams();
bool setFMAckParams();
bool setFMMiscParams();
bool setFMExtParams();
#endif
void printDebug();
RESP_TYPE_MMDVM getResponse();

View file

@ -1,59 +0,0 @@
/*
* Copyright (C) 2016,2020,2021,2025 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 "ModemSerialPort.h"
#include <cstdio>
#include <cassert>
IModemSerialPort::IModemSerialPort(CModem* modem) :
m_modem(modem)
{
assert(modem != nullptr);
}
IModemSerialPort::~IModemSerialPort()
{
}
bool IModemSerialPort::open()
{
return true;
}
int IModemSerialPort::write(const unsigned char* data, unsigned int length)
{
assert(data != nullptr);
assert(length > 0U);
bool ret = m_modem->writeSerial(data, length);
return ret ? int(length) : -1;
}
int IModemSerialPort::read(unsigned char* data, unsigned int length)
{
assert(data != nullptr);
assert(length > 0U);
return m_modem->readSerial(data, length);
}
void IModemSerialPort::close()
{
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (C) 2016,2020,2021 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 ModemSerialPort_H
#define ModemSerialPort_H
#include "SerialPort.h"
#include "Modem.h"
class IModemSerialPort : public ISerialPort {
public:
IModemSerialPort(CModem* modem);
virtual ~IModemSerialPort();
virtual bool open();
virtual int read(unsigned char* buffer, unsigned int length);
virtual int write(const unsigned char* buffer, unsigned int length);
virtual void close();
private:
CModem* m_modem;
};
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX
* Copyright (C) 2018,2023,2025 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
@ -19,6 +19,8 @@
#include "NXDNAudio.h"
#include "Golay24128.h"
#if defined(USE_NXDN)
#include <cstdio>
#include <cassert>
#include <cstring>
@ -575,3 +577,6 @@ void CNXDNAudio::encode(const unsigned char* in, unsigned char* out, unsigned in
WRITE_BIT(out, cPos, cOrig & MASK);
}
}
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018,2023 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
@ -19,6 +19,10 @@
#if !defined(NXDNAudio_H)
#define NXDNAudio_H
#include "Defines.h"
#if defined(USE_NXDN)
class CNXDNAudio {
public:
CNXDNAudio();
@ -35,3 +39,6 @@ private:
};
#endif
#endif

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