qtsoundmodem/tcpCode.cpp

1195 lines
25 KiB
C++
Raw Normal View History

2023-09-04 19:06:44 +01:00
/*
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
This file is part of QtSoundModem
QtSoundModem 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.
QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses
*/
2024-10-29 22:45:21 +00:00
2023-09-04 19:06:44 +01:00
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
#include <QMessageBox>
#include "QtSoundModem.h"
#include "UZ7HOStuff.h"
#include <QTimer>
#define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt))
2024-10-29 22:45:21 +00:00
QList<QTcpSocket*> _MgmtSockets;
2023-09-04 19:06:44 +01:00
QList<QTcpSocket*> _KISSSockets;
QList<QTcpSocket*> _AGWSockets;
QTcpServer * _KISSserver;
QTcpServer * _AGWserver;
2024-10-29 22:45:21 +00:00
QTcpServer * SixPackServer;
QTcpSocket *SixPackSocket;
QTcpServer * _MgmtServer;
TMgmtMode ** MgmtConnections = NULL;
int MgmtConCount = 0;
2023-09-04 19:06:44 +01:00
extern workerThread *t;
extern mynet m1;
2024-10-29 22:45:21 +00:00
extern serialThread *serial;
QString Response;
2023-09-04 19:06:44 +01:00
2024-10-29 22:45:21 +00:00
extern int MgmtPort;
2023-09-04 19:06:44 +01:00
extern "C" int KISSPort;
extern "C" void * initPulse();
extern "C" int SoundMode;
extern "C" int UDPClientPort;
extern "C" int UDPServerPort;
extern "C" int TXPort;
2024-10-29 22:45:21 +00:00
extern char SixPackDevice[256];
extern int SixPackPort;
extern int SixPackEnable;
2023-09-04 19:06:44 +01:00
char UDPHost[64] = "";
int UDPServ = 0; // UDP Server Active (ie broadcast sound frams as UDP packets)
QMutex mutex;
extern void saveSettings();
extern int Closing; // Set to stop background thread
extern "C"
{
void KISSDataReceived(void * sender, char * data, int length);
void AGW_explode_frame(void * soket, char * data, int len);
void KISS_add_stream(void * Socket);
void KISS_del_socket(void * Socket);
void AGW_add_socket(void * Socket);
void AGW_del_socket(void * socket);
void Debugprintf(const char * format, ...);
int InitSound(BOOL Report);
void soundMain();
void MainLoop();
void set_speed(int snd_ch, int Modem);
void init_speed(int snd_ch);
}
2024-10-29 22:45:21 +00:00
void Process6PackData(unsigned char * Bytes, int Len);
2023-09-04 19:06:44 +01:00
extern "C" int nonGUIMode;
QTimer *timer;
QTimer *timercopy;
void mynet::start()
{
if (SoundMode == 3)
OpenUDP();
if (UDPServ)
OpenUDP();
if (KISSServ)
{
_KISSserver = new(QTcpServer);
if (_KISSserver->listen(QHostAddress::Any, KISSPort))
connect(_KISSserver, SIGNAL(newConnection()), this, SLOT(onKISSConnection()));
else
{
if (nonGUIMode)
Debugprintf("Listen failed for KISS Port");
else
{
QMessageBox msgBox;
msgBox.setText("Listen failed for KISS Port.");
msgBox.exec();
}
}
}
if (AGWServ)
{
_AGWserver = new(QTcpServer);
if (_AGWserver->listen(QHostAddress::Any, AGWPort))
connect(_AGWserver, SIGNAL(newConnection()), this, SLOT(onAGWConnection()));
else
{
if (nonGUIMode)
Debugprintf("Listen failed for AGW Port");
else
{
QMessageBox msgBox;
msgBox.setText("Listen failed for AGW Port.");
msgBox.exec();
}
}
}
2024-10-29 22:45:21 +00:00
if (MgmtPort)
{
_MgmtServer = new(QTcpServer);
if (_MgmtServer->listen(QHostAddress::Any, MgmtPort))
connect(_MgmtServer, SIGNAL(newConnection()), this, SLOT(onMgmtConnection()));
else
{
if (nonGUIMode)
Debugprintf("Listen failed for Mgmt Port");
else
{
QMessageBox msgBox;
msgBox.setText("Listen failed for Mgmt Port.");
msgBox.exec();
}
}
}
2023-09-04 19:06:44 +01:00
QObject::connect(t, SIGNAL(sendtoKISS(void *, unsigned char *, int)), this, SLOT(sendtoKISS(void *, unsigned char *, int)), Qt::QueuedConnection);
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
timer->start(100);
2024-10-29 22:45:21 +00:00
if (SixPackEnable)
{
if (SixPackDevice[0] && strcmp(SixPackDevice, "None") != 0) // Using serial
{
serial->startSlave(SixPackDevice, 30000, Response);
serial->start();
// connect(serial, &serialThread::request, this, &QtSoundModem::showRequest);
// connect(serial, &serialThread::error, this, &QtSoundModem::processError);
// connect(serial, &serialThread::timeout, this, &QtSoundModem::processTimeout);
}
else if (SixPackPort) // using TCP
{
SixPackServer = new(QTcpServer);
if (SixPackServer->listen(QHostAddress::Any, SixPackPort))
connect(SixPackServer, SIGNAL(newConnection()), this, SLOT(on6PackConnection()));
}
}
2023-09-04 19:06:44 +01:00
}
void mynet::MyTimerSlot()
{
// 100 mS Timer Event
TimerEvent = TIMER_EVENT_ON;
}
void mynet::onAGWConnection()
{
QTcpSocket *clientSocket = _AGWserver->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onAGWReadyRead()));
connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onAGWSocketStateChanged(QAbstractSocket::SocketState)));
_AGWSockets.push_back(clientSocket);
AGW_add_socket(clientSocket);
Debugprintf("AGW Connect Sock %x", clientSocket);
}
void mynet::onAGWSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
AGW_del_socket(sender);
_AGWSockets.removeOne(sender);
}
}
void mynet::onAGWReadyRead()
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
QByteArray datas = sender->readAll();
AGW_explode_frame(sender, datas.data(), datas.length());
}
2024-10-29 22:45:21 +00:00
void mynet::on6PackReadyRead()
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
QByteArray datas = sender->readAll();
Process6PackData((unsigned char *)datas.data(), datas.length());
}
void mynet::on6PackSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
}
}
void mynet::on6PackConnection()
{
QTcpSocket *clientSocket = SixPackServer->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(on6PackReadyRead()));
connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on6PackSocketStateChanged(QAbstractSocket::SocketState)));
Debugprintf("6Pack Connect Sock %x", clientSocket);
}
2023-09-04 19:06:44 +01:00
void mynet::onKISSConnection()
{
QTcpSocket *clientSocket = _KISSserver->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onKISSReadyRead()));
connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onKISSSocketStateChanged(QAbstractSocket::SocketState)));
_KISSSockets.push_back(clientSocket);
KISS_add_stream(clientSocket);
Debugprintf("KISS Connect Sock %x", clientSocket);
}
2024-10-29 22:45:21 +00:00
void Mgmt_del_socket(void * socket)
{
int i;
TMgmtMode * MGMT = NULL;
if (MgmtConCount == 0)
return;
for (i = 0; i < MgmtConCount; i++)
{
if (MgmtConnections[i]->Socket == socket)
{
MGMT = MgmtConnections[i];
break;
}
}
if (MGMT == NULL)
return;
// Need to remove entry and move others down
MgmtConCount--;
while (i < MgmtConCount)
{
MgmtConnections[i] = MgmtConnections[i + 1];
i++;
}
}
void mynet::onMgmtConnection()
{
QTcpSocket *clientSocket = (QTcpSocket *)_MgmtServer->nextPendingConnection();
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onMgmtReadyRead()));
connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onMgmtSocketStateChanged(QAbstractSocket::SocketState)));
_MgmtSockets.append(clientSocket);
// Create a data structure to hold session info
TMgmtMode * MGMT;
MgmtConnections = (TMgmtMode **)realloc(MgmtConnections, (MgmtConCount + 1) * sizeof(void *));
MGMT = MgmtConnections[MgmtConCount++] = (TMgmtMode *)malloc(sizeof(*MGMT));
memset(MGMT, 0, sizeof(*MGMT));
MGMT->Socket = clientSocket;
Debugprintf("Mgmt Connect Sock %x", clientSocket);
clientSocket->write("Connected to QtSM\r");
}
void mynet::onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
Mgmt_del_socket(sender);
// free(sender->Msg);
_MgmtSockets.removeOne(sender);
Debugprintf("Mgmt Disconnect Sock %x", sender);
}
}
void mynet::onMgmtReadyRead()
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
MgmtProcessLine(sender);
}
extern "C" void SendMgmtPTT(int snd_ch, int PTTState)
{
// Won't work in non=gui mode
emit m1.mgmtSetPTT(snd_ch, PTTState);
}
extern "C" char * strlop(char * buf, char delim);
extern "C" char modes_name[modes_count][21];
extern "C" int speed[5];
#ifndef WIN32
extern "C" int memicmp(char *a, char *b, int n);
#endif
void mynet::MgmtProcessLine(QTcpSocket* socket)
{
// Find Session
TMgmtMode * MGMT = NULL;
for (int i = 0; i < MgmtConCount; i++)
{
if (MgmtConnections[i]->Socket == socket)
{
MGMT = MgmtConnections[i];
break;
}
}
if (MGMT == NULL)
{
socket->readAll();
return;
}
while (socket->bytesAvailable())
{
QByteArray datas = socket->peek(512);
char * Line = datas.data();
if (strchr(Line, '\r') == 0)
return;
char * rest = strlop(Line, '\r');
int used = rest - Line;
// Read the upto the cr Null
datas = socket->read(used);
Line = datas.data();
rest = strlop(Line, '\r');
if (memicmp(Line, "QtSMPort ", 8) == 0)
{
if (strlen(Line) > 10)
{
int port = atoi(&Line[8]);
int bpqport = atoi(&Line[10]);
if ((port > 0 && port < 5) && (bpqport > 0 && bpqport < 64))
{
MGMT->BPQPort[port - 1] = bpqport;
socket->write("Ok\r");
}
}
}
else if (memicmp(Line, "Modem ", 6) == 0)
{
int port = atoi(&Line[6]);
if (port > 0 && port < 5)
{
// if any more params - if a name follows, set it else return it
char reply[80];
sprintf(reply, "Port %d Chan %d Freq %d Modem %s \r", MGMT->BPQPort[port - 1], port, rx_freq[port - 1], modes_name[speed[port - 1]]);
socket->write(reply);
}
else
socket->write("Invalid Port\r");
}
else
{
socket->write("Invalid command\r");
}
}
}
2023-09-04 19:06:44 +01:00
void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
KISS_del_socket(sender);
_KISSSockets.removeOne(sender);
2024-10-29 22:45:21 +00:00
Debugprintf("KISS Disconnect Sock %x", sender);
2023-09-04 19:06:44 +01:00
}
}
void mynet::onKISSReadyRead()
{
QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
QByteArray datas = sender->readAll();
KISSDataReceived(sender, datas.data(), datas.length());
}
void mynet::displayError(QAbstractSocket::SocketError socketError)
{
if (socketError == QTcpSocket::RemoteHostClosedError)
return;
qDebug() << tcpClient->errorString();
tcpClient->close();
tcpServer->close();
}
void mynet::sendtoKISS(void * sock, unsigned char * Msg, int Len)
{
if (sock == NULL)
{
for (QTcpSocket* socket : _KISSSockets)
{
socket->write((char *)Msg, Len);
}
}
else
{
QTcpSocket* socket = (QTcpSocket*)sock;
socket->write((char *)Msg, Len);
}
2024-10-29 22:45:21 +00:00
free(Msg);
2023-09-04 19:06:44 +01:00
}
QTcpSocket * HAMLIBsock;
int HAMLIBConnected = 0;
int HAMLIBConnecting = 0;
2024-07-23 21:26:30 +01:00
QTcpSocket * FLRIGsock;
int FLRIGConnected = 0;
int FLRIGConnecting = 0;
2023-09-04 19:06:44 +01:00
void mynet::HAMLIBdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
if (nonGUIMode)
qDebug() << "HAMLIB host was not found. Please check the host name and port settings.";
else
{
QMessageBox::information(nullptr, tr("QtSM"),
tr("HAMLIB host was not found. Please check the "
"host name and port settings."));
}
break;
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "HAMLIB Connection Refused";
break;
default:
qDebug() << "HAMLIB Connection Failed";
break;
}
HAMLIBConnecting = 0;
HAMLIBConnected = 0;
}
void mynet::HAMLIBreadyRead()
{
unsigned char Buffer[4096];
QTcpSocket* Socket = static_cast<QTcpSocket*>(QObject::sender());
// read the data from the socket. Don't do anyhing with it at the moment
Socket->read((char *)Buffer, 4095);
}
void mynet::onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
HAMLIBConnected = 0;
qDebug() << "HAMLIB Connection Closed";
}
else if (socketState == QAbstractSocket::ConnectedState)
{
HAMLIBConnected = 1;
HAMLIBConnecting = 0;
qDebug() << "HAMLIB Connected";
}
}
void mynet::ConnecttoHAMLIB()
{
delete(HAMLIBsock);
HAMLIBConnected = 0;
HAMLIBConnecting = 1;
HAMLIBsock = new QTcpSocket();
connect(HAMLIBsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(HAMLIBdisplayError(QAbstractSocket::SocketError)));
connect(HAMLIBsock, SIGNAL(readyRead()), this, SLOT(HAMLIBreadyRead()));
connect(HAMLIBsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onHAMLIBSocketStateChanged(QAbstractSocket::SocketState)));
HAMLIBsock->connectToHost(HamLibHost, HamLibPort);
return;
}
extern "C" void HAMLIBSetPTT(int PTTState)
{
// Won't work in non=gui mode
emit m1.HLSetPTT(PTTState);
}
2024-07-23 21:26:30 +01:00
extern "C" void FLRigSetPTT(int PTTState)
{
// Won't work in non=gui mode
emit m1.FLRigSetPTT(PTTState);
}
QTcpSocket * FLRigsock;
int FLRigConnected = 0;
int FLRigConnecting = 0;
void mynet::FLRigdisplayError(QAbstractSocket::SocketError socketError)
{
switch (socketError)
{
case QAbstractSocket::RemoteHostClosedError:
break;
case QAbstractSocket::HostNotFoundError:
QMessageBox::information(nullptr, tr("QtSM"),
"FLRig host was not found. Please check the "
"host name and portsettings->");
break;
case QAbstractSocket::ConnectionRefusedError:
qDebug() << "FLRig Connection Refused";
break;
default:
qDebug() << "FLRig Connection Failed";
break;
}
FLRigConnecting = 0;
FLRigConnected = 0;
}
void mynet::FLRigreadyRead()
{
unsigned char Buffer[4096];
QTcpSocket* Socket = static_cast<QTcpSocket*>(QObject::sender());
// read the data from the socket. Don't do anyhing with it at the moment
Socket->read((char *)Buffer, 4095);
}
void mynet::onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState)
{
if (socketState == QAbstractSocket::UnconnectedState)
{
// Close any connections
FLRigConnected = 0;
FLRigConnecting = 0;
// delete (FLRigsock);
// FLRigsock = 0;
qDebug() << "FLRig Connection Closed";
}
else if (socketState == QAbstractSocket::ConnectedState)
{
FLRigConnected = 1;
FLRigConnecting = 0;
qDebug() << "FLRig Connected";
}
}
void mynet::ConnecttoFLRig()
{
delete(FLRigsock);
FLRigConnected = 0;
FLRigConnecting = 1;
FLRigsock = new QTcpSocket();
connect(FLRigsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(FLRigdisplayError(QAbstractSocket::SocketError)));
connect(FLRigsock, SIGNAL(readyRead()), this, SLOT(FLRigreadyRead()));
connect(FLRigsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onFLRigSocketStateChanged(QAbstractSocket::SocketState)));
FLRigsock->connectToHost(FLRigHost, FLRigPort);
return;
}
static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n"
"User-Agent: XMLRPC++ 0.8\r\n"
"Host: 127.0.0.1:7362\r\n"
"Content-Type: text/xml\r\n"
"Content-length: %d\r\n"
"\r\n%s";
static char Req[] = "<?xml version=\"1.0\"?>\r\n"
"<methodCall><methodName>%s</methodName>\r\n"
"%s"
"</methodCall>\r\n";
void mynet::doFLRigSetPTT(int c)
{
int Len;
char ReqBuf[512];
char SendBuff[512];
char ValueString[256] = "";
sprintf(ValueString, "<params><param><value><i4>%d</i4></value></param></params\r\n>", c);
Len = sprintf(ReqBuf, Req, "rig.set_ptt", ValueString);
Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf);
if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState)
ConnecttoFLRig();
if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState)
return;
FLRigsock->write(SendBuff);
FLRigsock->waitForBytesWritten(3000);
QByteArray datas = FLRigsock->readAll();
qDebug(datas.data());
}
2023-09-04 19:06:44 +01:00
extern "C" void startTimer(int Time)
{
// Won't work in non=gui mode
emit m1.startTimer(Time);
}
void mynet::dostartTimer(int Time)
{
timercopy->start(Time);
}
extern "C" void stopTimer()
{
// Won't work in non=gui mode
emit m1.stopTimer();
}
void mynet::dostopTimer()
{
timercopy->stop();
}
void mynet::doHLSetPTT(int c)
{
char Msg[16];
if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState)
ConnecttoHAMLIB();
sprintf(Msg, "T %d\r\n", c);
HAMLIBsock->write(Msg);
HAMLIBsock->waitForBytesWritten(30000);
QByteArray datas = HAMLIBsock->readAll();
qDebug(datas.data());
}
2024-10-29 22:45:21 +00:00
void mynet::domgmtSetPTT(int snd_ch, int PTTState)
{
char Msg[64];
uint64_t ret;
for (QTcpSocket* socket : _MgmtSockets)
{
// Find Session
TMgmtMode * MGMT = NULL;
for (int i = 0; i < MgmtConCount; i++)
{
if (MgmtConnections[i]->Socket == socket)
{
MGMT = MgmtConnections[i];
break;
}
}
if (MGMT == NULL)
continue;
if (MGMT->BPQPort[snd_ch])
{
sprintf(Msg, "PTT %d %s\r", MGMT->BPQPort[snd_ch], PTTState ? "ON" : "OFF");
ret = socket->write(Msg);
}
}
}
2023-09-04 19:06:44 +01:00
extern "C" void KISSSendtoServer(void * sock, Byte * Msg, int Len)
{
emit t->sendtoKISS(sock, Msg, Len);
}
void workerThread::run()
{
if (SoundMode == 2) // Pulse
{
if (initPulse() == nullptr)
{
if (nonGUIMode)
{
qDebug() << "PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA\n";
}
else
{
QMessageBox msgBox;
msgBox.setText("PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA");
msgBox.exec();
}
SoundMode = 0;
saveSettings();
}
}
soundMain();
if (SoundMode != 3)
{
if (!InitSound(1))
{
// QMessageBox msgBox;
// msgBox.setText("Open Sound Card Failed");
// msgBox.exec();
}
}
// Initialise Modems
init_speed(0);
init_speed(1);
init_speed(2);
init_speed(3);
// emit t->openSockets();
while (Closing == 0)
{
// Run scheduling loop
MainLoop();
this->msleep(10);
}
qDebug() << "Saving Settings";
saveSettings();
qDebug() << "Main Loop exited";
qApp->exit();
};
// Audio over UDP Code.
// Code can either send audio blocks from the sound card as UDP packets or use UDP packets from
// a suitable source (maybe another copy of QtSM) and process them instead of input frm a sound card/
// ie act as a server or client for UDP audio.
// of course we need bidirectional audio, so even when we are a client we send modem generated samples
// to the server and as a server pass received smaples to modem
// It isn't logical to run as both client and server, so probably can use just one socket
QUdpSocket * udpSocket;
qint64 udpServerSeqno= 0;
qint64 udpClientLastSeq = 0;
qint64 udpServerLastSeq = 0;
int droppedPackets = 0;
extern "C" int UDPSoundIsPlaying;
QQueue <unsigned char *> queue;
void mynet::OpenUDP()
{
udpSocket = new QUdpSocket();
if (UDPServ)
{
udpSocket->bind(QHostAddress("0.0.0.0"), UDPServerPort);
QTimer *timer = new QTimer(this);
timercopy = timer;
connect(timer, SIGNAL(timeout()), this, SLOT(dropPTT()));
}
else
udpSocket->bind(QHostAddress("0.0.0.0"), UDPClientPort);
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams()));
}
extern "C" void Flush();
void mynet::dropPTT()
{
timercopy->stop();
if (UDPSoundIsPlaying)
{
// Drop PTT when all sent
Flush();
UDPSoundIsPlaying = 0;
Debugprintf("PTT Off");
RadioPTT(0, 0);
}
}
void mynet::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams())
{
QHostAddress Addr;
quint16 rxPort;
char copy[1501];
// We expect datagrams of 1040 bytes containing a 16 byte header and 512 16 bit samples
// We should get a datagram every 43 mS. We need to use a timeout to drop PTT if running as server
if (UDPServ)
timercopy->start(200);
int Len = udpSocket->readDatagram(copy, 1500, &Addr, &rxPort);
if (Len == 1040)
{
qint64 Seq;
memcpy(&Seq, copy, sizeof(udpServerSeqno));
if (Seq < udpClientLastSeq || udpClientLastSeq == 0)
// Client or Server Restarted
udpClientLastSeq = Seq;
else
{
int Missed = Seq - udpClientLastSeq;
if (Missed > 100) // probably stopped in debug
Missed = 1;
while (--Missed)
{
droppedPackets++;
// insert silence to maintain timing
unsigned char * pkt = (unsigned char *)malloc(1024);
memset(pkt, 0, 1024);
mutex.lock();
queue.append(pkt);
mutex.unlock();
}
}
udpClientLastSeq = Seq;
unsigned char * pkt = (unsigned char *)malloc(1024);
memcpy(pkt, &copy[16], 1024);
mutex.lock();
queue.append(pkt);
mutex.unlock();
}
}
}
void mynet::socketError()
{
char errMsg[80];
sprintf(errMsg, "%d %s", udpSocket->state(), udpSocket->errorString().toLocal8Bit().constData());
// qDebug() << errMsg;
// QMessageBox::question(NULL, "ARDOP GUI", errMsg, QMessageBox::Yes | QMessageBox::No);
}
extern "C" void sendSamplestoStdout(short * Samples, int nSamples)
{
}
extern "C" void sendSamplestoUDP(short * Samples, int nSamples, int Port)
{
if (udpSocket == nullptr)
return;
unsigned char txBuff[1048];
memcpy(txBuff, &udpServerSeqno, sizeof(udpServerSeqno));
udpServerSeqno++;
if (nSamples > 512)
nSamples = 512;
nSamples <<= 1; // short to byte
memcpy(&txBuff[16], Samples, nSamples);
udpSocket->writeDatagram((char *)txBuff, nSamples + 16, QHostAddress(UDPHost), Port);
}
static int min = 0, max = 0, lastlevelGUI = 0, lastlevelreport = 0;
static UCHAR CurrentLevel = 0; // Peak from current samples
extern "C" int SoundIsPlaying;
extern "C" short * SendtoCard(short * buf, int n);
extern "C" short * DMABuffer;
extern "C" void ProcessNewSamples(short * Samples, int nSamples);
extern "C" void UDPPollReceivedSamples()
{
if (queue.isEmpty())
return;
short * ptr;
short * save;
// If we are using UDP for output (sound server) send samples to sound card.
// If for input (virtual sound card) then pass to modem
if (UDPServ)
{
// We only get packets if TX active (sound VOX) so raise PTT and start sending
// ?? do we ignore if local modem is already sending ??
if (SoundIsPlaying)
{
mutex.lock();
save = ptr = (short *)queue.dequeue();
mutex.unlock();
free(save);
}
if (UDPSoundIsPlaying == 0)
{
// Wait for a couple of packets to reduce risk of underrun (but not too many or delay will be excessive
if (queue.count() < 3)
return;
UDPSoundIsPlaying = 1;
Debugprintf("PTT On");
RadioPTT(0, 1); // UDP only use one channel
/// !! how do we drop ptt ??
}
while (queue.count() > 1)
{
short * outptr = DMABuffer;
boolean dropPTT1 = 1;
boolean dropPTT2 = 1;
// We get mono samples but soundcard expects stereo
// Sound card needs 1024 samples so send two packets
mutex.lock();
save = ptr = (short *)queue.dequeue();
mutex.unlock();
for (int n = 0; n < 512; n++)
{
*(outptr++) = *ptr;
*(outptr++) = *ptr++; // Duplicate
if (*ptr)
dropPTT1 = 0; // Drop PTT if all samples zero
}
free(save);
mutex.lock();
save = ptr = (short *)queue.dequeue();
mutex.unlock();
for (int n = 0; n < 512; n++)
{
*(outptr++) = *ptr;
*(outptr++) = *ptr++; // Duplicate
if (*ptr)
dropPTT2 = 0; // Drop PTT if all samples zero
}
free(save);
if (dropPTT1 && dropPTT2)
{
startTimer(1); // All zeros so no need to send
return;
}
DMABuffer = SendtoCard(DMABuffer, 1024);
if (dropPTT2) // 2nd all zeros
startTimer(1);
}
return;
}
mutex.lock();
save = ptr = (short *)queue.dequeue();
mutex.unlock();
// We get mono samples but modem expects stereo
short Buff[2048];
short * inptr = (short *)ptr;
short * outptr = Buff;
int i;
for (i = 0; i < ReceiveSize; i++)
{
if (*(ptr) < min)
min = *ptr;
else if (*(ptr) > max)
max = *ptr;
ptr++;
}
CurrentLevel = ((max - min) * 75) / 32768; // Scale to 150 max
if ((Now - lastlevelGUI) > 2000) // 2 Secs
{
lastlevelGUI = Now;
if ((Now - lastlevelreport) > 10000) // 10 Secs
{
char HostCmd[64];
lastlevelreport = Now;
sprintf(HostCmd, "INPUTPEAKS %d %d", min, max);
Debugprintf("Input peaks = %d, %d", min, max);
}
min = max = 0;
}
for (int n = 0; n < 512; n++)
{
*(outptr++) = *inptr;
*(outptr++) = *inptr++; // Duplicate
}
ProcessNewSamples(Buff, 512);
free(save);
}