kopia lustrzana https://github.com/jamescoxon/dl-fldigi
778 wiersze
19 KiB
C++
778 wiersze
19 KiB
C++
// ----------------------------------------------------------------------------
|
|
// arq_io.cxx
|
|
//
|
|
// support for ARQ server/client system such as pskmail and fl_arq
|
|
//
|
|
// Copyright (C) 2006-2010
|
|
// Dave Freese, W1HKJ
|
|
// Copyright (C) 2008-2010
|
|
// Stelios Bounanos, M0GLD
|
|
// Copyright (C) 2009-2010
|
|
// John Douyere, VK2ETA
|
|
//
|
|
// This file is part of fldigi.
|
|
//
|
|
// Fldigi 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.
|
|
//
|
|
// Fldigi 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 fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef __MINGW32__
|
|
# include "compat.h"
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
# include <sys/ipc.h>
|
|
# include <sys/msg.h>
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
#include "main.h"
|
|
#include "configuration.h"
|
|
#include "fl_digi.h"
|
|
#include "trx.h"
|
|
#include "arq_io.h"
|
|
|
|
#include "threads.h"
|
|
#include "socket.h"
|
|
#include "debug.h"
|
|
#include "qrunner.h"
|
|
|
|
#include <FL/Fl.H>
|
|
#include <FL/fl_ask.H>
|
|
|
|
LOG_FILE_SOURCE(debug::LOG_ARQCONTROL);
|
|
|
|
using namespace std;
|
|
|
|
//======================================================================
|
|
// test code for pskmail eol issues
|
|
|
|
const char *asc[256] = {
|
|
"<NUL>", "<SOH>", "<STX>", "<ETX>",
|
|
"<EOT>", "<ENQ>", "<ACK>", "<BEL>",
|
|
"<BS>", "<TAB>", "<LF>", "<VT>",
|
|
"<FF>", "<CR>", "<SO>", "<SI>",
|
|
"<DLE>", "<DC1>", "<DC2>", "<DC3>",
|
|
"<DC4>", "<NAK>", "<SYN>", "<ETB>",
|
|
"<CAN>", "<EM>", "<SUB>", "<ESC>",
|
|
"<FS>", "<GS>", "<RS>", "<US>",
|
|
" ", "!", "\"", "#",
|
|
"$", "%", "&", "\'",
|
|
"(", ")", "*", "+",
|
|
",", "-", ".", "/",
|
|
"0", "1", "2", "3",
|
|
"4", "5", "6", "7",
|
|
"8", "9", ":", ";",
|
|
"<", "=", ">", "?",
|
|
"@", "A", "B", "C",
|
|
"D", "E", "F", "G",
|
|
"H", "I", "J", "K",
|
|
"L", "M", "N", "O",
|
|
"P", "Q", "R", "S",
|
|
"T", "U", "V", "W",
|
|
"X", "Y", "Z", "[",
|
|
"\\", "]", "^", "_",
|
|
"`", "a", "b", "c",
|
|
"d", "e", "f", "g",
|
|
"h", "i", "j", "k",
|
|
"l", "m", "n", "o",
|
|
"p", "q", "r", "s",
|
|
"t", "u", "v", "w",
|
|
"x", "y", "z", "{",
|
|
"|", "}", "~", "<DEL>"
|
|
};
|
|
|
|
string noctrl(string src)
|
|
{
|
|
static string retstr;
|
|
retstr.clear();
|
|
for (size_t i = 0; i < src.length(); i++) retstr.append(asc[(int)src[i]]);
|
|
return retstr;
|
|
}
|
|
|
|
//======================================================================
|
|
|
|
static string arqtext;
|
|
//string::iterator pText;
|
|
size_t pText;
|
|
|
|
bool arq_text_available = false;
|
|
string txstring;
|
|
|
|
extern void send0x06();
|
|
extern void parse_arqtext(string &toparse);
|
|
|
|
static void set_button(Fl_Button* button, bool value)
|
|
{
|
|
button->value(value);
|
|
button->do_callback();
|
|
}
|
|
|
|
void ParseMode(string src)
|
|
{
|
|
if (src.find("PTTTUNE") != string::npos) {
|
|
int msecs = 100;
|
|
if (src.length() > 7)
|
|
sscanf( src.substr(7, src.length() - 7).c_str(), "%d", &msecs);
|
|
REQ_SYNC(&PTT::set, push2talk, true);
|
|
MilliSleep(msecs);
|
|
REQ_SYNC(&PTT::set, push2talk, false);
|
|
LOG_VERBOSE("%s", "ARQ ptt toggled");
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < NUM_MODES; ++i) {
|
|
if (strlen(mode_info[i].pskmail_name) > 0) {
|
|
if (src == mode_info[i].pskmail_name) {
|
|
while (trx_state != STATE_RX) MilliSleep(50);
|
|
REQ_SYNC(init_modem_sync, mode_info[i].mode, 0);
|
|
LOG_INFO("ARQ new modem set to %s", mode_info[i].pskmail_name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParseRSID(string src)
|
|
{
|
|
if (src == "ON") {
|
|
LOG_VERBOSE("%s", "RsID turned ON");
|
|
REQ(set_button, btnRSID, 1);
|
|
}
|
|
if (src == "OFF") {
|
|
LOG_VERBOSE("%s", "RsID turned OFF");
|
|
REQ(set_button, btnRSID, 0);
|
|
}
|
|
}
|
|
|
|
|
|
void ParseTxRSID(string src)
|
|
{
|
|
if (src == "ON") {
|
|
LOG_VERBOSE("%s", "TxRsID turned ON");
|
|
REQ(set_button, btnTxRSID, 1);
|
|
}
|
|
if (src == "OFF") {
|
|
LOG_VERBOSE("%s", "TxRsID turned OFF");
|
|
REQ(set_button, btnTxRSID, 0);
|
|
}
|
|
}
|
|
|
|
void parse_arqtext(string &toparse)
|
|
{
|
|
string strCmdText;
|
|
string strSubCmd;
|
|
unsigned long int idxCmd, idxCmdEnd, idxSubCmd, idxSubCmdEnd;
|
|
|
|
LOG_DEBUG("Arq text: %s", noctrl(toparse).c_str());
|
|
|
|
idxCmd = toparse.find("<cmd>");
|
|
idxCmdEnd = toparse.find("</cmd>");
|
|
|
|
while ( idxCmd != string::npos && idxCmdEnd != string::npos && idxCmdEnd > idxCmd ) {
|
|
|
|
LOG_DEBUG("Cmd text: %s", toparse.substr(idxCmd, idxCmdEnd + 6).c_str());
|
|
strCmdText = toparse.substr(idxCmd + 5, idxCmdEnd - idxCmd - 5);
|
|
if (strCmdText == "server" && mailserver == false && mailclient == false) {
|
|
mailserver = true;
|
|
mailclient = false;
|
|
string PskMailLogName = PskMailDir;
|
|
PskMailLogName += "gMFSK.log";
|
|
Maillogfile = new cLogfile(PskMailLogName.c_str());
|
|
Maillogfile->log_to_file_start();
|
|
REQ(set_button, wf->xmtlock, 1);
|
|
if (progdefaults.PSKmailSweetSpot)
|
|
active_modem->set_freq(progdefaults.PSKsweetspot);
|
|
active_modem->set_freqlock(true);
|
|
LOG_DEBUG("%s", "ARQ is set to pskmail server");
|
|
} else if (strCmdText == "client" && mailclient == false && mailserver == false) {
|
|
mailclient = true;
|
|
mailserver = false;
|
|
string PskMailLogName = PskMailDir;
|
|
PskMailLogName += "gMFSK.log";
|
|
Maillogfile = new cLogfile(PskMailLogName.c_str());
|
|
Maillogfile->log_to_file_start();
|
|
REQ(set_button, wf->xmtlock, 0);
|
|
active_modem->set_freqlock(false);
|
|
LOG_DEBUG("%s", "ARQ is set to pskmail client");
|
|
} else if (strCmdText == "normal") {
|
|
mailserver = false;
|
|
mailclient = false;
|
|
if (Maillogfile) {
|
|
delete Maillogfile;
|
|
Maillogfile = 0;
|
|
}
|
|
REQ(set_button, wf->xmtlock, 0);
|
|
active_modem->set_freqlock(false);
|
|
LOG_DEBUG("%s", "ARQ is reset to normal ops");
|
|
} else if ((idxSubCmd = strCmdText.find("<mode>")) != string::npos) {
|
|
idxSubCmdEnd = strCmdText.find("</mode>");
|
|
if ( idxSubCmdEnd != string::npos &&
|
|
idxSubCmdEnd > idxSubCmd ) {
|
|
strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
|
|
ParseMode(strSubCmd);
|
|
}
|
|
} else if ((idxSubCmd = strCmdText.find("<rsid>")) != string::npos) {
|
|
idxSubCmdEnd = strCmdText.find("</rsid>");
|
|
if ( idxSubCmdEnd != string::npos &&
|
|
idxSubCmdEnd > idxSubCmd ) {
|
|
strSubCmd = strCmdText.substr(idxSubCmd + 6, idxSubCmdEnd - idxSubCmd - 6);
|
|
ParseRSID(strSubCmd);
|
|
}
|
|
} else if ((idxSubCmd = strCmdText.find("<txrsid>")) != string::npos) {
|
|
idxSubCmdEnd = strCmdText.find("</txrsid>");
|
|
if ( idxSubCmdEnd != string::npos &&
|
|
idxSubCmdEnd > idxSubCmd ) {
|
|
strSubCmd = strCmdText.substr(idxSubCmd + 8, idxSubCmdEnd - idxSubCmd - 8);
|
|
ParseTxRSID(strSubCmd);
|
|
}
|
|
}
|
|
|
|
toparse.erase(idxCmd, idxCmdEnd - idxCmd + 6);
|
|
while (toparse[0] == '\n' || toparse[0] == '\r') toparse.erase(0, 1);
|
|
|
|
idxCmd = toparse.find("<cmd>");
|
|
idxCmdEnd = toparse.find("</cmd>");
|
|
}
|
|
}
|
|
|
|
#define TIMEOUT 180 // 3 minutes
|
|
|
|
bool bSend0x06 = false;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// SysV ARQ used only on Linux / Free-BSD or Unix type OS
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
void process_msgque()
|
|
{
|
|
memset(txmsgst.buffer, 0, ARQBUFSIZ);
|
|
int nbytes = msgrcv (txmsgid, (void *)&txmsgst, ARQBUFSIZ, 0, IPC_NOWAIT);
|
|
if (nbytes > 0) {
|
|
txstring.append(txmsgst.buffer);
|
|
parse_arqtext(txstring);
|
|
|
|
if (!bSend0x06 && arqtext.empty() && !txstring.empty()) {
|
|
arqtext = txstring;
|
|
if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
active_modem->set_freq(progdefaults.PSKsweetspot);
|
|
pText = 0;
|
|
arq_text_available = true;
|
|
active_modem->set_stopflag(false);
|
|
LOG_DEBUG("SYSV ARQ string: %s", arqtext.c_str());
|
|
start_tx();
|
|
txstring.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SysV_arqRx()
|
|
{
|
|
txmsgid = msgget( (key_t) progdefaults.tx_msgid, 0666 );
|
|
if (txmsgid != -1) {
|
|
process_msgque();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gmfsk ARQ file i/o used only on Linux
|
|
//-----------------------------------------------------------------------------
|
|
// checkTLF
|
|
// look for files named
|
|
// TLFfldigi ==> tlfio is true and
|
|
// ==> mailclient is true
|
|
// in $HOME
|
|
|
|
void checkTLF() {
|
|
string TLFfile;
|
|
string TLFlogname;
|
|
ifstream testFile;
|
|
|
|
tlfio = mailserver = mailclient = false;
|
|
|
|
TLFfile = PskMailDir;
|
|
TLFfile += "TLFfldigi";
|
|
|
|
testFile.open(TLFfile.c_str());
|
|
if (testFile.is_open()) {
|
|
testFile.close();
|
|
mailclient = true;
|
|
tlfio = true;
|
|
TLFlogname = PskMailDir;
|
|
TLFlogname += "gMFSK.log";
|
|
Maillogfile = new cLogfile(TLFlogname.c_str());
|
|
Maillogfile->log_to_file_start();
|
|
}
|
|
}
|
|
|
|
bool TLF_arqRx()
|
|
{
|
|
time_t start_time, prog_time;
|
|
static char mailline[1000];
|
|
string sAutoFile = PskMailDir;
|
|
sAutoFile += "gmfsk_autofile";
|
|
|
|
ifstream autofile(sAutoFile.c_str());
|
|
if(autofile) {
|
|
arqtext = "";
|
|
time(&start_time);
|
|
while (!autofile.eof()) {
|
|
memset(mailline, 0, sizeof(mailline));
|
|
autofile.getline(mailline, 998); // leave space for "\n" and null byte
|
|
txstring.append(mailline);
|
|
txstring.append("\n");
|
|
time(&prog_time);
|
|
if (prog_time - start_time > TIMEOUT) {
|
|
LOG_ERROR("TLF file I/O failure");
|
|
autofile.close();
|
|
std::remove (sAutoFile.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
autofile.close();
|
|
std::remove (sAutoFile.c_str());
|
|
|
|
parse_arqtext(txstring);
|
|
if (arqtext.empty() && !txstring.empty()) {
|
|
arqtext = txstring;
|
|
if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
active_modem->set_freq(progdefaults.PSKsweetspot);
|
|
pText = 0;
|
|
arq_text_available = true;
|
|
active_modem->set_stopflag(false);
|
|
LOG_DEBUG("%s", arqtext.c_str());
|
|
start_tx();
|
|
txstring.clear();
|
|
}
|
|
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Auto transmit of file contained in WRAP_auto_dir
|
|
//-----------------------------------------------------------------------------
|
|
bool WRAP_auto_arqRx()
|
|
{
|
|
time_t start_time, prog_time;
|
|
static char mailline[1000];
|
|
string sAutoFile = FLMSG_WRAP_auto_dir;
|
|
sAutoFile += "wrap_auto_file";
|
|
|
|
ifstream autofile(sAutoFile.c_str());
|
|
if(autofile) {
|
|
txstring.clear();
|
|
time(&start_time);
|
|
while (!autofile.eof()) {
|
|
memset(mailline,0,1000);
|
|
autofile.getline(mailline, 998); // leave space for "\n" and null byte
|
|
txstring.append(mailline);
|
|
txstring.append("\n");
|
|
time(&prog_time);
|
|
if (prog_time - start_time > TIMEOUT) {
|
|
LOG_ERROR("autowrap file I/O failure");
|
|
autofile.close();
|
|
std::remove (sAutoFile.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
autofile.close();
|
|
std::remove (sAutoFile.c_str());
|
|
|
|
if (!txstring.empty()) {
|
|
arqtext = "\n....start\n";
|
|
arqtext.append(txstring);
|
|
arqtext.append("\n......end\n");
|
|
pText = 0;
|
|
arq_text_available = true;
|
|
LOG_DEBUG("%s", arqtext.c_str());
|
|
start_tx();
|
|
txstring.clear();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Socket ARQ i/o used on all platforms
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern void arq_run(Socket s);
|
|
extern void arq_stop();
|
|
|
|
string errstring;
|
|
string cmdstring;
|
|
string response;
|
|
bool isTxChar = false;
|
|
bool isCmdChar = false;
|
|
|
|
bool isNotMULTIPSK = true;
|
|
|
|
static pthread_t* arq_socket_thread = 0;
|
|
ARQ_SOCKET_Server* ARQ_SOCKET_Server::inst = 0;
|
|
|
|
Socket arqclient;
|
|
bool isSocketConnected = false;
|
|
|
|
ARQ_SOCKET_Server::ARQ_SOCKET_Server()
|
|
{
|
|
server_socket = new Socket;
|
|
arq_socket_thread = new pthread_t;
|
|
run = true;
|
|
}
|
|
|
|
ARQ_SOCKET_Server::~ARQ_SOCKET_Server()
|
|
{
|
|
run = false;
|
|
if (arq_socket_thread) {
|
|
CANCEL_THREAD(*arq_socket_thread);
|
|
pthread_join(*arq_socket_thread, NULL);
|
|
delete arq_socket_thread;
|
|
arq_socket_thread = 0;
|
|
}
|
|
delete server_socket;
|
|
}
|
|
|
|
bool ARQ_SOCKET_Server::start(const char* node, const char* service)
|
|
{
|
|
if (inst)
|
|
return false;
|
|
|
|
inst = new ARQ_SOCKET_Server;
|
|
|
|
try {
|
|
inst->server_socket->open(Address(node, service));
|
|
inst->server_socket->bind();
|
|
#ifdef __WOE32__
|
|
inst->server_socket->listen();
|
|
inst->server_socket->set_timeout(0.1);
|
|
#endif
|
|
}
|
|
catch (const SocketException& e) {
|
|
errstring = "Could not start ARQ server (";
|
|
errstring.append(e.what()).append(")");
|
|
if (e.error() == EADDRINUSE)
|
|
errstring.append("\nMultiple instances of fldigi??");
|
|
LOG_ERROR("%s", errstring.c_str());
|
|
|
|
delete arq_socket_thread;
|
|
arq_socket_thread = 0;
|
|
delete inst;
|
|
inst = 0;
|
|
return false;
|
|
}
|
|
|
|
return !pthread_create(arq_socket_thread, NULL, thread_func, NULL);
|
|
}
|
|
|
|
void ARQ_SOCKET_Server::stop(void)
|
|
{
|
|
// FILEME - uncomment when we have an ARQ_SOCKET_Server than can be
|
|
// interrupted
|
|
// if (!inst)
|
|
// return;
|
|
// delete inst;
|
|
// inst = 0;
|
|
}
|
|
|
|
void* ARQ_SOCKET_Server::thread_func(void*)
|
|
{
|
|
SET_THREAD_ID(ARQSOCKET_TID);
|
|
|
|
SET_THREAD_CANCEL();
|
|
|
|
// On POSIX we block indefinitely and are interrupted by a signal.
|
|
// On woe32 we block for a short time and test for cancellation.
|
|
while (inst->run) {
|
|
try {
|
|
#ifdef __WOE32__
|
|
if (inst->server_socket->wait(0))
|
|
#endif
|
|
arq_run(inst->server_socket->accept());
|
|
TEST_THREAD_CANCEL();
|
|
}
|
|
catch (const SocketException& e) {
|
|
if (e.error() != EINTR) {
|
|
errstring = e.what();
|
|
LOG_ERROR("%s", errstring.c_str());
|
|
break;
|
|
}
|
|
}
|
|
catch (...) {
|
|
break;
|
|
}
|
|
}
|
|
arq_stop();
|
|
inst->server_socket->close();
|
|
return NULL;
|
|
}
|
|
|
|
void arq_run(Socket s)
|
|
{
|
|
struct timeval t = { 0, 20000 };
|
|
arqclient = s;
|
|
arqclient.set_timeout(t);
|
|
arqclient.set_nonblocking();
|
|
isSocketConnected = true;
|
|
arqmode = true;
|
|
}
|
|
|
|
void arq_stop()
|
|
{
|
|
arqclient.close();
|
|
isSocketConnected = false;
|
|
arqmode = false;
|
|
}
|
|
|
|
void WriteARQsocket(unsigned char* data, size_t len)
|
|
{
|
|
if (!isSocketConnected) return;
|
|
try {
|
|
arqclient.send(data, len);
|
|
}
|
|
catch (const SocketException& e) {
|
|
LOG_ERROR("%s", e.what());
|
|
arq_stop();
|
|
}
|
|
}
|
|
|
|
bool Socket_arqRx()
|
|
{
|
|
if (!isSocketConnected) return false;
|
|
|
|
string instr;
|
|
|
|
try {
|
|
size_t n = arqclient.recv(instr);
|
|
if ( n > 0)
|
|
txstring.append(instr);
|
|
|
|
if (!bSend0x06 && arqtext.empty() && !txstring.empty()) {
|
|
arqtext = txstring;
|
|
parse_arqtext(arqtext);
|
|
if (!arqtext.empty()) {
|
|
if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
active_modem->set_freq(progdefaults.PSKsweetspot);
|
|
pText = 0;//arqtext.begin();
|
|
arq_text_available = true;
|
|
active_modem->set_stopflag(false);
|
|
LOG_DEBUG("%s", arqtext.c_str());
|
|
start_tx();
|
|
}
|
|
txstring.clear();
|
|
cmdstring.clear();
|
|
return true;
|
|
}
|
|
cmdstring.clear();
|
|
return false;
|
|
}
|
|
catch (const SocketException& e) {
|
|
arq_stop();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Send ARQ characters to ARQ client
|
|
//-----------------------------------------------------------------------------
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
void WriteARQSysV(unsigned char data)
|
|
{
|
|
rxmsgid = msgget( (key_t) progdefaults.rx_msgid, 0666);
|
|
if ( rxmsgid != -1) {
|
|
rxmsgst.msg_type = 1;
|
|
rxmsgst.c = data;
|
|
msgsnd (rxmsgid, (void *)&rxmsgst, 1, IPC_NOWAIT);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void WriteARQ(unsigned char data)
|
|
{
|
|
WriteARQsocket(&data, 1);
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
WriteARQSysV(data);
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Write End of Transmit character to ARQ client
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void send0x06()
|
|
{
|
|
if (trx_state == STATE_RX) {
|
|
bSend0x06 = false;
|
|
WriteARQ(0x06);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Implementation using thread vice the fldigi timeout facility
|
|
// ============================================================================
|
|
static pthread_t arq_thread;
|
|
static pthread_mutex_t arq_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
static void *arq_loop(void *args);
|
|
|
|
static bool arq_exit = false;
|
|
static bool arq_enabled;
|
|
|
|
static void *arq_loop(void *args)
|
|
{
|
|
SET_THREAD_ID(ARQ_TID);
|
|
|
|
for (;;) {
|
|
/* see if we are being canceled */
|
|
if (arq_exit)
|
|
break;
|
|
|
|
pthread_mutex_lock (&arq_mutex);
|
|
|
|
if (bSend0x06)
|
|
send0x06();
|
|
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
// order of precedence; Socket, Wrap autofile, TLF autofile
|
|
if (!Socket_arqRx())
|
|
if (!SysV_arqRx())
|
|
if (!WRAP_auto_arqRx())
|
|
TLF_arqRx();
|
|
#else
|
|
if (!Socket_arqRx())
|
|
WRAP_auto_arqRx();
|
|
#endif
|
|
pthread_mutex_unlock (&arq_mutex);
|
|
MilliSleep(50);
|
|
|
|
}
|
|
// exit the arq thread
|
|
return NULL;
|
|
}
|
|
|
|
void arq_init()
|
|
{
|
|
arq_enabled = false;
|
|
|
|
txstring.clear();
|
|
cmdstring.clear();
|
|
|
|
if (!ARQ_SOCKET_Server::start( progdefaults.arq_address.c_str(), progdefaults.arq_port.c_str() ))
|
|
return;
|
|
|
|
if (pthread_create(&arq_thread, NULL, arq_loop, NULL) < 0) {
|
|
LOG_ERROR("arq init: pthread_create failed");
|
|
return;
|
|
}
|
|
|
|
arq_enabled = true;
|
|
}
|
|
|
|
void arq_close(void)
|
|
{
|
|
if (!arq_enabled) return;
|
|
|
|
ARQ_SOCKET_Server::stop();
|
|
|
|
// tell the arq thread to kill it self
|
|
arq_exit = true;
|
|
|
|
// and then wait for it to die
|
|
pthread_join(arq_thread, NULL);
|
|
arq_enabled = false;
|
|
|
|
arq_exit = false;
|
|
}
|
|
|
|
char arq_get_char()
|
|
{
|
|
char c = 0;
|
|
pthread_mutex_lock (&arq_mutex);
|
|
if (arq_text_available) {
|
|
if (pText != arqtext.length())
|
|
c = arqtext[pText++];
|
|
else {
|
|
arqtext.clear();
|
|
pText = 0;
|
|
bSend0x06 = true;
|
|
arq_text_available = false;
|
|
c = 0x03;
|
|
}
|
|
}
|
|
pthread_mutex_unlock (&arq_mutex);
|
|
return c;
|
|
}
|
|
|
|
// following function used if the T/R button is pressed to stop a transmission
|
|
// that is servicing the ARQ text buffer. It allows the ARQ client to reset
|
|
// itself properly
|
|
|
|
void AbortARQ() {
|
|
pthread_mutex_lock (&arq_mutex);
|
|
arqtext.clear();
|
|
pText = 0;
|
|
arq_text_available = false;
|
|
bSend0x06 = true;
|
|
pthread_mutex_unlock (&arq_mutex);
|
|
}
|
|
|
|
// Special notification for PSKMAIL: new mode marked only, in following
|
|
// format: "<DC2><Mode:newmode>", with <DC2> = '0x12'.
|
|
void pskmail_notify_rsid(trx_mode mode)
|
|
{
|
|
char buf[64];
|
|
int n = snprintf(buf, sizeof(buf), "%c<Mode:%s>\n", 0x12, mode_info[mode].name);
|
|
if (n > 0 && n < (int)sizeof(buf)) {
|
|
WriteARQsocket((unsigned char*)buf, n);
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
for (int ii=0; ii < n; ii++)
|
|
WriteARQSysV((unsigned char)buf[ii]);
|
|
#endif
|
|
ReceiveText->add(buf, FTextBase::CTRL);
|
|
}
|
|
}
|
|
|
|
// Special notification for PSKMAIL: signal to noise measured by decoder
|
|
// format "<DC2><s2n: CC, A.a, D.d>"
|
|
// where CC = count, A.a = average s/n, D.d = Std dev of s/n
|
|
void pskmail_notify_s2n(double s2n_ncount, double s2n_avg, double s2n_stddev)
|
|
{
|
|
char buf[64];
|
|
int n = snprintf(buf, sizeof(buf), "%c<s2n: %1.0f, %1.1f, %1.1f>",
|
|
0x12, s2n_ncount, s2n_avg, s2n_stddev);
|
|
if (n > 0 && n < (int)sizeof(buf)) {
|
|
WriteARQsocket((unsigned char*)buf, n);
|
|
#if !defined(__WOE32__) && !defined(__APPLE__)
|
|
for (int ii=0; ii < n; ii++)
|
|
WriteARQSysV((unsigned char)buf[ii]);
|
|
#endif
|
|
ReceiveText->add(buf, FTextBase::CTRL);
|
|
}
|
|
}
|
|
|