protocol 2
This commit is contained in:
@@ -88,6 +88,7 @@ DataEngine::DataEngine(QObject *parent)
|
||||
, m_hpsdrDevices(0)
|
||||
, m_configure(10)
|
||||
, m_timeout(5000)
|
||||
, m_discoveryProtocol(1)
|
||||
, m_remainingTime(0)
|
||||
, m_RxFrequencyChange(0)
|
||||
, m_forwardPower(0)
|
||||
@@ -459,33 +460,32 @@ bool DataEngine::startDataEngineWithoutConnection() {
|
||||
|
||||
bool DataEngine::findHPSDRDevices() {
|
||||
|
||||
if (!m_discoverer) createDiscoverer();
|
||||
for (int protocolVersion = 1; protocolVersion <= 2; ++protocolVersion) {
|
||||
|
||||
// HPSDR network IO thread
|
||||
if (!startDiscoverer(QThread::NormalPriority)) {
|
||||
m_discoveryProtocol = protocolVersion;
|
||||
|
||||
if (!m_discoverer)
|
||||
createDiscoverer();
|
||||
|
||||
if (!startDiscoverer(QThread::NormalPriority)) {
|
||||
|
||||
io.networkIOMutex.lock();
|
||||
DATA_ENGINE_DEBUG << "HPSDR device discovery thread could not be started.";
|
||||
io.networkIOMutex.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
io.networkIOMutex.lock();
|
||||
DATA_ENGINE_DEBUG << "HPSDR device discovery thread could not be started.";
|
||||
io.networkIOMutex.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
io.networkIOMutex.lock();
|
||||
DATA_ENGINE_DEBUG << "HPSDR network device detection...please wait.";
|
||||
set->setSystemMessage("HPSDR network device detection...please wait", 0);
|
||||
io.devicefound.wait(&io.networkIOMutex);
|
||||
|
||||
m_hpsdrDevices = set->getHpsdrNetworkDevices();
|
||||
if (m_hpsdrDevices == 0) {
|
||||
DATA_ENGINE_DEBUG << "HPSDR network device detection...please wait.";
|
||||
set->setSystemMessage("HPSDR network device detection...please wait", 0);
|
||||
io.devicefound.wait(&io.networkIOMutex);
|
||||
|
||||
m_hpsdrDevices = set->getHpsdrNetworkDevices();
|
||||
io.networkIOMutex.unlock();
|
||||
stopDiscoverer();
|
||||
DATA_ENGINE_DEBUG << "no device found. HPSDR hardware powered? Network connection established?";
|
||||
set->setSystemMessage("no device found. HPSDR hardware powered? Network connection established?", 10000);
|
||||
|
||||
setSystemState(QSDR::HwIOError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
||||
}
|
||||
else {
|
||||
if (m_hpsdrDevices == 0)
|
||||
continue;
|
||||
|
||||
emit clearSystemMessageEvent();
|
||||
if (m_hpsdrDevices > 1)
|
||||
@@ -493,31 +493,35 @@ bool DataEngine::findHPSDRDevices() {
|
||||
|
||||
QList<TNetworkDevicecard> metisList = set->getMetisCardsList();
|
||||
DATA_ENGINE_DEBUG << "found " << metisList.count() << " network device(s)";
|
||||
|
||||
for (int i = 0; i < metisList.count(); i++) {
|
||||
|
||||
for (int i = 0; i < metisList.count(); i++) {
|
||||
DATA_ENGINE_DEBUG << "Device "
|
||||
<< i << " @ "
|
||||
<< qPrintable(metisList.at(i).ip_address.toString())
|
||||
//<< " [" << qPrintable((char *) &metisList.at(i).mac_address) << "]";
|
||||
<< " [" << metisList.at(i).mac_address << "]";
|
||||
<< " [" << metisList.at(i).mac_address << "]"
|
||||
<< " P" << metisList.at(i).protocolVersion;
|
||||
}
|
||||
|
||||
io.hpsdrDeviceIPAddress = set->getCurrentMetisCard().ip_address;
|
||||
io.hpsdrDeviceName = set->getCurrentMetisCard().boardName;
|
||||
DATA_ENGINE_DEBUG << "using HPSDR network device at " << qPrintable(io.hpsdrDeviceIPAddress.toString());
|
||||
|
||||
//Sleep(100);
|
||||
SleeperThread::msleep(100);
|
||||
|
||||
// stop the discovery thread
|
||||
io.networkIOMutex.unlock();
|
||||
stopDiscoverer();
|
||||
if (set->getCurrentMetisCard().protocolVersion >= 2) {
|
||||
io.hermesFW = set->getCurrentMetisCard().firmwareVersion;
|
||||
set->setHermesVersion(io.hermesFW);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getFirmwareVersions()) return true;
|
||||
if (getFirmwareVersions())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
DATA_ENGINE_DEBUG << "no device found. HPSDR hardware powered? Network connection established?";
|
||||
set->setSystemMessage("no device found. HPSDR hardware powered? Network connection established?", 10000);
|
||||
setSystemState(QSDR::HwIOError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1434,7 +1438,10 @@ void DataEngine::disconnectDSPSlots() {
|
||||
|
||||
void DataEngine::createDiscoverer() {
|
||||
|
||||
m_discoverer = new DiscovererP1(&io);
|
||||
if (m_discoveryProtocol >= 2)
|
||||
m_discoverer = new DiscovererP2(&io);
|
||||
else
|
||||
m_discoverer = new DiscovererP1(&io);
|
||||
|
||||
m_discoveryThread = new QThreadEx();
|
||||
m_discoverer->moveToThread(m_discoveryThread);
|
||||
|
||||
@@ -47,9 +47,10 @@
|
||||
#include "cusdr_dataIO.h"
|
||||
#include "cusdr_receiver.h"
|
||||
#include "cusdr_chirpProcessor.h"
|
||||
#include "cusdr_audioReceiver.h"
|
||||
#include "cusdr_discoverer_P1.h"
|
||||
#include "Util/qcircularbuffer.h"
|
||||
#include "cusdr_audioReceiver.h"
|
||||
#include "cusdr_discoverer_P1.h"
|
||||
#include "cusdr_discoverer_P2.h"
|
||||
#include "Util/qcircularbuffer.h"
|
||||
#include "QtDSP/qtdsp_fft.h"
|
||||
#include "QtDSP/qtdsp_filter.h"
|
||||
#include "QtDSP/qtdsp_dualModeAverager.h"
|
||||
@@ -198,7 +199,7 @@ private:
|
||||
AudioEngine* m_audioEngine;
|
||||
AudioOutProcessor* m_audioOutProcessor;
|
||||
ChirpProcessor* m_chirpProcessor;
|
||||
DiscovererP1* m_discoverer;
|
||||
QObject* m_discoverer;
|
||||
|
||||
QThreadEx* m_discoveryThread;
|
||||
QThreadEx* m_dataIOThread;
|
||||
@@ -256,9 +257,10 @@ private:
|
||||
int m_fwCount;
|
||||
int m_configure;
|
||||
int m_timeout;
|
||||
int m_txFrame;
|
||||
int m_bytes;
|
||||
int m_remainingTime;
|
||||
int m_txFrame;
|
||||
int m_bytes;
|
||||
int m_discoveryProtocol;
|
||||
int m_remainingTime;
|
||||
int m_found;
|
||||
int m_RxFrequencyChange;
|
||||
int m_counter;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define LOG_DATAIO
|
||||
|
||||
#include "cusdr_dataIO.h"
|
||||
#include "cusdr_protocol2_io.h"
|
||||
#include "soundout.h"
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
@@ -41,6 +42,7 @@ DataIO::DataIO(THPSDRParameter *ioData)
|
||||
, set(Settings::instance())
|
||||
, io(ioData)
|
||||
, m_dataIOSocketOn(false)
|
||||
, m_protocol2(0)
|
||||
, m_setNetworkDeviceHeader(true)
|
||||
, m_sequence(0)
|
||||
, m_oldSequence(-1)
|
||||
@@ -56,6 +58,7 @@ DataIO::DataIO(THPSDRParameter *ioData)
|
||||
, m_stopped(false)
|
||||
{
|
||||
m_dataIOSocket = 0;
|
||||
m_protocol2 = new Protocol2DataPath(set, io, this);
|
||||
|
||||
m_metisGetDataSignature.resize(3);
|
||||
m_metisGetDataSignature[0] = (char)0xEF;
|
||||
@@ -119,6 +122,9 @@ void DataIO::stop() {
|
||||
m_stopped = true;
|
||||
io->networkIOMutex.unlock();
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
m_protocol2->stop();
|
||||
|
||||
if(m_pSoundCardOut) {
|
||||
SleeperThread::msleep(100);
|
||||
m_pSoundCardOut->Stop();
|
||||
@@ -128,6 +134,11 @@ void DataIO::stop() {
|
||||
|
||||
void DataIO::initDataReceiverSocket() {
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive()) {
|
||||
m_dataIOSocketOn = m_protocol2->initSockets();
|
||||
return;
|
||||
}
|
||||
|
||||
m_dataIOSocket = new QUdpSocket();
|
||||
|
||||
int newBufferSize;
|
||||
@@ -230,6 +241,9 @@ void DataIO::initDataReceiverSocket() {
|
||||
|
||||
void DataIO::readDeviceData() {
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
return;
|
||||
|
||||
while (m_dataIOSocket->hasPendingDatagrams()) {
|
||||
|
||||
QMutexLocker locker(&io->networkIOMutex);
|
||||
@@ -357,6 +371,11 @@ void DataIO::readData() {
|
||||
|
||||
void DataIO::sendInitFramesToNetworkDevice(int rx) {
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive()) {
|
||||
m_protocol2->sendInitFramesToNetworkDevice(rx);
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray initDatagram;
|
||||
initDatagram.resize(1032);
|
||||
|
||||
@@ -446,6 +465,11 @@ void DataIO::sendInitFramesToNetworkDevice(int rx) {
|
||||
|
||||
void DataIO::networkDeviceStartStop(char value) {
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive()) {
|
||||
m_protocol2->networkDeviceStartStop(value);
|
||||
return;
|
||||
}
|
||||
|
||||
TNetworkDevicecard metis = set->getCurrentMetisCard();
|
||||
//QUdpSocket socket;
|
||||
|
||||
@@ -517,6 +541,9 @@ void DataIO::sendAudio(u_char *buf) {
|
||||
|
||||
void DataIO::writeData() {
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
return;
|
||||
|
||||
if (m_setNetworkDeviceHeader) {
|
||||
|
||||
m_outDatagram.resize(0);
|
||||
@@ -561,6 +588,9 @@ void DataIO::setManualSocketBufferSize(QObject *sender, bool value) {
|
||||
|
||||
Q_UNUSED (sender)
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
return;
|
||||
|
||||
m_manualBufferSize = value;
|
||||
DATAIO_DEBUG << "m_manualBufferSize to change = " << m_manualBufferSize;
|
||||
int socketBufferSize = 1032 * set->getSocketBufferSize();
|
||||
@@ -601,6 +631,9 @@ void DataIO::setSocketBufferSize(QObject *sender, int value) {
|
||||
|
||||
Q_UNUSED (sender)
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
return;
|
||||
|
||||
int socketBufferSize = value * 1024;
|
||||
DATAIO_DEBUG << "m_socketBufferSize = " << value;
|
||||
|
||||
@@ -621,6 +654,9 @@ void DataIO::setSampleRate(QObject *sender, int value) {
|
||||
|
||||
Q_UNUSED(sender)
|
||||
|
||||
if (m_protocol2 && m_protocol2->isActive())
|
||||
return;
|
||||
|
||||
int bufferSize;
|
||||
io->networkIOMutex.lock();
|
||||
switch (value) {
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
//#include <QWaitCondition>
|
||||
//#include <QThread>
|
||||
|
||||
#include "cusdr_settings.h"
|
||||
#include "soundout.h"
|
||||
#include "cusdr_settings.h"
|
||||
#include "soundout.h"
|
||||
|
||||
#ifdef LOG_DATAIO
|
||||
# define DATAIO_DEBUG qDebug().nospace() << "DataIO::\t"
|
||||
@@ -46,7 +46,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
class DataIO : public QObject {
|
||||
class DataIO : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
@@ -71,10 +71,11 @@ private slots:
|
||||
void setSocketBufferSize(QObject *sender, int value);
|
||||
void readDeviceData();
|
||||
|
||||
private:
|
||||
Settings* set;
|
||||
QUdpSocket* m_dataIOSocket;
|
||||
//QMutex m_mutex;
|
||||
private:
|
||||
Settings* set;
|
||||
QUdpSocket* m_dataIOSocket;
|
||||
class Protocol2DataPath* m_protocol2;
|
||||
//QMutex m_mutex;
|
||||
QByteArray m_commandDatagram;
|
||||
QByteArray m_datagram;
|
||||
QByteArray m_wbDatagram;
|
||||
|
||||
@@ -226,6 +226,8 @@ int DiscovererP1::findHPSDRDevices() {
|
||||
|
||||
mcP1.boardID = no;
|
||||
mcP1.boardName = str;
|
||||
mcP1.protocolVersion = 1;
|
||||
mcP1.firmwareVersion = m_deviceDatagram.at(9) & 0xFF;
|
||||
io->networkIOMutex.lock();
|
||||
DISCOVERER_DEBUG << "Device board ID: " << no;
|
||||
DISCOVERER_DEBUG << "Device is: " << qPrintable(str);
|
||||
|
||||
@@ -44,6 +44,25 @@ DiscovererP2::~DiscovererP2() {
|
||||
|
||||
TNetworkDevicecard mcP2;
|
||||
|
||||
namespace {
|
||||
|
||||
static QString protocol2BoardName(int boardId) {
|
||||
|
||||
switch (boardId) {
|
||||
case 0: return "ATLAS";
|
||||
case 1: return "Hermes";
|
||||
case 2: return "Hermes";
|
||||
case 3: return "Angelia";
|
||||
case 4: return "Orion";
|
||||
case 5: return "Orion MkII";
|
||||
case 6: return "Hermes-Lite";
|
||||
case 10: return "Saturn";
|
||||
default: return QString("Protocol2-%1").arg(boardId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DiscovererP2::initHPSDRDevice() {
|
||||
|
||||
m_searchTime.start();
|
||||
@@ -185,7 +204,11 @@ int DiscovererP2::findHPSDRDevices() {
|
||||
m_deviceDatagram.resize(socket.pendingDatagramSize());
|
||||
socket.readDatagram(m_deviceDatagram.data(), m_deviceDatagram.size(), &mcP2.ip_address, &port);
|
||||
|
||||
if (m_deviceDatagram[0] == (char)0 && m_deviceDatagram[1] == (char)0 && m_deviceDatagram[2] == (char)0 && m_deviceDatagram[3] == (char)0) {
|
||||
if (m_deviceDatagram.size() >= 14 &&
|
||||
m_deviceDatagram[0] == (char)0 &&
|
||||
m_deviceDatagram[1] == (char)0 &&
|
||||
m_deviceDatagram[2] == (char)0 &&
|
||||
m_deviceDatagram[3] == (char)0) {
|
||||
status = m_deviceDatagram[4] & 0xFF;
|
||||
|
||||
if (status == 2 || status == 3) {
|
||||
@@ -196,29 +219,16 @@ int DiscovererP2::findHPSDRDevices() {
|
||||
|
||||
io->networkIOMutex.lock();
|
||||
DISCOVERER_DEBUG << "Device on protocol 2 found at " << qPrintable(mcP2.ip_address.toString()) << ":" << port << "; Mac addr: [" << mcP2.mac_address << "]";
|
||||
DISCOVERER_DEBUG << "Device protocol 2 code version: " << qPrintable(QString::number(m_deviceDatagram[13], 10));
|
||||
DISCOVERER_DEBUG << "Device protocol 2 code version: " << qPrintable(QString::number(m_deviceDatagram[13] & 0xFF, 10));
|
||||
io->networkIOMutex.unlock();
|
||||
|
||||
device = m_deviceDatagram[11] & 0xFF;
|
||||
QString str;
|
||||
if (device == 0)
|
||||
str = "ATLAS";
|
||||
else if (device == 1)
|
||||
str = "HERMES_ANAN-10_100)";
|
||||
else if (device == 2)
|
||||
str = "HERMES_ANAN_10E_100B)";
|
||||
else if (device == 3)
|
||||
str = "ANGELA_ANAN_100D)";
|
||||
else if (device == 4)
|
||||
str = "ORION_ANAN_200D)";
|
||||
else if (device == 5)
|
||||
str = "ORION_Mk_II_ANAN-7_8000DLE)";
|
||||
else if (device == 6) {
|
||||
str = "Hermes-Lite";
|
||||
}
|
||||
QString str = protocol2BoardName(device);
|
||||
|
||||
mcP2.boardID = device;
|
||||
mcP2.boardName = str;
|
||||
mcP2.protocolVersion = m_deviceDatagram[12] & 0xFF;
|
||||
mcP2.firmwareVersion = m_deviceDatagram[13] & 0xFF;
|
||||
io->networkIOMutex.lock();
|
||||
DISCOVERER_DEBUG << "Protocol 2 device board ID: " << device;
|
||||
DISCOVERER_DEBUG << "Protocol 2 device is: " << qPrintable(str);
|
||||
@@ -274,20 +284,11 @@ void DiscovererP2::clear() {
|
||||
|
||||
void DiscovererP2::shutdownHPSDRDevice() {
|
||||
|
||||
long sequence = 0;
|
||||
QByteArray arr;
|
||||
arr.resize(1444);
|
||||
|
||||
arr[0] = sequence >> 24;
|
||||
arr[1] = sequence >> 16;
|
||||
arr[2] = sequence >> 8;
|
||||
arr[3] = sequence;
|
||||
arr[4] = 0;
|
||||
QByteArray arr(1444, 0x00);
|
||||
|
||||
QUdpSocket socket;
|
||||
QHostAddress addr = mcP2.ip_address;
|
||||
|
||||
if (socket.writeDatagram(arr, addr, HIGH_PRIORITY_FROM_HOST_PORT) < 0) {
|
||||
DISCOVERER_DEBUG << "protocol 2 forced shutdown socket write failed.";
|
||||
}
|
||||
if (socket.writeDatagram(arr, addr, HIGH_PRIORITY_FROM_HOST_PORT) < 0)
|
||||
DISCOVERER_DEBUG << "protocol 2 forced shutdown socket write failed.";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,439 @@
|
||||
/**
|
||||
* @file cusdr_protocol2_io.cpp
|
||||
* @brief openHPSDR Protocol 2 transport helper
|
||||
*/
|
||||
|
||||
#define LOG_DATAIO
|
||||
|
||||
#include "cusdr_protocol2_io.h"
|
||||
|
||||
#ifdef LOG_DATAIO
|
||||
# define PROTOCOL2_DEBUG qDebug().nospace() << "Protocol2::\t"
|
||||
#else
|
||||
# define PROTOCOL2_DEBUG nullDebug()
|
||||
#endif
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
static const quint16 kProtocol2DdcSpecificPort = 1025;
|
||||
static const quint16 kProtocol2HighPriorityToPcPort = 1025;
|
||||
static const quint16 kProtocol2DdcAudioPort = 1028;
|
||||
static const quint16 kProtocol2DucIqPort = 1029;
|
||||
static const quint16 kProtocol2DdcBasePort = 1035;
|
||||
static const int kProtocol2GeneralPacketSize = 60;
|
||||
static const int kProtocol2HighPriorityPacketSize = 1444;
|
||||
static const int kProtocol2StatusPacketMinSize = 31;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Protocol2DataPath::Protocol2DataPath(Settings *settings, THPSDRParameter *ioData, QObject *parent)
|
||||
: QObject(parent)
|
||||
, set(settings)
|
||||
, io(ioData)
|
||||
, m_commandSocket(0)
|
||||
, m_statusSocket(0)
|
||||
, m_heartbeatTimer(0)
|
||||
, m_running(false)
|
||||
, m_generalSequence(0)
|
||||
, m_ddcSequence(0)
|
||||
, m_highPrioritySequence(0)
|
||||
{
|
||||
m_legacyControlBytes = QByteArray(5, 0x00);
|
||||
}
|
||||
|
||||
Protocol2DataPath::~Protocol2DataPath() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool Protocol2DataPath::isActive() const {
|
||||
return set->getCurrentMetisCard().protocolVersion >= 2;
|
||||
}
|
||||
|
||||
bool Protocol2DataPath::initSockets() {
|
||||
|
||||
if (!isActive())
|
||||
return false;
|
||||
|
||||
closeSockets();
|
||||
|
||||
const QHostAddress localAddress(set->getHPSDRDeviceLocalAddr());
|
||||
const quint16 discoveryPort = set->getMetisPort();
|
||||
const int receivers = qMax(1, set->getNumberOfReceivers());
|
||||
|
||||
m_commandSocket = new QUdpSocket(this);
|
||||
if (!m_commandSocket->bind(localAddress, discoveryPort, QUdpSocket::DontShareAddress)) {
|
||||
PROTOCOL2_DEBUG << "command socket bind failed on port " << discoveryPort;
|
||||
closeSockets();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_statusSocket = new QUdpSocket(this);
|
||||
if (!m_statusSocket->bind(localAddress, kProtocol2HighPriorityToPcPort, QUdpSocket::DontShareAddress)) {
|
||||
PROTOCOL2_DEBUG << "status socket bind failed on port " << kProtocol2HighPriorityToPcPort;
|
||||
closeSockets();
|
||||
return false;
|
||||
}
|
||||
CHECKED_CONNECT(
|
||||
m_statusSocket,
|
||||
SIGNAL(readyRead()),
|
||||
this,
|
||||
SLOT(readStatusData()));
|
||||
|
||||
m_ddcSampleBuffers.clear();
|
||||
m_ddcSockets.clear();
|
||||
for (int rx = 0; rx < receivers; ++rx) {
|
||||
|
||||
QUdpSocket *socket = new QUdpSocket(this);
|
||||
const quint16 port = kProtocol2DdcBasePort + rx;
|
||||
if (!socket->bind(localAddress, port, QUdpSocket::DontShareAddress)) {
|
||||
PROTOCOL2_DEBUG << "DDC socket bind failed on port " << port;
|
||||
delete socket;
|
||||
closeSockets();
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECKED_CONNECT(
|
||||
socket,
|
||||
SIGNAL(readyRead()),
|
||||
this,
|
||||
SLOT(readDdcData()));
|
||||
|
||||
m_ddcSockets.append(socket);
|
||||
m_ddcSampleBuffers.append(QList<Protocol2IQSample>());
|
||||
}
|
||||
|
||||
m_heartbeatTimer = new QTimer(this);
|
||||
m_heartbeatTimer->setInterval(100);
|
||||
CHECKED_CONNECT(
|
||||
m_heartbeatTimer,
|
||||
SIGNAL(timeout()),
|
||||
this,
|
||||
SLOT(sendHeartbeat()));
|
||||
|
||||
m_partialLegacyFrame.clear();
|
||||
m_partialLegacyFrame.reserve(BUFFER_SIZE);
|
||||
m_legacyControlBytes.fill(0x00, 5);
|
||||
m_running = false;
|
||||
|
||||
PROTOCOL2_DEBUG << "sockets initialized for " << receivers << " receiver(s).";
|
||||
return true;
|
||||
}
|
||||
|
||||
void Protocol2DataPath::stop() {
|
||||
|
||||
m_running = false;
|
||||
|
||||
if (m_heartbeatTimer)
|
||||
m_heartbeatTimer->stop();
|
||||
|
||||
if (m_commandSocket)
|
||||
sendHighPriorityPacket(false);
|
||||
|
||||
closeSockets();
|
||||
}
|
||||
|
||||
void Protocol2DataPath::sendInitFramesToNetworkDevice(int rx) {
|
||||
Q_UNUSED(rx)
|
||||
}
|
||||
|
||||
void Protocol2DataPath::networkDeviceStartStop(char value) {
|
||||
|
||||
if (!m_commandSocket)
|
||||
return;
|
||||
|
||||
if (value == 0) {
|
||||
m_running = false;
|
||||
if (m_heartbeatTimer)
|
||||
m_heartbeatTimer->stop();
|
||||
sendHighPriorityPacket(false);
|
||||
return;
|
||||
}
|
||||
|
||||
sendGeneralPacket();
|
||||
sendDdcSpecificPacket();
|
||||
sendHighPriorityPacket(true);
|
||||
|
||||
m_running = true;
|
||||
if (m_heartbeatTimer)
|
||||
m_heartbeatTimer->start();
|
||||
}
|
||||
|
||||
void Protocol2DataPath::readStatusData() {
|
||||
|
||||
while (m_statusSocket && m_statusSocket->hasPendingDatagrams()) {
|
||||
|
||||
m_statusDatagram.resize(m_statusSocket->pendingDatagramSize());
|
||||
m_statusSocket->readDatagram(m_statusDatagram.data(), m_statusDatagram.size());
|
||||
|
||||
if (m_statusDatagram.size() >= kProtocol2StatusPacketMinSize)
|
||||
updateLegacyControlBytes(m_statusDatagram);
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol2DataPath::readDdcData() {
|
||||
|
||||
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
|
||||
const int rx = m_ddcSockets.indexOf(socket);
|
||||
|
||||
if (rx < 0)
|
||||
return;
|
||||
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
|
||||
QByteArray datagram;
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
socket->readDatagram(datagram.data(), datagram.size());
|
||||
|
||||
if (datagram.size() < 16)
|
||||
continue;
|
||||
|
||||
const int bitsPerSample = ((quint8)datagram.at(12) << 8) | (quint8)datagram.at(13);
|
||||
const int sampleCount = ((quint8)datagram.at(14) << 8) | (quint8)datagram.at(15);
|
||||
const int payloadBytes = 16 + sampleCount * 6;
|
||||
|
||||
if (bitsPerSample != 24 || datagram.size() < payloadBytes)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < sampleCount; ++i) {
|
||||
|
||||
const char *sample = datagram.constData() + 16 + i * 6;
|
||||
Protocol2IQSample iqSample;
|
||||
iqSample.i = read24BitSample(sample);
|
||||
iqSample.q = read24BitSample(sample + 3);
|
||||
m_ddcSampleBuffers[rx].append(iqSample);
|
||||
}
|
||||
}
|
||||
|
||||
tryProduceLegacyFrames();
|
||||
}
|
||||
|
||||
void Protocol2DataPath::sendHeartbeat() {
|
||||
|
||||
if (!m_running)
|
||||
return;
|
||||
|
||||
sendHighPriorityPacket(true);
|
||||
}
|
||||
|
||||
void Protocol2DataPath::closeSockets() {
|
||||
|
||||
if (m_heartbeatTimer) {
|
||||
delete m_heartbeatTimer;
|
||||
m_heartbeatTimer = 0;
|
||||
}
|
||||
|
||||
foreach (QUdpSocket *socket, m_ddcSockets) {
|
||||
socket->close();
|
||||
delete socket;
|
||||
}
|
||||
m_ddcSockets.clear();
|
||||
m_ddcSampleBuffers.clear();
|
||||
|
||||
if (m_statusSocket) {
|
||||
m_statusSocket->close();
|
||||
delete m_statusSocket;
|
||||
m_statusSocket = 0;
|
||||
}
|
||||
|
||||
if (m_commandSocket) {
|
||||
m_commandSocket->close();
|
||||
delete m_commandSocket;
|
||||
m_commandSocket = 0;
|
||||
}
|
||||
|
||||
m_partialLegacyFrame.clear();
|
||||
}
|
||||
|
||||
void Protocol2DataPath::sendGeneralPacket() {
|
||||
|
||||
QByteArray datagram(kProtocol2GeneralPacketSize, 0x00);
|
||||
|
||||
datagram[0] = (m_generalSequence >> 24) & 0xFF;
|
||||
datagram[1] = (m_generalSequence >> 16) & 0xFF;
|
||||
datagram[2] = (m_generalSequence >> 8) & 0xFF;
|
||||
datagram[3] = m_generalSequence & 0xFF;
|
||||
datagram[4] = 0x00;
|
||||
datagram[5] = (kProtocol2DdcSpecificPort >> 8) & 0xFF;
|
||||
datagram[6] = kProtocol2DdcSpecificPort & 0xFF;
|
||||
datagram[7] = (kProtocol2DucIqPort >> 8) & 0xFF;
|
||||
datagram[8] = kProtocol2DucIqPort & 0xFF;
|
||||
datagram[9] = (HIGH_PRIORITY_FROM_HOST_PORT >> 8) & 0xFF;
|
||||
datagram[10] = HIGH_PRIORITY_FROM_HOST_PORT & 0xFF;
|
||||
datagram[11] = (kProtocol2HighPriorityToPcPort >> 8) & 0xFF;
|
||||
datagram[12] = kProtocol2HighPriorityToPcPort & 0xFF;
|
||||
datagram[13] = (kProtocol2DdcAudioPort >> 8) & 0xFF;
|
||||
datagram[14] = kProtocol2DdcAudioPort & 0xFF;
|
||||
datagram[15] = (kProtocol2DucIqPort >> 8) & 0xFF;
|
||||
datagram[16] = kProtocol2DucIqPort & 0xFF;
|
||||
datagram[17] = (kProtocol2DdcBasePort >> 8) & 0xFF;
|
||||
datagram[18] = kProtocol2DdcBasePort & 0xFF;
|
||||
datagram[21] = (HIGH_PRIORITY_FROM_HOST_PORT >> 8) & 0xFF;
|
||||
datagram[22] = HIGH_PRIORITY_FROM_HOST_PORT & 0xFF;
|
||||
datagram[24] = 0x02;
|
||||
datagram[26] = 0x10;
|
||||
datagram[27] = 20;
|
||||
datagram[28] = 32;
|
||||
|
||||
if (m_commandSocket->writeDatagram(datagram, set->getCurrentMetisCard().ip_address, DEVICE_PORT) >= 0)
|
||||
++m_generalSequence;
|
||||
else
|
||||
PROTOCOL2_DEBUG << "general packet write failed.";
|
||||
}
|
||||
|
||||
void Protocol2DataPath::sendDdcSpecificPacket() {
|
||||
|
||||
const int receivers = qMax(1, set->getNumberOfReceivers());
|
||||
QByteArray datagram(17 + receivers * 6, 0x00);
|
||||
|
||||
datagram[0] = (m_ddcSequence >> 24) & 0xFF;
|
||||
datagram[1] = (m_ddcSequence >> 16) & 0xFF;
|
||||
datagram[2] = (m_ddcSequence >> 8) & 0xFF;
|
||||
datagram[3] = m_ddcSequence & 0xFF;
|
||||
datagram[4] = 0x01;
|
||||
datagram[5] = io->ccTx.dither ? 0x01 : 0x00;
|
||||
datagram[6] = io->ccTx.random ? 0x01 : 0x00;
|
||||
|
||||
for (int rx = 0; rx < receivers; ++rx) {
|
||||
|
||||
datagram[7 + rx / 8] = datagram.at(7 + rx / 8) | (1 << (rx % 8));
|
||||
|
||||
const int offset = 17 + rx * 6;
|
||||
datagram[offset + 0] = 0x00;
|
||||
datagram[offset + 1] = (io->samplerate >> 8) & 0xFF;
|
||||
datagram[offset + 2] = io->samplerate & 0xFF;
|
||||
datagram[offset + 3] = 0x00;
|
||||
datagram[offset + 4] = 0x00;
|
||||
datagram[offset + 5] = 24;
|
||||
}
|
||||
|
||||
if (m_commandSocket->writeDatagram(datagram, set->getCurrentMetisCard().ip_address, kProtocol2DdcSpecificPort) >= 0)
|
||||
++m_ddcSequence;
|
||||
else
|
||||
PROTOCOL2_DEBUG << "DDC specific packet write failed.";
|
||||
}
|
||||
|
||||
void Protocol2DataPath::sendHighPriorityPacket(bool run) {
|
||||
|
||||
QByteArray datagram(kProtocol2HighPriorityPacketSize, 0x00);
|
||||
const QList<long> frequencies = set->getCtrFrequencies();
|
||||
const int receivers = qMax(1, set->getNumberOfReceivers());
|
||||
|
||||
datagram[0] = (m_highPrioritySequence >> 24) & 0xFF;
|
||||
datagram[1] = (m_highPrioritySequence >> 16) & 0xFF;
|
||||
datagram[2] = (m_highPrioritySequence >> 8) & 0xFF;
|
||||
datagram[3] = m_highPrioritySequence & 0xFF;
|
||||
datagram[4] = run ? 0x01 : 0x00;
|
||||
|
||||
for (int rx = 0; rx < receivers; ++rx) {
|
||||
|
||||
const quint32 encodedFrequency = encodeFrequency(frequencies.value(rx, frequencies.value(0, 0)));
|
||||
const int offset = 9 + rx * 4;
|
||||
|
||||
datagram[offset + 0] = (encodedFrequency >> 24) & 0xFF;
|
||||
datagram[offset + 1] = (encodedFrequency >> 16) & 0xFF;
|
||||
datagram[offset + 2] = (encodedFrequency >> 8) & 0xFF;
|
||||
datagram[offset + 3] = encodedFrequency & 0xFF;
|
||||
}
|
||||
|
||||
if (m_commandSocket->writeDatagram(datagram, set->getCurrentMetisCard().ip_address, HIGH_PRIORITY_FROM_HOST_PORT) >= 0)
|
||||
++m_highPrioritySequence;
|
||||
else
|
||||
PROTOCOL2_DEBUG << "high priority packet write failed.";
|
||||
}
|
||||
|
||||
void Protocol2DataPath::updateLegacyControlBytes(const QByteArray &statusDatagram) {
|
||||
|
||||
const uchar statusBits = (uchar)statusDatagram.at(4);
|
||||
const uchar overloadBits = (uchar)statusDatagram.at(5);
|
||||
|
||||
char control0 = 0x00;
|
||||
if (statusBits & 0x01) control0 |= 0x01;
|
||||
if (statusBits & 0x04) control0 |= 0x02;
|
||||
if (statusBits & 0x02) control0 |= 0x04;
|
||||
|
||||
m_legacyControlBytes[0] = control0;
|
||||
m_legacyControlBytes[1] = (overloadBits & 0x01) ? 0x01 : 0x00;
|
||||
m_legacyControlBytes[2] = 0x00;
|
||||
m_legacyControlBytes[3] = 0x00;
|
||||
m_legacyControlBytes[4] = (char)(set->getCurrentMetisCard().firmwareVersion & 0xFF);
|
||||
}
|
||||
|
||||
void Protocol2DataPath::tryProduceLegacyFrames() {
|
||||
|
||||
const int receivers = qMax(1, set->getNumberOfReceivers());
|
||||
const int halfFrameSamples = samplesPerLegacyHalfFrame();
|
||||
|
||||
if (halfFrameSamples <= 0)
|
||||
return;
|
||||
|
||||
while (true) {
|
||||
|
||||
bool hasEnoughData = true;
|
||||
for (int rx = 0; rx < receivers; ++rx) {
|
||||
if (rx >= m_ddcSampleBuffers.size() || m_ddcSampleBuffers[rx].size() < halfFrameSamples) {
|
||||
hasEnoughData = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasEnoughData)
|
||||
break;
|
||||
|
||||
QByteArray halfFrame(BUFFER_SIZE / 2, 0x00);
|
||||
int pos = 0;
|
||||
halfFrame[pos++] = SYNC;
|
||||
halfFrame[pos++] = SYNC;
|
||||
halfFrame[pos++] = SYNC;
|
||||
for (int i = 0; i < m_legacyControlBytes.size(); ++i)
|
||||
halfFrame[pos++] = m_legacyControlBytes.at(i);
|
||||
|
||||
for (int sampleIndex = 0; sampleIndex < halfFrameSamples; ++sampleIndex) {
|
||||
for (int rx = 0; rx < receivers; ++rx) {
|
||||
|
||||
const Protocol2IQSample sample = m_ddcSampleBuffers[rx].takeFirst();
|
||||
halfFrame[pos++] = (sample.i >> 16) & 0xFF;
|
||||
halfFrame[pos++] = (sample.i >> 8) & 0xFF;
|
||||
halfFrame[pos++] = sample.i & 0xFF;
|
||||
halfFrame[pos++] = (sample.q >> 16) & 0xFF;
|
||||
halfFrame[pos++] = (sample.q >> 8) & 0xFF;
|
||||
halfFrame[pos++] = sample.q & 0xFF;
|
||||
}
|
||||
|
||||
halfFrame[pos++] = 0x00;
|
||||
halfFrame[pos++] = 0x00;
|
||||
}
|
||||
|
||||
m_partialLegacyFrame += halfFrame;
|
||||
if (m_partialLegacyFrame.size() == BUFFER_SIZE) {
|
||||
if (!io->iq_queue.isFull())
|
||||
io->iq_queue.enqueue(m_partialLegacyFrame);
|
||||
m_partialLegacyFrame.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Protocol2DataPath::samplesPerLegacyHalfFrame() const {
|
||||
|
||||
const int receivers = qMax(1, set->getNumberOfReceivers());
|
||||
const int bytesPerSample = receivers * 6 + 2;
|
||||
|
||||
return (BUFFER_SIZE / 2 - 8) / bytesPerSample;
|
||||
}
|
||||
|
||||
quint32 Protocol2DataPath::encodeFrequency(long frequency) const {
|
||||
return (quint32)frequency;
|
||||
}
|
||||
|
||||
qint32 Protocol2DataPath::read24BitSample(const char *data) const {
|
||||
|
||||
qint32 value = ((qint32)(quint8)data[0] << 16) |
|
||||
((qint32)(quint8)data[1] << 8) |
|
||||
(qint32)(quint8)data[2];
|
||||
|
||||
if (value & 0x00800000)
|
||||
value |= 0xFF000000;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file cusdr_protocol2_io.h
|
||||
* @brief openHPSDR Protocol 2 transport helper
|
||||
*/
|
||||
|
||||
#ifndef _CUSDR_PROTOCOL2_IO_H
|
||||
#define _CUSDR_PROTOCOL2_IO_H
|
||||
|
||||
#include "cusdr_settings.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QTimer>
|
||||
#include <QUdpSocket>
|
||||
|
||||
struct Protocol2IQSample {
|
||||
qint32 i;
|
||||
qint32 q;
|
||||
};
|
||||
|
||||
class Protocol2DataPath : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Protocol2DataPath(Settings *settings, THPSDRParameter *ioData, QObject *parent = 0);
|
||||
~Protocol2DataPath();
|
||||
|
||||
bool isActive() const;
|
||||
bool initSockets();
|
||||
void stop();
|
||||
void sendInitFramesToNetworkDevice(int rx);
|
||||
void networkDeviceStartStop(char value);
|
||||
|
||||
private slots:
|
||||
void readStatusData();
|
||||
void readDdcData();
|
||||
void sendHeartbeat();
|
||||
|
||||
private:
|
||||
void closeSockets();
|
||||
void sendGeneralPacket();
|
||||
void sendDdcSpecificPacket();
|
||||
void sendHighPriorityPacket(bool run);
|
||||
void updateLegacyControlBytes(const QByteArray &statusDatagram);
|
||||
void tryProduceLegacyFrames();
|
||||
int samplesPerLegacyHalfFrame() const;
|
||||
quint32 encodeFrequency(long frequency) const;
|
||||
qint32 read24BitSample(const char *data) const;
|
||||
|
||||
private:
|
||||
Settings* set;
|
||||
THPSDRParameter* io;
|
||||
QUdpSocket* m_commandSocket;
|
||||
QUdpSocket* m_statusSocket;
|
||||
QList<QUdpSocket*> m_ddcSockets;
|
||||
QList<QList<Protocol2IQSample> > m_ddcSampleBuffers;
|
||||
QTimer* m_heartbeatTimer;
|
||||
QByteArray m_partialLegacyFrame;
|
||||
QByteArray m_statusDatagram;
|
||||
QByteArray m_legacyControlBytes;
|
||||
bool m_running;
|
||||
quint32 m_generalSequence;
|
||||
quint32 m_ddcSequence;
|
||||
quint32 m_highPrioritySequence;
|
||||
};
|
||||
|
||||
#endif // _CUSDR_PROTOCOL2_IO_H
|
||||
Reference in New Issue
Block a user