Split udpHandler into multiple class files

merge-requests/9/merge
Phil Taylor 2022-05-11 00:11:29 +01:00
rodzic 82faf2c463
commit 772375fb9d
12 zmienionych plików z 1448 dodań i 1309 usunięć

248
udpaudio.cpp 100644
Wyświetl plik

@ -0,0 +1,248 @@
#include "udpaudio.h"
#include "logcategories.h"
// Audio stream
udpAudio::udpAudio(QHostAddress local, QHostAddress ip, quint16 audioPort, quint16 lport, audioSetup rxSetup, audioSetup txSetup)
{
qInfo(logUdp()) << "Starting udpAudio";
this->localIP = local;
this->port = audioPort;
this->radioIP = ip;
if (txSetup.sampleRate == 0) {
enableTx = false;
}
init(lport); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpAudio::dataReceived);
rxaudio = new audioHandler();
rxAudioThread = new QThread(this);
rxAudioThread->setObjectName("rxAudio()");
rxaudio->moveToThread(rxAudioThread);
rxAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupRxAudio(audioSetup)), rxaudio, SLOT(init(audioSetup)));
// signal/slot not currently used.
connect(this, SIGNAL(haveAudioData(audioPacket)), rxaudio, SLOT(incomingAudio(audioPacket)));
connect(this, SIGNAL(haveChangeLatency(quint16)), rxaudio, SLOT(changeLatency(quint16)));
connect(this, SIGNAL(haveSetVolume(unsigned char)), rxaudio, SLOT(setVolume(unsigned char)));
connect(rxaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getRxLevels(quint16, quint16, quint16, bool)));
connect(rxAudioThread, SIGNAL(finished()), rxaudio, SLOT(deleteLater()));
sendControl(false, 0x03, 0x00); // First connect packet
pingTimer = new QTimer();
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
pingTimer->start(PING_PERIOD); // send ping packets every 100ms
if (enableTx) {
txaudio = new audioHandler();
txAudioThread = new QThread(this);
rxAudioThread->setObjectName("txAudio()");
txaudio->moveToThread(txAudioThread);
txAudioThread->start(QThread::TimeCriticalPriority);
connect(this, SIGNAL(setupTxAudio(audioSetup)), txaudio, SLOT(init(audioSetup)));
connect(txaudio, SIGNAL(haveAudioData(audioPacket)), this, SLOT(receiveAudioData(audioPacket)));
connect(txaudio, SIGNAL(haveLevels(quint16, quint16, quint16, bool)), this, SLOT(getTxLevels(quint16, quint16, quint16, bool)));
connect(txAudioThread, SIGNAL(finished()), txaudio, SLOT(deleteLater()));
emit setupTxAudio(txSetup);
}
emit setupRxAudio(rxSetup);
watchdogTimer = new QTimer();
connect(watchdogTimer, &QTimer::timeout, this, &udpAudio::watchdog);
watchdogTimer->start(WATCHDOG_PERIOD);
areYouThereTimer = new QTimer();
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
areYouThereTimer->start(AREYOUTHERE_PERIOD);
}
udpAudio::~udpAudio()
{
if (pingTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping pingTimer";
pingTimer->stop();
delete pingTimer;
pingTimer = Q_NULLPTR;
}
if (idleTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping idleTimer";
idleTimer->stop();
delete idleTimer;
idleTimer = Q_NULLPTR;
}
if (watchdogTimer != Q_NULLPTR)
{
qDebug(logUdp()) << "Stopping watchdogTimer";
watchdogTimer->stop();
delete watchdogTimer;
watchdogTimer = Q_NULLPTR;
}
if (rxAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping rxaudio thread";
rxAudioThread->quit();
rxAudioThread->wait();
}
if (txAudioThread != Q_NULLPTR) {
qDebug(logUdp()) << "Stopping txaudio thread";
txAudioThread->quit();
txAudioThread->wait();
}
qDebug(logUdp()) << "udpHandler successfully closed";
}
void udpAudio::watchdog()
{
static bool alerted = false;
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
{
if (!alerted) {
/* Just log it at the moment, maybe try signalling the control channel that it needs to
try requesting civ/audio again? */
qInfo(logUdp()) << " Audio Watchdog: no audio data received for 2s, restart required?";
alerted = true;
}
}
else
{
alerted = false;
}
}
void udpAudio::sendTxAudio()
{
if (txaudio == Q_NULLPTR) {
return;
}
}
void udpAudio::receiveAudioData(audioPacket audio) {
// I really can't see how this could be possible but a quick sanity check!
if (txaudio == Q_NULLPTR) {
return;
}
if (audio.data.length() > 0) {
int counter = 1;
int len = 0;
while (len < audio.data.length()) {
QByteArray partial = audio.data.mid(len, 1364);
audio_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + partial.length();
p.sentid = myId;
p.rcvdid = remoteId;
if (partial.length() == 0xa0) {
p.ident = 0x9781;
}
else {
p.ident = 0x0080; // TX audio is always this?
}
p.datalen = (quint16)qToBigEndian((quint16)partial.length());
p.sendseq = (quint16)qToBigEndian((quint16)sendAudioSeq); // THIS IS BIG ENDIAN!
QByteArray tx = QByteArray::fromRawData((const char*)p.packet, sizeof(p));
tx.append(partial);
len = len + partial.length();
//qInfo(logUdp()) << "Sending audio packet length: " << tx.length();
sendTrackedPacket(tx);
sendAudioSeq++;
counter++;
}
}
}
void udpAudio::changeLatency(quint16 value)
{
emit haveChangeLatency(value);
}
void udpAudio::setVolume(unsigned char value)
{
emit haveSetVolume(value);
}
void udpAudio::getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) {
emit haveRxLevels(amplitude, latency, current, under);
}
void udpAudio::getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under) {
emit haveTxLevels(amplitude, latency, current, under);
}
void udpAudio::dataReceived()
{
while (udp->hasPendingDatagrams()) {
QNetworkDatagram datagram = udp->receiveDatagram();
//qInfo(logUdp()) << "Received: " << datagram.data().mid(0,10);
QByteArray r = datagram.data();
switch (r.length())
{
case (16): // Response to control packet handled in udpBase
{
//control_packet_t in = (control_packet_t)r.constData();
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
*/
control_packet_t in = (control_packet_t)r.constData();
if (in->type != 0x01 && in->len >= 0x20) {
if (in->seq == 0)
{
// Seq number has rolled over.
seqPrefix++;
}
// 0xac is the smallest possible audio packet.
lastReceived = QTime::currentTime();
audioPacket tempAudio;
tempAudio.seq = (quint32)seqPrefix << 16 | in->seq;
tempAudio.time = lastReceived;
tempAudio.sent = 0;
tempAudio.data = r.mid(0x18);
// Prefer signal/slot to forward audio as it is thread/safe
// Need to do more testing but latency appears fine.
//rxaudio->incomingAudio(tempAudio);
emit haveAudioData(tempAudio);
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
datagram.clear();
}
}

83
udpaudio.h 100644
Wyświetl plik

@ -0,0 +1,83 @@
#ifndef UDPAUDIO_H
#define UDPAUDIO_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
#include "udpbase.h"
#include "audiohandler.h"
// Class for all audio communications.
class udpAudio : public udpBase
{
Q_OBJECT
public:
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
~udpAudio();
int audioLatency = 0;
signals:
void haveAudioData(audioPacket data);
void setupTxAudio(audioSetup setup);
void setupRxAudio(audioSetup setup);
void haveChangeLatency(quint16 value);
void haveSetVolume(unsigned char value);
void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
public slots:
void changeLatency(quint16 value);
void setVolume(unsigned char value);
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void receiveAudioData(audioPacket audio);
private:
void sendTxAudio();
void dataReceived();
void watchdog();
uint16_t sendAudioSeq = 0;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* txAudioTimer = Q_NULLPTR;
bool enableTx = true;
QMutex audioMutex;
};
#endif

545
udpbase.cpp 100644
Wyświetl plik

@ -0,0 +1,545 @@
#include "udpbase.h"
#include "logcategories.h"
void udpBase::init(quint16 lport)
{
//timeStarted.start();
udp = new QUdpSocket(this);
udp->bind(lport); // Bind to random port.
localPort = udp->localPort();
qInfo(logUdp()) << "UDP Stream bound to local port:" << localPort << " remote port:" << port;
uint32_t addr = localIP.toIPv4Address();
myId = (addr >> 8 & 0xff) << 24 | (addr & 0xff) << 16 | (localPort & 0xffff);
retransmitTimer = new QTimer();
connect(retransmitTimer, &QTimer::timeout, this, &udpBase::sendRetransmitRequest);
retransmitTimer->start(RETRANSMIT_PERIOD);
}
udpBase::~udpBase()
{
qInfo(logUdp()) << "Closing UDP stream :" << radioIP.toString() << ":" << port;
if (udp != Q_NULLPTR) {
sendControl(false, 0x05, 0x00); // Send disconnect
udp->close();
delete udp;
}
if (areYouThereTimer != Q_NULLPTR)
{
areYouThereTimer->stop();
delete areYouThereTimer;
}
if (pingTimer != Q_NULLPTR)
{
pingTimer->stop();
delete pingTimer;
}
if (idleTimer != Q_NULLPTR)
{
idleTimer->stop();
delete idleTimer;
}
if (retransmitTimer != Q_NULLPTR)
{
retransmitTimer->stop();
delete retransmitTimer;
}
pingTimer = Q_NULLPTR;
idleTimer = Q_NULLPTR;
areYouThereTimer = Q_NULLPTR;
retransmitTimer = Q_NULLPTR;
}
// Base class!
void udpBase::dataReceived(QByteArray r)
{
if (r.length() < 0x10)
{
return; // Packet too small do to anything with?
}
switch (r.length())
{
case (CONTROL_SIZE): // Empty response used for simple comms and retransmit requests.
{
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x01 && in->len == 0x10)
{
// Single packet request
packetsLost++;
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
txBufferMutex.lock();
auto match = txSeqBuf.find(in->seq);
if (match != txSeqBuf.end()) {
// Found matching entry?
// Send "untracked" as it has already been sent once.
// Don't constantly retransmit the same packet, give-up eventually
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (single packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
match->retransmitCount++;
udpMutex.lock();
udp->writeDatagram(match->data, radioIP, port);
udpMutex.unlock();
}
else {
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
<< QString("0x%1").arg(in->seq, 0, 16) <<
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
}
txBufferMutex.unlock();
}
if (in->type == 0x04) {
qInfo(logUdp()) << 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;
if (areYouThereTimer != Q_NULLPTR && areYouThereTimer->isActive()) {
// send ping packets every second
areYouThereTimer->stop();
}
sendControl(false, 0x06, 0x01); // Send Are you ready - untracked.
}
else if (in->type == 0x06)
{
// Just get the seqnum and ignore the rest.
}
break;
}
case (PING_SIZE): // ping packet
{
ping_packet_t in = (ping_packet_t)r.constData();
if (in->type == 0x07)
{
// 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;
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else if (in->reply == 0x01) {
if (in->seq == pingSendSeq)
{
// This is response to OUR request so increment counter
pingSendSeq++;
}
}
else {
qInfo(logUdp()) << this->metaObject()->className() << "Unhandled response to ping. I have never seen this! 0x10=" << r[16];
}
}
break;
}
default:
{
// All packets "should" be added to the incoming buffer.
// First check that we haven't already received it.
}
break;
}
// All packets except ping and retransmit requests should trigger this.
control_packet_t in = (control_packet_t)r.constData();
// This is a variable length retransmit request!
if (in->type == 0x01 && in->len != 0x10)
{
for (quint16 i = 0x10; i < r.length(); i = i + 2)
{
quint16 seq = (quint8)r[i] | (quint8)r[i + 1] << 8;
auto match = txSeqBuf.find(seq);
if (match == txSeqBuf.end()) {
qDebug(logUdp()) << this->metaObject()->className() << ": Remote requested packet"
<< QString("0x%1").arg(seq, 0, 16) <<
"not found, have " << QString("0x%1").arg(txSeqBuf.firstKey(), 0, 16) <<
"to" << QString("0x%1").arg(txSeqBuf.lastKey(), 0, 16);
// Just send idle packet.
sendControl(false, 0, seq);
}
else {
// Found matching entry?
// Send "untracked" as it has already been sent once.
qDebug(logUdp()) << this->metaObject()->className() << ": Sending (multiple packet) retransmit of " << QString("0x%1").arg(match->seqNum, 0, 16);
match->retransmitCount++;
udpMutex.lock();
udp->writeDatagram(match->data, radioIP, port);
udpMutex.unlock();
packetsLost++;
congestion = static_cast<double>(packetsSent) / packetsLost * 100;
}
}
}
else if (in->len != PING_SIZE && in->type == 0x00 && in->seq != 0x00)
{
rxBufferMutex.lock();
if (rxSeqBuf.isEmpty()) {
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
else
{
if (in->seq < rxSeqBuf.firstKey() || in->seq - rxSeqBuf.lastKey() > MAX_MISSING)
{
qInfo(logUdp()) << this->metaObject()->className() << "Large seq number gap detected, previous highest: " <<
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
//seqPrefix++;
// Looks like it has rolled over so clear buffer and start again.
rxSeqBuf.clear();
// Add this packet to the incoming buffer
rxSeqBuf.insert(in->seq, QTime::currentTime());
rxBufferMutex.unlock();
missingMutex.lock();
rxMissing.clear();
missingMutex.unlock();
return;
}
if (!rxSeqBuf.contains(in->seq))
{
// Add incoming packet to the received buffer and if it is in the missing buffer, remove it.
if (in->seq > rxSeqBuf.lastKey() + 1) {
qInfo(logUdp()) << this->metaObject()->className() << "1 or more missing packets detected, previous: " <<
QString("0x%1").arg(rxSeqBuf.lastKey(), 0, 16) << " current: " << QString("0x%1").arg(in->seq, 0, 16);
// We are likely missing packets then!
missingMutex.lock();
//int missCounter = 0;
// Sanity check!
for (quint16 f = rxSeqBuf.lastKey() + 1; f <= in->seq; f++)
{
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(f, QTime::currentTime());
if (f != in->seq && !rxMissing.contains(f))
{
rxMissing.insert(f, 0);
}
}
missingMutex.unlock();
}
else {
if (rxSeqBuf.size() > BUFSIZE)
{
rxSeqBuf.erase(rxSeqBuf.begin());
}
rxSeqBuf.insert(in->seq, QTime::currentTime());
}
}
else {
// This is probably one of our missing packets!
missingMutex.lock();
auto s = rxMissing.find(in->seq);
if (s != rxMissing.end())
{
qInfo(logUdp()) << this->metaObject()->className() << ": Missing SEQ has been received! " << QString("0x%1").arg(in->seq, 0, 16);
s = rxMissing.erase(s);
}
missingMutex.unlock();
}
}
rxBufferMutex.unlock();
}
}
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.
if (rxMissing.isEmpty()) {
return;
}
else if (rxMissing.size() > MAX_MISSING) {
qInfo(logUdp()) << "Too many missing packets," << rxMissing.size() << "flushing all buffers";
missingMutex.lock();
rxMissing.clear();
missingMutex.unlock();
rxBufferMutex.lock();
rxSeqBuf.clear();
rxBufferMutex.unlock();
return;
}
QByteArray missingSeqs;
missingMutex.lock();
auto it = rxMissing.begin();
while (it != rxMissing.end())
{
if (it.key() != 0x0)
{
if (it.value() < 4)
{
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
missingSeqs.append(it.key() & 0xff);
missingSeqs.append(it.key() >> 8 & 0xff);
it.value()++;
it++;
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": No response for missing packet" << QString("0x%1").arg(it.key(), 0, 16) << "deleting";
it = rxMissing.erase(it);
}
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": found empty key in missing buffer";
it++;
}
}
missingMutex.unlock();
if (missingSeqs.length() != 0)
{
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 = 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);
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for missing packet : " << QString("0x%1").arg(p.seq, 0, 16);
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else
{
qInfo(logUdp()) << this->metaObject()->className() << ": sending request for multiple missing packets : " << missingSeqs.toHex(':');
missingMutex.lock();
p.len = sizeof(p) + missingSeqs.size();
missingSeqs.insert(0, p.packet, sizeof(p));
missingMutex.unlock();
udpMutex.lock();
udp->writeDatagram(missingSeqs, radioIP, port);
udpMutex.unlock();
}
}
}
// Used to send idle and other "control" style messages
void udpBase::sendControl(bool tracked = true, quint8 type = 0, quint16 seq = 0)
{
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;
if (!tracked) {
p.seq = seq;
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
}
else {
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
}
return;
}
// Send periodic ping packets
void udpBase::sendPing()
{
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;
QTime now = QTime::currentTime();
p.time = (quint32)now.msecsSinceStartOfDay();
lastPingSentTime = QDateTime::currentDateTime();
udpMutex.lock();
udp->writeDatagram(QByteArray::fromRawData((const char*)p.packet, sizeof(p)), radioIP, port);
udpMutex.unlock();
return;
}
void udpBase::sendTrackedPacket(QByteArray d)
{
// As the radio can request retransmission of these packets, store them in a buffer
d[6] = sendSeq & 0xff;
d[7] = (sendSeq >> 8) & 0xff;
SEQBUFENTRY s;
s.seqNum = sendSeq;
s.timeSent = QTime::currentTime();
s.retransmitCount = 0;
s.data = d;
if (txBufferMutex.tryLock(100))
{
if (sendSeq == 0) {
// We are either the first ever sent packet or have rolled-over so clear the buffer.
txSeqBuf.clear();
congestion = 0;
}
if (txSeqBuf.size() > BUFSIZE)
{
txSeqBuf.erase(txSeqBuf.begin());
}
txSeqBuf.insert(sendSeq, s);
txBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
}
// 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)
sendSeq++;
udpMutex.lock();
udp->writeDatagram(d, radioIP, port);
if (congestion > 10) { // Poor quality connection?
udp->writeDatagram(d, radioIP, port);
if (congestion > 20) // Even worse so send again.
udp->writeDatagram(d, radioIP, port);
}
if (idleTimer != Q_NULLPTR && idleTimer->isActive()) {
idleTimer->start(IDLE_PERIOD); // Reset idle counter if it's running
}
udpMutex.unlock();
packetsSent++;
return;
}
/// <summary>
/// Once a packet has reached PURGE_SECONDS old (currently 10) then it is not likely to be any use.
/// </summary>
void udpBase::purgeOldEntries()
{
// Erase old entries from the tx packet buffer
if (txBufferMutex.tryLock(100))
{
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;
}
}
}
txBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": txBuffer mutex is locked";
}
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;
}
}
}
rxBufferMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": rxBuffer mutex is locked";
}
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());
}
}
missingMutex.unlock();
}
else {
qInfo(logUdp()) << this->metaObject()->className() << ": missingBuffer mutex is locked";
}
}
void udpBase::printHex(const QByteArray& pdata)
{
printHex(pdata, false, true);
}
void udpBase::printHex(const QByteArray& pdata, bool printVert, bool printHoriz)
{
qDebug(logUdp()) << "---- Begin hex dump -----:";
QString sdata("DATA: ");
QString index("INDEX: ");
QStringList strings;
for (int i = 0; i < pdata.length(); i++)
{
strings << QString("[%1]: %2").arg(i, 8, 10, QChar('0')).arg((unsigned char)pdata[i], 2, 16, QChar('0'));
sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')));
index.append(QString("%1 ").arg(i, 2, 10, QChar('0')));
}
if (printVert)
{
for (int i = 0; i < strings.length(); i++)
{
//sdata = QString(strings.at(i));
qDebug(logUdp()) << strings.at(i);
}
}
if (printHoriz)
{
qDebug(logUdp()) << index;
qDebug(logUdp()) << sdata;
}
qDebug(logUdp()) << "----- End hex dump -----";
}

