2021-02-03 20:00:40 +00:00
// Copyright 2021 Phil Taylor M0VSE
2021-02-05 10:45:19 +00:00
// This code is heavily based on "Kappanhang" by HA2NON, ES1AKOS and W6EL!
2021-02-03 20:00:40 +00:00
# include "udphandler.h"
2021-02-23 21:21:22 +00:00
# include "logcategories.h"
2021-02-07 18:46:47 +00:00
2021-06-04 07:24:26 +00:00
udpHandler : : udpHandler ( udpPreferences prefs , audioSetup rx , audioSetup tx ) :
2021-03-01 20:31:05 +00:00
controlPort ( prefs . controlLANPort ) ,
civPort ( 0 ) ,
audioPort ( 0 ) ,
2021-06-04 07:24:26 +00:00
rxSetup ( rx ) ,
txSetup ( tx )
2021-03-01 19:53:12 +00:00
{
2021-03-01 20:31:05 +00:00
2021-06-04 07:24:26 +00:00
2021-02-13 23:25:24 +00:00
this - > port = this - > controlPort ;
2021-03-01 19:53:12 +00:00
this - > username = prefs . username ;
this - > password = prefs . password ;
2021-03-27 16:07:17 +00:00
this - > compName = prefs . clientName . mid ( 0 , 8 ) + " -wfview " ;
2021-03-01 19:53:12 +00:00
2021-06-04 07:24:26 +00:00
qInfo ( logUdp ( ) ) < < " Starting udpHandler user: " < < username < < " rx latency: " < < rxSetup . latency < < " tx latency: " < < txSetup . latency < < " rx sample rate: " < < rxSetup . samplerate < <
" rx codec: " < < rxSetup . codec < < " tx sample rate: " < < txSetup . samplerate < < " tx codec: " < < txSetup . codec ;
2021-02-07 17:40:38 +00:00
2021-02-07 18:46:47 +00:00
// Try to set the IP address, if it is a hostname then perform a DNS lookup.
2021-03-01 19:53:12 +00:00
if ( ! radioIP . setAddress ( prefs . ipAddress ) )
2021-02-07 18:46:47 +00:00
{
2021-03-01 19:53:12 +00:00
QHostInfo remote = QHostInfo : : fromName ( prefs . ipAddress ) ;
2021-02-07 19:09:19 +00:00
foreach ( QHostAddress addr , remote . addresses ( ) )
2021-02-07 18:46:47 +00:00
{
2021-02-07 19:09:19 +00:00
if ( addr . protocol ( ) = = QAbstractSocket : : IPv4Protocol ) {
radioIP = addr ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Got IP Address : " < < prefs . ipAddress < < " : " < < addr . toString ( ) ;
2021-02-07 19:09:19 +00:00
break ;
}
2021-02-07 18:46:47 +00:00
}
2021-02-07 19:09:19 +00:00
if ( radioIP . isNull ( ) )
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Error obtaining IP Address for : " < < prefs . ipAddress < < " : " < < remote . errorString ( ) ;
2021-02-07 18:46:47 +00:00
return ;
}
}
2021-02-03 20:00:40 +00:00
// Convoluted way to find the external IP address, there must be a better way????
QString localhostname = QHostInfo : : localHostName ( ) ;
QList < QHostAddress > hostList = QHostInfo : : fromName ( localhostname ) . addresses ( ) ;
2021-02-07 18:46:47 +00:00
foreach ( const QHostAddress & address , hostList )
2021-02-04 19:53:48 +00:00
{
2021-02-07 18:46:47 +00:00
if ( address . protocol ( ) = = QAbstractSocket : : IPv4Protocol & & address . isLoopback ( ) = = false )
2021-02-04 19:53:48 +00:00
{
2021-02-04 06:00:13 +00:00
localIP = QHostAddress ( address . toString ( ) ) ;
2021-02-03 20:00:40 +00:00
}
}
2021-02-21 14:53:42 +00:00
}
void udpHandler : : init ( )
{
2021-02-13 23:25:24 +00:00
udpBase : : init ( ) ; // Perform UDP socket initialization.
2021-02-07 18:46:47 +00:00
2021-02-13 23:25:24 +00:00
// Connect socket to my dataReceived function.
QUdpSocket : : connect ( udp , & QUdpSocket : : readyRead , this , & udpHandler : : dataReceived ) ;
2021-02-07 18:46:47 +00:00
2021-02-13 23:25:24 +00:00
/*
Connect various timers
*/
2021-02-21 14:53:42 +00:00
tokenTimer = new QTimer ( ) ;
areYouThereTimer = new QTimer ( ) ;
pingTimer = new QTimer ( ) ;
idleTimer = new QTimer ( ) ;
2021-02-04 19:53:48 +00:00
2021-02-21 14:53:42 +00:00
connect ( tokenTimer , & QTimer : : timeout , this , std : : bind ( & udpHandler : : sendToken , this , 0x05 ) ) ;
2021-02-25 11:13:18 +00:00
connect ( areYouThereTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , false , 0x03 , 0 ) ) ;
2021-02-21 14:53:42 +00:00
connect ( pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
connect ( idleTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , true , 0 , 0 ) ) ;
2021-02-13 23:25:24 +00:00
2021-02-21 14:53:42 +00:00
// Start sending are you there packets - will be stopped once "I am here" received
areYouThereTimer - > start ( AREYOUTHERE_PERIOD ) ;
2021-02-03 20:00:40 +00:00
}
udpHandler : : ~ udpHandler ( )
{
2021-02-21 01:18:14 +00:00
if ( streamOpened ) {
2021-02-13 23:25:24 +00:00
if ( audio ! = Q_NULLPTR ) {
2021-02-03 20:00:40 +00:00
delete audio ;
2021-02-05 17:40:58 +00:00
}
2021-02-13 23:25:24 +00:00
if ( civ ! = Q_NULLPTR ) {
delete civ ;
2021-02-05 17:40:58 +00:00
}
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Sending token removal packet " ;
2021-02-13 23:25:24 +00:00
sendToken ( 0x01 ) ;
2021-02-25 17:53:01 +00:00
if ( tokenTimer ! = Q_NULLPTR )
{
tokenTimer - > stop ( ) ;
delete tokenTimer ;
}
if ( watchdogTimer ! = Q_NULLPTR )
{
watchdogTimer - > stop ( ) ;
delete watchdogTimer ;
}
2021-02-03 20:00:40 +00:00
}
}
2021-02-22 22:25:09 +00:00
2021-02-27 00:37:00 +00:00
void udpHandler : : changeLatency ( quint16 value )
2021-02-08 16:53:26 +00:00
{
2021-02-27 00:37:00 +00:00
emit haveChangeLatency ( value ) ;
2021-02-08 16:53:26 +00:00
}
2021-03-22 18:53:34 +00:00
void udpHandler : : setVolume ( unsigned char value )
{
emit haveSetVolume ( value ) ;
}
2021-02-13 23:25:24 +00:00
void udpHandler : : receiveFromCivStream ( QByteArray data )
2021-02-03 20:00:40 +00:00
{
emit haveDataFromPort ( data ) ;
}
2021-03-01 19:53:12 +00:00
void udpHandler : : receiveAudioData ( const audioPacket & data )
2021-02-28 20:10:07 +00:00
{
emit haveAudioData ( data ) ;
}
2021-02-03 20:00:40 +00:00
void udpHandler : : receiveDataFromUserToRig ( QByteArray data )
{
2021-02-13 23:25:24 +00:00
if ( civ ! = Q_NULLPTR )
2021-02-05 17:40:58 +00:00
{
2021-02-13 23:25:24 +00:00
civ - > send ( data ) ;
2021-02-05 17:40:58 +00:00
}
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
void udpHandler : : dataReceived ( )
2021-02-03 20:00:40 +00:00
{
while ( udp - > hasPendingDatagrams ( ) ) {
2021-02-25 17:53:01 +00:00
lastReceived = QTime : : currentTime ( ) ;
2021-02-03 20:00:40 +00:00
QNetworkDatagram datagram = udp - > receiveDatagram ( ) ;
QByteArray r = datagram . data ( ) ;
switch ( r . length ( ) )
{
2021-02-20 18:29:23 +00:00
case ( CONTROL_SIZE ) : // control packet
2021-02-05 21:23:00 +00:00
{
2021-02-20 18:29:23 +00:00
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
if ( in - > type = = 0x04 ) {
// If timer is active, stop it as they are obviously there!
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Received I am here from: " < < datagram . senderAddress ( ) ;
2021-03-09 17:22:16 +00:00
2021-02-21 14:53:42 +00:00
if ( areYouThereTimer - > isActive ( ) ) {
2021-02-20 18:29:23 +00:00
// send ping packets every second
2021-02-25 11:13:18 +00:00
areYouThereTimer - > stop ( ) ;
2021-02-21 14:53:42 +00:00
pingTimer - > start ( PING_PERIOD ) ;
idleTimer - > start ( IDLE_PERIOD ) ;
2021-02-05 17:40:58 +00:00
}
2021-02-14 07:15:49 +00:00
}
2021-02-20 18:29:23 +00:00
// This is "I am ready" in response to "Are you ready" so send login.
else if ( in - > type = = 0x06 )
2021-02-14 07:15:49 +00:00
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Received I am ready " ;
2021-02-20 18:29:23 +00:00
sendLogin ( ) ; // send login packet
2021-02-14 07:15:49 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
}
case ( PING_SIZE ) : // ping packet
{
ping_packet_t in = ( ping_packet_t ) r . constData ( ) ;
if ( in - > type = = 0x07 & & in - > reply = = 0x01 & & streamOpened )
2021-02-14 07:15:49 +00:00
{
2021-02-20 18:29:23 +00:00
// This is a response to our ping request so measure latency
latency + = lastPingSentTime . msecsTo ( QDateTime : : currentDateTime ( ) ) ;
latency / = 2 ;
quint32 totalsent = packetsSent ;
2021-02-24 22:56:40 +00:00
quint32 totallost = packetsLost ;
2021-02-20 18:29:23 +00:00
if ( audio ! = Q_NULLPTR ) {
totalsent = totalsent + audio - > packetsSent ;
2021-02-24 22:56:40 +00:00
totallost = totallost + audio - > packetsLost ;
2021-02-20 18:29:23 +00:00
}
if ( civ ! = Q_NULLPTR ) {
totalsent = totalsent + civ - > packetsSent ;
2021-02-24 22:56:40 +00:00
totallost = totallost + civ - > packetsLost ;
2021-02-20 18:29:23 +00:00
}
2021-02-24 22:56:40 +00:00
2021-05-27 10:41:08 +00:00
QString tempLatency ;
2021-06-04 07:24:26 +00:00
if ( rxSetup . latency > audio - > audioLatency )
2021-05-27 10:41:08 +00:00
{
2021-05-28 09:45:09 +00:00
tempLatency = QString ( " %1 ms " ) . arg ( audio - > audioLatency , 3 ) ;
2021-05-27 10:41:08 +00:00
}
else {
2021-05-28 09:45:09 +00:00
tempLatency = QString ( " <span style = \" color:red \" >%1 ms</span> " ) . arg ( audio - > audioLatency , 3 ) ;
2021-05-27 10:41:08 +00:00
}
2021-06-10 08:20:05 +00:00
QString txString = " " ;
if ( txSetup . codec = = 0 ) {
txString = " (no tx) " ;
}
2021-07-08 08:28:20 +00:00
emit haveNetworkStatus ( QString ( " <pre>%1 rx latency: %2 / rtt: %3 ms / loss: %4/%5</pre> " ) . arg ( txString ) . arg ( tempLatency ) . arg ( latency , 3 ) . arg ( totallost , 3 ) . arg ( totalsent , 3 ) ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
case ( TOKEN_SIZE ) : // Response to Token request
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
token_packet_t in = ( token_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > res = = 0x05 & & in - > type ! = 0x01 )
2021-02-05 17:40:58 +00:00
{
2021-02-20 18:29:23 +00:00
if ( in - > response = = 0x0000 )
2021-02-05 17:40:58 +00:00
{
2021-02-23 21:21:22 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Token renewal successful " ;
2021-02-21 14:53:42 +00:00
tokenTimer - > start ( TOKEN_RENEWAL ) ;
2021-02-20 18:29:23 +00:00
gotAuthOK = true ;
if ( ! streamOpened )
{
sendRequestStream ( ) ;
}
}
else if ( in - > response = = 0xffffffff )
{
qWarning ( ) < < this - > metaObject ( ) - > className ( ) < < " : Radio rejected token renewal, performing login " ;
remoteId = in - > sentid ;
2021-02-21 14:53:42 +00:00
tokRequest = in - > tokrequest ;
token = in - > token ;
2021-03-06 14:40:43 +00:00
streamOpened = false ;
sendRequestStream ( ) ;
2021-02-21 14:53:42 +00:00
// Got new token response
2021-03-06 14:40:43 +00:00
//sendToken(0x02); // Update it.
2021-02-20 18:29:23 +00:00
}
else
{
qWarning ( ) < < this - > metaObject ( ) - > className ( ) < < " : Unknown response to token renewal? " < < in - > response ;
2021-02-03 20:00:40 +00:00
}
2021-02-05 17:40:58 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
}
case ( STATUS_SIZE ) : // Status packet
{
status_packet_t in = ( status_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > type ! = 0x01 ) {
if ( in - > error = = 0x00ffffff & & ! streamOpened )
{
emit haveNetworkError ( radioIP . toString ( ) , " Auth failed, try rebooting the radio. " ) ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Auth failed, try rebooting the radio. " ;
2021-02-24 22:56:40 +00:00
}
else if ( in - > error = = 0x00000000 & & in - > disc = = 0x01 )
{
emit haveNetworkError ( radioIP . toString ( ) , " Got radio disconnected. " ) ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Got radio disconnected. " ;
2021-02-24 22:56:40 +00:00
if ( streamOpened ) {
// Close stream connections but keep connection open to the radio.
if ( audio ! = Q_NULLPTR ) {
delete audio ;
2021-03-29 08:38:31 +00:00
audio = Q_NULLPTR ;
2021-02-24 22:56:40 +00:00
}
if ( civ ! = Q_NULLPTR ) {
delete civ ;
2021-03-29 08:38:31 +00:00
civ = Q_NULLPTR ;
2021-02-24 22:56:40 +00:00
}
2021-03-29 08:38:31 +00:00
2021-02-24 22:56:40 +00:00
streamOpened = false ;
2021-02-21 14:53:42 +00:00
}
}
2021-03-01 19:53:12 +00:00
else {
civPort = qFromBigEndian ( in - > civport ) ;
audioPort = qFromBigEndian ( in - > audioport ) ;
}
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
case ( LOGIN_RESPONSE_SIZE ) : // Response to Login packet.
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
login_response_packet_t in = ( login_response_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > type ! = 0x01 ) {
if ( in - > error = = 0xfeffffff )
2021-02-20 18:29:23 +00:00
{
2021-02-24 22:56:40 +00:00
emit haveNetworkStatus ( " Invalid Username/Password " ) ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Invalid Username/Password " ;
2021-02-20 18:29:23 +00:00
}
2021-02-24 22:56:40 +00:00
else if ( ! isAuthenticated )
2021-02-20 18:29:23 +00:00
{
2021-02-24 22:56:40 +00:00
if ( in - > tokrequest = = tokRequest )
{
emit haveNetworkStatus ( " Radio Login OK! " ) ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Received matching token response to our request " ;
2021-02-24 22:56:40 +00:00
token = in - > token ;
sendToken ( 0x02 ) ;
tokenTimer - > start ( TOKEN_RENEWAL ) ; // Start token request timer
isAuthenticated = true ;
}
else
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Token response did not match, sent: " < < tokRequest < < " got " < < in - > tokrequest ;
2021-02-24 22:56:40 +00:00
}
2021-02-20 18:29:23 +00:00
}
2021-02-24 22:56:40 +00:00
if ( ! strcmp ( in - > connection , " FTTH " ) )
{
highBandwidthConnection = true ;
}
2021-06-23 19:25:45 +00:00
if ( ! strcmp ( in - > connection , " WFVIEW " ) )
{
}
else {
if ( rxSetup . codec > = 0x40 | | txSetup . codec > = 0x40 )
{
emit haveNetworkError ( QString ( " UDP " ) , QString ( " Opus codec not supported " ) ) ;
}
}
2021-02-24 22:56:40 +00:00
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Detected connection speed " < < in - > connection ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
case ( CONNINFO_SIZE ) :
2021-02-05 17:40:58 +00:00
{
2021-02-20 18:29:23 +00:00
conninfo_packet_t in = ( conninfo_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > type ! = 0x01 ) {
devName = in - > name ;
QHostAddress ip = QHostAddress ( qToBigEndian ( in - > ipaddress ) ) ;
if ( ! streamOpened & & in - > busy )
2021-02-20 18:29:23 +00:00
{
2021-02-24 22:56:40 +00:00
if ( in - > ipaddress ! = 0x00 & & strcmp ( in - > computer , compName . toLocal8Bit ( ) ) )
{
emit haveNetworkStatus ( devName + " in use by: " + in - > computer + " ( " + ip . toString ( ) + " ) " ) ;
sendControl ( false , 0x00 , in - > seq ) ; // Respond with an idle
}
else {
civ = new udpCivData ( localIP , radioIP , civPort ) ;
2021-05-18 08:32:56 +00:00
// TX is not supported
if ( txSampleRates < 2 ) {
2021-06-04 07:24:26 +00:00
txSetup . samplerate = 0 ;
txSetup . codec = 0 ;
2021-05-18 08:32:56 +00:00
}
2021-06-04 07:24:26 +00:00
audio = new udpAudio ( localIP , radioIP , audioPort , rxSetup , txSetup ) ;
2021-02-21 01:18:14 +00:00
2021-02-24 22:56:40 +00:00
QObject : : connect ( civ , SIGNAL ( receive ( QByteArray ) ) , this , SLOT ( receiveFromCivStream ( QByteArray ) ) ) ;
2021-03-01 19:53:12 +00:00
QObject : : connect ( audio , SIGNAL ( haveAudioData ( audioPacket ) ) , this , SLOT ( receiveAudioData ( audioPacket ) ) ) ;
2021-03-22 18:53:34 +00:00
QObject : : connect ( this , SIGNAL ( haveChangeLatency ( quint16 ) ) , audio , SLOT ( changeLatency ( quint16 ) ) ) ;
QObject : : connect ( this , SIGNAL ( haveSetVolume ( unsigned char ) ) , audio , SLOT ( setVolume ( unsigned char ) ) ) ;
2021-02-05 20:26:18 +00:00
2021-02-24 22:56:40 +00:00
streamOpened = true ;
2021-02-03 20:00:40 +00:00
2021-02-24 22:56:40 +00:00
emit haveNetworkStatus ( devName ) ;
2021-02-21 01:18:14 +00:00
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " Got serial and audio request success, device name: " < < devName ;
2021-02-24 22:56:40 +00:00
// Stuff can change in the meantime because of a previous login...
remoteId = in - > sentid ;
myId = in - > rcvdid ;
tokRequest = in - > tokrequest ;
token = in - > token ;
}
2021-02-20 18:29:23 +00:00
}
2021-02-24 22:56:40 +00:00
else if ( ! streamOpened & & ! in - > busy )
{
emit haveNetworkStatus ( devName + " available " ) ;
2021-02-21 01:18:14 +00:00
2021-02-24 22:56:40 +00:00
identa = in - > identa ;
identb = in - > identb ;
2021-02-21 01:18:14 +00:00
2021-02-24 22:56:40 +00:00
sendRequestStream ( ) ;
}
2021-02-25 17:53:01 +00:00
else if ( streamOpened )
/* If another client connects/disconnects from the server, the server will emit
a CONNINFO packet , send our details to confirm we still want the stream */
{
// Received while stream is open.
sendRequestStream ( ) ;
}
2021-02-20 18:29:23 +00:00
}
break ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
case ( CAPABILITIES_SIZE ) :
{
capabilities_packet_t in = ( capabilities_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > type ! = 0x01 )
{
audioType = in - > audio ;
devName = in - > name ;
2021-05-18 08:32:56 +00:00
civId = in - > civ ;
rxSampleRates = in - > rxsample ;
txSampleRates = in - > txsample ;
2021-05-21 15:30:52 +00:00
emit haveBaudRate ( qFromBigEndian ( in - > baudrate ) ) ;
2021-02-24 22:56:40 +00:00
//replyId = r.mid(0x42, 16);
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " Received radio capabilities, Name: " < <
2021-02-24 22:56:40 +00:00
devName < < " Audio: " < <
2021-05-18 08:32:56 +00:00
audioType < < " CIV: " < < hex < < civId ;
if ( txSampleRates < 2 )
{
// TX not supported
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " TX audio is disabled " ;
}
2021-02-24 22:56:40 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
}
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
udpBase : : dataReceived ( r ) ; // Call parent function to process the rest.
2021-02-04 20:09:09 +00:00
r . clear ( ) ;
datagram . clear ( ) ;
2021-02-03 20:00:40 +00:00
}
return ;
}
2021-02-20 18:29:23 +00:00
void udpHandler : : sendRequestStream ( )
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
2021-02-15 19:28:17 +00:00
QByteArray usernameEncoded ;
passcode ( username , usernameEncoded ) ;
2021-02-20 18:29:23 +00:00
conninfo_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . code = 0x0180 ;
p . res = 0x03 ;
2021-02-23 20:48:53 +00:00
p . commoncap = 0x8010 ;
2021-02-20 18:29:23 +00:00
p . identa = identa ;
p . identb = identb ;
2021-03-06 14:40:43 +00:00
p . innerseq = authSeq + + ;
2021-02-20 18:29:23 +00:00
p . tokrequest = tokRequest ;
p . token = token ;
2021-02-21 01:18:14 +00:00
memcpy ( & p . name , devName . toLocal8Bit ( ) . constData ( ) , devName . length ( ) ) ;
2021-02-20 18:29:23 +00:00
p . rxenable = 1 ;
2021-05-18 08:32:56 +00:00
if ( this - > txSampleRates > 1 ) {
p . txenable = 1 ;
2021-06-04 07:24:26 +00:00
p . txcodec = txSetup . codec ;
2021-05-18 08:32:56 +00:00
}
2021-06-04 07:24:26 +00:00
p . rxcodec = rxSetup . codec ;
2021-02-21 15:06:27 +00:00
memcpy ( & p . username , usernameEncoded . constData ( ) , usernameEncoded . length ( ) ) ;
2021-06-04 07:24:26 +00:00
p . rxsample = qToBigEndian ( ( quint32 ) rxSetup . samplerate ) ;
p . txsample = qToBigEndian ( ( quint32 ) txSetup . samplerate ) ;
2021-02-20 18:29:23 +00:00
p . civport = qToBigEndian ( ( quint32 ) civPort ) ;
p . audioport = qToBigEndian ( ( quint32 ) audioPort ) ;
2021-06-04 07:24:26 +00:00
p . txbuffer = qToBigEndian ( ( quint32 ) txSetup . latency ) ;
2021-03-09 17:22:16 +00:00
p . convert = 1 ;
2021-02-20 18:29:23 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-14 07:53:55 +00:00
void udpHandler : : sendAreYouThere ( )
{
if ( areYouThereCounter = = 20 )
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Radio not responding. " ;
2021-02-14 07:53:55 +00:00
emit haveNetworkStatus ( " Radio not responding! " ) ;
}
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Sending Are You There... " ;
2021-02-18 15:54:26 +00:00
2021-02-14 07:53:55 +00:00
areYouThereCounter + + ;
2021-02-18 15:54:26 +00:00
udpBase : : sendControl ( false , 0x03 , 0x00 ) ;
2021-02-14 07:53:55 +00:00
}
2021-02-13 23:25:24 +00:00
void udpHandler : : sendLogin ( ) // Only used on control stream.
2021-02-03 20:00:40 +00:00
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Sending login packet " ;
2021-02-13 23:25:24 +00:00
2021-02-20 18:29:23 +00:00
tokRequest = static_cast < quint16 > ( rand ( ) | rand ( ) < < 8 ) ; // Generate random token request.
2021-02-15 19:28:17 +00:00
QByteArray usernameEncoded ;
QByteArray passwordEncoded ;
passcode ( username , usernameEncoded ) ;
passcode ( password , passwordEncoded ) ;
2021-02-03 20:00:40 +00:00
2021-02-20 18:29:23 +00:00
login_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . code = 0x0170 ; // Not sure what this is?
2021-03-06 14:40:43 +00:00
p . innerseq = authSeq + + ;
2021-02-20 18:29:23 +00:00
p . tokrequest = tokRequest ;
memcpy ( p . username , usernameEncoded . constData ( ) , usernameEncoded . length ( ) ) ;
memcpy ( p . password , passwordEncoded . constData ( ) , passwordEncoded . length ( ) ) ;
2021-02-21 01:18:14 +00:00
memcpy ( p . name , compName . toLocal8Bit ( ) . constData ( ) , compName . length ( ) ) ;
2021-02-03 20:00:40 +00:00
2021-02-20 18:29:23 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
void udpHandler : : sendToken ( uint8_t magic )
2021-02-03 20:00:40 +00:00
{
2021-02-23 21:21:22 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " Sending Token request: " < < magic ;
2021-02-03 20:00:40 +00:00
2021-02-20 18:29:23 +00:00
token_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . code = 0x0130 ; // Not sure what this is?
p . res = magic ;
2021-03-06 14:40:43 +00:00
p . innerseq = authSeq + + ;
2021-02-20 18:29:23 +00:00
p . tokrequest = tokRequest ;
p . token = token ;
2021-02-14 15:30:34 +00:00
2021-02-20 18:29:23 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-27 09:34:56 +00:00
// The radio should request a repeat of the token renewal packet via retransmission!
2021-02-24 22:56:40 +00:00
//tokenTimer->start(100); // Set 100ms timer for retry (this will be cancelled if a response is received)
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
// Class that manages all Civ Data to/from the rig
udpCivData : : udpCivData ( QHostAddress local , QHostAddress ip , quint16 civPort )
2021-02-05 17:40:58 +00:00
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Starting udpCivData " ;
2021-02-04 19:53:48 +00:00
localIP = local ;
2021-02-13 23:25:24 +00:00
port = civPort ;
2021-02-03 20:00:40 +00:00
radioIP = ip ;
2021-02-13 23:25:24 +00:00
udpBase : : init ( ) ; // Perform connection
QUdpSocket : : connect ( udp , & QUdpSocket : : readyRead , this , & udpCivData : : dataReceived ) ;
2021-02-03 20:00:40 +00:00
2021-02-18 15:54:26 +00:00
sendControl ( false , 0x03 , 0x00 ) ; // First connect packet
2021-02-13 23:25:24 +00:00
/*
Connect various timers
*/
2021-02-21 14:53:42 +00:00
pingTimer = new QTimer ( ) ;
idleTimer = new QTimer ( ) ;
2021-02-25 11:13:18 +00:00
areYouThereTimer = new QTimer ( ) ;
2021-02-25 17:53:01 +00:00
startCivDataTimer = new QTimer ( ) ;
watchdogTimer = new QTimer ( ) ;
2021-02-21 14:53:42 +00:00
connect ( pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
2021-02-25 17:53:01 +00:00
connect ( watchdogTimer , & QTimer : : timeout , this , & udpCivData : : watchdog ) ;
2021-02-21 14:53:42 +00:00
connect ( idleTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , true , 0 , 0 ) ) ;
2021-02-25 17:53:01 +00:00
connect ( startCivDataTimer , & QTimer : : timeout , this , std : : bind ( & udpCivData : : sendOpenClose , this , false ) ) ;
2021-02-25 11:13:18 +00:00
connect ( areYouThereTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , false , 0x03 , 0 ) ) ;
2021-02-25 17:53:01 +00:00
watchdogTimer - > start ( WATCHDOG_PERIOD ) ;
2021-02-25 11:13:18 +00:00
// Start sending are you there packets - will be stopped once "I am here" received
2021-02-13 23:25:24 +00:00
// send ping packets every 100 ms (maybe change to less frequent?)
2021-02-21 14:53:42 +00:00
pingTimer - > start ( PING_PERIOD ) ;
2021-02-13 23:25:24 +00:00
// Send idle packets every 100ms, this timer will be reset everytime a non-idle packet is sent.
2021-02-21 14:53:42 +00:00
idleTimer - > start ( IDLE_PERIOD ) ;
2021-02-25 11:13:18 +00:00
areYouThereTimer - > start ( AREYOUTHERE_PERIOD ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-25 17:53:01 +00:00
udpCivData : : ~ udpCivData ( )
{
2021-02-13 23:25:24 +00:00
sendOpenClose ( true ) ;
2021-02-25 17:53:01 +00:00
if ( startCivDataTimer ! = Q_NULLPTR )
{
startCivDataTimer - > stop ( ) ;
delete startCivDataTimer ;
startCivDataTimer = Q_NULLPTR ;
}
2021-03-29 08:38:31 +00:00
if ( pingTimer ! = Q_NULLPTR )
{
pingTimer - > stop ( ) ;
delete pingTimer ;
pingTimer = Q_NULLPTR ;
}
if ( idleTimer ! = Q_NULLPTR )
{
idleTimer - > stop ( ) ;
delete idleTimer ;
idleTimer = Q_NULLPTR ;
}
if ( watchdogTimer ! = Q_NULLPTR )
{
watchdogTimer - > stop ( ) ;
delete watchdogTimer ;
watchdogTimer = Q_NULLPTR ;
}
2021-02-25 17:53:01 +00:00
}
void udpCivData : : watchdog ( )
{
static bool alerted = false ;
2021-05-17 15:03:53 +00:00
if ( lastReceived . msecsTo ( QTime : : currentTime ( ) ) > 2000 )
2021-02-25 17:53:01 +00:00
{
if ( ! alerted ) {
2021-05-17 15:03:53 +00:00
qInfo ( logUdp ( ) ) < < " CIV Watchdog: no CIV data received for 2s, requesting data start. " ;
2021-02-25 17:53:01 +00:00
if ( startCivDataTimer ! = Q_NULLPTR )
{
startCivDataTimer - > start ( 100 ) ;
}
alerted = true ;
}
}
else
{
alerted = false ;
}
2021-02-13 23:25:24 +00:00
}
2021-02-03 20:00:40 +00:00
2021-02-13 23:25:24 +00:00
void udpCivData : : send ( QByteArray d )
2021-02-03 20:00:40 +00:00
{
2021-05-31 16:55:01 +00:00
//qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d;
2021-02-20 18:29:23 +00:00
data_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
2021-05-13 22:15:03 +00:00
p . len = sizeof ( p ) + d . length ( ) ;
2021-02-20 18:29:23 +00:00
p . sentid = myId ;
p . rcvdid = remoteId ;
p . reply = ( char ) 0xc1 ;
p . datalen = d . length ( ) ;
p . sendseq = qToBigEndian ( sendSeqB ) ; // THIS IS BIG ENDIAN!
QByteArray t = QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ;
2021-02-03 20:00:40 +00:00
t . append ( d ) ;
2021-02-13 23:25:24 +00:00
sendTrackedPacket ( t ) ;
2021-02-03 20:00:40 +00:00
sendSeqB + + ;
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
void udpCivData : : sendOpenClose ( bool close )
2021-02-03 20:00:40 +00:00
{
2021-02-25 17:53:01 +00:00
uint8_t magic = 0x04 ;
2021-02-03 20:00:40 +00:00
2021-02-05 17:40:58 +00:00
if ( close )
{
2021-02-03 20:00:40 +00:00
magic = 0x00 ;
2021-02-05 17:40:58 +00:00
}
2021-02-03 20:00:40 +00:00
2021-02-21 01:18:14 +00:00
openclose_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . data = 0x01c0 ; // Not sure what other values are available:
p . sendseq = qToBigEndian ( sendSeqB ) ;
p . magic = magic ;
2021-02-03 20:00:40 +00:00
sendSeqB + + ;
2021-02-21 01:18:14 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
void udpCivData : : dataReceived ( )
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
while ( udp - > hasPendingDatagrams ( ) )
{
2021-02-03 20:00:40 +00:00
QNetworkDatagram datagram = udp - > receiveDatagram ( ) ;
2021-05-15 17:53:16 +00:00
//qInfo(logUdp()) << "Received: " << datagram.data();
2021-02-03 20:00:40 +00:00
QByteArray r = datagram . data ( ) ;
2021-05-31 16:55:01 +00:00
2021-02-03 20:00:40 +00:00
switch ( r . length ( ) )
{
2021-02-20 18:29:23 +00:00
case ( CONTROL_SIZE ) : // Control packet
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
2021-02-25 11:13:18 +00:00
if ( in - > type = = 0x04 )
{
areYouThereTimer - > stop ( ) ;
}
else if ( in - > type = = 0x06 )
2021-02-20 18:29:23 +00:00
{
// Update remoteId
remoteId = in - > sentid ;
2021-02-25 17:53:01 +00:00
// Manually send a CIV start request and start the timer if it isn't received.
// The timer will be stopped as soon as valid CIV data is received.
2021-02-20 18:29:23 +00:00
sendOpenClose ( false ) ;
2021-02-25 17:53:01 +00:00
if ( startCivDataTimer ! = Q_NULLPTR ) {
startCivDataTimer - > start ( 100 ) ;
}
2021-02-05 20:26:18 +00:00
}
2021-02-20 20:23:21 +00:00
break ;
2021-02-20 18:29:23 +00:00
}
default :
{
2021-02-28 20:10:07 +00:00
if ( r . length ( ) > 21 ) {
data_packet_t in = ( data_packet_t ) r . constData ( ) ;
if ( in - > type ! = 0x01 ) {
// Process this packet, any re-transmit requests will happen later.
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
// We have received some Civ data so stop sending Start packets!
if ( startCivDataTimer ! = Q_NULLPTR ) {
startCivDataTimer - > stop ( ) ;
}
lastReceived = QTime : : currentTime ( ) ;
2021-02-28 20:15:34 +00:00
if ( quint16 ( in - > datalen + 0x15 ) = = ( quint16 ) in - > len )
2021-02-28 20:10:07 +00:00
{
2021-05-31 16:55:01 +00:00
//if (r.mid(0x15).length() != 157)
emit receive ( r . mid ( 0x15 ) ) ;
//qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length();
2021-02-28 20:10:07 +00:00
}
2021-02-20 18:29:23 +00:00
}
}
break ;
2021-02-03 20:00:40 +00:00
}
}
2021-02-13 23:25:24 +00:00
udpBase : : dataReceived ( r ) ; // Call parent function to process the rest.
2021-05-31 16:55:01 +00:00
2021-02-04 20:09:09 +00:00
r . clear ( ) ;
datagram . clear ( ) ;
2021-02-03 20:00:40 +00:00
}
}
// Audio stream
2021-06-04 07:24:26 +00:00
udpAudio : : udpAudio ( QHostAddress local , QHostAddress ip , quint16 audioPort , audioSetup rxSetup , audioSetup txSetup )
2021-02-03 20:00:40 +00:00
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Starting udpAudio " ;
2021-02-09 12:43:28 +00:00
this - > localIP = local ;
2021-02-13 23:25:24 +00:00
this - > port = audioPort ;
2021-02-09 12:43:28 +00:00
this - > radioIP = ip ;
2021-02-03 20:00:40 +00:00
2021-06-06 21:11:48 +00:00
if ( txSetup . samplerate = = 0 ) {
2021-05-18 08:32:56 +00:00
enableTx = false ;
}
2021-02-04 20:20:23 +00:00
init ( ) ; // Perform connection
2021-02-04 19:53:48 +00:00
2021-02-13 23:25:24 +00:00
QUdpSocket : : connect ( udp , & QUdpSocket : : readyRead , this , & udpAudio : : dataReceived ) ;
2021-02-03 20:00:40 +00:00
2021-02-11 19:18:35 +00:00
rxaudio = new audioHandler ( ) ;
2021-02-08 08:31:48 +00:00
rxAudioThread = new QThread ( this ) ;
rxaudio - > moveToThread ( rxAudioThread ) ;
2021-03-13 09:23:06 +00:00
rxAudioThread - > start ( ) ;
2021-06-04 07:24:26 +00:00
connect ( this , SIGNAL ( setupRxAudio ( audioSetup ) ) , rxaudio , SLOT ( init ( audioSetup ) ) ) ;
2021-02-27 00:37:00 +00:00
2021-05-27 16:59:01 +00:00
// signal/slot not currently used.
2021-05-27 17:34:44 +00:00
connect ( this , SIGNAL ( haveAudioData ( audioPacket ) ) , rxaudio , SLOT ( incomingAudio ( audioPacket ) ) ) ;
2021-02-27 00:37:00 +00:00
connect ( this , SIGNAL ( haveChangeLatency ( quint16 ) ) , rxaudio , SLOT ( changeLatency ( quint16 ) ) ) ;
2021-03-22 18:53:34 +00:00
connect ( this , SIGNAL ( haveSetVolume ( unsigned char ) ) , rxaudio , SLOT ( setVolume ( unsigned char ) ) ) ;
2021-02-08 10:22:20 +00:00
connect ( rxAudioThread , SIGNAL ( finished ( ) ) , rxaudio , SLOT ( deleteLater ( ) ) ) ;
2021-03-13 09:23:06 +00:00
2021-06-04 07:24:26 +00:00
txSetup . radioChan = 1 ;
2021-02-11 19:18:35 +00:00
txaudio = new audioHandler ( ) ;
txAudioThread = new QThread ( this ) ;
txaudio - > moveToThread ( txAudioThread ) ;
txAudioThread - > start ( ) ;
2021-03-13 09:23:06 +00:00
2021-06-04 07:24:26 +00:00
connect ( this , SIGNAL ( setupTxAudio ( audioSetup ) ) , txaudio , SLOT ( init ( audioSetup ) ) ) ;
2021-05-22 20:09:04 +00:00
2021-03-13 09:23:06 +00:00
connect ( txAudioThread , SIGNAL ( finished ( ) ) , txaudio , SLOT ( deleteLater ( ) ) ) ;
2021-02-11 19:18:35 +00:00
2021-02-18 15:54:26 +00:00
sendControl ( false , 0x03 , 0x00 ) ; // First connect packet
2021-02-13 23:25:24 +00:00
2021-02-21 14:53:42 +00:00
pingTimer = new QTimer ( ) ;
connect ( pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
pingTimer - > start ( PING_PERIOD ) ; // send ping packets every 100ms
2021-02-13 23:25:24 +00:00
2021-05-18 08:32:56 +00:00
if ( enableTx ) {
2021-06-04 07:24:26 +00:00
emit setupTxAudio ( txSetup ) ;
2021-05-18 08:32:56 +00:00
}
2021-06-04 07:24:26 +00:00
emit setupRxAudio ( rxSetup ) ;
2021-02-14 07:15:49 +00:00
2021-02-25 17:53:01 +00:00
watchdogTimer = new QTimer ( ) ;
connect ( watchdogTimer , & QTimer : : timeout , this , & udpAudio : : watchdog ) ;
watchdogTimer - > start ( WATCHDOG_PERIOD ) ;
2021-02-21 14:53:42 +00:00
txAudioTimer = new QTimer ( ) ;
txAudioTimer - > setTimerType ( Qt : : PreciseTimer ) ;
connect ( txAudioTimer , & QTimer : : timeout , this , & udpAudio : : sendTxAudio ) ;
2021-02-25 11:13:18 +00:00
areYouThereTimer = new QTimer ( ) ;
connect ( areYouThereTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , false , 0x03 , 0 ) ) ;
areYouThereTimer - > start ( AREYOUTHERE_PERIOD ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-05 17:40:58 +00:00
udpAudio : : ~ udpAudio ( )
{
2021-03-29 08:38:31 +00:00
if ( pingTimer ! = Q_NULLPTR )
{
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping pingTimer " ;
2021-03-29 08:38:31 +00:00
pingTimer - > stop ( ) ;
delete pingTimer ;
pingTimer = Q_NULLPTR ;
}
if ( idleTimer ! = Q_NULLPTR )
{
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping idleTimer " ;
2021-03-29 08:38:31 +00:00
idleTimer - > stop ( ) ;
delete idleTimer ;
idleTimer = Q_NULLPTR ;
}
if ( watchdogTimer ! = Q_NULLPTR )
{
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping watchdogTimer " ;
2021-03-29 08:38:31 +00:00
watchdogTimer - > stop ( ) ;
delete watchdogTimer ;
watchdogTimer = Q_NULLPTR ;
}
2021-02-21 14:53:42 +00:00
if ( txAudioTimer ! = Q_NULLPTR )
2021-02-13 23:25:24 +00:00
{
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping txaudio timer " ;
2021-02-21 14:53:42 +00:00
txAudioTimer - > stop ( ) ;
delete txAudioTimer ;
2021-02-13 23:25:24 +00:00
}
2021-03-13 09:23:06 +00:00
if ( rxAudioThread ! = Q_NULLPTR ) {
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping rxaudio thread " ;
2021-02-08 10:22:20 +00:00
rxAudioThread - > quit ( ) ;
rxAudioThread - > wait ( ) ;
2021-02-08 08:31:48 +00:00
}
2021-02-13 23:25:24 +00:00
2021-03-13 09:23:06 +00:00
if ( txAudioThread ! = Q_NULLPTR ) {
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " Stopping txaudio thread " ;
2021-02-11 19:18:35 +00:00
txAudioThread - > quit ( ) ;
txAudioThread - > wait ( ) ;
}
2021-05-22 16:02:51 +00:00
qDebug ( logUdp ( ) ) < < " udpHandler successfully closed " ;
2021-02-11 19:18:35 +00:00
}
2021-02-25 17:53:01 +00:00
void udpAudio : : watchdog ( )
{
static bool alerted = false ;
2021-05-17 15:03:53 +00:00
if ( lastReceived . msecsTo ( QTime : : currentTime ( ) ) > 2000 )
2021-02-25 17:53:01 +00:00
{
if ( ! alerted ) {
/* Just log it at the moment, maybe try signalling the control channel that it needs to
try requesting civ / audio again ? */
2021-05-31 16:55:01 +00:00
qInfo ( logUdp ( ) ) < < " Audio Watchdog: no audio data received for 2s, restart required? " ;
2021-02-25 17:53:01 +00:00
alerted = true ;
}
}
else
{
alerted = false ;
}
}
2021-02-12 20:42:56 +00:00
2021-02-12 23:56:02 +00:00
void udpAudio : : sendTxAudio ( )
2021-02-11 19:18:35 +00:00
{
2021-05-27 12:54:52 +00:00
if ( txaudio = = Q_NULLPTR ) {
return ;
}
QByteArray audio ;
txaudio - > getNextAudioChunk ( audio ) ;
2021-06-06 21:11:48 +00:00
2021-05-27 12:54:52 +00:00
if ( audio . length ( ) > 0 ) {
2021-02-20 20:19:18 +00:00
int counter = 1 ;
int len = 0 ;
2021-03-22 09:10:03 +00:00
2021-02-20 20:19:18 +00:00
while ( len < audio . length ( ) ) {
QByteArray partial = audio . mid ( len , 1364 ) ;
2021-02-28 20:10:07 +00:00
audio_packet p ;
2021-02-20 18:29:23 +00:00
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
2021-02-28 20:10:07 +00:00
p . len = sizeof ( p ) + partial . length ( ) ;
2021-02-20 18:29:23 +00:00
p . sentid = myId ;
p . rcvdid = remoteId ;
2021-03-09 17:22:16 +00:00
if ( partial . length ( ) = = 0xa0 ) {
p . ident = 0x9781 ;
}
else {
p . ident = 0x0080 ; // TX audio is always this?
}
2021-02-20 20:19:18 +00:00
p . datalen = ( quint16 ) qToBigEndian ( ( quint16 ) partial . length ( ) ) ;
2021-02-21 14:53:42 +00:00
p . sendseq = ( quint16 ) qToBigEndian ( ( quint16 ) sendAudioSeq ) ; // THIS IS BIG ENDIAN!
2021-02-20 18:29:23 +00:00
QByteArray tx = QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ;
2021-02-12 23:56:02 +00:00
tx . append ( partial ) ;
2021-02-20 20:19:18 +00:00
len = len + partial . length ( ) ;
2021-05-15 17:53:16 +00:00
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
2021-02-13 23:25:24 +00:00
sendTrackedPacket ( tx ) ;
2021-02-12 23:56:02 +00:00
sendAudioSeq + + ;
2021-02-20 20:19:18 +00:00
counter + + ;
2021-02-12 23:56:02 +00:00
}
2021-02-13 11:04:26 +00:00
}
2021-02-05 17:40:58 +00:00
}
2021-02-03 20:00:40 +00:00
2021-02-27 00:37:00 +00:00
void udpAudio : : changeLatency ( quint16 value )
2021-02-08 16:53:26 +00:00
{
2021-02-27 00:37:00 +00:00
emit haveChangeLatency ( value ) ;
2021-02-08 16:53:26 +00:00
}
2021-03-22 18:53:34 +00:00
void udpAudio : : setVolume ( unsigned char value )
{
emit haveSetVolume ( value ) ;
}
2021-02-12 20:42:56 +00:00
2021-02-13 23:25:24 +00:00
void udpAudio : : dataReceived ( )
2021-02-03 20:00:40 +00:00
{
while ( udp - > hasPendingDatagrams ( ) ) {
QNetworkDatagram datagram = udp - > receiveDatagram ( ) ;
2021-05-22 16:02:51 +00:00
//qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10);
2021-02-03 20:00:40 +00:00
QByteArray r = datagram . data ( ) ;
switch ( r . length ( ) )
{
2021-02-24 22:56:40 +00:00
case ( 16 ) : // Response to control packet handled in udpBase
{
2021-02-25 11:13:18 +00:00
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
2021-05-18 08:32:56 +00:00
if ( in - > type = = 0x04 & & enableTx )
2021-02-25 11:13:18 +00:00
{
txAudioTimer - > start ( TXAUDIO_PERIOD ) ;
}
2021-02-24 22:56:40 +00:00
break ;
}
default :
{
/* Audio packets start as follows:
PCM 16 bit and PCM8 / uLAW stereo : 0x44 , 0x02 for first packet and 0x6c , 0x05 for second .
uLAW 8 bit / PCM 8 bit 0xd8 , 0x03 for all packets
PCM 16 bit stereo 0x6c , 0x05 first & second 0x70 , 0x04 third
2021-02-09 12:43:28 +00:00
2021-02-24 22:56:40 +00:00
*/
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
2021-03-14 08:44:30 +00:00
2021-06-16 22:44:59 +00:00
if ( in - > type ! = 0x01 & & in - > len > = 0x20 ) {
2021-03-14 08:44:30 +00:00
if ( in - > seq = = 0 )
{
// Seq number has rolled over.
seqPrefix + + ;
}
2021-03-09 17:22:16 +00:00
// 0xac is the smallest possible audio packet.
lastReceived = QTime : : currentTime ( ) ;
audioPacket tempAudio ;
2021-03-14 08:44:30 +00:00
tempAudio . seq = ( quint32 ) seqPrefix < < 16 | in - > seq ;
2021-03-09 17:22:16 +00:00
tempAudio . time = lastReceived ;
tempAudio . sent = 0 ;
2021-05-27 10:41:08 +00:00
tempAudio . data = r . mid ( 0x18 ) ;
2021-03-09 17:22:16 +00:00
// Prefer signal/slot to forward audio as it is thread/safe
// Need to do more testing but latency appears fine.
2021-05-27 17:34:44 +00:00
//audioLatency = rxaudio->incomingAudio(tempAudio);
2021-05-27 16:59:01 +00:00
emit haveAudioData ( tempAudio ) ;
2021-05-27 17:34:44 +00:00
audioLatency = rxaudio - > getLatency ( ) ;
2021-02-24 22:56:40 +00:00
}
break ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
}
2021-02-27 00:37:00 +00:00
2021-02-13 23:25:24 +00:00
udpBase : : dataReceived ( r ) ; // Call parent function to process the rest.
2021-02-04 20:09:09 +00:00
r . clear ( ) ;
datagram . clear ( ) ;
2021-02-03 20:00:40 +00:00
}
}
2021-02-04 20:20:23 +00:00
void udpBase : : init ( )
2021-02-04 19:53:48 +00:00
{
2021-02-20 18:29:23 +00:00
timeStarted . start ( ) ;
2021-02-04 19:53:48 +00:00
udp = new QUdpSocket ( this ) ;
udp - > bind ( ) ; // Bind to random port.
localPort = udp - > localPort ( ) ;
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " UDP Stream bound to local port: " < < localPort < < " remote port: " < < port ;
2021-02-04 19:53:48 +00:00
uint32_t addr = localIP . toIPv4Address ( ) ;
2021-02-13 23:25:24 +00:00
myId = ( addr > > 8 & 0xff ) < < 24 | ( addr & 0xff ) < < 16 | ( localPort & 0xffff ) ;
2021-02-27 00:37:00 +00:00
retransmitTimer = new QTimer ( ) ;
connect ( retransmitTimer , & QTimer : : timeout , this , & udpBase : : sendRetransmitRequest ) ;
retransmitTimer - > start ( RETRANSMIT_PERIOD ) ;
2021-02-04 19:53:48 +00:00
}
2021-02-13 23:25:24 +00:00
2021-02-04 19:53:48 +00:00
udpBase : : ~ udpBase ( )
{
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < " Closing UDP stream : " < < radioIP . toString ( ) < < " : " < < port ;
2021-02-07 12:59:41 +00:00
if ( udp ! = Q_NULLPTR ) {
2021-02-18 15:54:26 +00:00
sendControl ( false , 0x05 , 0x00 ) ; // Send disconnect
2021-02-04 19:53:48 +00:00
udp - > close ( ) ;
delete udp ;
}
2021-02-25 17:53:01 +00:00
if ( areYouThereTimer ! = Q_NULLPTR )
{
areYouThereTimer - > stop ( ) ;
delete areYouThereTimer ;
}
2021-02-21 14:53:42 +00:00
if ( pingTimer ! = Q_NULLPTR )
2021-02-07 12:59:41 +00:00
{
2021-02-21 14:53:42 +00:00
pingTimer - > stop ( ) ;
delete pingTimer ;
2021-02-07 12:59:41 +00:00
}
2021-02-21 14:53:42 +00:00
if ( idleTimer ! = Q_NULLPTR )
2021-02-07 12:59:41 +00:00
{
2021-02-21 14:53:42 +00:00
idleTimer - > stop ( ) ;
delete idleTimer ;
2021-02-07 12:59:41 +00:00
}
2021-02-27 00:37:00 +00:00
if ( retransmitTimer ! = Q_NULLPTR )
{
retransmitTimer - > stop ( ) ;
delete retransmitTimer ;
}
2021-02-25 17:53:01 +00:00
2021-02-21 14:53:42 +00:00
pingTimer = Q_NULLPTR ;
idleTimer = Q_NULLPTR ;
2021-02-25 17:53:01 +00:00
areYouThereTimer = Q_NULLPTR ;
2021-02-27 00:37:00 +00:00
retransmitTimer = Q_NULLPTR ;
2021-02-21 14:53:42 +00:00
2021-02-04 19:53:48 +00:00
}
2021-02-03 20:00:40 +00:00
// Base class!
2021-02-13 23:25:24 +00:00
void udpBase : : dataReceived ( QByteArray r )
2021-02-04 18:52:00 +00:00
{
2021-02-22 22:25:09 +00:00
if ( r . length ( ) < 0x10 )
{
return ; // Packet too small do to anything with?
}
2021-02-04 18:52:00 +00:00
switch ( r . length ( ) )
{
2021-02-20 18:29:23 +00:00
case ( CONTROL_SIZE ) : // Empty response used for simple comms and retransmit requests.
{
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
if ( in - > type = = 0x01 )
2021-02-04 18:52:00 +00:00
{
2021-02-24 22:56:40 +00:00
// Single packet request
2021-02-22 22:25:09 +00:00
packetsLost + + ;
2021-05-17 15:03:53 +00:00
congestion + + ;
txBufferMutex . lock ( ) ;
QMap < quint16 , SEQBUFENTRY > : : iterator match = txSeqBuf . find ( in - > seq ) ;
2021-02-23 20:48:53 +00:00
if ( match ! = txSeqBuf . end ( ) ) {
2021-02-22 22:25:09 +00:00
// Found matching entry?
// Send "untracked" as it has already been sent once.
2021-02-23 20:48:53 +00:00
// Don't constantly retransmit the same packet, give-up eventually
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Sending retransmit of " < < hex < < match - > seqNum ;
2021-02-24 22:56:40 +00:00
match - > retransmitCount + + ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-24 22:56:40 +00:00
udp - > writeDatagram ( match - > data , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-20 18:29:23 +00:00
}
2021-05-17 15:03:53 +00:00
txBufferMutex . unlock ( ) ;
2021-02-04 18:52:00 +00:00
}
2021-02-24 22:56:40 +00:00
if ( in - > type = = 0x04 ) {
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Received I am here " ;
2021-02-24 22:56:40 +00:00
areYouThereCounter = 0 ;
// I don't think that we will ever receive an "I am here" other than in response to "Are you there?"
remoteId = in - > sentid ;
2021-02-25 11:13:18 +00:00
if ( areYouThereTimer ! = Q_NULLPTR & & areYouThereTimer - > isActive ( ) ) {
// send ping packets every second
areYouThereTimer - > stop ( ) ;
}
2021-02-24 22:56:40 +00:00
sendControl ( false , 0x06 , 0x01 ) ; // Send Are you ready - untracked.
}
else if ( in - > type = = 0x06 )
{
// Just get the seqnum and ignore the rest.
}
2021-02-20 20:27:35 +00:00
break ;
2021-02-04 18:52:00 +00:00
}
2021-02-20 18:29:23 +00:00
case ( PING_SIZE ) : // ping packet
2021-02-04 18:52:00 +00:00
{
2021-02-20 18:29:23 +00:00
ping_packet_t in = ( ping_packet_t ) r . constData ( ) ;
if ( in - > type = = 0x07 )
2021-02-04 18:52:00 +00:00
{
2021-02-20 18:29:23 +00:00
// It is a ping request/response
if ( in - > reply = = 0x00 )
{
ping_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . type = 0x07 ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . reply = 0x01 ;
p . seq = in - > seq ;
p . time = in - > time ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-20 18:29:23 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-20 18:29:23 +00:00
}
2021-02-22 22:25:09 +00:00
else if ( in - > reply = = 0x01 ) {
2021-02-20 18:29:23 +00:00
if ( in - > seq = = pingSendSeq )
{
// This is response to OUR request so increment counter
pingSendSeq + + ;
}
else {
2021-02-22 22:25:09 +00:00
// Not sure what to do here, need to spend more time with the protocol but will try sending ping with same seq next time.
2021-05-15 17:53:16 +00:00
//qInfo(logUdp()) << this->metaObject()->className() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << in->seq;
2021-02-20 18:29:23 +00:00
}
2021-02-13 23:25:24 +00:00
}
else {
2021-05-15 17:53:16 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " Unhandled response to ping. I have never seen this! 0x10= " < < r [ 16 ] ;
2021-02-13 23:25:24 +00:00
}
2021-02-20 18:29:23 +00:00
2021-02-15 19:28:17 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
}
default :
2021-02-22 22:25:09 +00:00
{
// All packets "should" be added to the incoming buffer.
// First check that we haven't already received it.
}
break ;
2021-02-04 18:52:00 +00:00
}
2021-02-22 22:25:09 +00:00
// All packets except ping and retransmit requests should trigger this.
control_packet_t in = ( control_packet_t ) r . constData ( ) ;
2021-02-24 22:56:40 +00:00
// This is a variable length retransmit request!
if ( in - > type = = 0x01 & & in - > len ! = 0x10 )
{
2021-05-17 15:03:53 +00:00
2021-02-24 22:56:40 +00:00
for ( quint16 i = 0x10 ; i < r . length ( ) ; i = i + 2 )
{
quint16 seq = ( quint8 ) r [ i ] | ( quint8 ) r [ i + 1 ] < < 8 ;
2021-05-17 15:03:53 +00:00
QMap < quint16 , SEQBUFENTRY > : : iterator match = txSeqBuf . find ( seq ) ;
2021-02-24 22:56:40 +00:00
if ( match = = txSeqBuf . end ( ) ) {
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Requested packet " < < hex < < seq < < " not found " ;
2021-02-24 22:56:40 +00:00
// Just send idle packet.
sendControl ( false , 0 , match - > seqNum ) ;
}
else {
// Found matching entry?
// Send "untracked" as it has already been sent once.
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Sending retransmit (range) of " < < hex < < match - > seqNum ;
2021-02-24 22:56:40 +00:00
match - > retransmitCount + + ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-24 22:56:40 +00:00
udp - > writeDatagram ( match - > data , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-24 22:56:40 +00:00
match + + ;
packetsLost + + ;
2021-05-17 15:03:53 +00:00
congestion + + ;
2021-02-24 22:56:40 +00:00
}
}
2021-02-27 00:37:00 +00:00
}
else if ( in - > len ! = PING_SIZE & & in - > type = = 0x00 & & in - > seq ! = 0x00 )
2021-02-22 22:25:09 +00:00
{
2021-05-17 15:03:53 +00:00
rxBufferMutex . lock ( ) ;
2021-02-24 22:56:40 +00:00
if ( rxSeqBuf . isEmpty ( ) ) {
2021-07-08 08:23:53 +00:00
if ( rxSeqBuf . size ( ) > 400 )
{
rxSeqBuf . erase ( rxSeqBuf . begin ( ) ) ;
}
2021-05-17 15:03:53 +00:00
rxSeqBuf . insert ( in - > seq , QTime : : currentTime ( ) ) ;
2021-02-24 22:56:40 +00:00
}
2021-02-27 00:37:00 +00:00
else
2021-02-22 22:25:09 +00:00
{
2021-05-17 15:03:53 +00:00
//std::sort(rxSeqBuf.begin(), rxSeqBuf.end());
if ( in - > seq < rxSeqBuf . firstKey ( ) )
2021-02-24 22:56:40 +00:00
{
2021-05-17 15:03:53 +00:00
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : ******* seq number has rolled over ****** previous highest: " < < hex < < rxSeqBuf . lastKey ( ) < < " current: " < < hex < < in - > seq ;
2021-03-14 08:44:30 +00:00
//seqPrefix++;
2021-02-24 22:56:40 +00:00
// Looks like it has rolled over so clear buffer and start again.
rxSeqBuf . clear ( ) ;
2021-05-17 15:03:53 +00:00
rxMissing . clear ( ) ;
rxBufferMutex . unlock ( ) ;
2021-02-27 00:37:00 +00:00
return ;
2021-02-24 22:56:40 +00:00
}
2021-02-22 22:25:09 +00:00
2021-02-24 22:56:40 +00:00
if ( ! rxSeqBuf . contains ( in - > seq ) )
{
2021-02-27 00:37:00 +00:00
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
2021-05-17 15:03:53 +00:00
rxSeqBuf . insert ( in - > seq , QTime : : currentTime ( ) ) ;
2021-07-08 08:23:53 +00:00
if ( rxSeqBuf . size ( ) > 400 )
{
rxSeqBuf . erase ( rxSeqBuf . begin ( ) ) ;
}
2021-05-17 15:03:53 +00:00
}
else {
// This is probably one of our missing packets!
missingMutex . lock ( ) ;
QMap < quint16 , int > : : iterator s = rxMissing . find ( in - > seq ) ;
2021-02-24 22:56:40 +00:00
if ( s ! = rxMissing . end ( ) )
{
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Missing SEQ has been received! " < < hex < < in - > seq ;
2021-02-24 22:56:40 +00:00
s = rxMissing . erase ( s ) ;
}
2021-05-17 15:03:53 +00:00
missingMutex . unlock ( ) ;
2021-02-24 22:56:40 +00:00
}
2021-02-27 00:37:00 +00:00
}
2021-05-17 15:03:53 +00:00
rxBufferMutex . unlock ( ) ;
2021-02-27 00:37:00 +00:00
}
}
2021-05-17 15:03:53 +00:00
bool missing ( quint16 i , quint16 j ) {
return ( i + 1 ! = j ) ;
}
2021-02-27 00:37:00 +00:00
void udpBase : : sendRetransmitRequest ( )
{
// Find all gaps in received packets and then send requests for them.
// This will run every 100ms so out-of-sequence packets will not trigger a retransmit request.
QByteArray missingSeqs ;
2021-05-17 15:03:53 +00:00
rxBufferMutex . lock ( ) ;
if ( ! rxSeqBuf . empty ( ) & & rxSeqBuf . size ( ) < = rxSeqBuf . lastKey ( ) - rxSeqBuf . firstKey ( ) )
{
2021-05-18 14:41:27 +00:00
if ( ( rxSeqBuf . lastKey ( ) - rxSeqBuf . firstKey ( ) - rxSeqBuf . size ( ) ) > 20 )
2021-02-27 00:37:00 +00:00
{
2021-05-18 14:41:27 +00:00
// Too many packets to process, flush buffers and start again!
qDebug ( logUdp ( ) ) < < " Too many missing packets, flushing buffer: " < < rxSeqBuf . lastKey ( ) < < " missing= " < < rxSeqBuf . lastKey ( ) - rxSeqBuf . firstKey ( ) - rxSeqBuf . size ( ) + 1 ;
rxMissing . clear ( ) ;
2021-05-17 15:03:53 +00:00
missingMutex . lock ( ) ;
2021-05-18 14:41:27 +00:00
rxSeqBuf . clear ( ) ;
missingMutex . unlock ( ) ;
}
else {
// We have at least 1 missing packet!
qDebug ( logUdp ( ) ) < < " Missing Seq: size= " < < rxSeqBuf . size ( ) < < " firstKey= " < < rxSeqBuf . firstKey ( ) < < " lastKey= " < < rxSeqBuf . lastKey ( ) < < " missing= " < < rxSeqBuf . lastKey ( ) - rxSeqBuf . firstKey ( ) - rxSeqBuf . size ( ) + 1 ;
// We are missing packets so iterate through the buffer and add the missing ones to missing packet list
for ( int i = 0 ; i < rxSeqBuf . keys ( ) . length ( ) - 1 ; i + + ) {
missingMutex . lock ( ) ;
for ( quint16 j = rxSeqBuf . keys ( ) [ i ] + 1 ; j < rxSeqBuf . keys ( ) [ i + 1 ] ; j + + ) {
auto s = rxMissing . find ( j ) ;
2021-02-27 00:37:00 +00:00
if ( s = = rxMissing . end ( ) )
2021-02-24 22:56:40 +00:00
{
2021-02-27 00:37:00 +00:00
// We haven't seen this missing packet before
2021-05-18 14:41:27 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : Adding to missing buffer (len= " < < rxMissing . size ( ) < < " ): " < < j ;
2021-07-08 08:23:53 +00:00
if ( rxMissing . size ( ) > 25 )
{
rxMissing . erase ( rxMissing . begin ( ) ) ;
}
2021-05-18 14:41:27 +00:00
rxMissing . insert ( j , 0 ) ;
2021-07-08 08:23:53 +00:00
if ( rxSeqBuf . size ( ) > 400 )
{
rxSeqBuf . erase ( rxSeqBuf . begin ( ) ) ;
}
2021-05-18 14:41:27 +00:00
rxSeqBuf . insert ( j , QTime : : currentTime ( ) ) ; // Add this missing packet to the rxbuffer as we now long about it.
2021-02-27 00:37:00 +00:00
packetsLost + + ;
}
else {
2021-05-18 14:41:27 +00:00
if ( s . value ( ) = = 4 )
2021-02-24 22:56:40 +00:00
{
2021-02-27 00:37:00 +00:00
// We have tried 4 times to request this packet, time to give up!
s = rxMissing . erase ( s ) ;
2021-02-24 22:56:40 +00:00
}
2021-02-27 00:37:00 +00:00
2021-02-24 22:56:40 +00:00
}
2021-02-24 11:00:23 +00:00
}
2021-05-18 14:41:27 +00:00
missingMutex . unlock ( ) ;
2021-02-27 00:37:00 +00:00
}
}
}
2021-05-17 15:03:53 +00:00
rxBufferMutex . unlock ( ) ;
2021-02-22 22:25:09 +00:00
2021-05-17 15:03:53 +00:00
missingMutex . lock ( ) ;
2021-02-27 00:37:00 +00:00
for ( auto it = rxMissing . begin ( ) ; it ! = rxMissing . end ( ) ; + + it )
{
2021-05-17 15:03:53 +00:00
if ( it . value ( ) < 10 )
2021-02-27 00:37:00 +00:00
{
2021-05-17 15:03:53 +00:00
missingSeqs . append ( it . key ( ) & 0xff ) ;
missingSeqs . append ( it . key ( ) > > 8 & 0xff ) ;
missingSeqs . append ( it . key ( ) & 0xff ) ;
missingSeqs . append ( it . key ( ) > > 8 & 0xff ) ;
it . value ( ) + + ;
2021-02-27 00:37:00 +00:00
}
}
2021-05-17 15:03:53 +00:00
missingMutex . unlock ( ) ;
2021-02-27 00:37:00 +00:00
if ( missingSeqs . length ( ) ! = 0 )
{
control_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . type = 0x01 ;
p . seq = 0x0000 ;
p . sentid = myId ;
p . rcvdid = remoteId ;
if ( missingSeqs . length ( ) = = 4 ) // This is just a single missing packet so send using a control.
{
p . seq = ( missingSeqs [ 0 ] & 0xff ) | ( quint16 ) ( missingSeqs [ 1 ] < < 8 ) ;
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : sending request for missing packet : " < < hex < < p . seq ;
udpMutex . lock ( ) ;
2021-02-27 00:37:00 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-27 00:37:00 +00:00
}
else
{
2021-05-17 15:03:53 +00:00
qDebug ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : sending request for multiple missing packets : " < < missingSeqs . toHex ( ) ;
missingMutex . lock ( ) ;
2021-02-27 00:37:00 +00:00
missingSeqs . insert ( 0 , p . packet , sizeof ( p . packet ) ) ;
2021-05-17 15:03:53 +00:00
missingMutex . unlock ( ) ;
udpMutex . lock ( ) ;
2021-02-27 00:37:00 +00:00
udp - > writeDatagram ( missingSeqs , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-22 22:25:09 +00:00
}
}
2021-02-27 00:37:00 +00:00
2021-02-04 18:52:00 +00:00
}
2021-02-27 00:37:00 +00:00
2021-02-18 15:54:26 +00:00
// Used to send idle and other "control" style messages
2021-05-17 15:03:53 +00:00
void udpBase : : sendControl ( bool tracked = true , quint8 type = 0 , quint16 seq = 0 )
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
control_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . type = type ;
p . sentid = myId ;
p . rcvdid = remoteId ;
2021-02-03 20:00:40 +00:00
2021-02-04 13:12:08 +00:00
if ( ! tracked ) {
2021-02-20 18:29:23 +00:00
p . seq = seq ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-20 18:29:23 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-04 13:12:08 +00:00
}
else {
2021-02-20 18:29:23 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-04 13:12:08 +00:00
}
2021-02-03 20:00:40 +00:00
return ;
}
2021-02-13 23:25:24 +00:00
// Send periodic ping packets
void udpBase : : sendPing ( )
2021-02-03 20:00:40 +00:00
{
2021-02-20 18:29:23 +00:00
ping_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . type = 0x07 ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . seq = pingSendSeq ;
p . time = timeStarted . msecsSinceStartOfDay ( ) ;
2021-02-13 23:25:24 +00:00
lastPingSentTime = QDateTime : : currentDateTime ( ) ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-20 18:29:23 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-22 22:25:09 +00:00
return ;
}
2021-05-17 15:03:53 +00:00
void udpBase : : sendRetransmitRange ( quint16 first , quint16 second , quint16 third , quint16 fourth )
2021-02-22 22:25:09 +00:00
{
retransmit_range_packet p ;
memset ( p . packet , 0x0 , sizeof ( p ) ) ; // We can't be sure it is initialized with 0x00!
p . len = sizeof ( p ) ;
p . type = 0x00 ;
p . sentid = myId ;
p . rcvdid = remoteId ;
p . first = first ;
2021-02-24 11:00:23 +00:00
p . second = second ;
p . third = third ;
p . fourth = fourth ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-22 22:25:09 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-03 20:00:40 +00:00
return ;
}
2021-02-13 23:25:24 +00:00
void udpBase : : sendTrackedPacket ( QByteArray d )
2021-02-03 20:00:40 +00:00
{
2021-05-13 22:15:03 +00:00
// As the radio can request retransmission of these packets, store them in a buffer
2021-02-03 20:00:40 +00:00
d [ 6 ] = sendSeq & 0xff ;
d [ 7 ] = ( sendSeq > > 8 ) & 0xff ;
2021-02-04 13:12:08 +00:00
SEQBUFENTRY s ;
s . seqNum = sendSeq ;
2021-02-24 22:56:40 +00:00
s . timeSent = QTime : : currentTime ( ) ;
2021-02-24 11:00:23 +00:00
s . retransmitCount = 0 ;
2021-02-24 22:56:40 +00:00
s . data = d ;
2021-05-22 13:58:52 +00:00
if ( txBufferMutex . tryLock ( 100 ) )
{
2021-05-17 15:03:53 +00:00
2021-05-22 13:58:52 +00:00
if ( sendSeq = = 0 ) {
// We are either the first ever sent packet or have rolled-over so clear the buffer.
txSeqBuf . clear ( ) ;
congestion = 0 ;
}
txSeqBuf . insert ( sendSeq , s ) ;
2021-07-08 08:23:53 +00:00
if ( txSeqBuf . size ( ) > 400 )
{
txSeqBuf . erase ( txSeqBuf . begin ( ) ) ;
}
2021-05-22 13:58:52 +00:00
txBufferMutex . unlock ( ) ;
} else {
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : txBuffer mutex is locked " ;
2021-05-17 15:03:53 +00:00
}
2021-07-08 08:23:53 +00:00
// Stop using purgeOldEntries() as it is likely slower than just removing the earliest packet.
//qInfo(logUdp()) << this->metaObject()->className() << "RX:" << rxSeqBuf.size() << "TX:" <<txSeqBuf.size() << "MISS:" << rxMissing.size();
//purgeOldEntries(); // Delete entries older than PURGE_SECONDS seconds (currently 5)
2021-02-03 20:00:40 +00:00
sendSeq + + ;
2021-05-17 15:03:53 +00:00
udpMutex . lock ( ) ;
2021-02-13 23:25:24 +00:00
udp - > writeDatagram ( d , radioIP , port ) ;
2021-05-17 15:03:53 +00:00
if ( congestion > 10 ) { // Poor quality connection?
udp - > writeDatagram ( d , radioIP , port ) ;
if ( congestion > 20 ) // Even worse so send again.
udp - > writeDatagram ( d , radioIP , port ) ;
}
2021-02-21 14:53:42 +00:00
if ( idleTimer ! = Q_NULLPTR & & idleTimer - > isActive ( ) ) {
idleTimer - > start ( IDLE_PERIOD ) ; // Reset idle counter if it's running
2021-02-13 23:25:24 +00:00
}
2021-05-17 15:03:53 +00:00
udpMutex . unlock ( ) ;
2021-02-14 10:40:47 +00:00
packetsSent + + ;
2021-02-13 23:25:24 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-24 22:56:40 +00:00
/// <summary>
/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use.
/// </summary>
2021-02-13 23:25:24 +00:00
void udpBase : : purgeOldEntries ( )
2021-02-03 20:00:40 +00:00
{
2021-02-24 22:56:40 +00:00
// Erase old entries from the tx packet buffer
2021-05-22 13:58:52 +00:00
if ( txBufferMutex . tryLock ( 100 ) )
2021-02-26 16:53:11 +00:00
{
2021-05-22 13:58:52 +00:00
if ( ! txSeqBuf . isEmpty ( ) )
{
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
for ( auto it = txSeqBuf . begin ( ) ; it ! = txSeqBuf . end ( ) ; ) {
if ( it . value ( ) . timeSent . secsTo ( QTime : : currentTime ( ) ) > PURGE_SECONDS ) {
txSeqBuf . erase ( it + + ) ;
}
else {
break ;
}
2021-05-17 15:03:53 +00:00
}
}
2021-05-22 13:58:52 +00:00
txBufferMutex . unlock ( ) ;
2021-02-23 20:48:53 +00:00
2021-05-22 13:58:52 +00:00
} else {
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : txBuffer mutex is locked " ;
2021-02-26 16:53:11 +00:00
}
2021-02-24 22:56:40 +00:00
2021-02-25 17:53:01 +00:00
2021-05-22 13:58:52 +00:00
if ( rxBufferMutex . tryLock ( 100 ) )
{
if ( ! rxSeqBuf . isEmpty ( ) ) {
// Loop through the earliest items in the buffer and delete if older than PURGE_SECONDS
for ( auto it = rxSeqBuf . begin ( ) ; it ! = rxSeqBuf . end ( ) ; ) {
if ( it . value ( ) . secsTo ( QTime : : currentTime ( ) ) > PURGE_SECONDS ) {
rxSeqBuf . erase ( it + + ) ;
}
else {
break ;
}
}
2021-05-17 15:03:53 +00:00
}
2021-05-22 13:58:52 +00:00
rxBufferMutex . unlock ( ) ;
} else {
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : rxBuffer mutex is locked " ;
2021-05-17 15:03:53 +00:00
}
2021-02-25 17:53:01 +00:00
2021-05-22 13:58:52 +00:00
if ( missingMutex . tryLock ( 100 ) )
{
// Erase old entries from the missing packets buffer
if ( ! rxMissing . isEmpty ( ) & & rxMissing . size ( ) > 50 ) {
for ( size_t i = 0 ; i < 25 ; + + i ) {
rxMissing . erase ( rxMissing . begin ( ) ) ;
}
2021-02-26 16:53:11 +00:00
}
2021-05-22 13:58:52 +00:00
missingMutex . unlock ( ) ;
} else {
qInfo ( logUdp ( ) ) < < this - > metaObject ( ) - > className ( ) < < " : missingBuffer mutex is locked " ;
2021-02-22 22:25:09 +00:00
}
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
/// <summary>
/// passcode function used to generate secure (ish) code
/// </summary>
/// <param name="str"></param>
/// <returns>pointer to encoded username or password</returns>
2021-02-15 19:28:17 +00:00
void passcode ( QString in , QByteArray & out )
2021-02-03 20:00:40 +00:00
{
2021-02-09 12:43:28 +00:00
const quint8 sequence [ ] =
2021-02-03 20:00:40 +00:00
{
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
0x47 , 0x5d , 0x4c , 0x42 , 0x66 , 0x20 , 0x23 , 0x46 , 0x4e , 0x57 , 0x45 , 0x3d , 0x67 , 0x76 , 0x60 , 0x41 , 0x62 , 0x39 , 0x59 , 0x2d , 0x68 , 0x7e ,
0x7c , 0x65 , 0x7d , 0x49 , 0x29 , 0x72 , 0x73 , 0x78 , 0x21 , 0x6e , 0x5a , 0x5e , 0x4a , 0x3e , 0x71 , 0x2c , 0x2a , 0x54 , 0x3c , 0x3a , 0x63 , 0x4f ,
0x43 , 0x75 , 0x27 , 0x79 , 0x5b , 0x35 , 0x70 , 0x48 , 0x6b , 0x56 , 0x6f , 0x34 , 0x32 , 0x6c , 0x30 , 0x61 , 0x6d , 0x7b , 0x2f , 0x4b , 0x64 , 0x38 ,
0x2b , 0x2e , 0x50 , 0x40 , 0x3f , 0x55 , 0x33 , 0x37 , 0x25 , 0x77 , 0x24 , 0x26 , 0x74 , 0x6a , 0x28 , 0x53 , 0x4d , 0x69 , 0x22 , 0x5c , 0x44 , 0x31 ,
0x36 , 0x58 , 0x3b , 0x7a , 0x51 , 0x5f , 0x52 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
} ;
2021-02-15 19:28:17 +00:00
QByteArray ba = in . toLocal8Bit ( ) ;
2021-02-03 20:00:40 +00:00
uchar * ascii = ( uchar * ) ba . constData ( ) ;
2021-02-15 19:28:17 +00:00
for ( int i = 0 ; i < in . length ( ) & & i < 16 ; i + + )
2021-02-03 20:00:40 +00:00
{
int p = ascii [ i ] + i ;
2021-02-05 17:40:58 +00:00
if ( p > 126 )
{
2021-02-03 20:00:40 +00:00
p = 32 + p % 127 ;
2021-02-05 17:40:58 +00:00
}
2021-02-15 19:28:17 +00:00
out . append ( sequence [ p ] ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-15 19:28:17 +00:00
return ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
/// <summary>
/// returns a QByteArray of a null terminated string
/// </summary>
/// <param name="c"></param>
/// <param name="s"></param>
/// <returns></returns>
QByteArray parseNullTerminatedString ( QByteArray c , int s )
2021-02-03 20:00:40 +00:00
{
2021-02-07 17:40:38 +00:00
//QString res = "";
QByteArray res ;
2021-02-03 20:00:40 +00:00
for ( int i = s ; i < c . length ( ) ; i + + )
{
if ( c [ i ] ! = ' \0 ' )
2021-02-05 17:40:58 +00:00
{
2021-02-07 17:40:38 +00:00
res . append ( c [ i ] ) ;
2021-02-05 17:40:58 +00:00
}
2021-02-03 20:00:40 +00:00
else
2021-02-05 17:40:58 +00:00
{
2021-02-03 20:00:40 +00:00
break ;
2021-02-05 17:40:58 +00:00
}
2021-02-03 20:00:40 +00:00
}
return res ;
2021-02-04 06:00:13 +00:00
}
2021-02-14 18:32:58 +00:00