1473 lines
35 KiB
C++
Executable File
1473 lines
35 KiB
C++
Executable File
/*
|
|
* Copyright (C) 2015,2016,2017,2018,2020 by Jonathan Naylor G4KLX
|
|
* Copyright (C) 2017,2018 by Andy Uribe CA6JAU
|
|
* Copyright (C) 2018 by 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 "MMDVMCal.h"
|
|
#include "Version.h"
|
|
|
|
#include <cstdio>
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#define EOL "\n"
|
|
#else
|
|
#include <unistd.h>
|
|
#define EOL "\r\n"
|
|
#endif
|
|
|
|
#include "Utils.h"
|
|
|
|
const unsigned char MMDVM_GET_STATUS = 0x01U;
|
|
const unsigned char MMDVM_FRAME_START = 0xE0U;
|
|
const unsigned char MMDVM_GET_VERSION = 0x00U;
|
|
const unsigned char MMDVM_SET_CONFIG = 0x02U;
|
|
const unsigned char MMDVM_SET_FREQ = 0x04U;
|
|
const unsigned char MMDVM_CAL_DATA = 0x08U;
|
|
|
|
const unsigned char MMDVM_ACK = 0x70U;
|
|
const unsigned char MMDVM_NAK = 0x7FU;
|
|
|
|
const unsigned int MAX_RESPONSES = 30U;
|
|
const unsigned int BUFFER_LENGTH = 2000U;
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if (argc < 2) {
|
|
::fprintf(stderr, "Usage: MMDVMCal <port>\n");
|
|
return 1;
|
|
}
|
|
|
|
CMMDVMCal cal(argv[1]);
|
|
|
|
return cal.run();
|
|
}
|
|
|
|
CMMDVMCal::CMMDVMCal(const std::string& port) :
|
|
m_serial(port, SERIAL_115200),
|
|
m_console(),
|
|
m_transmit(false),
|
|
m_carrier(false),
|
|
m_txLevel(50.0F),
|
|
m_rxLevel(50.0F),
|
|
m_txDCOffset(0),
|
|
m_rxDCOffset(0),
|
|
m_txInvert(false),
|
|
m_rxInvert(false),
|
|
m_pttInvert(false),
|
|
m_frequency(433000000U),
|
|
m_startfrequency(433000000U),
|
|
m_step(50U),
|
|
m_power(100.0F),
|
|
m_mode(STATE_DSTARCAL),
|
|
m_duplex(true),
|
|
m_debug(false),
|
|
m_buffer(NULL),
|
|
m_length(0U),
|
|
m_offset(0U),
|
|
m_dstarEnabled(false),
|
|
m_dmrEnabled(false),
|
|
m_dmrBERFEC(true),
|
|
m_ysfEnabled(false),
|
|
m_p25Enabled(false),
|
|
m_nxdnEnabled(false),
|
|
m_pocsagEnabled(false),
|
|
m_fmEnabled(false)
|
|
{
|
|
m_buffer = new unsigned char[BUFFER_LENGTH];
|
|
}
|
|
|
|
CMMDVMCal::~CMMDVMCal()
|
|
{
|
|
}
|
|
|
|
int CMMDVMCal::run()
|
|
{
|
|
bool ret = m_serial.open();
|
|
if (!ret)
|
|
return 1;
|
|
|
|
ret = initModem();
|
|
if (!ret) {
|
|
m_serial.close();
|
|
return 1;
|
|
}
|
|
|
|
ret = m_console.open();
|
|
if (!ret) {
|
|
m_serial.close();
|
|
return 1;
|
|
}
|
|
|
|
if (m_hwType == HWT_MMDVM)
|
|
loop_MMDVM();
|
|
else if (m_hwType == HWT_MMDVM_HS)
|
|
loop_MMDVM_HS();
|
|
|
|
if (m_transmit)
|
|
setTransmit();
|
|
|
|
m_serial.close();
|
|
m_console.close();
|
|
|
|
if (m_hwType == HWT_MMDVM) {
|
|
::fprintf(stdout, "PTT Invert: %s, RX Invert: %s, TX Invert: %s, RX Level: %.1f%%, TX Level: %.1f%%, TX DC Offset: %d, RX DC Offset: %d" EOL,
|
|
m_pttInvert ? "yes" : "no", m_rxInvert ? "yes" : "no", m_txInvert ? "yes" : "no",
|
|
m_rxLevel, m_txLevel, m_txDCOffset, m_rxDCOffset);
|
|
}
|
|
else if (m_hwType == HWT_MMDVM_HS) {
|
|
::fprintf(stdout, "TX Level: %.1f%%, Frequency Offset: %d, RF Level: %.1f%%" EOL, m_txLevel, (int)(m_frequency - m_startfrequency), m_power);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CMMDVMCal::loop_MMDVM()
|
|
{
|
|
displayHelp_MMDVM();
|
|
|
|
unsigned int counter=0;
|
|
bool end = false;
|
|
while (!end) {
|
|
int c = m_console.getChar();
|
|
switch (c) {
|
|
case 'H':
|
|
case 'h':
|
|
displayHelp_MMDVM();
|
|
break;
|
|
case 'W':
|
|
case 'w':
|
|
setDebug();
|
|
break;
|
|
case 'T':
|
|
setTXLevel(1);
|
|
break;
|
|
case 't':
|
|
setTXLevel(-1);
|
|
break;
|
|
case 'R':
|
|
setRXLevel(1);
|
|
break;
|
|
case 'r':
|
|
setRXLevel(-1);
|
|
break;
|
|
case ' ':
|
|
setTransmit();
|
|
break;
|
|
case 'I':
|
|
setTXInvert();
|
|
break;
|
|
case 'i':
|
|
setRXInvert();
|
|
break;
|
|
case 'P':
|
|
case 'p':
|
|
setPTTInvert();
|
|
break;
|
|
case 'o':
|
|
setTXDCOffset(-1);
|
|
break;
|
|
case 'O':
|
|
setTXDCOffset(1);
|
|
break;
|
|
case 'c':
|
|
setRXDCOffset(-1);
|
|
break;
|
|
case 'C':
|
|
setRXDCOffset(1);
|
|
break;
|
|
case 'Q':
|
|
case 'q':
|
|
end = true;
|
|
break;
|
|
case 'V':
|
|
case 'v':
|
|
::fprintf(stdout, VERSION EOL);
|
|
break;
|
|
case 'F':
|
|
setFMDeviation();
|
|
break;
|
|
case 'D':
|
|
setDMRDeviation();
|
|
break;
|
|
case 'd':
|
|
setDSTAR();
|
|
break;
|
|
case 'L':
|
|
case 'l':
|
|
setLowFrequencyCal();
|
|
break;
|
|
case 'A':
|
|
setDMRCal1K();
|
|
break;
|
|
case 'M':
|
|
case 'm':
|
|
setDMRDMO1K();
|
|
break;
|
|
case 'K':
|
|
case 'k':
|
|
setDSTARBER_FEC();
|
|
break;
|
|
case 'b':
|
|
setDMRBER_FEC();
|
|
break;
|
|
case 'B':
|
|
setDMRBER_1K();
|
|
break;
|
|
case 'J':
|
|
setYSFBER_FEC();
|
|
break;
|
|
case 'j':
|
|
setP25BER_FEC();
|
|
break;
|
|
case 'n':
|
|
setNXDNBER_FEC();
|
|
break;
|
|
case 'a':
|
|
setP25Cal1K();
|
|
break;
|
|
case 'N':
|
|
setNXDNCal1K();
|
|
break;
|
|
case 'g':
|
|
setPOCSAGCal();
|
|
break;
|
|
case 'S':
|
|
case 's':
|
|
setRSSI();
|
|
break;
|
|
case -1:
|
|
case 0:
|
|
break;
|
|
default:
|
|
::fprintf(stderr, "Unknown command - %c (H/h for help)" EOL, c);
|
|
break;
|
|
}
|
|
|
|
RESP_TYPE_MMDVM resp = getResponse();
|
|
|
|
if (resp == RTM_OK)
|
|
displayModem(m_buffer, m_length);
|
|
|
|
m_ber.clock();
|
|
sleep(5U);
|
|
|
|
if(counter >= 200)
|
|
{
|
|
if (getStatus())
|
|
displayModem(m_buffer, m_length);
|
|
counter=0;
|
|
}
|
|
counter++;
|
|
|
|
}
|
|
}
|
|
|
|
void CMMDVMCal::displayHelp_MMDVM()
|
|
{
|
|
::fprintf(stdout, "The commands are:" EOL);
|
|
::fprintf(stdout, " H/h Display help" EOL);
|
|
::fprintf(stdout, " Q/q Quit" EOL);
|
|
::fprintf(stdout, " W/w Enable/disable modem debug messages" EOL);
|
|
::fprintf(stdout, " I Toggle transmit inversion" EOL);
|
|
::fprintf(stdout, " i Toggle receive inversion" EOL);
|
|
::fprintf(stdout, " O Increase TX DC offset level" EOL);
|
|
::fprintf(stdout, " o Decrease TX DC offset level" EOL);
|
|
::fprintf(stdout, " C Increase RX DC offset level" EOL);
|
|
::fprintf(stdout, " c Decrease RX DC offset level" EOL);
|
|
::fprintf(stdout, " P/p Toggle PTT inversion" EOL);
|
|
::fprintf(stdout, " R Increase receive level" EOL);
|
|
::fprintf(stdout, " r Decrease receive level" EOL);
|
|
::fprintf(stdout, " T Increase transmit level" EOL);
|
|
::fprintf(stdout, " t Decrease transmit level" EOL);
|
|
::fprintf(stdout, " d D-Star Mode" EOL);
|
|
::fprintf(stdout, " F FM Deviation Mode (Adjust for correct Deviation)" EOL);
|
|
::fprintf(stdout, " D DMR Deviation Mode (Adjust for 2.75Khz Deviation)" EOL);
|
|
::fprintf(stdout, " L/l DMR Low Frequency Mode (80 Hz square wave)" EOL);
|
|
::fprintf(stdout, " A DMR Duplex 1031 Hz Test Pattern (TS2 CC1 ID1 TG9)" EOL);
|
|
::fprintf(stdout, " M/m DMR Simplex 1031 Hz Test Pattern (CC1 ID1 TG9)" EOL);
|
|
::fprintf(stdout, " a P25 1011 Hz Test Pattern (NAC293 ID1 TG1)" EOL);
|
|
::fprintf(stdout, " N NXDN 1031 Hz Test Pattern (RAN1 ID1 TG1)" EOL);
|
|
::fprintf(stdout, " K/k BER Test Mode (FEC) for D-Star" EOL);
|
|
::fprintf(stdout, " b BER Test Mode (FEC) for DMR Simplex (CC1)" EOL);
|
|
::fprintf(stdout, " B BER Test Mode (1031 Hz Test Pattern) for DMR Simplex (CC1 ID1 TG9)" EOL);
|
|
::fprintf(stdout, " J BER Test Mode (FEC) for YSF" EOL);
|
|
::fprintf(stdout, " j BER Test Mode (FEC) for P25" EOL);
|
|
::fprintf(stdout, " n BER Test Mode (FEC) for NXDN" EOL);
|
|
::fprintf(stdout, " g POCSAG 600Hz Test Pattern" EOL);
|
|
::fprintf(stdout, " S/s RSSI Mode" EOL);
|
|
::fprintf(stdout, " V/v Display version of MMDVMCal" EOL);
|
|
::fprintf(stdout, " <space> Toggle transmit" EOL);
|
|
}
|
|
|
|
void CMMDVMCal::loop_MMDVM_HS()
|
|
{
|
|
m_mode = STATE_DMRCAL;
|
|
unsigned int counter=0;
|
|
|
|
setFrequency();
|
|
writeConfig(m_txLevel, m_debug);
|
|
|
|
displayHelp_MMDVM_HS();
|
|
|
|
bool end = false;
|
|
while (!end) {
|
|
int c = m_console.getChar();
|
|
switch (c) {
|
|
case 'H':
|
|
case 'h':
|
|
displayHelp_MMDVM_HS();
|
|
break;
|
|
case 'W':
|
|
case 'w':
|
|
setDebug();
|
|
break;
|
|
case 'C':
|
|
case 'c':
|
|
setCarrier();
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
setEnterFreq();
|
|
break;
|
|
case 'T':
|
|
setTXLevel(1);
|
|
break;
|
|
case 't':
|
|
setTXLevel(-1);
|
|
break;
|
|
case 'F':
|
|
setFreq(1);
|
|
break;
|
|
case 'f':
|
|
setFreq(-1);
|
|
break;
|
|
case 'Z':
|
|
case 'z':
|
|
setStepFreq();
|
|
break;
|
|
case 'P':
|
|
setPower(1);
|
|
break;
|
|
case 'p':
|
|
setPower(-1);
|
|
break;
|
|
case ' ':
|
|
setTransmit();
|
|
break;
|
|
case 'Q':
|
|
case 'q':
|
|
end = true;
|
|
break;
|
|
case 'V':
|
|
case 'v':
|
|
::fprintf(stdout, VERSION EOL);
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
setDMRDeviation();
|
|
break;
|
|
case 'M':
|
|
case 'm':
|
|
setDMRDMO1K();
|
|
break;
|
|
case 'K':
|
|
case 'k':
|
|
setDSTARBER_FEC();
|
|
break;
|
|
case 'b':
|
|
setDMRBER_FEC();
|
|
break;
|
|
case 'B':
|
|
setDMRBER_1K();
|
|
break;
|
|
case 'J':
|
|
setYSFBER_FEC();
|
|
break;
|
|
case 'j':
|
|
setP25BER_FEC();
|
|
break;
|
|
case 'n':
|
|
setNXDNBER_FEC();
|
|
break;
|
|
case 'g':
|
|
setPOCSAGCal();
|
|
break;
|
|
case 'S':
|
|
case 's':
|
|
setRSSI();
|
|
break;
|
|
case 'I':
|
|
case 'i':
|
|
setIntCal();
|
|
break;
|
|
case -1:
|
|
case 0:
|
|
break;
|
|
default:
|
|
::fprintf(stderr, "Unknown command - %c (H/h for help)" EOL, c);
|
|
break;
|
|
}
|
|
|
|
RESP_TYPE_MMDVM resp = getResponse();
|
|
|
|
if (resp == RTM_OK)
|
|
displayModem(m_buffer, m_length);
|
|
|
|
m_ber.clock();
|
|
sleep(5U);
|
|
|
|
if(counter >= 200)
|
|
{
|
|
if (getStatus())
|
|
displayModem(m_buffer, m_length);
|
|
counter=0;
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
void CMMDVMCal::displayHelp_MMDVM_HS()
|
|
{
|
|
::fprintf(stdout, "The commands are:" EOL);
|
|
::fprintf(stdout, " H/h Display help" EOL);
|
|
::fprintf(stdout, " Q/q Quit" EOL);
|
|
::fprintf(stdout, " W/w Enable/disable modem debug messages" EOL);
|
|
::fprintf(stdout, " E/e Enter frequency (current: %u Hz)" EOL, m_frequency);
|
|
::fprintf(stdout, " F Increase frequency" EOL);
|
|
::fprintf(stdout, " f Decrease frequency" EOL);
|
|
::fprintf(stdout, " Z/z Enter frequency step" EOL);
|
|
::fprintf(stdout, " T Increase deviation" EOL);
|
|
::fprintf(stdout, " t Decrease deviation" EOL);
|
|
::fprintf(stdout, " P Increase RF power" EOL);
|
|
::fprintf(stdout, " p Decrease RF power" EOL);
|
|
::fprintf(stdout, " C/c Carrier Only Mode" EOL);
|
|
::fprintf(stdout, " K/k Set FM Deviation Modes" EOL);
|
|
::fprintf(stdout, " D/d DMR Deviation Mode (Adjust for 2.75Khz Deviation)" EOL);
|
|
::fprintf(stdout, " M/m DMR Simplex 1031 Hz Test Pattern (CC1 ID1 TG9)" EOL);
|
|
::fprintf(stdout, " K/k BER Test Mode (FEC) for D-Star" EOL);
|
|
::fprintf(stdout, " b BER Test Mode (FEC) for DMR Simplex (CC1)" EOL);
|
|
::fprintf(stdout, " B BER Test Mode (1031 Hz Test Pattern) for DMR Simplex (CC1 ID1 TG9)" EOL);
|
|
::fprintf(stdout, " J BER Test Mode (FEC) for YSF" EOL);
|
|
::fprintf(stdout, " j BER Test Mode (FEC) for P25" EOL);
|
|
::fprintf(stdout, " n BER Test Mode (FEC) for NXDN" EOL);
|
|
::fprintf(stdout, " g POCSAG 600Hz Test Pattern" EOL);
|
|
::fprintf(stdout, " S/s RSSI Mode" EOL);
|
|
::fprintf(stdout, " I/i Interrupt Counter Mode" EOL);
|
|
::fprintf(stdout, " V/v Display version of MMDVMCal" EOL);
|
|
::fprintf(stdout, " <space> Toggle transmit" EOL);
|
|
}
|
|
|
|
bool CMMDVMCal::initModem()
|
|
{
|
|
sleep(2000U); // 2s
|
|
|
|
for (unsigned int i = 0U; i < 6U; i++) {
|
|
unsigned char buffer[3U];
|
|
|
|
buffer[0U] = MMDVM_FRAME_START;
|
|
buffer[1U] = 3U;
|
|
buffer[2U] = MMDVM_GET_VERSION;
|
|
|
|
int ret = m_serial.write(buffer, 3U);
|
|
if (ret != 3)
|
|
return false;
|
|
|
|
#if defined(__APPLE__)
|
|
m_serial.setNonblock(true);
|
|
#endif
|
|
|
|
for (unsigned int count = 0U; count < MAX_RESPONSES; count++) {
|
|
sleep(10U);
|
|
RESP_TYPE_MMDVM resp = getResponse();
|
|
if (resp == RTM_OK && m_buffer[2U] == MMDVM_GET_VERSION) {
|
|
::fprintf(stderr, "Version: %u, description: %.*s" EOL, m_buffer[3U], m_length - 4U, m_buffer + 4U);
|
|
if (::memcmp(m_buffer + 4U, "MMDVM ", 6U) == 0)
|
|
m_hwType = HWT_MMDVM;
|
|
else if ((::memcmp(m_buffer + 4U, "ZUMspot", 7U) == 0) || (::memcmp(m_buffer + 4U, "MMDVM_HS_Hat", 12U) == 0) || (::memcmp(m_buffer + 4U, "MMDVM_HS_Dual_Hat", 17U) == 0) || (::memcmp(m_buffer + 4U, "Nano_hotSPOT", 12U) == 0) || (::memcmp(m_buffer + 4U, "Nano_DV", 7U) == 0) || (::memcmp(m_buffer + 4U, "MMDVM_HS-", 9U) == 0) || (::memcmp(m_buffer + 4U, "D2RG_MMDVM_HS", 13U) == 0))
|
|
m_hwType = HWT_MMDVM_HS;
|
|
else {
|
|
::fprintf(stderr, "Board not supported" EOL);
|
|
return false;
|
|
}
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
sleep(1500U);
|
|
}
|
|
|
|
::fprintf(stderr, "Unable to read the firmware version after six attempts" EOL);
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool CMMDVMCal::writeConfig(float txlevel, bool debug)
|
|
{
|
|
unsigned char buffer[50U];
|
|
|
|
buffer[0U] = MMDVM_FRAME_START;
|
|
buffer[1U] = 24U;
|
|
buffer[2U] = MMDVM_SET_CONFIG;
|
|
|
|
buffer[3U] = 0x00U;
|
|
if (m_rxInvert)
|
|
buffer[3U] |= 0x01U;
|
|
if (m_txInvert)
|
|
buffer[3U] |= 0x02U;
|
|
if (m_pttInvert)
|
|
buffer[3U] |= 0x04U;
|
|
if (!m_duplex)
|
|
buffer[3U] |= 0x80U;
|
|
if (debug)
|
|
buffer[3U] |= 0x10U;
|
|
|
|
buffer[4U] = 0x00U;
|
|
if (m_dstarEnabled)
|
|
buffer[4U] |= 0x01U;
|
|
if (m_dmrEnabled)
|
|
buffer[4U] |= 0x02U;
|
|
if (m_ysfEnabled)
|
|
buffer[4U] |= 0x04U;
|
|
if (m_p25Enabled)
|
|
buffer[4U] |= 0x08U;
|
|
if (m_nxdnEnabled)
|
|
buffer[4U] |= 0x10U;
|
|
if (m_pocsagEnabled)
|
|
buffer[4U] |= 0x20U;
|
|
if (m_fmEnabled)
|
|
buffer[4U] |= 0x40U;
|
|
|
|
buffer[5U] = 0U;
|
|
buffer[6U] = m_mode;
|
|
buffer[7U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F);
|
|
buffer[8U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[9U] = 1U;
|
|
buffer[10U] = 0U;
|
|
buffer[11U] = 128U;
|
|
buffer[12U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[13U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[14U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[15U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[16U] = (unsigned char)(m_txDCOffset + 128);
|
|
buffer[17U] = (unsigned char)(m_rxDCOffset + 128);
|
|
buffer[18U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[19U] = 0U;
|
|
buffer[20U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[21U] = (unsigned char)(txlevel * 2.55F + 0.5F);
|
|
buffer[22U] = 0U;
|
|
buffer[23U] = 0U;
|
|
|
|
int ret = m_serial.write(buffer, 24U);
|
|
if (ret <= 0)
|
|
return false;
|
|
|
|
unsigned int count = 0U;
|
|
RESP_TYPE_MMDVM resp;
|
|
do {
|
|
sleep(10U);
|
|
|
|
resp = getResponse();
|
|
if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
::fprintf(stdout, "The MMDVM is not responding to the SET_CONFIG command" EOL);
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK);
|
|
|
|
if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) {
|
|
::fprintf(stdout, "Received a NAK to the SET_CONFIG command from the modem: %u" EOL, m_buffer[4U]);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setRXInvert()
|
|
{
|
|
m_rxInvert = !m_rxInvert;
|
|
|
|
::fprintf(stdout, "RX Invert: %s" EOL, m_rxInvert ? "On" : "Off");
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setTXInvert()
|
|
{
|
|
m_txInvert = !m_txInvert;
|
|
|
|
::fprintf(stdout, "TX Invert: %s" EOL, m_txInvert ? "On" : "Off");
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setPTTInvert()
|
|
{
|
|
m_pttInvert = !m_pttInvert;
|
|
|
|
::fprintf(stdout, "PTT Invert: %s" EOL, m_pttInvert ? "On" : "Off");
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDebug()
|
|
{
|
|
m_debug = !m_debug;
|
|
|
|
::fprintf(stdout, "Modem debug: %s" EOL, m_debug ? "On" : "Off");
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDMRDeviation()
|
|
{
|
|
m_mode = STATE_DMRCAL;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "DMR Deviation Mode (Set to 2.75Khz Deviation)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setFMDeviation()
|
|
{
|
|
switch (m_mode)
|
|
{
|
|
case STATE_FMCAL10K:
|
|
m_mode = STATE_FMCAL12K;
|
|
::fprintf(stdout, "FM 12.5Khz channel spacing with 2.50Khz Deviation (1039.5hz)" EOL);
|
|
break;
|
|
case STATE_FMCAL12K:
|
|
m_mode = STATE_FMCAL15K;
|
|
::fprintf(stdout, "FM 15Khz channel spacing with 3.0Khz Deviation (1247hz)" EOL);
|
|
break;
|
|
case STATE_FMCAL15K:
|
|
m_mode = STATE_FMCAL20K;
|
|
::fprintf(stdout, "FM 20Khz channel spacing with 4.0Khz Deviation (1663hz)" EOL);
|
|
break;
|
|
case STATE_FMCAL20K:
|
|
m_mode = STATE_FMCAL25K;
|
|
::fprintf(stdout, "FM 25Khz channel spacing with 5.0Khz Deviation (2079hz)" EOL);
|
|
break;
|
|
case STATE_FMCAL25K:
|
|
m_mode = STATE_FMCAL30K;
|
|
::fprintf(stdout, "FM 30Khz channel spacing with 6.0Khz Deviation (2495hz)" EOL);
|
|
break;
|
|
default:
|
|
m_mode = STATE_FMCAL10K;
|
|
::fprintf(stdout, "FM 10Khz channel spacing with 2.30Khz Deviation (956hz)" EOL);
|
|
break;
|
|
}
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setLowFrequencyCal()
|
|
{
|
|
m_mode = STATE_LFCAL;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "DMR Low Frequency Mode (80 Hz square wave)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDMRCal1K()
|
|
{
|
|
m_mode = STATE_DMRCAL1K;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "DMR Duplex 1031 Hz Test Pattern (TS2 CC1 ID1 TG9)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDMRDMO1K()
|
|
{
|
|
if (m_transmit && (m_hwType == HWT_MMDVM_HS)) {
|
|
::fprintf(stdout, "First turn off the transmitter" EOL);
|
|
return false;
|
|
}
|
|
else {
|
|
m_mode = STATE_DMRDMO1K;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "DMR Simplex 1031 Hz Test Pattern (CC1 ID1 TG9)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
}
|
|
|
|
bool CMMDVMCal::setP25Cal1K()
|
|
{
|
|
m_mode = STATE_P25CAL1K;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "P25 1011 Hz Test Pattern (NAC293 ID1 TG1)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setNXDNCal1K()
|
|
{
|
|
m_mode = STATE_NXDNCAL1K;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "NXDN 1031 Hz Test Pattern (RAN1 ID1 TG1)" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setPOCSAGCal()
|
|
{
|
|
m_mode = STATE_POCSAGCAL;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "POCSAG 600 Hz Test Pattern" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDSTARBER_FEC()
|
|
{
|
|
m_mode = STATE_DSTAR;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = true;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (FEC) for D-Star" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDMRBER_FEC()
|
|
{
|
|
m_mode = STATE_DMR;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = true;
|
|
m_dmrBERFEC = true;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (FEC) for DMR Simplex" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDMRBER_1K()
|
|
{
|
|
m_mode = STATE_DMR;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = true;
|
|
m_dmrBERFEC = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (1031 Hz Test Pattern) for DMR Simplex" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setYSFBER_FEC()
|
|
{
|
|
m_mode = STATE_YSF;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = true;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (FEC) for YSF" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setP25BER_FEC()
|
|
{
|
|
m_mode = STATE_P25;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = true;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (FEC) for P25" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setNXDNBER_FEC()
|
|
{
|
|
m_mode = STATE_NXDN;
|
|
m_carrier = false;
|
|
m_duplex = false;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = true;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "BER Test Mode (FEC) for NXDN" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setDSTAR()
|
|
{
|
|
m_mode = STATE_DSTARCAL;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "D-Star Mode" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setRSSI()
|
|
{
|
|
m_mode = STATE_RSSICAL;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "RSSI Mode" EOL);
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setIntCal()
|
|
{
|
|
m_mode = STATE_INTCAL;
|
|
m_carrier = false;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "Interrupt Counter Mode" EOL);
|
|
|
|
return writeConfig(m_txLevel, true);
|
|
}
|
|
|
|
bool CMMDVMCal::setCarrier()
|
|
{
|
|
m_mode = STATE_DMRCAL;
|
|
m_carrier = true;
|
|
m_duplex = true;
|
|
m_dstarEnabled = false;
|
|
m_dmrEnabled = false;
|
|
m_ysfEnabled = false;
|
|
m_p25Enabled = false;
|
|
m_nxdnEnabled = false;
|
|
m_pocsagEnabled = false;
|
|
m_fmEnabled = false;
|
|
|
|
::fprintf(stdout, "Carrier Only Mode: %u Hz" EOL, m_frequency);
|
|
|
|
return writeConfig(0.0F, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setEnterFreq()
|
|
{
|
|
char buff[256U];
|
|
|
|
::fprintf(stdout, "Enter frequency (current %u Hz):" EOL, m_frequency);
|
|
|
|
m_console.close();
|
|
|
|
if (std::fgets(buff, 256, stdin) != NULL ) {
|
|
|
|
unsigned long int freq = std::strtoul(buff, NULL, 10);
|
|
|
|
if (freq >= 100000000U && freq <= 999999999U) {
|
|
m_frequency = (unsigned int)freq;
|
|
m_startfrequency = m_frequency;
|
|
::fprintf(stdout, "New frequency: %u Hz" EOL, m_frequency);
|
|
setFrequency();
|
|
}
|
|
else
|
|
::fprintf(stdout, "Not valid frequency" EOL);
|
|
}
|
|
|
|
m_console.open();
|
|
|
|
displayHelp_MMDVM_HS();
|
|
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
bool CMMDVMCal::setRXLevel(int incr)
|
|
{
|
|
if (incr > 0 && m_rxLevel < 100.0F) {
|
|
m_rxLevel += 0.5F;
|
|
::fprintf(stdout, "RX Level: %.1f%%" EOL, m_rxLevel);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
if (incr < 0 && m_rxLevel > 0.0F) {
|
|
m_rxLevel -= 0.5F;
|
|
::fprintf(stdout, "RX Level: %.1f%%" EOL, m_rxLevel);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setTXLevel(int incr)
|
|
{
|
|
if (!m_carrier) {
|
|
if (incr > 0 && m_txLevel < 100.0F) {
|
|
m_txLevel += 0.5F;
|
|
::fprintf(stdout, "TX Level: %.1f%%" EOL, m_txLevel);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
if (incr < 0 && m_txLevel > 0.0F) {
|
|
m_txLevel -= 0.5F;
|
|
::fprintf(stdout, "TX Level: %.1f%%" EOL, m_txLevel);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setFreq(int incr)
|
|
{
|
|
bool ret;
|
|
|
|
if (incr > 0) {
|
|
m_frequency += m_step;
|
|
::fprintf(stdout, "TX frequency: %u" EOL, m_frequency);
|
|
setFrequency();
|
|
if (m_carrier)
|
|
ret = writeConfig(0.0F, m_debug);
|
|
else
|
|
ret = writeConfig(m_txLevel, m_debug);
|
|
return ret;
|
|
}
|
|
|
|
if (incr < 0) {
|
|
m_frequency -= m_step;
|
|
::fprintf(stdout, "TX frequency: %u" EOL, m_frequency);
|
|
setFrequency();
|
|
if (m_carrier)
|
|
ret = writeConfig(0.0F, m_debug);
|
|
else
|
|
ret = writeConfig(m_txLevel, m_debug);
|
|
return ret;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setStepFreq()
|
|
{
|
|
char buff[256U];
|
|
|
|
::fprintf(stdout, "Enter frequency step (current %u Hz):" EOL, m_step);
|
|
|
|
m_console.close();
|
|
|
|
if (std::fgets(buff, 256, stdin) != NULL ) {
|
|
|
|
unsigned long int freq = std::strtoul(buff, NULL, 10);
|
|
|
|
if (freq >= 10U && freq <= 25000U) {
|
|
m_step = (unsigned int)freq;
|
|
::fprintf(stdout, "New frequency step: %u Hz" EOL, m_step);
|
|
}
|
|
else
|
|
::fprintf(stdout, "Not valid frequency step" EOL);
|
|
}
|
|
|
|
m_console.open();
|
|
|
|
displayHelp_MMDVM_HS();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setPower(int incr)
|
|
{
|
|
if (incr > 0 && m_power < 100.0F) {
|
|
m_power += 1.0F;
|
|
::fprintf(stdout, "RF power: %.1f%%" EOL, m_power);
|
|
setFrequency();
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
if (incr < 0 && m_power > 0.0F) {
|
|
m_power -= 1.0F;
|
|
::fprintf(stdout, "RF power: %.1f%%" EOL, m_power);
|
|
setFrequency();
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setTXDCOffset(int incr)
|
|
{
|
|
if (incr > 0 && m_txDCOffset < 127) {
|
|
m_txDCOffset++;
|
|
::fprintf(stdout, "TX DC Offset: %d" EOL, m_txDCOffset);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
if (incr < 0 && m_txDCOffset > -128) {
|
|
m_txDCOffset--;
|
|
::fprintf(stdout, "TX DC Offset: %d" EOL, m_txDCOffset);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setRXDCOffset(int incr)
|
|
{
|
|
if (incr > 0 && m_rxDCOffset < 127) {
|
|
m_rxDCOffset++;
|
|
::fprintf(stdout, "RX DC Offset: %d" EOL, m_rxDCOffset);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
if (incr < 0 && m_rxDCOffset > -128) {
|
|
m_rxDCOffset--;
|
|
::fprintf(stdout, "RX DC Offset: %d" EOL, m_rxDCOffset);
|
|
return writeConfig(m_txLevel, m_debug);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMMDVMCal::setTransmit()
|
|
{
|
|
m_transmit = !m_transmit;
|
|
|
|
unsigned char buffer[50U];
|
|
|
|
buffer[0U] = MMDVM_FRAME_START;
|
|
buffer[1U] = 4U;
|
|
buffer[2U] = MMDVM_CAL_DATA;
|
|
buffer[3U] = m_transmit ? 0x01U : 0x00U;
|
|
|
|
int ret = m_serial.write(buffer, 4U);
|
|
if (ret <= 0)
|
|
return false;
|
|
|
|
unsigned int count = 0U;
|
|
RESP_TYPE_MMDVM resp;
|
|
do {
|
|
sleep(10U);
|
|
|
|
resp = getResponse();
|
|
if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
::fprintf(stdout, "The MMDVM is not responding to the CAL_DATA command" EOL);
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK);
|
|
|
|
if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) {
|
|
::fprintf(stdout, "Received a NAK to the CAL_DATA command from the modem: %u" EOL, m_buffer[4U]);
|
|
return false;
|
|
}
|
|
|
|
if (m_transmit)
|
|
::fprintf(stdout, "Set transmitter ON" EOL);
|
|
else
|
|
::fprintf(stdout, "Set transmitter OFF" EOL);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMMDVMCal::displayModem(const unsigned char *buffer, unsigned int length)
|
|
{
|
|
if (buffer[2U] == MMDVM_GET_STATUS) {
|
|
bool adcOverflow = (buffer[5U] & 0x02U) == 0x02U;
|
|
if (adcOverflow)
|
|
::fprintf(stderr, "MMDVM ADC levels have overflowed" EOL);
|
|
bool rxOverflow = (buffer[5U] & 0x04U) == 0x04U;
|
|
if (rxOverflow)
|
|
::fprintf(stderr,"MMDVM RX buffer has overflowed" EOL);
|
|
bool txOverflow = (buffer[5U] & 0x08U) == 0x08U;
|
|
if (txOverflow)
|
|
::fprintf(stderr,"MMDVM TX buffer has overflowed" EOL);
|
|
bool dacOverflow = (buffer[5U] & 0x20U) == 0x20U;
|
|
if (dacOverflow)
|
|
::fprintf(stderr,"MMDVM DAC levels have overflowed" EOL);
|
|
} else if (buffer[2U] == 0x08U) {
|
|
bool inverted = (buffer[3U] == 0x80U);
|
|
short high = buffer[4U] << 8 | buffer[5U];
|
|
short low = buffer[6U] << 8 | buffer[7U];
|
|
short diff = high - low;
|
|
short centre = (high + low) / 2;
|
|
::fprintf(stdout, "Levels: inverted: %s, max: %d, min: %d, diff: %d, centre: %d" EOL, inverted ? "yes" : "no", high, low, diff, centre);
|
|
} else if (buffer[2U] == 0x09U) {
|
|
unsigned short max = buffer[3U] << 8 | buffer[4U];
|
|
unsigned short min = buffer[5U] << 8 | buffer[6U];
|
|
unsigned short ave = buffer[7U] << 8 | buffer[8U];
|
|
::fprintf(stdout, "RSSI: max: %u, min: %u, ave: %u" EOL, max, min, ave);
|
|
} else if (buffer[2U] == 0xF1U) {
|
|
::fprintf(stdout, "Debug: %.*s" EOL, length - 3U, buffer + 3U);
|
|
} else if (buffer[2U] == 0xF2U) {
|
|
short val1 = (buffer[length - 2U] << 8) | buffer[length - 1U];
|
|
::fprintf(stdout, "Debug: %.*s %d" EOL, length - 5U, buffer + 3U, val1);
|
|
} else if (buffer[2U] == 0xF3U) {
|
|
short val1 = (buffer[length - 4U] << 8) | buffer[length - 3U];
|
|
short val2 = (buffer[length - 2U] << 8) | buffer[length - 1U];
|
|
::fprintf(stdout, "Debug: %.*s %d %d" EOL, length - 7U, buffer + 3U, val1, val2);
|
|
} else if (buffer[2U] == 0xF4U) {
|
|
short val1 = (buffer[length - 6U] << 8) | buffer[length - 5U];
|
|
short val2 = (buffer[length - 4U] << 8) | buffer[length - 3U];
|
|
short val3 = (buffer[length - 2U] << 8) | buffer[length - 1U];
|
|
::fprintf(stdout, "Debug: %.*s %d %d %d" EOL, length - 9U, buffer + 3U, val1, val2, val3);
|
|
} else if (buffer[2U] == 0xF5U) {
|
|
short val1 = (buffer[length - 8U] << 8) | buffer[length - 7U];
|
|
short val2 = (buffer[length - 6U] << 8) | buffer[length - 5U];
|
|
short val3 = (buffer[length - 4U] << 8) | buffer[length - 3U];
|
|
short val4 = (buffer[length - 2U] << 8) | buffer[length - 1U];
|
|
::fprintf(stdout, "Debug: %.*s %d %d %d %d" EOL, length - 11U, buffer + 3U, val1, val2, val3, val4);
|
|
} else if (buffer[2U] == 0x10U || buffer[2U] == 0x11U || buffer[2U] == 0x12U || buffer[2U] == 0x13U) {
|
|
m_ber.DSTARFEC(buffer + 3U, buffer[2U]);
|
|
} else if (buffer[2U] == 0x18U || buffer[2U] == 0x1AU) {
|
|
if (m_dmrBERFEC)
|
|
m_ber.DMRFEC(buffer + 4U, buffer[3]);
|
|
else
|
|
m_ber.DMR1K(buffer + 4U, buffer[3]);
|
|
} else if (buffer[2U] == 0x20U) {
|
|
m_ber.YSFFEC(buffer + 4U);
|
|
} else if (buffer[2U] == 0x30U || buffer[2U] == 0x31U) {
|
|
m_ber.P25FEC(buffer + 4U);
|
|
} else if (buffer[2U] == 0x40U) {
|
|
m_ber.NXDNFEC(buffer + 4U, buffer[3U]);
|
|
} else if (m_hwType == HWT_MMDVM && m_mode != STATE_DMR && m_mode != STATE_P25 && m_mode != STATE_NXDN) {
|
|
CUtils::dump("Response", buffer, length);
|
|
}
|
|
}
|
|
|
|
bool CMMDVMCal::setFrequency()
|
|
{
|
|
unsigned char buffer[16U];
|
|
|
|
buffer[0U] = MMDVM_FRAME_START;
|
|
|
|
buffer[1U] = 13U;
|
|
|
|
buffer[2U] = MMDVM_SET_FREQ;
|
|
|
|
buffer[3U] = 0x00U;
|
|
|
|
buffer[4U] = (m_frequency >> 0) & 0xFFU;
|
|
buffer[5U] = (m_frequency >> 8) & 0xFFU;
|
|
buffer[6U] = (m_frequency >> 16) & 0xFFU;
|
|
buffer[7U] = (m_frequency >> 24) & 0xFFU;
|
|
|
|
buffer[8U] = (m_frequency >> 0) & 0xFFU;
|
|
buffer[9U] = (m_frequency >> 8) & 0xFFU;
|
|
buffer[10U] = (m_frequency >> 16) & 0xFFU;
|
|
buffer[11U] = (m_frequency >> 24) & 0xFFU;
|
|
|
|
buffer[12U] = (unsigned char)(m_power * 2.55F + 0.5F);
|
|
|
|
int ret = m_serial.write(buffer, 13U);
|
|
if (ret != 13U)
|
|
return false;
|
|
|
|
unsigned int count = 0U;
|
|
RESP_TYPE_MMDVM resp;
|
|
do {
|
|
sleep(10U);
|
|
|
|
resp = getResponse();
|
|
if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
::fprintf(stderr, "The MMDVM is not responding to the SET_FREQ command" EOL);
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK);
|
|
|
|
if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) {
|
|
::fprintf(stderr, "Received a NAK to the SET_FREQ command from the modem, %u" EOL, m_buffer[4U]);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CMMDVMCal::getStatus()
|
|
{
|
|
unsigned char buffer[16U];
|
|
buffer[0U] = MMDVM_FRAME_START;
|
|
buffer[1U] = 3U;
|
|
buffer[2U] = MMDVM_GET_STATUS;
|
|
|
|
int ret = m_serial.write(buffer, 3U);
|
|
if (ret != 3)
|
|
return false;
|
|
unsigned int count = 0U;
|
|
RESP_TYPE_MMDVM resp;
|
|
do {
|
|
sleep(10U);
|
|
|
|
resp = getResponse();
|
|
if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
::fprintf(stderr, "The MMDVM is not responding to the GET_STATUS command" EOL);
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK);
|
|
|
|
if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) {
|
|
::fprintf(stderr, "Received a NAK to the GET_STATUS command from the modem, %u" EOL, m_buffer[4U]);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
RESP_TYPE_MMDVM CMMDVMCal::getResponse()
|
|
{
|
|
if (m_offset == 0U) {
|
|
// Get the start of the frame or nothing at all
|
|
int ret = m_serial.read(m_buffer + 0U, 1U);
|
|
if (ret < 0) {
|
|
::fprintf(stderr, "Error when reading from the modem" EOL);
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RTM_TIMEOUT;
|
|
|
|
if (m_buffer[0U] != MMDVM_FRAME_START)
|
|
return RTM_TIMEOUT;
|
|
|
|
m_offset = 1U;
|
|
}
|
|
|
|
if (m_offset == 1U) {
|
|
// Get the length of the frame
|
|
int ret = m_serial.read(m_buffer + 1U, 1U);
|
|
if (ret < 0) {
|
|
::fprintf(stderr, "Error when reading from the modem" EOL);
|
|
m_offset = 0U;
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RTM_TIMEOUT;
|
|
|
|
if (m_buffer[1U] >= 250U) {
|
|
::fprintf(stderr, "Invalid length received from the modem - %u" EOL, m_buffer[1U]);
|
|
m_offset = 0U;
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
m_length = m_buffer[1U];
|
|
m_offset = 2U;
|
|
}
|
|
|
|
if (m_offset == 2U) {
|
|
// Get the frame type
|
|
int ret = m_serial.read(m_buffer + 2U, 1U);
|
|
if (ret < 0) {
|
|
::fprintf(stderr, "Error when reading from the modem" EOL);
|
|
m_offset = 0U;
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RTM_TIMEOUT;
|
|
|
|
m_offset = 3U;
|
|
}
|
|
|
|
if (m_offset >= 3U) {
|
|
// Use later two byte length field
|
|
if (m_length == 0U) {
|
|
int ret = m_serial.read(m_buffer + 3U, 2U);
|
|
if (ret < 0) {
|
|
::fprintf(stderr, "Error when reading from the modem" EOL);
|
|
m_offset = 0U;
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RTM_TIMEOUT;
|
|
|
|
m_length = (m_buffer[3U] << 8) | m_buffer[4U];
|
|
m_offset = 5U;
|
|
}
|
|
|
|
while (m_offset < m_length) {
|
|
int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset);
|
|
if (ret < 0) {
|
|
::fprintf(stderr, "Error when reading from the modem" EOL);
|
|
m_offset = 0U;
|
|
return RTM_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RTM_TIMEOUT;
|
|
|
|
if (ret > 0)
|
|
m_offset += ret;
|
|
}
|
|
}
|
|
|
|
m_offset = 0;
|
|
|
|
return RTM_OK;
|
|
}
|
|
|
|
void CMMDVMCal::sleep(unsigned int ms)
|
|
{
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
::Sleep(ms);
|
|
#else
|
|
::usleep(ms * 1000);
|
|
#endif
|
|
}
|