implement initial NXDN support (EXPERIMENTAL);

usb-support
Bryan Biedenkapp 4 years ago
parent cc2b47de4f
commit 7e9f4079f3

@ -70,11 +70,14 @@ uint16_t TX_F_Divider; // Tx - 15-bit Frational_N
uint16_t dmrDev; uint16_t dmrDev;
uint16_t p25Dev; uint16_t p25Dev;
uint16_t nxdnDev;
int8_t m_dmrDiscBWAdj; int8_t m_dmrDiscBWAdj;
int8_t m_p25DiscBWAdj; int8_t m_p25DiscBWAdj;
int8_t m_nxdnDiscBWAdj;
int8_t m_dmrPostBWAdj; int8_t m_dmrPostBWAdj;
int8_t m_p25PostBWAdj; int8_t m_p25PostBWAdj;
int8_t m_nxdnPostBWAdj;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Global Functions // Global Functions
@ -315,6 +318,9 @@ void IO::rf1Conf(DVM_STATE modemState, bool reset)
case STATE_P25: case STATE_P25:
AFC_OFFSET = AFC_OFFSET_P25; AFC_OFFSET = AFC_OFFSET_P25;
break; break;
case STATE_NXDN:
AFC_OFFSET = AFC_OFFSET_NXDN;
break;
default: default:
break; break;
} }
@ -614,10 +620,12 @@ void IO::rf2Conf(DVM_STATE modemState)
/// </summary> /// </summary>
/// <param name="dmrTXLevel"></param> /// <param name="dmrTXLevel"></param>
/// <param name="p25TXLevel"></param> /// <param name="p25TXLevel"></param>
void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel) /// <param name="nxdnTXLevel"></param>
void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel)
{ {
dmrDev = uint16_t((ADF7021_DEV_DMR * uint16_t(dmrTXLevel)) / 128U); dmrDev = uint16_t((ADF7021_DEV_DMR * uint16_t(dmrTXLevel)) / 128U);
p25Dev = uint16_t((ADF7021_DEV_P25 * uint16_t(p25TXLevel)) / 128U); p25Dev = uint16_t((ADF7021_DEV_P25 * uint16_t(p25TXLevel)) / 128U);
nxdnDev = uint16_t((ADF7021_DEV_NXDN * uint16_t(nxdnTXLevel)) / 128U);
} }
/// <summary> /// <summary>
@ -627,14 +635,18 @@ void IO::setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel)
/// <param name="p25DevAdj"></param> /// <param name="p25DevAdj"></param>
/// <param name="dmrDiscBWAdj"></param> /// <param name="dmrDiscBWAdj"></param>
/// <param name="p25DiscBWAdj"></param> /// <param name="p25DiscBWAdj"></param>
/// <param name="nxdnDiscBWAdj"></param>
/// <param name="dmrPostBWAdj"></param> /// <param name="dmrPostBWAdj"></param>
/// <param name="p25PostBWAdj"></param> /// <param name="p25PostBWAdj"></param>
void IO::setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj) /// <param name="nxdnPostBWAdj"></param>
void IO::setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWADJ)
{ {
m_dmrDiscBWAdj = dmrDiscBWAdj; m_dmrDiscBWAdj = dmrDiscBWAdj;
m_p25DiscBWAdj = p25DiscBWAdj; m_p25DiscBWAdj = p25DiscBWAdj;
m_nxdnDiscBWAdj = nxdnDiscBWAdj;
m_dmrPostBWAdj = dmrPostBWAdj; m_dmrPostBWAdj = dmrPostBWAdj;
m_p25PostBWAdj = p25PostBWAdj; m_p25PostBWAdj = p25PostBWAdj;
m_nxdnPostBWAdj = nxdnPostBWADJ;
} }
/// <summary> /// <summary>
@ -872,6 +884,7 @@ void IO::configureTxRx(DVM_STATE modemState)
{ {
uint16_t dmrDiscBW = ADF7021_DISC_BW_DMR, dmrPostBW = ADF7021_POST_BW_DMR; uint16_t dmrDiscBW = ADF7021_DISC_BW_DMR, dmrPostBW = ADF7021_POST_BW_DMR;
uint16_t p25DiscBW = ADF7021_DISC_BW_P25, p25PostBW = ADF7021_POST_BW_P25; uint16_t p25DiscBW = ADF7021_DISC_BW_P25, p25PostBW = ADF7021_POST_BW_P25;
uint16_t nxdnDiscBW = ADF7021_DISC_BW_NXDN, nxdnPostBW = ADF7021_POST_BW_NXDN;
// configure DMR discriminator and post demodulator BW // configure DMR discriminator and post demodulator BW
if (dmrDiscBW + m_dmrDiscBWAdj < 0U) if (dmrDiscBW + m_dmrDiscBWAdj < 0U)
@ -903,6 +916,21 @@ void IO::configureTxRx(DVM_STATE modemState)
if (p25PostBW > ADF7021_POST_BW_MAX) if (p25PostBW > ADF7021_POST_BW_MAX)
p25PostBW = ADF7021_POST_BW_MAX; p25PostBW = ADF7021_POST_BW_MAX;
// configure NXDN discriminator and post demodulator BW
if (nxdnDiscBW + m_nxdnDiscBWAdj < 0U)
nxdnDiscBW = 0U;
else
nxdnDiscBW = ADF7021_DISC_BW_NXDN + m_nxdnDiscBWAdj;
if (nxdnDiscBW > ADF7021_DISC_BW_MAX)
nxdnDiscBW = ADF7021_DISC_BW_MAX;
if (nxdnPostBW + m_nxdnPostBWAdj < 0)
nxdnPostBW = 0U;
else
nxdnPostBW = ADF7021_POST_BW_NXDN + m_nxdnPostBWAdj;
if (nxdnPostBW > ADF7021_POST_BW_MAX)
nxdnPostBW = ADF7021_POST_BW_MAX;
/* /*
** Configure the remaining registers based on modem state. ** Configure the remaining registers based on modem state.
*/ */
@ -1171,6 +1199,93 @@ void IO::configureTxRx(DVM_STATE modemState)
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_RC_5 << 30; // R-Cosine Alpha ADF7021_REG2 |= (uint32_t)ADF7021_REG2_RC_5 << 30; // R-Cosine Alpha
} }
break; break;
case STATE_NXDN: // 4FSK
{
// Dev: +1 symb 350 Hz, symb rate = 2400
/*
** Tx/Rx Clock (Register 3)
*/
/** Support for 14.7456 MHz TCXO (modified RF7021SE boards) */
#if defined(ADF7021_14_7456)
ADF7021_REG3 = (uint32_t)ADF7021_REG3_ADDR; // Register Address 3
ADF7021_REG3 |= (uint32_t)ADF7021_REG3_BBOS_DIV_8 << 4; // Base Band Clock Divider
ADF7021_REG3 |= (uint32_t)(3 & 0xFU) << 6; // Demodulator Clock Divider
ADF7021_REG3 |= (uint32_t)(32 & 0xFFU) << 10; // Data/Clock Recovery Divider (CDR)
ADF7021_REG3 |= (uint32_t)(147 & 0xFFU) << 18; // Sequencer Clock Divider
ADF7021_REG3 |= (uint32_t)(10 & 0x3FU) << 26; // AGC Clock Divider
/** Support for 12.2880 MHz TCXO */
#elif defined(ADF7021_12_2880)
ADF7021_REG3 = (uint32_t)ADF7021_REG3_ADDR; // Register Address 3
ADF7021_REG3 |= (uint32_t)ADF7021_REG3_BBOS_DIV_8 << 4; // Base Band Clock Divider
ADF7021_REG3 |= (uint32_t)(2 & 0xFU) << 6; // Demodulator Clock Divider
ADF7021_REG3 |= (uint32_t)(40 & 0xFFU) << 10; // Data/Clock Recovery Divider (CDR)
ADF7021_REG3 |= (uint32_t)(123 & 0xFFU) << 18; // Sequencer Clock Divider
ADF7021_REG3 |= (uint32_t)(10 & 0x3FU) << 26; // AGC Clock Divider
#endif
/*
** AFC (Register 10)
*/
/** Support for 14.7456 MHz TCXO (modified RF7021SE boards) */
#if defined(ADF7021_14_7456)
ADF7021_REG10 = (uint32_t)ADF7021_REG10_ADDR; // Register Address 10
#if defined(ADF7021_ENABLE_4FSK_AFC)
ADF7021_REG10 |= (uint32_t)ADF7021_REG10_AFC_ENABLE << 4; // AFC Enable/Disable
#else
ADF7021_REG10 |= (uint32_t)ADF7021_REG10_AFC_DISABLE << 4; // AFC Enable/Disable
#endif
ADF7021_REG10 |= (uint32_t)(569 & 0xFFFU) << 5; // AFC Scaling Factor
ADF7021_REG10 |= (uint32_t)(15 & 0xFU) << 17; // KI
ADF7021_REG10 |= (uint32_t)(4 & 0x7U) << 21; // KP
ADF7021_REG10 |= (uint32_t)(4 & 0xFFU) << 24; // Maximum AFC Range
/** Support for 12.2880 MHz TCXO */
#elif defined(ADF7021_12_2880)
ADF7021_REG10 = (uint32_t)ADF7021_REG10_ADDR; // Register Address 10
#if defined(ADF7021_ENABLE_4FSK_AFC)
ADF7021_REG10 |= (uint32_t)ADF7021_REG10_AFC_ENABLE << 4; // AFC Enable/Disable
#else
ADF7021_REG10 |= (uint32_t)ADF7021_REG10_AFC_DISABLE << 4; // AFC Enable/Disable
#endif
ADF7021_REG10 |= (uint32_t)(683 & 0xFFFU) << 5; // AFC Scaling Factor
ADF7021_REG10 |= (uint32_t)(15 & 0xFU) << 17; // KI
ADF7021_REG10 |= (uint32_t)(4 & 0x7U) << 21; // KP
ADF7021_REG10 |= (uint32_t)(4 & 0xFFU) << 24; // Maximum AFC Range
#endif
/*
** Demodulator Setup (Register 4)
*/
// K=32
ADF7021_REG4 = (uint32_t)ADF7021_REG4_ADDR; // Register Address 4
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_MODE_4FSK << 4; // Demodulation Scheme
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_CROSS_PROD << 7; // Dot Product
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_INV_CLKDAT << 8; // Clock/Data Inversion
ADF7021_REG4 |= (uint32_t)(nxdnDiscBW & 0x3FFU) << 10; // Discriminator BW
ADF7021_REG4 |= (uint32_t)(nxdnPostBW & 0xFFFU) << 20; // Post Demod BW
ADF7021_REG4 |= (uint32_t)ADF7021_REG4_IF_125K << 30; // IF Filter
/*
** 3FSK/4FSK Demod (Register 13)
*/
ADF7021_REG13 = (uint32_t)ADF70210_REG13_ADDR; // Register Address 13
ADF7021_REG13 |= (uint32_t)ADF7021_SLICER_TH_NXDN << 4; // Slicer Threshold
/*
** Transmit Modulation (Register 2)
*/
ADF7021_REG2 = (uint32_t)ADF7021_REG2_ADDR; // Register Address 2
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_MOD_4FSKRC << 4; // Modulation Scheme
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_PA_DEF << 7; // PA Enable & PA Bias
ADF7021_REG2 |= (uint32_t)(m_rfPower & 0x3FU) << 13; // PA Level (0 - Off, 63 - 13 dBm)
ADF7021_REG2 |= (uint32_t)(nxdnDev / div2) << 19; // Freq. Deviation
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_INV_DATA << 28; // Clock/Data Inversion
ADF7021_REG2 |= (uint32_t)ADF7021_REG2_RC_5 << 30; // R-Cosine Alpha
}
break;
default: // GMSK default: // GMSK
{ {
// Dev: 1200 Hz, symb rate = 4800 // Dev: 1200 Hz, symb rate = 4800
@ -1253,8 +1368,9 @@ void IO::configureTxRx(DVM_STATE modemState)
} }
DEBUG5("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrDiscBW/dmrPostBW/p25DiscBW/p25PostBW", dmrDiscBW, dmrPostBW, p25DiscBW, p25PostBW); DEBUG5("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrDiscBW/dmrPostBW/p25DiscBW/p25PostBW", dmrDiscBW, dmrPostBW, p25DiscBW, p25PostBW);
DEBUG4("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrSymDev/p25SymDev/rfPower", (uint16_t)((ADF7021_PFD * dmrDev) / (f_div * 65536)), DEBUG3("IO::configureTxRx(): configuring ADF Tx/Rx values; nxdnDiscBW/nxdnPostBW", nxdnDiscBW, nxdnPostBW);
(uint16_t)((ADF7021_PFD * p25Dev) / (f_div * 65536)), m_rfPower); DEBUG5("IO::configureTxRx(): configuring ADF Tx/Rx values; dmrSymDev/p25SymDev/nxdnSymDev/rfPower", (uint16_t)((ADF7021_PFD * dmrDev) / (f_div * 65536)),
(uint16_t)((ADF7021_PFD * p25Dev) / (f_div * 65536)), (uint16_t)((ADF7021_PFD * nxdnDev) / (f_div * 65536)), m_rfPower);
} }
/// <summary> /// <summary>

