MMDVM/SerialPort.cpp

876 lines
20 KiB
C++

/*
* Copyright (C) 2013,2015,2016 by Jonathan Naylor G4KLX
* Copyright (C) 2016 by Colin Durbridge G4EML
*
* 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.
*/
// #define WANT_DEBUG
#include "Config.h"
#include "Globals.h"
#include "SerialPort.h"
const uint8_t MMDVM_FRAME_START = 0xE0U;
const uint8_t MMDVM_GET_VERSION = 0x00U;
const uint8_t MMDVM_GET_STATUS = 0x01U;
const uint8_t MMDVM_SET_CONFIG = 0x02U;
const uint8_t MMDVM_SET_MODE = 0x03U;
const uint8_t MMDVM_SET_FREQ = 0x04U;
const uint8_t MMDVM_CAL_DATA = 0x08U;
const uint8_t MMDVM_SEND_CWID = 0x0AU;
const uint8_t MMDVM_DSTAR_HEADER = 0x10U;
const uint8_t MMDVM_DSTAR_DATA = 0x11U;
const uint8_t MMDVM_DSTAR_LOST = 0x12U;
const uint8_t MMDVM_DSTAR_EOT = 0x13U;
const uint8_t MMDVM_DMR_DATA1 = 0x18U;
const uint8_t MMDVM_DMR_LOST1 = 0x19U;
const uint8_t MMDVM_DMR_DATA2 = 0x1AU;
const uint8_t MMDVM_DMR_LOST2 = 0x1BU;
const uint8_t MMDVM_DMR_SHORTLC = 0x1CU;
const uint8_t MMDVM_DMR_START = 0x1DU;
const uint8_t MMDVM_YSF_DATA = 0x20U;
const uint8_t MMDVM_YSF_LOST = 0x21U;
const uint8_t MMDVM_ACK = 0x70U;
const uint8_t MMDVM_NAK = 0x7FU;
const uint8_t MMDVM_DEBUG1 = 0xF1U;
const uint8_t MMDVM_DEBUG2 = 0xF2U;
const uint8_t MMDVM_DEBUG3 = 0xF3U;
const uint8_t MMDVM_DEBUG4 = 0xF4U;
const uint8_t MMDVM_DEBUG5 = 0xF5U;
const uint8_t HARDWARE[] = "MMDVM 20160509 24kHz (D-Star/DMR/System Fusion/CW Id)";
const uint8_t PROTOCOL_VERSION = 1U;
CSerialPort::CSerialPort() :
#if defined(__MBED__)
m_serial(SERIAL_TX, SERIAL_RX),
#endif
m_buffer(),
m_ptr(0U),
m_len(0U)
{
}
void CSerialPort::sendACK()
{
uint8_t reply[4U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 4U;
reply[2U] = MMDVM_ACK;
reply[3U] = m_buffer[2U];
write(reply, 4);
}
void CSerialPort::sendNAK(uint8_t err)
{
uint8_t reply[5U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 5U;
reply[2U] = MMDVM_NAK;
reply[3U] = m_buffer[2U];
reply[4U] = err;
write(reply, 5);
}
void CSerialPort::getStatus()
{
io.resetWatchdog();
uint8_t reply[11U];
// Send all sorts of interesting internal values
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 10U;
reply[2U] = MMDVM_GET_STATUS;
reply[3U] = 0x00U;
if (m_dstarEnable)
reply[3U] |= 0x01U;
if (m_dmrEnable)
reply[3U] |= 0x02U;
if (m_ysfEnable)
reply[3U] |= 0x04U;
reply[4U] = uint8_t(m_modemState);
reply[5U] = m_tx ? 0x01U : 0x00U;
if (io.hasADCOverflow())
reply[5U] |= 0x02U;
if (io.hasRXOverflow())
reply[5U] |= 0x04U;
if (io.hasTXOverflow())
reply[5U] |= 0x08U;
if (io.hasLockout())
reply[5U] |= 0x10U;
if (m_dstarEnable)
reply[6U] = dstarTX.getSpace();
else
reply[6U] = 0U;
if (m_dmrEnable) {
reply[7U] = dmrTX.getSpace1();
reply[8U] = dmrTX.getSpace2();
} else {
reply[7U] = 0U;
reply[8U] = 0U;
}
if (m_ysfEnable)
reply[9U] = ysfTX.getSpace();
else
reply[9U] = 0U;
write(reply, 10);
}
void CSerialPort::getVersion()
{
uint8_t reply[100U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_GET_VERSION;
reply[3U] = PROTOCOL_VERSION;
uint8_t count = 4U;
for (uint8_t i = 0U; HARDWARE[i] != 0x00U; i++, count++)
reply[count] = HARDWARE[i];
reply[1U] = count;
write(reply, count);
}
uint8_t CSerialPort::setConfig(const uint8_t* data, uint8_t length)
{
if (length < 9U)
return 4U;
bool rxInvert = (data[0U] & 0x01U) == 0x01U;
bool txInvert = (data[0U] & 0x02U) == 0x02U;
bool pttInvert = (data[0U] & 0x04U) == 0x04U;
bool dstarEnable = (data[1U] & 0x01U) == 0x01U;
bool dmrEnable = (data[1U] & 0x02U) == 0x02U;
bool ysfEnable = (data[1U] & 0x04U) == 0x04U;
uint8_t txDelay = data[2U];
if (txDelay > 50U)
return 4U;
MMDVM_STATE modemState = MMDVM_STATE(data[3U]);
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL)
return 4U;
if (modemState == STATE_DSTAR && !dstarEnable)
return 4U;
if (modemState == STATE_DMR && !dmrEnable)
return 4U;
if (modemState == STATE_YSF && !ysfEnable)
return 4U;
uint8_t rxLevel = data[4U];
uint8_t txLevel = data[5U];
uint8_t colorCode = data[6U];
if (colorCode > 15U)
return 4U;
uint8_t dmrDelay = data[7U];
int8_t oscOffset = int8_t(data[8U]) - 128;
if (oscOffset < 0) {
m_sampleCount = 1000000U / uint32_t(-oscOffset);
m_sampleInsert = false;
} else if (oscOffset > 0) {
m_sampleCount = 1000000U / uint32_t(oscOffset);
m_sampleInsert = true;
} else {
m_sampleCount = 0U;
m_sampleInsert = false;
}
m_modemState = modemState;
m_dstarEnable = dstarEnable;
m_dmrEnable = dmrEnable;
m_ysfEnable = ysfEnable;
dstarTX.setTXDelay(txDelay);
ysfTX.setTXDelay(txDelay);
dmrTX.setColorCode(colorCode);
dmrRX.setColorCode(colorCode);
dmrRX.setDelay(dmrDelay);
dmrIdleRX.setColorCode(colorCode);
io.setParameters(rxInvert, txInvert, pttInvert, rxLevel, txLevel);
io.start();
return 0U;
}
uint8_t CSerialPort::setMode(const uint8_t* data, uint8_t length)
{
if (length < 1U)
return 4U;
MMDVM_STATE modemState = MMDVM_STATE(data[0U]);
if (modemState == m_modemState)
return 0U;
if (modemState != STATE_IDLE && modemState != STATE_DSTAR && modemState != STATE_DMR && modemState != STATE_YSF && modemState != STATE_DSTARCAL && modemState != STATE_DMRCAL)
return 4U;
if (modemState == STATE_DSTAR && !m_dstarEnable)
return 4U;
if (modemState == STATE_DMR && !m_dmrEnable)
return 4U;
if (modemState == STATE_YSF && !m_ysfEnable)
return 4U;
setMode(modemState);
return 0U;
}
void CSerialPort::setMode(MMDVM_STATE modemState)
{
switch (modemState) {
case STATE_DMR:
DEBUG1("Mode set to DMR");
dstarRX.reset();
ysfRX.reset();
cwIdTX.reset();
break;
case STATE_DSTAR:
DEBUG1("Mode set to D-Star");
dmrIdleRX.reset();
dmrRX.reset();
ysfRX.reset();
cwIdTX.reset();
break;
case STATE_YSF:
DEBUG1("Mode set to System Fusion");
dmrIdleRX.reset();
dmrRX.reset();
dstarRX.reset();
cwIdTX.reset();
break;
case STATE_DSTARCAL:
DEBUG1("Mode set to D-Star Calibrate");
dmrIdleRX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
cwIdTX.reset();
break;
case STATE_DMRCAL:
DEBUG1("Mode set to DMR Calibrate");
dmrIdleRX.reset();
dmrRX.reset();
dstarRX.reset();
ysfRX.reset();
cwIdTX.reset();
break;
default:
DEBUG1("Mode set to Idle");
// STATE_IDLE
break;
}
m_modemState = modemState;
}
void CSerialPort::start()
{
#if defined(__MBED__)
m_serial.baud(115200);
#else
Serial.begin(115200);
#endif
}
void CSerialPort::process()
{
#if defined(__MBED__)
while (m_serial.readable()) {
uint8_t c = m_serial.getc();
#else
while (Serial.available()) {
uint8_t c = Serial.read();
#endif
if (m_ptr == 0U && c == MMDVM_FRAME_START) {
// Handle the frame start correctly
m_buffer[0U] = c;
m_ptr = 1U;
m_len = 0U;
} else if (m_ptr > 0U) {
// Any other bytes are added to the buffer
m_buffer[m_ptr] = c;
m_ptr++;
// Once we have enough bytes, calculate the expected length
if (m_ptr == 2U)
m_len = m_buffer[1U];
if (m_ptr == 3U && m_len > 130U) {
sendNAK(3U);
m_ptr = 0U;
m_len = 0U;
}
// The full packet has been received, process it
if (m_ptr == m_len) {
uint8_t err = 2U;
switch (m_buffer[2U]) {
case MMDVM_GET_STATUS:
getStatus();
break;
case MMDVM_GET_VERSION:
getVersion();
break;
case MMDVM_SET_CONFIG:
err = setConfig(m_buffer + 3U, m_len - 3U);
if (err == 0U)
sendACK();
else
sendNAK(err);
break;
case MMDVM_SET_MODE:
err = setMode(m_buffer + 3U, m_len - 3U);
if (err == 0U)
sendACK();
else
sendNAK(err);
break;
case MMDVM_SET_FREQ:
sendACK();
break;
case MMDVM_CAL_DATA:
if (m_modemState == STATE_DSTARCAL)
err = calDStarTX.write(m_buffer + 3U, m_len - 3U);
if (m_modemState == STATE_DMRCAL)
err = calDMR.write(m_buffer + 3U, m_len - 3U);
if (err == 0U) {
sendACK();
} else {
DEBUG2("Received invalid calibration data", err);
sendNAK(err);
}
break;
case MMDVM_SEND_CWID:
if (m_modemState == STATE_IDLE)
err = cwIdTX.write(m_buffer + 3U, m_len - 3U);
if (err != 0U) {
DEBUG2("Invalid CW Id data", err);
sendNAK(err);
}
break;
case MMDVM_DSTAR_HEADER:
if (m_dstarEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_DSTAR)
err = dstarTX.writeHeader(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star header", err);
sendNAK(err);
}
break;
case MMDVM_DSTAR_DATA:
if (m_dstarEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_DSTAR)
err = dstarTX.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star data", err);
sendNAK(err);
}
break;
case MMDVM_DSTAR_EOT:
if (m_dstarEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_DSTAR)
err = dstarTX.writeEOT();
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_DSTAR);
} else {
DEBUG2("Received invalid D-Star EOT", err);
sendNAK(err);
}
break;
case MMDVM_DMR_DATA1:
if (m_dmrEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_DMR)
err = dmrTX.writeData1(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
}
break;
case MMDVM_DMR_DATA2:
if (m_dmrEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_DMR)
err = dmrTX.writeData2(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_DMR);
} else {
DEBUG2("Received invalid DMR data", err);
sendNAK(err);
}
break;
case MMDVM_DMR_START:
if (m_dmrEnable) {
err = 4U;
if (m_len == 4U) {
if (m_buffer[3U] == 0x01U && m_modemState == STATE_DMR) {
if (!m_tx)
dmrTX.setStart(true);
err = 0U;
} else if (m_buffer[3U] == 0x00U && m_modemState == STATE_DMR) {
if (m_tx)
dmrTX.setStart(false);
err = 0U;
}
}
}
if (err != 0U) {
DEBUG2("Received invalid DMR start", err);
sendNAK(err);
}
break;
case MMDVM_DMR_SHORTLC:
if (m_dmrEnable)
err = dmrTX.writeShortLC(m_buffer + 3U, m_len - 3U);
if (err != 0U) {
DEBUG2("Received invalid DMR Short LC", err);
sendNAK(err);
}
break;
case MMDVM_YSF_DATA:
if (m_ysfEnable) {
if (m_modemState == STATE_IDLE || m_modemState == STATE_YSF)
err = ysfTX.writeData(m_buffer + 3U, m_len - 3U);
}
if (err == 0U) {
if (m_modemState == STATE_IDLE)
setMode(STATE_YSF);
} else {
DEBUG2("Received invalid System Fusion data", err);
sendNAK(err);
}
break;
default:
// Handle this, send a NAK back
sendNAK(1U);
break;
}
m_ptr = 0U;
m_len = 0U;
}
}
}
}
void CSerialPort::writeDStarHeader(const uint8_t* header, uint8_t length)
{
if (m_modemState != STATE_DSTAR && m_modemState != STATE_IDLE)
return;
if (!m_dstarEnable)
return;
uint8_t reply[50U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_DSTAR_HEADER;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = header[i];
reply[1U] = count;
write(reply, count);
}
void CSerialPort::writeDStarData(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_DSTAR && m_modemState != STATE_IDLE)
return;
if (!m_dstarEnable)
return;
uint8_t reply[20U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_DSTAR_DATA;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
write(reply, count);
}
void CSerialPort::writeDStarLost()
{
if (m_modemState != STATE_DSTAR && m_modemState != STATE_IDLE)
return;
if (!m_dstarEnable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_DSTAR_LOST;
write(reply, 3);
}
void CSerialPort::writeDStarEOT()
{
if (m_modemState != STATE_DSTAR && m_modemState != STATE_IDLE)
return;
if (!m_dstarEnable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_DSTAR_EOT;
write(reply, 3);
}
void CSerialPort::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] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = slot ? MMDVM_DMR_DATA2 : MMDVM_DMR_DATA1;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
write(reply, count);
}
void CSerialPort::writeDMRLost(bool slot)
{
if (m_modemState != STATE_DMR && m_modemState != STATE_IDLE)
return;
if (!m_dmrEnable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = slot ? MMDVM_DMR_LOST2 : MMDVM_DMR_LOST1;
write(reply, 3);
}
void CSerialPort::writeYSFData(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_YSF && m_modemState != STATE_IDLE)
return;
if (!m_ysfEnable)
return;
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_YSF_DATA;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
write(reply, count);
}
void CSerialPort::writeYSFLost()
{
if (m_modemState != STATE_YSF && m_modemState != STATE_IDLE)
return;
if (!m_ysfEnable)
return;
uint8_t reply[3U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 3U;
reply[2U] = MMDVM_YSF_LOST;
write(reply, 3);
}
void CSerialPort::writeCalData(const uint8_t* data, uint8_t length)
{
if (m_modemState != STATE_DSTARCAL)
return;
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_CAL_DATA;
uint8_t count = 3U;
for (uint8_t i = 0U; i < length; i++, count++)
reply[count] = data[i];
reply[1U] = count;
write(reply, count);
}
void CSerialPort::write(const uint8_t* data, uint16_t length, bool flush)
{
#if defined(__MBED__)
for (uint16_t i = 0U; i < length; i++)
m_serial.putc(data[i]);
// No explicit flush function
#else
Serial.write(data, length);
if (flush)
Serial.flush();
#endif
}
void CSerialPort::writeDebug(const char* text)
{
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_DEBUG1;
uint8_t count = 3U;
for (uint8_t i = 0U; text[i] != '\0'; i++, count++)
reply[count] = text[i];
reply[1U] = count;
write(reply, count, true);
}
void CSerialPort::writeDebug(const char* text, int16_t n1)
{
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_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;
write(reply, count, true);
}
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2)
{
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_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;
write(reply, count, true);
}
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3)
{
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_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;
write(reply, count, true);
}
void CSerialPort::writeDebug(const char* text, int16_t n1, int16_t n2, int16_t n3, int16_t n4)
{
uint8_t reply[130U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_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;
write(reply, count, true);
}
void CSerialPort::writeAssert(bool cond, const char* text, const char* file, long line)
{
if (cond)
return;
uint8_t reply[200U];
reply[0U] = MMDVM_FRAME_START;
reply[1U] = 0U;
reply[2U] = MMDVM_DEBUG2;
uint8_t count = 3U;
for (uint8_t i = 0U; text[i] != '\0'; i++, count++)
reply[count] = text[i];
reply[count++] = ' ';
for (uint8_t i = 0U; file[i] != '\0'; i++, count++)
reply[count] = file[i];
reply[count++] = (line >> 8) & 0xFF;
reply[count++] = (line >> 0) & 0xFF;
reply[1U] = count;
write(reply, count, true);
}