From a6b6219c6d10bcc15e9bbaed4a60d5a03f920ee7 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 07:52:18 +0000 Subject: [PATCH 1/3] 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 b34d3c3dbd6e0d2ecae8aca5102fb2d749e4e39c Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 7 Feb 2018 18:07:51 +0000 Subject: [PATCH 2/3] 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 3/3] 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; }