@ -96,11 +96,13 @@
#define AFC_OFFSET_DMR -125 #define AFC_OFFSET_DMR -125
#define AFC_OFFSET_P25 -125 #define AFC_OFFSET_P25 -125
#define AFC_OFFSET_NXDN -125
#else #else
#define AFC_OFFSET_DMR 125 #define AFC_OFFSET_DMR 125
#define AFC_OFFSET_P25 125 #define AFC_OFFSET_P25 125
#define AFC_OFFSET_NXDN 125
#endif // ADF7021_AFC_POS #endif // ADF7021_AFC_POS
@ -108,6 +110,7 @@
#define AFC_OFFSET_DMR 0 #define AFC_OFFSET_DMR 0
#define AFC_OFFSET_P25 0 #define AFC_OFFSET_P25 0
#define AFC_OFFSET_NXDN 0
#endif // ADF7021_ENABLE_4FSK_AFC #endif // ADF7021_ENABLE_4FSK_AFC
@ -169,6 +172,8 @@
#define ADF7021_DEV_P25 22U #define ADF7021_DEV_P25 22U
#endif // ENABLE_P25_WIDE #endif // ENABLE_P25_WIDE
#define ADF7021_DEV_NXDN 13U
/* /*
** Demodulator Setup (Register 4) ** Demodulator Setup (Register 4)
*/ */
@ -177,11 +182,13 @@
#define ADF7021_DISC_BW_DEFAULT 522U // K=85 #define ADF7021_DISC_BW_DEFAULT 522U // K=85
#define ADF7021_DISC_BW_DMR 393U // K=32 #define ADF7021_DISC_BW_DMR 393U // K=32
#define ADF7021_DISC_BW_P25 393U // K=32 #define ADF7021_DISC_BW_P25 393U // K=32
#define ADF7021_DISC_BW_NXDN 295U // K=32
// Post demodulator bandwith // Post demodulator bandwith
#define ADF7021_POST_BW_DEFAULT 10U #define ADF7021_POST_BW_DEFAULT 10U
#define ADF7021_POST_BW_DMR 80U #define ADF7021_POST_BW_DMR 80U
#define ADF7021_POST_BW_P25 6U #define ADF7021_POST_BW_P25 6U
#define ADF7021_POST_BW_NXDN 7U
/* /*
** IF Coarse Cal Setup (Register 5) ** IF Coarse Cal Setup (Register 5)
@ -235,6 +242,8 @@
#define ADF7021_DEV_P25 13U #define ADF7021_DEV_P25 13U
#endif // ENABLE_P25_WIDE #endif // ENABLE_P25_WIDE
#define ADF7021_DEV_NXDN 8U
/* /*
** Demodulator Setup (Register 4) ** Demodulator Setup (Register 4)
*/ */
@ -243,11 +252,13 @@
#define ADF7021_DISC_BW_DEFAULT 522U // K=85 #define ADF7021_DISC_BW_DEFAULT 522U // K=85
#define ADF7021_DISC_BW_DMR 491U // K=32 #define ADF7021_DISC_BW_DMR 491U // K=32
#define ADF7021_DISC_BW_P25 491U // K=32 #define ADF7021_DISC_BW_P25 491U // K=32
#define ADF7021_DISC_BW_NXDN 246U // K=32
// Post demodulator bandwith // Post demodulator bandwith
#define ADF7021_POST_BW_DEFAULT 10U #define ADF7021_POST_BW_DEFAULT 10U
#define ADF7021_POST_BW_DMR 80U #define ADF7021_POST_BW_DMR 80U
#define ADF7021_POST_BW_P25 6U #define ADF7021_POST_BW_P25 6U
#define ADF7021_POST_BW_NXDN 8U
/* /*
** IF Coarse Cal Setup (Register 5) ** IF Coarse Cal Setup (Register 5)
@ -336,11 +347,14 @@
#define ADF7021_SLICER_TH_DMR 51U #define ADF7021_SLICER_TH_DMR 51U
#define ADF7021_SLICER_TH_P25 43U #define ADF7021_SLICER_TH_P25 43U
#define ADF7021_SLICER_TH_NXDN 26U
#else #else
#define ADF7021_SLICER_TH_DMR 57U #define ADF7021_SLICER_TH_DMR 57U
#define ADF7021_SLICER_TH_P25 47U #define ADF7021_SLICER_TH_P25 47U
#define ADF7021_SLICER_TH_NXDN 26U
#endif // ADF7021_N_VER #endif // ADF7021_N_VER

@ -98,6 +98,9 @@ typedef unsigned long long ulong64_t;
// Allow the P25 protocol // Allow the P25 protocol
#define ENABLE_P25 #define ENABLE_P25
// Allow the NXDN protocol
#define ENABLE_NXDN
// Enable ADF7021 support // Enable ADF7021 support
#define ENABLE_ADF7021 #define ENABLE_ADF7021

@ -52,6 +52,11 @@ bool m_p25Enable = true;
#else #else
bool m_p25Enable = false; bool m_p25Enable = false;
#endif #endif
#ifdef ENABLE_NXDN
bool m_nxdnEnable = true;
#else
bool m_nxdnEnable = false;
#endif
bool m_duplex = false; bool m_duplex = false;
@ -75,9 +80,14 @@ dmr::DMRDMOTX dmrDMOTX;
p25::P25RX p25RX; p25::P25RX p25RX;
p25::P25TX p25TX; p25::P25TX p25TX;
/** NXDN */
nxdn::NXDNRX nxdnRX;
nxdn::NXDNTX nxdnTX;
/** Calibration */ /** Calibration */
dmr::CalDMR calDMR; dmr::CalDMR calDMR;
p25::CalP25 calP25; p25::CalP25 calP25;
nxdn::CalNXDN calNXDN;
CalRSSI calRSSI; CalRSSI calRSSI;
/** CW */ /** CW */
@ -117,6 +127,9 @@ void loop()
if (m_p25Enable && m_modemState == STATE_P25) if (m_p25Enable && m_modemState == STATE_P25)
p25TX.process(); p25TX.process();
if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process();
if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K || if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K ||
m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL || m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL ||
m_modemState == STATE_INT_CAL) m_modemState == STATE_INT_CAL)
@ -125,6 +138,9 @@ void loop()
if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_LF_CAL || m_modemState == STATE_P25_CAL) if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_LF_CAL || m_modemState == STATE_P25_CAL)
calP25.process(); calP25.process();
if (m_modemState == STATE_NXDN_CAL)
calNXDN.process();
if (m_modemState == STATE_RSSI_CAL) if (m_modemState == STATE_RSSI_CAL)
calRSSI.process(); calRSSI.process();