205
udpbase.h 100644
Wyświetl plik

@ -0,0 +1,205 @@
#ifndef UDPBASE_H
#define UDPBASE_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
struct udpPreferences {
QString ipAddress;
quint16 controlLANPort;
quint16 serialLANPort;
quint16 audioLANPort;
QString username;
QString password;
QString clientName;
quint8 waterfallFormat;
};
struct networkStatus {
quint8 rxAudioBufferPercent;
quint8 txAudioBufferPercent;
quint8 rxAudioLevel;
quint8 txAudioLevel;
quint16 rxLatency;
quint16 txLatency;
bool rxUnderrun;
bool txUnderrun;
quint16 rxCurrentLatency;
quint16 txCurrentLatency;
quint32 packetsSent = 0;
quint32 packetsLost = 0;
quint16 rtt = 0;
quint32 networkLatency = 0;
QString message;
};
// Parent class that contains all common items.
class udpBase : public QObject
{
public:
~udpBase();
void init(quint16 local);
void reconnect();
void dataReceived(QByteArray r);
void sendPing();
void sendRetransmitRange(quint16 first, quint16 second, quint16 third, quint16 fourth);
void sendControl(bool tracked, quint8 id, quint16 seq);
void printHex(const QByteArray& pdata);
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
//QTime timeStarted;
QUdpSocket* udp = Q_NULLPTR;
uint32_t myId = 0;
uint32_t remoteId = 0;
uint16_t authSeq = 0x30;
uint16_t sendSeqB = 0;
uint16_t sendSeq = 1;
uint16_t lastReceivedSeq = 1;
uint16_t pkt0SendSeq = 0;
uint16_t periodicSeq = 0;
quint64 latency = 0;
QString username = "";
QString password = "";
QHostAddress radioIP;
QHostAddress localIP;
bool isAuthenticated = false;
quint16 localPort = 0;
quint16 port = 0;
bool periodicRunning = false;
bool sentPacketConnect2 = false;
QTime lastReceived = QTime::currentTime();
QMutex udpMutex;
QMutex txBufferMutex;
QMutex rxBufferMutex;
QMutex missingMutex;
struct SEQBUFENTRY {
QTime timeSent;
uint16_t seqNum;
QByteArray data;
quint8 retransmitCount;
};
QMap<quint16, QTime> rxSeqBuf;
QMap<quint16, SEQBUFENTRY> txSeqBuf;
QMap<quint16, int> rxMissing;
void sendTrackedPacket(QByteArray d);
void purgeOldEntries();
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
QTimer* watchdogTimer = Q_NULLPTR;
QTimer* retransmitTimer = Q_NULLPTR;
QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0;
quint16 areYouThereCounter = 0;
quint32 packetsSent = 0;
quint32 packetsLost = 0;
quint16 seqPrefix = 0;
QString connectionType = "";
int congestion = 0;
private:
void sendRetransmitRequest();
};
/// <summary>
/// passcode function used to generate secure (ish) code
/// </summary>
/// <param name="str"></param>
/// <returns>pointer to encoded username or password</returns>
static inline void passcode(QString in, QByteArray& out)
{
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
};
QByteArray ba = in.toLocal8Bit();
uchar* ascii = (uchar*)ba.constData();
for (int i = 0; i < in.length() && i < 16; i++)
{
int p = ascii[i] + i;
if (p > 126)
{
p = 32 + p % 127;
}
out.append(sequence[p]);
}
return;
}
/// <summary>
/// returns a QByteArray of a null terminated string
/// </summary>
/// <param name="c"></param>
/// <param name="s"></param>
/// <returns></returns>
static inline QByteArray 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;
}
#endif

