You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
802 lines
17 KiB
802 lines
17 KiB
/*
|
|
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
|
|
*
|
|
* 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 "DVRPTRV3Controller.h"
|
|
#include "DStarDefines.h"
|
|
#include "Logger.h"
|
|
#include "Timer.h"
|
|
|
|
#include <chrono>
|
|
#include <thread>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#if !defined(_WIN32)
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
const unsigned int MAX_RESPONSES = 30U;
|
|
|
|
const unsigned int BUFFER_LENGTH = 200U;
|
|
|
|
CDVRPTRV3Controller::CDVRPTRV3Controller(const std::string& port, const std::string& path, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) :
|
|
CModem(),
|
|
m_connection(CT_USB),
|
|
m_usbPort(port),
|
|
m_usbPath(path),
|
|
m_address(),
|
|
m_port(0U),
|
|
m_txInvert(txInvert),
|
|
m_modLevel(modLevel),
|
|
m_duplex(duplex),
|
|
m_callsign(callsign),
|
|
m_txDelay(txDelay),
|
|
m_usb(nullptr),
|
|
m_network(nullptr),
|
|
m_buffer(nullptr),
|
|
m_txData(1000U),
|
|
m_rx(false)
|
|
{
|
|
assert(!port.empty());
|
|
|
|
m_usb = new CSerialDataController(port, SERIAL_115200);
|
|
|
|
m_buffer = new unsigned char[BUFFER_LENGTH];
|
|
}
|
|
|
|
CDVRPTRV3Controller::CDVRPTRV3Controller(const std::string& address, unsigned int port, bool txInvert, unsigned int modLevel, bool duplex, const std::string& callsign, unsigned int txDelay) :
|
|
CModem(),
|
|
m_connection(CT_NETWORK),
|
|
m_usbPort(),
|
|
m_usbPath(),
|
|
m_address(address),
|
|
m_port(port),
|
|
m_txInvert(txInvert),
|
|
m_modLevel(modLevel),
|
|
m_duplex(duplex),
|
|
m_callsign(callsign),
|
|
m_txDelay(txDelay),
|
|
m_usb(nullptr),
|
|
m_network(nullptr),
|
|
m_buffer(nullptr),
|
|
m_txData(1000U),
|
|
m_rx(false)
|
|
{
|
|
assert(!address.empty());
|
|
assert(port > 0U);
|
|
|
|
m_network = new CTCPReaderWriter(address, port);
|
|
|
|
m_buffer = new unsigned char[BUFFER_LENGTH];
|
|
}
|
|
|
|
CDVRPTRV3Controller::~CDVRPTRV3Controller()
|
|
{
|
|
delete m_usb;
|
|
delete m_network;
|
|
|
|
delete[] m_buffer;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::start()
|
|
{
|
|
findPort();
|
|
|
|
bool ret = openModem();
|
|
if (!ret)
|
|
return false;
|
|
|
|
findPath();
|
|
|
|
m_thread = std::thread(&CDVRPTRV3Controller::entry, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDVRPTRV3Controller::entry()
|
|
{
|
|
wxLogMessage("Starting DV-RPTR3 Modem Controller thread");
|
|
|
|
// Clock every 5ms-ish
|
|
CTimer pollTimer(200U, 0U, 250U);
|
|
pollTimer.start();
|
|
|
|
unsigned int space = 0U;
|
|
|
|
while (!m_stopped) {
|
|
// Poll the modem status every 250ms
|
|
if (pollTimer.hasExpired()) {
|
|
bool ret = readSpace();
|
|
if (!ret) {
|
|
ret = findModem();
|
|
if (!ret) {
|
|
wxLogMessage("Stopping DV-RPTR3 Modem Controller thread");
|
|
return;
|
|
}
|
|
}
|
|
|
|
pollTimer.start();
|
|
}
|
|
|
|
unsigned int length;
|
|
RESP_TYPE_V3 type = getResponse(m_buffer, length);
|
|
|
|
switch (type) {
|
|
case RT3_TIMEOUT:
|
|
break;
|
|
|
|
case RT3_ERROR: {
|
|
bool ret = findModem();
|
|
if (!ret) {
|
|
wxLogMessage("Stopping DV-RPTR3 Modem Controller thread");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RT3_HEADER: {
|
|
// CUtils::dump("RT3_HEADER", m_buffer, length);
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
unsigned char data[2U];
|
|
data[0U] = DSMTT_HEADER;
|
|
data[1U] = RADIO_HEADER_LENGTH_BYTES;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
m_rxData.addData(m_buffer + 9U, RADIO_HEADER_LENGTH_BYTES - 2U);
|
|
|
|
// Dummy checksum
|
|
data[0U] = 0xFFU;
|
|
data[1U] = 0xFFU;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
data[0U] = DSMTT_DATA;
|
|
data[1U] = DV_FRAME_LENGTH_BYTES;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
m_rxData.addData(m_buffer + 51U, DV_FRAME_LENGTH_BYTES);
|
|
|
|
m_rx = true;
|
|
}
|
|
break;
|
|
|
|
case RT3_DATA: {
|
|
// CUtils::dump("RT3_DATA", m_buffer, length);
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
unsigned char data[2U];
|
|
data[0U] = DSMTT_DATA;
|
|
data[1U] = DV_FRAME_LENGTH_BYTES;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
m_rxData.addData(m_buffer + 5U, DV_FRAME_LENGTH_BYTES);
|
|
|
|
m_rx = true;
|
|
|
|
// End of transmission?
|
|
bool end = (m_buffer[19U] & 0x40U) == 0x40U;
|
|
if (end) {
|
|
data[0U] = DSMTT_EOT;
|
|
data[1U] = 0U;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
m_rx = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case RT3_SPACE:
|
|
space = m_buffer[9U];
|
|
// CUtils::dump("RT3_SPACE", m_buffer, length);
|
|
break;
|
|
|
|
// These should not be received in this loop, but don't complain if we do
|
|
case RT3_QUERY:
|
|
case RT3_CONFIG:
|
|
break;
|
|
|
|
default:
|
|
wxLogMessage("Unknown DV-RPTR3 message, type");
|
|
CUtils::dump("Buffer dump", m_buffer, length);
|
|
break;
|
|
}
|
|
|
|
if (space >= 4U) {
|
|
if (m_txData.hasData()) {
|
|
unsigned char len = 0U;
|
|
unsigned char data[200U];
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
m_txData.getData(&len, 1U);
|
|
m_txData.getData(data, len);
|
|
}
|
|
|
|
// CUtils::dump("Write", data, len);
|
|
|
|
bool ret = writeModem(data, len);
|
|
if (!ret) {
|
|
ret = findModem();
|
|
if (!ret) {
|
|
wxLogMessage("Stopping DV-RPTR3 Modem Controller thread");
|
|
return;
|
|
}
|
|
} else {
|
|
if (len > 100U)
|
|
space -= 4U;
|
|
else
|
|
space--;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
|
|
pollTimer.clock();
|
|
}
|
|
|
|
wxLogMessage("Stopping DV-RPTR3 Modem Controller thread");
|
|
|
|
closeModem();
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::writeHeader(const CHeaderData& header)
|
|
{
|
|
bool ret = m_txData.hasSpace(106U);
|
|
if (!ret) {
|
|
wxLogWarning("No space to write the header");
|
|
return false;
|
|
}
|
|
|
|
unsigned char buffer[105U];
|
|
|
|
::memset(buffer, 0x00U, 105U);
|
|
|
|
buffer[0U] = 'H';
|
|
buffer[1U] = 'E';
|
|
buffer[2U] = 'A';
|
|
buffer[3U] = 'D';
|
|
buffer[4U] = 'X';
|
|
buffer[5U] = '0';
|
|
buffer[6U] = '0';
|
|
buffer[7U] = '0';
|
|
buffer[8U] = '2';
|
|
|
|
::memset(buffer + 9U, ' ', RADIO_HEADER_LENGTH_BYTES);
|
|
|
|
buffer[9U] = header.getFlag1();
|
|
buffer[10U] = header.getFlag2();
|
|
buffer[11U] = header.getFlag3();
|
|
|
|
std::string rpt2 = header.getRptCall2();
|
|
for (unsigned int i = 0U; i < rpt2.size() && i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[i + 12U] = rpt2[i];
|
|
|
|
std::string rpt1 = header.getRptCall1();
|
|
for (unsigned int i = 0U; i < rpt1.size() && i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[i + 20U] = rpt1[i];
|
|
|
|
std::string your = header.getYourCall();
|
|
for (unsigned int i = 0U; i < your.size() && i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[i + 28U] = your[i];
|
|
|
|
std::string my1 = header.getMyCall1();
|
|
for (unsigned int i = 0U; i < my1.size() && i < LONG_CALLSIGN_LENGTH; i++)
|
|
buffer[i + 36U] = my1[i];
|
|
|
|
std::string my2 = header.getMyCall2();
|
|
for (unsigned int i = 0U; i < my2.size() && i < SHORT_CALLSIGN_LENGTH; i++)
|
|
buffer[i + 44U] = my2[i];
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
unsigned char len = 105U;
|
|
m_txData.addData(&len, 1U);
|
|
|
|
m_txData.addData(buffer, 105U);
|
|
|
|
m_tx = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::writeData(const unsigned char* data, unsigned int, bool end)
|
|
{
|
|
bool ret = m_txData.hasSpace(18U);
|
|
if (!ret) {
|
|
wxLogWarning("No space to write data");
|
|
return false;
|
|
}
|
|
|
|
unsigned char buffer[17U];
|
|
|
|
::memset(buffer, 0x00U, 17U);
|
|
|
|
buffer[0U] = 'H';
|
|
buffer[1U] = 'E';
|
|
buffer[2U] = 'A';
|
|
buffer[3U] = 'D';
|
|
buffer[4U] = 'Z';
|
|
|
|
::memcpy(buffer + 5U, data, DV_FRAME_LENGTH_BYTES);
|
|
|
|
if (end) {
|
|
buffer[14U] = 0x55U;
|
|
buffer[15U] = 0x55U;
|
|
buffer[16U] = 0x55U;
|
|
|
|
m_tx = false;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
unsigned char len = 17U;
|
|
m_txData.addData(&len, 1U);
|
|
|
|
m_txData.addData(buffer, 17U);
|
|
|
|
return true;
|
|
}
|
|
|
|
unsigned int CDVRPTRV3Controller::getSpace()
|
|
{
|
|
return m_txData.freeSpace() / 18U;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::isTXReady()
|
|
{
|
|
return m_txData.isEmpty();
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::readSerial()
|
|
{
|
|
unsigned char buffer[105U];
|
|
|
|
::memset(buffer, 0x00U, 105U);
|
|
|
|
buffer[0U] = 'H';
|
|
buffer[1U] = 'E';
|
|
buffer[2U] = 'A';
|
|
buffer[3U] = 'D';
|
|
buffer[4U] = 'X';
|
|
buffer[5U] = '9';
|
|
buffer[6U] = '0';
|
|
buffer[7U] = '0';
|
|
buffer[8U] = '0';
|
|
|
|
// CUtils::dump("Written", buffer, 105U);
|
|
|
|
bool ret = writeModem(buffer, 105U);
|
|
if (!ret)
|
|
return false;
|
|
|
|
unsigned int count = 0U;
|
|
unsigned int length;
|
|
RESP_TYPE_V3 resp;
|
|
do {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
resp = getResponse(m_buffer, length);
|
|
|
|
if (resp != RT3_QUERY) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
wxLogError("The DV-RPTR modem is not responding to the query command");
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp != RT3_QUERY);
|
|
|
|
wxLogInfo("DV-RPTR Modem Hardware serial: 0x%02X%02X%02x%02X", m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U]);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::readSpace()
|
|
{
|
|
unsigned char buffer[10U];
|
|
|
|
buffer[0U] = 'H';
|
|
buffer[1U] = 'E';
|
|
buffer[2U] = 'A';
|
|
buffer[3U] = 'D';
|
|
buffer[4U] = 'Y';
|
|
buffer[5U] = '9';
|
|
buffer[6U] = '0';
|
|
buffer[7U] = '1';
|
|
buffer[8U] = '1';
|
|
buffer[9U] = 0x00U;
|
|
|
|
// CUtils::dump("Written", buffer, 10U);
|
|
|
|
return writeModem(buffer, 10U);
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::setConfig()
|
|
{
|
|
unsigned char buffer[105U];
|
|
|
|
::memset(buffer, 0x00U, 105U);
|
|
|
|
buffer[0U] = 'H';
|
|
buffer[1U] = 'E';
|
|
buffer[2U] = 'A';
|
|
buffer[3U] = 'D';
|
|
buffer[4U] = 'X';
|
|
buffer[5U] = '9';
|
|
buffer[6U] = '0';
|
|
buffer[7U] = '0';
|
|
buffer[8U] = '1';
|
|
|
|
::memset(buffer + 9U, ' ', LONG_CALLSIGN_LENGTH);
|
|
for (unsigned int i = 0U; i < LONG_CALLSIGN_LENGTH && i < m_callsign.size(); i++)
|
|
buffer[9U + i] = m_callsign[i];
|
|
|
|
buffer[65U] = m_duplex ? 0x03U : 0x00U;
|
|
|
|
buffer[66U] = m_txInvert ? 0x01U : 0x00U;
|
|
|
|
buffer[73U] = (m_modLevel * 256U) / 100U;
|
|
|
|
// Internal and external speaker on
|
|
buffer[86U] = 0x03U;
|
|
|
|
buffer[87U] = 0x01U;
|
|
|
|
if (m_txDelay < 100U)
|
|
m_txDelay = 100U;
|
|
if (m_txDelay > 850U)
|
|
m_txDelay = 850U;
|
|
|
|
buffer[89U] = m_txDelay / 10U;
|
|
|
|
// CUtils::dump("Written", buffer, 105U);
|
|
|
|
bool ret = writeModem(buffer, 105U);
|
|
if (!ret)
|
|
return false;
|
|
|
|
unsigned int count = 0U;
|
|
unsigned int length;
|
|
RESP_TYPE_V3 resp;
|
|
do {
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
resp = getResponse(m_buffer, length);
|
|
|
|
if (resp != RT3_CONFIG) {
|
|
count++;
|
|
if (count >= MAX_RESPONSES) {
|
|
wxLogError("The DV-RPTR modem is not responding to the configure command");
|
|
return false;
|
|
}
|
|
}
|
|
} while (resp != RT3_CONFIG);
|
|
|
|
// CUtils::dump("Response", m_buffer, length);
|
|
|
|
std::string firmware((char*)(m_buffer + 9U));
|
|
|
|
wxLogInfo("DV-RPTR Modem Firmware version: %s", firmware.c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
// Reads one "HEAD" protocol frame. See CDVRPTRV2Controller::getResponse()
|
|
// for full protocol documentation; V3 uses an identical frame structure.
|
|
RESP_TYPE_V3 CDVRPTRV3Controller::getResponse(unsigned char *buffer, unsigned int& length)
|
|
{
|
|
// Get the start of the frame or nothing at all
|
|
int ret = readModem(buffer + 0U, 5U);
|
|
if (ret < 0) {
|
|
wxLogError("Error when reading from the DV-RPTR");
|
|
return RT3_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RT3_TIMEOUT;
|
|
|
|
while (::memcmp(buffer + 0U, "HEAD", 4U) != 0) {
|
|
buffer[0U] = buffer[1U];
|
|
buffer[1U] = buffer[2U];
|
|
buffer[2U] = buffer[3U];
|
|
buffer[3U] = buffer[4U];
|
|
|
|
ret = readModem(buffer + 4U, 1U);
|
|
if (ret < 0) {
|
|
wxLogError("Error when reading from the DV-RPTR");
|
|
return RT3_ERROR;
|
|
}
|
|
|
|
if (ret == 0)
|
|
return RT3_TIMEOUT;
|
|
}
|
|
|
|
switch (buffer[4U]) {
|
|
case 'X':
|
|
length = 105U;
|
|
break;
|
|
case 'Y':
|
|
length = 10U;
|
|
break;
|
|
case 'Z':
|
|
length = 20U;
|
|
break;
|
|
default:
|
|
wxLogError("DV-RPTR frame type is incorrect - 0x%02X", buffer[4U]);
|
|
return RT3_UNKNOWN;
|
|
}
|
|
|
|
unsigned int offset = 5U;
|
|
|
|
while (offset < length) {
|
|
ret = readModem(buffer + offset, length - offset);
|
|
if (ret < 0) {
|
|
wxLogError("Error when reading from the DV-RPTR");
|
|
return RT3_ERROR;
|
|
}
|
|
|
|
if (ret > 0)
|
|
offset += ret;
|
|
|
|
if (ret == 0)
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
}
|
|
|
|
// CUtils::dump("Received", buffer, length);
|
|
|
|
if (::memcmp(buffer + 0U, "HEADZ", 5U) == 0) {
|
|
return RT3_DATA;
|
|
} else if (::memcmp(buffer + 5U, "0001", 4U) == 0) {
|
|
if (buffer[104U] == 0x01U)
|
|
return RT3_HEADER;
|
|
} else if (::memcmp(buffer + 5U, "9900", 4U) == 0) {
|
|
return RT3_QUERY;
|
|
} else if (::memcmp(buffer + 5U, "9001", 4U) == 0) {
|
|
return RT3_CONFIG;
|
|
} else if (::memcmp(buffer + 5U, "9011", 4U) == 0) {
|
|
return RT3_SPACE;
|
|
} else if (::memcmp(buffer + 5U, "9021", 4U) == 0) {
|
|
return RT3_TIMEOUT;
|
|
}
|
|
|
|
return RT3_UNKNOWN;
|
|
}
|
|
|
|
std::string CDVRPTRV3Controller::getPath() const
|
|
{
|
|
return m_usbPath;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::findPort()
|
|
{
|
|
#if !defined(_WIN32)
|
|
if (m_connection != CT_USB)
|
|
return true;
|
|
|
|
if (m_usbPath.empty())
|
|
return false;
|
|
|
|
DIR* dir = ::opendir("/sys/class/tty");
|
|
if (dir == nullptr) {
|
|
wxLogError("Cannot open directory /sys/class/tty");
|
|
return false;
|
|
}
|
|
|
|
struct dirent* entry;
|
|
while ((entry = ::readdir(dir)) != nullptr) {
|
|
std::string fileName(entry->d_name);
|
|
|
|
// Match ttyACM* entries
|
|
if (fileName.substr(0, 6) != "ttyACM")
|
|
continue;
|
|
|
|
std::string path = "/sys/class/tty/" + fileName;
|
|
|
|
char cpath[255U];
|
|
::strncpy(cpath, path.c_str(), sizeof(cpath) - 1);
|
|
cpath[sizeof(cpath) - 1] = '\0';
|
|
|
|
char symlink[255U];
|
|
int ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1);
|
|
if (ret2 < 0) {
|
|
::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1);
|
|
ret2 = ::readlink(cpath, symlink, sizeof(symlink) - 1);
|
|
if (ret2 < 0) {
|
|
wxLogError("Error from readlink()");
|
|
::closedir(dir);
|
|
return false;
|
|
}
|
|
symlink[ret2] = '\0';
|
|
path = std::string(symlink, ret2);
|
|
} else {
|
|
symlink[ret2] = '\0';
|
|
std::string fullPath(symlink, ret2);
|
|
size_t pos = fullPath.rfind('/');
|
|
path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath;
|
|
}
|
|
|
|
if (path == m_usbPath) {
|
|
m_usbPort = "/dev/" + fileName;
|
|
|
|
wxLogMessage("Found modem port of %s based on the path", m_usbPort.c_str());
|
|
|
|
::closedir(dir);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
::closedir(dir);
|
|
return false;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::findPath()
|
|
{
|
|
#if !defined(_WIN32)
|
|
if (m_connection != CT_USB)
|
|
return true;
|
|
|
|
std::string path = "/sys/class/tty/" + m_usbPort.substr(5U);
|
|
|
|
char cpath[255U];
|
|
::strncpy(cpath, path.c_str(), sizeof(cpath) - 1);
|
|
cpath[sizeof(cpath) - 1] = '\0';
|
|
|
|
char symlink[255U];
|
|
int ret = ::readlink(cpath, symlink, sizeof(symlink) - 1);
|
|
if (ret < 0) {
|
|
::strncat(cpath, "/device", sizeof(cpath) - ::strlen(cpath) - 1);
|
|
ret = ::readlink(cpath, symlink, sizeof(symlink) - 1);
|
|
if (ret < 0) {
|
|
wxLogError("Error from readlink()");
|
|
return false;
|
|
}
|
|
symlink[ret] = '\0';
|
|
path = std::string(symlink, ret);
|
|
} else {
|
|
symlink[ret] = '\0';
|
|
std::string fullPath(symlink, ret);
|
|
size_t pos = fullPath.rfind('/');
|
|
path = (pos != std::string::npos) ? fullPath.substr(0, pos) : fullPath;
|
|
}
|
|
|
|
if (m_usbPath.empty())
|
|
wxLogMessage("Found modem path of %s", path.c_str());
|
|
|
|
m_usbPath = path;
|
|
|
|
return true;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::findModem()
|
|
{
|
|
closeModem();
|
|
|
|
// Tell the repeater that the signal has gone away
|
|
if (m_rx) {
|
|
std::lock_guard<std::mutex> lock(m_mutex);
|
|
|
|
unsigned char data[2U];
|
|
data[0U] = DSMTT_EOT;
|
|
data[1U] = 0U;
|
|
m_rxData.addData(data, 2U);
|
|
|
|
m_rx = false;
|
|
}
|
|
|
|
unsigned int count = 0U;
|
|
|
|
// Purge the transmit buffer every 500ms to avoid overflow, but only try and reopen the modem every 2s
|
|
while (!m_stopped) {
|
|
count++;
|
|
if (count >= 4U) {
|
|
wxLogMessage("Trying to reopen the modem");
|
|
|
|
bool ret = findPort();
|
|
if (ret) {
|
|
ret = openModem();
|
|
if (ret)
|
|
return true;
|
|
}
|
|
|
|
count = 0U;
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::openModem()
|
|
{
|
|
bool ret = false;
|
|
|
|
switch (m_connection) {
|
|
case CT_USB:
|
|
ret = m_usb->open();
|
|
break;
|
|
case CT_NETWORK:
|
|
ret = m_network->open();
|
|
break;
|
|
default:
|
|
wxLogError("Invalid connection type: %d", int(m_connection));
|
|
break;
|
|
}
|
|
|
|
if (!ret)
|
|
return false;
|
|
|
|
ret = readSerial();
|
|
if (!ret) {
|
|
closeModem();
|
|
return false;
|
|
}
|
|
|
|
ret = setConfig();
|
|
if (!ret) {
|
|
closeModem();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int CDVRPTRV3Controller::readModem(unsigned char* buffer, unsigned int length)
|
|
{
|
|
switch (m_connection) {
|
|
case CT_USB:
|
|
return m_usb->read(buffer, length);
|
|
case CT_NETWORK:
|
|
return m_network->read(buffer, length, 0U);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool CDVRPTRV3Controller::writeModem(const unsigned char* buffer, unsigned int length)
|
|
{
|
|
switch (m_connection) {
|
|
case CT_USB:
|
|
return m_usb->write(buffer, length) == int(length);
|
|
case CT_NETWORK:
|
|
return m_network->write(buffer, length);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CDVRPTRV3Controller::closeModem()
|
|
{
|
|
switch (m_connection) {
|
|
case CT_USB:
|
|
return m_usb->close();
|
|
case CT_NETWORK:
|
|
return m_network->close();
|
|
default:
|
|
return;
|
|
}
|
|
}
|