@ -57,6 +57,9 @@
#include "p25/P25RX.h" #include "p25/P25RX.h"
#include "p25/P25TX.h" #include "p25/P25TX.h"
#include "p25/CalP25.h" #include "p25/CalP25.h"
#include "nxdn/NXDNRX.h"
#include "nxdn/NXDNTX.h"
#include "nxdn/CalNXDN.h"
#include "CalRSSI.h" #include "CalRSSI.h"
#include "CWIdTX.h" #include "CWIdTX.h"
#include "IO.h" #include "IO.h"
@ -94,6 +97,7 @@ extern uint8_t m_cwIdTXLevel;
extern bool m_dmrEnable; extern bool m_dmrEnable;
extern bool m_p25Enable; extern bool m_p25Enable;
extern bool m_nxdnEnable;
extern bool m_duplex; extern bool m_duplex;
@ -120,9 +124,14 @@ extern dmr::DMRDMOTX dmrDMOTX;
extern p25::P25RX p25RX; extern p25::P25RX p25RX;
extern p25::P25TX p25TX; extern p25::P25TX p25TX;
/** NXDN BS */
extern nxdn::NXDNRX nxdnRX;
extern nxdn::NXDNTX nxdnTX;
/** Calibration */ /** Calibration */
extern dmr::CalDMR calDMR; extern dmr::CalDMR calDMR;
extern p25::CalP25 calP25; extern p25::CalP25 calP25;
extern nxdn::CalNXDN calNXDN;
extern CalRSSI calRSSI; extern CalRSSI calRSSI;
/** CW */ /** CW */

@ -63,6 +63,7 @@ IO::IO():
setPTTInt(LOW); setPTTInt(LOW);
setDMRInt(LOW); setDMRInt(LOW);
setP25Int(LOW); setP25Int(LOW);
setNXDNInt(LOW);
setCOSInt(LOW); setCOSInt(LOW);
#if !defined(BIDIR_DATA_PIN) #if !defined(BIDIR_DATA_PIN)
@ -162,6 +163,10 @@ void IO::process()
/** Project 25 */ /** Project 25 */
p25RX.databit(bit); p25RX.databit(bit);
} }
else if (m_modemState == STATE_NXDN) {
/* Next Generation Digital Narrowband */
nxdnRX.databit(bit);
}
} }
} }
@ -229,6 +234,7 @@ void IO::setMode(DVM_STATE modemState)
setDMRInt(relativeState == STATE_DMR); setDMRInt(relativeState == STATE_DMR);
setP25Int(relativeState == STATE_P25); setP25Int(relativeState == STATE_P25);
setNXDNInt(relativeState == STATE_NXDN);
} }
/// <summary> /// <summary>
@ -353,6 +359,7 @@ void IO::selfTest()
setPTTInt(ledValue); setPTTInt(ledValue);
setDMRInt(ledValue); setDMRInt(ledValue);
setP25Int(ledValue); setP25Int(ledValue);
setNXDNInt(ledValue);
setCOSInt(ledValue); setCOSInt(ledValue);
blinks++; blinks++;

@ -109,11 +109,11 @@ public:
#endif #endif
/// <summary></summary> /// <summary></summary>
void setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel); void setDeviations(uint8_t dmrTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel);
/// <summary>Sets the RF parameters.</summary> /// <summary>Sets the RF parameters.</summary>
uint8_t setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode); uint8_t setRFParams(uint32_t rxFreq, uint32_t txFreq, uint8_t rfPower, ADF_GAIN_MODE gainMode);
/// <summary>Sets the RF adjustment parameters.</summary> /// <summary>Sets the RF adjustment parameters.</summary>
void setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj); void setRFAdjust(int8_t dmrDiscBWAdj, int8_t p25DiscBWAdj, int8_t nxdnDiscBWAdj, int8_t dmrPostBWAdj, int8_t p25PostBWAdj, int8_t nxdnPostBWAdj);
/// <summary>Flag indicating the TX ring buffer has overflowed.</summary> /// <summary>Flag indicating the TX ring buffer has overflowed.</summary>
bool hasTXOverflow(void); bool hasTXOverflow(void);
@ -242,6 +242,8 @@ private:
void setDMRInt(bool on); void setDMRInt(bool on);
/// <summary></summary> /// <summary></summary>
void setP25Int(bool on); void setP25Int(bool on);
/// <summary></summary>
void setNXDNInt(bool on);
}; };
#endif #endif