268
udpcivdata.cpp 100644
Wyświetl plik

@ -0,0 +1,268 @@
#include "udpcivdata.h"
#include "logcategories.h"
// Class that manages all Civ Data to/from the rig
udpCivData::udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 localPort = 0)
{
qInfo(logUdp()) << "Starting udpCivData";
localIP = local;
port = civPort;
radioIP = ip;
splitWaterfall = splitWf;
udpBase::init(localPort); // Perform connection
QUdpSocket::connect(udp, &QUdpSocket::readyRead, this, &udpCivData::dataReceived);
sendControl(false, 0x03, 0x00); // First connect packet
/*
Connect various timers
*/
pingTimer = new QTimer();
idleTimer = new QTimer();
areYouThereTimer = new QTimer();
startCivDataTimer = new QTimer();
watchdogTimer = new QTimer();
connect(pingTimer, &QTimer::timeout, this, &udpBase::sendPing);
connect(watchdogTimer, &QTimer::timeout, this, &udpCivData::watchdog);
connect(idleTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, true, 0, 0));
connect(startCivDataTimer, &QTimer::timeout, this, std::bind(&udpCivData::sendOpenClose, this, false));
connect(areYouThereTimer, &QTimer::timeout, this, std::bind(&udpBase::sendControl, this, false, 0x03, 0));
watchdogTimer->start(WATCHDOG_PERIOD);
// Start sending are you there packets - will be stopped once "I am here" received
// send ping packets every 100 ms (maybe change to less frequent?)
pingTimer->start(PING_PERIOD);
// Send idle packets every 100ms, this timer will be reset every time a non-idle packet is sent.
idleTimer->start(IDLE_PERIOD);
areYouThereTimer->start(AREYOUTHERE_PERIOD);
}
udpCivData::~udpCivData()
{
sendOpenClose(true);
if (startCivDataTimer != Q_NULLPTR)
{
startCivDataTimer->stop();
delete startCivDataTimer;
startCivDataTimer = Q_NULLPTR;
}
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;
}
}
void udpCivData::watchdog()
{
static bool alerted = false;
if (lastReceived.msecsTo(QTime::currentTime()) > 2000)
{
if (!alerted) {
qInfo(logUdp()) << " CIV Watchdog: no CIV data received for 2s, requesting data start.";
if (startCivDataTimer != Q_NULLPTR)
{
startCivDataTimer->start(100);
}
alerted = true;
}
}
else
{
alerted = false;
}
}
void udpCivData::send(QByteArray d)
{
//qInfo(logUdp()) << "Sending: (" << d.length() << ") " << d;
data_packet p;
memset(p.packet, 0x0, sizeof(p)); // We can't be sure it is initialized with 0x00!
p.len = sizeof(p) + d.length();
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));
t.append(d);
sendTrackedPacket(t);
sendSeqB++;
return;
}
void udpCivData::sendOpenClose(bool close)
{
uint8_t magic = 0x04;
if (close)
{
magic = 0x00;
}
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;
sendSeqB++;
sendTrackedPacket(QByteArray::fromRawData((const char*)p.packet, sizeof(p)));
return;
}
void udpCivData::dataReceived()
{
while (udp->hasPendingDatagrams())
{
QNetworkDatagram datagram = udp->receiveDatagram();
//qInfo(logUdp()) << "Received: " << datagram.data();
QByteArray r = datagram.data();
switch (r.length())
{
case (CONTROL_SIZE): // Control packet
{
control_packet_t in = (control_packet_t)r.constData();
if (in->type == 0x04)
{
areYouThereTimer->stop();
}
else if (in->type == 0x06)
{
// Update remoteId
remoteId = in->sentid;
// 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.
sendOpenClose(false);
if (startCivDataTimer != Q_NULLPTR) {
startCivDataTimer->start(100);
}
}
break;
}
default:
{
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();
if (quint16(in->datalen + 0x15) == (quint16)in->len)
{
//if (r.mid(0x15).length() != 157)
// Find data length
int pos = r.indexOf(QByteArrayLiteral("\x27\x00\x00")) + 2;
int len = r.mid(pos).indexOf(QByteArrayLiteral("\xfd"));
//splitWaterfall = false;
if (splitWaterfall && pos > 1 && len > 100) {
// We need to split waterfall data into its component parts
// There are only 2 types that we are currently aware of
int numDivisions = 0;
if (len == 490) // IC705, IC9700, IC7300(LAN)
{
numDivisions = 11;
}
else if (len == 704) // IC7610, IC7851, ICR8600
{
numDivisions = 15;
}
else {
qInfo(logUdp()) << "Unknown spectrum size" << len;
break;
}
// (sequence #1) includes center/fixed mode at [05]. No pixels.
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 "
// "DATA: 27 00 00 01 11 01 00 00 00 14 00 00 00 35 14 00 00 fd "
// (sequences 2-10, 50 pixels)
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 "
// "DATA: 27 00 00 07 11 27 13 15 01 00 22 21 09 08 06 19 0e 20 23 25 2c 2d 17 27 29 16 14 1b 1b 21 27 1a 18 17 1e 21 1b 24 21 22 23 13 19 23 2f 2d 25 25 0a 0e 1e 20 1f 1a 0c fd "
// ^--^--(seq 7/11)
// ^-- start waveform data 0x00 to 0xA0, index 05 to 54
// (sequence #11)
// "INDEX: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 "
// "DATA: 27 00 00 11 11 0b 13 21 23 1a 1b 22 1e 1a 1d 13 21 1d 26 28 1f 19 1a 18 09 2c 2c 2c 1a 1b fd "
int divSize = (len / numDivisions) + 6;
QByteArray wfPacket;
for (int i = 0; i < numDivisions; i++) {
wfPacket = r.mid(pos - 6, 9); // First part of packet
wfPacket = r.mid(pos - 6, 9); // First part of packet
char tens = ((i + 1) / 10);
char units = ((i + 1) - (10 * tens));
wfPacket[7] = units | (tens << 4);
tens = (numDivisions / 10);
units = (numDivisions - (10 * tens));
wfPacket[8] = units | (tens << 4);
if (i == 0) {
//Just send initial data, first BCD encode the max number:
wfPacket.append(r.mid(pos + 3, 12));
}
else
{
wfPacket.append(r.mid((pos + 15) + ((i - 1) * divSize), divSize));
}
if (i < numDivisions - 1) {
wfPacket.append('\xfd');
}
//printHex(wfPacket, false, true);
emit receive(wfPacket);
wfPacket.clear();
}
//qDebug(logUdp()) << "Waterfall packet len" << len << "Num Divisions" << numDivisions << "Division Size" << divSize;
}
else {
// Not waterfall data or split not enabled.
emit receive(r.mid(0x15));
}
//qDebug(logUdp()) << "Got incoming CIV datagram" << r.mid(0x15).length();
}
}
}
break;
}
}
udpBase::dataReceived(r); // Call parent function to process the rest.
r.clear();
datagram.clear();
}
}

