From b5ca5e3839d9534a1da01e1f3a3fb84cfecf5422 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 10 Jan 2018 20:30:35 +0000 Subject: [PATCH 01/29] NXDN first cut, not working yet. --- Globals.h | 9 +- IO.cpp | 50 +++++- IO.h | 6 +- IODue.cpp | 12 +- IOSTM.cpp | 67 +++++++- IOSTM_CMSIS.cpp | 12 +- IOTeensy.cpp | 9 +- MMDVM.cpp | 10 +- MMDVM.ino | 9 +- NXDNDefines.h | 54 +++++++ NXDNRX.cpp | 405 ++++++++++++++++++++++++++++++++++++++++++++++++ NXDNRX.h | 69 +++++++++ NXDNTX.cpp | 150 ++++++++++++++++++ NXDNTX.h | 51 ++++++ SerialPort.cpp | 109 ++++++++++++- SerialPort.h | 6 +- 16 files changed, 1005 insertions(+), 23 deletions(-) create mode 100644 NXDNDefines.h create mode 100644 NXDNRX.cpp create mode 100644 NXDNRX.h create mode 100644 NXDNTX.cpp create mode 100644 NXDNTX.h diff --git a/Globals.h b/Globals.h index d671e7b..4e0f62b 100644 --- a/Globals.h +++ b/Globals.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,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 @@ -48,6 +48,7 @@ enum MMDVM_STATE { STATE_DMR = 2, STATE_YSF = 3, STATE_P25 = 4, + STATE_NXDN = 5, // Dummy states start at 90 STATE_P25CAL1K = 93, @@ -71,6 +72,8 @@ enum MMDVM_STATE { #include "YSFTX.h" #include "P25RX.h" #include "P25TX.h" +#include "NXDNRX.h" +#include "NXDNTX.h" #include "CalDStarRX.h" #include "CalDStarTX.h" #include "CalDMR.h" @@ -95,6 +98,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; @@ -120,6 +124,9 @@ extern CYSFTX ysfTX; extern CP25RX p25RX; extern CP25TX p25TX; +extern CNXDNRX nxdnRX; +extern CNXDNTX nxdnTX; + extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; diff --git a/IO.cpp b/IO.cpp index bbea122..aa6745d 100644 --- a/IO.cpp +++ b/IO.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -62,6 +62,7 @@ m_dstarTXLevel(128 * 128), m_dmrTXLevel(128 * 128), m_ysfTXLevel(128 * 128), m_p25TXLevel(128 * 128), +m_nxdnTXLevel(128 * 128), m_rxDCOffset(DC_OFFSET), m_txDCOffset(DC_OFFSET), m_ledCount(0U), @@ -114,6 +115,7 @@ void CIO::selfTest() setDMRInt(ledValue); setYSFInt(ledValue); setP25Int(ledValue); + setNXDNInt(ledValue); #endif delayInt(250); } @@ -123,6 +125,7 @@ void CIO::selfTest() setDMRInt(false); setYSFInt(false); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -130,6 +133,7 @@ void CIO::selfTest() setDMRInt(true); setYSFInt(false); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -137,6 +141,7 @@ void CIO::selfTest() setDMRInt(true); setYSFInt(true); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -144,6 +149,23 @@ void CIO::selfTest() setDMRInt(true); setYSFInt(true); setP25Int(true); + setNXDNInt(false); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(true); + + delayInt(250); + + setDStarInt(true); + setDMRInt(true); + setYSFInt(true); + setP25Int(true); + setNXDNInt(false); delayInt(250); @@ -151,6 +173,7 @@ void CIO::selfTest() setDMRInt(true); setYSFInt(true); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -158,6 +181,7 @@ void CIO::selfTest() setDMRInt(true); setYSFInt(false); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -165,6 +189,7 @@ void CIO::selfTest() setDMRInt(false); setYSFInt(false); setP25Int(false); + setNXDNInt(false); delayInt(250); @@ -172,6 +197,7 @@ void CIO::selfTest() setDMRInt(false); setYSFInt(false); setP25Int(false); + setNXDNInt(false); #endif } @@ -193,7 +219,7 @@ void CIO::process() if (m_started) { // Two seconds timeout if (m_watchdog >= 48000U) { - if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF) { + if (m_modemState == STATE_DSTAR || m_modemState == STATE_DMR || m_modemState == STATE_YSF || m_modemState == STATE_P25 || m_modemState == STATE_NXDN) { if (m_modemState == STATE_DMR && m_tx) dmrTX.setStart(false); m_modemState = STATE_IDLE; @@ -282,7 +308,7 @@ void CIO::process() } // XXX YSF should use dcSamples, but DMR not - if (m_dmrEnable || m_ysfEnable) { + if (m_dmrEnable || m_ysfEnable || m_nxdnEnable) { q15_t C4FSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE); @@ -295,6 +321,9 @@ void CIO::process() if (m_ysfEnable) ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + + if (m_nxdnEnable) + nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -332,6 +361,13 @@ void CIO::process() p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); } + } else if (m_modemState == STATE_NXDN) { + if (m_nxdnEnable) { + q15_t C4FSKVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); + + nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + } } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); @@ -371,6 +407,9 @@ void CIO::write(MMDVM_STATE mode, q15_t* samples, uint16_t length, const uint8_t case STATE_P25: txLevel = m_p25TXLevel; break; + case STATE_NXDN: + txLevel = m_nxdnTXLevel; + break; default: txLevel = m_cwIdTXLevel; break; @@ -417,10 +456,11 @@ void CIO::setMode() setDMRInt(m_modemState == STATE_DMR); setYSFInt(m_modemState == STATE_YSF); setP25Int(m_modemState == STATE_P25); + setNXDNInt(m_modemState == STATE_NXDN); #endif } -void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, int16_t txDCOffset, int16_t rxDCOffset) +void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnTXLevel, int16_t txDCOffset, int16_t rxDCOffset) { m_pttInvert = pttInvert; @@ -430,6 +470,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_dmrTXLevel = q15_t(dmrTXLevel * 128); m_ysfTXLevel = q15_t(ysfTXLevel * 128); m_p25TXLevel = q15_t(p25TXLevel * 128); + m_nxdnTXLevel = q15_t(nxdnTXLevel * 128); m_rxDCOffset = DC_OFFSET + rxDCOffset; m_txDCOffset = DC_OFFSET + txDCOffset; @@ -442,6 +483,7 @@ void CIO::setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rx m_dmrTXLevel = -m_dmrTXLevel; m_ysfTXLevel = -m_ysfTXLevel; m_p25TXLevel = -m_p25TXLevel; + m_nxdnTXLevel = -m_nxdnTXLevel; } } diff --git a/IO.h b/IO.h index 48373ec..0f1f62d 100644 --- a/IO.h +++ b/IO.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,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 @@ -42,7 +42,7 @@ public: void interrupt(); - void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, int16_t txDCOffset, int16_t rxDCOffset); + void setParameters(bool rxInvert, bool txInvert, bool pttInvert, uint8_t rxLevel, uint8_t cwIdTXLevel, uint8_t dstarTXLevel, uint8_t dmrTXLevel, uint8_t ysfTXLevel, uint8_t p25TXLevel, uint8_t nxdnLevel, int16_t txDCOffset, int16_t rxDCOffset); void getOverflow(bool& adcOverflow, bool& dacOverflow); @@ -80,6 +80,7 @@ private: q15_t m_dmrTXLevel; q15_t m_ysfTXLevel; q15_t m_p25TXLevel; + q15_t m_nxdnTXLevel; uint16_t m_rxDCOffset; uint16_t m_txDCOffset; @@ -110,6 +111,7 @@ private: void setDMRInt(bool on); void setYSFInt(bool on); void setP25Int(bool on); + void setNXDNInt(bool on); void delayInt(unsigned int dly); }; diff --git a/IODue.cpp b/IODue.cpp index 6eeeb82..7e86ae5 100644 --- a/IODue.cpp +++ b/IODue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2015 by Jim Mclaughlin KI6ZUM * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -33,6 +33,7 @@ #define PIN_DMR 17 #define PIN_YSF 18 #define PIN_P25 19 +#define PIN_NXDN 20 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -46,6 +47,7 @@ #define PIN_DMR 8 #define PIN_YSF 7 #define PIN_P25 6 +#define PIN_NXDN 5 #define ADC_CHER_Chan (1<<13) // ADC on Due pin A11 - Due AD13 - (1 << 13) #define ADC_ISR_EOC_Chan ADC_ISR_EOC13 #define ADC_CDR_Chan 13 @@ -61,6 +63,7 @@ #define PIN_DMR 8 #define PIN_YSF 7 #define PIN_P25 6 +#define PIN_NXDN 5 #define ADC_CHER_Chan (1<<7) // ADC on Due pin A0 - Due AD7 - (1 << 7) #define ADC_ISR_EOC_Chan ADC_ISR_EOC7 #define ADC_CDR_Chan 7 @@ -95,6 +98,7 @@ void CIO::initInt() pinMode(PIN_DMR, OUTPUT); pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); + pinMode(PIN_NXDN, OUTPUT); #endif } @@ -226,9 +230,15 @@ void CIO::setP25Int(bool on) digitalWrite(PIN_P25, on ? HIGH : LOW); } +void CIO::setNXDNInt(bool on) +{ + digitalWrite(PIN_NXDN, on ? HIGH : LOW); +} + void CIO::delayInt(unsigned int dly) { delay(dly); } #endif + diff --git a/IOSTM.cpp b/IOSTM.cpp index 3b2081c..6c326c5 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU - * Copyright (C) 2017 by Jonathan Naylor G4KLX + * Copyright (C) 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 @@ -37,6 +37,7 @@ DSTAR PD12 output P1 Pin44 DMR PD13 output P1 Pin45 YSF PD14 output P1 Pin46 P25 PD11 output P1 Pin43 +NXDN PD10 output P1 Pin42 RX PA0 analog input P1 Pin12 RSSI PA1 analog input P1 Pin11 @@ -65,6 +66,10 @@ EXT_CLK PA15 input P2 Pin40 #define PORT_P25 GPIOD #define RCC_Per_P25 RCC_AHB1Periph_GPIOD +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOD +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOD + #define PIN_DSTAR GPIO_Pin_12 #define PORT_DSTAR GPIOD #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOD @@ -107,6 +112,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output +P25 PC10 output RX PA0 analog input RSSI PA7 analog input @@ -135,6 +141,10 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC +#define PIN_P25 GPIO_Pin_10 +#define PORT_P25 GPIOC +#define RCC_Per_P25 RCC_AHB1Periph_GPIOC + #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC @@ -177,6 +187,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output +NXDN PC10 output RX PA0 analog input RSSI PA7 analog input @@ -205,6 +216,10 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOC +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOC + #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC @@ -247,6 +262,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output +NXDN PC10 output RX PA0 analog input RSSI PA7 analog input @@ -275,6 +291,10 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOC +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOC + #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOC @@ -319,11 +339,13 @@ DSTAR PB10 output CN10 Pin25 DMR PB4 output CN10 Pin27 YSF PB5 output CN10 Pin29 P25 PB3 output CN10 Pin31 +NXDN PB2 output CN10 Pin32 MDSTAR PC4 output CN10 Pin34 MDMR PC5 output CN10 Pin6 MYSF PC2 output CN7 Pin35 MP25 PC3 output CN7 Pin37 +MNXDN PC6 output CN10 Pin?? RX PA0 analog input CN7 Pin28 RSSI PA1 analog input CN7 Pin30 @@ -352,6 +374,10 @@ EXT_CLK PA15 input CN7 Pin17 #define PORT_P25 GPIOB #define RCC_Per_P25 RCC_AHB1Periph_GPIOB +#define PIN_NXDN GPIO_Pin_2 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB + #define PIN_DSTAR GPIO_Pin_10 #define PORT_DSTAR GPIOB #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB @@ -369,6 +395,10 @@ EXT_CLK PA15 input CN7 Pin17 #define PORT_MP25 GPIOC #define RCC_Per_MP25 RCC_AHB1Periph_GPIOC +#define PIN_MNXDN GPIO_Pin_1 +#define PORT_MNXDN GPIOC +#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC + #define PIN_MDSTAR GPIO_Pin_4 #define PORT_MDSTAR GPIOC #define RCC_Per_MDSTAR RCC_AHB1Periph_GPIOC @@ -412,6 +442,7 @@ DSTAR PA1 output CN8 Pin2 DMR PA4 output CN8 Pin3 YSF PB0 output CN8 Pin4 P25 PC1 output CN8 Pin5 +NXDN PC? output CN8 Pin6 RX PA0 analog input CN8 Pin1 RSSI PC0 analog input CN8 Pin6 @@ -440,6 +471,10 @@ EXT_CLK PB8 input CN5 Pin10 #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_6 +#define PORT_NXDN GPIOC +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOC + #define PIN_DSTAR GPIO_Pin_1 #define PORT_DSTAR GPIOA #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOA @@ -486,11 +521,13 @@ DSTAR PB10 output CN12 Pin25 DMR PB4 output CN12 Pin27 YSF PB5 output CN12 Pin29 P25 PB3 output CN12 Pin31 +NXDN PB2 output CN12 Pin32 MDSTAR PC4 output CN12 Pin34 MDMR PC5 output CN12 Pin6 MYSF PC2 output CN11 Pin35 MP25 PC3 output CN11 Pin37 +MNXDN PC6 output CN11 Pin?? RX PA0 analog input CN11 Pin28 RSSI PA1 analog input CN11 Pin30 @@ -519,6 +556,10 @@ EXT_CLK PA15 input CN11 Pin17 #define PORT_P25 GPIOB #define RCC_Per_P25 RCC_AHB1Periph_GPIOB +#define PIN_NXDN GPIO_Pin_2 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB + #define PIN_DSTAR GPIO_Pin_10 #define PORT_DSTAR GPIOB #define RCC_Per_DSTAR RCC_AHB1Periph_GPIOB @@ -536,6 +577,10 @@ EXT_CLK PA15 input CN11 Pin17 #define PORT_MP25 GPIOC #define RCC_Per_MP25 RCC_AHB1Periph_GPIOC +#define PIN_MNXDN GPIO_Pin_6 +#define PORT_MNXDN GPIOC +#define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC + #define PIN_MDSTAR GPIO_Pin_4 #define PORT_MDSTAR GPIOC #define RCC_Per_MDSTAR RCC_AHB1Periph_GPIOC @@ -640,6 +685,12 @@ void CIO::initInt() GPIO_InitStruct.GPIO_Pin = PIN_P25; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(PORT_P25, &GPIO_InitStruct); + + // NXDN pin + RCC_AHB1PeriphClockCmd(RCC_Per_NXDN, ENABLE); + GPIO_InitStruct.GPIO_Pin = PIN_NXDN; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(PORT_NXDN, &GPIO_InitStruct); #endif #if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO) @@ -666,6 +717,12 @@ void CIO::initInt() GPIO_InitStruct.GPIO_Pin = PIN_MP25; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(PORT_MP25, &GPIO_InitStruct); + + // NXDN mode pin + RCC_AHB1PeriphClockCmd(RCC_Per_MNXDN, ENABLE); + GPIO_InitStruct.GPIO_Pin = PIN_MNXDN; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; + GPIO_Init(PORT_MNXDN, &GPIO_InitStruct); #endif } @@ -905,6 +962,14 @@ void CIO::setP25Int(bool on) #endif } +void CIO::setNXDNInt(bool on) +{ + GPIO_WriteBit(PORT_NXDN, PIN_NXDN, on ? Bit_SET : Bit_RESET); +#if defined(STM32F4_NUCLEO_MODE_PINS) && defined(STM32F4_NUCLEO_MORPHO_HEADER) && defined(STM32F4_NUCLEO) + GPIO_WriteBit(PORT_MNXDN, PIN_MNXDN, on ? Bit_SET : Bit_RESET); +#endif +} + // Simple delay function for STM32 // Example from: http://thehackerworkshop.com/?p=1209 void CIO::delayInt(unsigned int dly) diff --git a/IOSTM_CMSIS.cpp b/IOSTM_CMSIS.cpp index 4ae144d..997c92b 100644 --- a/IOSTM_CMSIS.cpp +++ b/IOSTM_CMSIS.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU - * Copyright (C) 2017 by Jonathan Naylor G4KLX + * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2017 by Wojciech Krutnik N0CALL * * This program is free software; you can redistribute it and/or modify @@ -39,6 +39,7 @@ DSTAR PB7 output DMR PB6 output YSF PB8 output P25 PB9 output +NXDN PB10 output RX PB0 analog input (ADC1_8) RSSI PB1 analog input (ADC2_9) @@ -76,6 +77,9 @@ USART1_RXD PA10 input (AF) #define PIN_P25 9 #define PORT_P25 GPIOB #define BB_P25 *((bitband_t)BITBAND_PERIPH(&PORT_P25->ODR, PIN_P25)) +#define PIN_NXDN 10 +#define PORT_NXDN GPIOB +#define BB_NXDN *((bitband_t)BITBAND_PERIPH(&PORT_NXDN->ODR, PIN_NXDN)) #define PIN_RX 0 #define PIN_RX_ADC_CH 8 @@ -214,6 +218,7 @@ static inline void GPIOInit() GPIOConfigPin(PORT_DMR, PIN_DMR, GPIO_CRL_MODE0_1); GPIOConfigPin(PORT_YSF, PIN_YSF, GPIO_CRL_MODE0_1); GPIOConfigPin(PORT_P25, PIN_P25, GPIO_CRL_MODE0_1); + GPIOConfigPin(PORT_NXDN, PIN_NXDN, GPIO_CRL_MODE0_1); GPIOConfigPin(PORT_RX, PIN_RX, 0); #if defined(SEND_RSSI_DATA) @@ -424,6 +429,11 @@ void CIO::setP25Int(bool on) BB_P25 = !!on; } +void CIO::setNXDNInt(bool on) +{ + BB_NXDN = !!on; +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/IOTeensy.cpp b/IOTeensy.cpp index de0651a..0f551a0 100644 --- a/IOTeensy.cpp +++ b/IOTeensy.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * 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 @@ -34,6 +34,7 @@ #define PIN_DMR 10 #define PIN_YSF 11 #define PIN_P25 12 +#define PIN_NXDN 8 #define PIN_ADC 5 // A0, Pin 14 #define PIN_RSSI 4 // Teensy 3.5/3.6, A16, Pin 35. Teensy 3.1/3.2, A17, Pin 28 @@ -63,6 +64,7 @@ void CIO::initInt() pinMode(PIN_DMR, OUTPUT); pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); + pinMode(PIN_NXDN, OUTPUT); #endif } @@ -210,6 +212,11 @@ void CIO::setP25Int(bool on) digitalWrite(PIN_P25, on ? HIGH : LOW); } +void CIO::setNXDNInt(bool on) +{ + digitalWrite(PIN_NXDN, on ? HIGH : LOW); +} + void CIO::delayInt(unsigned int dly) { delay(dly); diff --git a/MMDVM.cpp b/MMDVM.cpp index 37c0a41..a2e9433 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Mathis Schmieder DB9MAT * Copyright (C) 2016 by Colin Durbridge G4EML * @@ -30,6 +30,7 @@ bool m_dstarEnable = true; bool m_dmrEnable = true; bool m_ysfEnable = true; bool m_p25Enable = true; +bool m_nxdnEnable = true; bool m_duplex = true; @@ -52,6 +53,9 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CNXDNRX nxdnRX; +CNXDNTX nxdnTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -91,6 +95,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_DSTARCAL) calDStarTX.process(); @@ -113,4 +120,3 @@ int main() } #endif - diff --git a/MMDVM.ino b/MMDVM.ino index d132f2a..af74591 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ bool m_dstarEnable = true; bool m_dmrEnable = true; bool m_ysfEnable = true; bool m_p25Enable = true; +bool m_nxdnEnable = true; bool m_duplex = true; @@ -49,6 +50,9 @@ CYSFTX ysfTX; CP25RX p25RX; CP25TX p25TX; +CNXDNRX nxdnRX; +CNXDNTX nxdnTX; + CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; @@ -88,6 +92,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_DSTARCAL) calDStarTX.process(); diff --git a/NXDNDefines.h b/NXDNDefines.h new file mode 100644 index 0000000..1253b8e --- /dev/null +++ b/NXDNDefines.h @@ -0,0 +1,54 @@ +/* + * 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 = 5U; // At 24 kHz sample rate + +const unsigned int NXDN_FRAME_LENGTH_BYTES = 120U; +const unsigned int NXDN_FRAME_LENGTH_BITS = NXDN_FRAME_LENGTH_BYTES * 8U; +const unsigned int NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BYTES * 4U; +const unsigned int NXDN_FRAME_LENGTH_SAMPLES = NXDN_FRAME_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; + +const unsigned int NXDN_SYNC_LENGTH_BYTES = 5U; +const unsigned int NXDN_SYNC_LENGTH_BITS = NXDN_SYNC_LENGTH_BYTES * 8U; +const unsigned int NXDN_SYNC_LENGTH_SYMBOLS = NXDN_SYNC_LENGTH_BYTES * 4U; +const unsigned int NXDN_SYNC_LENGTH_SAMPLES = NXDN_SYNC_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; + +const unsigned int NXDN_FICH_LENGTH_BITS = 200U; +const unsigned int NXDN_FICH_LENGTH_SYMBOLS = 100U; +const unsigned int NXDN_FICH_LENGTH_SAMPLES = NXDN_FICH_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; + +const uint8_t NXDN_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; +const uint8_t NXDN_SYNC_BYTES_LENGTH = 5U; + +const uint64_t NXDN_SYNC_BITS = 0x000000D471C9634DU; +const uint64_t NXDN_SYNC_BITS_MASK = 0x000000FFFFFFFFFFU; + +// D 4 7 1 C 9 6 3 4 D +// 11 01 01 00 01 11 00 01 11 00 10 01 01 10 00 11 01 00 11 01 +// -3 +3 +3 +1 +3 -3 +1 +3 -3 +1 -1 +3 +3 -1 +3 -3 +3 +1 -3 +3 + +const int8_t NXDN_SYNC_SYMBOLS_VALUES[] = {-3, +3, +3, +1, +3, -3, +1, +3, -3, +1, -1, +3, +3, -1, +3, -3, +3, +1, -3, +3}; + +const uint32_t NXDN_SYNC_SYMBOLS = 0x0007B5ADU; +const uint32_t NXDN_SYNC_SYMBOLS_MASK = 0x000FFFFFU; + +#endif + diff --git a/NXDNRX.cpp b/NXDNRX.cpp new file mode 100644 index 0000000..360a3a8 --- /dev/null +++ b/NXDNRX.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2009-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. + */ + +#include "Config.h" +#include "Globals.h" +#include "NXDNRX.h" +#include "Utils.h" + +const q15_t SCALING_FACTOR = 18750; // Q15(0.55) + +const uint8_t MAX_SYNC_BIT_START_ERRS = 2U; +const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U; + +const uint8_t MAX_SYNC_SYMBOLS_ERRS = 3U; + +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]) + +const uint8_t NOAVEPTR = 99U; + +const uint16_t NOENDPTR = 9999U; + +const unsigned int MAX_SYNC_FRAMES = 4U + 1U; + +CNXDNRX::CNXDNRX() : +m_state(NXDNRXS_NONE), +m_bitBuffer(), +m_buffer(), +m_bitPtr(0U), +m_dataPtr(0U), +m_startPtr(NOENDPTR), +m_endPtr(NOENDPTR), +m_syncPtr(NOENDPTR), +m_minSyncPtr(NOENDPTR), +m_maxSyncPtr(NOENDPTR), +m_maxCorr(0), +m_lostCount(0U), +m_countdown(0U), +m_centre(), +m_centreVal(0), +m_threshold(), +m_thresholdVal(0), +m_averagePtr(NOAVEPTR), +m_rssiAccum(0U), +m_rssiCount(0U) +{ +} + +void CNXDNRX::reset() +{ + m_state = NXDNRXS_NONE; + m_dataPtr = 0U; + m_bitPtr = 0U; + m_maxCorr = 0; + m_averagePtr = NOAVEPTR; + m_startPtr = NOENDPTR; + m_endPtr = NOENDPTR; + m_syncPtr = NOENDPTR; + m_minSyncPtr = NOENDPTR; + m_maxSyncPtr = NOENDPTR; + m_centreVal = 0; + m_thresholdVal = 0; + m_lostCount = 0U; + m_countdown = 0U; + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + +void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length) +{ + for (uint8_t i = 0U; i < length; i++) { + q15_t sample = samples[i]; + + m_rssiAccum += rssi[i]; + m_rssiCount++; + + m_bitBuffer[m_bitPtr] <<= 1; + if (sample < 0) + m_bitBuffer[m_bitPtr] |= 0x01U; + + m_buffer[m_dataPtr] = sample; + + switch (m_state) { + case NXDNRXS_DATA: + processData(sample); + break; + default: + processNone(sample); + break; + } + + m_dataPtr++; + if (m_dataPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_dataPtr = 0U; + + m_bitPtr++; + if (m_bitPtr >= NXDN_RADIO_SYMBOL_LENGTH) + m_bitPtr = 0U; + } +} + +void CNXDNRX::processNone(q15_t sample) +{ + bool ret = correlateSync(); + if (ret) { + // On the first sync, start the countdown to the state change + if (m_countdown == 0U) { + m_rssiAccum = 0U; + m_rssiCount = 0U; + + io.setDecode(true); + io.setADCDetection(true); + + m_averagePtr = NOAVEPTR; + + m_countdown = 5U; + } + } + + if (m_countdown > 0U) + m_countdown--; + + if (m_countdown == 1U) { + m_minSyncPtr = m_syncPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxSyncPtr = m_syncPtr + 1U; + if (m_maxSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_state = NXDNRXS_DATA; + m_countdown = 0U; + } +} + +void CNXDNRX::processData(q15_t sample) +{ + if (m_minSyncPtr < m_maxSyncPtr) { + if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr) + correlateSync(); + } else { + if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr) + correlateSync(); + } + + if (m_dataPtr == m_endPtr) { + // Only update the centre and threshold if they are from a good sync + if (m_lostCount == MAX_SYNC_FRAMES) { + m_minSyncPtr = m_syncPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxSyncPtr = m_syncPtr + 1U; + if (m_maxSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + } + + calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS); + + DEBUG4("NXDNRX: sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U]; + samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + // 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); + io.setADCDetection(false); + + serial.writeNXDNLost(); + + m_state = NXDNRXS_NONE; + m_endPtr = NOENDPTR; + m_averagePtr = NOAVEPTR; + m_countdown = 0U; + m_maxCorr = 0; + } else { + frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U; + writeRSSIData(frame); + m_maxCorr = 0; + } + } +} + +bool CNXDNRX::correlateSync() +{ + if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_SYNC_SYMBOLS_MASK) ^ NXDN_SYNC_SYMBOLS) <= MAX_SYNC_SYMBOLS_ERRS) { + uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_FRAME_LENGTH_SAMPLES; + + q31_t corr = 0; + q15_t min = 16000; + q15_t max = -16000; + + for (uint8_t i = 0U; i < NXDN_SYNC_LENGTH_SYMBOLS; i++) { + q15_t val = m_buffer[ptr]; + + if (val > max) + max = val; + if (val < min) + min = val; + + switch (NXDN_SYNC_SYMBOLS_VALUES[i]) { + case +3: + corr -= (val + val + val); + break; + case +1: + corr -= val; + break; + case -1: + corr += val; + break; + default: // -3 + corr += (val + val + val); + break; + } + + ptr += NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_FRAME_LENGTH_SAMPLES; + } + + if (corr > m_maxCorr) { + if (m_averagePtr == NOAVEPTR) { + m_centreVal = (max + min) >> 1; + + q31_t v1 = (max - m_centreVal) * SCALING_FACTOR; + m_thresholdVal = q15_t(v1 >> 15); + } + + uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES) + startPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + uint8_t sync[NXDN_SYNC_BYTES_LENGTH]; + samplesToBits(startPtr, NXDN_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); + + uint8_t maxErrs; + if (m_state == NXDNRXS_NONE) + maxErrs = MAX_SYNC_BIT_START_ERRS; + else + maxErrs = MAX_SYNC_BIT_RUN_ERRS; + + uint8_t errs = 0U; + for (uint8_t i = 0U; i < NXDN_SYNC_BYTES_LENGTH; i++) + errs += countBits8(sync[i] ^ NXDN_SYNC_BYTES[i]); + + if (errs <= maxErrs) { + m_maxCorr = corr; + m_lostCount = MAX_SYNC_FRAMES; + m_syncPtr = m_dataPtr; + + m_startPtr = startPtr; + + m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES - 1U; + if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + return true; + } + } + } + + return false; +} + +void CNXDNRX::calculateLevels(uint16_t start, uint16_t count) +{ + q15_t maxPos = -16000; + q15_t minPos = 16000; + q15_t maxNeg = 16000; + q15_t minNeg = -16000; + + for (uint16_t i = 0U; i < count; i++) { + q15_t sample = m_buffer[start]; + + if (sample > 0) { + if (sample > maxPos) + maxPos = sample; + if (sample < minPos) + minPos = sample; + } else { + if (sample < maxNeg) + maxNeg = sample; + if (sample > minNeg) + minNeg = sample; + } + + start += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_FRAME_LENGTH_SAMPLES; + } + + q15_t posThresh = (maxPos + minPos) >> 1; + q15_t negThresh = (maxNeg + minNeg) >> 1; + + q15_t centre = (posThresh + negThresh) >> 1; + + q15_t threshold = posThresh - centre; + + DEBUG5("NXDNRX: pos/neg/centre/threshold", posThresh, negThresh, centre, threshold); + + if (m_averagePtr == NOAVEPTR) { + for (uint8_t i = 0U; i < 16U; i++) { + m_centre[i] = centre; + m_threshold[i] = threshold; + } + + m_averagePtr = 0U; + } else { + m_centre[m_averagePtr] = centre; + m_threshold[m_averagePtr] = threshold; + + m_averagePtr++; + if (m_averagePtr >= 16U) + m_averagePtr = 0U; + } + + m_centreVal = 0; + m_thresholdVal = 0; + + for (uint8_t i = 0U; i < 16U; i++) { + m_centreVal += m_centre[i]; + m_thresholdVal += m_threshold[i]; + } + + m_centreVal >>= 4; + m_thresholdVal >>= 4; +} + +void CNXDNRX::samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold) +{ + for (uint16_t i = 0U; i < count; i++) { + q15_t sample = m_buffer[start] - centre; + + if (sample < -threshold) { + WRITE_BIT1(buffer, offset, false); + offset++; + WRITE_BIT1(buffer, offset, true); + offset++; + } else if (sample < 0) { + WRITE_BIT1(buffer, offset, false); + offset++; + WRITE_BIT1(buffer, offset, false); + offset++; + } else if (sample < threshold) { + WRITE_BIT1(buffer, offset, true); + offset++; + WRITE_BIT1(buffer, offset, false); + offset++; + } else { + WRITE_BIT1(buffer, offset, true); + offset++; + WRITE_BIT1(buffer, offset, true); + offset++; + } + + start += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_FRAME_LENGTH_SAMPLES; + } +} + +void CNXDNRX::writeRSSIData(uint8_t* data) +{ +#if defined(SEND_RSSI_DATA) + if (m_rssiCount > 0U) { + uint16_t rssi = m_rssiAccum / m_rssiCount; + + data[121U] = (rssi >> 8) & 0xFFU; + data[122U] = (rssi >> 0) & 0xFFU; + + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 3U); + } else { + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); + } +#else + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); +#endif + + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + diff --git a/NXDNRX.h b/NXDNRX.h new file mode 100644 index 0000000..7fc81f6 --- /dev/null +++ b/NXDNRX.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015,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(NXDNRX_H) +#define NXDNRX_H + +#include "Config.h" +#include "NXDNDefines.h" + +enum NXDNRX_STATE { + NXDNRXS_NONE, + NXDNRXS_DATA +}; + +class CNXDNRX { +public: + CNXDNRX(); + + void samples(const q15_t* samples, uint16_t* rssi, uint8_t length); + + void reset(); + +private: + NXDNRX_STATE m_state; + uint32_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; + q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES]; + uint16_t m_bitPtr; + uint16_t m_dataPtr; + uint16_t m_startPtr; + uint16_t m_endPtr; + uint16_t m_syncPtr; + uint16_t m_minSyncPtr; + uint16_t m_maxSyncPtr; + q31_t m_maxCorr; + uint16_t m_lostCount; + uint8_t m_countdown; + q15_t m_centre[16U]; + q15_t m_centreVal; + q15_t m_threshold[16U]; + q15_t m_thresholdVal; + uint8_t m_averagePtr; + uint32_t m_rssiAccum; + uint16_t m_rssiCount; + + void processNone(q15_t sample); + void processData(q15_t sample); + bool correlateSync(); + void calculateLevels(uint16_t start, uint16_t count); + void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold); + void writeRSSIData(uint8_t* data); +}; + +#endif + diff --git a/NXDNTX.cpp b/NXDNTX.cpp new file mode 100644 index 0000000..d450e98 --- /dev/null +++ b/NXDNTX.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * Copyright (C) 2017 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" + +// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442, + -5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442, + -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L + +const q15_t NXDN_LEVELA = 1893; +const q15_t NXDN_LEVELB = 631; +const q15_t NXDN_LEVELC = -631; +const q15_t NXDN_LEVELD = -1893; + +const uint8_t NXDN_START_SYNC = 0x77U; +const uint8_t NXDN_END_SYNC = 0xFFU; + +CNXDNTX::CNXDNTX() : +m_buffer(1500U), +m_modFilter(), +m_modState(), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(240U) // 200ms +{ + ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); + + m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH; + m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN; + m_modFilter.pCoeffs = RRC_0_2_FILTER; + m_modFilter.pState = m_modState; +} + +void CNXDNTX::process() +{ + if (m_buffer.getData() == 0U && m_poLen == 0U) + return; + + if (m_poLen == 0U) { + if (!m_tx) { + for (uint16_t i = 0U; i < m_txDelay; i++) + m_poBuffer[m_poLen++] = NXDN_START_SYNC; + } else { + for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { + uint8_t c = m_buffer.get(); + m_poBuffer[m_poLen++] = c; + } + } + + m_poPtr = 0U; + } + + if (m_poLen > 0U) { + uint16_t space = io.getSpace(); + + while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) { + uint8_t c = m_poBuffer[m_poPtr++]; + writeByte(c); + + space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + 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) +{ + q15_t inBuffer[4U]; + q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + + const uint8_t MASK = 0xC0U; + + for (uint8_t i = 0U; i < 4U; i++, c <<= 2) { + switch (c & MASK) { + case 0xC0U: + inBuffer[i] = NXDN_LEVELA; + break; + case 0x80U: + inBuffer[i] = NXDN_LEVELB; + break; + case 0x00U: + inBuffer[i] = NXDN_LEVELC; + break; + default: + inBuffer[i] = NXDN_LEVELD; + break; + } + } + + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U); + + io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); +} + +void CNXDNTX::setTXDelay(uint8_t delay) +{ + m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay + + if (m_txDelay > 1200U) + m_txDelay = 1200U; +} + +uint8_t CNXDNTX::getSpace() const +{ + return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES; +} + diff --git a/NXDNTX.h b/NXDNTX.h new file mode 100644 index 0000000..f98cf48 --- /dev/null +++ b/NXDNTX.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015,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(NXDNTX_H) +#define NXDNTX_H + +#include "Config.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); + + uint8_t getSpace() const; + +private: + CSerialRB m_buffer; + arm_fir_interpolate_instance_q15 m_modFilter; + q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare + uint8_t m_poBuffer[1200U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint16_t m_txDelay; + + void writeByte(uint8_t c); +}; + +#endif + diff --git a/SerialPort.cpp b/SerialPort.cpp index d734013..231ff30 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013,2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2013,2015,2016,2017,2018 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * * This program is free software; you can redistribute it and/or modify @@ -60,6 +60,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; @@ -73,9 +76,9 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #if defined(EXTERNAL_OSC) -#define DESCRIPTION "MMDVM 20170501 TCXO (D-Star/DMR/System Fusion/P25/RSSI/CW Id)" +#define DESCRIPTION "MMDVM 20170501 TCXO (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" #else -#define DESCRIPTION "MMDVM 20170501 (D-Star/DMR/System Fusion/P25/RSSI/CW Id)" +#define DESCRIPTION "MMDVM 20170501 (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" #endif #if defined(GITVERSION) @@ -143,6 +146,8 @@ void CSerialPort::getStatus() reply[3U] |= 0x04U; if (m_p25Enable) reply[3U] |= 0x08U; + if (m_nxdnEnable) + reply[3U] |= 0x10U; reply[4U] = uint8_t(m_modemState); @@ -197,7 +202,12 @@ void CSerialPort::getStatus() else reply[10U] = 0U; - writeInt(1U, reply, 11); + if (m_nxdnEnable) + reply[11U] = nxdnTX.getSpace(); + else + reply[11U] = 0U; + + writeInt(1U, reply, 12); } void CSerialPort::getVersion() @@ -221,7 +231,7 @@ void CSerialPort::getVersion() uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) { - if (length < 15U) + if (length < 16U) return 4U; bool rxInvert = (data[0U] & 0x01U) == 0x01U; @@ -236,6 +246,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) @@ -243,7 +254,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 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -253,6 +264,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 rxLevel = data[4U]; @@ -271,18 +284,22 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) int16_t txDCOffset = int16_t(data[13U]) - 128; int16_t rxDCOffset = int16_t(data[14U]) - 128; + uint8_t nxdnTXLevel = data[15U]; + m_modemState = modemState; m_dstarEnable = dstarEnable; m_dmrEnable = dmrEnable; m_ysfEnable = ysfEnable; m_p25Enable = p25Enable; + m_nxdnEnable = nxdnEnable; m_duplex = !simplex; dstarTX.setTXDelay(txDelay); ysfTX.setTXDelay(txDelay); p25TX.setTXDelay(txDelay); dmrDMOTX.setTXDelay(txDelay); + nxdnTX.setTXDelay(txDelay); dmrTX.setColorCode(colorCode); dmrRX.setColorCode(colorCode); @@ -292,7 +309,7 @@ uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length) ysfTX.setLoDev(ysfLoDev); - io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, txDCOffset, rxDCOffset); + io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, txDCOffset, rxDCOffset); io.start(); @@ -309,7 +326,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 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -319,6 +336,8 @@ 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); @@ -333,6 +352,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_DSTAR: @@ -342,6 +362,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_YSF: @@ -351,6 +372,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); dstarRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_P25: @@ -360,6 +382,17 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dmrRX.reset(); dstarRX.reset(); ysfRX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_NXDN: + DEBUG1("Mode set to NXDN"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); cwIdTX.reset(); break; case STATE_DSTARCAL: @@ -370,6 +403,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_DMRCAL: @@ -380,6 +414,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_RSSICAL: @@ -390,6 +425,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_LFCAL: @@ -400,6 +436,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_DMRCAL1K: @@ -410,6 +447,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; case STATE_P25CAL1K: @@ -420,6 +458,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; default: @@ -682,6 +721,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) case MMDVM_SERIAL: { for (uint8_t i = 3U; i < m_len; i++) @@ -949,6 +1002,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::writeCalData(const uint8_t* data, uint8_t length) { if (m_modemState != STATE_DSTARCAL) diff --git a/SerialPort.h b/SerialPort.h index 69ce699..728b6b3 100644 --- a/SerialPort.h +++ b/SerialPort.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,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 @@ -47,6 +47,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 writeCalData(const uint8_t* data, uint8_t length); void writeRSSIData(const uint8_t* data, uint8_t length); @@ -80,3 +83,4 @@ private: }; #endif + From 9b5bbee265380b1452a1a04010a10ea0c4e1e637 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 10 Jan 2018 21:39:29 +0000 Subject: [PATCH 02/29] Add actual NXDN values. --- NXDNDefines.h | 38 ++++++++++----------- NXDNRX.cpp | 92 +++++++++++++++++++++++++-------------------------- NXDNRX.h | 10 +++--- NXDNTX.cpp | 8 ++--- 4 files changed, 72 insertions(+), 76 deletions(-) diff --git a/NXDNDefines.h b/NXDNDefines.h index 1253b8e..956ffaf 100644 --- a/NXDNDefines.h +++ b/NXDNDefines.h @@ -21,34 +21,30 @@ const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate -const unsigned int NXDN_FRAME_LENGTH_BYTES = 120U; -const unsigned int NXDN_FRAME_LENGTH_BITS = NXDN_FRAME_LENGTH_BYTES * 8U; -const unsigned int NXDN_FRAME_LENGTH_SYMBOLS = NXDN_FRAME_LENGTH_BYTES * 4U; +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_SYNC_LENGTH_BYTES = 5U; -const unsigned int NXDN_SYNC_LENGTH_BITS = NXDN_SYNC_LENGTH_BYTES * 8U; -const unsigned int NXDN_SYNC_LENGTH_SYMBOLS = NXDN_SYNC_LENGTH_BYTES * 4U; -const unsigned int NXDN_SYNC_LENGTH_SAMPLES = NXDN_SYNC_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 unsigned int NXDN_FICH_LENGTH_BITS = 200U; -const unsigned int NXDN_FICH_LENGTH_SYMBOLS = 100U; -const unsigned int NXDN_FICH_LENGTH_SAMPLES = NXDN_FICH_LENGTH_SYMBOLS * NXDN_RADIO_SYMBOL_LENGTH; +const uint8_t NXDN_FSW_BYTES[] = {0x0CU, 0xDFU, 0x59U}; +const uint8_t NXDN_FSW_BYTES_MASK[] = {0x0FU, 0xFFU, 0xFFU}; +const uint8_t NXDN_FSW_BYTES_LENGTH = 3U; -const uint8_t NXDN_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; -const uint8_t NXDN_SYNC_BYTES_LENGTH = 5U; +const uint32_t NXDN_FSW_BITS = 0x000CDF59U; +const uint32_t NXDN_FSW_BITS_MASK = 0x000FFFFFU; -const uint64_t NXDN_SYNC_BITS = 0x000000D471C9634DU; -const uint64_t NXDN_SYNC_BITS_MASK = 0x000000FFFFFFFFFFU; +// 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 -// D 4 7 1 C 9 6 3 4 D -// 11 01 01 00 01 11 00 01 11 00 10 01 01 10 00 11 01 00 11 01 -// -3 +3 +3 +1 +3 -3 +1 +3 -3 +1 -1 +3 +3 -1 +3 -3 +3 +1 -3 +3 +const int8_t NXDN_FSW_SYMBOLS_VALUES[] = {-3, +1, -3, +3, -3, -3, +3, +3, -1, +3}; -const int8_t NXDN_SYNC_SYMBOLS_VALUES[] = {-3, +3, +3, +1, +3, -3, +1, +3, -3, +1, -1, +3, +3, -1, +3, -3, +3, +1, -3, +3}; - -const uint32_t NXDN_SYNC_SYMBOLS = 0x0007B5ADU; -const uint32_t NXDN_SYNC_SYMBOLS_MASK = 0x000FFFFFU; +const uint16_t NXDN_FSW_SYMBOLS = 0x014DU; +const uint16_t NXDN_FSW_SYMBOLS_MASK = 0x03FFU; #endif diff --git a/NXDNRX.cpp b/NXDNRX.cpp index 360a3a8..6087579 100644 --- a/NXDNRX.cpp +++ b/NXDNRX.cpp @@ -23,10 +23,10 @@ const q15_t SCALING_FACTOR = 18750; // Q15(0.55) -const uint8_t MAX_SYNC_BIT_START_ERRS = 2U; -const uint8_t MAX_SYNC_BIT_RUN_ERRS = 4U; +const uint8_t MAX_FSW_BIT_START_ERRS = 2U; +const uint8_t MAX_FSW_BIT_RUN_ERRS = 4U; -const uint8_t MAX_SYNC_SYMBOLS_ERRS = 3U; +const uint8_t MAX_FSW_SYMBOLS_ERRS = 3U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -36,7 +36,7 @@ const uint8_t NOAVEPTR = 99U; const uint16_t NOENDPTR = 9999U; -const unsigned int MAX_SYNC_FRAMES = 4U + 1U; +const unsigned int MAX_FSW_FRAMES = 4U + 1U; CNXDNRX::CNXDNRX() : m_state(NXDNRXS_NONE), @@ -46,9 +46,9 @@ m_bitPtr(0U), m_dataPtr(0U), m_startPtr(NOENDPTR), m_endPtr(NOENDPTR), -m_syncPtr(NOENDPTR), -m_minSyncPtr(NOENDPTR), -m_maxSyncPtr(NOENDPTR), +m_fswPtr(NOENDPTR), +m_minFSWPtr(NOENDPTR), +m_maxFSWPtr(NOENDPTR), m_maxCorr(0), m_lostCount(0U), m_countdown(0U), @@ -71,9 +71,9 @@ void CNXDNRX::reset() m_averagePtr = NOAVEPTR; m_startPtr = NOENDPTR; m_endPtr = NOENDPTR; - m_syncPtr = NOENDPTR; - m_minSyncPtr = NOENDPTR; - m_maxSyncPtr = NOENDPTR; + m_fswPtr = NOENDPTR; + m_minFSWPtr = NOENDPTR; + m_maxFSWPtr = NOENDPTR; m_centreVal = 0; m_thresholdVal = 0; m_lostCount = 0U; @@ -117,7 +117,7 @@ void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length) void CNXDNRX::processNone(q15_t sample) { - bool ret = correlateSync(); + bool ret = correlateFSW(); if (ret) { // On the first sync, start the countdown to the state change if (m_countdown == 0U) { @@ -137,13 +137,13 @@ void CNXDNRX::processNone(q15_t sample) m_countdown--; if (m_countdown == 1U) { - m_minSyncPtr = m_syncPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; - if (m_minSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_minSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - m_maxSyncPtr = m_syncPtr + 1U; - if (m_maxSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_maxSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; m_state = NXDNRXS_DATA; m_countdown = 0U; @@ -152,29 +152,29 @@ void CNXDNRX::processNone(q15_t sample) void CNXDNRX::processData(q15_t sample) { - if (m_minSyncPtr < m_maxSyncPtr) { - if (m_dataPtr >= m_minSyncPtr && m_dataPtr <= m_maxSyncPtr) - correlateSync(); + if (m_minFSWPtr < m_maxFSWPtr) { + if (m_dataPtr >= m_minFSWPtr && m_dataPtr <= m_maxFSWPtr) + correlateFSW(); } else { - if (m_dataPtr >= m_minSyncPtr || m_dataPtr <= m_maxSyncPtr) - correlateSync(); + if (m_dataPtr >= m_minFSWPtr || m_dataPtr <= m_maxFSWPtr) + correlateFSW(); } if (m_dataPtr == m_endPtr) { // Only update the centre and threshold if they are from a good sync - if (m_lostCount == MAX_SYNC_FRAMES) { - m_minSyncPtr = m_syncPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; - if (m_minSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_minSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + if (m_lostCount == MAX_FSW_FRAMES) { + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - m_maxSyncPtr = m_syncPtr + 1U; - if (m_maxSyncPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_maxSyncPtr -= NXDN_FRAME_LENGTH_SAMPLES; + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; } calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS); - DEBUG4("NXDNRX: sync found pos/centre/threshold", m_syncPtr, m_centreVal, m_thresholdVal); + DEBUG4("NXDNRX: sync found pos/centre/threshold", m_fswPtr, m_centreVal, m_thresholdVal); uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U]; samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); @@ -195,17 +195,17 @@ void CNXDNRX::processData(q15_t sample) m_countdown = 0U; m_maxCorr = 0; } else { - frame[0U] = m_lostCount == (MAX_SYNC_FRAMES - 1U) ? 0x01U : 0x00U; + frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; writeRSSIData(frame); m_maxCorr = 0; } } } -bool CNXDNRX::correlateSync() +bool CNXDNRX::correlateFSW() { - if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_SYNC_SYMBOLS_MASK) ^ NXDN_SYNC_SYMBOLS) <= MAX_SYNC_SYMBOLS_ERRS) { - uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_FSW_SYMBOLS_MASK) ^ NXDN_FSW_SYMBOLS) <= MAX_FSW_SYMBOLS_ERRS) { + uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) ptr -= NXDN_FRAME_LENGTH_SAMPLES; @@ -213,7 +213,7 @@ bool CNXDNRX::correlateSync() q15_t min = 16000; q15_t max = -16000; - for (uint8_t i = 0U; i < NXDN_SYNC_LENGTH_SYMBOLS; i++) { + for (uint8_t i = 0U; i < NXDN_FSW_LENGTH_SYMBOLS; i++) { q15_t val = m_buffer[ptr]; if (val > max) @@ -221,7 +221,7 @@ bool CNXDNRX::correlateSync() if (val < min) min = val; - switch (NXDN_SYNC_SYMBOLS_VALUES[i]) { + switch (NXDN_FSW_SYMBOLS_VALUES[i]) { case +3: corr -= (val + val + val); break; @@ -249,31 +249,31 @@ bool CNXDNRX::correlateSync() m_thresholdVal = q15_t(v1 >> 15); } - uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES) startPtr -= NXDN_FRAME_LENGTH_SAMPLES; - uint8_t sync[NXDN_SYNC_BYTES_LENGTH]; - samplesToBits(startPtr, NXDN_SYNC_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); + uint8_t sync[NXDN_FSW_BYTES_LENGTH]; + samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); uint8_t maxErrs; if (m_state == NXDNRXS_NONE) - maxErrs = MAX_SYNC_BIT_START_ERRS; + maxErrs = MAX_FSW_BIT_START_ERRS; else - maxErrs = MAX_SYNC_BIT_RUN_ERRS; + maxErrs = MAX_FSW_BIT_RUN_ERRS; uint8_t errs = 0U; - for (uint8_t i = 0U; i < NXDN_SYNC_BYTES_LENGTH; i++) - errs += countBits8(sync[i] ^ NXDN_SYNC_BYTES[i]); + for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) + errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); if (errs <= maxErrs) { m_maxCorr = corr; - m_lostCount = MAX_SYNC_FRAMES; - m_syncPtr = m_dataPtr; + m_lostCount = MAX_FSW_FRAMES; + m_fswPtr = m_dataPtr; m_startPtr = startPtr; - m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_SYNC_LENGTH_SAMPLES - 1U; + m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES - 1U; if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES) m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES; diff --git a/NXDNRX.h b/NXDNRX.h index 7fc81f6..1bff2ac 100644 --- a/NXDNRX.h +++ b/NXDNRX.h @@ -37,15 +37,15 @@ public: private: NXDNRX_STATE m_state; - uint32_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; + uint16_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES]; uint16_t m_bitPtr; uint16_t m_dataPtr; uint16_t m_startPtr; uint16_t m_endPtr; - uint16_t m_syncPtr; - uint16_t m_minSyncPtr; - uint16_t m_maxSyncPtr; + uint16_t m_fswPtr; + uint16_t m_minFSWPtr; + uint16_t m_maxFSWPtr; q31_t m_maxCorr; uint16_t m_lostCount; uint8_t m_countdown; @@ -59,7 +59,7 @@ private: void processNone(q15_t sample); void processData(q15_t sample); - bool correlateSync(); + bool correlateFSW(); void calculateLevels(uint16_t start, uint16_t count); void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold); void writeRSSIData(uint8_t* data); diff --git a/NXDNTX.cpp b/NXDNTX.cpp index d450e98..2e2ca0a 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -29,10 +29,10 @@ static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172 -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t NXDN_LEVELA = 1893; -const q15_t NXDN_LEVELB = 631; -const q15_t NXDN_LEVELC = -631; -const q15_t NXDN_LEVELD = -1893; +const q15_t NXDN_LEVELA = 1683; +const q15_t NXDN_LEVELB = 561; +const q15_t NXDN_LEVELC = -561; +const q15_t NXDN_LEVELD = -1683; const uint8_t NXDN_START_SYNC = 0x77U; const uint8_t NXDN_END_SYNC = 0xFFU; From 4f49f65a1ca5a5cf495773d92a0b9185c57642cd Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 11 Jan 2018 01:50:41 -0300 Subject: [PATCH 03/29] Fixing STM32F4 and STM32F7 pin definitions for NXDN --- IOSTM.cpp | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/IOSTM.cpp b/IOSTM.cpp index 6c326c5..9a57a3d 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 by Jim McLaughlin KI6ZUM - * Copyright (C) 2016, 2017 by Andy Uribe CA6JAU + * Copyright (C) 2016,2017,2018 by Andy Uribe CA6JAU * Copyright (C) 2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify @@ -112,7 +112,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output -P25 PC10 output +NXDN PB1 output RX PA0 analog input RSSI PA7 analog input @@ -141,9 +141,9 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC -#define PIN_P25 GPIO_Pin_10 -#define PORT_P25 GPIOC -#define RCC_Per_P25 RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC @@ -187,7 +187,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output -NXDN PC10 output +NXDN PB1 output RX PA0 analog input RSSI PA7 analog input @@ -216,9 +216,9 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC -#define PIN_NXDN GPIO_Pin_10 -#define PORT_NXDN GPIOC -#define RCC_Per_NXDN RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC @@ -262,7 +262,7 @@ DSTAR PC7 output DMR PC8 output YSF PA8 output P25 PC9 output -NXDN PC10 output +NXDN PB1 output RX PA0 analog input RSSI PA7 analog input @@ -291,9 +291,9 @@ EXT_CLK PA15 input #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC -#define PIN_NXDN GPIO_Pin_10 -#define PORT_NXDN GPIOC -#define RCC_Per_NXDN RCC_AHB1Periph_GPIOC +#define PIN_NXDN GPIO_Pin_1 +#define PORT_NXDN GPIOB +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB #define PIN_DSTAR GPIO_Pin_7 #define PORT_DSTAR GPIOC @@ -339,13 +339,13 @@ DSTAR PB10 output CN10 Pin25 DMR PB4 output CN10 Pin27 YSF PB5 output CN10 Pin29 P25 PB3 output CN10 Pin31 -NXDN PB2 output CN10 Pin32 +NXDN PA10 output CN10 Pin33 MDSTAR PC4 output CN10 Pin34 MDMR PC5 output CN10 Pin6 MYSF PC2 output CN7 Pin35 MP25 PC3 output CN7 Pin37 -MNXDN PC6 output CN10 Pin?? +MNXDN PC6 output CN10 Pin4 RX PA0 analog input CN7 Pin28 RSSI PA1 analog input CN7 Pin30 @@ -374,9 +374,9 @@ EXT_CLK PA15 input CN7 Pin17 #define PORT_P25 GPIOB #define RCC_Per_P25 RCC_AHB1Periph_GPIOB -#define PIN_NXDN GPIO_Pin_2 -#define PORT_NXDN GPIOB -#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOA +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA #define PIN_DSTAR GPIO_Pin_10 #define PORT_DSTAR GPIOB @@ -395,7 +395,7 @@ EXT_CLK PA15 input CN7 Pin17 #define PORT_MP25 GPIOC #define RCC_Per_MP25 RCC_AHB1Periph_GPIOC -#define PIN_MNXDN GPIO_Pin_1 +#define PIN_MNXDN GPIO_Pin_6 #define PORT_MNXDN GPIOC #define RCC_Per_MNXDN RCC_AHB1Periph_GPIOC @@ -442,7 +442,7 @@ DSTAR PA1 output CN8 Pin2 DMR PA4 output CN8 Pin3 YSF PB0 output CN8 Pin4 P25 PC1 output CN8 Pin5 -NXDN PC? output CN8 Pin6 +NXDN PC0 output CN8 Pin6 RX PA0 analog input CN8 Pin1 RSSI PC0 analog input CN8 Pin6 @@ -471,7 +471,7 @@ EXT_CLK PB8 input CN5 Pin10 #define PORT_P25 GPIOC #define RCC_Per_P25 RCC_AHB1Periph_GPIOC -#define PIN_NXDN GPIO_Pin_6 +#define PIN_NXDN GPIO_Pin_0 #define PORT_NXDN GPIOC #define RCC_Per_NXDN RCC_AHB1Periph_GPIOC @@ -521,13 +521,13 @@ DSTAR PB10 output CN12 Pin25 DMR PB4 output CN12 Pin27 YSF PB5 output CN12 Pin29 P25 PB3 output CN12 Pin31 -NXDN PB2 output CN12 Pin32 +NXDN PA10 output CN12 Pin33 MDSTAR PC4 output CN12 Pin34 MDMR PC5 output CN12 Pin6 MYSF PC2 output CN11 Pin35 MP25 PC3 output CN11 Pin37 -MNXDN PC6 output CN11 Pin?? +MNXDN PC6 output CN12 Pin4 RX PA0 analog input CN11 Pin28 RSSI PA1 analog input CN11 Pin30 @@ -556,9 +556,9 @@ EXT_CLK PA15 input CN11 Pin17 #define PORT_P25 GPIOB #define RCC_Per_P25 RCC_AHB1Periph_GPIOB -#define PIN_NXDN GPIO_Pin_2 -#define PORT_NXDN GPIOB -#define RCC_Per_NXDN RCC_AHB1Periph_GPIOB +#define PIN_NXDN GPIO_Pin_10 +#define PORT_NXDN GPIOA +#define RCC_Per_NXDN RCC_AHB1Periph_GPIOA #define PIN_DSTAR GPIO_Pin_10 #define PORT_DSTAR GPIOB From d69df42de43c880bf87e4fbabffb7c07969f2172 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 11 Jan 2018 18:29:58 +0000 Subject: [PATCH 04/29] Add the NXDN TX preamble. --- NXDNTX.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 2e2ca0a..b3fb55c 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -34,8 +34,8 @@ const q15_t NXDN_LEVELB = 561; const q15_t NXDN_LEVELC = -561; const q15_t NXDN_LEVELD = -1683; -const uint8_t NXDN_START_SYNC = 0x77U; -const uint8_t NXDN_END_SYNC = 0xFFU; +const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; +const uint8_t NXDN_SYNC = 0x5FU; CNXDNTX::CNXDNTX() : m_buffer(1500U), @@ -62,7 +62,10 @@ void CNXDNTX::process() if (m_poLen == 0U) { if (!m_tx) { for (uint16_t i = 0U; i < m_txDelay; i++) - m_poBuffer[m_poLen++] = NXDN_START_SYNC; + 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 { for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { uint8_t c = m_buffer.get(); From c59c32b350446ba8b616319a3142ab93ebb5bc2c Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 11 Jan 2018 19:47:23 +0000 Subject: [PATCH 05/29] Incorporate NXDN specification values for errors. --- NXDNRX.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/NXDNRX.cpp b/NXDNRX.cpp index 6087579..79bf10e 100644 --- a/NXDNRX.cpp +++ b/NXDNRX.cpp @@ -23,10 +23,9 @@ const q15_t SCALING_FACTOR = 18750; // Q15(0.55) -const uint8_t MAX_FSW_BIT_START_ERRS = 2U; -const uint8_t MAX_FSW_BIT_RUN_ERRS = 4U; +const uint8_t MAX_FSW_BIT_ERRS = 2U; -const uint8_t MAX_FSW_SYMBOLS_ERRS = 3U; +const uint8_t MAX_FSW_SYMBOLS_ERRS = 1U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -36,7 +35,7 @@ const uint8_t NOAVEPTR = 99U; const uint16_t NOENDPTR = 9999U; -const unsigned int MAX_FSW_FRAMES = 4U + 1U; +const unsigned int MAX_FSW_FRAMES = 5U + 1U; CNXDNRX::CNXDNRX() : m_state(NXDNRXS_NONE), @@ -256,17 +255,11 @@ bool CNXDNRX::correlateFSW() uint8_t sync[NXDN_FSW_BYTES_LENGTH]; samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); - uint8_t maxErrs; - if (m_state == NXDNRXS_NONE) - maxErrs = MAX_FSW_BIT_START_ERRS; - else - maxErrs = MAX_FSW_BIT_RUN_ERRS; - uint8_t errs = 0U; for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); - if (errs <= maxErrs) { + if (errs <= MAX_FSW_BIT_ERRS) { m_maxCorr = corr; m_lostCount = MAX_FSW_FRAMES; m_fswPtr = m_dataPtr; @@ -388,8 +381,8 @@ void CNXDNRX::writeRSSIData(uint8_t* data) if (m_rssiCount > 0U) { uint16_t rssi = m_rssiAccum / m_rssiCount; - data[121U] = (rssi >> 8) & 0xFFU; - data[122U] = (rssi >> 0) & 0xFFU; + data[49U] = (rssi >> 8) & 0xFFU; + data[50U] = (rssi >> 0) & 0xFFU; serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 3U); } else { From 615c705044abca3e9546d94d100e4ce0d819c823 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 15 Jan 2018 20:07:00 +0000 Subject: [PATCH 06/29] Fix byte ordering for NXDN syncs. --- NXDNDefines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NXDNDefines.h b/NXDNDefines.h index 956ffaf..cbb7731 100644 --- a/NXDNDefines.h +++ b/NXDNDefines.h @@ -30,8 +30,8 @@ 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[] = {0x0CU, 0xDFU, 0x59U}; -const uint8_t NXDN_FSW_BYTES_MASK[] = {0x0FU, 0xFFU, 0xFFU}; +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; From dfc262bbcd2570817e4f8e03156a94ec00537d9b Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 18 Jan 2018 21:25:26 +0000 Subject: [PATCH 07/29] Changes based on first NXDN RF tests. --- IO.cpp | 23 ++++++++++++----------- NXDNRX.cpp | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/IO.cpp b/IO.cpp index aa6745d..bfbb6f4 100644 --- a/IO.cpp +++ b/IO.cpp @@ -300,15 +300,19 @@ void CIO::process() dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE); } - if (m_p25Enable) { - q15_t P25Vals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); + if (m_p25Enable || m_nxdnEnable) { + q15_t C4FSKVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); - p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); + if (m_p25Enable) + p25RX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + + if (m_nxdnEnable) + nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); } // XXX YSF should use dcSamples, but DMR not - if (m_dmrEnable || m_ysfEnable || m_nxdnEnable) { + if (m_dmrEnable || m_ysfEnable) { q15_t C4FSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE); @@ -321,9 +325,6 @@ void CIO::process() if (m_ysfEnable) ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); - - if (m_nxdnEnable) - nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -363,10 +364,10 @@ void CIO::process() } } else if (m_modemState == STATE_NXDN) { if (m_nxdnEnable) { - q15_t C4FSKVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); + q15_t NXDNVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); - nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTARCAL) { q15_t GMSKVals[RX_BLOCK_SIZE]; diff --git a/NXDNRX.cpp b/NXDNRX.cpp index 79bf10e..eab67dd 100644 --- a/NXDNRX.cpp +++ b/NXDNRX.cpp @@ -23,7 +23,7 @@ const q15_t SCALING_FACTOR = 18750; // Q15(0.55) -const uint8_t MAX_FSW_BIT_ERRS = 2U; +const uint8_t MAX_FSW_BIT_ERRS = 1U; const uint8_t MAX_FSW_SYMBOLS_ERRS = 1U; From e7a40343b1363afd96bc94334ea38f3ad0b136d9 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 22 Jan 2018 19:58:39 +0000 Subject: [PATCH 08/29] Adjust NXDN deviation slightly. --- NXDNTX.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NXDNTX.cpp b/NXDNTX.cpp index b3fb55c..16e6d55 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -29,10 +29,10 @@ static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172 -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t NXDN_LEVELA = 1683; -const q15_t NXDN_LEVELB = 561; -const q15_t NXDN_LEVELC = -561; -const q15_t NXDN_LEVELD = -1683; +const q15_t NXDN_LEVELA = 1680; +const q15_t NXDN_LEVELB = 560; +const q15_t NXDN_LEVELC = -560; +const q15_t NXDN_LEVELD = -1680; const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; const uint8_t NXDN_SYNC = 0x5FU; From 7b88e883f993c2e3468ee81adc764412c50c4476 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sat, 3 Feb 2018 20:10:25 -0300 Subject: [PATCH 09/29] Adding some additional idle frames after a DMR Abort --- DMRTX.cpp | 8 +++++++- DMRTX.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) mode change 100644 => 100755 DMRTX.cpp mode change 100644 => 100755 DMRTX.h diff --git a/DMRTX.cpp b/DMRTX.cpp old mode 100644 new mode 100755 index 9824f9c..b4d43b4 --- a/DMRTX.cpp +++ b/DMRTX.cpp @@ -54,6 +54,7 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02 #define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) const uint32_t STARTUP_COUNT = 20U; +const uint32_t ABORT_COUNT = 8U; CDMRTX::CDMRTX() : m_fifo(), @@ -69,6 +70,7 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_frameCount(0U), +m_abortCount(0U), m_abort() { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); @@ -210,10 +212,12 @@ uint8_t CDMRTX::writeAbort(const uint8_t* data, uint8_t length) switch (data[0U]) { case 1U: + m_abortCount = 0U; m_abort[0U] = true; return 0U; case 2U: + m_abortCount = 0U; m_abort[1U] = true; return 0U; @@ -227,6 +231,7 @@ void CDMRTX::setStart(bool start) m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE; m_frameCount = 0U; + m_abortCount = 0U; m_abort[0U] = false; m_abort[1U] = false; @@ -282,7 +287,7 @@ uint8_t CDMRTX::getSpace2() const void CDMRTX::createData(uint8_t slotIndex) { - if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT) { + if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT && m_abortCount >= ABORT_COUNT) { for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { m_poBuffer[i] = m_fifo[slotIndex].get(); m_markBuffer[i] = MARK_NONE; @@ -335,6 +340,7 @@ void CDMRTX::createCal() void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex) { m_frameCount++; + m_abortCount++; if (m_cachPtr >= 12U) m_cachPtr = 0U; diff --git a/DMRTX.h b/DMRTX.h old mode 100644 new mode 100755 index 00977d5..867ee4c --- a/DMRTX.h +++ b/DMRTX.h @@ -72,6 +72,7 @@ private: uint16_t m_poLen; uint16_t m_poPtr; uint32_t m_frameCount; + uint32_t m_abortCount; bool m_abort[2U]; void createData(uint8_t slotIndex); From 555c308749a5561eb808b6186f78b085199be92d Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 5 Feb 2018 09:12:05 -0300 Subject: [PATCH 10/29] Updating DMR abort delay --- DMRTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DMRTX.cpp b/DMRTX.cpp index b4d43b4..b708f46 100755 --- a/DMRTX.cpp +++ b/DMRTX.cpp @@ -54,7 +54,7 @@ const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02 #define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) const uint32_t STARTUP_COUNT = 20U; -const uint32_t ABORT_COUNT = 8U; +const uint32_t ABORT_COUNT = 6U; CDMRTX::CDMRTX() : m_fifo(), From 91e5d588b06cf9e8358a2fe1795f28e4f345c3bb Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 5 Feb 2018 09:42:40 -0300 Subject: [PATCH 11/29] Fix mistake reading COS input pin using STM32F4 and STM32F7 --- IOSTM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IOSTM.cpp b/IOSTM.cpp index 3b2081c..932ba57 100644 --- a/IOSTM.cpp +++ b/IOSTM.cpp @@ -855,7 +855,7 @@ void CIO::interrupt() bool CIO::getCOSInt() { - return GPIO_ReadOutputDataBit(PORT_COS, PIN_COS) == Bit_SET; + return GPIO_ReadInputDataBit(PORT_COS, PIN_COS) == Bit_SET; } void CIO::setLEDInt(bool on) From caaf090b101cd28c5394d0fd5d998584a7ebca79 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 5 Feb 2018 22:36:05 -0300 Subject: [PATCH 12/29] DMR abort counter for each slot --- DMRTX.cpp | 17 +++++++++++------ DMRTX.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/DMRTX.cpp b/DMRTX.cpp index b708f46..0f4f56c 100755 --- a/DMRTX.cpp +++ b/DMRTX.cpp @@ -70,7 +70,7 @@ m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_frameCount(0U), -m_abortCount(0U), +m_abortCount(), m_abort() { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); @@ -85,6 +85,9 @@ m_abort() m_abort[0U] = false; m_abort[1U] = false; + + m_abortCount[0U] = 0U; + m_abortCount[1U] = 0U; } void CDMRTX::process() @@ -212,12 +215,12 @@ uint8_t CDMRTX::writeAbort(const uint8_t* data, uint8_t length) switch (data[0U]) { case 1U: - m_abortCount = 0U; + m_abortCount[0U] = 0U; m_abort[0U] = true; return 0U; case 2U: - m_abortCount = 0U; + m_abortCount[1U] = 0U; m_abort[1U] = true; return 0U; @@ -231,7 +234,8 @@ void CDMRTX::setStart(bool start) m_state = start ? DMRTXSTATE_SLOT1 : DMRTXSTATE_IDLE; m_frameCount = 0U; - m_abortCount = 0U; + m_abortCount[0U] = 0U; + m_abortCount[1U] = 0U; m_abort[0U] = false; m_abort[1U] = false; @@ -287,7 +291,7 @@ uint8_t CDMRTX::getSpace2() const void CDMRTX::createData(uint8_t slotIndex) { - if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT && m_abortCount >= ABORT_COUNT) { + if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) { for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { m_poBuffer[i] = m_fifo[slotIndex].get(); m_markBuffer[i] = MARK_NONE; @@ -340,7 +344,8 @@ void CDMRTX::createCal() void CDMRTX::createCACH(uint8_t txSlotIndex, uint8_t rxSlotIndex) { m_frameCount++; - m_abortCount++; + m_abortCount[0U]++; + m_abortCount[1U]++; if (m_cachPtr >= 12U) m_cachPtr = 0U; diff --git a/DMRTX.h b/DMRTX.h index 867ee4c..1e2f617 100755 --- a/DMRTX.h +++ b/DMRTX.h @@ -72,7 +72,7 @@ private: uint16_t m_poLen; uint16_t m_poPtr; uint32_t m_frameCount; - uint32_t m_abortCount; + uint32_t m_abortCount[2U]; bool m_abort[2U]; void createData(uint8_t slotIndex); From a6b6219c6d10bcc15e9bbaed4a60d5a03f920ee7 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 07:52:18 +0000 Subject: [PATCH 13/29] Start work on 4800 baud NXDN support. --- NXDN48Defines.h | 50 +++++ NXDNRX.cpp => NXDN48RX.cpp | 0 NXDNRX.h => NXDN48RX.h | 0 NXDNTX.cpp => NXDN48TX.cpp | 0 NXDNTX.h => NXDN48TX.h | 0 NXDN96Defines.h | 50 +++++ NXDN96RX.cpp | 398 +++++++++++++++++++++++++++++++++++++ NXDN96RX.h | 69 +++++++ NXDN96TX.cpp | 153 ++++++++++++++ NXDN96TX.h | 51 +++++ 10 files changed, 771 insertions(+) create mode 100644 NXDN48Defines.h rename NXDNRX.cpp => NXDN48RX.cpp (100%) rename NXDNRX.h => NXDN48RX.h (100%) rename NXDNTX.cpp => NXDN48TX.cpp (100%) rename NXDNTX.h => NXDN48TX.h (100%) create mode 100644 NXDN96Defines.h create mode 100644 NXDN96RX.cpp create mode 100644 NXDN96RX.h create mode 100644 NXDN96TX.cpp create mode 100644 NXDN96TX.h diff --git a/NXDN48Defines.h b/NXDN48Defines.h new file mode 100644 index 0000000..cbb7731 --- /dev/null +++ b/NXDN48Defines.h @@ -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 = 5U; // 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 + diff --git a/NXDNRX.cpp b/NXDN48RX.cpp similarity index 100% rename from NXDNRX.cpp rename to NXDN48RX.cpp diff --git a/NXDNRX.h b/NXDN48RX.h similarity index 100% rename from NXDNRX.h rename to NXDN48RX.h diff --git a/NXDNTX.cpp b/NXDN48TX.cpp similarity index 100% rename from NXDNTX.cpp rename to NXDN48TX.cpp diff --git a/NXDNTX.h b/NXDN48TX.h similarity index 100% rename from NXDNTX.h rename to NXDN48TX.h diff --git a/NXDN96Defines.h b/NXDN96Defines.h new file mode 100644 index 0000000..cbb7731 --- /dev/null +++ b/NXDN96Defines.h @@ -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 = 5U; // 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 + diff --git a/NXDN96RX.cpp b/NXDN96RX.cpp new file mode 100644 index 0000000..eab67dd --- /dev/null +++ b/NXDN96RX.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2009-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. + */ + +#include "Config.h" +#include "Globals.h" +#include "NXDNRX.h" +#include "Utils.h" + +const q15_t SCALING_FACTOR = 18750; // Q15(0.55) + +const uint8_t MAX_FSW_BIT_ERRS = 1U; + +const uint8_t MAX_FSW_SYMBOLS_ERRS = 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]) + +const uint8_t NOAVEPTR = 99U; + +const uint16_t NOENDPTR = 9999U; + +const unsigned int MAX_FSW_FRAMES = 5U + 1U; + +CNXDNRX::CNXDNRX() : +m_state(NXDNRXS_NONE), +m_bitBuffer(), +m_buffer(), +m_bitPtr(0U), +m_dataPtr(0U), +m_startPtr(NOENDPTR), +m_endPtr(NOENDPTR), +m_fswPtr(NOENDPTR), +m_minFSWPtr(NOENDPTR), +m_maxFSWPtr(NOENDPTR), +m_maxCorr(0), +m_lostCount(0U), +m_countdown(0U), +m_centre(), +m_centreVal(0), +m_threshold(), +m_thresholdVal(0), +m_averagePtr(NOAVEPTR), +m_rssiAccum(0U), +m_rssiCount(0U) +{ +} + +void CNXDNRX::reset() +{ + m_state = NXDNRXS_NONE; + m_dataPtr = 0U; + m_bitPtr = 0U; + m_maxCorr = 0; + m_averagePtr = NOAVEPTR; + m_startPtr = NOENDPTR; + m_endPtr = NOENDPTR; + m_fswPtr = NOENDPTR; + m_minFSWPtr = NOENDPTR; + m_maxFSWPtr = NOENDPTR; + m_centreVal = 0; + m_thresholdVal = 0; + m_lostCount = 0U; + m_countdown = 0U; + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + +void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length) +{ + for (uint8_t i = 0U; i < length; i++) { + q15_t sample = samples[i]; + + m_rssiAccum += rssi[i]; + m_rssiCount++; + + m_bitBuffer[m_bitPtr] <<= 1; + if (sample < 0) + m_bitBuffer[m_bitPtr] |= 0x01U; + + m_buffer[m_dataPtr] = sample; + + switch (m_state) { + case NXDNRXS_DATA: + processData(sample); + break; + default: + processNone(sample); + break; + } + + m_dataPtr++; + if (m_dataPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_dataPtr = 0U; + + m_bitPtr++; + if (m_bitPtr >= NXDN_RADIO_SYMBOL_LENGTH) + m_bitPtr = 0U; + } +} + +void CNXDNRX::processNone(q15_t sample) +{ + bool ret = correlateFSW(); + if (ret) { + // On the first sync, start the countdown to the state change + if (m_countdown == 0U) { + m_rssiAccum = 0U; + m_rssiCount = 0U; + + io.setDecode(true); + io.setADCDetection(true); + + m_averagePtr = NOAVEPTR; + + m_countdown = 5U; + } + } + + if (m_countdown > 0U) + m_countdown--; + + if (m_countdown == 1U) { + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_state = NXDNRXS_DATA; + m_countdown = 0U; + } +} + +void CNXDNRX::processData(q15_t sample) +{ + if (m_minFSWPtr < m_maxFSWPtr) { + if (m_dataPtr >= m_minFSWPtr && m_dataPtr <= m_maxFSWPtr) + correlateFSW(); + } else { + if (m_dataPtr >= m_minFSWPtr || m_dataPtr <= m_maxFSWPtr) + correlateFSW(); + } + + if (m_dataPtr == m_endPtr) { + // Only update the centre and threshold if they are from a good sync + if (m_lostCount == MAX_FSW_FRAMES) { + m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; + if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + m_maxFSWPtr = m_fswPtr + 1U; + if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; + } + + calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS); + + DEBUG4("NXDNRX: sync found pos/centre/threshold", m_fswPtr, m_centreVal, m_thresholdVal); + + uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U]; + samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); + + // 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); + io.setADCDetection(false); + + serial.writeNXDNLost(); + + m_state = NXDNRXS_NONE; + m_endPtr = NOENDPTR; + m_averagePtr = NOAVEPTR; + m_countdown = 0U; + m_maxCorr = 0; + } else { + frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; + writeRSSIData(frame); + m_maxCorr = 0; + } + } +} + +bool CNXDNRX::correlateFSW() +{ + if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_FSW_SYMBOLS_MASK) ^ NXDN_FSW_SYMBOLS) <= MAX_FSW_SYMBOLS_ERRS) { + uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_FRAME_LENGTH_SAMPLES; + + q31_t corr = 0; + q15_t min = 16000; + q15_t max = -16000; + + for (uint8_t i = 0U; i < NXDN_FSW_LENGTH_SYMBOLS; i++) { + q15_t val = m_buffer[ptr]; + + if (val > max) + max = val; + if (val < min) + min = val; + + switch (NXDN_FSW_SYMBOLS_VALUES[i]) { + case +3: + corr -= (val + val + val); + break; + case +1: + corr -= val; + break; + case -1: + corr += val; + break; + default: // -3 + corr += (val + val + val); + break; + } + + ptr += NXDN_RADIO_SYMBOL_LENGTH; + if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) + ptr -= NXDN_FRAME_LENGTH_SAMPLES; + } + + if (corr > m_maxCorr) { + if (m_averagePtr == NOAVEPTR) { + m_centreVal = (max + min) >> 1; + + q31_t v1 = (max - m_centreVal) * SCALING_FACTOR; + m_thresholdVal = q15_t(v1 >> 15); + } + + uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; + if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES) + startPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + uint8_t sync[NXDN_FSW_BYTES_LENGTH]; + samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); + + uint8_t errs = 0U; + for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) + errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); + + if (errs <= MAX_FSW_BIT_ERRS) { + m_maxCorr = corr; + m_lostCount = MAX_FSW_FRAMES; + m_fswPtr = m_dataPtr; + + m_startPtr = startPtr; + + m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES - 1U; + if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES) + m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES; + + return true; + } + } + } + + return false; +} + +void CNXDNRX::calculateLevels(uint16_t start, uint16_t count) +{ + q15_t maxPos = -16000; + q15_t minPos = 16000; + q15_t maxNeg = 16000; + q15_t minNeg = -16000; + + for (uint16_t i = 0U; i < count; i++) { + q15_t sample = m_buffer[start]; + + if (sample > 0) { + if (sample > maxPos) + maxPos = sample; + if (sample < minPos) + minPos = sample; + } else { + if (sample < maxNeg) + maxNeg = sample; + if (sample > minNeg) + minNeg = sample; + } + + start += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_FRAME_LENGTH_SAMPLES; + } + + q15_t posThresh = (maxPos + minPos) >> 1; + q15_t negThresh = (maxNeg + minNeg) >> 1; + + q15_t centre = (posThresh + negThresh) >> 1; + + q15_t threshold = posThresh - centre; + + DEBUG5("NXDNRX: pos/neg/centre/threshold", posThresh, negThresh, centre, threshold); + + if (m_averagePtr == NOAVEPTR) { + for (uint8_t i = 0U; i < 16U; i++) { + m_centre[i] = centre; + m_threshold[i] = threshold; + } + + m_averagePtr = 0U; + } else { + m_centre[m_averagePtr] = centre; + m_threshold[m_averagePtr] = threshold; + + m_averagePtr++; + if (m_averagePtr >= 16U) + m_averagePtr = 0U; + } + + m_centreVal = 0; + m_thresholdVal = 0; + + for (uint8_t i = 0U; i < 16U; i++) { + m_centreVal += m_centre[i]; + m_thresholdVal += m_threshold[i]; + } + + m_centreVal >>= 4; + m_thresholdVal >>= 4; +} + +void CNXDNRX::samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold) +{ + for (uint16_t i = 0U; i < count; i++) { + q15_t sample = m_buffer[start] - centre; + + if (sample < -threshold) { + WRITE_BIT1(buffer, offset, false); + offset++; + WRITE_BIT1(buffer, offset, true); + offset++; + } else if (sample < 0) { + WRITE_BIT1(buffer, offset, false); + offset++; + WRITE_BIT1(buffer, offset, false); + offset++; + } else if (sample < threshold) { + WRITE_BIT1(buffer, offset, true); + offset++; + WRITE_BIT1(buffer, offset, false); + offset++; + } else { + WRITE_BIT1(buffer, offset, true); + offset++; + WRITE_BIT1(buffer, offset, true); + offset++; + } + + start += NXDN_RADIO_SYMBOL_LENGTH; + if (start >= NXDN_FRAME_LENGTH_SAMPLES) + start -= NXDN_FRAME_LENGTH_SAMPLES; + } +} + +void CNXDNRX::writeRSSIData(uint8_t* data) +{ +#if defined(SEND_RSSI_DATA) + if (m_rssiCount > 0U) { + uint16_t rssi = m_rssiAccum / m_rssiCount; + + 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); + } +#else + serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); +#endif + + m_rssiAccum = 0U; + m_rssiCount = 0U; +} + diff --git a/NXDN96RX.h b/NXDN96RX.h new file mode 100644 index 0000000..1bff2ac --- /dev/null +++ b/NXDN96RX.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015,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(NXDNRX_H) +#define NXDNRX_H + +#include "Config.h" +#include "NXDNDefines.h" + +enum NXDNRX_STATE { + NXDNRXS_NONE, + NXDNRXS_DATA +}; + +class CNXDNRX { +public: + CNXDNRX(); + + void samples(const q15_t* samples, uint16_t* rssi, uint8_t length); + + void reset(); + +private: + NXDNRX_STATE m_state; + uint16_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; + q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES]; + uint16_t m_bitPtr; + uint16_t m_dataPtr; + uint16_t m_startPtr; + uint16_t m_endPtr; + uint16_t m_fswPtr; + uint16_t m_minFSWPtr; + uint16_t m_maxFSWPtr; + q31_t m_maxCorr; + uint16_t m_lostCount; + uint8_t m_countdown; + q15_t m_centre[16U]; + q15_t m_centreVal; + q15_t m_threshold[16U]; + q15_t m_thresholdVal; + uint8_t m_averagePtr; + uint32_t m_rssiAccum; + uint16_t m_rssiCount; + + void processNone(q15_t sample); + void processData(q15_t sample); + bool correlateFSW(); + void calculateLevels(uint16_t start, uint16_t count); + void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold); + void writeRSSIData(uint8_t* data); +}; + +#endif + diff --git a/NXDN96TX.cpp b/NXDN96TX.cpp new file mode 100644 index 0000000..16e6d55 --- /dev/null +++ b/NXDN96TX.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX + * Copyright (C) 2017 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" + +// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442, + -5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442, + -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 +const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L + +const q15_t NXDN_LEVELA = 1680; +const q15_t NXDN_LEVELB = 560; +const q15_t NXDN_LEVELC = -560; +const q15_t NXDN_LEVELD = -1680; + +const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; +const uint8_t NXDN_SYNC = 0x5FU; + +CNXDNTX::CNXDNTX() : +m_buffer(1500U), +m_modFilter(), +m_modState(), +m_poBuffer(), +m_poLen(0U), +m_poPtr(0U), +m_txDelay(240U) // 200ms +{ + ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); + + m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH; + m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN; + m_modFilter.pCoeffs = RRC_0_2_FILTER; + m_modFilter.pState = m_modState; +} + +void CNXDNTX::process() +{ + if (m_buffer.getData() == 0U && m_poLen == 0U) + return; + + if (m_poLen == 0U) { + if (!m_tx) { + for (uint16_t i = 0U; i < m_txDelay; 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 { + for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { + uint8_t c = m_buffer.get(); + m_poBuffer[m_poLen++] = c; + } + } + + m_poPtr = 0U; + } + + if (m_poLen > 0U) { + uint16_t space = io.getSpace(); + + while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) { + uint8_t c = m_poBuffer[m_poPtr++]; + writeByte(c); + + space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; + + if (m_poPtr >= m_poLen) { + m_poPtr = 0U; + m_poLen = 0U; + 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) +{ + q15_t inBuffer[4U]; + q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; + + const uint8_t MASK = 0xC0U; + + for (uint8_t i = 0U; i < 4U; i++, c <<= 2) { + switch (c & MASK) { + case 0xC0U: + inBuffer[i] = NXDN_LEVELA; + break; + case 0x80U: + inBuffer[i] = NXDN_LEVELB; + break; + case 0x00U: + inBuffer[i] = NXDN_LEVELC; + break; + default: + inBuffer[i] = NXDN_LEVELD; + break; + } + } + + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U); + + io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); +} + +void CNXDNTX::setTXDelay(uint8_t delay) +{ + m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay + + if (m_txDelay > 1200U) + m_txDelay = 1200U; +} + +uint8_t CNXDNTX::getSpace() const +{ + return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES; +} + diff --git a/NXDN96TX.h b/NXDN96TX.h new file mode 100644 index 0000000..f98cf48 --- /dev/null +++ b/NXDN96TX.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015,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(NXDNTX_H) +#define NXDNTX_H + +#include "Config.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); + + uint8_t getSpace() const; + +private: + CSerialRB m_buffer; + arm_fir_interpolate_instance_q15 m_modFilter; + q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare + uint8_t m_poBuffer[1200U]; + uint16_t m_poLen; + uint16_t m_poPtr; + uint16_t m_txDelay; + + void writeByte(uint8_t c); +}; + +#endif + From 2bc99deec24f90d9dd63edc5feca392f1c702863 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Wed, 7 Feb 2018 13:55:15 -0300 Subject: [PATCH 14/29] Additional available data FIFO check --- DMRTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DMRTX.cpp b/DMRTX.cpp index 0f4f56c..81437ec 100755 --- a/DMRTX.cpp +++ b/DMRTX.cpp @@ -291,7 +291,7 @@ uint8_t CDMRTX::getSpace2() const void CDMRTX::createData(uint8_t slotIndex) { - if (m_fifo[slotIndex].getData() > 0U && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) { + if (m_fifo[slotIndex].getData() >= DMR_FRAME_LENGTH_BYTES && m_frameCount >= STARTUP_COUNT && m_abortCount[slotIndex] >= ABORT_COUNT) { for (unsigned int i = 0U; i < DMR_FRAME_LENGTH_BYTES; i++) { m_poBuffer[i] = m_fifo[slotIndex].get(); m_markBuffer[i] = MARK_NONE; From b34d3c3dbd6e0d2ecae8aca5102fb2d749e4e39c Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 18:07:51 +0000 Subject: [PATCH 15/29] Revert "Start work on 4800 baud NXDN support." This reverts commit a6b6219c6d10bcc15e9bbaed4a60d5a03f920ee7. --- NXDN48Defines.h | 50 ----- NXDN96Defines.h | 50 ----- NXDN96RX.cpp | 398 ------------------------------------- NXDN96RX.h | 69 ------- NXDN96TX.cpp | 153 -------------- NXDN96TX.h | 51 ----- NXDN48RX.cpp => NXDNRX.cpp | 0 NXDN48RX.h => NXDNRX.h | 0 NXDN48TX.cpp => NXDNTX.cpp | 0 NXDN48TX.h => NXDNTX.h | 0 10 files changed, 771 deletions(-) delete mode 100644 NXDN48Defines.h delete mode 100644 NXDN96Defines.h delete mode 100644 NXDN96RX.cpp delete mode 100644 NXDN96RX.h delete mode 100644 NXDN96TX.cpp delete mode 100644 NXDN96TX.h rename NXDN48RX.cpp => NXDNRX.cpp (100%) rename NXDN48RX.h => NXDNRX.h (100%) rename NXDN48TX.cpp => NXDNTX.cpp (100%) rename NXDN48TX.h => NXDNTX.h (100%) diff --git a/NXDN48Defines.h b/NXDN48Defines.h deleted file mode 100644 index cbb7731..0000000 --- a/NXDN48Defines.h +++ /dev/null @@ -1,50 +0,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(NXDNDEFINES_H) -#define NXDNDEFINES_H - -const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 5U; // 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 - diff --git a/NXDN96Defines.h b/NXDN96Defines.h deleted file mode 100644 index cbb7731..0000000 --- a/NXDN96Defines.h +++ /dev/null @@ -1,50 +0,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(NXDNDEFINES_H) -#define NXDNDEFINES_H - -const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 5U; // 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 - diff --git a/NXDN96RX.cpp b/NXDN96RX.cpp deleted file mode 100644 index eab67dd..0000000 --- a/NXDN96RX.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2009-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. - */ - -#include "Config.h" -#include "Globals.h" -#include "NXDNRX.h" -#include "Utils.h" - -const q15_t SCALING_FACTOR = 18750; // Q15(0.55) - -const uint8_t MAX_FSW_BIT_ERRS = 1U; - -const uint8_t MAX_FSW_SYMBOLS_ERRS = 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]) - -const uint8_t NOAVEPTR = 99U; - -const uint16_t NOENDPTR = 9999U; - -const unsigned int MAX_FSW_FRAMES = 5U + 1U; - -CNXDNRX::CNXDNRX() : -m_state(NXDNRXS_NONE), -m_bitBuffer(), -m_buffer(), -m_bitPtr(0U), -m_dataPtr(0U), -m_startPtr(NOENDPTR), -m_endPtr(NOENDPTR), -m_fswPtr(NOENDPTR), -m_minFSWPtr(NOENDPTR), -m_maxFSWPtr(NOENDPTR), -m_maxCorr(0), -m_lostCount(0U), -m_countdown(0U), -m_centre(), -m_centreVal(0), -m_threshold(), -m_thresholdVal(0), -m_averagePtr(NOAVEPTR), -m_rssiAccum(0U), -m_rssiCount(0U) -{ -} - -void CNXDNRX::reset() -{ - m_state = NXDNRXS_NONE; - m_dataPtr = 0U; - m_bitPtr = 0U; - m_maxCorr = 0; - m_averagePtr = NOAVEPTR; - m_startPtr = NOENDPTR; - m_endPtr = NOENDPTR; - m_fswPtr = NOENDPTR; - m_minFSWPtr = NOENDPTR; - m_maxFSWPtr = NOENDPTR; - m_centreVal = 0; - m_thresholdVal = 0; - m_lostCount = 0U; - m_countdown = 0U; - m_rssiAccum = 0U; - m_rssiCount = 0U; -} - -void CNXDNRX::samples(const q15_t* samples, uint16_t* rssi, uint8_t length) -{ - for (uint8_t i = 0U; i < length; i++) { - q15_t sample = samples[i]; - - m_rssiAccum += rssi[i]; - m_rssiCount++; - - m_bitBuffer[m_bitPtr] <<= 1; - if (sample < 0) - m_bitBuffer[m_bitPtr] |= 0x01U; - - m_buffer[m_dataPtr] = sample; - - switch (m_state) { - case NXDNRXS_DATA: - processData(sample); - break; - default: - processNone(sample); - break; - } - - m_dataPtr++; - if (m_dataPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_dataPtr = 0U; - - m_bitPtr++; - if (m_bitPtr >= NXDN_RADIO_SYMBOL_LENGTH) - m_bitPtr = 0U; - } -} - -void CNXDNRX::processNone(q15_t sample) -{ - bool ret = correlateFSW(); - if (ret) { - // On the first sync, start the countdown to the state change - if (m_countdown == 0U) { - m_rssiAccum = 0U; - m_rssiCount = 0U; - - io.setDecode(true); - io.setADCDetection(true); - - m_averagePtr = NOAVEPTR; - - m_countdown = 5U; - } - } - - if (m_countdown > 0U) - m_countdown--; - - if (m_countdown == 1U) { - m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; - if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - - m_maxFSWPtr = m_fswPtr + 1U; - if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - - m_state = NXDNRXS_DATA; - m_countdown = 0U; - } -} - -void CNXDNRX::processData(q15_t sample) -{ - if (m_minFSWPtr < m_maxFSWPtr) { - if (m_dataPtr >= m_minFSWPtr && m_dataPtr <= m_maxFSWPtr) - correlateFSW(); - } else { - if (m_dataPtr >= m_minFSWPtr || m_dataPtr <= m_maxFSWPtr) - correlateFSW(); - } - - if (m_dataPtr == m_endPtr) { - // Only update the centre and threshold if they are from a good sync - if (m_lostCount == MAX_FSW_FRAMES) { - m_minFSWPtr = m_fswPtr + NXDN_FRAME_LENGTH_SAMPLES - 1U; - if (m_minFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_minFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - - m_maxFSWPtr = m_fswPtr + 1U; - if (m_maxFSWPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_maxFSWPtr -= NXDN_FRAME_LENGTH_SAMPLES; - } - - calculateLevels(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS); - - DEBUG4("NXDNRX: sync found pos/centre/threshold", m_fswPtr, m_centreVal, m_thresholdVal); - - uint8_t frame[NXDN_FRAME_LENGTH_BYTES + 3U]; - samplesToBits(m_startPtr, NXDN_FRAME_LENGTH_SYMBOLS, frame, 8U, m_centreVal, m_thresholdVal); - - // 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); - io.setADCDetection(false); - - serial.writeNXDNLost(); - - m_state = NXDNRXS_NONE; - m_endPtr = NOENDPTR; - m_averagePtr = NOAVEPTR; - m_countdown = 0U; - m_maxCorr = 0; - } else { - frame[0U] = m_lostCount == (MAX_FSW_FRAMES - 1U) ? 0x01U : 0x00U; - writeRSSIData(frame); - m_maxCorr = 0; - } - } -} - -bool CNXDNRX::correlateFSW() -{ - if (countBits32((m_bitBuffer[m_bitPtr] & NXDN_FSW_SYMBOLS_MASK) ^ NXDN_FSW_SYMBOLS) <= MAX_FSW_SYMBOLS_ERRS) { - uint16_t ptr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; - if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) - ptr -= NXDN_FRAME_LENGTH_SAMPLES; - - q31_t corr = 0; - q15_t min = 16000; - q15_t max = -16000; - - for (uint8_t i = 0U; i < NXDN_FSW_LENGTH_SYMBOLS; i++) { - q15_t val = m_buffer[ptr]; - - if (val > max) - max = val; - if (val < min) - min = val; - - switch (NXDN_FSW_SYMBOLS_VALUES[i]) { - case +3: - corr -= (val + val + val); - break; - case +1: - corr -= val; - break; - case -1: - corr += val; - break; - default: // -3 - corr += (val + val + val); - break; - } - - ptr += NXDN_RADIO_SYMBOL_LENGTH; - if (ptr >= NXDN_FRAME_LENGTH_SAMPLES) - ptr -= NXDN_FRAME_LENGTH_SAMPLES; - } - - if (corr > m_maxCorr) { - if (m_averagePtr == NOAVEPTR) { - m_centreVal = (max + min) >> 1; - - q31_t v1 = (max - m_centreVal) * SCALING_FACTOR; - m_thresholdVal = q15_t(v1 >> 15); - } - - uint16_t startPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES + NXDN_RADIO_SYMBOL_LENGTH; - if (startPtr >= NXDN_FRAME_LENGTH_SAMPLES) - startPtr -= NXDN_FRAME_LENGTH_SAMPLES; - - uint8_t sync[NXDN_FSW_BYTES_LENGTH]; - samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); - - uint8_t errs = 0U; - for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) - errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); - - if (errs <= MAX_FSW_BIT_ERRS) { - m_maxCorr = corr; - m_lostCount = MAX_FSW_FRAMES; - m_fswPtr = m_dataPtr; - - m_startPtr = startPtr; - - m_endPtr = m_dataPtr + NXDN_FRAME_LENGTH_SAMPLES - NXDN_FSW_LENGTH_SAMPLES - 1U; - if (m_endPtr >= NXDN_FRAME_LENGTH_SAMPLES) - m_endPtr -= NXDN_FRAME_LENGTH_SAMPLES; - - return true; - } - } - } - - return false; -} - -void CNXDNRX::calculateLevels(uint16_t start, uint16_t count) -{ - q15_t maxPos = -16000; - q15_t minPos = 16000; - q15_t maxNeg = 16000; - q15_t minNeg = -16000; - - for (uint16_t i = 0U; i < count; i++) { - q15_t sample = m_buffer[start]; - - if (sample > 0) { - if (sample > maxPos) - maxPos = sample; - if (sample < minPos) - minPos = sample; - } else { - if (sample < maxNeg) - maxNeg = sample; - if (sample > minNeg) - minNeg = sample; - } - - start += NXDN_RADIO_SYMBOL_LENGTH; - if (start >= NXDN_FRAME_LENGTH_SAMPLES) - start -= NXDN_FRAME_LENGTH_SAMPLES; - } - - q15_t posThresh = (maxPos + minPos) >> 1; - q15_t negThresh = (maxNeg + minNeg) >> 1; - - q15_t centre = (posThresh + negThresh) >> 1; - - q15_t threshold = posThresh - centre; - - DEBUG5("NXDNRX: pos/neg/centre/threshold", posThresh, negThresh, centre, threshold); - - if (m_averagePtr == NOAVEPTR) { - for (uint8_t i = 0U; i < 16U; i++) { - m_centre[i] = centre; - m_threshold[i] = threshold; - } - - m_averagePtr = 0U; - } else { - m_centre[m_averagePtr] = centre; - m_threshold[m_averagePtr] = threshold; - - m_averagePtr++; - if (m_averagePtr >= 16U) - m_averagePtr = 0U; - } - - m_centreVal = 0; - m_thresholdVal = 0; - - for (uint8_t i = 0U; i < 16U; i++) { - m_centreVal += m_centre[i]; - m_thresholdVal += m_threshold[i]; - } - - m_centreVal >>= 4; - m_thresholdVal >>= 4; -} - -void CNXDNRX::samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold) -{ - for (uint16_t i = 0U; i < count; i++) { - q15_t sample = m_buffer[start] - centre; - - if (sample < -threshold) { - WRITE_BIT1(buffer, offset, false); - offset++; - WRITE_BIT1(buffer, offset, true); - offset++; - } else if (sample < 0) { - WRITE_BIT1(buffer, offset, false); - offset++; - WRITE_BIT1(buffer, offset, false); - offset++; - } else if (sample < threshold) { - WRITE_BIT1(buffer, offset, true); - offset++; - WRITE_BIT1(buffer, offset, false); - offset++; - } else { - WRITE_BIT1(buffer, offset, true); - offset++; - WRITE_BIT1(buffer, offset, true); - offset++; - } - - start += NXDN_RADIO_SYMBOL_LENGTH; - if (start >= NXDN_FRAME_LENGTH_SAMPLES) - start -= NXDN_FRAME_LENGTH_SAMPLES; - } -} - -void CNXDNRX::writeRSSIData(uint8_t* data) -{ -#if defined(SEND_RSSI_DATA) - if (m_rssiCount > 0U) { - uint16_t rssi = m_rssiAccum / m_rssiCount; - - 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); - } -#else - serial.writeNXDNData(data, NXDN_FRAME_LENGTH_BYTES + 1U); -#endif - - m_rssiAccum = 0U; - m_rssiCount = 0U; -} - diff --git a/NXDN96RX.h b/NXDN96RX.h deleted file mode 100644 index 1bff2ac..0000000 --- a/NXDN96RX.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2015,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(NXDNRX_H) -#define NXDNRX_H - -#include "Config.h" -#include "NXDNDefines.h" - -enum NXDNRX_STATE { - NXDNRXS_NONE, - NXDNRXS_DATA -}; - -class CNXDNRX { -public: - CNXDNRX(); - - void samples(const q15_t* samples, uint16_t* rssi, uint8_t length); - - void reset(); - -private: - NXDNRX_STATE m_state; - uint16_t m_bitBuffer[NXDN_RADIO_SYMBOL_LENGTH]; - q15_t m_buffer[NXDN_FRAME_LENGTH_SAMPLES]; - uint16_t m_bitPtr; - uint16_t m_dataPtr; - uint16_t m_startPtr; - uint16_t m_endPtr; - uint16_t m_fswPtr; - uint16_t m_minFSWPtr; - uint16_t m_maxFSWPtr; - q31_t m_maxCorr; - uint16_t m_lostCount; - uint8_t m_countdown; - q15_t m_centre[16U]; - q15_t m_centreVal; - q15_t m_threshold[16U]; - q15_t m_thresholdVal; - uint8_t m_averagePtr; - uint32_t m_rssiAccum; - uint16_t m_rssiCount; - - void processNone(q15_t sample); - void processData(q15_t sample); - bool correlateFSW(); - void calculateLevels(uint16_t start, uint16_t count); - void samplesToBits(uint16_t start, uint16_t count, uint8_t* buffer, uint16_t offset, q15_t centre, q15_t threshold); - void writeRSSIData(uint8_t* data); -}; - -#endif - diff --git a/NXDN96TX.cpp b/NXDN96TX.cpp deleted file mode 100644 index 16e6d55..0000000 --- a/NXDN96TX.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2009-2018 by Jonathan Naylor G4KLX - * Copyright (C) 2017 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" - -// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB -static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442, - -5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442, - -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 -const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L - -const q15_t NXDN_LEVELA = 1680; -const q15_t NXDN_LEVELB = 560; -const q15_t NXDN_LEVELC = -560; -const q15_t NXDN_LEVELD = -1680; - -const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; -const uint8_t NXDN_SYNC = 0x5FU; - -CNXDNTX::CNXDNTX() : -m_buffer(1500U), -m_modFilter(), -m_modState(), -m_poBuffer(), -m_poLen(0U), -m_poPtr(0U), -m_txDelay(240U) // 200ms -{ - ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); - - m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH; - m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN; - m_modFilter.pCoeffs = RRC_0_2_FILTER; - m_modFilter.pState = m_modState; -} - -void CNXDNTX::process() -{ - if (m_buffer.getData() == 0U && m_poLen == 0U) - return; - - if (m_poLen == 0U) { - if (!m_tx) { - for (uint16_t i = 0U; i < m_txDelay; 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 { - for (uint8_t i = 0U; i < NXDN_FRAME_LENGTH_BYTES; i++) { - uint8_t c = m_buffer.get(); - m_poBuffer[m_poLen++] = c; - } - } - - m_poPtr = 0U; - } - - if (m_poLen > 0U) { - uint16_t space = io.getSpace(); - - while (space > (4U * NXDN_RADIO_SYMBOL_LENGTH)) { - uint8_t c = m_poBuffer[m_poPtr++]; - writeByte(c); - - space -= 4U * NXDN_RADIO_SYMBOL_LENGTH; - - if (m_poPtr >= m_poLen) { - m_poPtr = 0U; - m_poLen = 0U; - 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) -{ - q15_t inBuffer[4U]; - q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; - - const uint8_t MASK = 0xC0U; - - for (uint8_t i = 0U; i < 4U; i++, c <<= 2) { - switch (c & MASK) { - case 0xC0U: - inBuffer[i] = NXDN_LEVELA; - break; - case 0x80U: - inBuffer[i] = NXDN_LEVELB; - break; - case 0x00U: - inBuffer[i] = NXDN_LEVELC; - break; - default: - inBuffer[i] = NXDN_LEVELD; - break; - } - } - - ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U); - - io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); -} - -void CNXDNTX::setTXDelay(uint8_t delay) -{ - m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay - - if (m_txDelay > 1200U) - m_txDelay = 1200U; -} - -uint8_t CNXDNTX::getSpace() const -{ - return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES; -} - diff --git a/NXDN96TX.h b/NXDN96TX.h deleted file mode 100644 index f98cf48..0000000 --- a/NXDN96TX.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2015,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(NXDNTX_H) -#define NXDNTX_H - -#include "Config.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); - - uint8_t getSpace() const; - -private: - CSerialRB m_buffer; - arm_fir_interpolate_instance_q15 m_modFilter; - q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare - uint8_t m_poBuffer[1200U]; - uint16_t m_poLen; - uint16_t m_poPtr; - uint16_t m_txDelay; - - void writeByte(uint8_t c); -}; - -#endif - diff --git a/NXDN48RX.cpp b/NXDNRX.cpp similarity index 100% rename from NXDN48RX.cpp rename to NXDNRX.cpp diff --git a/NXDN48RX.h b/NXDNRX.h similarity index 100% rename from NXDN48RX.h rename to NXDNRX.h diff --git a/NXDN48TX.cpp b/NXDNTX.cpp similarity index 100% rename from NXDN48TX.cpp rename to NXDNTX.cpp diff --git a/NXDN48TX.h b/NXDNTX.h similarity index 100% rename from NXDN48TX.h rename to NXDNTX.h From 46528127dcd9f13120d281cd0109cc1db6d1dbf9 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 20:25:16 +0000 Subject: [PATCH 16/29] Add support for NXDN 4800. --- IO.cpp | 102 +++++++++++++++++++++++++++++++------------------- IO.h | 8 +++- NXDNDefines.h | 2 +- NXDNTX.cpp | 21 ++++++----- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/IO.cpp b/IO.cpp index bfbb6f4..779dafa 100644 --- a/IO.cpp +++ b/IO.cpp @@ -28,10 +28,18 @@ const uint32_t DC_FILTER_STAGES = 1U; // One Biquad stage // Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB static q15_t RRC_0_2_FILTER[] = {401, 104, -340, -731, -847, -553, 112, 909, 1472, 1450, 683, -675, -2144, -3040, -2706, -770, 2667, 6995, - 11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112, - -553, -847, -731, -340, 104, 401, 0}; + 11237, 14331, 15464, 14331, 11237, 6995, 2667, -770, -2706, -3040, -2144, -675, 683, 1450, 1472, 909, 112, + -553, -847, -731, -340, 104, 401, 0}; const uint16_t RRC_0_2_FILTER_LEN = 42U; +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t NXDN_0_2_FILTER[] = {284, 198, 73, -78, -240, -393, -517, -590, -599, -533, -391, -181, 79, 364, 643, 880, 1041, 1097, 1026, 819, + 483, 39, -477, -1016, -1516, -1915, -2150, -2164, -1914, -1375, -545, 557, 1886, 3376, 4946, 6502, 7946, 9184, + 10134, 10731, 10935, 10731, 10134, 9184, 7946, 6502, 4946, 3376, 1886, 557, -545, -1375, -1914, -2164, -2150, + -1915, -1516, -1016, -477, 39, 483, 819, 1026, 1097, 1041, 880, 643, 364, 79, -181, -391, -533, -599, -590, + -517, -393, -240, -78, 73, 198, 284, 0}; +const uint16_t NXDN_0_2_FILTER_LEN = 82U; + // Generated using gaussfir(0.5, 4, 5) in MATLAB static q15_t GAUSSIAN_0_5_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0}; const uint16_t GAUSSIAN_0_5_FILTER_LEN = 12U; @@ -49,12 +57,16 @@ m_txBuffer(TX_RINGBUFFER_SIZE), m_rssiBuffer(RX_RINGBUFFER_SIZE), m_dcFilter(), m_dcState(), -m_rrcFilter(), +m_dmrFilter(), m_gaussianFilter(), m_boxcarFilter(), -m_rrcState(), +m_nxdnFilter(), +m_ysfFilter(), +m_dmrState(), m_gaussianState(), m_boxcarState(), +m_nxdnState(), +m_ysfState(), m_pttInvert(false), m_rxLevel(128 * 128), m_cwIdTXLevel(128 * 128), @@ -73,19 +85,21 @@ m_dacOverflow(0U), m_watchdog(0U), m_lockout(false) { - ::memset(m_rrcState, 0x00U, 70U * sizeof(q15_t)); - ::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t)); - ::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t)); - ::memset(m_dcState, 0x00U, 4U * sizeof(q31_t)); + ::memset(m_dmrState, 0x00U, 70U * sizeof(q15_t)); + ::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t)); + ::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t)); + ::memset(m_nxdnState, 0x00U, 110U * sizeof(q15_t)); + ::memset(m_ysfState, 0x00U, 70U * sizeof(q15_t)); + ::memset(m_dcState, 0x00U, 4U * sizeof(q31_t)); m_dcFilter.numStages = DC_FILTER_STAGES; m_dcFilter.pState = m_dcState; m_dcFilter.pCoeffs = DC_FILTER; m_dcFilter.postShift = 0; - m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN; - m_rrcFilter.pState = m_rrcState; - m_rrcFilter.pCoeffs = RRC_0_2_FILTER; + m_dmrFilter.numTaps = RRC_0_2_FILTER_LEN; + m_dmrFilter.pState = m_dmrState; + m_dmrFilter.pCoeffs = RRC_0_2_FILTER; m_gaussianFilter.numTaps = GAUSSIAN_0_5_FILTER_LEN; m_gaussianFilter.pState = m_gaussianState; @@ -95,6 +109,14 @@ m_lockout(false) m_boxcarFilter.pState = m_boxcarState; m_boxcarFilter.pCoeffs = BOXCAR_FILTER; + m_nxdnFilter.numTaps = NXDN_0_2_FILTER_LEN; + m_nxdnFilter.pState = m_nxdnState; + m_nxdnFilter.pCoeffs = NXDN_0_2_FILTER; + + m_ysfFilter.numTaps = RRC_0_2_FILTER_LEN; + m_ysfFilter.pState = m_ysfState; + m_ysfFilter.pCoeffs = RRC_0_2_FILTER; + initInt(); selfTest(); @@ -300,31 +322,35 @@ void CIO::process() dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE); } - if (m_p25Enable || m_nxdnEnable) { + if (m_p25Enable) { q15_t C4FSKVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); - if (m_p25Enable) - p25RX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); - - if (m_nxdnEnable) - nxdnRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + p25RX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); } - // XXX YSF should use dcSamples, but DMR not - if (m_dmrEnable || m_ysfEnable) { - q15_t C4FSKVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE); + if (m_nxdnEnable) { + q15_t NXDNVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); - if (m_dmrEnable) { - if (m_duplex) - dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE); - else - dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); - } + nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); + } - if (m_ysfEnable) - ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + if (m_ysfEnable) { + q15_t YSFVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_ysfFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); + + ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE); + } + + if (m_dmrEnable) { + q15_t DMRVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_dmrFilter, samples, DMRVals, RX_BLOCK_SIZE); + + if (m_duplex) + dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE); + else + dmrDMORX.samples(DMRVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -335,25 +361,25 @@ void CIO::process() } } else if (m_modemState == STATE_DMR) { if (m_dmrEnable) { - q15_t C4FSKVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_rrcFilter, samples, C4FSKVals, RX_BLOCK_SIZE); + q15_t DMRVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_dmrFilter, samples, DMRVals, RX_BLOCK_SIZE); if (m_duplex) { // If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs if (m_tx) - dmrRX.samples(C4FSKVals, rssi, control, RX_BLOCK_SIZE); + dmrRX.samples(DMRVals, rssi, control, RX_BLOCK_SIZE); else - dmrIdleRX.samples(C4FSKVals, RX_BLOCK_SIZE); + dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE); } else { - dmrDMORX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + dmrDMORX.samples(DMRVals, rssi, RX_BLOCK_SIZE); } } } else if (m_modemState == STATE_YSF) { if (m_ysfEnable) { - q15_t C4FSKVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); + q15_t YSFVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_ysfFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); - ysfRX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_P25) { if (m_p25Enable) { @@ -365,7 +391,7 @@ void CIO::process() } else if (m_modemState == STATE_NXDN) { if (m_nxdnEnable) { q15_t NXDNVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } diff --git a/IO.h b/IO.h index 0f1f62d..511253b 100644 --- a/IO.h +++ b/IO.h @@ -66,12 +66,16 @@ private: arm_biquad_casd_df1_inst_q31 m_dcFilter; q31_t m_dcState[4]; - arm_fir_instance_q15 m_rrcFilter; + arm_fir_instance_q15 m_dmrFilter; arm_fir_instance_q15 m_gaussianFilter; arm_fir_instance_q15 m_boxcarFilter; - q15_t m_rrcState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare + arm_fir_instance_q15 m_nxdnFilter; + arm_fir_instance_q15 m_ysfFilter; + q15_t m_dmrState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare + q15_t m_nxdnState[110U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare + q15_t m_ysfState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare bool m_pttInvert; q15_t m_rxLevel; diff --git a/NXDNDefines.h b/NXDNDefines.h index cbb7731..a98166f 100644 --- a/NXDNDefines.h +++ b/NXDNDefines.h @@ -19,7 +19,7 @@ #if !defined(NXDNDEFINES_H) #define NXDNDEFINES_H -const unsigned int NXDN_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate +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; diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 16e6d55..e655937 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -23,16 +23,19 @@ #include "NXDNDefines.h" -// Generated using rcosdesign(0.2, 8, 5, 'sqrt') in MATLAB -static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 850, 219, -720, -1548, -1795, -1172, 237, 1927, 3120, 3073, 1447, -1431, -4544, -6442, - -5735, -1633, 5651, 14822, 23810, 30367, 32767, 30367, 23810, 14822, 5651, -1633, -5735, -6442, - -4544, -1431, 1447, 3073, 3120, 1927, 237, -1172, -1795, -1548, -720, 219, 850}; // numTaps = 45, L = 5 +// Generated using rcosdesign(0.2, 8, 10, 'sqrt') in MATLAB +static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -720, -1179, -1548, -1769, -1795, -1597, -1172, + -544, 237, 1092, 1927, 2637, 3120, 3286, 3073, 2454, 1447, 116, -1431, -3043, -4544, -5739, -6442, + -6483, -5735, -4121, -1633, 1669, 5651, 10118, 14822, 19484, 23810, 27520, 30367, 32156, 32767, + 32156, 30367, 27520, 23810, 19484, 14822, 10118, 5651, 1669, -1633, -4121, -5735, -6483, -6442, + -5739, -4544, -3043, -1431, 116, 1447, 2454, 3073, 3286, 3120, 2637, 1927, 1092, 237, -544, -1172, + -1597, -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10 const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t NXDN_LEVELA = 1680; -const q15_t NXDN_LEVELB = 560; -const q15_t NXDN_LEVELC = -560; -const q15_t NXDN_LEVELD = -1680; +const q15_t NXDN_LEVELA = 840; +const q15_t NXDN_LEVELB = 280; +const q15_t NXDN_LEVELC = -280; +const q15_t NXDN_LEVELD = -840; const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; const uint8_t NXDN_SYNC = 0x5FU; @@ -148,6 +151,6 @@ void CNXDNTX::setTXDelay(uint8_t delay) uint8_t CNXDNTX::getSpace() const { - return m_buffer.getSpace() / YSF_FRAME_LENGTH_BYTES; + return m_buffer.getSpace() / NXDN_FRAME_LENGTH_BYTES; } From 9f28d2f69ac9564fad266f25dc80545956a205d5 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 21:02:07 +0000 Subject: [PATCH 17/29] Small cleanup. --- IO.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IO.cpp b/IO.cpp index 779dafa..c9bb583 100644 --- a/IO.cpp +++ b/IO.cpp @@ -323,10 +323,10 @@ void CIO::process() } if (m_p25Enable) { - q15_t C4FSKVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, C4FSKVals, RX_BLOCK_SIZE); + q15_t P25Vals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); - p25RX.samples(C4FSKVals, rssi, RX_BLOCK_SIZE); + p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); } if (m_nxdnEnable) { From 1d4bd4e40965e31411f70d5980317766bdcca8ca Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Thu, 8 Feb 2018 18:40:23 +0000 Subject: [PATCH 18/29] Make NXDN sync detection more lenient when receiving a transmission. --- NXDNRX.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NXDNRX.cpp b/NXDNRX.cpp index eab67dd..e9bd41b 100644 --- a/NXDNRX.cpp +++ b/NXDNRX.cpp @@ -23,9 +23,10 @@ const q15_t SCALING_FACTOR = 18750; // Q15(0.55) -const uint8_t MAX_FSW_BIT_ERRS = 1U; +const uint8_t MAX_FSW_BIT_START_ERRS = 1U; +const uint8_t MAX_FSW_BIT_RUN_ERRS = 3U; -const uint8_t MAX_FSW_SYMBOLS_ERRS = 1U; +const uint8_t MAX_FSW_SYMBOLS_ERRS = 2U; const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -255,11 +256,17 @@ bool CNXDNRX::correlateFSW() uint8_t sync[NXDN_FSW_BYTES_LENGTH]; samplesToBits(startPtr, NXDN_FSW_LENGTH_SYMBOLS, sync, 0U, m_centreVal, m_thresholdVal); - uint8_t errs = 0U; + uint8_t maxErrs; + if (m_state == NXDNRXS_NONE) + maxErrs = MAX_FSW_BIT_START_ERRS; + else + maxErrs = MAX_FSW_BIT_RUN_ERRS; + + uint8_t errs = 0U; for (uint8_t i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) errs += countBits8((sync[i] & NXDN_FSW_BYTES_MASK[i]) ^ NXDN_FSW_BYTES[i]); - if (errs <= MAX_FSW_BIT_ERRS) { + if (errs <= maxErrs) { m_maxCorr = corr; m_lostCount = MAX_FSW_FRAMES; m_fswPtr = m_dataPtr; From eebfc56013339c59f230d681802e70692d3457e8 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 8 Feb 2018 18:34:53 -0300 Subject: [PATCH 19/29] Add inverse SINC filter for NXDN demodulator --- IO.cpp | 19 +++++++++++++++++-- IO.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/IO.cpp b/IO.cpp index 779dafa..47dd8c5 100644 --- a/IO.cpp +++ b/IO.cpp @@ -40,6 +40,10 @@ static q15_t NXDN_0_2_FILTER[] = {284, 198, 73, -78, -240, -393, -517, -590, -59 -517, -393, -240, -78, 73, 198, 284, 0}; const uint16_t NXDN_0_2_FILTER_LEN = 82U; +static q15_t NXDN_ISINC_FILTER[] = {790, -1085, -1073, -553, 747, 2341, 3156, 2152, -893, -4915, -7834, -7536, -3102, 4441, 12354, 17394, 17394, + 12354, 4441, -3102, -7536, -7834, -4915, -893, 2152, 3156, 2341, 747, -553, -1073, -1085, 790}; +const uint16_t NXDN_ISINC_FILTER_LEN = 32U; + // Generated using gaussfir(0.5, 4, 5) in MATLAB static q15_t GAUSSIAN_0_5_FILTER[] = {8, 104, 760, 3158, 7421, 9866, 7421, 3158, 760, 104, 8, 0}; const uint16_t GAUSSIAN_0_5_FILTER_LEN = 12U; @@ -61,11 +65,13 @@ m_dmrFilter(), m_gaussianFilter(), m_boxcarFilter(), m_nxdnFilter(), +m_nxdnISincFilter(), m_ysfFilter(), m_dmrState(), m_gaussianState(), m_boxcarState(), m_nxdnState(), +m_nxdnISincState(), m_ysfState(), m_pttInvert(false), m_rxLevel(128 * 128), @@ -89,6 +95,7 @@ m_lockout(false) ::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t)); ::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t)); ::memset(m_nxdnState, 0x00U, 110U * sizeof(q15_t)); + ::memset(m_nxdnISincState, 0x00U, 60U * sizeof(q15_t)); ::memset(m_ysfState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_dcState, 0x00U, 4U * sizeof(q31_t)); @@ -112,6 +119,10 @@ m_lockout(false) m_nxdnFilter.numTaps = NXDN_0_2_FILTER_LEN; m_nxdnFilter.pState = m_nxdnState; m_nxdnFilter.pCoeffs = NXDN_0_2_FILTER; + + m_nxdnISincFilter.numTaps = NXDN_ISINC_FILTER_LEN; + m_nxdnISincFilter.pState = m_nxdnISincState; + m_nxdnISincFilter.pCoeffs = NXDN_ISINC_FILTER; m_ysfFilter.numTaps = RRC_0_2_FILTER_LEN; m_ysfFilter.pState = m_ysfState; @@ -331,7 +342,9 @@ void CIO::process() if (m_nxdnEnable) { q15_t NXDNVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); + q15_t NXDNValsTmp[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); + ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } @@ -391,7 +404,9 @@ void CIO::process() } else if (m_modemState == STATE_NXDN) { if (m_nxdnEnable) { q15_t NXDNVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNVals, RX_BLOCK_SIZE); + q15_t NXDNValsTmp[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); + ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } diff --git a/IO.h b/IO.h index 511253b..4600bd9 100644 --- a/IO.h +++ b/IO.h @@ -70,11 +70,13 @@ private: arm_fir_instance_q15 m_gaussianFilter; arm_fir_instance_q15 m_boxcarFilter; arm_fir_instance_q15 m_nxdnFilter; + arm_fir_instance_q15 m_nxdnISincFilter; arm_fir_instance_q15 m_ysfFilter; q15_t m_dmrState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare q15_t m_nxdnState[110U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare + q15_t m_nxdnISincState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare q15_t m_ysfState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare bool m_pttInvert; From 3ea95ce53d73e34f359e2e048066decd60f01733 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Thu, 8 Feb 2018 21:31:45 -0300 Subject: [PATCH 20/29] Add SINC filter for NXDN modulator and new deviation values --- NXDNTX.cpp | 24 +++++++++++++++++++----- NXDNTX.h | 2 ++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/NXDNTX.cpp b/NXDNTX.cpp index e655937..23ed5d4 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -32,10 +32,14 @@ static q15_t RRC_0_2_FILTER[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 850, 592, 219, -234, -1597, -1795, -1769, -1548, -1179, -720, -234, 219, 592, 850}; // numTaps = 90, L = 10 const uint16_t RRC_0_2_FILTER_PHASE_LEN = 9U; // phaseLength = numTaps/L -const q15_t NXDN_LEVELA = 840; -const q15_t NXDN_LEVELB = 280; -const q15_t NXDN_LEVELC = -280; -const q15_t NXDN_LEVELD = -840; +static q15_t NXDN_SINC_FILTER[] = {572, -1003, -253, 254, 740, 1290, 1902, 2527, 3090, 3517, 3747, 3747, 3517, 3090, 2527, 1902, + 1290, 740, 254, -253, -1003, 572}; +const uint16_t NXDN_SINC_FILTER_LEN = 22U; + +const q15_t NXDN_LEVELA = 735; +const q15_t NXDN_LEVELB = 245; +const q15_t NXDN_LEVELC = -245; +const q15_t NXDN_LEVELD = -735; const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; const uint8_t NXDN_SYNC = 0x5FU; @@ -43,18 +47,25 @@ const uint8_t NXDN_SYNC = 0x5FU; CNXDNTX::CNXDNTX() : m_buffer(1500U), m_modFilter(), +m_sincFilter(), m_modState(), +m_sincState(), m_poBuffer(), m_poLen(0U), m_poPtr(0U), m_txDelay(240U) // 200ms { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); + ::memset(m_sincState, 0x00U, 70U * sizeof(q15_t)); m_modFilter.L = NXDN_RADIO_SYMBOL_LENGTH; m_modFilter.phaseLength = RRC_0_2_FILTER_PHASE_LEN; m_modFilter.pCoeffs = RRC_0_2_FILTER; m_modFilter.pState = m_modState; + + m_sincFilter.numTaps = NXDN_SINC_FILTER_LEN; + m_sincFilter.pState = m_sincState; + m_sincFilter.pCoeffs = NXDN_SINC_FILTER; } void CNXDNTX::process() @@ -115,6 +126,7 @@ uint8_t CNXDNTX::writeData(const uint8_t* data, uint8_t length) void CNXDNTX::writeByte(uint8_t c) { q15_t inBuffer[4U]; + q15_t intBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; q15_t outBuffer[NXDN_RADIO_SYMBOL_LENGTH * 4U]; const uint8_t MASK = 0xC0U; @@ -136,7 +148,9 @@ void CNXDNTX::writeByte(uint8_t c) } } - ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, outBuffer, 4U); + ::arm_fir_interpolate_q15(&m_modFilter, inBuffer, intBuffer, 4U); + + ::arm_fir_fast_q15(&m_sincFilter, intBuffer, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); io.write(STATE_NXDN, outBuffer, NXDN_RADIO_SYMBOL_LENGTH * 4U); } diff --git a/NXDNTX.h b/NXDNTX.h index f98cf48..eec74d4 100644 --- a/NXDNTX.h +++ b/NXDNTX.h @@ -38,7 +38,9 @@ public: private: CSerialRB m_buffer; arm_fir_interpolate_instance_q15 m_modFilter; + arm_fir_instance_q15 m_sincFilter; q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare + q15_t m_sincState[70U]; // NoTaps + BlockSize - 1, 22 + 40 - 1 plus some spare uint8_t m_poBuffer[1200U]; uint16_t m_poLen; uint16_t m_poPtr; From 8a1ac05df21c1fff35a415f81d0ae8a838b3d5b4 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sat, 10 Feb 2018 14:44:30 -0300 Subject: [PATCH 21/29] Fixing inverse SINC TX filter for P25 --- P25TX.cpp | 12 +++++------- P25TX.h | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/P25TX.cpp b/P25TX.cpp index 2901a74..57a8db0 100644 --- a/P25TX.cpp +++ b/P25TX.cpp @@ -30,12 +30,10 @@ static q15_t RC_0_2_FILTER[] = {-897, -1636, -1840, -1278, 0, 1613, 2936, 3310, -3011, 0, 2315, 3310, 2936, 1613, 0, -1278, -1840, -1636, -897, 0}; // numTaps = 40, L = 5 const uint16_t RC_0_2_FILTER_PHASE_LEN = 8U; // phaseLength = numTaps/L -// Generated in MATLAB using the following commands, and then normalised for unity gain -// shape2 = 'Inverse-sinc Lowpass'; -// d2 = fdesign.interpolator(1, shape2); -// h2 = design(d2, 'SystemObject', true); -static q15_t LOWPASS_FILTER[] = {1294, -2251, 4312, -8402, 20999, 20999, -8402, 4312, -2251, 1294}; -const uint16_t LOWPASS_FILTER_LEN = 10U; +static q15_t LOWPASS_FILTER[] = {124, -188, -682, 1262, 556, -621, -1912, -911, 2058, 3855, 1234, -4592, -7692, -2799, + 8556, 18133, 18133, 8556, -2799, -7692, -4592, 1234, 3855, 2058, -911, -1912, -621, + 556, 1262, -682, -188, 124}; +const uint16_t LOWPASS_FILTER_LEN = 32U; const q15_t P25_LEVELA = 1260; const q15_t P25_LEVELB = 420; @@ -56,7 +54,7 @@ m_poPtr(0U), m_txDelay(240U) // 200ms { ::memset(m_modState, 0x00U, 16U * sizeof(q15_t)); - ::memset(m_lpState, 0x00U, 70U * sizeof(q15_t)); + ::memset(m_lpState, 0x00U, 60U * sizeof(q15_t)); m_modFilter.L = P25_RADIO_SYMBOL_LENGTH; m_modFilter.phaseLength = RC_0_2_FILTER_PHASE_LEN; diff --git a/P25TX.h b/P25TX.h index 61f6a87..1d542f9 100644 --- a/P25TX.h +++ b/P25TX.h @@ -40,7 +40,7 @@ private: arm_fir_interpolate_instance_q15 m_modFilter; arm_fir_instance_q15 m_lpFilter; q15_t m_modState[16U]; // blockSize + phaseLength - 1, 4 + 9 - 1 plus some spare - q15_t m_lpState[70U]; // NoTaps + BlockSize - 1, 44 + 20 - 1 plus some spare + q15_t m_lpState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare uint8_t m_poBuffer[1200U]; uint16_t m_poLen; uint16_t m_poPtr; From 66241f2f1bcfb31e930cb2e60e05e60279613b83 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sun, 11 Feb 2018 21:37:54 -0300 Subject: [PATCH 22/29] Add DMR 1031 Hz Test Pattern for DMO mode --- CalDMR.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++--- CalDMR.h | 2 ++ Globals.h | 1 + MMDVM.cpp | 2 +- MMDVM.ino | 2 +- SerialPort.cpp | 18 +++++++++--- 6 files changed, 92 insertions(+), 10 deletions(-) diff --git a/CalDMR.cpp b/CalDMR.cpp index 1fc4058..5a92fb0 100644 --- a/CalDMR.cpp +++ b/CalDMR.cpp @@ -21,25 +21,37 @@ #include "Globals.h" #include "CalDMR.h" -// Voice LC Header, CC: 1, srcID: 1, dstID: TG9 +// Voice LC BS Header, CC: 1, srcID: 1, dstID: TG9 const uint8_t VH_1K[] = {0x00U, 0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U, 0x60U, 0x84U, 0x6DU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xDEU, 0x30U, 0x30U, 0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU}; -// Voice Term with LC, CC: 1, srcID: 1, dstID: TG9 +// Voice Term BS with LC, CC: 1, srcID: 1, dstID: TG9 const uint8_t VT_1K[] = {0x00U, 0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U, 0xC0U, 0x84U, 0xADU, 0xFFU, 0x57U, 0xD7U, 0x5DU, 0xF5U, 0xD9U, 0x65U, 0x24U, 0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU}; +// Voice LC MS Header, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VH_DMO1K[] = {0x00U, + 0x00U, 0x20U, 0x08U, 0x08U, 0x02U, 0x38U, 0x15U, 0x00U, 0x2CU, 0xA0U, 0x14U, + 0x60U, 0x84U, 0x6DU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x7EU, 0x30U, 0x30U, + 0x01U, 0x10U, 0x01U, 0x40U, 0x03U, 0xC0U, 0x13U, 0xC1U, 0x1EU, 0x80U, 0x6FU}; + +// Voice Term MS with LC, CC: 1, srcID: 1, dstID: TG9 +const uint8_t VT_DMO1K[] = {0x00U, + 0x00U, 0x4FU, 0x08U, 0xDCU, 0x02U, 0x88U, 0x15U, 0x78U, 0x2CU, 0xD0U, 0x14U, + 0xC0U, 0x84U, 0xADU, 0x5DU, 0x7FU, 0x77U, 0xFDU, 0x75U, 0x79U, 0x65U, 0x24U, + 0x02U, 0x28U, 0x06U, 0x20U, 0x0FU, 0x80U, 0x1BU, 0xC1U, 0x07U, 0x80U, 0x5CU}; + // Voice coding data + FEC, 1031 Hz Test Pattern const uint8_t VOICE_1K[] = {0x00U, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xA0U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x0CU, 0xC4U, 0x58U, 0x20U, 0x0AU, 0xCEU, 0xA8U, 0xFEU, 0x83U, 0xACU, 0xC4U, 0x58U, 0x20U, 0x0AU}; -// Embedded LC: CC: 1, srcID: 1, dstID: TG9 +// Embedded LC BS: CC: 1, srcID: 1, dstID: TG9 const uint8_t SYNCEMB_1K[6][7] = { {0x07U, 0x55U, 0xFDU, 0x7DU, 0xF7U, 0x5FU, 0x70U}, // BS VOICE SYNC (audio seq 0) {0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1) @@ -48,6 +60,15 @@ const uint8_t SYNCEMB_1K[6][7] = { {0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4) {0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5) +// Embedded LC MS: CC: 1, srcID: 1, dstID: TG9 +const uint8_t SYNCEMB_DMO1K[6][7] = { + {0x07U, 0xF7U, 0xD5U, 0xDDU, 0x57U, 0xDFU, 0xD0U}, // MS VOICE SYNC (audio seq 0) + {0x01U, 0x30U, 0x00U, 0x00U, 0x90U, 0x09U, 0x10U}, // EMB + Embedded LC1 (audio seq 1) + {0x01U, 0x70U, 0x00U, 0x90U, 0x00U, 0x07U, 0x40U}, // EMB + Embedded LC2 (audio seq 2) + {0x01U, 0x70U, 0x00U, 0x31U, 0x40U, 0x07U, 0x40U}, // EMB + Embedded LC3 (audio seq 3) + {0x01U, 0x50U, 0xA1U, 0x71U, 0xD1U, 0x70U, 0x70U}, // EMB + Embedded LC4 (audio seq 4) + {0x01U, 0x10U, 0x00U, 0x00U, 0x00U, 0x0EU, 0x20U}}; // EMB (audio seq 5) + // Short LC: // TS1: dstID: 0, ACTIVITY_NONE // TS2: dstID: TG9, ACTIVITY_VOICE @@ -78,6 +99,9 @@ void CCalDMR::process() case STATE_DMRCAL1K: dmr1kcal(); break; + case STATE_DMRDMO1K: + dmrdmo1k(); + break; default: break; } @@ -94,6 +118,17 @@ void CCalDMR::createData1k(uint8_t n) m_dmr1k[20U] |= SYNCEMB_1K[n][6] & 0xF0U; } +void CCalDMR::createDataDMO1k(uint8_t n) +{ + for(uint8_t i = 0; i < 5U; i++) + m_dmr1k[i + 15U] = SYNCEMB_DMO1K[n][i + 1U]; + + m_dmr1k[14U] &= 0xF0U; + m_dmr1k[20U] &= 0x0FU; + m_dmr1k[14U] |= SYNCEMB_DMO1K[n][0] & 0x0FU; + m_dmr1k[20U] |= SYNCEMB_DMO1K[n][6] & 0xF0U; +} + void CCalDMR::dmr1kcal() { dmrTX.process(); @@ -139,6 +174,40 @@ void CCalDMR::dmr1kcal() } } +void CCalDMR::dmrdmo1k() +{ + dmrDMOTX.process(); + + uint16_t space = dmrDMOTX.getSpace(); + if (space < 1U) + return; + + switch (m_state) { + case DMRCAL1K_VH: + dmrDMOTX.writeData(VH_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U); + m_state = DMRCAL1K_VOICE; + break; + case DMRCAL1K_VOICE: + createDataDMO1k(m_audioSeq); + dmrDMOTX.writeData(m_dmr1k, DMR_FRAME_LENGTH_BYTES + 1U); + if(m_audioSeq == 5U) { + m_audioSeq = 0U; + if(!m_transmit) + m_state = DMRCAL1K_VT; + } else + m_audioSeq++; + break; + case DMRCAL1K_VT: + dmrDMOTX.writeData(VT_DMO1K, DMR_FRAME_LENGTH_BYTES + 1U); + m_state = DMRCAL1K_IDLE; + break; + default: + m_state = DMRCAL1K_IDLE; + m_audioSeq = 0U; + break; + } +} + uint8_t CCalDMR::write(const uint8_t* data, uint8_t length) { if (length != 1U) @@ -146,7 +215,7 @@ uint8_t CCalDMR::write(const uint8_t* data, uint8_t length) m_transmit = data[0U] == 1U; - if(m_transmit && m_state == DMRCAL1K_IDLE && m_modemState == STATE_DMRCAL1K) + if(m_transmit && m_state == DMRCAL1K_IDLE && (m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K)) m_state = DMRCAL1K_VH; return 0U; diff --git a/CalDMR.h b/CalDMR.h index 8ff5ae5..06ff6e6 100644 --- a/CalDMR.h +++ b/CalDMR.h @@ -37,7 +37,9 @@ public: void process(); void dmr1kcal(); + void dmrdmo1k(); void createData1k(uint8_t n); + void createDataDMO1k(uint8_t n); uint8_t write(const uint8_t* data, uint8_t length); diff --git a/Globals.h b/Globals.h index d671e7b..fc7d0eb 100644 --- a/Globals.h +++ b/Globals.h @@ -50,6 +50,7 @@ enum MMDVM_STATE { STATE_P25 = 4, // Dummy states start at 90 + STATE_DMRDMO1K = 92, STATE_P25CAL1K = 93, STATE_DMRCAL1K = 94, STATE_LFCAL = 95, diff --git a/MMDVM.cpp b/MMDVM.cpp index 37c0a41..cc03554 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -94,7 +94,7 @@ void loop() if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); - if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) calDMR.process(); if (m_modemState == STATE_P25CAL1K) diff --git a/MMDVM.ino b/MMDVM.ino index d132f2a..35527a8 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -91,7 +91,7 @@ void loop() if (m_modemState == STATE_DSTARCAL) calDStarTX.process(); - if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) calDMR.process(); if (m_modemState == STATE_P25CAL1K) diff --git a/SerialPort.cpp b/SerialPort.cpp index d734013..ff95875 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -243,7 +243,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 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -309,7 +309,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 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -403,7 +403,7 @@ void CSerialPort::setMode(MMDVM_STATE modemState) cwIdTX.reset(); break; case STATE_DMRCAL1K: - DEBUG1("Mode set to DMR 1031 Hz Calibrate"); + DEBUG1("Mode set to DMR BS 1031 Hz Calibrate"); dmrIdleRX.reset(); dmrDMORX.reset(); dmrRX.reset(); @@ -422,6 +422,16 @@ void CSerialPort::setMode(MMDVM_STATE modemState) p25RX.reset(); cwIdTX.reset(); break; + case STATE_DMRDMO1K: + DEBUG1("Mode set to DMR MS 1031 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + cwIdTX.reset(); + break; default: DEBUG1("Mode set to Idle"); // STATE_IDLE @@ -503,7 +513,7 @@ void CSerialPort::process() case MMDVM_CAL_DATA: if (m_modemState == STATE_DSTARCAL) err = calDStarTX.write(m_buffer + 3U, m_len - 3U); - if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K) + if (m_modemState == STATE_DMRCAL || m_modemState == STATE_LFCAL || m_modemState == STATE_DMRCAL1K || m_modemState == STATE_DMRDMO1K) err = calDMR.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_P25CAL1K) err = calP25.write(m_buffer + 3U, m_len - 3U); From ce6d03ae137c8a1fec44fcde5b18fb9f8a9a6cde Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 12 Feb 2018 10:34:49 -0300 Subject: [PATCH 23/29] Add NXDN 1031 Hz Test Pattern --- CalNXDN.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ CalNXDN.h | 44 +++++++++++++++++++++++ Globals.h | 3 ++ MMDVM.cpp | 4 +++ MMDVM.ino | 4 +++ SerialPort.cpp | 18 ++++++++-- 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 CalNXDN.cpp create mode 100644 CalNXDN.h diff --git a/CalNXDN.cpp b/CalNXDN.cpp new file mode 100644 index 0000000..c223f59 --- /dev/null +++ b/CalNXDN.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "Config.h" +#include "Globals.h" +#include "CalNXDN.h" + +// 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}}; + +CCalNXDN::CCalNXDN() : +m_transmit(false), +m_state(NXDNCAL1K_IDLE), +m_audioSeq(0U) +{ +} + +void CCalNXDN::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; + } +} + +uint8_t CCalNXDN::write(const uint8_t* data, uint8_t length) +{ + if (length != 1U) + return 4U; + + m_transmit = data[0U] == 1U; + + if(m_transmit && m_state == NXDNCAL1K_IDLE) + m_state = NXDNCAL1K_TX; + + return 0U; +} + diff --git a/CalNXDN.h b/CalNXDN.h new file mode 100644 index 0000000..f0051ef --- /dev/null +++ b/CalNXDN.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 by Andy Uribe CA6JAU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(CALNXDN_H) +#define CALNXDN_H + +#include "Config.h" + +enum NXDNCAL1K { + NXDNCAL1K_IDLE, + NXDNCAL1K_TX +}; + +class CCalNXDN { +public: + CCalNXDN(); + + void process(); + + uint8_t write(const uint8_t* data, uint8_t length); + +private: + bool m_transmit; + NXDNCAL1K m_state; + uint8_t m_audioSeq; +}; + +#endif + diff --git a/Globals.h b/Globals.h index 1f5b932..9ab7d31 100644 --- a/Globals.h +++ b/Globals.h @@ -51,6 +51,7 @@ enum MMDVM_STATE { STATE_NXDN = 5, // Dummy states start at 90 + STATE_NXDNCAL1K = 91, STATE_DMRDMO1K = 92, STATE_P25CAL1K = 93, STATE_DMRCAL1K = 94, @@ -79,6 +80,7 @@ enum MMDVM_STATE { #include "CalDStarTX.h" #include "CalDMR.h" #include "CalP25.h" +#include "CalNXDN.h" #include "CalRSSI.h" #include "CWIdTX.h" #include "Debug.h" @@ -132,6 +134,7 @@ extern CCalDStarRX calDStarRX; extern CCalDStarTX calDStarTX; extern CCalDMR calDMR; extern CCalP25 calP25; +extern CCalNXDN calNXDN; extern CCalRSSI calRSSI; extern CCWIdTX cwIdTX; diff --git a/MMDVM.cpp b/MMDVM.cpp index 861dc3d..d4a12a9 100644 --- a/MMDVM.cpp +++ b/MMDVM.cpp @@ -60,6 +60,7 @@ CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; CCalP25 calP25; +CCalNXDN calNXDN; CCalRSSI calRSSI; CCWIdTX cwIdTX; @@ -107,6 +108,9 @@ void loop() if (m_modemState == STATE_P25CAL1K) calP25.process(); + if (m_modemState == STATE_NXDNCAL1K) + calNXDN.process(); + if (m_modemState == STATE_IDLE) cwIdTX.process(); } diff --git a/MMDVM.ino b/MMDVM.ino index c5237f2..0b0f852 100644 --- a/MMDVM.ino +++ b/MMDVM.ino @@ -57,6 +57,7 @@ CCalDStarRX calDStarRX; CCalDStarTX calDStarTX; CCalDMR calDMR; CCalP25 calP25; +CCalNXDN calNXDN; CCalRSSI calRSSI; CCWIdTX cwIdTX; @@ -104,6 +105,9 @@ void loop() if (m_modemState == STATE_P25CAL1K) calP25.process(); + if (m_modemState == STATE_NXDNCAL1K) + calNXDN.process(); + if (m_modemState == STATE_IDLE) cwIdTX.process(); } diff --git a/SerialPort.cpp b/SerialPort.cpp index d51dfa1..8365ad2 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -254,7 +254,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 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !dstarEnable) return 4U; @@ -326,7 +326,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 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K) + if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_P25 && modemState != STATE_NXDN && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL && modemState != STATE_RSSICAL && modemState != STATE_LFCAL && modemState != STATE_DMRCAL1K && modemState != STATE_P25CAL1K && modemState != STATE_DMRDMO1K && modemState != STATE_NXDNCAL1K) return 4U; if (modemState == STATE_DSTAR && !m_dstarEnable) return 4U; @@ -469,6 +469,18 @@ void CSerialPort::setMode(MMDVM_STATE modemState) dstarRX.reset(); ysfRX.reset(); p25RX.reset(); + nxdnRX.reset(); + cwIdTX.reset(); + break; + case STATE_NXDNCAL1K: + DEBUG1("Mode set to NXDN 1031 Hz Calibrate"); + dmrIdleRX.reset(); + dmrDMORX.reset(); + dmrRX.reset(); + dstarRX.reset(); + ysfRX.reset(); + p25RX.reset(); + nxdnRX.reset(); cwIdTX.reset(); break; default: @@ -556,6 +568,8 @@ void CSerialPort::process() err = calDMR.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_P25CAL1K) err = calP25.write(m_buffer + 3U, m_len - 3U); + if (m_modemState == STATE_NXDNCAL1K) + err = calNXDN.write(m_buffer + 3U, m_len - 3U); if (err == 0U) { sendACK(); } else { From 97a72f5b7fbfb75bcd54ca0cfa328ccf6b1ce20f Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Mon, 12 Feb 2018 10:45:38 -0300 Subject: [PATCH 24/29] Fix Config.h overwrite for previous commit --- Config.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Config.h b/Config.h index 37bfb5d..b201991 100644 --- a/Config.h +++ b/Config.h @@ -39,7 +39,7 @@ // #define USE_COS_AS_LOCKOUT // Use pins to output the current mode -#define ARDUINO_MODE_PINS +// #define ARDUINO_MODE_PINS // For the original Arduino Due pin layout // #define ARDUINO_DUE_PAPA @@ -54,7 +54,7 @@ // #define ARDUINO_DUE_NTH // For ST Nucleo-64 STM32F446RE board -#define STM32F4_NUCLEO_MORPHO_HEADER +// #define STM32F4_NUCLEO_MORPHO_HEADER // #define STM32F4_NUCLEO_ARDUINO_HEADER // Use separate mode pins to switch external filters/bandwidth for example @@ -64,6 +64,6 @@ // #define SEND_RSSI_DATA // Use the modem as a serial repeater for Nextion displays -#define SERIAL_REPEATER +// #define SERIAL_REPEATER #endif From d5c2fe92800695d6f77b77489d7ba8eb0e5e172d Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 12 Feb 2018 19:23:21 +0000 Subject: [PATCH 25/29] Change the TX Delay calculation because of the longer symbol length. --- NXDNTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 23ed5d4..495a46f 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -157,7 +157,7 @@ void CNXDNTX::writeByte(uint8_t c) void CNXDNTX::setTXDelay(uint8_t delay) { - m_txDelay = 600U + uint16_t(delay) * 12U; // 500ms + tx delay + m_txDelay = 300U + uint16_t(delay) * 6U; // 500ms + tx delay if (m_txDelay > 1200U) m_txDelay = 1200U; From 9410e724b8906f9645be5da283c023c0cc2996b0 Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Sun, 18 Feb 2018 12:21:20 -0300 Subject: [PATCH 26/29] Increasing TX buffer for NXDN --- NXDNTX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NXDNTX.cpp b/NXDNTX.cpp index 495a46f..905c502 100644 --- a/NXDNTX.cpp +++ b/NXDNTX.cpp @@ -45,7 +45,7 @@ const uint8_t NXDN_PREAMBLE[] = {0x57U, 0x75U, 0xFDU}; const uint8_t NXDN_SYNC = 0x5FU; CNXDNTX::CNXDNTX() : -m_buffer(1500U), +m_buffer(2000U), m_modFilter(), m_sincFilter(), m_modState(), From 23f27cf496b2bb831c2e08972de35fafa7564c7f Mon Sep 17 00:00:00 2001 From: Andy CA6JAU Date: Fri, 23 Feb 2018 16:14:26 -0300 Subject: [PATCH 27/29] Bump the version date --- SerialPort.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SerialPort.cpp b/SerialPort.cpp index 8365ad2..e0d6193 100644 --- a/SerialPort.cpp +++ b/SerialPort.cpp @@ -76,9 +76,9 @@ const uint8_t MMDVM_DEBUG5 = 0xF5U; #if defined(EXTERNAL_OSC) -#define DESCRIPTION "MMDVM 20170501 TCXO (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" +#define DESCRIPTION "MMDVM 20180223 TCXO (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" #else -#define DESCRIPTION "MMDVM 20170501 (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" +#define DESCRIPTION "MMDVM 20180223 (D-Star/DMR/System Fusion/P25/NXDN/RSSI/CW Id)" #endif #if defined(GITVERSION) From 01b3dcc6898b7cf2e1b853330af0300f7017967b Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 26 Feb 2018 19:37:15 +0000 Subject: [PATCH 28/29] Reduce CPU load on idle. --- IO.cpp | 47 +++++++++++++++++++---------------------------- IO.h | 6 ++---- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/IO.cpp b/IO.cpp index 7508bf5..1f95d14 100644 --- a/IO.cpp +++ b/IO.cpp @@ -61,18 +61,16 @@ m_txBuffer(TX_RINGBUFFER_SIZE), m_rssiBuffer(RX_RINGBUFFER_SIZE), m_dcFilter(), m_dcState(), -m_dmrFilter(), +m_rrcFilter(), m_gaussianFilter(), m_boxcarFilter(), m_nxdnFilter(), m_nxdnISincFilter(), -m_ysfFilter(), -m_dmrState(), +m_rrcState(), m_gaussianState(), m_boxcarState(), m_nxdnState(), m_nxdnISincState(), -m_ysfState(), m_pttInvert(false), m_rxLevel(128 * 128), m_cwIdTXLevel(128 * 128), @@ -91,12 +89,11 @@ m_dacOverflow(0U), m_watchdog(0U), m_lockout(false) { - ::memset(m_dmrState, 0x00U, 70U * sizeof(q15_t)); + ::memset(m_rrcState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_gaussianState, 0x00U, 40U * sizeof(q15_t)); ::memset(m_boxcarState, 0x00U, 30U * sizeof(q15_t)); ::memset(m_nxdnState, 0x00U, 110U * sizeof(q15_t)); ::memset(m_nxdnISincState, 0x00U, 60U * sizeof(q15_t)); - ::memset(m_ysfState, 0x00U, 70U * sizeof(q15_t)); ::memset(m_dcState, 0x00U, 4U * sizeof(q31_t)); m_dcFilter.numStages = DC_FILTER_STAGES; @@ -104,9 +101,9 @@ m_lockout(false) m_dcFilter.pCoeffs = DC_FILTER; m_dcFilter.postShift = 0; - m_dmrFilter.numTaps = RRC_0_2_FILTER_LEN; - m_dmrFilter.pState = m_dmrState; - m_dmrFilter.pCoeffs = RRC_0_2_FILTER; + m_rrcFilter.numTaps = RRC_0_2_FILTER_LEN; + m_rrcFilter.pState = m_rrcState; + m_rrcFilter.pCoeffs = RRC_0_2_FILTER; m_gaussianFilter.numTaps = GAUSSIAN_0_5_FILTER_LEN; m_gaussianFilter.pState = m_gaussianState; @@ -124,10 +121,6 @@ m_lockout(false) m_nxdnISincFilter.pState = m_nxdnISincState; m_nxdnISincFilter.pCoeffs = NXDN_ISINC_FILTER; - m_ysfFilter.numTaps = RRC_0_2_FILTER_LEN; - m_ysfFilter.pState = m_ysfState; - m_ysfFilter.pCoeffs = RRC_0_2_FILTER; - initInt(); selfTest(); @@ -349,21 +342,19 @@ void CIO::process() nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); } - if (m_ysfEnable) { - q15_t YSFVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_ysfFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); + if (m_dmrEnable || m_ysfEnable) { + q15_t RRCVals[RX_BLOCK_SIZE]; + ::arm_fir_fast_q15(&m_rrcFilter, samples, RRCVals, RX_BLOCK_SIZE); - ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE); - } + if (m_ysfEnable) + ysfRX.samples(RRCVals, rssi, RX_BLOCK_SIZE); - if (m_dmrEnable) { - q15_t DMRVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_dmrFilter, samples, DMRVals, RX_BLOCK_SIZE); - - if (m_duplex) - dmrIdleRX.samples(DMRVals, RX_BLOCK_SIZE); - else - dmrDMORX.samples(DMRVals, rssi, RX_BLOCK_SIZE); + if (m_dmrEnable) { + if (m_duplex) + dmrIdleRX.samples(RRCVals, RX_BLOCK_SIZE); + else + dmrDMORX.samples(RRCVals, rssi, RX_BLOCK_SIZE); + } } } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { @@ -375,7 +366,7 @@ void CIO::process() } else if (m_modemState == STATE_DMR) { if (m_dmrEnable) { q15_t DMRVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_dmrFilter, samples, DMRVals, RX_BLOCK_SIZE); + ::arm_fir_fast_q15(&m_rrcFilter, samples, DMRVals, RX_BLOCK_SIZE); if (m_duplex) { // If the transmitter isn't on, use the DMR idle RX to detect the wakeup CSBKs @@ -390,7 +381,7 @@ void CIO::process() } else if (m_modemState == STATE_YSF) { if (m_ysfEnable) { q15_t YSFVals[RX_BLOCK_SIZE]; - ::arm_fir_fast_q15(&m_ysfFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); + ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE); } diff --git a/IO.h b/IO.h index 4600bd9..79a2b69 100644 --- a/IO.h +++ b/IO.h @@ -66,18 +66,16 @@ private: arm_biquad_casd_df1_inst_q31 m_dcFilter; q31_t m_dcState[4]; - arm_fir_instance_q15 m_dmrFilter; + arm_fir_instance_q15 m_rrcFilter; arm_fir_instance_q15 m_gaussianFilter; arm_fir_instance_q15 m_boxcarFilter; arm_fir_instance_q15 m_nxdnFilter; arm_fir_instance_q15 m_nxdnISincFilter; - arm_fir_instance_q15 m_ysfFilter; - q15_t m_dmrState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare + q15_t m_rrcState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare q15_t m_gaussianState[40U]; // NoTaps + BlockSize - 1, 12 + 20 - 1 plus some spare q15_t m_boxcarState[30U]; // NoTaps + BlockSize - 1, 6 + 20 - 1 plus some spare q15_t m_nxdnState[110U]; // NoTaps + BlockSize - 1, 82 + 20 - 1 plus some spare q15_t m_nxdnISincState[60U]; // NoTaps + BlockSize - 1, 32 + 20 - 1 plus some spare - q15_t m_ysfState[70U]; // NoTaps + BlockSize - 1, 42 + 20 - 1 plus some spare bool m_pttInvert; q15_t m_rxLevel; From b21c1f21a4e54ef958fb00bbcfb9d264e6086135 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Mon, 26 Feb 2018 19:55:14 +0000 Subject: [PATCH 29/29] Make the DC blocker optional to reduce CPU load. --- Config.h | 3 +++ IO.cpp | 45 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/Config.h b/Config.h index b201991..1de81a0 100644 --- a/Config.h +++ b/Config.h @@ -66,4 +66,7 @@ // Use the modem as a serial repeater for Nextion displays // #define SERIAL_REPEATER +// To reduce CPU load, you can remove the DC blocker by commenting out the next line +#define USE_DCBLOCKER + #endif diff --git a/IO.cpp b/IO.cpp index 1f95d14..4639090 100644 --- a/IO.cpp +++ b/IO.cpp @@ -301,13 +301,14 @@ void CIO::process() if (m_lockout) return; - q31_t dcLevel = 0; - q31_t dcValues[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) q31_t q31Samples[RX_BLOCK_SIZE]; - ::arm_q15_to_q31(samples, q31Samples, RX_BLOCK_SIZE); + + q31_t dcValues[RX_BLOCK_SIZE]; ::arm_biquad_cascade_df1_q31(&m_dcFilter, q31Samples, dcValues, RX_BLOCK_SIZE); + q31_t dcLevel = 0; for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++) dcLevel += dcValues[i]; dcLevel /= RX_BLOCK_SIZE; @@ -317,26 +318,37 @@ void CIO::process() q15_t dcSamples[RX_BLOCK_SIZE]; for (uint8_t i = 0U; i < RX_BLOCK_SIZE; i++) dcSamples[i] = samples[i] - offset; +#endif if (m_modemState == STATE_IDLE) { if (m_dstarEnable) { q15_t GMSKVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_gaussianFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE); - +#else + ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); +#endif dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE); } if (m_p25Enable) { q15_t P25Vals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); - +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE); +#endif p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); } if (m_nxdnEnable) { - q15_t NXDNVals[RX_BLOCK_SIZE]; q15_t NXDNValsTmp[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE); +#endif + q15_t NXDNVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE); @@ -359,8 +371,11 @@ void CIO::process() } else if (m_modemState == STATE_DSTAR) { if (m_dstarEnable) { q15_t GMSKVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_gaussianFilter, dcSamples, GMSKVals, RX_BLOCK_SIZE); - +#else + ::arm_fir_fast_q15(&m_gaussianFilter, samples, GMSKVals, RX_BLOCK_SIZE); +#endif dstarRX.samples(GMSKVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_DMR) { @@ -381,22 +396,32 @@ void CIO::process() } else if (m_modemState == STATE_YSF) { if (m_ysfEnable) { q15_t YSFVals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_rrcFilter, dcSamples, YSFVals, RX_BLOCK_SIZE); - +#else + ::arm_fir_fast_q15(&m_rrcFilter, samples, YSFVals, RX_BLOCK_SIZE); +#endif ysfRX.samples(YSFVals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_P25) { if (m_p25Enable) { q15_t P25Vals[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_boxcarFilter, dcSamples, P25Vals, RX_BLOCK_SIZE); - +#else + ::arm_fir_fast_q15(&m_boxcarFilter, samples, P25Vals, RX_BLOCK_SIZE); +#endif p25RX.samples(P25Vals, rssi, RX_BLOCK_SIZE); } } else if (m_modemState == STATE_NXDN) { if (m_nxdnEnable) { - q15_t NXDNVals[RX_BLOCK_SIZE]; q15_t NXDNValsTmp[RX_BLOCK_SIZE]; +#if defined(USE_DCBLOCKER) ::arm_fir_fast_q15(&m_nxdnFilter, dcSamples, NXDNValsTmp, RX_BLOCK_SIZE); +#else + ::arm_fir_fast_q15(&m_nxdnFilter, samples, NXDNValsTmp, RX_BLOCK_SIZE); +#endif + q15_t NXDNVals[RX_BLOCK_SIZE]; ::arm_fir_fast_q15(&m_nxdnISincFilter, NXDNValsTmp, NXDNVals, RX_BLOCK_SIZE); nxdnRX.samples(NXDNVals, rssi, RX_BLOCK_SIZE);