@ -85,6 +85,9 @@
#define PIN_DEB GPIO_Pin_11 #define PIN_DEB GPIO_Pin_11
#define PORT_DEB GPIOA #define PORT_DEB GPIOA
#define PIN_NXDN_LED GPIO_Pin_8
#define PORT_NXDN_LED GPIOA
#define PIN_DMR_LED GPIO_Pin_15 #define PIN_DMR_LED GPIO_Pin_15
#define PORT_DMR_LED GPIOB #define PORT_DMR_LED GPIOB
@ -159,6 +162,13 @@
#define PIN_DEB GPIO_Pin_9 #define PIN_DEB GPIO_Pin_9
#define PORT_DEB GPIOB #define PORT_DEB GPIOB
#if defined(STM32_USB_HOST)
#define PIN_NXDN_LED GPIO_Pin_1
#else
#define PIN_NXDN_LED GPIO_Pin_7
#endif
#define PORT_NXDN_LED GPIOA
#define PIN_DMR_LED GPIO_Pin_13 #define PIN_DMR_LED GPIO_Pin_13
#define PORT_DMR_LED GPIOB #define PORT_DMR_LED GPIOB
@ -222,6 +232,9 @@
#define PIN_DEB GPIO_Pin_9 #define PIN_DEB GPIO_Pin_9
#define PORT_DEB GPIOB #define PORT_DEB GPIOB
#define PIN_NXDN_LED GPIO_Pin_8
#define PORT_NXDN_LED GPIOA
#define PIN_DMR_LED GPIO_Pin_13 #define PIN_DMR_LED GPIO_Pin_13
#define PORT_DMR_LED GPIOB #define PORT_DMR_LED GPIOB
@ -667,6 +680,12 @@ void IO::initInt()
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_DEB, &GPIO_InitStruct); GPIO_Init(PORT_DEB, &GPIO_InitStruct);
// NXDN LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_NXDN_LED;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PORT_NXDN_LED, &GPIO_InitStruct);
// DMR LED // DMR LED
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_DMR_LED; GPIO_InitStruct.GPIO_Pin = PIN_DMR_LED;
@ -877,4 +896,13 @@ void IO::setP25Int(bool on)
GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET); GPIO_WriteBit(PORT_P25_LED, PIN_P25_LED, on ? Bit_SET : Bit_RESET);
} }
/// <summary>
///
/// </summary>
/// <param name="on"></param>
void IO::setNXDNInt(bool on)
{
GPIO_WriteBit(PORT_NXDN_LED, PIN_NXDN_LED, on ? Bit_SET : Bit_RESET);
}
#endif // STM32F10X_MD #endif // STM32F10X_MD