56
udpcivdata.h 100644
Wyświetl plik

@ -0,0 +1,56 @@
// Class for all (pseudo) serial communications
#ifndef UDPCIVDATA_H
#define UDPCIVDATA_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostInfo>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QByteArray>
#include <QVector>
#include <QMap>
#include <QUuid>
// Allow easy endian-ness conversions
#include <QtEndian>
// Needed for audio
#include <QBuffer>
#include <QThread>
#include <QDebug>
#include "packettypes.h"
#include "udpbase.h"
class udpCivData : public udpBase
{
Q_OBJECT
public:
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport);
~udpCivData();
QMutex serialmutex;
signals:
int receive(QByteArray);
public slots:
void send(QByteArray d);
private:
void watchdog();
void dataReceived();
void sendOpenClose(bool close);
QTimer* startCivDataTimer = Q_NULLPTR;
bool splitWaterfall = false;
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -1,6 +1,7 @@
#ifndef UDPHANDLER_H
#define UDPHANDLER_H
#include <QObject>
#include <QUdpSocket>
#include <QNetworkDatagram>
@ -22,207 +23,11 @@
#include <QDebug>
#include "audiohandler.h"
#include "packettypes.h"
struct udpPreferences {
QString ipAddress;
quint16 controlLANPort;
quint16 serialLANPort;
quint16 audioLANPort;
QString username;
QString password;
QString clientName;
quint8 waterfallFormat;
};
struct networkStatus {
quint8 rxAudioBufferPercent;
quint8 txAudioBufferPercent;
quint8 rxAudioLevel;
quint8 txAudioLevel;
quint16 rxLatency;
quint16 txLatency;
bool rxUnderrun;
bool txUnderrun;
quint16 rxCurrentLatency;
quint16 txCurrentLatency;
quint32 packetsSent=0;
quint32 packetsLost=0;
quint16 rtt=0;
quint32 networkLatency=0;
QString message;
};
void passcode(QString in, QByteArray& out);
QByteArray parseNullTerminatedString(QByteArray c, int s);
// Parent class that contains all common items.
class udpBase : public QObject
{
public:
~udpBase();
void init(quint16 local);
void reconnect();
void dataReceived(QByteArray r);
void sendPing();
void sendRetransmitRange(quint16 first, quint16 second, quint16 third,quint16 fourth);
void sendControl(bool tracked,quint8 id, quint16 seq);
void printHex(const QByteArray& pdata);
void printHex(const QByteArray& pdata, bool printVert, bool printHoriz);
//QTime timeStarted;
QUdpSocket* udp=Q_NULLPTR;
uint32_t myId = 0;
uint32_t remoteId = 0;
uint16_t authSeq = 0x30;
uint16_t sendSeqB = 0;
uint16_t sendSeq = 1;
uint16_t lastReceivedSeq = 1;
uint16_t pkt0SendSeq = 0;
uint16_t periodicSeq = 0;
quint64 latency = 0;
QString username = "";
QString password = "";
QHostAddress radioIP;
QHostAddress localIP;
bool isAuthenticated = false;
quint16 localPort=0;
quint16 port=0;
bool periodicRunning = false;
bool sentPacketConnect2 = false;
QTime lastReceived =QTime::currentTime();
QMutex udpMutex;
QMutex txBufferMutex;
QMutex rxBufferMutex;
QMutex missingMutex;
struct SEQBUFENTRY {
QTime timeSent;
uint16_t seqNum;
QByteArray data;
quint8 retransmitCount;
};
QMap<quint16, QTime> rxSeqBuf;
QMap<quint16, SEQBUFENTRY> txSeqBuf;
QMap<quint16, int> rxMissing;
void sendTrackedPacket(QByteArray d);
void purgeOldEntries();
QTimer* areYouThereTimer = Q_NULLPTR; // Send are-you-there packets every second until a response is received.
QTimer* pingTimer = Q_NULLPTR; // Start sending pings immediately.
QTimer* idleTimer = Q_NULLPTR; // Start watchdog once we are connected.
QTimer* watchdogTimer = Q_NULLPTR;
QTimer* retransmitTimer = Q_NULLPTR;
QDateTime lastPingSentTime;
uint16_t pingSendSeq = 0;
quint16 areYouThereCounter=0;
quint32 packetsSent=0;
quint32 packetsLost=0;
quint16 seqPrefix = 0;
QString connectionType="";
int congestion = 0;
private:
void sendRetransmitRequest();
};
// Class for all (pseudo) serial communications
class udpCivData : public udpBase
{
Q_OBJECT
public:
udpCivData(QHostAddress local, QHostAddress ip, quint16 civPort, bool splitWf, quint16 lport);
~udpCivData();
QMutex serialmutex;
signals:
int receive(QByteArray);
public slots:
void send(QByteArray d);
private:
void watchdog();
void dataReceived();
void sendOpenClose(bool close);
QTimer* startCivDataTimer = Q_NULLPTR;
bool splitWaterfall = false;
};
// Class for all audio communications.
class udpAudio : public udpBase
{
Q_OBJECT
public:
udpAudio(QHostAddress local, QHostAddress ip, quint16 aport, quint16 lport, audioSetup rxSetup, audioSetup txSetup);
~udpAudio();
int audioLatency = 0;
signals:
void haveAudioData(audioPacket data);
void setupTxAudio(audioSetup setup);
void setupRxAudio(audioSetup setup);
void haveChangeLatency(quint16 value);
void haveSetVolume(unsigned char value);
void haveRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void haveTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
public slots:
void changeLatency(quint16 value);
void setVolume(unsigned char value);
void getRxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void getTxLevels(quint16 amplitude, quint16 latency, quint16 current, bool under);
void receiveAudioData(audioPacket audio);
private:
void sendTxAudio();
void dataReceived();
void watchdog();
uint16_t sendAudioSeq = 0;
audioHandler* rxaudio = Q_NULLPTR;
QThread* rxAudioThread = Q_NULLPTR;
audioHandler* txaudio = Q_NULLPTR;
QThread* txAudioThread = Q_NULLPTR;
QTimer* txAudioTimer = Q_NULLPTR;
bool enableTx = true;
QMutex audioMutex;
};
#include "audiohandler.h"
#include "udpbase.h"
#include "udpcivdata.h"
#include "udpaudio.h"

