You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

435 lines
11 KiB
C++

3 years ago
/*
Copyright (C) 2019-2021 Doug McLain
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 3 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, see <https://www.gnu.org/licenses/>.
*/
#include <QMap>
#include <QThread>
#include <QDebug>
#include "serialmodem.h"
//#define DEBUGHW
#define ENDLINE "\n"
const unsigned char MODE_IDLE = 0U;
const unsigned char MODE_DSTAR = 1U;
const unsigned char MODE_DMR = 2U;
const unsigned char MODE_YSF = 3U;
const unsigned char MODE_P25 = 4U;
const unsigned char MODE_NXDN = 5U;
const unsigned char MODE_CW = 98U;
const unsigned char MODE_LOCKOUT = 99U;
const unsigned char MODE_ERROR = 100U;
const unsigned char TAG_HEADER = 0x00U;
const unsigned char TAG_DATA = 0x01U;
const unsigned char TAG_LOST = 0x02U;
const unsigned char TAG_EOT = 0x03U;
const unsigned char TAG_NODATA = 0x04U;
enum HW_TYPE {
HWT_MMDVM,
HWT_DVMEGA,
HWT_MMDVM_ZUMSPOT,
HWT_MMDVM_HS_HAT,
HWT_NANO_HOTSPOT,
HWT_MMDVM_HS,
HWT_UNKNOWN
};
enum RPT_RF_STATE {
RS_RF_LISTENING,
RS_RF_LATE_ENTRY,
RS_RF_AUDIO,
RS_RF_DATA,
RS_RF_REJECTED,
RS_RF_INVALID
};
enum RPT_NET_STATE {
RS_NET_IDLE,
RS_NET_AUDIO,
RS_NET_DATA
};
enum B_STATUS {
BS_NO_DATA,
BS_DATA,
BS_MISSING
};
const unsigned char MMDVM_FRAME_START = 0xE0U;
const unsigned char MMDVM_GET_VERSION = 0x00U;
const unsigned char MMDVM_GET_STATUS = 0x01U;
const unsigned char MMDVM_SET_CONFIG = 0x02U;
const unsigned char MMDVM_SET_MODE = 0x03U;
const unsigned char MMDVM_SET_FREQ = 0x04U;
const unsigned char MMDVM_SEND_CWID = 0x0AU;
const unsigned char MMDVM_DSTAR_HEADER = 0x10U;
const unsigned char MMDVM_DSTAR_DATA = 0x11U;
const unsigned char MMDVM_DSTAR_LOST = 0x12U;
const unsigned char MMDVM_DSTAR_EOT = 0x13U;
const unsigned char MMDVM_DMR_DATA1 = 0x18U;
const unsigned char MMDVM_DMR_LOST1 = 0x19U;
const unsigned char MMDVM_DMR_DATA2 = 0x1AU;
const unsigned char MMDVM_DMR_LOST2 = 0x1BU;
const unsigned char MMDVM_DMR_SHORTLC = 0x1CU;
const unsigned char MMDVM_DMR_START = 0x1DU;
const unsigned char MMDVM_DMR_ABORT = 0x1EU;
const unsigned char MMDVM_YSF_DATA = 0x20U;
const unsigned char MMDVM_YSF_LOST = 0x21U;
const unsigned char MMDVM_P25_HDR = 0x30U;
const unsigned char MMDVM_P25_LDU = 0x31U;
const unsigned char MMDVM_P25_LOST = 0x32U;
const unsigned char MMDVM_NXDN_DATA = 0x40U;
const unsigned char MMDVM_NXDN_LOST = 0x41U;
const unsigned char MMDVM_POCSAG_DATA = 0x50U;
const unsigned char MMDVM_FM_PARAMS1 = 0x60U;
const unsigned char MMDVM_FM_PARAMS2 = 0x61U;
const unsigned char MMDVM_FM_PARAMS3 = 0x62U;
const unsigned char MMDVM_ACK = 0x70U;
const unsigned char MMDVM_NAK = 0x7FU;
const unsigned char MMDVM_SERIAL = 0x80U;
const unsigned char MMDVM_TRANSPARENT = 0x90U;
const unsigned char MMDVM_QSO_INFO = 0x91U;
const unsigned char MMDVM_DEBUG1 = 0xF1U;
const unsigned char MMDVM_DEBUG2 = 0xF2U;
const unsigned char MMDVM_DEBUG3 = 0xF3U;
const unsigned char MMDVM_DEBUG4 = 0xF4U;
const unsigned char MMDVM_DEBUG5 = 0xF5U;
const unsigned int MAX_RESPONSES = 30U;
const unsigned int BUFFER_LENGTH = 2000U;
SerialModem::SerialModem(QString protocol)
{
set_mode(protocol);
m_dmrDelay = 0;
m_debug = false;
m_dmrColorCode = 1;
}
SerialModem::~SerialModem()
{
m_serial->close();
}
void SerialModem::set_mode(QString m)
{
m_dstarEnabled = 0;
m_dmrEnabled = 0;
m_ysfEnabled = 0;
m_p25Enabled = 0;
m_nxdnEnabled = 0;
m_pocsagEnabled = 0;
if((m == "REF") || (m == "DCS") || (m == "XRF")){
m_dstarEnabled = 1;
}
else if(m == "DMR"){
m_dmrEnabled = 1;
}
else if((m == "YSF") || (m == "FCS")){
m_ysfEnabled = 1;
}
else if(m == "P25"){
m_p25Enabled = 1;
}
else if(m == "NXDN"){
m_nxdnEnabled = 1;
}
}
void SerialModem::set_modem_flags(bool rxInvert, bool txInvert, bool pttInvert, bool useCOSAsLockout, bool duplex)
{
m_rxInvert = rxInvert;
m_txInvert = txInvert;
m_pttInvert = pttInvert;
m_useCOSAsLockout = useCOSAsLockout;
m_duplex = duplex;
m_ysfLoDev = 0;
}
void SerialModem::set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t txDelay, float rxLevel, float rfLevel, uint32_t ysfTXHang, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel)
{
m_rxfreq = rxfreq;
m_txfreq = txfreq;
m_txDelay = txDelay;
m_rxLevel = rxLevel;
m_rfLevel = rfLevel;
m_cwIdTXLevel = cwIdTXLevel;
m_dstarTXLevel = dstarTXLevel;
m_dmrTXLevel = dmrTXLevel;
m_ysfTXLevel = ysfTXLevel;
m_p25TXLevel = p25TXLevel;
m_nxdnTXLevel = nxdnTXLevel;
m_pocsagTXLevel = pocsagTXLevel;
m_ysfTXHang = ysfTXHang;
}
void SerialModem::connect_to_serial(QString p)
{
#ifndef Q_OS_ANDROID
m_serial = new QSerialPort;
#else
m_serial = &AndroidSerialPort::GetInstance();
#endif
m_serial->setPortName(p);
m_serial->setBaudRate(115200);
m_serial->setDataBits(QSerialPort::Data8);
m_serial->setStopBits(QSerialPort::OneStop);
m_serial->setParity(QSerialPort::NoParity);
//out << "Baud rate == " << serial->baudRate() << endl;
if (m_serial->open(QIODevice::ReadWrite)) {
m_modemtimer = new QTimer();
connect(m_modemtimer, SIGNAL(timeout()), this, SLOT(process_modem()));
m_modemtimer->start(19);
#ifndef Q_OS_ANDROID
connect(m_serial, &QSerialPort::readyRead, this, &SerialModem::process_serial);
#else
//connect(m_serial, &AndroidSerialPort::readyRead, this, &SerialModem::process_serial);
connect(m_serial, SIGNAL(data_received(QByteArray)), this, SLOT(receive_serial(QByteArray)));
#endif
m_serial->setFlowControl(QSerialPort::HardwareControl);
m_serial->setRequestToSend(true);
QByteArray a;
a.clear();
a.append(MMDVM_FRAME_START);
a.append(3);
a.append(MMDVM_GET_VERSION);
m_serial->write(a);
#ifdef DEBUGHW
fprintf(stderr, "MODEMTX %d:%d:", a.size(), m_serialdata.size());
for(int i = 0; i < a.size(); ++i){
//if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){
// i+= 6;
//}
fprintf(stderr, "%02x ", (unsigned char)a.data()[i]);
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}
}
void SerialModem::receive_serial(QByteArray d)
{
for(int i = 0; i < d.size(); i++){
m_serialdata.enqueue(d[i]);
}
//qDebug() << "SerialModem::process_serial()" << d.toHex();
}
void SerialModem::process_serial()
{
QByteArray d = m_serial->readAll();
for(int i = 0; i < d.size(); i++){
m_serialdata.enqueue(d[i]);
}
//qDebug() << "SerialModem::process_serial()" << d.toHex();
#ifdef DEBUGHW
fprintf(stderr, "MODEMRX %d:%d:", d.size(), m_serialdata.size());
for(int i = 0; i < d.size(); ++i){
//if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){
// i+= 6;
//}
fprintf(stderr, "%02x ", (unsigned char)d.data()[i]);
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}
void SerialModem::process_modem()
{
QByteArray out;
if(m_serialdata.size() < 3){
return;
}
//qDebug() << "process_modem() " << (uint8_t)m_serialdata[0] << ":" << (uint8_t)m_serialdata[1] << ":" << m_serialdata.size();
if(((uint8_t)m_serialdata[0] == MMDVM_FRAME_START) && (m_serialdata.size() >= m_serialdata[1])){
const uint8_t r = (uint8_t)m_serialdata[2];
const uint8_t s = (uint8_t)m_serialdata[1];
if(m_serialdata.size() >= m_serialdata[1]){
for(int i = 0; i < s; ++i){
out.append(m_serialdata.dequeue());
}
emit modem_data_ready(out);
}
if(r == MMDVM_GET_VERSION){
QThread::msleep(100);
set_freq();
QThread::msleep(100);
set_config();
}
}
}
void SerialModem::set_freq()
{
QByteArray out;
uint32_t pfreq = 433000000U;
qDebug() << "set_freq() rx:tx == " << m_rxfreq << ":" << m_txfreq;
out.clear();
out.append(MMDVM_FRAME_START);
out.append(17);
out.append(MMDVM_SET_FREQ);
out.append('\00');
out.append((m_rxfreq >> 0) & 0xFFU);
out.append((m_rxfreq >> 8) & 0xFFU);
out.append((m_rxfreq >> 16) & 0xFFU);
out.append((m_rxfreq >> 24) & 0xFFU);
out.append((m_txfreq >> 0) & 0xFFU);
out.append((m_txfreq >> 8) & 0xFFU);
out.append((m_txfreq >> 16) & 0xFFU);
out.append((m_txfreq >> 24) & 0xFFU);
out.append((unsigned char)(m_rfLevel * 2.55F + 0.5F));
out.append((pfreq >> 0) & 0xFFU);
out.append((pfreq >> 8) & 0xFFU);
out.append((pfreq >> 16) & 0xFFU);
out.append((pfreq >> 24) & 0xFFU);
m_serial->write(out);
#ifdef DEBUGHW
fprintf(stderr, "MODEMTX %d:%d:", out.size(), m_serialdata.size());
for(int i = 0; i < out.size(); ++i){
//if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){
// i+= 6;
//}
fprintf(stderr, "%02x ", (unsigned char)out.data()[i]);
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}
void SerialModem::set_config()
{
QByteArray out;
uint8_t c;
out.clear();
out.append(MMDVM_FRAME_START);
out.append(24U);
out.append(MMDVM_SET_CONFIG);
c = 0x00U;
if (m_rxInvert)
c |= 0x01U;
if (m_txInvert)
c |= 0x02U;
if (m_pttInvert)
c |= 0x04U;
if (m_ysfLoDev)
c |= 0x08U;
if (m_debug)
c |= 0x10U;
if (m_useCOSAsLockout)
c |= 0x20U;
if (!m_duplex)
c |= 0x80U;
out.append(c);
c = 0x00U;
if (m_dstarEnabled)
c |= 0x01U;
if (m_dmrEnabled)
c |= 0x02U;
if (m_ysfEnabled)
c |= 0x04U;
if (m_p25Enabled)
c |= 0x08U;
if (m_nxdnEnabled)
c |= 0x10U;
if (m_pocsagEnabled)
c |= 0x20U;
out.append(c);
out.append(m_txDelay / 10U); // In 10ms units
out.append(MODE_IDLE);
out.append((unsigned char)(m_rxLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F));
out.append(m_dmrColorCode);
out.append(m_dmrDelay);
out.append(128U); // Was OscOffset
out.append((unsigned char)(m_dstarTXLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_dmrTXLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_ysfTXLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_p25TXLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_txDCOffset + 128));
out.append((unsigned char)(m_rxDCOffset + 128));
out.append((unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F));
out.append((unsigned char)m_ysfTXHang);
out.append((unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F));
out.append((unsigned char)(m_fmTXLevel * 2.55F + 0.5F));
out.append((unsigned char)m_p25TXHang);
out.append((unsigned char)m_nxdnTXHang);
m_serial->write(out);
#ifdef DEBUGHW
fprintf(stderr, "MODEMTX %d:%d:", out.size(), m_serialdata.size());
for(int i = 0; i < out.size(); ++i){
//if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){
// i+= 6;
//}
fprintf(stderr, "%02x ", (unsigned char)out.data()[i]);
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}
void SerialModem::set_mode(uint8_t m)
{
QByteArray out;
out.clear();
out.append(MMDVM_FRAME_START);
out.append(4);
out.append(MMDVM_SET_MODE);
out.append(m);
m_serial->write(out);
}
void SerialModem::write(QByteArray b)
{
m_serial->write(b);
#ifdef DEBUGHW
fprintf(stderr, "MODEMTX %d:%d:", b.size(), m_serialdata.size());
for(int i = 0; i < b.size(); ++i){
fprintf(stderr, "%02x ", (unsigned char)b.data()[i]);
}
fprintf(stderr, "\n");
fflush(stderr);
#endif
}