@ -74,7 +74,7 @@ ifndef $(OSC)
endif endif
# Build object lists # Build object lists
CXXSRC=$(wildcard ./*.cpp) $(wildcard ./dmr/*.cpp) $(wildcard ./p25/*.cpp) CXXSRC=$(wildcard ./*.cpp) $(wildcard ./dmr/*.cpp) $(wildcard ./p25/*.cpp) $(wildcard ./nxdn/*.cpp)
CSRC_STD_F1=$(wildcard $(STD_LIB_F1)/*.c) CSRC_STD_F1=$(wildcard $(STD_LIB_F1)/*.c)
SYS_F1=$(wildcard $(SYS_DIR_F1)/*.c) SYS_F1=$(wildcard $(SYS_DIR_F1)/*.c)
STARTUP_F1=$(wildcard $(STARTUP_DIR_F1)/*.c) STARTUP_F1=$(wildcard $(STARTUP_DIR_F1)/*.c)
@ -195,10 +195,12 @@ $(OBJDIR_F1):
mkdir $@ mkdir $@
mkdir $@/dmr mkdir $@/dmr
mkdir $@/p25 mkdir $@/p25
mkdir $@/nxdn
$(OBJDIR_F4): $(OBJDIR_F4):
mkdir $@ mkdir $@
mkdir $@/dmr mkdir $@/dmr
mkdir $@/p25 mkdir $@/p25
mkdir $@/nxdn
$(BINDIR)/$(BINBIN_F1BL): $(BINDIR)/$(BINELF_F1BL) $(BINDIR)/$(BINBIN_F1BL): $(BINDIR)/$(BINELF_F1BL)
$(CP) -O binary $< $@ $(CP) -O binary $< $@

@ -48,6 +48,11 @@
#else #else
#define DESCR_P25 "" #define DESCR_P25 ""
#endif #endif
#if defined(ENABLE_NXDN)
#define DESCR_NXDN "NXDN, "
#else
#define DESCR_NXDN ""
#endif
#if defined(SEND_RSSI_DATA) #if defined(SEND_RSSI_DATA)
#define DESCR_RSSI "RSSI, " #define DESCR_RSSI "RSSI, "
@ -86,7 +91,7 @@
#define RF_CHIP "ADF7021, " #define RF_CHIP "ADF7021, "
#endif #endif
#define DESCRIPTION "Digital Voice Modem DSP Hotspot [" BOARD_INFO "] (" RF_CHIP DESCR_DMR DESCR_P25 DESCR_OSC DESCR_RSSI "CW Id)" #define DESCRIPTION "Digital Voice Modem DSP Hotspot [" BOARD_INFO "] (" RF_CHIP DESCR_DMR DESCR_P25 DESCR_NXDN DESCR_OSC DESCR_RSSI "CW Id)"
#define concat(a, b, c) a " (build " b " " c ")" #define concat(a, b, c) a " (build " b " " c ")"
const char HARDWARE[] = concat(DESCRIPTION, __TIME__, __DATE__); const char HARDWARE[] = concat(DESCRIPTION, __TIME__, __DATE__);
@ -358,6 +363,22 @@ void SerialPort::process()
} }
break; break;
/* Next Generation Digital Narrowband */
case CMD_NXDN_DATA:
if (m_nxdnEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_NXDN)
err = p25TX.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == RSN_OK) {
if (m_modemState == STATE_IDLE)
setMode(STATE_NXDN);
}
else {
DEBUG2("SerialPort: process(): Received invalid NXDN data", err);
sendNAK(err);
}
break;
default: default:
// Handle this, send a NAK back // Handle this, send a NAK back
sendNAK(RSN_NAK); sendNAK(RSN_NAK);
@ -388,7 +409,7 @@ bool SerialPort::isCalState(DVM_STATE state)
state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K || state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K ||
state == STATE_DMR_LF_CAL || state == STATE_P25_LF_CAL || state == STATE_DMR_LF_CAL || state == STATE_P25_LF_CAL ||
state == STATE_RSSI_CAL || state == STATE_RSSI_CAL ||
state == STATE_P25_CAL || state == STATE_DMR_CAL || state == STATE_P25_CAL || state == STATE_DMR_CAL || state == STATE_NXDN_CAL ||
state == STATE_INT_CAL) { state == STATE_INT_CAL) {
return true; return true;
} }
@ -405,12 +426,14 @@ DVM_STATE SerialPort::calRelativeState(DVM_STATE state)
{ {
if (isCalState(state)) { if (isCalState(state)) {
if (state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K || if (state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K ||
state == STATE_DMR_LF_CAL || state == STATE_DMR_CAL || state == STATE_DMR_LF_CAL || state == STATE_DMR_CAL || state == STATE_NXDN_CAL ||
state == STATE_RSSI_CAL || state == STATE_INT_CAL) { state == STATE_RSSI_CAL || state == STATE_INT_CAL) {
return STATE_DMR; return STATE_DMR;
} else if (state == STATE_P25_CAL_1K || state == STATE_P25_LF_CAL || } else if (state == STATE_P25_CAL_1K || state == STATE_P25_LF_CAL ||
state == STATE_P25_CAL) { state == STATE_P25_CAL) {
return STATE_P25; return STATE_P25;
} else if (state == STATE_NXDN_CAL) {
return STATE_NXDN;
} }
} }
@ -515,6 +538,54 @@ void SerialPort::writeP25Lost()
writeInt(1U, reply, 3); writeInt(1U, reply, 3);
} }
/// <summary>
/// Write NXDN frame data to serial port.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
void SerialPort::writeNXDNData(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE)
return;
if (!m_nxdnEnable)
return;
uint8_t reply[130U];
reply[0U] = DVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = CMD_NXDN_DATA;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
writeInt(1U, reply, count);
}
/// <summary>
/// Write lost NXDN frame data to serial port.
/// </summary>
void SerialPort::writeNXDNLost()
{
if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE)
return;
if (!m_nxdnEnable)
return;
uint8_t reply[3U];
reply[0U] = DVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = CMD_NXDN_LOST;
writeInt(1U, reply, 3);
}
/// <summary> /// <summary>
/// Write calibration frame data to serial port. /// Write calibration frame data to serial port.
/// </summary> /// </summary>
@ -812,6 +883,8 @@ void SerialPort::getStatus()
reply[3U] |= 0x02U; reply[3U] |= 0x02U;
if (m_p25Enable) if (m_p25Enable)
reply[3U] |= 0x08U; reply[3U] |= 0x08U;
if (m_nxdnEnable)
reply[3U] |= 0x10U;
reply[4U] = uint8_t(m_modemState); reply[4U] = uint8_t(m_modemState);
@ -852,7 +925,12 @@ void SerialPort::getStatus()
else else
reply[10U] = 0U; reply[10U] = 0U;
writeInt(1U, reply, 11); if (m_nxdnEnable)
reply[11U] = nxdnTX.getSpace();
else
reply[11U] = 0U;
writeInt(1U, reply, 12);
} }
/// <summary> /// <summary>
@ -891,13 +969,13 @@ void SerialPort::getVersion()
uint8_t SerialPort::modemStateCheck(DVM_STATE state) uint8_t SerialPort::modemStateCheck(DVM_STATE state)
{ {
// invalid mode check // invalid mode check
if (state != STATE_IDLE && state != STATE_DMR && state != STATE_P25 && if (state != STATE_IDLE && state != STATE_DMR && state != STATE_P25 && state != STATE_NXDN &&
state != STATE_P25_CAL_1K && state != STATE_P25_CAL_1K &&
state != STATE_DMR_DMO_CAL_1K && state != STATE_DMR_CAL_1K && state != STATE_DMR_DMO_CAL_1K && state != STATE_DMR_CAL_1K &&
state != STATE_DMR_LF_CAL && state != STATE_P25_LF_CAL && state != STATE_DMR_LF_CAL && state != STATE_P25_LF_CAL &&
state != STATE_RSSI_CAL && state != STATE_RSSI_CAL &&
state != STATE_P25_CAL && state != STATE_DMR_CAL && state != STATE_P25_CAL && state != STATE_DMR_CAL &&
state != STATE_INT_CAL) state != STATE_NXDN_CAL)
return RSN_INVALID_MODE; return RSN_INVALID_MODE;
/* /*
// DMR without DMR being enabled // DMR without DMR being enabled
@ -906,6 +984,9 @@ uint8_t SerialPort::modemStateCheck(DVM_STATE state)
// P25 without P25 being enabled // P25 without P25 being enabled
if (state == STATE_P25 && !m_p25Enable) if (state == STATE_P25 && !m_p25Enable)
return RSN_P25_DISABLED; return RSN_P25_DISABLED;
// NXDN without NXDN being enabled
if (state == STATE_NXDN && !m_nxdnEnable)
return RSN_NXDN_DISABLED;
*/ */
return RSN_OK; return RSN_OK;
} }
@ -918,7 +999,7 @@ uint8_t SerialPort::modemStateCheck(DVM_STATE state)
/// <returns></returns> /// <returns></returns>
uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length) uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length)
{ {
if (length < 14U) if (length < 15U)
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
bool simplex = (data[0U] & 0x80U) == 0x80U; bool simplex = (data[0U] & 0x80U) == 0x80U;
@ -927,6 +1008,7 @@ uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length)
bool dmrEnable = (data[1U] & 0x02U) == 0x02U; bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
bool p25Enable = (data[1U] & 0x08U) == 0x08U; bool p25Enable = (data[1U] & 0x08U) == 0x08U;
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
uint8_t fdmaPreamble = data[2U]; uint8_t fdmaPreamble = data[2U];
if (fdmaPreamble > 255U) if (fdmaPreamble > 255U)
@ -954,14 +1036,20 @@ uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length)
uint8_t dmrTXLevel = data[10U]; uint8_t dmrTXLevel = data[10U];
uint8_t p25TXLevel = data[12U]; uint8_t p25TXLevel = data[12U];
uint8_t nxdnTXLevel = data[15U];
m_modemState = modemState; m_modemState = modemState;
m_dmrEnable = dmrEnable; m_dmrEnable = dmrEnable;
m_p25Enable = p25Enable; m_p25Enable = p25Enable;
m_nxdnEnable = nxdnEnable;
if (m_dmrEnable && m_p25Enable) if (m_dmrEnable && m_p25Enable)
return RSN_HS_NO_DUAL_MODE; return RSN_HS_NO_DUAL_MODE;
if (m_dmrEnable && m_nxdnEnable)
return RSN_HS_NO_DUAL_MODE;
if (m_p25Enable && m_nxdnEnable)
return RSN_HS_NO_DUAL_MODE;
m_duplex = !simplex; m_duplex = !simplex;
@ -977,10 +1065,11 @@ uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length)
} }
#endif #endif
io.setDeviations(dmrTXLevel, p25TXLevel); io.setDeviations(dmrTXLevel, p25TXLevel, nxdnTXLevel);
p25TX.setPreambleCount(fdmaPreamble); p25TX.setPreambleCount(fdmaPreamble);
dmrDMOTX.setPreambleCount(fdmaPreamble); dmrDMOTX.setPreambleCount(fdmaPreamble);
//nxdnTX.setPreambleCount(fdmaPreamble);
p25RX.setNAC(nac); p25RX.setNAC(nac);
@ -1039,6 +1128,7 @@ void SerialPort::setMode(DVM_STATE modemState)
case STATE_DMR: case STATE_DMR:
DEBUG1("SerialPort: setMode(): mode set to DMR"); DEBUG1("SerialPort: setMode(): mode set to DMR");
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_P25: case STATE_P25:
@ -1048,6 +1138,17 @@ void SerialPort::setMode(DVM_STATE modemState)
dmrRX.reset(); dmrRX.reset();
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
nxdnRX.reset();
cwIdTX.reset();
break;
case STATE_NXDN:
DEBUG1("SerialPort: setMode(): mode set to NXDN");
#if defined(DUPLEX)
dmrIdleRX.reset();
dmrRX.reset();
#endif
dmrDMORX.reset();
p25RX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_DMR_CAL: case STATE_DMR_CAL:
@ -1058,6 +1159,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_P25_CAL: case STATE_P25_CAL:
@ -1068,6 +1170,18 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset();
break;
case STATE_NXDN_CAL:
DEBUG1("SerialPort: setMode(): mode set to NXDN Calibrate");
#if defined(DUPLEX)
dmrIdleRX.reset();
dmrRX.reset();
#endif
dmrDMORX.reset();
p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_P25_LF_CAL: case STATE_P25_LF_CAL:
@ -1078,6 +1192,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_RSSI_CAL: case STATE_RSSI_CAL:
@ -1088,6 +1203,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_DMR_LF_CAL: case STATE_DMR_LF_CAL:
@ -1098,6 +1214,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_DMR_CAL_1K: case STATE_DMR_CAL_1K:
@ -1108,6 +1225,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_DMR_DMO_CAL_1K: case STATE_DMR_DMO_CAL_1K:
@ -1118,6 +1236,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
case STATE_P25_CAL_1K: case STATE_P25_CAL_1K:
@ -1128,6 +1247,7 @@ void SerialPort::setMode(DVM_STATE modemState)
#endif #endif
dmrDMORX.reset(); dmrDMORX.reset();
p25RX.reset(); p25RX.reset();
nxdnRX.reset();
cwIdTX.reset(); cwIdTX.reset();
break; break;
default: default:
@ -1149,7 +1269,7 @@ void SerialPort::setMode(DVM_STATE modemState)
/// <returns></returns> /// <returns></returns>
uint8_t SerialPort::setRFParams(const uint8_t* data, uint8_t length) uint8_t SerialPort::setRFParams(const uint8_t* data, uint8_t length)
{ {
if (length < 15U) if (length < 17U)
return RSN_ILLEGAL_LENGTH; return RSN_ILLEGAL_LENGTH;
uint32_t rxFreq, txFreq; uint32_t rxFreq, txFreq;
@ -1180,6 +1300,12 @@ uint8_t SerialPort::setRFParams(const uint8_t* data, uint8_t length)
if (p25DiscBWAdj < -128) if (p25DiscBWAdj < -128)
return RSN_INVALID_REQUEST; return RSN_INVALID_REQUEST;
int8_t nxdnDiscBWAdj = int8_t(data[15U]) - 128;
if (nxdnDiscBWAdj > 128)
return RSN_INVALID_REQUEST;
if (nxdnDiscBWAdj < -128)
return RSN_INVALID_REQUEST;
int8_t dmrPostBWAdj = int8_t(data[12U]) - 128; int8_t dmrPostBWAdj = int8_t(data[12U]) - 128;
if (dmrPostBWAdj > 128) if (dmrPostBWAdj > 128)
return RSN_INVALID_REQUEST; return RSN_INVALID_REQUEST;
@ -1192,9 +1318,15 @@ uint8_t SerialPort::setRFParams(const uint8_t* data, uint8_t length)
if (p25PostBWAdj < -128) if (p25PostBWAdj < -128)
return RSN_INVALID_REQUEST; return RSN_INVALID_REQUEST;
int8_t nxdnPostBWAdj = int8_t(data[16U]) - 128;
if (nxdnPostBWAdj > 128)
return RSN_INVALID_REQUEST;
if (nxdnPostBWAdj < -128)
return RSN_INVALID_REQUEST;
gainMode = (ADF_GAIN_MODE)data[14U]; gainMode = (ADF_GAIN_MODE)data[14U];
io.setRFAdjust(dmrDiscBWAdj, p25DiscBWAdj, dmrPostBWAdj, p25PostBWAdj); io.setRFAdjust(dmrDiscBWAdj, p25DiscBWAdj, nxdnDiscBWAdj, dmrPostBWAdj, p25PostBWAdj, nxdnPostBWAdj);
return io.setRFParams(rxFreq, txFreq, rfPower, gainMode); return io.setRFParams(rxFreq, txFreq, rfPower, gainMode);
} }

