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/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 4e0f62b..9ab7d31 100644 --- a/Globals.h +++ b/Globals.h @@ -51,6 +51,8 @@ enum MMDVM_STATE { STATE_NXDN = 5, // Dummy states start at 90 + STATE_NXDNCAL1K = 91, + STATE_DMRDMO1K = 92, STATE_P25CAL1K = 93, STATE_DMRCAL1K = 94, STATE_LFCAL = 95, @@ -78,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" @@ -131,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 a2e9433..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; @@ -101,12 +102,15 @@ 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) 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 af74591..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; @@ -98,12 +99,15 @@ 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) 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 231ff30..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) + 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) + 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; @@ -440,7 +440,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(); @@ -461,6 +461,28 @@ void CSerialPort::setMode(MMDVM_STATE modemState) nxdnRX.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(); + 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: DEBUG1("Mode set to Idle"); // STATE_IDLE @@ -542,10 +564,12 @@ 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); + if (m_modemState == STATE_NXDNCAL1K) + err = calNXDN.write(m_buffer + 3U, m_len - 3U); if (err == 0U) { sendACK(); } else {