Add NXDN, first commit

pull/32/head
Andy CA6JAU 8 years ago
parent 84d3258a5d
commit 3788ad540f

@ -33,6 +33,7 @@ enum MMDVM_STATE {
STATE_DMR = 2,
STATE_YSF = 3,
STATE_P25 = 4,
STATE_NXDN = 5,
// Dummy states start at 90
STATE_CWID = 97
@ -62,6 +63,8 @@ const uint8_t MARK_NONE = 0x00U;
#include "YSFTX.h"
#include "P25RX.h"
#include "P25TX.h"
#include "NXDNRX.h"
#include "NXDNTX.h"
#include "CWIdTX.h"
#include "Debug.h"
#include "Utils.h"
@ -81,6 +84,7 @@ extern bool m_dstarEnable;
extern bool m_dmrEnable;
extern bool m_ysfEnable;
extern bool m_p25Enable;
extern bool m_nxdnEnable;
extern bool m_duplex;
@ -110,6 +114,9 @@ extern CYSFTX ysfTX;
extern CP25RX p25RX;
extern CP25TX p25TX;
extern CNXDNRX nxdnRX;
extern CNXDNTX nxdnTX;
extern CCWIdTX cwIdTX;
#endif

@ -38,6 +38,7 @@ bool m_dstarEnable = true;
bool m_dmrEnable = true;
bool m_ysfEnable = true;
bool m_p25Enable = true;
bool m_nxdnEnable = true;
bool m_duplex = false;
@ -64,6 +65,9 @@ CYSFTX ysfTX;
CP25RX p25RX;
CP25TX p25TX;
CNXDNRX nxdnRX;
CNXDNTX nxdnTX;
CCWIdTX cwIdTX;
CSerialPort serial;
@ -101,6 +105,9 @@ void loop()
if (m_p25Enable && m_modemState == STATE_P25)
p25TX.process();
if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process();
if (m_modemState == STATE_IDLE)
cwIdTX.process();
}

@ -34,6 +34,7 @@ bool m_dstarEnable = true;
bool m_dmrEnable = true;
bool m_ysfEnable = true;
bool m_p25Enable = true;
bool m_nxdnEnable = true;
bool m_duplex = false;
@ -60,6 +61,9 @@ CYSFTX ysfTX;
CP25RX p25RX;
CP25TX p25TX;
CNXDNRX nxdnRX;
CNXDNTX nxdnTX;
CCWIdTX cwIdTX;
CSerialPort serial;
@ -96,6 +100,9 @@ void loop()
if (m_p25Enable && m_modemState == STATE_P25)
p25TX.process();
if (m_nxdnEnable && m_modemState == STATE_NXDN)
nxdnTX.process();
if (m_modemState == STATE_IDLE)
cwIdTX.process();
}