Wyświetl plik

@ -139,7 +139,10 @@ SOURCES += main.cpp\
rigcommander.cpp \
freqmemory.cpp \
rigidentities.cpp \
udpbase.cpp \
udphandler.cpp \
udpcivdata.cpp \
udpaudio.cpp \
logcategories.cpp \
audiohandler.cpp \
audioconverter.cpp \
@ -155,7 +158,10 @@ HEADERS += servermain.h \
rigcommander.h \
freqmemory.h \
rigidentities.h \
udpbase.h \
udphandler.h \
udpcivdata.h \
udpaudio.h \
logcategories.h \
audiohandler.h \
audioconverter.h \

Wyświetl plik

@ -162,7 +162,10 @@ SOURCES += main.cpp\
rigcommander.cpp \
freqmemory.cpp \
rigidentities.cpp \
udpbase.cpp \
udphandler.cpp \
udpcivdata.cpp \
udpaudio.cpp \
logcategories.cpp \
audiohandler.cpp \
audioconverter.cpp \
@ -185,7 +188,10 @@ HEADERS += wfmain.h \
rigcommander.h \
freqmemory.h \
rigidentities.h \
udpbase.h \
udphandler.h \
udpcivdata.h \
udpaudio.h \
logcategories.h \
audiohandler.h \
audioconverter.h \

