/** * Digital Voice Modem - DSP Firmware (Hotspot) * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / DSP Firmware (Hotspot) * */ // // Based on code from the MMDVM_HS project. (https://github.com/juribeparada/MMDVM_HS) // Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0) // /* * Copyright (C) 2013,2015,2016,2018,2020,2021 by Jonathan Naylor G4KLX * Copyright (C) 2016 by Colin Durbridge G4EML * Copyright (C) 2016,2017,2018,2019 by Andy Uribe CA6JAU * Copyright (C) 2019 by Florian Wolters DF2ET * Copyright (C) 2017-2021 Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "Globals.h" #include "SerialPort.h" // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- #if defined(ENABLE_DMR) #define DESCR_DMR "DMR, " #else #define DESCR_DMR "" #endif #if defined(ENABLE_P25) #define DESCR_P25 "P25, " #else #define DESCR_P25 "" #endif #if defined(SEND_RSSI_DATA) #define DESCR_RSSI "RSSI, " #else #define DESCR_RSSI "" #endif #if defined(ZUMSPOT_ADF7021) #define BOARD_INFO "ZUMspot" #elif defined(MMDVM_HS_HAT_REV12) #define BOARD_INFO "MMDVM_HS_Hat" #elif defined(MMDVM_HS_DUAL_HAT_REV10) #define BOARD_INFO "MMDVM_HS_Dual_Hat" #elif defined(NANO_HOTSPOT) #define BOARD_INFO "Nano_hotSPOT" #elif defined(NANO_DV_REV11) #define BOARD_INFO "Nano_DV" #elif defined(SKYBRIDGE_HS) #define BOARD_INFO "SkyBridge" #elif defined(LONESTAR_USB) #define BOARD_INFO "LS_USB_STICK" #else #define BOARD_INFO "MMDVM_HS" #endif #if defined(ADF7021_14_7456) #define DESCR_OSC "TCXO 14.7456, " #endif #if defined(ADF7021_12_2880) #define DESCR_OSC "TCXO 12.2880, " #endif #if defined(ENABLE_ADF7021) && defined(ADF7021_N_VER) #define RF_CHIP "ADF7021N, " #elif defined(ENABLE_ADF7021) #define RF_CHIP "ADF7021, " #endif #define DESCRIPTION "Digital Voice Modem DSP Hotspot [" BOARD_INFO "] (" RF_CHIP DESCR_DMR DESCR_P25 DESCR_OSC DESCR_RSSI "CW Id)" #define concat(a, b, c) a " (build " b " " c ")" const char HARDWARE[] = concat(DESCRIPTION, __TIME__, __DATE__); const uint8_t PROTOCOL_VERSION = 2U; // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- /// /// Initializes a new instance of the SerialPort class. /// SerialPort::SerialPort() : m_buffer(), m_ptr(0U), m_len(0U), m_debug(false) { // stub } /// /// Starts serial port communications. /// void SerialPort::start() { beginInt(1U, SERIAL_SPEED); } /// /// Process data from serial port. /// void SerialPort::process() { while (availableInt(1U)) { uint8_t c = readInt(1U); if (m_ptr == 0U) { if (c == DVM_FRAME_START) { // Handle the frame start correctly m_buffer[0U] = c; m_ptr = 1U; m_len = 0U; } else { m_ptr = 0U; m_len = 0U; } } else if (m_ptr == 1U) { // Handle the frame length m_len = m_buffer[m_ptr] = c; m_ptr = 2U; } else { // Any other bytes are added to the buffer m_buffer[m_ptr] = c; m_ptr++; // The full packet has been received, process it if (m_ptr == m_len) { uint8_t err = 2U; switch (m_buffer[2U]) { case CMD_GET_STATUS: getStatus(); break; case CMD_GET_VERSION: getVersion(); break; case CMD_SET_CONFIG: err = setConfig(m_buffer + 3U, m_len - 3U); if (err == RSN_OK) sendACK(); else sendNAK(err); break; case CMD_SET_MODE: err = setMode(m_buffer + 3U, m_len - 3U); if (err == RSN_OK) sendACK(); else sendNAK(err); break; case CMD_SET_SYMLVLADJ: sendACK(); // CMD_SET_RXLEVEL not supported by HS break; case CMD_SET_RXLEVEL: sendACK(); // CMD_SET_RXLEVEL not supported by HS break; case CMD_SET_RFPARAMS: err = setRFParams(m_buffer + 3U, m_len - 3U); if (err == RSN_OK) sendACK(); else sendNAK(err); break; case CMD_CAL_DATA: if (m_modemState == STATE_DMR_DMO_CAL_1K || m_modemState == STATE_DMR_CAL_1K || m_modemState == STATE_DMR_LF_CAL || m_modemState == STATE_DMR_CAL) err = calDMR.write(m_buffer + 3U, m_len - 3U); if (m_modemState == STATE_P25_CAL_1K || m_modemState == STATE_P25_LF_CAL || m_modemState == STATE_P25_CAL) err = calP25.write(m_buffer + 3U, m_len - 3U); if (err == RSN_OK) { sendACK(); } else { DEBUG2("SerialPort: process(): received invalid calibration data", err); sendNAK(err); } break; /** CW */ case CMD_SEND_CWID: err = RSN_RINGBUFF_FULL; if (m_modemState == STATE_IDLE) { m_cwIdState = true; io.rf1Conf(STATE_CW, true); err = cwIdTX.write(m_buffer + 3U, m_len - 3U); } if (err != RSN_OK) { DEBUG2("SerialPort: process(): invalid CW Id data", err); sendNAK(err); } break; /** Digital Mobile Radio */ case CMD_DMR_DATA1: #if defined(DUPLEX) if (m_dmrEnable) { if (m_modemState == STATE_IDLE || m_modemState == STATE_DMR) { if (m_duplex) err = dmrTX.writeData1(m_buffer + 3U, m_len - 3U); } } if (err == RSN_OK) { if (m_modemState == STATE_IDLE) setMode(STATE_DMR); } else { DEBUG2("SerialPort: process() received invalid DMR data", err); sendNAK(err); } #else sendNAK(RSN_INVALID_REQUEST); #endif break; case CMD_DMR_DATA2: if (m_dmrEnable) { if (m_modemState == STATE_IDLE || m_modemState == STATE_DMR) { #if defined(DUPLEX) if (m_duplex) err = dmrTX.writeData2(m_buffer + 3U, m_len - 3U); else err = dmrDMOTX.writeData(m_buffer + 3U, m_len - 3U); #else err = dmrDMOTX.writeData(m_buffer + 3U, m_len - 3U); #endif } } if (err == RSN_OK) { if (m_modemState == STATE_IDLE) setMode(STATE_DMR); } else { DEBUG2("SerialPort: process(): received invalid DMR data", err); sendNAK(err); } break; case CMD_DMR_START: #if defined(DUPLEX) if (m_dmrEnable) { err = RSN_INVALID_DMR_START; if (m_len == 4U) { if (m_buffer[3U] == 0x01U && m_modemState == STATE_DMR) { if (!m_tx) dmrTX.setStart(true); err = RSN_OK; } else if (m_buffer[3U] == 0x00U && m_modemState == STATE_DMR) { if (m_tx) dmrTX.setStart(false); err = RSN_OK; } } } if (err != RSN_OK) { DEBUG3("SerialPort: process(): received invalid DMR start", err, m_len); sendNAK(err); } #else sendNAK(RSN_INVALID_REQUEST); #endif break; case CMD_DMR_SHORTLC: #if defined(DUPLEX) if (m_dmrEnable) err = dmrTX.writeShortLC(m_buffer + 3U, m_len - 3U); if (err != RSN_OK) { DEBUG2("SerialPort: process(): received invalid DMR Short LC", err); sendNAK(err); } #else sendNAK(RSN_INVALID_REQUEST); #endif break; case CMD_DMR_ABORT: #if defined(DUPLEX) if (m_dmrEnable) err = dmrTX.writeAbort(m_buffer + 3U, m_len - 3U); if (err != RSN_OK) { DEBUG2("SerialPort: process(): received invalid DMR Abort", err); sendNAK(err); } #else sendNAK(RSN_INVALID_REQUEST); #endif break; /** Project 25 */ case CMD_P25_DATA: if (m_p25Enable) { if (m_modemState == STATE_IDLE || m_modemState == STATE_P25) err = p25TX.writeData(m_buffer + 3U, m_len - 3U); } if (err == RSN_OK) { if (m_modemState == STATE_IDLE) setMode(STATE_P25); } else { DEBUG2("SerialPort: process(): Received invalid P25 data", err); sendNAK(err); } break; case CMD_P25_CLEAR: if (m_p25Enable) { if (m_modemState == STATE_IDLE || m_modemState == STATE_P25) p25TX.clear(); } break; default: // Handle this, send a NAK back sendNAK(RSN_NAK); break; } m_ptr = 0U; m_len = 0U; } } } if (io.getWatchdog() >= 48000U) { m_ptr = 0U; m_len = 0U; } } /// /// Helper to check if the modem is in a calibration state. /// /// /// bool SerialPort::isCalState(DVM_STATE state) { // calibration mode check if (state == STATE_P25_CAL_1K || state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K || state == STATE_DMR_LF_CAL || state == STATE_P25_LF_CAL || state == STATE_RSSI_CAL || state == STATE_P25_CAL || state == STATE_DMR_CAL || state == STATE_INT_CAL) { return true; } return false; } /// /// Helper to determine digital mode if the modem is in a calibration state. /// /// /// DVM_STATE SerialPort::calRelativeState(DVM_STATE state) { if (isCalState(state)) { if (state == STATE_DMR_DMO_CAL_1K || state == STATE_DMR_CAL_1K || state == STATE_DMR_LF_CAL || state == STATE_DMR_CAL || state == STATE_RSSI_CAL || state == STATE_INT_CAL) { return STATE_DMR; } else if(state == STATE_P25_CAL_1K || state == STATE_P25_LF_CAL || state == STATE_P25_CAL) { return STATE_P25; } } return STATE_CW; } /// /// Write DMR frame data to serial port. /// /// /// /// void SerialPort::writeDMRData(bool slot, const uint8_t* data, uint8_t length) { if (m_modemState != STATE_DMR && m_modemState != STATE_IDLE) return; if (!m_dmrEnable) return; uint8_t reply[40U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = slot ? CMD_DMR_DATA2 : CMD_DMR_DATA1; uint8_t count = 3U; for (uint8_t i = 0U; i < length; i++, count++) reply[count] = data[i]; reply[1U] = count; writeInt(1U, reply, count); } /// /// Write lost DMR frame data to serial port. /// /// void SerialPort::writeDMRLost(bool slot) { if (m_modemState != STATE_DMR && m_modemState != STATE_IDLE) return; if (!m_dmrEnable) return; uint8_t reply[3U]; reply[0U] = DVM_FRAME_START; reply[1U] = 3U; reply[2U] = slot ? CMD_DMR_LOST2 : CMD_DMR_LOST1; writeInt(1U, reply, 3); } /// /// Write P25 frame data to serial port. /// /// /// void SerialPort::writeP25Data(const uint8_t* data, uint8_t length) { if (m_modemState != STATE_P25 && m_modemState != STATE_IDLE) return; if (!m_p25Enable) return; uint8_t reply[250U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_P25_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); } /// /// Write lost P25 frame data to serial port. /// void SerialPort::writeP25Lost() { if (m_modemState != STATE_P25 && m_modemState != STATE_IDLE) return; if (!m_p25Enable) return; uint8_t reply[3U]; reply[0U] = DVM_FRAME_START; reply[1U] = 3U; reply[2U] = CMD_P25_LOST; writeInt(1U, reply, 3); } /// /// Write calibration frame data to serial port. /// /// /// void SerialPort::writeCalData(const uint8_t* data, uint8_t length) { uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_CAL_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); } /// /// Write RSSI frame data to serial port. /// /// /// void SerialPort::writeRSSIData(const uint8_t* data, uint8_t length) { if (m_modemState != STATE_RSSI_CAL) return; uint8_t reply[30U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_RSSI_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 SerialPort::writeDebug(const char* text) { if (!m_debug) return; uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_DEBUG1; uint8_t count = 3U; for (uint8_t i = 0U; text[i] != '\0'; i++, count++) reply[count] = text[i]; reply[1U] = count; writeInt(1U, reply, count, true); } /// /// /// /// /// void SerialPort::writeDebug(const char* text, int16_t n1) { if (!m_debug) return; uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_DEBUG2; uint8_t count = 3U; for (uint8_t i = 0U; text[i] != '\0'; i++, count++) reply[count] = text[i]; reply[count++] = (n1 >> 8) & 0xFF; reply[count++] = (n1 >> 0) & 0xFF; reply[1U] = count; writeInt(1U, reply, count, true); } /// /// /// /// /// /// void SerialPort::writeDebug(const char* text, int16_t n1, int16_t n2) { if (!m_debug) return; uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_DEBUG3; uint8_t count = 3U; for (uint8_t i = 0U; text[i] != '\0'; i++, count++) reply[count] = text[i]; reply[count++] = (n1 >> 8) & 0xFF; reply[count++] = (n1 >> 0) & 0xFF; reply[count++] = (n2 >> 8) & 0xFF; reply[count++] = (n2 >> 0) & 0xFF; reply[1U] = count; writeInt(1U, reply, count, true); } /// /// /// /// /// /// /// void SerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3) { if (!m_debug) return; uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_DEBUG4; uint8_t count = 3U; for (uint8_t i = 0U; text[i] != '\0'; i++, count++) reply[count] = text[i]; reply[count++] = (n1 >> 8) & 0xFF; reply[count++] = (n1 >> 0) & 0xFF; reply[count++] = (n2 >> 8) & 0xFF; reply[count++] = (n2 >> 0) & 0xFF; reply[count++] = (n3 >> 8) & 0xFF; reply[count++] = (n3 >> 0) & 0xFF; reply[1U] = count; writeInt(1U, reply, count, true); } /// /// /// /// /// /// /// /// void SerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4) { if (!m_debug) return; uint8_t reply[130U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_DEBUG5; uint8_t count = 3U; for (uint8_t i = 0U; text[i] != '\0'; i++, count++) reply[count] = text[i]; reply[count++] = (n1 >> 8) & 0xFF; reply[count++] = (n1 >> 0) & 0xFF; reply[count++] = (n2 >> 8) & 0xFF; reply[count++] = (n2 >> 0) & 0xFF; reply[count++] = (n3 >> 8) & 0xFF; reply[count++] = (n3 >> 0) & 0xFF; reply[count++] = (n4 >> 8) & 0xFF; reply[count++] = (n4 >> 0) & 0xFF; reply[1U] = count; writeInt(1U, reply, count, true); } /// /// /// /// /// void SerialPort::writeDump(const uint8_t* data, uint16_t length) { if (!m_debug) return; uint8_t reply[512U]; reply[0U] = DVM_FRAME_START; if (length > 252U) { reply[1U] = 0U; reply[2U] = (length + 4U) - 255U; reply[3U] = CMD_DEBUG_DUMP; for (uint8_t i = 0U; i < length; i++) reply[i + 4U] = data[i]; writeInt(1U, reply, length + 4U); } else { reply[1U] = length + 3U; reply[2U] = CMD_DEBUG_DUMP; for (uint8_t i = 0U; i < length; i++) reply[i + 3U] = data[i]; writeInt(1U, reply, length + 3U); } } // --------------------------------------------------------------------------- // Private Class Members // --------------------------------------------------------------------------- /// /// Write acknowlegement. /// void SerialPort::sendACK() { uint8_t reply[4U]; reply[0U] = DVM_FRAME_START; reply[1U] = 4U; reply[2U] = CMD_ACK; reply[3U] = m_buffer[2U]; writeInt(1U, reply, 4); } /// /// Write negative acknowlegement. /// /// void SerialPort::sendNAK(uint8_t err) { uint8_t reply[5U]; reply[0U] = DVM_FRAME_START; reply[1U] = 5U; reply[2U] = CMD_NAK; reply[3U] = m_buffer[2U]; reply[4U] = err; writeInt(1U, reply, 5); } /// /// Write modem DSP status. /// void SerialPort::getStatus() { io.resetWatchdog(); uint8_t reply[15U]; // send all sorts of interesting internal values reply[0U] = DVM_FRAME_START; reply[1U] = 11U; reply[2U] = CMD_GET_STATUS; reply[3U] = 0x01U; if (m_dmrEnable) reply[3U] |= 0x02U; if (m_p25Enable) reply[3U] |= 0x08U; reply[4U] = uint8_t(m_modemState); reply[5U] = m_tx ? 0x01U : 0x00U; if (io.hasRXOverflow()) reply[5U] |= 0x04U; if (io.hasTXOverflow()) reply[5U] |= 0x08U; reply[5U] |= m_dcd ? 0x40U : 0x00U; reply[6U] = 0U; if (m_dmrEnable) { #if defined(DUPLEX) if (m_duplex) { reply[7U] = dmrTX.getSpace1(); reply[8U] = dmrTX.getSpace2(); } else { reply[7U] = 10U; reply[8U] = dmrDMOTX.getSpace(); } #else reply[7U] = 10U; reply[8U] = dmrDMOTX.getSpace(); #endif } else { reply[7U] = 0U; reply[8U] = 0U; } reply[9U] = 0U; if (m_p25Enable) reply[10U] = p25TX.getSpace(); else reply[10U] = 0U; writeInt(1U, reply, 11); } /// /// Write modem DSP version. /// void SerialPort::getVersion() { uint8_t reply[200U]; reply[0U] = DVM_FRAME_START; reply[1U] = 0U; reply[2U] = CMD_GET_VERSION; reply[3U] = PROTOCOL_VERSION; reply[4U] = io.getCPU(); // Reserve 16 bytes for the UDID ::memcpy(reply + 5U, 0x00U, 16U); io.getUDID(reply + 5U); uint8_t count = 21U; for (uint8_t i = 0U; HARDWARE[i] != 0x00U; i++, count++) reply[count] = HARDWARE[i]; reply[1U] = count; writeInt(1U, reply, count); } /// /// Helper to validate the passed modem state is valid. /// /// /// uint8_t SerialPort::modemStateCheck(DVM_STATE state) { // invalid mode check if (state != STATE_IDLE && state != STATE_DMR && state != STATE_P25 && state != STATE_P25_CAL_1K && state != STATE_DMR_DMO_CAL_1K && state != STATE_DMR_CAL_1K && state != STATE_DMR_LF_CAL && state != STATE_P25_LF_CAL && state != STATE_RSSI_CAL && state != STATE_P25_CAL && state != STATE_DMR_CAL && state != STATE_INT_CAL) return RSN_INVALID_MODE; /* // DMR without DMR being enabled if (state == STATE_DMR && !m_dmrEnable) return RSN_DMR_DISABLED; // P25 without P25 being enabled if (state == STATE_P25 && !m_p25Enable) return RSN_P25_DISABLED; */ return RSN_OK; } /// /// Set modem DSP configuration from serial port data. /// /// /// /// uint8_t SerialPort::setConfig(const uint8_t* data, uint8_t length) { if (length < 14U) return RSN_ILLEGAL_LENGTH; bool simplex = (data[0U] & 0x80U) == 0x80U; m_debug = (data[0U] & 0x10U) == 0x10U; bool dmrEnable = (data[1U] & 0x02U) == 0x02U; bool p25Enable = (data[1U] & 0x08U) == 0x08U; uint8_t fdmaPreamble = data[2U]; if (fdmaPreamble > 255U) return RSN_INVALID_FDMA_PREAMBLE; DVM_STATE modemState = DVM_STATE(data[3U]); uint8_t ret = modemStateCheck(modemState); if (ret != RSN_OK) return ret; uint8_t colorCode = data[6U]; if (colorCode > 15U) return RSN_INVALID_DMR_CC; #if defined(DUPLEX) uint8_t dmrRxDelay = data[7U]; if (dmrRxDelay > 255U) return RSN_INVALID_DMR_RX_DELAY; #endif uint16_t nac = (data[8U] << 4) + (data[9U] >> 4); m_cwIdTXLevel = data[5U] >> 2; // ?? uint8_t dmrTXLevel = data[10U]; uint8_t p25TXLevel = data[12U]; m_modemState = modemState; m_dmrEnable = dmrEnable; m_p25Enable = p25Enable; if (m_dmrEnable && m_p25Enable) return RSN_HS_NO_DUAL_MODE; m_duplex = !simplex; #if !defined(DUPLEX) if (m_duplex) { DEBUG1("Full duplex not supported with this firmware"); return RSN_INVALID_REQUEST; } #elif defined(DUPLEX) && (defined(ZUMSPOT_ADF7021) || defined(LONESTAR_USB) || defined(SKYBRIDGE_HS)) if (io.isDualBand() && m_duplex) { DEBUG1("Full duplex is not supported on this board"); return RSN_INVALID_REQUEST; } #endif io.setDeviations(dmrTXLevel, p25TXLevel); p25TX.setPreambleCount(fdmaPreamble); dmrDMOTX.setPreambleCount(fdmaPreamble); p25RX.setNAC(nac); #if defined(DUPLEX) dmrTX.setColorCode(colorCode); dmrRX.setColorCode(colorCode); dmrRX.setRxDelay(dmrRxDelay); dmrIdleRX.setColorCode(colorCode); #endif dmrDMORX.setColorCode(colorCode); if (m_modemState != STATE_IDLE && isCalState(m_modemState)) { io.updateCal(calRelativeState(m_modemState)); } setMode(m_modemState); io.start(); return RSN_OK; } /// /// Set modem DSP mode from serial port data. /// /// /// /// uint8_t SerialPort::setMode(const uint8_t* data, uint8_t length) { if (length < 1U) return RSN_ILLEGAL_LENGTH; DVM_STATE modemState = DVM_STATE(data[0U]); if (modemState == m_modemState) return RSN_OK; uint8_t ret = modemStateCheck(modemState); if (ret != RSN_OK) return ret; setMode(modemState); return RSN_OK; } /// /// Sets the modem state. /// /// void SerialPort::setMode(DVM_STATE modemState) { switch (modemState) { case STATE_DMR: DEBUG1("SerialPort: setMode(): mode set to DMR"); p25RX.reset(); cwIdTX.reset(); break; case STATE_P25: DEBUG1("SerialPort: setMode(): mode set to P25"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); cwIdTX.reset(); break; case STATE_DMR_CAL: DEBUG1("SerialPort: setMode(): mode set to DMR Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_P25_CAL: DEBUG1("SerialPort: setMode(): mode set to P25 Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_P25_LF_CAL: DEBUG1("SerialPort: setMode(): mode set to P25 80Hz Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_RSSI_CAL: DEBUG1("SerialPort: setMode(): mode set to RSSI Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_DMR_LF_CAL: DEBUG1("SerialPort: setMode(): mode set to DMR 80Hz Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_DMR_CAL_1K: DEBUG1("SerialPort: setMode(): mode set to DMR BS 1031Hz Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_DMR_DMO_CAL_1K: DEBUG1("SerialPort: setMode(): mode set to DMR MS 1031Hz Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; case STATE_P25_CAL_1K: DEBUG1("SerialPort: setMode(): mode set to P25 1011Hz Calibrate"); #if defined(DUPLEX) dmrIdleRX.reset(); dmrRX.reset(); #endif dmrDMORX.reset(); p25RX.reset(); cwIdTX.reset(); break; default: DEBUG1("SerialPort: setMode(): mode set to Idle"); // STATE_IDLE break; } m_modemState = modemState; io.setMode(m_modemState); } /// /// Sets the RF parameters. /// /// /// /// uint8_t SerialPort::setRFParams(const uint8_t* data, uint8_t length) { if (length < 15U) return RSN_ILLEGAL_LENGTH; uint32_t rxFreq, txFreq; uint8_t rfPower; ADF_GAIN_MODE gainMode; rxFreq = data[1U] << 0; rxFreq |= data[2U] << 8; rxFreq |= data[3U] << 16; rxFreq |= data[4U] << 24; txFreq = data[5U] << 0; txFreq |= data[6U] << 8; txFreq |= data[7U] << 16; txFreq |= data[8U] << 24; rfPower = data[9U]; int8_t dmrDiscBWAdj = int8_t(data[10U]) - 128; if (dmrDiscBWAdj > 128) return RSN_INVALID_REQUEST; if (dmrDiscBWAdj < -128) return RSN_INVALID_REQUEST; int8_t p25DiscBWAdj = int8_t(data[11U]) - 128; if (p25DiscBWAdj > 128) return RSN_INVALID_REQUEST; if (p25DiscBWAdj < -128) return RSN_INVALID_REQUEST; int8_t dmrPostBWAdj = int8_t(data[12U]) - 128; if (dmrPostBWAdj > 128) return RSN_INVALID_REQUEST; if (dmrPostBWAdj < -128) return RSN_INVALID_REQUEST; int8_t p25PostBWAdj = int8_t(data[13U]) - 128; if (p25PostBWAdj > 128) return RSN_INVALID_REQUEST; if (p25PostBWAdj < -128) return RSN_INVALID_REQUEST; gainMode = (ADF_GAIN_MODE)data[14U]; io.setRFAdjust(dmrDiscBWAdj, p25DiscBWAdj, dmrPostBWAdj, p25PostBWAdj); return io.setRFParams(rxFreq, txFreq, rfPower, gainMode); }