@ -46,11 +46,14 @@ enum DVM_STATE {
STATE_DMR = 1U, STATE_DMR = 1U,
// Project 25 // Project 25
STATE_P25 = 2U, STATE_P25 = 2U,
// NXDN
STATE_NXDN = 3U,
// CW // CW
STATE_CW = 10U, STATE_CW = 10U,
// Calibration States // Calibration States
STATE_INT_CAL = 90U,
STATE_P25_LF_CAL = 91U, STATE_P25_LF_CAL = 91U,
STATE_P25_CAL_1K = 92U, STATE_P25_CAL_1K = 92U,
@ -62,7 +65,7 @@ enum DVM_STATE {
STATE_P25_CAL = 97U, STATE_P25_CAL = 97U,
STATE_DMR_CAL = 98U, STATE_DMR_CAL = 98U,
STATE_INT_CAL = 99U STATE_NXDN_CAL = 99U
}; };
enum DVM_COMMANDS { enum DVM_COMMANDS {
@ -92,6 +95,9 @@ enum DVM_COMMANDS {
CMD_P25_LOST = 0x32U, CMD_P25_LOST = 0x32U,
CMD_P25_CLEAR = 0x33U, CMD_P25_CLEAR = 0x33U,
CMD_NXDN_DATA = 0x41U,
CMD_NXDN_LOST = 0x42U,
CMD_ACK = 0x70U, CMD_ACK = 0x70U,
CMD_NAK = 0x7FU, CMD_NAK = 0x7FU,
@ -133,6 +139,7 @@ enum CMD_REASON_CODE {
RSN_DMR_DISABLED = 63U, RSN_DMR_DISABLED = 63U,
RSN_P25_DISABLED = 64U, RSN_P25_DISABLED = 64U,
RSN_NXDN_DISABLED = 65U
}; };
const uint8_t DVM_FRAME_START = 0xFEU; const uint8_t DVM_FRAME_START = 0xFEU;
@ -171,6 +178,11 @@ public:
/// <summary>Write lost P25 frame data to serial port.</summary> /// <summary>Write lost P25 frame data to serial port.</summary>
void writeP25Lost(); void writeP25Lost();
/// <summary>Write NXDN frame data to serial port.</summary>
void writeNXDNData(const uint8_t* data, uint8_t length);
/// <summary>Write lost NXDN frame data to serial port.</summary>
void writeNXDNLost();
/// <summary>Write calibration frame data to serial port.</summary> /// <summary>Write calibration frame data to serial port.</summary>
void writeCalData(const uint8_t* data, uint8_t length); void writeCalData(const uint8_t* data, uint8_t length);
/// <summary>Write RSSI frame data to serial port.</summary> /// <summary>Write RSSI frame data to serial port.</summary>

@ -0,0 +1,128 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 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 "Globals.h"
#include "nxdn/CalNXDN.h"
using namespace nxdn;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
// NXDN 1031 Hz Test Pattern, RAN: 1, Unit ID: 1, Dst Group ID: 1, Outbound Direction
const uint8_t NXDN_CAL1K[4][49] = {
{ 0x00U,
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0xFAU, 0x0AU, 0x6EU, 0x8AU, 0x23U, 0x56U, 0xE8U,
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U },
{ 0x00U,
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x7CU, 0x6DU, 0xBBU, 0x0EU, 0xB3U, 0xA4U, 0x26U, 0xA8U,
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U },
{ 0x00U,
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x76U, 0x3AU, 0x1BU, 0x4AU, 0x81U, 0xA8U, 0xE2U, 0x80U,
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U },
{ 0x00U,
0xCDU, 0xF5U, 0x9DU, 0x5DU, 0x74U, 0x28U, 0x83U, 0x02U, 0xB0U, 0x2DU, 0x07U, 0xE2U,
0x4CU, 0xAAU, 0xDEU, 0x8BU, 0x26U, 0xE4U, 0xF2U, 0x82U, 0x88U,
0xC6U, 0x8AU, 0x74U, 0x29U, 0xA4U, 0xECU, 0xD0U, 0x08U, 0x22U,
0xCEU, 0xA2U, 0xFCU, 0x01U, 0x8CU, 0xECU, 0xDAU, 0x0AU, 0xA0U,
0xEEU, 0x8AU, 0x7EU, 0x2BU, 0x26U, 0xCCU, 0xF8U, 0x8AU, 0x08U }
};
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the CalNXDN class.
/// </summary>
CalNXDN::CalNXDN() :
m_transmit(false),
m_state(NXDNCAL1K_IDLE),
m_audioSeq(0U)
{
/* stub */
}
/// <summary>
/// Process local state and transmit on the air interface.
/// </summary>
void CalNXDN::process()
{
nxdnTX.process();
uint16_t space = nxdnTX.getSpace();
if (space < 1U)
return;
switch (m_state) {
case NXDNCAL1K_TX:
nxdnTX.writeData(NXDN_CAL1K[m_audioSeq], NXDN_FRAME_LENGTH_BYTES + 1U);
m_audioSeq = (m_audioSeq + 1U) % 4U;
if(!m_transmit)
m_state = NXDNCAL1K_IDLE;
break;
default:
m_state = NXDNCAL1K_IDLE;
m_audioSeq = 0U;
break;
}
}
/// <summary>
/// Write P25 calibration data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t CalNXDN::write(const uint8_t* data, uint16_t length)
{
if (length != 1U)
return RSN_ILLEGAL_LENGTH;
m_transmit = data[0U] == 1U;
if(m_transmit && m_state == NXDNCAL1K_IDLE)
m_state = NXDNCAL1K_TX;
return RSN_OK;
}

@ -0,0 +1,73 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2018 by Andy Uribe CA6JAU
* Copyright (C) 2020 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(__CAL_NXDN_H__)
#define __CAL_NXDN_H__
#include "Defines.h"
#include "nxdn/NXDNDefines.h"
namespace nxdn
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum NXDNCAL1K {
NXDNCAL1K_IDLE,
NXDNCAL1K_TX
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements logic for NXDN calibration mode.
// ---------------------------------------------------------------------------
class DSP_FW_API CalNXDN {
public:
/// <summary>Initializes a new instance of the CalNXDN class.</summary>
CalNXDN();
/// <summary>Process local state and transmit on the air interface.</summary>
void process();
/// <summary>Write NXDN calibration state.</summary>
uint8_t write(const uint8_t* data, uint16_t length);
private:
bool m_transmit;
NXDNCAL1K m_state;
uint8_t m_audioSeq;
};
} // namespace nxdn
#endif // __CAL_NXDN_H__

@ -0,0 +1,74 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2016,2017,2018 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(__NXDN_DEFINES_H__)
#define __NXDN_DEFINES_H__
#include "Defines.h"
namespace nxdn
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint32_t NXDN_RADIO_SYMBOL_LENGTH = 10U; // At 24 kHz sample rate
const uint32_t NXDN_FRAME_LENGTH_BITS = 384U;
const uint32_t NXDN_FRAME_LENGTH_BYTES = NXDN_FRAME_LENGTH_BITS / 8U;
const uint32_t NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BITS / 2U;
const uint32_t NXDN_FRAME_LENGTH_SAMPLES = NXDN_FRAME_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH;
const uint32_t NXDN_FSW_LENGTH_BITS = 20U;
const uint32_t NXDN_FSW_LENGTH_SYMBOLS = NXDN_FSW_LENGTH_BITS / 2U;
const uint32_t NXDN_FSW_LENGTH_SAMPLES = NXDN_FSW_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH;
const uint8_t NXDN_FSW_BYTES[] = { 0xCDU, 0xF5U, 0x90U };
const uint8_t NXDN_FSW_BYTES_MASK[] = { 0xFFU, 0xFFU, 0xF0U };
const uint8_t NXDN_FSW_BYTES_LENGTH = 3U;
const uint32_t NXDN_FSW_BITS = 0x000CDF59U;
const uint32_t NXDN_FSW_BITS_MASK = 0x000FFFFFU;
const uint8_t NXDN_PREAMBLE[] = { 0x57U, 0x75U, 0xFDU };
const uint8_t NXDN_SYNC = 0x5FU;
// C D F 5 9
// 11 00 11 01 11 11 01 01 10 01
// -3 +1 -3 +3 -3 -3 +3 +3 -1 +3
const int8_t NXDN_FSW_SYMBOLS_VALUES[] = {-3, +1, -3, +3, -3, -3, +3, +3, -1, +3};
const uint16_t NXDN_FSW_SYMBOLS = 0x014DU;
const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU;
const uint32_t NXDN_TX_BUFFER_LEN = 2000U;
} // namespace nxdn
#endif // __NXDN_DEFINES_H__

@ -0,0 +1,201 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2018,2020 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 "Globals.h"
#include "nxdn/NXDNRX.h"
#include "Utils.h"
using namespace nxdn;
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const uint8_t MAX_FSW_BIT_START_ERRS = 0U;
const uint8_t MAX_FSW_BIT_RUN_ERRS = 3U;
const unsigned int MAX_FSW_FRAMES = 5U + 1U;
const uint16_t NOENDPTR = 9999U;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the NXDNRX class.
/// </summary>
NXDNRX::NXDNRX() :
m_bitBuffer(0x00U),
m_buffer(),
m_dataPtr(0U),
m_endPtr(NOENDPTR),
m_lostCount(0U),
m_state(NXDNRXS_NONE)
{
::memset(m_buffer, 0x00U, NXDN_FRAME_LENGTH_BYTES + 3U);
}
/// <summary>
/// Helper to reset data values to defaults.
/// </summary>
void NXDNRX::reset()
{
m_bitBuffer = 0x00U;
m_dataPtr = 0U;
m_endPtr = NOENDPTR;
m_lostCount = 0U;
m_state = NXDNRXS_NONE;
}
/// <summary>
/// Sample NXDN bits from the air interface.
/// </summary>
/// <param name="bit"></param>
void NXDNRX::databit(bool bit)
{
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
if (m_state != NXDNRXS_NONE) {
_WRITE_BIT(m_buffer, m_dataPtr, bit);
m_dataPtr++;
if (m_dataPtr > NXDN_FRAME_LENGTH_BITS) {
m_dataPtr = 0U;
}
}
if (m_state == NXDNRXS_DATA) {
processData(bit);
}
else {
bool ret = correlateSync();
if (ret) {
DEBUG3("P25RX: databit(): dataPtr/endPtr", m_dataPtr, m_endPtr);
m_state = NXDNRXS_DATA;
}
io.setDecode(m_state != NXDNRXS_NONE);
}
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Helper to process NXDN data bits.
/// </summary>
/// <param name="bit"></param>
void NXDNRX::processData(bool bit)
{
// only search for a sync in the right place +-2 bits
if (m_dataPtr >= (NXDN_FSW_LENGTH_BITS - 2U) && m_dataPtr <= (NXDN_FSW_LENGTH_BITS + 2U)) {
correlateSync();
}
// process voice frame
if (m_dataPtr == m_endPtr) {
m_lostCount--;
// we've not seen a data sync for too long, signal sync lost and change to NXDNRXS_NONE
if (m_lostCount == 0U) {
DEBUG1("NXDNRX: processData(): sync timeout in PDU, lost lock");
io.setDecode(false);
serial.writeP25Lost();
reset();
}
else {
DEBUG2("NXDNRX: processData(): sync found in PDU pos", m_dataPtr);
uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 1U];
::memcpy(frame + 1U, m_buffer, m_endPtr / 8U);
frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; // set sync flag
serial.writeP25Data(frame, NXDN_FRAME_LENGTH_BYTES + 1U);
}
}
}
/// <summary>
/// Frame synchronization correlator.
/// </summary>
/// <returns></returns>
bool NXDNRX::correlateSync()
{
uint8_t maxErrs;
if (m_state == NXDNRXS_NONE)
maxErrs = MAX_FSW_BIT_START_ERRS;
else
maxErrs = MAX_FSW_BIT_RUN_ERRS;
// unpack sync bytes
uint8_t sync[NXDN_FSW_BYTES_LENGTH];
sync[0U] = (uint8_t)((m_bitBuffer >> 16) & 0xFFU);
sync[1U] = (uint8_t)((m_bitBuffer >> 8) & 0xFFU);
sync[2U] = (uint8_t)((m_bitBuffer >> 0) & 0xF0U);
uint8_t errs = 0U;
for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++)
errs += countBits8(sync[i] ^ NXDN_FSW_BYTES[i]);
if (errs <= maxErrs) {
::memset(m_buffer, 0x00U, NXDN_FRAME_LENGTH_BYTES + 3U);
DEBUG2("NXDNRX: correlateSync(): correlateSync errs", errs);
DEBUG4("NXDNRX: correlateSync(): sync [b0 - b2]", sync[0], sync[1], sync[2]);
for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++)
m_buffer[i] = sync[i];
// DEBUG1("NXDNRX: m_buffer dump");
// DEBUG_DUMP(m_buffer, P25_LDU_FRAME_LENGTH_BYTES);
m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_BITS - NXDN_FSW_LENGTH_BITS;
if (m_endPtr >= NXDN_FRAME_LENGTH_BITS)
m_endPtr -= NXDN_FRAME_LENGTH_BITS;
m_lostCount = MAX_FSW_FRAMES;
m_dataPtr = NXDN_FSW_LENGTH_BITS;
DEBUG3("NXDNRX: correlateSync(): dataPtr/endPtr", m_dataPtr, m_endPtr);
return true;
}
return false;
}

