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-13 23:25:24 +00:00
udpHandler : : udpHandler ( QString ip , quint16 controlPort , quint16 civPort , quint16 audioPort , QString username , QString password ,
quint16 buffer , quint16 rxsample , quint8 rxcodec , quint16 txsample , quint8 txcodec ) :
controlPort ( controlPort ) ,
civPort ( civPort ) ,
audioPort ( audioPort )
2021-02-03 20:00:40 +00:00
{
2021-02-07 18:46:47 +00:00
2021-02-13 23:25:24 +00:00
this - > port = this - > controlPort ;
2021-02-03 20:00:40 +00:00
this - > username = username ;
this - > password = password ;
2021-02-09 12:43:28 +00:00
this - > rxBufferSize = buffer ;
this - > rxSampleRate = rxsample ;
this - > txSampleRate = txsample ;
this - > rxCodec = rxcodec ;
this - > txCodec = txcodec ;
2021-02-13 23:25:24 +00:00
qDebug ( ) < < " Starting udpHandler user: " < < username < < " buffer: " < < buffer < < " rx sample rate: " < < rxsample < <
" rx codec: " < < rxcodec < < " tx sample rate: " < < txsample < < " tx codec: " < < txcodec ;
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.
if ( ! radioIP . setAddress ( ip ) )
{
QHostInfo remote = QHostInfo : : fromName ( ip ) ;
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 ;
qDebug ( ) < < " Got IP Address : " < < ip < < " : " < < addr . toString ( ) ;
break ;
}
2021-02-07 18:46:47 +00:00
}
2021-02-07 19:09:19 +00:00
if ( radioIP . isNull ( ) )
{
qDebug ( ) < < " Error obtaining IP Address for : " < < ip < < " : " < < 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-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
*/
connect ( & tokenTimer , & QTimer : : timeout , this , std : : bind ( & udpHandler : : sendToken , this , 0x05 ) ) ;
2021-02-14 07:53:55 +00:00
connect ( & areYouThereTimer , & QTimer : : timeout , this , QOverload < > : : of ( & udpHandler : : sendAreYouThere ) ) ;
2021-02-13 23:25:24 +00:00
connect ( & pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
2021-02-18 15:54:26 +00:00
connect ( & idleTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , true , 0 , 0 ) ) ;
2021-02-04 19:53:48 +00:00
2021-02-13 23:25:24 +00:00
// Start sending are you there packets - will be stopped once "I am here" received
2021-02-14 07:53:55 +00:00
areYouThereTimer . start ( AREYOUTHERE_PERIOD ) ;
2021-02-03 20:00:40 +00:00
2021-02-13 23:25:24 +00:00
// Set my computer name. Should this be configurable?
2021-02-07 17:40:38 +00:00
compName = QString ( " wfview " ) . toUtf8 ( ) ;
2021-02-13 23:25:24 +00:00
2021-02-03 20:00:40 +00:00
}
udpHandler : : ~ udpHandler ( )
{
2021-02-13 23:25:24 +00:00
if ( isAuthenticated ) {
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-02-03 20:00:40 +00:00
2021-02-13 23:25:24 +00:00
qDebug ( ) < < " Sending token removal packet " ;
sendToken ( 0x01 ) ;
2021-02-03 20:00:40 +00:00
}
}
2021-02-08 16:53:26 +00:00
void udpHandler : : changeBufferSize ( quint16 value )
{
emit haveChangeBufferSize ( 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 ) ;
}
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 ( ) ) {
lastReceived = time ( 0 ) ;
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!
if ( areYouThereTimer . isActive ( ) ) {
areYouThereTimer . stop ( ) ;
// send ping packets every second
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-02-20 18:29:23 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Received I am ready " ;
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 ;
quint32 totallost = packetsLost / 2 ;
if ( audio ! = Q_NULLPTR ) {
totalsent = totalsent + audio - > packetsSent ;
totallost = totallost + audio - > packetsLost / 2 ;
}
if ( civ ! = Q_NULLPTR ) {
totalsent = totalsent + civ - > packetsSent ;
totallost = totallost + civ - > packetsLost / 2 ;
}
//double perclost = 1.0 * totallost / totalsent * 100.0 ;
emit haveNetworkStatus ( " rtt: " + QString : : number ( latency ) + " ms, loss: ( " + QString : : number ( packetsLost ) + " / " + QString : : number ( packetsSent ) + " ) " ) ;
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 ( ) ;
if ( in - > res = = 0x05 )
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-20 18:29:23 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Token renewal successful " ;
tokenTimer . start ( TOKEN_RENEWAL ) ;
gotAuthOK = true ;
if ( ! streamOpened )
{
sendRequestStream ( ) ;
}
}
else if ( in - > response = = 0xffffffff )
{
qWarning ( ) < < this - > metaObject ( ) - > className ( ) < < " : Radio rejected token renewal, performing login " ;
remoteId = in - > sentid ;
isAuthenticated = false ;
sendLogin ( ) ; // Try sending login packet (didn't seem to work?)
}
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 ( ) ;
if ( in - > error = = 0x00ffffff & & ! streamOpened )
{
emit haveNetworkError ( radioIP . toString ( ) , " Auth failed, try rebooting the radio. " ) ;
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Auth failed, try rebooting the radio. " ;
}
else if ( in - > error = = 0x00000000 & & in - > disc = = 0x01 )
2021-02-05 17:40:58 +00:00
{
2021-02-04 19:53:48 +00:00
emit haveNetworkError ( radioIP . toString ( ) , " Got radio disconnected. " ) ;
2021-02-14 07:15:49 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Got radio disconnected. " ;
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 ( ) ;
if ( in - > error = = 0xfeffffff )
2021-02-03 20:00:40 +00:00
{
2021-02-13 23:25:24 +00:00
emit haveNetworkStatus ( " Invalid Username/Password " ) ;
2021-02-14 07:15:49 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Invalid Username/Password " ;
2021-02-03 20:00:40 +00:00
}
else if ( ! isAuthenticated )
{
2021-02-20 18:29:23 +00:00
if ( in - > tokrequest = = tokRequest )
{
emit haveNetworkStatus ( " Radio Login OK! " ) ;
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Received matching token response to our request " ;
token = in - > token ;
sendToken ( 0x02 ) ;
tokenTimer . start ( TOKEN_RENEWAL ) ; // Start token request timer
isAuthenticated = true ;
}
else
{
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Token response did not match, sent: " < < tokRequest < < " got " < < in - > tokrequest ;
}
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
if ( ! strcmp ( in - > connection , " FTTH " ) )
2021-02-14 07:53:55 +00:00
{
highBandwidthConnection = true ;
}
2021-02-20 18:29:23 +00:00
2021-02-14 07:53:55 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Detected connection speed " < < QString : : fromUtf8 ( parseNullTerminatedString ( r , 0x40 ) ) ;
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 ( ) ;
if ( ! streamOpened & & ! in - > ready )
2021-02-07 17:40:38 +00:00
{
2021-02-20 18:29:23 +00:00
devName = parseNullTerminatedString ( in - > computer , 0x00 ) ;
QHostAddress ip = QHostAddress ( in - > ipaddress ) ;
if ( ! ip . isEqual ( QHostAddress ( " 0.0.0.0 " ) ) & & parseNullTerminatedString ( r , 0x64 ) ! = compName ) // || ip != localIP ) // TODO: More testing of IP address detection code!
{
emit haveNetworkStatus ( QString : : fromUtf8 ( devName ) + " in use by: " + QString : : fromUtf8 ( parseNullTerminatedString ( r , 0x64 ) ) + " ( " + ip . toString ( ) + " ) " ) ;
sendControl ( false , 0x00 , in - > seq ) ; // Respond with an idle
}
else {
emit haveNetworkStatus ( QString : : fromUtf8 ( devName ) + " available " ) ;
2021-02-05 20:26:18 +00:00
2021-02-20 18:29:23 +00:00
identa = in - > identa ;
identb = in - > identb ;
2021-02-03 20:00:40 +00:00
2021-02-20 18:29:23 +00:00
sendRequestStream ( ) ;
}
}
else if ( ! streamOpened & & in - > ready )
{
devName = parseNullTerminatedString ( in - > computer , 0 ) ;
civ = new udpCivData ( localIP , radioIP , civPort ) ;
audio = new udpAudio ( localIP , radioIP , audioPort , rxBufferSize , rxSampleRate , rxCodec , txSampleRate , txCodec ) ;
2021-02-13 23:25:24 +00:00
QObject : : connect ( civ , SIGNAL ( receive ( QByteArray ) ) , this , SLOT ( receiveFromCivStream ( QByteArray ) ) ) ;
2021-02-08 16:53:26 +00:00
QObject : : connect ( this , SIGNAL ( haveChangeBufferSize ( quint16 ) ) , audio , SLOT ( changeBufferSize ( quint16 ) ) ) ;
2021-02-20 18:29:23 +00:00
streamOpened = true ;
2021-02-07 17:40:38 +00:00
emit haveNetworkStatus ( QString : : fromUtf8 ( devName ) ) ;
2021-02-20 18:29:23 +00:00
2021-02-13 23:25:24 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " Got serial and audio request success, device name: " < < QString : : fromUtf8 ( devName ) ;
2021-02-20 18:29:23 +00:00
2021-02-07 17:40:38 +00:00
// Stuff can change in the meantime because of a previous login...
2021-02-20 18:29:23 +00:00
remoteId = in - > sentid ;
myId = in - > rcvdid ;
tokRequest = in - > tokrequest ;
token = in - > token ;
}
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 ( ) ;
audioType = parseNullTerminatedString ( in - > audio , 0 ) ;
devName = parseNullTerminatedString ( in - > name , 0 ) ;
//replyId = r.mid(0x42, 16);
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " Received radio capabilities, Name: " < <
QString : : fromUtf8 ( devName ) < < " Audio: " < <
QString : : fromUtf8 ( audioType ) ;
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
int txSeqBufLengthMs = 300 ;
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 ;
p . resb = 0x8010 ;
p . identa = identa ;
p . identb = identb ;
p . innerseq = authInnerSendSeq ;
p . tokrequest = tokRequest ;
p . token = token ;
memcpy ( & p . name , devName . constData ( ) , devName . length ( ) ) ;
p . rxenable = 1 ;
p . txenable = 1 ;
p . rxcodec = rxCodec ;
p . txcodec = txCodec ;
memcpy ( & p . ident , QByteArrayLiteral ( " &96D7 " ) . constData ( ) , 5 ) ;
p . rxsample = qToBigEndian ( ( quint32 ) rxSampleRate ) ;
p . txsample = qToBigEndian ( ( quint32 ) txSampleRate ) ;
p . civport = qToBigEndian ( ( quint32 ) civPort ) ;
p . audioport = qToBigEndian ( ( quint32 ) audioPort ) ;
p . txbuffer = qToBigEndian ( ( quint32 ) txSeqBufLengthMs ) ;
2021-02-14 15:30:34 +00:00
2021-02-03 20:00:40 +00:00
authInnerSendSeq + + ;
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 )
{
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Radio not responding. " ;
emit haveNetworkStatus ( " Radio not responding! " ) ;
}
2021-02-18 15:54:26 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Sending Are You There... " ;
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-02-13 23:25:24 +00:00
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Sending login packet " ;
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?
p . innerseq = authInnerSendSeq ;
p . tokrequest = tokRequest ;
memcpy ( p . username , usernameEncoded . constData ( ) , usernameEncoded . length ( ) ) ;
memcpy ( p . password , passwordEncoded . constData ( ) , passwordEncoded . length ( ) ) ;
memcpy ( p . name , compName . constData ( ) , compName . length ( ) ) ;
2021-02-03 20:00:40 +00:00
authInnerSendSeq + + ;
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-14 16:23:25 +00:00
qDebug ( ) < < 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 ;
p . innerseq = authInnerSendSeq ;
p . tokrequest = tokRequest ;
p . token = token ;
2021-02-14 15:30:34 +00:00
2021-02-03 20:00:40 +00:00
authInnerSendSeq + + ;
2021-02-20 18:29:23 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) ) ;
2021-02-15 19:28:17 +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-02-13 23:25:24 +00:00
qDebug ( ) < < " 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
*/
connect ( & pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
2021-02-18 15:54:26 +00:00
connect ( & idleTimer , & QTimer : : timeout , this , std : : bind ( & udpBase : : sendControl , this , true , 0 , 0 ) ) ;
2021-02-13 23:25:24 +00:00
// send ping packets every 100 ms (maybe change to less frequent?)
2021-02-14 07:53:55 +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-14 07:53:55 +00:00
idleTimer . start ( IDLE_PERIOD ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-13 23:25:24 +00:00
udpCivData : : ~ udpCivData ( ) {
sendOpenClose ( true ) ;
}
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
{
// qDebug() << "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!
p . len = sizeof ( p ) ;
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
{
uint8_t magic = 0x05 ;
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-18 15:54:26 +00:00
quint8 p [ OPENCLOSE_SIZE ] ;
memset ( p , 0x0 , sizeof ( p ) ) ;
qToLittleEndian ( ( quint16 ) sizeof ( p ) , p + 0x00 ) ;
qToLittleEndian ( myId , p + 0x08 ) ;
qToLittleEndian ( remoteId , p + 0x0c ) ;
memcpy ( p + 0x10 , QByteArrayLiteral ( " \xc0 \x01 " ) . constData ( ) , 2 ) ;
qToLittleEndian ( sendSeqB , p + 0x13 ) ;
p [ 0x15 ] = magic ;
2021-02-03 20:00:40 +00:00
sendSeqB + + ;
2021-02-13 23:25:24 +00:00
sendTrackedPacket ( QByteArray : : fromRawData ( ( const char * ) p , sizeof ( p ) ) ) ;
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 ( ) ;
//qDebug() << "Received: " << datagram.data();
QByteArray r = datagram . data ( ) ;
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 ( ) ;
if ( in - > type = = 0x06 )
{
// Update remoteId
remoteId = in - > sentid ;
sendOpenClose ( false ) ;
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 :
{
if ( r . length ( ) > 21 ) {
// First check if we are missing any packets?
uint16_t gotSeq = qFromLittleEndian < quint16 > ( r . mid ( 6 , 2 ) ) ;
if ( lastReceivedSeq = = 0 | | lastReceivedSeq > gotSeq ) {
lastReceivedSeq = gotSeq ;
}
2021-02-05 20:26:18 +00:00
2021-02-20 18:29:23 +00:00
for ( uint16_t f = lastReceivedSeq + 1 ; f < gotSeq ; f + + ) {
// Do we need to request a retransmit?
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Missing Sequence: ( " < < r . length ( ) < < " ) " < < f ;
}
2021-02-05 20:26:18 +00:00
2021-02-14 16:14:56 +00:00
2021-02-20 18:29:23 +00:00
lastReceivedSeq = gotSeq ;
2021-02-05 20:26:18 +00:00
2021-02-20 18:29:23 +00:00
quint8 temp = r [ 0 ] - 0x15 ;
if ( ( quint8 ) r [ 16 ] = = 0xc1 & & ( quint8 ) r [ 17 ] = = temp )
{
emit receive ( r . mid ( 0x15 ) ) ;
}
}
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
}
}
// Audio stream
2021-02-13 23:25:24 +00:00
udpAudio : : udpAudio ( QHostAddress local , QHostAddress ip , quint16 audioPort , quint16 buffer , quint16 rxsample , quint8 rxcodec , quint16 txsample , quint8 txcodec )
2021-02-03 20:00:40 +00:00
{
qDebug ( ) < < " 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 ;
this - > bufferSize = buffer ;
this - > rxSampleRate = rxsample ;
this - > txSampleRate = txsample ;
this - > rxCodec = rxcodec ;
this - > txCodec = txcodec ;
2021-02-03 20:00:40 +00:00
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-12 14:28:55 +00:00
/*
0x72 is RX audio codec
0x73 is TX audio codec ( only single channel options )
0x01 uLaw 1 ch 8 bit
0x02 PCM 1 ch 8 bit
0x04 PCM 1 ch 16 bit
0x08 PCM 2 ch 8 bit
0x10 PCM 2 ch 16 bit
0x20 uLaw 2 ch 8 bit
*/
if ( rxCodec = = 0x01 | | rxCodec = = 0x20 ) {
2021-02-09 12:43:28 +00:00
rxIsUlawCodec = true ;
2021-02-12 14:28:55 +00:00
}
if ( rxCodec = = 0x08 | | rxCodec = = 0x10 | | rxCodec = = 0x20 ) {
2021-02-09 12:43:28 +00:00
rxChannelCount = 2 ;
2021-02-12 14:28:55 +00:00
}
if ( rxCodec = = 0x04 | | rxCodec = = 0x10 ) {
rxNumSamples = 16 ;
}
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-02-12 11:04:42 +00:00
connect ( this , SIGNAL ( setupRxAudio ( quint8 , quint8 , quint16 , quint16 , bool , bool ) ) , rxaudio , SLOT ( init ( quint8 , quint8 , quint16 , quint16 , bool , bool ) ) ) ;
2021-02-12 23:56:02 +00:00
//connect(this, SIGNAL(haveAudioData(QByteArray)), rxaudio, SLOT(incomingAudio(QByteArray)));
2021-02-08 16:53:26 +00:00
connect ( this , SIGNAL ( haveChangeBufferSize ( quint16 ) ) , rxaudio , SLOT ( changeBufferSize ( quint16 ) ) ) ;
2021-02-08 10:22:20 +00:00
connect ( rxAudioThread , SIGNAL ( finished ( ) ) , rxaudio , SLOT ( deleteLater ( ) ) ) ;
2021-02-08 08:31:48 +00:00
2021-02-11 19:18:35 +00:00
if ( txCodec = = 0x01 )
txIsUlawCodec = true ;
2021-02-12 14:28:55 +00:00
else if ( txCodec = = 0x04 )
txNumSamples = 16 ;
2021-02-11 19:18:35 +00:00
txChannelCount = 1 ; // Only 1 channel is supported.
txaudio = new audioHandler ( ) ;
txAudioThread = new QThread ( this ) ;
txaudio - > moveToThread ( txAudioThread ) ;
connect ( this , SIGNAL ( setupTxAudio ( quint8 , quint8 , quint16 , quint16 , bool , bool ) ) , txaudio , SLOT ( init ( quint8 , quint8 , quint16 , quint16 , bool , bool ) ) ) ;
2021-02-12 23:56:02 +00:00
//connect(txaudio, SIGNAL(haveAudioData(QByteArray)), this, SLOT(sendTxAudio(QByteArray)));
2021-02-11 19:18:35 +00:00
connect ( txAudioThread , SIGNAL ( finished ( ) ) , txaudio , SLOT ( deleteLater ( ) ) ) ;
2021-02-08 11:14:17 +00:00
rxAudioThread - > start ( ) ;
2021-02-11 19:18:35 +00:00
txAudioThread - > start ( ) ;
2021-02-18 15:54:26 +00:00
sendControl ( false , 0x03 , 0x00 ) ; // First connect packet
2021-02-13 23:25:24 +00:00
connect ( & pingTimer , & QTimer : : timeout , this , & udpBase : : sendPing ) ;
2021-02-14 07:53:55 +00:00
pingTimer . start ( PING_PERIOD ) ; // send ping packets every 100ms
2021-02-13 23:25:24 +00:00
connect ( & txAudioTimer , & QTimer : : timeout , this , & udpAudio : : sendTxAudio ) ;
2021-02-14 07:53:55 +00:00
txAudioTimer . start ( TXAUDIO_PERIOD ) ;
2021-02-13 23:25:24 +00:00
2021-02-14 07:15:49 +00:00
emit setupTxAudio ( txNumSamples , txChannelCount , txSampleRate , bufferSize , txIsUlawCodec , true ) ;
emit setupRxAudio ( rxNumSamples , rxChannelCount , rxSampleRate , bufferSize , rxIsUlawCodec , false ) ;
2021-02-03 20:00:40 +00:00
}
2021-02-05 17:40:58 +00:00
udpAudio : : ~ udpAudio ( )
{
2021-02-13 23:25:24 +00:00
if ( txAudioTimer . isActive ( ) )
{
txAudioTimer . stop ( ) ;
}
2021-02-08 10:22:20 +00:00
if ( rxAudioThread ) {
rxAudioThread - > quit ( ) ;
rxAudioThread - > wait ( ) ;
2021-02-08 08:31:48 +00:00
}
2021-02-13 23:25:24 +00:00
2021-02-11 19:18:35 +00:00
if ( txAudioThread ) {
txAudioThread - > quit ( ) ;
txAudioThread - > wait ( ) ;
}
}
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-02-18 15:54:26 +00:00
2021-02-13 11:04:26 +00:00
if ( txaudio - > chunkAvailable ) {
QByteArray audio ;
txaudio - > getNextAudioChunk ( audio ) ;
2021-02-20 20:19:18 +00:00
int counter = 1 ;
int len = 0 ;
while ( len < audio . length ( ) ) {
QByteArray partial = audio . mid ( len , 1364 ) ;
txaudio_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-20 20:19:18 +00:00
p . len = sizeof ( p ) + partial . length ( ) ;
2021-02-20 18:29:23 +00:00
p . sentid = myId ;
p . rcvdid = remoteId ;
2021-02-20 20:19:18 +00:00
if ( counter % 2 = = 0 )
{
p . ident = 0x0680 ;
}
else {
p . ident = 0x0681 ;
}
p . datalen = ( quint16 ) qToBigEndian ( ( quint16 ) partial . length ( ) ) ;
p . sendseq = ( quint16 ) qToBigEndian ( 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-02-12 23:56:02 +00:00
//qDebug() << "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-08 16:53:26 +00:00
void udpAudio : : changeBufferSize ( quint16 value )
{
emit haveChangeBufferSize ( 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 ( ) ;
//qDebug() << "Received: " << datagram.data();
QByteArray r = datagram . data ( ) ;
switch ( r . length ( ) )
{
2021-02-20 18:29:23 +00:00
case ( 16 ) : // Response to control packet handled in udpBase
2021-02-03 20:00:40 +00:00
break ;
default :
2021-02-20 18:29:23 +00:00
{
2021-02-09 12:43:28 +00:00
/* 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-20 18:29:23 +00:00
if ( r . mid ( 0 , 2 ) = = QByteArrayLiteral ( " \x6c \x05 " ) | |
2021-02-09 12:43:28 +00:00
r . mid ( 0 , 2 ) = = QByteArrayLiteral ( " \x44 \x02 " ) | |
r . mid ( 0 , 2 ) = = QByteArrayLiteral ( " \xd8 \x03 " ) | |
r . mid ( 0 , 2 ) = = QByteArrayLiteral ( " \x70 \x04 " ) )
2021-02-03 20:00:40 +00:00
{
2021-02-14 16:14:56 +00:00
// First check if we are missing any packets as seq should be sequential.
2021-02-03 20:00:40 +00:00
uint16_t gotSeq = qFromLittleEndian < quint16 > ( r . mid ( 6 , 2 ) ) ;
2021-02-05 20:26:18 +00:00
if ( lastReceivedSeq = = 0 | | lastReceivedSeq > gotSeq ) {
lastReceivedSeq = gotSeq ;
2021-02-03 20:00:40 +00:00
}
2021-02-20 18:29:23 +00:00
for ( uint16_t f = lastReceivedSeq + 1 ; f < gotSeq ; f + + ) {
2021-02-05 20:26:18 +00:00
// Do we need to request a retransmit?
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Missing Sequence: ( " < < r . length ( ) < < " ) " < < f ;
2021-02-03 20:00:40 +00:00
}
2021-02-05 20:26:18 +00:00
lastReceivedSeq = gotSeq ;
2021-02-12 23:56:02 +00:00
rxaudio - > incomingAudio ( r . mid ( 24 ) ) ;
2021-02-03 20:00:40 +00:00
}
break ;
}
2021-02-20 18:29:23 +00:00
}
2021-02-04 18:52: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 ( ) ;
qDebug ( ) < < " UDP Stream bound to local port: " < < localPort < < " remote port: " < < port ;
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-04 19:53:48 +00:00
}
2021-02-13 23:25:24 +00:00
2021-02-04 19:53:48 +00:00
udpBase : : ~ udpBase ( )
{
qDebug ( ) < < " 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-13 23:25:24 +00:00
if ( pingTimer . isActive ( ) )
2021-02-07 12:59:41 +00:00
{
2021-02-13 23:25:24 +00:00
pingTimer . stop ( ) ;
2021-02-07 12:59:41 +00:00
}
2021-02-13 23:25:24 +00:00
if ( idleTimer . isActive ( ) )
2021-02-07 12:59:41 +00:00
{
2021-02-13 23:25:24 +00:00
idleTimer . stop ( ) ;
2021-02-07 12:59:41 +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
{
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 ( ) ;
// We should check for missing packets here
// for now just store received seq.
lastReceivedSeq = in - > seq ;
if ( in - > type = = 0x04 ) {
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Received I am here " ;
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 ;
sendControl ( false , 0x06 , 0x01 ) ; // Send Are you ready - untracked.
2021-02-04 18:52:00 +00:00
}
2021-02-20 18:29:23 +00:00
else if ( in - > type = = 0x06 )
2021-02-04 18:52:00 +00:00
{
2021-02-20 18:29:23 +00:00
// Just get the seqnum and ignore the rest.
2021-02-04 18:52:00 +00:00
}
2021-02-20 18:29:23 +00:00
else if ( in - > type = = 0x01 ) // retransmit request
{
// retransmit request
// Send an idle with the requested seqnum if not found.
bool found = false ;
for ( int f = txSeqBuf . length ( ) - 1 ; f > = 0 ; f - - )
2021-02-04 18:52:00 +00:00
{
2021-02-20 18:29:23 +00:00
packetsLost + + ;
if ( txSeqBuf [ f ] . seqNum = = in - > seq ) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker ( & mutex ) ;
udp - > writeDatagram ( txSeqBuf [ f ] . data , radioIP , port ) ;
found = true ;
break ;
2021-02-05 13:16:48 +00:00
}
2021-02-04 18:52:00 +00:00
}
2021-02-20 18:29:23 +00:00
if ( ! found )
{
// Packet was not found in buffer
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl ( false , 0 , in - > seq ) ;
}
2021-02-04 18:52:00 +00:00
}
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
//uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
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 ;
QMutexLocker locker ( & mutex ) ;
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
}
else if ( r [ 0x10 ] = = ( char ) 0x01 ) {
if ( in - > seq = = pingSendSeq )
{
// This is response to OUR request so increment counter
pingSendSeq + + ;
}
else {
// Not sure what to do here, need to spend more time with the protocol but try sending ping with same seq next time?
//qDebug() << "Received out-of-sequence ping response. Sent:" << pingSendSeq << " received " << gotSeq;
}
2021-02-13 23:25:24 +00:00
}
else {
2021-02-20 18:29:23 +00:00
qDebug ( ) < < " 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 ;
}
case ( 0x18 ) :
{
if ( r . mid ( 0 , 6 ) = = QByteArrayLiteral ( " \x18 \x00 \x00 \x00 \x01 \x00 " ) )
{ // retransmit range request, can contain multiple ranges.
for ( int f = 16 ; f < r . length ( ) - 4 ; f = f + 4 )
{
quint16 start = qFromLittleEndian < quint16 > ( r . mid ( f , 2 ) ) ;
quint16 end = qFromLittleEndian < quint16 > ( r . mid ( f + 2 , 2 ) ) ;
packetsLost = packetsLost + ( end - start ) ;
qDebug ( ) < < this - > metaObject ( ) - > className ( ) < < " : Retransmit range request for: " < < start < < " to " < < end ;
for ( quint16 gotSeq = start ; gotSeq < = end ; gotSeq + + )
{
bool found = false ;
for ( int h = txSeqBuf . length ( ) - 1 ; h > = 0 ; h - - )
if ( txSeqBuf [ h ] . seqNum = = gotSeq ) {
//qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
QMutexLocker locker ( & mutex ) ;
udp - > writeDatagram ( txSeqBuf [ h ] . data , radioIP , port ) ;
found = true ;
break ;
}
if ( ! found )
{
//qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
sendControl ( false , 0 , gotSeq ) ;
}
}
}
2021-02-13 23:25:24 +00:00
}
2021-02-20 18:29:23 +00:00
break ;
2021-02-04 18:52:00 +00:00
}
2021-02-20 18:29:23 +00:00
default :
break ;
2021-02-04 18:52:00 +00:00
}
}
2021-02-18 15:54:26 +00:00
// Used to send idle and other "control" style messages
2021-02-20 18:29:23 +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-02-13 23:25:24 +00:00
QMutexLocker locker ( & mutex ) ;
2021-02-20 18:29:23 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
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-14 07:53:55 +00:00
if ( idleTimer . isActive ( ) ) {
idleTimer . start ( IDLE_PERIOD ) ; // Reset idle counter if it's running
}
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 ( ) ;
QMutexLocker locker ( & mutex ) ;
2021-02-20 18:29:23 +00:00
udp - > writeDatagram ( QByteArray : : fromRawData ( ( const char * ) p . packet , sizeof ( p ) ) , radioIP , port ) ;
2021-02-06 10:54:20 +00:00
innerSendSeq + + ;
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
{
// As the radio can request retransmission of these packets, store them in a buffer (eventually!)
d [ 6 ] = sendSeq & 0xff ;
d [ 7 ] = ( sendSeq > > 8 ) & 0xff ;
2021-02-04 13:12:08 +00:00
SEQBUFENTRY s ;
s . seqNum = sendSeq ;
2021-02-05 13:16:48 +00:00
s . timeSent = time ( NULL ) ;
2021-02-04 13:12:08 +00:00
s . data = ( d ) ;
txSeqBuf . append ( s ) ;
2021-02-13 23:25:24 +00:00
purgeOldEntries ( ) ; // Delete entries older than PURGE_SECONDS seconds (currently 5)
2021-02-03 20:00:40 +00:00
sendSeq + + ;
2021-02-13 23:25:24 +00:00
QMutexLocker locker ( & mutex ) ;
udp - > writeDatagram ( d , radioIP , port ) ;
if ( idleTimer . isActive ( ) ) {
2021-02-14 07:53:55 +00:00
idleTimer . start ( IDLE_PERIOD ) ; // Reset idle counter if it's running
2021-02-13 23:25:24 +00:00
}
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-13 23:25:24 +00:00
void udpBase : : purgeOldEntries ( )
2021-02-03 20:00:40 +00:00
{
for ( int f = txSeqBuf . length ( ) - 1 ; f > = 0 ; f - - )
{
2021-02-13 23:25:24 +00:00
if ( difftime ( time ( NULL ) , txSeqBuf [ f ] . timeSent ) > PURGE_SECONDS )
2021-02-05 17:40:58 +00:00
{
2021-02-03 20:00:40 +00:00
txSeqBuf . removeAt ( f ) ;
2021-02-05 17:40:58 +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-09 12:43:28 +00:00
quint8 * res = new quint8 [ 16 ] ;
2021-02-03 20:00:40 +00:00
memset ( res , 0 , 16 ) ; // Make sure res buffer is empty!
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