Wyświetl plik

@ -220,6 +220,9 @@
<ClCompile Include="selectradio.cpp" />
<ClCompile Include="tcpserver.cpp" />
<ClCompile Include="transceiveradjustments.cpp" />
<ClCompile Include="udpaudio.cpp" />
<ClCompile Include="udpbase.cpp" />
<ClCompile Include="udpcivdata.cpp" />
<ClCompile Include="udphandler.cpp" />
<ClCompile Include="udpserver.cpp" />
<ClCompile Include="wfmain.cpp" />
@ -269,6 +272,9 @@
</QtMoc>
<QtMoc Include="udpserver.h">
</QtMoc>
<QtMoc Include="udpaudio.h" />
<ClInclude Include="udpbase.h" />
<QtMoc Include="udpcivdata.h" />
<ClInclude Include="ulaw.h" />
<QtMoc Include="wfmain.h">
</QtMoc>

Wyświetl plik

@ -119,6 +119,15 @@
<ClCompile Include="audioconverter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpbase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpaudio.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="udpcivdata.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<QtMoc Include="aboutbox.h">
@ -208,6 +217,12 @@
<QtMoc Include="audioconverter.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udpcivdata.h">
<Filter>Header Files</Filter>
</QtMoc>
<QtMoc Include="udpaudio.h">
<Filter>Header Files</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt">
@ -374,4 +389,9 @@
<ItemGroup>
<ResourceCompile Include=".\wfview_resource.rc" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="udpbase.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>