@ -0,0 +1,83 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017,2018,2020 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(__NXDN_RX_H__)
#define __NXDN_RX_H__
#include "Defines.h"
#include "nxdn/NXDNDefines.h"
namespace nxdn
{
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
enum NXDNRX_STATE {
NXDNRXS_NONE,
NXDNRXS_DATA
};
// ---------------------------------------------------------------------------
// Class Declaration
// Implements receiver logic for DMR slots.
// ---------------------------------------------------------------------------
class DSP_FW_API NXDNRX {
public:
/// <summary>Initializes a new instance of the NXDNRX class.</summary>
NXDNRX();
/// <summary>Helper to reset data values to defaults.</summary>
void reset();
/// <summary>Sample NXDN bits from the air interface.</summary>
void databit(bool bit);
private:
uint64_t m_bitBuffer;
uint8_t m_buffer[NXDN_FRAME_LENGTH_BYTES + 3U];
uint16_t m_dataPtr;
uint16_t m_endPtr;
uint16_t m_lostCount;
NXDNRX_STATE m_state;
/// <summary>Helper to process NXDN data bits.</summary>
void processData(bool bit);
/// <summary>Frame synchronization correlator.</summary>
bool correlateSync();
};
} // namespace nxdn
#endif // __NXDN_RX_H__

