kopia lustrzana https://gitlab.com/eliggett/wfview
998 wiersze
39 KiB
C++
998 wiersze
39 KiB
C++
// Copyright 2021 Phil Taylor M0VSE
|
|
// This code is heavily based on "Kappanhang" by HA2NON, ES1AKOS and W6EL!
|
|
|
|
#include "udphandler.h"
|
|
|
|
udpHandler::udpHandler(QString ip, quint16 cport, quint16 sport, quint16 aport, QString username, QString password,
|
|
quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
|
{
|
|
qDebug() << "Starting udpHandler user:" << username << " buffer:" << buffer << " rx sample rate: " << rxsample <<
|
|
" rx codec: " << rxcodec << " tx sample rate: " << txsample << " tx codec: " << txcodec;
|
|
|
|
// Lookup IP address
|
|
|
|
this->port = cport;
|
|
this->aport = aport;
|
|
this->sport = sport;
|
|
this->username = username;
|
|
this->password = password;
|
|
this->rxBufferSize = buffer;
|
|
this->rxSampleRate = rxsample;
|
|
this->txSampleRate = txsample;
|
|
this->rxCodec = rxcodec;
|
|
this->txCodec = txcodec;
|
|
|
|
/*
|
|
0x01 uLaw 1ch 8bit
|
|
0x02 PCM 1ch 8bit
|
|
0x04 PCM 1ch 16bit
|
|
0x08 PCM 2ch 8bit
|
|
0x10 PCM 2ch 16bit
|
|
0x20 uLaw 2ch 8bit
|
|
*/
|
|
this->rxCodec = rxcodec;
|
|
this->txCodec = txcodec;
|
|
|
|
|
|
// 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);
|
|
foreach(QHostAddress addr, remote.addresses())
|
|
{
|
|
if (addr.protocol() == QAbstractSocket::IPv4Protocol) {
|
|
radioIP = addr;
|
|
qDebug() << "Got IP Address :" << ip << ": " << addr.toString();
|
|
break;
|
|
}
|
|
}
|
|
if (radioIP.isNull())
|
|
{
|
|
qDebug() << "Error obtaining IP Address for :" << ip << ": " << remote.errorString();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
foreach(const QHostAddress & address, hostList)
|
|
{
|
|
if (address.protocol() == QAbstractSocket::IPv4Protocol && address.isLoopback() == false)
|
|
{
|
|
localIP = QHostAddress(address.toString());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
init(); // Perform connection
|
|
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpHandler::DataReceived);
|
|
|
|
connect(&reauthTimer, &QTimer::timeout, this, QOverload<>::of(&udpHandler::ReAuth));
|
|
|
|
udpBase::SendPacketConnect(); // First connect packet
|
|
compName = QString("wfview").toUtf8();
|
|
}
|
|
|
|
udpHandler::~udpHandler()
|
|
{
|
|
if (isAuthenticated)
|
|
{
|
|
if (audio != Q_NULLPTR)
|
|
{
|
|
delete audio;
|
|
}
|
|
|
|
if (serial != Q_NULLPTR)
|
|
{
|
|
delete serial;
|
|
}
|
|
|
|
qDebug() << "Sending De-Auth packet to radio";
|
|
SendPacketAuth(0x01);
|
|
|
|
}
|
|
}
|
|
|
|
void udpHandler::changeBufferSize(quint16 value)
|
|
{
|
|
emit haveChangeBufferSize(value);
|
|
}
|
|
|
|
void udpHandler::ReAuth()
|
|
{
|
|
qDebug() << "Performing ReAuth";
|
|
SendPacketAuth(0x05);
|
|
}
|
|
|
|
void udpHandler::receiveFromSerialStream(QByteArray data)
|
|
{
|
|
emit haveDataFromPort(data);
|
|
}
|
|
|
|
void udpHandler::receiveDataFromUserToRig(QByteArray data)
|
|
{
|
|
if (serial != Q_NULLPTR)
|
|
{
|
|
serial->Send(data);
|
|
}
|
|
}
|
|
|
|
void udpHandler::DataReceived()
|
|
{
|
|
while (udp->hasPendingDatagrams()) {
|
|
lastReceived = time(0);
|
|
QNetworkDatagram datagram = udp->receiveDatagram();
|
|
//qDebug() << "Received: " << datagram.data();
|
|
QByteArray r = datagram.data();
|
|
|
|
switch (r.length())
|
|
{
|
|
case (16): // Response to pkt0
|
|
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
|
|
{
|
|
// Update remoteSID
|
|
if (!sentPacketLogin) {
|
|
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
|
|
SendPacketLogin(); // second login packet
|
|
sentPacketLogin = true;
|
|
}
|
|
}
|
|
break;
|
|
case (21): // pkt7,
|
|
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00") && r[16] == (char)0x01 && serialAndAudioOpened)
|
|
{
|
|
//qDebug("Got response!");
|
|
// This is a response to our pkt7 request so measure latency (only once fully connected though.
|
|
latency += lastPacket7Sent.msecsTo(QDateTime::currentDateTime());
|
|
latency /= 2;
|
|
emit haveNetworkStatus(" rtt: " + QString::number(latency) + " ms");
|
|
}
|
|
break;
|
|
case (64): // Response to Auth packet?
|
|
if (r.mid(0, 6) == QByteArrayLiteral("\x40\x00\x00\x00\x00\x00"))
|
|
{
|
|
if (r[21] == (char)0x05)
|
|
{
|
|
// Request serial and audio!
|
|
gotAuthOK = true;
|
|
if (!serialAndAudioOpened)
|
|
{
|
|
SendRequestSerialAndAudio();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case (80): // Status packet
|
|
if (r.mid(0, 6) == QByteArrayLiteral("\x50\x00\x00\x00\x00\x00"))
|
|
{
|
|
if (r.mid(48, 3) == QByteArrayLiteral("\xff\xff\xff"))
|
|
{
|
|
if (!serialAndAudioOpened)
|
|
{
|
|
emit haveNetworkError(radioIP.toString(), "Auth failed, try rebooting the radio.");
|
|
qDebug() << "Auth failed, try rebooting the radio.";
|
|
}
|
|
}
|
|
if (r.mid(48, 3) == QByteArrayLiteral("\x00\x00\x00") && r[64] == (char)0x01)
|
|
{
|
|
emit haveNetworkError(radioIP.toString(), "Got radio disconnected.");
|
|
qDebug() << "Got radio disconnected.";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case(96): // Response to Login packet.
|
|
if (r.mid(0, 6) == QByteArrayLiteral("\x60\x00\x00\x00\x00\x00"))
|
|
{
|
|
if (r.mid(48, 4) == QByteArrayLiteral("\xff\xff\xff\xfe"))
|
|
{
|
|
emit haveNetworkError(radioIP.toString(), "Invalid Username/Password");
|
|
qDebug() << "Invalid Username/Password";
|
|
|
|
}
|
|
else if (!isAuthenticated)
|
|
{
|
|
emit haveNetworkError(radioIP.toString(), "Radio Login OK!");
|
|
qDebug() << "Login OK!";
|
|
|
|
authID[0] = r[26];
|
|
authID[1] = r[27];
|
|
authID[2] = r[28];
|
|
authID[3] = r[29];
|
|
authID[4] = r[30];
|
|
authID[5] = r[31];
|
|
|
|
pkt7Timer = new QTimer(this);
|
|
connect(pkt7Timer, &QTimer::timeout, this, &udpBase::SendPkt7Idle);
|
|
pkt7Timer->start(3000); // send pkt7 idle packets every 3 seconds
|
|
|
|
SendPacketAuth(0x02);
|
|
|
|
pkt0Timer = new QTimer(this);
|
|
connect(pkt0Timer, &QTimer::timeout, this, std::bind(&udpBase::SendPkt0Idle, this, true, 0));
|
|
pkt0Timer->start(100);
|
|
|
|
SendPacketAuth(0x05);
|
|
|
|
reauthTimer.start(reauthInterval);
|
|
|
|
isAuthenticated = true;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case (144):
|
|
if (!serialAndAudioOpened && r.mid(0, 6) == QByteArrayLiteral("\x90\x00\x00\x00\x00\x00") && r[0x60] == (char)0x01)
|
|
{
|
|
devName = parseNullTerminatedString(r, 0x40);
|
|
QHostAddress ip = QHostAddress(qFromBigEndian<quint32>(r.mid(0x84, 4)));
|
|
if (parseNullTerminatedString(r, 0x64) != compName) // || ip != localIP ) // TODO: More testing of IP address detection code!
|
|
{
|
|
emit haveNetworkStatus("Radio in use by: " + QString::fromUtf8(parseNullTerminatedString(r, 0x64))+" ("+ip.toString()+")");
|
|
}
|
|
else
|
|
{
|
|
|
|
serial = new udpSerial(localIP, radioIP, sport);
|
|
audio = new udpAudio(localIP, radioIP, aport,rxBufferSize,rxSampleRate, rxCodec,txSampleRate,txCodec);
|
|
|
|
QObject::connect(serial, SIGNAL(Receive(QByteArray)), this, SLOT(receiveFromSerialStream(QByteArray)));
|
|
QObject::connect(this, SIGNAL(haveChangeBufferSize(quint16)), audio, SLOT(changeBufferSize(quint16)));
|
|
|
|
serialAndAudioOpened = true;
|
|
|
|
emit haveNetworkStatus(QString::fromUtf8(devName));
|
|
|
|
qDebug() << "Got serial and audio request success, device name: " << QString::fromUtf8(devName);
|
|
|
|
// Stuff can change in the meantime because of a previous login...
|
|
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
|
|
localSID = qFromBigEndian<quint32>(r.mid(12, 4));
|
|
authID[0] = r[26];
|
|
authID[1] = r[27];
|
|
authID[2] = r[28];
|
|
authID[3] = r[29];
|
|
authID[4] = r[30];
|
|
authID[5] = r[31];
|
|
}
|
|
// Is there already somebody connected to the radio?
|
|
}
|
|
break;
|
|
|
|
case (168):
|
|
|
|
if (r.mid(0, 6) == QByteArrayLiteral("\xa8\x00\x00\x00\x00\x00"))
|
|
{
|
|
a8replyID[0] = r[66];
|
|
a8replyID[1] = r[67];
|
|
a8replyID[2] = r[68];
|
|
a8replyID[3] = r[69];
|
|
a8replyID[4] = r[70];
|
|
a8replyID[5] = r[71];
|
|
a8replyID[6] = r[72];
|
|
a8replyID[7] = r[73];
|
|
a8replyID[8] = r[74];
|
|
a8replyID[9] = r[75];
|
|
a8replyID[10] = r[76];
|
|
a8replyID[11] = r[77];
|
|
a8replyID[12] = r[78];
|
|
a8replyID[13] = r[79];
|
|
a8replyID[14] = r[80];
|
|
a8replyID[15] = r[81];
|
|
gotA8ReplyID = true;
|
|
}
|
|
break;
|
|
}
|
|
udpBase::DataReceived(r); // Call parent function to process the rest.
|
|
r.clear();
|
|
datagram.clear();
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
qint64 udpHandler::SendRequestSerialAndAudio()
|
|
{
|
|
|
|
/*
|
|
0x72 is RX audio codec
|
|
0x73 is TX audio codec (only single channel options)
|
|
0x01 uLaw 1ch 8bit
|
|
0x02 PCM 1ch 8bit
|
|
0x04 PCM 1ch 16bit
|
|
0x08 PCM 2ch 8bit
|
|
0x10 PCM 2ch 16bit
|
|
0x20 uLaw 2ch 8bit
|
|
*/
|
|
|
|
quint8* usernameEncoded = Passcode(username);
|
|
int txSeqBufLengthMs = 200;
|
|
const quint8 p[] = {
|
|
0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0x00, 0x00, 0x00, 0x80, 0x01, 0x03, 0x00, static_cast<quint8>(authInnerSendSeq & 0xff), static_cast<quint8>(authInnerSendSeq >> 8 & 0xff),
|
|
0x00, static_cast<quint8>(authID[0]), static_cast<quint8>(authID[1]), static_cast<quint8>(authID[2]),
|
|
static_cast<quint8>(authID[3]), static_cast<quint8>(authID[4]), static_cast<quint8>(authID[5]),
|
|
static_cast<quint8>(a8replyID[0]), static_cast<quint8>(a8replyID[1]), static_cast<quint8>(a8replyID[2]), static_cast<quint8>(a8replyID[3]),
|
|
static_cast<quint8>(a8replyID[4]), static_cast<quint8>(a8replyID[5]), static_cast<quint8>(a8replyID[6]), static_cast<quint8>(a8replyID[7]),
|
|
static_cast<quint8>(a8replyID[8]), static_cast<quint8>(a8replyID[9]), static_cast<quint8>(a8replyID[10]), static_cast<quint8>(a8replyID[11]),
|
|
static_cast<quint8>(a8replyID[12]), static_cast<quint8>(a8replyID[13]), static_cast<quint8>(a8replyID[14]), static_cast<quint8>(a8replyID[15]),
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x49, 0x43, 0x2d, 0x37, 0x38, 0x35, 0x31, 0x00, // IC-7851 in plain text
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
usernameEncoded[0], usernameEncoded[1], usernameEncoded[2], usernameEncoded[3],
|
|
usernameEncoded[4], usernameEncoded[5], usernameEncoded[6], usernameEncoded[7],
|
|
usernameEncoded[8], usernameEncoded[9], usernameEncoded[10], usernameEncoded[11],
|
|
usernameEncoded[12], usernameEncoded[13], usernameEncoded[14], usernameEncoded[15],
|
|
0x01, 0x01, rxCodec, txCodec, 0x00, 0x00, static_cast<quint8>(rxSampleRate >> 8 & 0xff), static_cast<quint8>(rxSampleRate & 0xff),
|
|
0x00, 0x00, static_cast<quint8>(txSampleRate >> 8 & 0xff), static_cast<quint8>(txSampleRate & 0xff),
|
|
0x00, 0x00, static_cast<quint8>(sport >> 8 & 0xff), static_cast<quint8>(sport & 0xff),
|
|
0x00, 0x00, static_cast<quint8>(aport >> 8 & 0xff), static_cast<quint8>(aport & 0xff), 0x00, 0x00,
|
|
static_cast<quint8>(txSeqBufLengthMs >> 8 & 0xff), static_cast<quint8>(txSeqBufLengthMs & 0xff), 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
authInnerSendSeq++;
|
|
delete[] usernameEncoded;
|
|
|
|
return SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
}
|
|
|
|
|
|
qint64 udpHandler::SendPacketLogin() // Only used on control stream.
|
|
{
|
|
|
|
uint16_t authStartID = rand() | rand() << 8;
|
|
quint8* usernameEncoded = Passcode(username);
|
|
quint8* passwordEncoded = Passcode(password);
|
|
|
|
quint8 p[] = {
|
|
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, static_cast<quint8>(authInnerSendSeq & 0xff), static_cast<quint8>(authInnerSendSeq >> 8 & 0xff),
|
|
0x00, static_cast<quint8>(authStartID & 0xff), static_cast<quint8>(authStartID >> 8 & 0xff), 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
usernameEncoded[0], usernameEncoded[1], usernameEncoded[2], usernameEncoded[3],
|
|
usernameEncoded[4], usernameEncoded[5], usernameEncoded[6], usernameEncoded[7],
|
|
usernameEncoded[8], usernameEncoded[9], usernameEncoded[10], usernameEncoded[11],
|
|
usernameEncoded[12], usernameEncoded[13], usernameEncoded[14], usernameEncoded[15],
|
|
passwordEncoded[0], passwordEncoded[1], passwordEncoded[2], passwordEncoded[3],
|
|
passwordEncoded[4], passwordEncoded[5], passwordEncoded[6], passwordEncoded[7],
|
|
passwordEncoded[8], passwordEncoded[9], passwordEncoded[10], passwordEncoded[11],
|
|
passwordEncoded[12], passwordEncoded[13], passwordEncoded[14], passwordEncoded[15],
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
memcpy(p + 0x60, compName.constData(), compName.length());
|
|
|
|
delete[] usernameEncoded;
|
|
delete[] passwordEncoded;
|
|
|
|
authInnerSendSeq++;
|
|
return SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
}
|
|
|
|
|
|
qint64 udpHandler::SendPacketAuth(uint8_t magic)
|
|
{
|
|
|
|
const quint8 p[] = {
|
|
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0x00, 0x00, 0x00, 0x30, 0x01, static_cast<quint8>(magic), 0x00, static_cast<quint8>(authInnerSendSeq & 0xff), static_cast<quint8>((authInnerSendSeq) >> 8 & 0xff), 0x00,
|
|
static_cast<quint8>(authID[0]), static_cast<quint8>(authID[1]), static_cast<quint8>(authID[2]),
|
|
static_cast<quint8>(authID[3]), static_cast<quint8>(authID[4]), static_cast<quint8>(authID[5]),
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
authInnerSendSeq++;
|
|
return SendTrackedPacket(QByteArray::fromRawData((const char *)p, sizeof(p)));
|
|
}
|
|
|
|
|
|
// (pseudo) serial class
|
|
udpSerial::udpSerial(QHostAddress local, QHostAddress ip, quint16 sport)
|
|
{
|
|
qDebug() << "Starting udpSerial";
|
|
localIP = local;
|
|
port = sport;
|
|
radioIP = ip;
|
|
|
|
init(); // Perform connection
|
|
|
|
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpSerial::DataReceived);
|
|
SendPacketConnect(); // First connect packet
|
|
}
|
|
|
|
|
|
int udpSerial::Send(QByteArray d)
|
|
{
|
|
// qDebug() << "Sending: (" << d.length() << ") " << d;
|
|
|
|
uint16_t l = d.length();
|
|
const quint8 p[] = { static_cast<quint8>(0x15 + l), 0x00, 0x00, 0x00, 0x00, 0x00,0x00,0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0xc1, static_cast<quint8>(l), 0x00, static_cast<quint8>(sendSeqB >> 8 & 0xff),static_cast<quint8>(sendSeqB & 0xff)
|
|
};
|
|
QByteArray t = QByteArray::fromRawData((const char*)p, sizeof(p));
|
|
t.append(d);
|
|
SendTrackedPacket(t);
|
|
sendSeqB++;
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
void udpSerial::SendIdle()
|
|
{
|
|
const quint8 p[] = { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
|
|
SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
}
|
|
|
|
void udpSerial::SendPeriodic()
|
|
{
|
|
const quint8 p[] = { 0x15, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
|
|
SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
|
|
}
|
|
|
|
qint64 udpSerial::SendPacketOpenClose(bool close)
|
|
{
|
|
uint8_t magic = 0x05;
|
|
|
|
if (close)
|
|
{
|
|
magic = 0x00;
|
|
}
|
|
|
|
const quint8 p[] = {
|
|
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0xc0, 0x01, 0x00, static_cast<const quint8>(sendSeqB >> 8 & 0xff), static_cast<const quint8>(sendSeqB & 0xff),static_cast<quint8>(magic)
|
|
};
|
|
|
|
sendSeqB++;
|
|
|
|
return SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
}
|
|
|
|
|
|
|
|
void udpSerial::DataReceived()
|
|
{
|
|
while (udp->hasPendingDatagrams()) {
|
|
QNetworkDatagram datagram = udp->receiveDatagram();
|
|
//qDebug() << "Received: " << datagram.data();
|
|
QByteArray r = datagram.data();
|
|
|
|
switch (r.length())
|
|
{
|
|
case (16): // Response to pkt0
|
|
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
|
|
{
|
|
// Update remoteSID
|
|
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
|
|
|
|
if (!periodicRunning) {
|
|
SendPacketOpenClose(false); // First connect packet
|
|
pkt7Timer = new QTimer(this);
|
|
connect(pkt7Timer, &QTimer::timeout, this, &udpBase::SendPkt7Idle);
|
|
pkt7Timer->start(3000); // send pkt7 idle packets every 3 seconds
|
|
|
|
pkt0Timer = new QTimer(this);
|
|
connect(pkt0Timer, &QTimer::timeout, this, std::bind(&udpBase::SendPkt0Idle,this,true,0));
|
|
pkt0Timer->start(100);
|
|
|
|
periodicRunning = true;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
lastReceivedSeq = gotSeq;
|
|
|
|
quint8 temp = r[0] - 0x15;
|
|
if ((quint8)r[16] == 0xc1 && (quint8)r[17] == temp)
|
|
{
|
|
emit Receive(r.mid(21));
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
udpBase::DataReceived(r); // Call parent function to process the rest.
|
|
r.clear();
|
|
datagram.clear();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Audio stream
|
|
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 buffer, quint16 rxsample, quint8 rxcodec, quint16 txsample, quint8 txcodec)
|
|
{
|
|
qDebug() << "Starting udpAudio";
|
|
this->localIP = local;
|
|
this->port = aport;
|
|
this->radioIP = ip;
|
|
this->bufferSize = buffer;
|
|
this->rxSampleRate = rxsample;
|
|
this->txSampleRate = txsample;
|
|
this->rxCodec = rxcodec;
|
|
this->txCodec = txcodec;
|
|
|
|
init(); // Perform connection
|
|
|
|
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::DataReceived);
|
|
|
|
if (rxCodec == 0x01 || rxCodec == 0x20)
|
|
rxIsUlawCodec = true;
|
|
if (rxCodec == 0x08 || rxCodec == 0x10 || rxCodec == 0x20)
|
|
rxChannelCount = 2;
|
|
if (rxCodec == 0x02 || rxCodec == 0x8)
|
|
rxNumSamples = 8; // uLaw is actually 16bit.
|
|
|
|
// Init audio
|
|
format.setSampleRate(rxSampleRate);
|
|
format.setChannelCount(rxChannelCount);
|
|
format.setSampleSize(rxNumSamples);
|
|
format.setCodec("audio/pcm");
|
|
format.setByteOrder(QAudioFormat::LittleEndian);
|
|
format.setSampleType(QAudioFormat::SignedInt);
|
|
|
|
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
|
|
|
|
if (info.isFormatSupported(format))
|
|
{
|
|
qDebug() << "Audio format supported";
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "Audio format not supported!";
|
|
if (info.isNull())
|
|
{
|
|
qDebug() << "No device was found. You probably need to install libqt5multimedia-plugins.";
|
|
}
|
|
else {
|
|
qDebug() << "Audio Devices found: ";
|
|
const auto deviceInfos = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
|
for (const QAudioDeviceInfo& deviceInfo : deviceInfos)
|
|
{
|
|
qDebug() << "Device name: " << deviceInfo.deviceName();
|
|
qDebug() << "is null (probably not good):" << deviceInfo.isNull();
|
|
qDebug() << "channel count:" << deviceInfo.supportedChannelCounts();
|
|
qDebug() << "byte order:" << deviceInfo.supportedByteOrders();
|
|
qDebug() << "supported codecs:" << deviceInfo.supportedCodecs();
|
|
qDebug() << "sample rates:" << deviceInfo.supportedSampleRates();
|
|
qDebug() << "sample sizes:" << deviceInfo.supportedSampleSizes();
|
|
qDebug() << "sample types:" << deviceInfo.supportedSampleTypes();
|
|
}
|
|
qDebug() << "----- done with audio info -----";
|
|
}
|
|
}
|
|
|
|
|
|
rxaudio = new rxAudioHandler();
|
|
rxAudioThread = new QThread(this);
|
|
|
|
rxaudio->moveToThread(rxAudioThread);
|
|
|
|
connect(this,SIGNAL(setupAudio(QAudioFormat,quint16,bool)), rxaudio, SLOT(setup(QAudioFormat,quint16,bool)));
|
|
connect(this, SIGNAL(haveAudioData(QByteArray)), rxaudio, SLOT(incomingAudio(QByteArray)));
|
|
connect(this, SIGNAL(haveChangeBufferSize(quint16)), rxaudio, SLOT(changeBufferSize(quint16)));
|
|
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
|
|
|
|
rxAudioThread->start();
|
|
emit setupAudio(format, bufferSize,rxIsUlawCodec);
|
|
SendPacketConnect(); // First connect packet, audio should start very soon after.
|
|
}
|
|
|
|
udpAudio::~udpAudio()
|
|
{
|
|
if (rxAudioThread) {
|
|
rxAudioThread->quit();
|
|
rxAudioThread->wait();
|
|
}
|
|
}
|
|
|
|
void udpAudio::changeBufferSize(quint16 value)
|
|
{
|
|
emit haveChangeBufferSize(value);
|
|
}
|
|
|
|
void udpAudio::DataReceived()
|
|
{
|
|
while (udp->hasPendingDatagrams()) {
|
|
QNetworkDatagram datagram = udp->receiveDatagram();
|
|
//qDebug() << "Received: " << datagram.data();
|
|
QByteArray r = datagram.data();
|
|
|
|
switch (r.length())
|
|
{
|
|
case (16): // Response to pkt0
|
|
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x06\x00\x01\x00"))
|
|
{
|
|
// Update remoteSID in case it has changed.
|
|
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
|
|
if (!periodicRunning) {
|
|
periodicRunning = true;
|
|
pkt7Timer = new QTimer(this);
|
|
connect(pkt7Timer, &QTimer::timeout, this, &udpBase::SendPkt7Idle);
|
|
pkt7Timer->start(3000); // send pkt7 idle packets every 3 seconds
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Audio packets start as follows:
|
|
PCM 16bit and PCM8/uLAW stereo: 0x44,0x02 for first packet and 0x6c,0x05 for second.
|
|
uLAW 8bit/PCM 8bit 0xd8,0x03 for all packets
|
|
PCM 16bit stereo 0x6c,0x05 first & second 0x70,0x04 third
|
|
|
|
|
|
*/
|
|
if (r.mid(0, 2) == QByteArrayLiteral("\x6c\x05") ||
|
|
r.mid(0, 2) == QByteArrayLiteral("\x44\x02") ||
|
|
r.mid(0, 2) == QByteArrayLiteral("\xd8\x03") ||
|
|
r.mid(0, 2) == QByteArrayLiteral("\x70\x04"))
|
|
{
|
|
// First check if we are missing any packets
|
|
// Audio stream does not send periodic pkt0 so seq "should" be sequential.
|
|
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
if (lastReceivedSeq == 0 || lastReceivedSeq > gotSeq) {
|
|
lastReceivedSeq = gotSeq;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
lastReceivedSeq = gotSeq;
|
|
|
|
emit haveAudioData(r.mid(24));
|
|
}
|
|
break;
|
|
}
|
|
|
|
udpBase::DataReceived(r); // Call parent function to process the rest.
|
|
r.clear();
|
|
datagram.clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void udpBase::init()
|
|
{
|
|
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();
|
|
localSID = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff);
|
|
}
|
|
udpBase::~udpBase()
|
|
{
|
|
qDebug() << "Closing UDP stream :" << radioIP.toString() << ":" << port;
|
|
if (udp != Q_NULLPTR) {
|
|
SendPacketDisconnect();
|
|
udp->close();
|
|
delete udp;
|
|
}
|
|
if (pkt0Timer != Q_NULLPTR)
|
|
{
|
|
pkt0Timer->stop();
|
|
delete pkt0Timer;
|
|
}
|
|
if (pkt7Timer != Q_NULLPTR)
|
|
{
|
|
pkt7Timer->stop();
|
|
delete pkt7Timer;
|
|
}
|
|
}
|
|
|
|
// Base class!
|
|
|
|
void udpBase::DataReceived(QByteArray r)
|
|
{
|
|
switch (r.length())
|
|
{
|
|
case (16): // Response to pkt0
|
|
if (r.mid(0, 8) == QByteArrayLiteral("\x10\x00\x00\x00\x04\x00\x00\x00"))
|
|
{
|
|
if (!sentPacketConnect2)
|
|
{
|
|
remoteSID = qFromBigEndian<quint32>(r.mid(8, 4));
|
|
SendPacketConnect2(); // second connect packet
|
|
sentPacketConnect2 = true;
|
|
}
|
|
}
|
|
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x00\x00"))
|
|
{ // pkt0
|
|
// Just get the seqnum and ignore the rest.
|
|
lastReceivedSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
}
|
|
else if (r.mid(0, 6) == QByteArrayLiteral("\x10\x00\x00\x00\x01\x00"))
|
|
{ // retransmit request
|
|
// Send an idle with the requested seqnum if not found.
|
|
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
//qDebug() << "Retransmit request for "<< this->metaObject()->className() <<" : " << gotSeq;
|
|
bool found=false;
|
|
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
|
|
{
|
|
if (txSeqBuf[f].seqNum == gotSeq) {
|
|
qDebug() << this->metaObject()->className() << ": retransmitting packet :" << gotSeq << " (len=" << txSeqBuf[f].data.length() << ")";
|
|
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
|
|
udp->writeDatagram(txSeqBuf[f].data, radioIP, port);
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
{
|
|
// Packet was not found in buffer
|
|
qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
|
SendPkt0Idle(false, gotSeq);
|
|
}
|
|
}
|
|
else 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));
|
|
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() << ")";
|
|
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
|
|
udp->writeDatagram(txSeqBuf[h].data, radioIP, port);
|
|
found = true;
|
|
break;
|
|
}
|
|
if (!found)
|
|
{
|
|
qDebug() << this->metaObject()->className() << ": Could not find requested packet " << gotSeq << ", sending idle.";
|
|
SendPkt0Idle(false, gotSeq);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case (21): // pkt7, send response if request.
|
|
if (r.mid(1, 5) == QByteArrayLiteral("\x00\x00\x00\x07\x00"))
|
|
{
|
|
uint16_t gotSeq = qFromLittleEndian<quint16>(r.mid(6, 2));
|
|
|
|
if (r[16] == (char)0x00)
|
|
{
|
|
QMutexLocker locker(&mutex);
|
|
|
|
const quint8 p[] = { 0x15, 0x00, 0x00, 0x00, 0x07, 0x00,static_cast<quint8>(gotSeq & 0xff),static_cast<quint8>((gotSeq >> 8) & 0xff),
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0x01,static_cast<quint8>(r[17]),static_cast<quint8>(r[18]),static_cast<quint8>(r[19]),static_cast<quint8>(r[20])
|
|
};
|
|
|
|
udp->writeDatagram(QByteArray::fromRawData((const char *)p, sizeof(p)), radioIP, port);
|
|
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Send periodic idle packets (every 100ms)
|
|
void udpBase::SendPkt0Idle(bool tracked=true,quint16 seq=0)
|
|
{
|
|
quint8 p[] = { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
|
|
lastPacket0Sent = QDateTime::currentDateTime(); // Is this used?
|
|
|
|
if (!tracked) {
|
|
p[6] = seq & 0xff;
|
|
p[7] = (seq >> 8) & 0xff;
|
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
}
|
|
else {
|
|
SendTrackedPacket(QByteArray::fromRawData((const char*)p, sizeof(p)));
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Send periodic idle packets (every 3000ms)
|
|
void udpBase::SendPkt7Idle()
|
|
{
|
|
QMutexLocker locker(&mutex);
|
|
//qDebug() << this->metaObject()->className() << " tx buffer size:" << txSeqBuf.length();
|
|
|
|
const quint8 p[] = { 0x15, 0x00, 0x00, 0x00, 0x07, 0x00, static_cast<quint8>(pkt7SendSeq & 0xff),static_cast<quint8>(pkt7SendSeq >> 8 & 0xff),
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff),
|
|
0x00, static_cast<quint8>(rand()),static_cast<quint8>(innerSendSeq & 0xff),static_cast<quint8>(innerSendSeq >> 8 & 0xff), 0x06
|
|
};
|
|
//qDebug() << this->metaObject()->className() << ": Send pkt7: " << QByteArray::fromRawData((const char*)p, sizeof(p));
|
|
lastPacket7Sent = QDateTime::currentDateTime();
|
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
pkt7SendSeq++;
|
|
innerSendSeq++;
|
|
return;
|
|
}
|
|
|
|
|
|
qint64 udpBase::SendTrackedPacket(QByteArray d)
|
|
{
|
|
QMutexLocker locker(&mutex);
|
|
// As the radio can request retransmission of these packets, store them in a buffer (eventually!)
|
|
d[6] = sendSeq & 0xff;
|
|
d[7] = (sendSeq >> 8) & 0xff;
|
|
SEQBUFENTRY s;
|
|
s.seqNum = sendSeq;
|
|
s.timeSent = time(NULL);
|
|
s.data = (d);
|
|
txSeqBuf.append(s);
|
|
PurgeOldEntries();
|
|
sendSeq++;
|
|
|
|
return udp->writeDatagram(d, radioIP, port);
|
|
}
|
|
|
|
|
|
void udpBase::PurgeOldEntries()
|
|
{
|
|
for (int f = txSeqBuf.length() - 1; f >= 0; f--)
|
|
{
|
|
// Delete any entries older than 1 second.
|
|
if (difftime(time(NULL), txSeqBuf[f].timeSent) > 60) // Delete anything more than 60 seconds old.
|
|
{
|
|
txSeqBuf.removeAt(f);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
qint64 udpBase::SendPacketConnect()
|
|
{
|
|
qDebug() << this->metaObject()->className() << ": Sending Connect";
|
|
QMutexLocker locker(&mutex);
|
|
const quint8 p[] = { 0x10, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
|
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
return udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
}
|
|
|
|
|
|
qint64 udpBase::SendPacketConnect2()
|
|
{
|
|
qDebug() << this->metaObject()->className() << ": Sending Connect2";
|
|
QMutexLocker locker(&mutex);
|
|
const quint8 p[] = { 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
|
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
return udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
}
|
|
|
|
qint64 udpBase::SendPacketDisconnect() // Unmanaged packet
|
|
{
|
|
QMutexLocker locker(&mutex);
|
|
//qDebug() << "Sending Stream Disconnect";
|
|
|
|
const quint8 p[] = { 0x10, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
|
|
static_cast<quint8>(localSID >> 24 & 0xff), static_cast<quint8>(localSID >> 16 & 0xff), static_cast<quint8>(localSID >> 8 & 0xff), static_cast<quint8>(localSID & 0xff),
|
|
static_cast<quint8>(remoteSID >> 24 & 0xff), static_cast<quint8>(remoteSID >> 16 & 0xff), static_cast<quint8>(remoteSID >> 8 & 0xff), static_cast<quint8>(remoteSID & 0xff)
|
|
};
|
|
udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
|
|
return udp->writeDatagram(QByteArray::fromRawData((const char*)p, sizeof(p)), radioIP, port);
|
|
}
|
|
|
|
|
|
quint8* udpBase::Passcode(QString str)
|
|
{
|
|
const quint8 sequence[] =
|
|
{
|
|
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
|
|
|
|
};
|
|
|
|
quint8* res = new quint8[16];
|
|
memset(res, 0, 16); // Make sure res buffer is empty!
|
|
QByteArray ba = str.toLocal8Bit();
|
|
uchar* ascii = (uchar*)ba.constData();
|
|
for (int i = 0; i < str.length() && i < 16; i++)
|
|
{
|
|
int p = ascii[i] + i;
|
|
if (p > 126)
|
|
{
|
|
p = 32 + p % 127;
|
|
}
|
|
res[i] = sequence[p];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
QByteArray udpBase::parseNullTerminatedString(QByteArray c, int s)
|
|
{
|
|
//QString res = "";
|
|
QByteArray res;
|
|
for (int i = s; i < c.length(); i++)
|
|
{
|
|
if (c[i] != '\0')
|
|
{
|
|
res.append(c[i]);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
}
|