@ -0,0 +1,50 @@
/*
* 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(NXDNDEFINES_H)
#define NXDNDEFINES_H
const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 10U; // At 24 kHz sample rate
const unsigned int NXDN_FRAME_LENGTH_BITS = 384U;
const unsigned int NXDN_FRAME_LENGTH_BYTES = NXDN_FRAME_LENGTH_BITS / 8U;
const unsigned int NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BITS / 2U;
const unsigned int NXDN_FRAME_LENGTH_SAMPLES = NXDN_FRAME_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH;
const unsigned int NXDN_FSW_LENGTH_BITS = 20U;
const unsigned int NXDN_FSW_LENGTH_SYMBOLS = NXDN_FSW_LENGTH_BITS / 2U;
const unsigned int 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;
// 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;
#endif

@ -0,0 +1,139 @@
/*
* Copyright (C) 2009-2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Config.h"
#include "Globals.h"
#include "NXDNRX.h"
#include "Utils.h"
const uint8_t MAX_FSW_BIT_START_ERRS = 1U;
const uint8_t MAX_FSW_BIT_RUN_ERRS = 3U;
const unsigned int MAX_FSW_FRAMES = 5U + 1U;
const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
CNXDNRX::CNXDNRX() :
m_prev(false),
m_state(NXDNRXS_NONE),
m_bitBuffer(0x00U),
m_outBuffer(),
m_buffer(NULL),
m_bufferPtr(0U),
m_lostCount(0U)
{
m_buffer = m_outBuffer + 1U;
}
void CNXDNRX::reset()
{
m_prev = false;
m_state = NXDNRXS_NONE;
m_bitBuffer = 0x00U;
m_bufferPtr = 0U;
m_lostCount = 0U;
}
void CNXDNRX::databit(bool bit)
{
if (m_state == NXDNRXS_NONE)
processNone(bit);
else
processData(bit);
}
void CNXDNRX::processNone(bool bit)
{
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
// Fuzzy matching of the data sync bit sequence
if (countBits64((m_bitBuffer & NXDN_FSW_BITS_MASK) ^ NXDN_FSW_BITS) <= MAX_FSW_BIT_START_ERRS) {
DEBUG1("NXDNRX: sync found in None");
for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++)
m_buffer[i] = NXDN_FSW_BYTES[i];
m_lostCount = MAX_FSW_FRAMES;
m_bufferPtr = NXDN_FSW_LENGTH_BITS;
m_state = NXDNRXS_DATA;
io.setDecode(true);
}
}
void CNXDNRX::processData(bool bit)
{
m_bitBuffer <<= 1;
if (bit)
m_bitBuffer |= 0x01U;
WRITE_BIT1(m_buffer, m_bufferPtr, bit);
m_bufferPtr++;
// Only search for a sync in the right place +-2 symbols
if (m_bufferPtr >= (NXDN_FSW_LENGTH_BITS - 2U) && m_bufferPtr <= (NXDN_FSW_LENGTH_BITS + 2U)) {
// Fuzzy matching of the data sync bit sequence
if (countBits64((m_bitBuffer & NXDN_FSW_BITS_MASK) ^ NXDN_FSW_BITS) <= MAX_FSW_BIT_RUN_ERRS) {
DEBUG2("NXDNRX: found sync in Data, pos", m_bufferPtr - NXDN_FSW_LENGTH_BITS);
m_lostCount = MAX_FSW_FRAMES;
m_bufferPtr = NXDN_FSW_LENGTH_BITS;
}
}
// Send a data frame to the host if the required number of bits have been received
if (m_bufferPtr == NXDN_FRAME_LENGTH_BITS) {
// We've not seen a data sync for too long, signal RXLOST and change to RX_NONE
m_lostCount--;
if (m_lostCount == 0U) {
DEBUG1("NXDNRX: sync timed out, lost lock");
io.setDecode(false);
serial.writeNXDNLost();
m_state = NXDNRXS_NONE;
} else {
m_outBuffer[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U;
writeRSSIData(m_outBuffer);
// Start the next frame
::memset(m_outBuffer, 0x00U, NXDN_FRAME_LENGTH_BYTES + 3U);
m_bufferPtr = 0U;
}
}
}
void CNXDNRX::writeRSSIData(uint8_t* data)
{
#if defined(SEND_RSSI_DATA)
uint16_t rssi = io.readRSSI();
data[49U] = (rssi >> 8) & 0xFFU;
data[50U] = (rssi >> 0) & 0xFFU;
serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 3U);
#else
serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U);
#endif
}

@ -0,0 +1,54 @@
/*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(NXDNRX_H)
#define NXDNRX_H
#include "NXDNDefines.h"
enum NXDNRX_STATE {
NXDNRXS_NONE,
NXDNRXS_DATA
};
class CNXDNRX {
public:
CNXDNRX();
void databit(bool bit);
void reset();
private:
bool m_prev;
NXDNRX_STATE m_state;
uint64_t m_bitBuffer;
uint8_t m_outBuffer[NXDN_FRAME_LENGTH_BYTES + 3U];
uint8_t* m_buffer;
uint16_t m_bufferPtr;
uint16_t m_lostCount;
void processNone(bool bit);
void processData(bool bit);
void writeRSSIData(uint8_t* data);
};
#endif

@ -0,0 +1,120 @@
/*
* Copyright (C) 2009-2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "Config.h"
#include "Globals.h"
#include "NXDNTX.h"
#include "NXDNDefines.h"
const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU};
const uint8_t NXDN_SYNC = 0x5FU;
CNXDNTX::CNXDNTX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U), // 200ms
m_count(0U)
{
}
void CNXDNTX::process()
{
if (m_buffer.getData() == 0U && m_poLen == 0U)
return;
if (m_poLen == 0U) {
if (!m_tx) {
m_delay = true;
m_count = 0U;
m_poLen = m_txDelay;
} else {
m_delay = false;
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++)
m_poBuffer[m_poLen++] = m_buffer.get();
}
m_poPtr = 0U;
}
if (m_poLen > 0U) {
uint16_t space = io.getSpace();
while (space > 8U) {
if (m_delay) {
m_poPtr++;
writeByte(NXDN_SYNC);
}
else
writeByte(m_poBuffer[m_poPtr++]);
space -= 8U;
if (m_poPtr >= m_poLen) {
m_poPtr = 0U;
m_poLen = 0U;
m_delay = false;
return;
}
}
}
}
uint8_t CNXDNTX::writeData(const uint8_t* data, uint8_t length)
{
if (length != (NXDN_FRAME_LENGTH_BYTES + 1U))
return 4U;
uint16_t space = m_buffer.getSpace();
if (space < NXDN_FRAME_LENGTH_BYTES)
return 5U;
for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++)
m_buffer.put(data[i + 1U]);
return 0U;
}
void CNXDNTX::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);
}
}
void CNXDNTX::setTXDelay(uint8_t delay)
{
m_txDelay = 300U + uint16_t(delay) * 6U; // 500ms + tx delay
}
uint16_t CNXDNTX::getSpace() const
{
return m_buffer.getSpace() / NXDN_FRAME_LENGTH_BYTES;
}

@ -0,0 +1,50 @@
/*
* Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(NXDNTX_H)
#define NXDNTX_H
#include "SerialRB.h"
class CNXDNTX {
public:
CNXDNTX();
uint8_t writeData(const uint8_t* data, uint8_t length);
void process();
void setTXDelay(uint8_t delay);
uint16_t getSpace() const;
private:
CSerialRB m_buffer;
uint8_t m_poBuffer[60U];
uint16_t m_poLen;
uint16_t m_poPtr;
uint16_t m_txDelay;
uint32_t m_count;
bool m_delay;
void writeByte(uint8_t c);
};
#endif

@ -1,7 +1,7 @@
/*
* Copyright (C) 2013,2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2013,2015,2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
* Copyright (C) 2016,2017 by Andy Uribe CA6JAU
* Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -55,6 +55,9 @@ const uint8_t MMDVM_P25_HDR = 0x30U;
const uint8_t MMDVM_P25_LDU = 0x31U;
const uint8_t MMDVM_P25_LOST = 0x32U;
const uint8_t MMDVM_NXDN_DATA = 0x40U;
const uint8_t MMDVM_NXDN_LOST = 0x41U;
const uint8_t MMDVM_ACK = 0x70U;
const uint8_t MMDVM_NAK = 0x7FU;
@ -201,6 +204,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
bool p25Enable = (data[1U] & 0x08U) == 0x08U;
bool nxdnEnable = (data[1U] & 0x10U) == 0x10U;
uint8_t txDelay = data[2U];
if (txDelay > 50U)
@ -208,7 +212,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
MMDVM_STATE modemState = MMDVM_STATE(data[3U]);
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25)
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN)
return 4U;
if (modemState == STATE_DSTAR && !dstarEnable)
return 4U;
@ -218,6 +222,8 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
return 4U;
if (modemState == STATE_P25 && !p25Enable)
return 4U;
if (modemState == STATE_NXDN && !nxdnEnable)
return 4U;
uint8_t colorCode = data[6U];
if (colorCode > 15U)
@ -279,7 +285,7 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
if (modemState == m_modemState)
return 0U;
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25)
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN)
return 4U;
if (modemState == STATE_DSTAR && !m_dstarEnable)
return 4U;
@ -289,7 +295,9 @@ uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
return 4U;
if (modemState == STATE_P25 && !m_p25Enable)
return 4U;
if (modemState == STATE_NXDN && !m_nxdnEnable)
return 4U;
setMode(modemState);
return 0U;
@ -367,6 +375,18 @@ void CSerialPort::setMode(MMDVM_STATE modemState)
ysfRX.reset();
cwIdTX.reset();
break;
case STATE_NXDN:
DEBUG1("Mode set to NXDN");
#if defined(DUPLEX)
dmrIdleRX.reset();
dmrRX.reset();
#endif
dmrDMORX.reset();
dstarRX.reset();
ysfRX.reset();
p25RX.reset();
cwIdTX.reset();
break;
default:
DEBUG1("Mode set to Idle");
// STATE_IDLE
@ -639,6 +659,20 @@ void CSerialPort::process()
}
break;
case MMDVM_NXDN_DATA:
if (m_nxdnEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_NXDN)
err = nxdnTX.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_NXDN);
} else {
DEBUG2("Received invalid NXDN data", err);
sendNAK(err);
}
break;
#if defined(SERIAL_REPEATER) || defined(SERIAL_REPEATER_USART1)
case MMDVM_SERIAL:
writeInt(3U, m_buffer + 3U, m_len - 3U);
@ -891,6 +925,46 @@ void CSerialPort::writeP25Lost()
writeInt(1U, reply, 3);
}
void CSerialPort::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] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_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);
}
void CSerialPort::writeNXDNLost()
{
if (m_modemState != STATE_NXDN && m_modemState != STATE_IDLE)
return;
if (!m_nxdnEnable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_NXDN_LOST;
writeInt(1U, reply, 3);
}
void CSerialPort::writeDebug(const char* text)
{
if (!m_debug)

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX
* Copyright (C) 2018 by Andy Uribe CA6JAU
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -44,6 +45,9 @@ public:
void writeP25Ldu(const uint8_t* data, uint8_t length);
void writeP25Lost();
void writeNXDNData(const uint8_t* data, uint8_t length);
void writeNXDNLost();
void writeDebug(const char* text);
void writeDebug(const char* text, int16_t n1);
void writeDebugI(const char* text, int32_t n1);

Loading…
Cancel
Save

Powered by TurnKey Linux.