@ -0,0 +1,234 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2009-2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2017 by Andy Uribe CA6JAU
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* 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 "Globals.h"
#include "nxdn/NXDNTX.h"
#include "nxdn/NXDNDefines.h"
using namespace nxdn;
// ---------------------------------------------------------------------------
// Public Class Members
// ---------------------------------------------------------------------------
/// <summary>
/// Initializes a new instance of the NXDNTX class.
/// </summary>
NXDNTX::NXDNTX() :
m_fifo(NXDN_TX_BUFFER_LEN),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_preambleCnt(240U), // 200ms
m_txHang(3000U), // 5s
m_tailCnt(0U)
{
/* stub */
}
/// <summary>
/// Process local buffer and transmit on the air interface.
/// </summary>
void NXDNTX::process()
{
if (m_fifo.getData() == 0U && m_poLen == 0U && m_tailCnt > 0U) {
// transmit silence until the hang timer has expired
uint16_t space = io.getSpace();
while (space > 8U) {
writeSilence();
space -= 8U;
m_tailCnt--;
if (m_tailCnt == 0U)
return;
if (m_fifo.getData() > 0U) {
m_tailCnt = 0U;
return;
}
}
if (m_fifo.getData() == 0U && m_poLen == 0U)
return;
}
if (m_poLen == 0U) {
if (m_fifo.getData() == 0U)
return;
createData();
DEBUG2("NXDNTX: process(): poLen", m_poLen);
}
if (m_poLen > 0U) {
uint16_t space = io.getSpace();
while (space > 8U) {
uint8_t c = m_poBuffer[m_poPtr++];
writeByte(c);
space -= 8U;
m_tailCnt = m_txHang;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
return;
}
}
}
}
/// <summary>
/// Write data to the local buffer.
/// </summary>
/// <param name="data"></param>
/// <param name="length"></param>
/// <returns></returns>
uint8_t NXDNTX::writeData(const uint8_t* data, uint16_t length)
{
if (length != (NXDN_FRAME_LENGTH_BYTES + 1U))
return RSN_ILLEGAL_LENGTH;
uint16_t space = m_fifo.getSpace();
DEBUG3("NXDNTX: writeData(): dataLength/fifoLength", length, space);
if (space < length) {
m_fifo.reset();
return RSN_RINGBUFF_FULL;
}
if (space < NXDN_FRAME_LENGTH_BYTES)
return RSN_RINGBUFF_FULL;
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++)
m_fifo.put(data[i + 1U]);
return RSN_OK;
}
/// <summary>
/// Clears the local buffer.
/// </summary>
void NXDNTX::clear()
{
m_fifo.reset();
}
/// <summary>
/// Sets the FDMA preamble count.
/// </summary>
/// <param name="preambleCnt">Count of preambles.</param>
void NXDNTX::setPreambleCount(uint8_t preambleCnt)
{
m_preambleCnt = 300U + uint16_t(preambleCnt) * 6U; // 500ms + tx delay
if (m_preambleCnt > 1200U)
m_preambleCnt = 1200U;
}
/// <summary>
/// Sets the Tx hang time.
/// </summary>
/// <param name="txHang">Transmit hang time in seconds.</param>
void NXDNTX::setTxHang(uint8_t txHang)
{
m_txHang = txHang * 600U;
}
/// <summary>
/// Helper to get how much space the ring buffer has for samples.
/// </summary>
/// <returns></returns>
uint8_t NXDNTX::getSpace() const
{
return m_fifo.getSpace() / NXDN_FRAME_LENGTH_BYTES;
}
// ---------------------------------------------------------------------------
// Private Class Members
// ---------------------------------------------------------------------------
/// <summary>
///
/// </summary>
void NXDNTX::createData()
{
if (!m_tx) {
for (uint16_t i = 0U; i < m_preambleCnt; i++)
m_poBuffer[m_poLen++] = NXDN_SYNC;
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[0U];
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[1U];
m_poBuffer[m_poLen++] = NXDN_PREAMBLE[2U];
}
else {
uint8_t length = m_fifo.get();
DEBUG3("P25TX: createData(): dataLength/fifoSpace", length, m_fifo.getSpace());
for (uint8_t i = 0U; i < length; i++) {
m_poBuffer[m_poLen++] = m_fifo.get();
}
}
m_poPtr = 0U;
}
/// <summary>
///
/// </summary>
/// <param name="c"></param>
void NXDNTX::writeByte(uint8_t c)
{
uint8_t bit;
uint8_t mask = 0x80U;
for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
if ((c & mask) == mask)
bit = 1U;
else
bit = 0U;
io.write(&bit, 1);
}
}
/// <summary>
///
/// </summary>
void NXDNTX::writeSilence()
{
uint8_t bit;
for (uint8_t i = 0U; i < 4U; i++) {
bit = 0U;
io.write(&bit, 1);
}
}

@ -0,0 +1,88 @@
/**
* Digital Voice Modem - DSP Firmware
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / DSP Firmware
*
*/
//
// Based on code from the MMDVM project. (https://github.com/g4klx/MMDVM)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
* Copyright (C) 2022 by Bryan Biedenkapp N2PLL
*
* 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(__NXDN_TX_H__)
#define __NXDN_TX_H__
#include "Defines.h"
#include "SerialBuffer.h"
namespace nxdn
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements transmitter logic for NXDN mode operation.
// ---------------------------------------------------------------------------
class DSP_FW_API NXDNTX {
public:
/// <summary>Initializes a new instance of the NXDNTX class.</summary>
NXDNTX();
/// <summary>Process local buffer and transmit on the air interface.</summary>
void process();
/// <summary>Write data to the local buffer.</summary>
uint8_t writeData(const uint8_t* data, uint16_t length);
/// <summary>Clears the local buffer.</summary>
void clear();
/// <summary>Sets the FDMA preamble count.</summary>
void setPreambleCount(uint8_t preambleCnt);
/// <summary>Sets the transmit hang time.</summary>
void setTxHang(uint8_t txHang);
/// <summary>Helper to get how much space the ring buffer has for samples.</summary>
uint8_t getSpace() const;
private:
SerialBuffer m_fifo;
uint8_t m_poBuffer[60U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint16_t m_preambleCnt;
uint32_t m_txHang;
uint32_t m_tailCnt;
/// <summary></summary>
void createData();
/// <summary></summary>
void writeByte(uint8_t c);
/// <summary></summary>
void writeSilence();
};
} // namespace nxdn
#endif // __NXDN_TX_H__
Loading…
Cancel
Save

Powered by TurnKey Linux.