3777 lines
101 KiB
C++
3777 lines
101 KiB
C++
/**
|
|
* @file cusdr_dataEngine.cpp
|
|
* @brief cuSDR data engine class
|
|
* @author Hermann von Hasseln, DL3HVH
|
|
* @version 0.1
|
|
* @date 2011-02-02
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* Copyright 2010 Hermann von Hasseln, DL3HVH
|
|
*
|
|
* using original C code by John Melton, G0ORX/N6LYT and Dave McQuate, WA8YWQ
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Library General Public License version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
//#define TESTING
|
|
|
|
#define LOG_DATA_ENGINE
|
|
// use DATA_ENGINE_DEBUG
|
|
#define LOG_DATA_PROCESSOR
|
|
// use DATA_PROCESSOR_DEBUG
|
|
#define LOG_AUDIO_PROCESSOR
|
|
// use AUDIO_PROCESSOR
|
|
#define LOG_WIDEBAND_PROCESSOR
|
|
// use WIDEBAND_PROCESSOR_DEBUG
|
|
|
|
#include "cusdr_dataEngine.h"
|
|
|
|
|
|
/*!
|
|
\class DataEngine
|
|
\brief The DataEngine class implements the main SDR functionality.
|
|
*/
|
|
/*!
|
|
\brief Implements interfaces to the HPSDR hardware and various Server and DSP functionality.
|
|
- set up HW interfaces to Metis or other resp.
|
|
- initializes Metis.
|
|
- set up parameters for HPSDR hardware.
|
|
- implements the data receiver thread.
|
|
- implements the data processor thread.
|
|
- implements the wide band data processor thread.
|
|
- implements the audio receiver thread.
|
|
- implements the audio processor thread.
|
|
- implements the interface to the Chirp WSPR decoding functionality.
|
|
*/
|
|
DataEngine::DataEngine(QObject *parent)
|
|
: QObject(parent)
|
|
, set(Settings::instance())
|
|
, m_serverMode(set->getCurrentServerMode())
|
|
, m_hwInterface(set->getHWInterface())
|
|
, m_dataEngineState(QSDR::DataEngineDown)
|
|
, m_meterType(SIGNAL_STRENGTH)
|
|
, m_restart(false)
|
|
, m_networkDeviceRunning(false)
|
|
, m_soundFileLoaded(false)
|
|
, m_chirpInititalized(false)
|
|
, m_discoveryThreadRunning(false)
|
|
, m_dataIOThreadRunning(false)
|
|
, m_chirpDataProcThreadRunning(false)
|
|
, m_dataProcThreadRunning(false)
|
|
, m_audioRcvrThreadRunning(false)
|
|
, m_audioInProcThreadRunning(false)
|
|
, m_audioOutProcThreadRunning(false)
|
|
, m_frequencyChange(false)
|
|
//, m_wbSpectrumAveraging(set->getSpectrumAveraging())
|
|
//, m_wbSpectrumAveraging(true)
|
|
, m_hamBandChanged(true)
|
|
, m_chirpThreadStopped(true)
|
|
, m_hpsdrDevices(0)
|
|
, m_configure(10)
|
|
, m_timeout(5000)
|
|
, m_remainingTime(0)
|
|
, m_RxFrequencyChange(0)
|
|
, m_forwardPower(0)
|
|
, m_rxSamples(0)
|
|
, m_chirpSamples(0)
|
|
, m_spectrumSize(set->getSpectrumSize())
|
|
, m_sendState(0)
|
|
, m_sMeterCalibrationOffset(0.0f)//(35.0f)
|
|
{
|
|
qRegisterMetaType<QAbstractSocket::SocketError>();
|
|
|
|
this->setObjectName(QString::fromUtf8("dataEngine"));
|
|
|
|
m_clientConnected = false;
|
|
|
|
//currentRx = 0;
|
|
m_discoverer = 0;
|
|
m_dataIO = 0;
|
|
m_dataProcessor = 0;
|
|
m_wbDataProcessor = 0;
|
|
m_audioReceiver = 0;
|
|
m_audioOutProcessor = 0;
|
|
m_chirpProcessor = 0;
|
|
//m_wbAverager = 0;
|
|
|
|
set->setMercuryVersion(0);
|
|
set->setPenelopeVersion(0);
|
|
set->setPennyLaneVersion(0);
|
|
set->setMetisVersion(0);
|
|
set->setHermesVersion(0);
|
|
|
|
io.metisFW = 0;
|
|
io.hermesFW = 0;
|
|
io.mercuryFW = 0;
|
|
|
|
//m_audioBuffer.resize(0);
|
|
//m_audiobuf.resize(IO_BUFFER_SIZE);
|
|
|
|
initAudioEngine();
|
|
setupConnections();
|
|
|
|
// test
|
|
/*audioringbuffer.reserve(3);
|
|
|
|
audioringbuffer.append(1);
|
|
audioringbuffer.append(2);
|
|
audioringbuffer.append(3);
|
|
|
|
for (int i = 0; i < audioringbuffer.size(); ++i) {
|
|
qDebug() << audioringbuffer.at(i);
|
|
}
|
|
|
|
audioringbuffer.append(4);
|
|
audioringbuffer.append(5);
|
|
|
|
for (int i = 0; i < audioringbuffer.size(); ++i) {
|
|
qDebug() << audioringbuffer.at(i);
|
|
}*/
|
|
|
|
m_message = "audio sample = %1";
|
|
m_counter = 0;
|
|
}
|
|
|
|
DataEngine::~DataEngine() {
|
|
}
|
|
|
|
void DataEngine::initAudioEngine() {
|
|
m_audioEngine = new AudioEngine();
|
|
//m_audioEngine->setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, m_dataEngineState);
|
|
}
|
|
|
|
void DataEngine::setupConnections() {
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(systemStateChanged(
|
|
QObject *,
|
|
QSDR::_Error,
|
|
QSDR::_HWInterfaceMode,
|
|
QSDR::_ServerMode,
|
|
QSDR::_DataEngineState)),
|
|
this,
|
|
SLOT(systemStateChanged(
|
|
QObject *,
|
|
QSDR::_Error,
|
|
QSDR::_HWInterfaceMode,
|
|
QSDR::_ServerMode,
|
|
QSDR::_DataEngineState)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(rxListChanged(QList<Receiver *>)),
|
|
this,
|
|
SLOT(rxListChanged(QList<Receiver *>)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(numberOfRXChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setNumberOfRx(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(currentReceiverChanged(QObject *,int)),
|
|
this,
|
|
SLOT(setCurrentReceiver(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(hamBandChanged(QObject *, int, bool, HamBand)),
|
|
this,
|
|
SLOT(setHamBand(QObject *, int, bool, HamBand)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(sampleRateChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setSampleRate(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(mercuryAttenuatorChanged(QObject *, HamBand, int)),
|
|
this,
|
|
SLOT(setMercuryAttenuator(QObject *, HamBand, int)));
|
|
|
|
// CHECKED_CONNECT(
|
|
// set,
|
|
// SIGNAL(mercuryAttenuatorsChanged(QObject *, QList<int>)),
|
|
// this,
|
|
// SLOT(setMercuryAttenuators(QObject *, QList<int>)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(ditherChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setDither(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(randomChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setRandom(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(src10MhzChanged(QObject *, int)),
|
|
this,
|
|
SLOT(set10MhzSource(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(src122_88MhzChanged(QObject *, int)),
|
|
this,
|
|
SLOT(set122_88MhzSource(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(micSourceChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setMicSource(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(classChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setMercuryClass(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(timingChanged(QObject *, int)),
|
|
this,
|
|
SLOT(setMercuryTiming(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(clientDisconnectedEvent(int)),
|
|
this,
|
|
SLOT(setClientDisconnected(int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(clientNoConnectedChanged(QObject*, int)),
|
|
this,
|
|
SLOT(setClientConnected(QObject*, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(rxConnectedStatusChanged(QObject*, int, bool)),
|
|
this,
|
|
SLOT(setRxConnectedStatus(QObject*, int, bool)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(audioRxChanged(QObject*, int)),
|
|
this,
|
|
SLOT(setAudioReceiver(QObject*, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(framesPerSecondChanged(QObject*, int, int)),
|
|
this,
|
|
SLOT(setFramesPerSecond(QObject*, int, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(searchMetisSignal()),
|
|
this,
|
|
SLOT(searchHpsdrNetworkDevices()));
|
|
|
|
/*CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(spectrumAveragingChanged(QObject*, int, bool)),
|
|
this,
|
|
SLOT(setWbSpectrumAveraging(QObject*, int, bool)));*/
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(networkDeviceNumberChanged(int)),
|
|
this,
|
|
SLOT(setHPSDRDeviceNumber(int)));
|
|
|
|
// CHECKED_CONNECT(
|
|
// set,
|
|
// SIGNAL(alexConfigurationChanged(const QList<TAlexConfiguration> &)),
|
|
// this,
|
|
// SLOT(setAlexConfiguration(const QList<TAlexConfiguration> &)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(alexConfigurationChanged(quint16)),
|
|
this,
|
|
SLOT(setAlexConfiguration(quint16)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(alexStateChanged(HamBand, const QList<int> &)),
|
|
this,
|
|
SLOT(setAlexStates(HamBand, const QList<int> &)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(pennyOCEnabledChanged(bool)),
|
|
this,
|
|
SLOT(setPennyOCEnabled(bool)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(rxJ6PinsChanged(const QList<int> &)),
|
|
this,
|
|
SLOT(setRxJ6Pins(const QList<int> &)));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(txJ6PinsChanged(const QList<int> &)),
|
|
this,
|
|
SLOT(setTxJ6Pins(const QList<int> &)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(formatChanged(QObject *, const QAudioFormat )),
|
|
set,
|
|
SLOT(setAudioFormat(QObject *, const QAudioFormat )));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(formatChanged(QObject *, const QAudioFormat )),
|
|
this,
|
|
SLOT(setAudioFileFormat(QObject *, const QAudioFormat )));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(playPositionChanged(QObject *, qint64)),
|
|
set,
|
|
SLOT(setAudioPosition(QObject *, qint64)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(playPositionChanged(QObject *, qint64)),
|
|
this,
|
|
SLOT(setAudioFilePosition(QObject *, qint64)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(bufferChanged(QObject *, qint64, qint64, const QByteArray)),
|
|
set,
|
|
SLOT(setAudioBuffer(QObject *, qint64, qint64, const QByteArray)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(bufferChanged(QObject *, qint64, qint64, const QByteArray)),
|
|
this,
|
|
SLOT(setAudioFileBuffer(QObject *, qint64, qint64, const QByteArray)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioEngine,
|
|
SIGNAL(audiofileBufferChanged(const QList<qreal>)),
|
|
this,
|
|
SLOT(setAudioFileBuffer(const QList<qreal>)));
|
|
}
|
|
|
|
//********************************************************
|
|
// start/stop data engine
|
|
bool DataEngine::startDataEngineWithoutConnection() {
|
|
|
|
DATA_ENGINE_DEBUG << "no HPSDR-HW interface";
|
|
|
|
if (io.inputBuffer.length() > 0) {
|
|
|
|
initReceivers(1);
|
|
if (!m_dataIO) createDataIO();
|
|
if (!m_dataProcessor) createDataProcessor();
|
|
|
|
// data receiver thread
|
|
if (!startDataIO(QThread::NormalPriority)) {
|
|
|
|
setSystemState(QSDR::DataReceiverThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
|
|
switch (m_serverMode) {
|
|
|
|
case QSDR::SDRMode:
|
|
case QSDR::ChirpWSPR:
|
|
case QSDR::NoServerMode:
|
|
case QSDR::DemoMode:
|
|
return false;
|
|
|
|
case QSDR::ChirpWSPRFile:
|
|
|
|
if (!m_chirpInititalized) createChirpDataProcessor();
|
|
|
|
m_chirpProcessor->generateLocalChirp();
|
|
|
|
if (!startChirpDataProcessor(QThread::NormalPriority)) {
|
|
|
|
setSystemState(QSDR::ChirpDataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
|
|
m_chirpDspEngine = new QDSPEngine(this, 0, 2*BUFFER_SIZE);
|
|
|
|
cpxIn.resize(2*BUFFER_SIZE);
|
|
cpxOut.resize(2*BUFFER_SIZE);
|
|
|
|
RX.at(0)->setConnectedStatus(true);
|
|
set->setRxList(RX);
|
|
|
|
m_rxSamples = 0;
|
|
m_chirpSamples = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
// IQ data processing thread
|
|
if (!startDataProcessor(QThread::NormalPriority)) {
|
|
|
|
setSystemState(QSDR::DataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, QSDR::DataEngineUp);
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
DATA_ENGINE_DEBUG << "no data available - data file loaded?";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool DataEngine::findHPSDRDevices() {
|
|
|
|
if (!m_discoverer) createDiscoverer();
|
|
|
|
// HPSDR network IO thread
|
|
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 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) {
|
|
|
|
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 {
|
|
|
|
emit clearSystemMessageEvent();
|
|
if (m_hpsdrDevices > 1)
|
|
set->showNetworkIODialog();
|
|
|
|
QList<TNetworkDevicecard> metisList = set->getMetisCardsList();
|
|
DATA_ENGINE_DEBUG << "found " << metisList.count() << " network device(s)";
|
|
|
|
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 << "]";
|
|
}
|
|
|
|
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 (getFirmwareVersions()) return true;
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool DataEngine::getFirmwareVersions() {
|
|
|
|
m_fwCount = 0;
|
|
|
|
// init receivers
|
|
int rcvrs = set->getNumberOfReceivers();
|
|
|
|
QString str = "Initializing %1 receiver(s)...please wait";
|
|
set->setSystemMessage(str.arg(set->getNumberOfReceivers()), rcvrs * 500);
|
|
|
|
if (!initReceivers(rcvrs)) return false;
|
|
|
|
|
|
if (!m_dataIO) createDataIO();
|
|
|
|
if (!m_dataProcessor) createDataProcessor();
|
|
|
|
switch (m_serverMode) {
|
|
|
|
case QSDR::SDRMode:
|
|
|
|
for (int i = 0; i < set->getNumberOfReceivers(); i++) {
|
|
|
|
RX.at(i)->setConnectedStatus(true);
|
|
}
|
|
setTimeStamp(this, false);
|
|
break;
|
|
|
|
default:
|
|
|
|
DATA_ENGINE_DEBUG << "no valid server mode";
|
|
setSystemState(QSDR::ServerModeError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
|
|
return false;
|
|
}
|
|
|
|
set->setRxList(RX);
|
|
connectDSPSlots();
|
|
|
|
for (int i = 0; i < set->getNumberOfReceivers(); i++)
|
|
RX.at(i)->setAudioVolume(this, i, 0.0f);
|
|
|
|
// IQ data processing thread
|
|
if (!startDataProcessor(QThread::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "data processor thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
// data IO thread
|
|
if (!startDataIO(QThread::NormalPriority)) {// ::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "data IO thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
//setSampleRate(this, set->getSampleRate());
|
|
SleeperThread::msleep(100);
|
|
|
|
// pre-conditioning
|
|
for (int i = 0; i < io.receivers; i++)
|
|
m_dataIO->sendInitFramesToNetworkDevice(i);
|
|
|
|
if (m_serverMode == QSDR::SDRMode)
|
|
m_dataIO->networkDeviceStartStop(0x01); // 0x01 for starting Metis without wide band data
|
|
|
|
m_networkDeviceRunning = true;
|
|
setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, QSDR::DataEngineUp);
|
|
SleeperThread::msleep(300);
|
|
|
|
io.metisFW = set->getMetisVersion();
|
|
io.mercuryFW = set->getMercuryVersion();
|
|
io.penelopeFW = set->getPenelopeVersion();
|
|
io.pennylaneFW = set->getPennyLaneVersion();
|
|
io.hermesFW = set->getHermesVersion();
|
|
|
|
// if we have 4096 * 16 bit = 8 * 1024 raw consecutive ADC samples, m_wbBuffers = 8
|
|
// we have 16384 * 16 bit = 32 * 1024 raw consecutive ADC samples, m_wbBuffers = 32
|
|
int wbBuffers = 0;
|
|
if (io.mercuryFW > 32 || io.hermesFW > 11)
|
|
wbBuffers = BIGWIDEBANDSIZE / 512;
|
|
else
|
|
wbBuffers = SMALLWIDEBANDSIZE / 512;
|
|
|
|
set->setWidebandBuffers(this, wbBuffers);
|
|
|
|
if (set->getFirmwareVersionCheck())
|
|
return checkFirmwareVersions();
|
|
else
|
|
return true;
|
|
}
|
|
|
|
// credits go to George Byrkit, K9TRV: the older FW checkings are shamelessly taken from the KISS Konsole!
|
|
bool DataEngine::checkFirmwareVersions() {
|
|
|
|
if (io.metisFW != 0 && io.hpsdrDeviceName == "Hermes") {
|
|
|
|
stop();
|
|
|
|
QString msg = "Metis selected, but Hermes found!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
if (io.hermesFW != 0 && io.hpsdrDeviceName == "Metis") {
|
|
|
|
stop();
|
|
|
|
QString msg = "Hermes selected, but Metis found!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
if (io.penelopeFW == 0 && (set->getPenelopePresence() || set->getPennyLanePresence())) {
|
|
|
|
stop();
|
|
|
|
QString msg = "Penelope or Pennylane selected, but firmware version = 0 !";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
if (io.mercuryFW < 27 && set->getNumberOfReceivers() > 4 && io.hpsdrDeviceName == "Metis") {
|
|
|
|
stop();
|
|
|
|
QString msg = "Mercury FW must be V2.7 or higher!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
if (io.hpsdrDeviceName == "Metis") {
|
|
|
|
QString msg;
|
|
switch (io.metisFW) {
|
|
|
|
case 13:
|
|
if (((set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
(io.penelopeFW == 13 || io.pennylaneFW == 13)) ||
|
|
io.mercuryFW != 29)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.3 and Mercury FW V2.7 requires Metis FW V1.3!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 14:
|
|
if (((set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
(io.penelopeFW == 14 || io.pennylaneFW == 14)) ||
|
|
io.mercuryFW != 29)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.4 and Mercury FW V2.7 requires Metis FW V1.4!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if (((set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
(io.penelopeFW == 15 || io.pennylaneFW == 15)) ||
|
|
io.mercuryFW != 30)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.5 and Mercury FW V3.0 requires Metis FW V1.5!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 16:
|
|
|
|
if (((set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
(io.penelopeFW == 16 || io.pennylaneFW == 16)) ||
|
|
io.mercuryFW != 31)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.6 and Mercury FW V3.1 requires Metis FW V1.6!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 17:
|
|
case 18:
|
|
|
|
if (((set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
(io.penelopeFW == 17 || io.pennylaneFW == 17)) ||
|
|
io.mercuryFW != 32)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.7 and Mercury FW V3.2 requires Metis FW V1.7 or V1.8!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case 19:
|
|
case 20:
|
|
|
|
stop();
|
|
|
|
msg = "Metis FW V1.9 or V2.0 have some problems - please upgrade to Metis V2.1!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
break;
|
|
|
|
case 21:
|
|
|
|
if ((set->getPenelopePresence() && io.penelopeFW != 17) ||
|
|
(set->getPennyLanePresence() && io.pennylaneFW != 17)||
|
|
io.mercuryFW != 33)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.7 and Mercury FW V3.3 required for Metis FW V2.1!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// case 22:
|
|
//
|
|
// if ((set->getPenelopePresence() && m_penelopeFW != 17) ||
|
|
// (set->getPennyLanePresence() && m_pennylaneFW != 17)||
|
|
// m_mercuryFW != 33)
|
|
// {
|
|
// stop();
|
|
//
|
|
// msg = "Penny[Lane] FW Version V1.7 and Mercury FW V3.3 required for Metis FW >= V2.1!";
|
|
// set->showWarningDialog(msg);
|
|
// return false;
|
|
// }
|
|
// break;
|
|
|
|
case 26:
|
|
|
|
if ((set->getPenelopePresence() && io.penelopeFW != 18) ||
|
|
(set->getPennyLanePresence() && io.pennylaneFW != 18)||
|
|
io.mercuryFW != 34)
|
|
{
|
|
stop();
|
|
|
|
msg = "Penny[Lane] FW Version V1.8 and Mercury FW V3.4 required for Metis FW V2.6!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
//stop();
|
|
|
|
msg = "Not a standard Metis FW version !";
|
|
set->showWarningDialog(msg);
|
|
//return false;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (io.mercuryFW < 33 && set->getNumberOfReceivers() > 4 && io.hpsdrDeviceName == "Metis") {
|
|
|
|
stop();
|
|
|
|
QString msg = "Mercury FW < V3.3 has only 4 receivers!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
if (io.hermesFW < 18 && set->getNumberOfReceivers() > 2 && io.hpsdrDeviceName == "Hermes") {
|
|
|
|
stop();
|
|
|
|
QString msg = "Hermes FW < V1.8 has only 2 receivers!";
|
|
set->showWarningDialog(msg);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DataEngine::start() {
|
|
|
|
m_fwCount = 0;
|
|
m_sendState = 0;
|
|
|
|
int rcvrs = set->getNumberOfReceivers();
|
|
if (!initReceivers(rcvrs)) return false;
|
|
|
|
if (!m_dataIO) createDataIO();
|
|
|
|
if (!m_dataProcessor) createDataProcessor();
|
|
|
|
if (m_serverMode == QSDR::SDRMode && !m_wbDataProcessor)
|
|
createWideBandDataProcessor();
|
|
|
|
if ((m_serverMode == QSDR::ChirpWSPR) && !m_chirpProcessor)
|
|
createChirpDataProcessor();
|
|
|
|
switch (m_serverMode) {
|
|
|
|
//case QSDR::ExternalDSP:
|
|
|
|
/*
|
|
//CHECKED_CONNECT(
|
|
// set,
|
|
// SIGNAL(frequencyChanged(QObject*, bool, int, long)),
|
|
// this,
|
|
// SLOT(setFrequency(QObject*, bool, int, long)));
|
|
|
|
//if (!m_audioProcessorRunning) {
|
|
|
|
// //if (!m_audioProcessor) createAudioProcessor();
|
|
// if (!m_audioReceiver) createAudioReceiver();
|
|
|
|
// m_audioInProcThread->start();
|
|
// if (m_audioInProcThread->isRunning()) {
|
|
//
|
|
// m_audioInProcThreadRunning = true;
|
|
// DATA_ENGINE_DEBUG << "Audio processor process started.";
|
|
// }
|
|
// else {
|
|
|
|
// m_audioInProcThreadRunning = false;
|
|
// setSystemState(
|
|
// QSDR::AudioThreadError,
|
|
// m_hwInterface,
|
|
// m_serverMode,
|
|
// QSDR::DataEngineDown);
|
|
// return false;
|
|
// }
|
|
//
|
|
// io.audio_rx = 0;
|
|
// io.clientList.append(0);
|
|
|
|
// m_audioProcessorRunning = true;
|
|
// setSystemState(
|
|
// QSDR::NoError,
|
|
// m_hwInterface,
|
|
// m_serverMode,
|
|
// QSDR::DataEngineUp);
|
|
//}
|
|
*/
|
|
//return false;
|
|
|
|
case QSDR::SDRMode:
|
|
|
|
setTimeStamp(this, false);
|
|
break;
|
|
|
|
case QSDR::ChirpWSPR:
|
|
//case QSDR::ChirpWSPRFile:
|
|
|
|
// turn time stamping on
|
|
setTimeStamp(this, true);
|
|
|
|
if (!startChirpDataProcessor(QThread::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "data processor thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
//RX.at(0)->setConnectedStatus(true);
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(ctrFrequencyChanged(QObject *, int, int, long)),
|
|
this,
|
|
SLOT(setFrequency(QObject *, int, int, long)));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DATA_ENGINE_DEBUG << "no valid server mode";
|
|
|
|
setSystemState(QSDR::ServerModeError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
} // end switch (m_serverMode)
|
|
|
|
set->setRxList(RX);
|
|
connectDSPSlots();
|
|
|
|
for (int i = 0; i < rcvrs; i++) {
|
|
|
|
RX.at(i)->setConnectedStatus(true);
|
|
RX.at(i)->setAudioVolume(this, i, RX.at(i)->getAudioVolume());
|
|
setFrequency(this, true, i, set->getCtrFrequencies().at(i));
|
|
|
|
//CHECKED_CONNECT(
|
|
// RX.at(i),
|
|
// SIGNAL(outputBufferSignal(int, const CPX &)),
|
|
// this, //m_dataProcessor,
|
|
// SLOT(setOutputBuffer(int, const CPX &)));
|
|
|
|
CHECKED_CONNECT(
|
|
RX.at(i),
|
|
SIGNAL(outputBufferSignal(int, const CPX &)),
|
|
m_dataProcessor,
|
|
SLOT(setOutputBuffer(int, const CPX &)));
|
|
|
|
m_dspThreadList.at(i)->start(QThread::NormalPriority);//QThread::TimeCriticalPriority);
|
|
|
|
if (m_dspThreadList.at(i)->isRunning()) {
|
|
|
|
//m_dataProcThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "receiver processor thread started for Rx " << i;
|
|
io.networkIOMutex.unlock();
|
|
}
|
|
else {
|
|
|
|
//m_dataProcThreadRunning = false;
|
|
//setSystemState(QSDR::DataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (m_serverMode != QSDR::ChirpWSPR && !startWideBandDataProcessor(QThread::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "wide band data processor thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
// data IO thread
|
|
// if (!startDataIO(QThread::HighPriority)) {// ::NormalPriority)) {
|
|
//
|
|
// DATA_ENGINE_DEBUG << "data receiver thread could not be started.";
|
|
// return false;
|
|
// }
|
|
|
|
// IQ data processing thread
|
|
if (!startDataProcessor(QThread::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "data processor thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
// data IO thread
|
|
if (!startDataIO(QThread::NormalPriority)) {// ::NormalPriority::HighPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "data IO thread could not be started.";
|
|
return false;
|
|
}
|
|
|
|
// start Sync,ADC and S-Meter timers
|
|
//m_SyncChangedTime.start();
|
|
//m_ADCChangedTime.start();
|
|
//m_smeterTime.start();
|
|
|
|
// start the "frames-per-second" timer for all receivers
|
|
for (int i = 0; i < rcvrs; i++)
|
|
RX.at(i)->highResTimer->start();
|
|
|
|
// just give them a little time..
|
|
SleeperThread::msleep(100);
|
|
|
|
// pre-conditioning
|
|
for (int i = 0; i < io.receivers; i++)
|
|
m_dataIO->sendInitFramesToNetworkDevice(i);
|
|
|
|
if (m_serverMode == QSDR::SDRMode && set->getWidebandData())
|
|
m_dataIO->networkDeviceStartStop(0x03); // 0x03 for starting the device with wide band data
|
|
else
|
|
m_dataIO->networkDeviceStartStop(0x01); // 0x01 for starting the device without wide band data
|
|
|
|
m_networkDeviceRunning = true;
|
|
|
|
setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, QSDR::DataEngineUp);
|
|
set->setSystemMessage("System running", 4000);
|
|
|
|
DATA_ENGINE_DEBUG << "Data Engine thread: " << this->thread();
|
|
|
|
return true;
|
|
}
|
|
|
|
void DataEngine::stop() {
|
|
|
|
if (m_dataEngineState == QSDR::DataEngineUp) {
|
|
|
|
switch (m_hwInterface) {
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
|
|
// turn time stamping off
|
|
setTimeStamp(this, false);
|
|
|
|
// stop the device
|
|
m_dataIO->networkDeviceStartStop(0);
|
|
m_networkDeviceRunning = false;
|
|
DATA_ENGINE_DEBUG << "HPSDR device stopped";
|
|
|
|
// stop the threads
|
|
//SleeperThread::msleep(100);
|
|
stopDataIO();
|
|
SleeperThread::msleep(100);
|
|
stopDataProcessor();
|
|
//stopChirpDataProcessor();
|
|
if (m_wbDataProcessor)
|
|
stopWideBandDataProcessor();
|
|
|
|
// clear device list
|
|
SleeperThread::msleep(100);
|
|
set->clearMetisCardList();
|
|
DATA_ENGINE_DEBUG << "device cards list cleared.";
|
|
break;
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
|
|
stopDataIO();
|
|
|
|
DATA_ENGINE_DEBUG << "data queue count: " << io.data_queue.count();
|
|
DATA_ENGINE_DEBUG << "chirp queue count: " << io.chirp_queue.count();
|
|
|
|
stopDataProcessor();
|
|
stopChirpDataProcessor();
|
|
}
|
|
|
|
while (!io.au_queue.isEmpty())
|
|
io.au_queue.dequeue();
|
|
|
|
// clear receiver thread list
|
|
foreach (QThread* thread, m_dspThreadList) {
|
|
|
|
thread->quit();
|
|
thread->wait();
|
|
}
|
|
qDeleteAll(m_dspThreadList.begin(), m_dspThreadList.end());
|
|
m_dspThreadList.clear();
|
|
|
|
// clear receiver list
|
|
foreach (Receiver *rx, RX) {
|
|
|
|
rx->stop();
|
|
rx->setConnectedStatus(false);
|
|
disconnectDSPSlots();
|
|
|
|
disconnect(
|
|
rx,
|
|
SIGNAL(spectrumBufferChanged(int, const qVectorFloat&)),
|
|
set,
|
|
SLOT(setSpectrumBuffer(int, const qVectorFloat&)));
|
|
|
|
disconnect(
|
|
rx,
|
|
SIGNAL(sMeterValueChanged(int, float)),
|
|
set,
|
|
SLOT(setSMeterValue(int, float)));
|
|
|
|
/*disconnect(
|
|
rx,
|
|
SIGNAL(outputBufferSignal(int, const CPX &)),
|
|
this,
|
|
SLOT(setOutputBuffer(int, const CPX &)));*/
|
|
|
|
/*disconnect(
|
|
rx,
|
|
SIGNAL(outputBufferSignal(int, const CPX &)),
|
|
m_dataProcessor,
|
|
SLOT(setOutputBuffer(int, const CPX &)));*/
|
|
|
|
//rx->deleteDSPInterface();
|
|
//DATA_ENGINE_DEBUG << "DSP core deleted.";
|
|
}
|
|
qDeleteAll(RX.begin(), RX.end());
|
|
RX.clear();
|
|
set->setRxList(RX);
|
|
DATA_ENGINE_DEBUG << "receiver threads deleted, receivers deleted, receiver & thread list cleared.";
|
|
set->setSystemMessage("Data engine shut down.", 4000);
|
|
|
|
setSystemState(QSDR::NoError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
}
|
|
|
|
m_rxSamples = 0;
|
|
m_chirpSamples = 0;
|
|
m_restart = true;
|
|
m_found = 0;
|
|
m_hpsdrDevices = 0;
|
|
|
|
set->setMercuryVersion(0);
|
|
set->setPenelopeVersion(0);
|
|
set->setMetisVersion(0);
|
|
set->setHermesVersion(0);
|
|
|
|
//set->setPeakHold(false);
|
|
//set->resetWidebandSpectrumBuffer();
|
|
|
|
/*disconnect(
|
|
set,
|
|
SIGNAL(ctrFrequencyChanged(QObject*, int, int, long)),
|
|
this,
|
|
SLOT(setFrequency(QObject*, int, int, long)));*/
|
|
|
|
DATA_ENGINE_DEBUG << "shut down done.";
|
|
}
|
|
|
|
bool DataEngine::initDataEngine() {
|
|
|
|
#ifdef TESTING
|
|
qDebug() << "************************** TESTING MODUS ***********************************";
|
|
return start();
|
|
#endif
|
|
|
|
if (m_hwInterface == QSDR::NoInterfaceMode) {
|
|
|
|
return startDataEngineWithoutConnection();
|
|
}
|
|
else {
|
|
|
|
if (findHPSDRDevices()) {
|
|
|
|
if (io.mercuryFW > 0 || io.hermesFW > 0) {
|
|
|
|
stop();
|
|
DATA_ENGINE_DEBUG << "got firmware versions:";
|
|
DATA_ENGINE_DEBUG << " Metis firmware: " << io.metisFW;
|
|
DATA_ENGINE_DEBUG << " Mercury firmware: " << io.mercuryFW;
|
|
DATA_ENGINE_DEBUG << " Penelope firmware: " << io.penelopeFW;
|
|
DATA_ENGINE_DEBUG << " Pennylane firmware: " << io.pennylaneFW;
|
|
DATA_ENGINE_DEBUG << " Hermes firmware: " << io.hermesFW;
|
|
DATA_ENGINE_DEBUG << "stopping and restarting data engine.";
|
|
|
|
return start();
|
|
}
|
|
else {
|
|
|
|
DATA_ENGINE_DEBUG << "did not get firmware versions!";
|
|
setSystemState(QSDR::FirmwareError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool DataEngine::initReceivers(int rcvrs) {
|
|
|
|
for (int i = 0; i < rcvrs; i++) {
|
|
|
|
Receiver *rx = new Receiver(i);
|
|
|
|
// init the DSP core
|
|
DATA_ENGINE_DEBUG << "trying to init a DSP core for rx " << i;
|
|
|
|
if (rx->initDSPInterface()) {
|
|
|
|
DATA_ENGINE_DEBUG << "init DSP core for rx " << i << " successful !";
|
|
|
|
rx->setConnectedStatus(false);
|
|
rx->setServerMode(m_serverMode);
|
|
|
|
// create dsp thread
|
|
QThreadEx* thread = new QThreadEx();
|
|
rx->moveToThread(thread);
|
|
|
|
//CHECKED_CONNECT(this, SIGNAL(doDSP()), rx, SLOT(dspProcessing()));
|
|
|
|
CHECKED_CONNECT(
|
|
rx,
|
|
SIGNAL(spectrumBufferChanged(int, const qVectorFloat&)),
|
|
set,
|
|
SLOT(setSpectrumBuffer(int, const qVectorFloat&)));
|
|
|
|
CHECKED_CONNECT(
|
|
rx,
|
|
SIGNAL(sMeterValueChanged(int, float)),
|
|
set,
|
|
SLOT(setSMeterValue(int, float)));
|
|
|
|
/*CHECKED_CONNECT(
|
|
rx,
|
|
SIGNAL(outputBufferSignal(int, const CPX &)),
|
|
m_dataProcessor,
|
|
SLOT(setOutputBuffer(int, const CPX &)));*/
|
|
|
|
m_dspThreadList.append(thread);
|
|
RX.append(rx);
|
|
}
|
|
else {
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
set->setRxList(RX);
|
|
|
|
m_txFrame = 0;
|
|
|
|
io.currentReceiver = 0;
|
|
io.receivers = rcvrs;
|
|
|
|
io.timing = 0;
|
|
m_configure = io.receivers + 1;
|
|
|
|
// init cc Rc parameters
|
|
io.ccRx.devices.mercuryFWVersion = 0;
|
|
io.ccRx.devices.penelopeFWVersion = 0;
|
|
io.ccRx.devices.pennylaneFWVersion = 0;
|
|
io.ccRx.devices.hermesFWVersion = 0;
|
|
io.ccRx.devices.metisFWVersion = 0;
|
|
|
|
io.ccRx.ptt = false;
|
|
io.ccRx.dash = false;
|
|
io.ccRx.dot = false;
|
|
io.ccRx.lt2208 = false;
|
|
io.ccRx.ain1 = 0;
|
|
io.ccRx.ain2 = 0;
|
|
io.ccRx.ain3 = 0;
|
|
io.ccRx.ain4 = 0;
|
|
io.ccRx.ain5 = 0;
|
|
io.ccRx.ain6 = 0;
|
|
io.ccRx.hermesI01 = false;
|
|
io.ccRx.hermesI02 = false;
|
|
io.ccRx.hermesI03 = false;
|
|
io.ccRx.hermesI04 = false;
|
|
io.ccRx.mercury1_LT2208 = false;
|
|
io.ccRx.mercury2_LT2208 = false;
|
|
io.ccRx.mercury3_LT2208 = false;
|
|
io.ccRx.mercury4_LT2208 = false;
|
|
|
|
// init cc Tx parameters
|
|
io.ccTx.currentBand = RX.at(0)->getHamBand();
|
|
io.ccTx.mercuryAttenuators = RX.at(0)->getMercuryAttenuators();
|
|
io.ccTx.mercuryAttenuator = RX.at(0)->getMercuryAttenuators().at(io.ccTx.currentBand);
|
|
io.ccTx.dither = set->getMercuryDither();
|
|
io.ccTx.random = set->getMercuryRandom();
|
|
io.ccTx.duplex = 1;
|
|
io.ccTx.mox = false;
|
|
io.ccTx.ptt = false;
|
|
io.ccTx.alexStates = set->getAlexStates();
|
|
io.ccTx.vnaMode = false;
|
|
io.ccTx.alexConfig = set->getAlexConfig();
|
|
io.ccTx.timeStamp = 0;
|
|
io.ccTx.commonMercuryFrequencies = 0;
|
|
io.ccTx.pennyOCenabled = set->getPennyOCEnabled();
|
|
io.ccTx.rxJ6pinList = set->getRxJ6Pins();
|
|
io.ccTx.txJ6pinList = set->getTxJ6Pins();
|
|
|
|
setAlexConfiguration(io.ccTx.alexConfig);
|
|
|
|
io.rxClass = set->getRxClass();
|
|
io.mic_gain = 0.26F;
|
|
io.rx_freq_change = -1;
|
|
io.tx_freq_change = -1;
|
|
io.clients = 0;
|
|
io.sendIQ_toggle = true;
|
|
io.rcveIQ_toggle = false;
|
|
io.alexForwardVolts = 0.0;
|
|
io.alexReverseVolts = 0.0;
|
|
io.alexForwardPower = 0.0;
|
|
io.alexReversePower = 0.0;
|
|
io.penelopeForwardVolts = 0.0;
|
|
io.penelopeForwardPower = 0.0;
|
|
io.ain3Volts = 0.0;
|
|
io.ain4Volts = 0.0;
|
|
io.supplyVolts = 0.0f;
|
|
|
|
|
|
//*****************************
|
|
// C&C bytes
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
io.control_in[i] = 0x00;
|
|
io.control_out[i] = 0x00;
|
|
}
|
|
|
|
// C0
|
|
// 0 0 0 0 0 0 0 0
|
|
// |
|
|
// +------------ MOX (1 = active, 0 = inactive)
|
|
|
|
io.control_out[0] |= MOX_DISABLED;
|
|
|
|
// set C1
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + +------------ Speed (00 = 48kHz, 01 = 96kHz, 10 = 192kHz)
|
|
// | | | | + +---------------- 10MHz Ref. (00 = Atlas/Excalibur, 01 = Penelope, 10 = Mercury)*
|
|
// | | | +-------------------- 122.88MHz source (0 = Penelope, 1 = Mercury)*
|
|
// | + +---------------------- Config (00 = nil, 01 = Penelope, 10 = Mercury, 11 = both)*
|
|
// +-------------------------- Mic source (0 = Janus, 1 = Penelope)*
|
|
|
|
// Bits 1,0
|
|
setSampleRate(this, set->getSampleRate());
|
|
|
|
// Bits 7,..,2
|
|
setHPSDRConfig();
|
|
|
|
io.control_out[1] &= 0x03; // 0 0 0 0 0 0 1 1
|
|
io.control_out[1] |= io.ccTx.clockByte;
|
|
|
|
// set C2
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | |
|
|
// | | +------------ Mode (1 = Class E, 0 = All other modes)
|
|
// +---------- +-------------- Open Collector Outputs on Penelope or Hermes (bit 6..bit 0)
|
|
|
|
io.control_out[2] = io.control_out[2] & 0xFE; // 1 1 1 1 1 1 1 0
|
|
io.control_out[2] = io.control_out[2] | io.rxClass;
|
|
|
|
// set C3
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + +------------ Alex Attenuator (00 = 0dB, 01 = 10dB, 10 = 20dB, 11 = 30dB)
|
|
// | | | | | +---------------- Preamp On/Off (0 = Off, 1 = On)
|
|
// | | | | +------------------ LT2208 Dither (0 = Off, 1 = On)
|
|
// | | | + ------------------- LT2208 Random (0= Off, 1 = On)
|
|
// | + + --------------------- Alex Rx Antenna (00 = none, 01 = Rx1, 10 = Rx2, 11 = XV)
|
|
// + ------------------------- Alex Rx out (0 = off, 1 = on). Set if Alex Rx Antenna > 00.
|
|
|
|
io.control_out[3] = io.control_out[3] & 0xFB; // 1 1 1 1 1 0 1 1
|
|
io.control_out[3] = io.control_out[3] | (io.ccTx.mercuryAttenuator << 2);
|
|
|
|
io.control_out[3] = io.control_out[3] & 0xF7; // 1 1 1 1 0 1 1 1
|
|
io.control_out[3] = io.control_out[3] | (io.ccTx.dither << 3);
|
|
|
|
io.control_out[3] = io.control_out[3] & 0xEF; // 1 1 1 0 1 1 1 1
|
|
io.control_out[3] = io.control_out[3] | (io.ccTx.random << 4);
|
|
|
|
// set C4
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + + ----------- Alex Tx relay (00 = Tx1, 01= Tx2, 10 = Tx3)
|
|
// | | | | | + --------------- Duplex (0 = off, 1 = on)
|
|
// | | + + +------------------ Number of Receivers (000 = 1, 111 = 8)
|
|
// | +------------------------ Time stamp - 1PPS on LSB of Mic data (0 = off, 1 = on)
|
|
// +-------------------------- Common Mercury Frequency (0 = independent frequencies to Mercury
|
|
// Boards, 1 = same frequency to all Mercury boards)
|
|
|
|
//io.control_out[4] &= 0xC7; // 1 1 0 0 0 1 1 1
|
|
io.control_out[4] = (io.ccTx.duplex << 2) | ((io.receivers - 1) << 3);
|
|
|
|
return true;
|
|
}
|
|
|
|
void DataEngine::setHPSDRConfig() {
|
|
|
|
io.ccTx.clockByte = 0x0;
|
|
|
|
// C1
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + +------------ Speed (00 = 48kHz, 01 = 96kHz, 10 = 192kHz)
|
|
// | | | | + +---------------- 10MHz Ref. (00 = Atlas/Excalibur, 01 = Penelope, 10 = Mercury)*
|
|
// | | | +-------------------- 122.88MHz source (0 = Penelope, 1 = Mercury)*
|
|
// | + +---------------------- Config (00 = nil, 01 = Penelope, 10 = Mercury, 11 = both)*
|
|
// +-------------------------- Mic source (0 = Janus, 1 = Penelope)*
|
|
//
|
|
// * Ignored by Hermes
|
|
|
|
if (
|
|
(set->getPenelopePresence() || set->getPennyLanePresence()) &&
|
|
((set->get10MHzSource() == 0) || set->getExcaliburPresence())
|
|
)
|
|
{
|
|
|
|
io.ccTx.clockByte = MIC_SOURCE_PENELOPE | MERCURY_PRESENT | PENELOPE_PRESENT | MERCURY_122_88MHZ_SOURCE | ATLAS_10MHZ_SOURCE;
|
|
}
|
|
else if ((set->getPenelopePresence() || set->getPennyLanePresence()) && (set->get10MHzSource() == 1)) {
|
|
|
|
io.ccTx.clockByte = MIC_SOURCE_PENELOPE | MERCURY_PRESENT | PENELOPE_PRESENT | MERCURY_122_88MHZ_SOURCE | PENELOPE_10MHZ_SOURCE;
|
|
}
|
|
else if ((set->getPenelopePresence() || set->getPennyLanePresence()) && (set->get10MHzSource() == 2)) {
|
|
|
|
io.ccTx.clockByte = MIC_SOURCE_PENELOPE | MERCURY_PRESENT | PENELOPE_PRESENT | MERCURY_122_88MHZ_SOURCE | MERCURY_10MHZ_SOURCE;
|
|
}
|
|
else if ((set->get10MHzSource() == 0) || set->getExcaliburPresence()) {
|
|
|
|
io.ccTx.clockByte = MERCURY_PRESENT | MERCURY_122_88MHZ_SOURCE | ATLAS_10MHZ_SOURCE;
|
|
}
|
|
else {
|
|
|
|
io.ccTx.clockByte = MERCURY_PRESENT | MERCURY_122_88MHZ_SOURCE | MERCURY_10MHZ_SOURCE;
|
|
}
|
|
}
|
|
|
|
void DataEngine::connectDSPSlots() {
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(ctrFrequencyChanged(QObject *, int, int, long)),
|
|
this,
|
|
SLOT(setFrequency(QObject *, int, int, long)));
|
|
}
|
|
|
|
void DataEngine::disconnectDSPSlots() {
|
|
|
|
disconnect(
|
|
set,
|
|
SIGNAL(ctrFrequencyChanged(QObject *, int, int, long)),
|
|
this,
|
|
SLOT(setFrequency(QObject *, int, int, long)));
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop HPSDR device network IO
|
|
|
|
void DataEngine::createDiscoverer() {
|
|
|
|
m_discoverer = new Discoverer(&io);
|
|
|
|
m_discoveryThread = new QThreadEx();
|
|
m_discoverer->moveToThread(m_discoveryThread);
|
|
|
|
m_discoverer->connect(
|
|
m_discoveryThread,
|
|
SIGNAL(started()),
|
|
SLOT(initHPSDRDevice()));
|
|
}
|
|
|
|
bool DataEngine::startDiscoverer(QThread::Priority prio) {
|
|
|
|
m_discoveryThread->start(prio);
|
|
|
|
if (m_discoveryThread->isRunning()) {
|
|
|
|
m_discoveryThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
qDebug() << "";
|
|
DATA_ENGINE_DEBUG << "HPSDR device discovery thread started.";
|
|
io.networkIOMutex.unlock();
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
m_discoveryThreadRunning = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DataEngine::stopDiscoverer() {
|
|
|
|
if (m_discoveryThread->isRunning()) {
|
|
|
|
m_discoveryThread->quit();
|
|
m_discoveryThread->wait(1000);
|
|
delete m_discoveryThread;
|
|
delete m_discoverer;
|
|
m_discoverer = 0;
|
|
|
|
m_discoveryThreadRunning = false;
|
|
|
|
DATA_ENGINE_DEBUG << "HPSDR discovery thread stopped and deleted.";
|
|
}
|
|
else
|
|
DATA_ENGINE_DEBUG << "HPSDR discovery thread wasn't started.";
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop data receiver
|
|
|
|
void DataEngine::createDataIO() {
|
|
|
|
m_dataIO = new DataIO(&io);
|
|
|
|
switch (m_serverMode) {
|
|
|
|
//case QSDR::ExternalDSP:
|
|
// break;
|
|
|
|
//case QSDR::InternalDSP:
|
|
case QSDR::SDRMode:
|
|
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "configured for "
|
|
<< qPrintable(QString::number(set->getNumberOfReceivers()))
|
|
<< " receiver(s) at "
|
|
<< qPrintable(QString::number(set->getSampleRate()/1000))
|
|
<< " kHz sample rate";
|
|
io.networkIOMutex.unlock();
|
|
// sendMessage(
|
|
// m_message.arg(
|
|
// QString::number(set->getNumberOfReceivers()),
|
|
// QString::number(set->getSampleRate()/1000)));
|
|
break;
|
|
|
|
case QSDR::ChirpWSPR:
|
|
case QSDR::ChirpWSPRFile:
|
|
break;
|
|
|
|
case QSDR::NoServerMode:
|
|
case QSDR::DemoMode:
|
|
break;
|
|
}
|
|
|
|
m_dataIOThread = new QThreadEx();
|
|
m_dataIO->moveToThread(m_dataIOThread);
|
|
|
|
switch (m_hwInterface) {
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
|
|
m_dataIO->connect(
|
|
m_dataIOThread,
|
|
SIGNAL(started()),
|
|
SLOT(readData()));
|
|
break;
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
|
|
m_dataIO->connect(
|
|
m_dataIOThread,
|
|
SIGNAL(started()),
|
|
SLOT(initDataReceiverSocket()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool DataEngine::startDataIO(QThread::Priority prio) {
|
|
|
|
m_dataIOThread->start(prio);
|
|
|
|
if (m_dataIOThread->isRunning()) {
|
|
|
|
m_dataIOThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "data IO thread started.";
|
|
io.networkIOMutex.unlock();
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
m_dataIOThreadRunning = false;
|
|
setSystemState(QSDR::DataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DataEngine::stopDataIO() {
|
|
|
|
if (m_dataIOThread->isRunning()) {
|
|
|
|
m_dataIO->stop();
|
|
m_dataIOThread->quit();
|
|
|
|
while (!m_dataIOThread->isFinished()) {
|
|
|
|
DATA_ENGINE_DEBUG << "data IO thread not yet finished...";
|
|
if (m_dataIOThread->wait(100)) break;
|
|
}
|
|
m_dataIOThreadRunning = false;
|
|
|
|
delete m_dataIOThread;
|
|
delete m_dataIO;
|
|
m_dataIO = 0;
|
|
|
|
if (m_serverMode == QSDR::ChirpWSPRFile) {
|
|
|
|
while (!io.chirp_queue.isEmpty())
|
|
io.chirp_queue.dequeue();
|
|
}
|
|
|
|
DATA_ENGINE_DEBUG << "data IO thread deleted.";
|
|
}
|
|
else
|
|
DATA_ENGINE_DEBUG << "data IO thread wasn't started.";
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop data processor
|
|
|
|
void DataEngine::createDataProcessor() {
|
|
|
|
m_dataProcessor = new DataProcessor(this, m_serverMode, m_hwInterface);
|
|
sendSocket = new QUdpSocket();
|
|
|
|
CHECKED_CONNECT(
|
|
sendSocket,
|
|
SIGNAL(error(QAbstractSocket::SocketError)),
|
|
m_dataProcessor,
|
|
SLOT(displayDataProcessorSocketError(QAbstractSocket::SocketError)));
|
|
|
|
switch (m_serverMode) {
|
|
|
|
// The signal iqDataReady is generated by the function
|
|
// processInputBuffer when a block of input data are
|
|
// decoded.
|
|
|
|
case QSDR::SDRMode:
|
|
case QSDR::ChirpWSPR:
|
|
case QSDR::ChirpWSPRFile:
|
|
|
|
/*connect(
|
|
this,
|
|
SIGNAL(iqDataReady(int)),
|
|
SLOT(dttSPDspProcessing(int)),
|
|
Qt::DirectConnection);*/
|
|
|
|
break;
|
|
|
|
case QSDR::NoServerMode:
|
|
case QSDR::DemoMode:
|
|
break;
|
|
|
|
/*
|
|
case QSDR::ExternalDSP:
|
|
|
|
CHECKED_CONNECT_OPT(
|
|
this,
|
|
SIGNAL(iqDataReady(int)),
|
|
m_dataProcessor,
|
|
SLOT(externalDspProcessing(int)),
|
|
Qt::DirectConnection);
|
|
|
|
break;
|
|
|
|
case QSDR::ChirpWSPR:
|
|
case QSDR::ChirpWSPRFile:
|
|
break;
|
|
*/
|
|
}
|
|
|
|
m_dataProcThread = new QThreadEx();
|
|
m_dataProcessor->moveToThread(m_dataProcThread);
|
|
sendSocket->moveToThread(m_dataProcThread);
|
|
|
|
switch (m_hwInterface) {
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
m_dataProcessor->connect(
|
|
m_dataProcThread,
|
|
SIGNAL(started()),
|
|
SLOT(processData()));
|
|
break;
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
m_dataProcessor->connect(
|
|
m_dataProcThread,
|
|
SIGNAL(started()),
|
|
SLOT(processDeviceData()));
|
|
|
|
break;
|
|
}
|
|
|
|
//m_dataProcessor->connect(m_dataProcThread, SIGNAL(started()), SLOT(initDataProcessorSocket()));
|
|
}
|
|
|
|
bool DataEngine::startDataProcessor(QThread::Priority prio) {
|
|
|
|
m_dataProcThread->start(prio);
|
|
|
|
if (m_dataProcThread->isRunning()) {
|
|
|
|
m_dataProcThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "data processor thread started.";
|
|
io.networkIOMutex.unlock();
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
m_dataProcThreadRunning = false;
|
|
setSystemState(QSDR::DataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DataEngine::stopDataProcessor() {
|
|
|
|
if (m_dataProcThread->isRunning()) {
|
|
|
|
m_dataProcessor->stop();
|
|
|
|
if (m_serverMode == QSDR::SDRMode || m_serverMode == QSDR::ChirpWSPR) {
|
|
|
|
if (io.iq_queue.isEmpty()) {
|
|
io.iq_queue.enqueue(QByteArray(BUFFER_SIZE, 0x0));
|
|
}
|
|
}
|
|
else if (m_serverMode == QSDR::ChirpWSPRFile) {
|
|
|
|
if (io.data_queue.isEmpty()) {
|
|
|
|
QList<qreal> buf;
|
|
for (int i = 0; i < 128; i++) buf << 0.0f;
|
|
io.data_queue.enqueue(buf);
|
|
}
|
|
}
|
|
|
|
m_dataProcThread->quit();
|
|
m_dataProcThread->wait();
|
|
delete m_dataProcThread;
|
|
delete m_dataProcessor;
|
|
m_dataProcessor = 0;
|
|
|
|
if (m_serverMode == QSDR::SDRMode || m_serverMode == QSDR::ChirpWSPR) {
|
|
|
|
while (!io.iq_queue.isEmpty())
|
|
io.iq_queue.dequeue();
|
|
|
|
DATA_ENGINE_DEBUG << "iq_queue empty.";
|
|
}
|
|
else if (m_serverMode == QSDR::ChirpWSPRFile) {
|
|
|
|
while (!io.data_queue.isEmpty())
|
|
io.data_queue.dequeue();
|
|
|
|
DATA_ENGINE_DEBUG << "data_queue empty.";
|
|
chirpData.clear();
|
|
}
|
|
|
|
m_dataProcThreadRunning = false;
|
|
|
|
DATA_ENGINE_DEBUG << "data processor thread deleted.";
|
|
}
|
|
else
|
|
DATA_ENGINE_DEBUG << "data processor thread wasn't started.";
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop audio out processor
|
|
|
|
void DataEngine::createAudioOutProcessor() {
|
|
|
|
m_audioOutProcessor = new AudioOutProcessor(this, m_serverMode);
|
|
|
|
switch (m_serverMode) {
|
|
|
|
//case QSDR::ExternalDSP:
|
|
// break;
|
|
|
|
case QSDR::SDRMode:
|
|
|
|
/*connect(
|
|
this,
|
|
SIGNAL(iqDataReady(int)),
|
|
SLOT(dttSPDspProcessing(int)),
|
|
Qt::DirectConnection);*/
|
|
|
|
break;
|
|
|
|
case QSDR::NoServerMode:
|
|
case QSDR::DemoMode:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_audioOutProcThread = new QThreadEx();
|
|
m_audioOutProcessor->moveToThread(m_audioOutProcThread);
|
|
|
|
switch (m_hwInterface) {
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
/*m_audioOutProcessor->connect(
|
|
m_audioOutProcThread,
|
|
SIGNAL(started()),
|
|
SLOT(processData()));*/
|
|
break;
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
/*m_audioOutProcessor->connect(
|
|
m_audioOutProcThread,
|
|
SIGNAL(started()),
|
|
SLOT(processDeviceData()));*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DataEngine::startAudioOutProcessor(QThread::Priority prio) {
|
|
|
|
Q_UNUSED (prio)
|
|
}
|
|
|
|
void DataEngine::stopAudioOutProcessor() {
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop winde band data processor
|
|
|
|
void DataEngine::createWideBandDataProcessor() {
|
|
|
|
int size;
|
|
|
|
if (io.mercuryFW > 32 || io.hermesFW > 11)
|
|
size = BIGWIDEBANDSIZE;
|
|
else
|
|
size = SMALLWIDEBANDSIZE;
|
|
|
|
m_wbDataProcessor = new WideBandDataProcessor(&io, m_serverMode, size);
|
|
|
|
m_wbDataProcThread = new QThreadEx();
|
|
m_wbDataProcessor->moveToThread(m_wbDataProcThread);
|
|
m_wbDataProcessor->connect(
|
|
m_wbDataProcThread,
|
|
SIGNAL(started()),
|
|
SLOT(processWideBandData()));
|
|
|
|
CHECKED_CONNECT(
|
|
set,
|
|
SIGNAL(spectrumAveragingChanged(QObject*, int, bool)),
|
|
m_wbDataProcessor,
|
|
SLOT(setWbSpectrumAveraging(QObject*, int, bool)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_wbDataProcessor,
|
|
SIGNAL(wbSpectrumBufferChanged(const qVectorFloat&)),
|
|
set,
|
|
SLOT(setWidebandSpectrumBuffer(const qVectorFloat&)));
|
|
}
|
|
|
|
bool DataEngine::startWideBandDataProcessor(QThread::Priority prio) {
|
|
|
|
m_wbDataProcThread->start(prio);//(QThread::TimeCriticalPriority);//(QThread::HighPriority);//(QThread::LowPriority);
|
|
|
|
if (m_wbDataProcThread->isRunning()) {
|
|
|
|
m_wbDataRcvrThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "wide band data processor thread started.";
|
|
io.networkIOMutex.unlock();
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
m_wbDataRcvrThreadRunning = false;
|
|
setSystemState(QSDR::WideBandDataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DataEngine::stopWideBandDataProcessor() {
|
|
|
|
if (m_wbDataProcThread->isRunning()) {
|
|
|
|
m_wbDataProcessor->stop();
|
|
if (io.wb_queue.isEmpty())
|
|
io.wb_queue.enqueue(m_datagram);
|
|
|
|
m_wbDataProcThread->quit();
|
|
m_wbDataProcThread->wait();
|
|
delete m_wbDataProcThread;
|
|
delete m_wbDataProcessor;
|
|
m_wbDataProcessor = 0;
|
|
|
|
m_wbDataRcvrThreadRunning = false;
|
|
|
|
DATA_ENGINE_DEBUG << "wide band data processor thread deleted.";
|
|
}
|
|
else
|
|
DATA_ENGINE_DEBUG << "wide band data processor thread wasn't started.";
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop chirp processor
|
|
void DataEngine::createChirpDataProcessor() {
|
|
|
|
m_chirpProcessor = new ChirpProcessor(&io);
|
|
DATA_ENGINE_DEBUG << "chirp decoder initialized";
|
|
|
|
CHECKED_CONNECT_OPT(
|
|
m_audioEngine,
|
|
SIGNAL(chirpSignalChanged()),
|
|
m_chirpProcessor,
|
|
SLOT(generateLocalChirp()),
|
|
Qt::DirectConnection);
|
|
|
|
m_audioEngine->reset();
|
|
if (m_audioEngine->generateSweptTone())
|
|
DATA_ENGINE_DEBUG << "audio chirp signal initialized";
|
|
else
|
|
DATA_ENGINE_DEBUG << "audio chirp signal initialization failed";
|
|
|
|
|
|
m_chirpDataProcThread = new QThreadEx();
|
|
m_chirpProcessor->moveToThread(m_chirpDataProcThread);
|
|
m_chirpProcessor->connect(
|
|
m_chirpDataProcThread,
|
|
SIGNAL(started()),
|
|
m_chirpProcessor,
|
|
SLOT(processChirpData()));
|
|
|
|
m_chirpInititalized = true;
|
|
}
|
|
|
|
bool DataEngine::startChirpDataProcessor(QThread::Priority prio) {
|
|
|
|
m_chirpDataProcThread->start(prio);//(QThread::TimeCriticalPriority);//(QThread::HighPriority);//(QThread::LowPriority);
|
|
|
|
if (m_chirpDataProcThread->isRunning()) {
|
|
|
|
m_chirpDataProcThreadRunning = true;
|
|
io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "chirp data processor thread started.";
|
|
io.networkIOMutex.unlock();
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
|
|
m_chirpDataProcThreadRunning = false;
|
|
setSystemState(QSDR::DataProcessThreadError, m_hwInterface, m_serverMode, QSDR::DataEngineDown);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void DataEngine::stopChirpDataProcessor() {
|
|
|
|
if (m_chirpInititalized) {
|
|
|
|
m_chirpProcessor->stop();
|
|
if (io.chirp_queue.isEmpty()) {
|
|
|
|
QList<qreal> buf;
|
|
for (int i = 0; i < 128; i++) buf << 0.0f;
|
|
io.chirp_queue.enqueue(buf);
|
|
}
|
|
|
|
m_chirpDataProcThread->quit();
|
|
m_chirpDataProcThread->wait();
|
|
delete m_chirpDataProcThread;
|
|
delete m_chirpProcessor;
|
|
m_chirpProcessor = 0;
|
|
|
|
if (m_hwInterface == QSDR::NoInterfaceMode) {
|
|
|
|
//freeCPX(io.cpxIn);
|
|
//freeCPX(io.cpxOut);
|
|
delete m_chirpDspEngine;
|
|
|
|
while (!io.chirp_queue.isEmpty())
|
|
io.chirp_queue.dequeue();
|
|
|
|
DATA_ENGINE_DEBUG << "io.cpxIn, io.cpxOut, fft deleted, io.chirp_queue empty.";
|
|
}
|
|
|
|
m_chirpInititalized = false;
|
|
|
|
DATA_ENGINE_DEBUG << "chirp data processor thread deleted.";
|
|
}
|
|
else
|
|
DATA_ENGINE_DEBUG << "chirp data processor thread wasn't started.";
|
|
}
|
|
|
|
//********************************************************
|
|
// create, start/stop audio receiver
|
|
|
|
void DataEngine::createAudioReceiver() {
|
|
|
|
m_audioReceiver = new AudioReceiver(&io);
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioReceiver,
|
|
SIGNAL(rcveIQEvent(QObject *, int)),
|
|
this,
|
|
SLOT(setRcveIQSignal(QObject *, int)));
|
|
|
|
CHECKED_CONNECT(
|
|
m_audioReceiver,
|
|
SIGNAL(clientConnectedEvent(bool)),
|
|
this,
|
|
SLOT(setClientConnected(bool)));
|
|
|
|
|
|
m_AudioRcvrThread = new QThreadEx();
|
|
m_audioReceiver->moveToThread(m_AudioRcvrThread);
|
|
|
|
m_audioReceiver->connect(
|
|
m_AudioRcvrThread,
|
|
SIGNAL(started()),
|
|
SLOT(initClient()));
|
|
}
|
|
|
|
|
|
void DataEngine::processFileBuffer(const QList<qreal> buffer) {
|
|
|
|
|
|
int topsize = 2*BUFFER_SIZE - 1;
|
|
//float specMax = -100.0f;
|
|
//float specMin = 0.0f;
|
|
|
|
Q_ASSERT(buffer.length() == 128);
|
|
|
|
for (int i = 0; i < 64; i++) {
|
|
|
|
cpxIn[i + m_rxSamples].re = buffer.at(2*i);
|
|
cpxIn[i + m_rxSamples].im = buffer.at(2*i+1);
|
|
|
|
chirpData << buffer.at(2*i);
|
|
chirpData << buffer.at(2*i+1);
|
|
|
|
m_chirpSamples++;
|
|
if (m_chirpSamples == io.samplerate) {
|
|
|
|
io.chirp_queue.enqueue(chirpData);
|
|
chirpData.clear();
|
|
m_chirpSamples = 0;
|
|
}
|
|
}
|
|
m_rxSamples += 64;
|
|
|
|
if (m_rxSamples == 2*BUFFER_SIZE) {
|
|
|
|
m_chirpDspEngine->fft->DoFFTWForward(cpxIn, cpxOut, 2*BUFFER_SIZE);
|
|
|
|
// reorder the spectrum buffer
|
|
for (int i = 0; i < BUFFER_SIZE; i++) {
|
|
|
|
m_spectrumBuffer[topsize - i] =
|
|
(float)(10.0 * log10(MagCPX(cpxOut[i+BUFFER_SIZE]) + 1.5E-45));
|
|
m_spectrumBuffer[BUFFER_SIZE - i] =
|
|
(float)(10.0 * log10(MagCPX(cpxOut[i]) + 1.5E-45));
|
|
}
|
|
|
|
/*float specMean = 0.0f;
|
|
for (int i = BUFFER_SIZE+20; i < BUFFER_SIZE+105; i++) {
|
|
|
|
specMean += m_spectrumBuffer[i];
|
|
if (m_spectrumBuffer[i] > specMax) specMax = m_spectrumBuffer[i];
|
|
if (m_spectrumBuffer[i] < specMin) specMin = m_spectrumBuffer[i];
|
|
}*/
|
|
//specMean *= 1.0f/BUFFER_SIZE;
|
|
//DATA_PROCESSOR_DEBUG << "pan min" << specMin << "max" << specMax << "mean" << specMean;
|
|
|
|
SleeperThread::usleep(42667);
|
|
|
|
//emit spectrumBufferChanged(m_spectrumBuffer);
|
|
//set->setSpectrumBuffer(m_spectrumBuffer);
|
|
//set->setSpectrumBuffer(0, m_spectrumBuffer);
|
|
|
|
m_rxSamples = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
|
|
void DataEngine::systemStateChanged(
|
|
QObject *sender,
|
|
QSDR::_Error err,
|
|
QSDR::_HWInterfaceMode hwmode,
|
|
QSDR::_ServerMode mode,
|
|
QSDR::_DataEngineState state)
|
|
{
|
|
Q_UNUSED (sender)
|
|
Q_UNUSED (err)
|
|
|
|
io.mutex.lock();
|
|
if (m_hwInterface != hwmode)
|
|
m_hwInterface = hwmode;
|
|
|
|
if (m_serverMode != mode)
|
|
m_serverMode = mode;
|
|
|
|
if (m_dataEngineState != state)
|
|
m_dataEngineState = state;
|
|
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setSystemState(
|
|
QSDR::_Error err,
|
|
QSDR::_HWInterfaceMode hwmode,
|
|
QSDR::_ServerMode statemode,
|
|
QSDR::_DataEngineState enginestate)
|
|
{
|
|
io.networkIOMutex.lock();
|
|
set->setSystemState(this, err, hwmode, statemode, enginestate);
|
|
io.networkIOMutex.unlock();
|
|
}
|
|
|
|
float DataEngine::getFilterSizeCalibrationOffset() {
|
|
|
|
//int size=1024; // dspBufferSize
|
|
float i = log10((qreal) BUFFER_SIZE);
|
|
return 3.0f*(11.0f - i);
|
|
}
|
|
|
|
void DataEngine::searchHpsdrNetworkDevices() {
|
|
|
|
if (!m_discoverer) createDiscoverer();
|
|
|
|
// HPSDR network IO thread
|
|
if (!startDiscoverer(QThread::NormalPriority)) {
|
|
|
|
DATA_ENGINE_DEBUG << "HPSDR network discovery thread could not be started.";
|
|
return;
|
|
}
|
|
|
|
io.networkIOMutex.lock();
|
|
io.devicefound.wait(&io.networkIOMutex);
|
|
|
|
//m_discoverer->findHPSDRDevices();
|
|
|
|
// stop the discovery thread
|
|
io.networkIOMutex.unlock();
|
|
stopDiscoverer();
|
|
}
|
|
|
|
void DataEngine::setHPSDRDeviceNumber(int value) {
|
|
|
|
m_hpsdrDevices = value;
|
|
}
|
|
|
|
void DataEngine::rxListChanged(QList<Receiver *> list) {
|
|
|
|
io.mutex.lock();
|
|
RX = list;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setCurrentReceiver(QObject *sender, int rx) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.currentReceiver = rx;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setFramesPerSecond(QObject *sender, int rx, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
Q_UNUSED(rx)
|
|
Q_UNUSED(value)
|
|
|
|
/*io.mutex.lock();
|
|
if (m_fpsList.length() > 0)
|
|
m_fpsList[rx] = (int)(1000000.0/value);
|
|
io.mutex.unlock();*/
|
|
}
|
|
|
|
void DataEngine::setSampleRate(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
switch (value) {
|
|
|
|
case 48000:
|
|
io.samplerate = value;
|
|
io.speed = 0;
|
|
io.outputMultiplier = 1;
|
|
break;
|
|
|
|
case 96000:
|
|
io.samplerate = value;
|
|
io.speed = 1;
|
|
io.outputMultiplier = 2;
|
|
break;
|
|
|
|
case 192000:
|
|
io.samplerate = value;
|
|
io.speed = 2;
|
|
io.outputMultiplier = 4;
|
|
break;
|
|
|
|
case 384000:
|
|
io.samplerate = value;
|
|
io.speed = 3;
|
|
io.outputMultiplier = 8;
|
|
break;
|
|
|
|
default:
|
|
DATA_ENGINE_DEBUG << "invalid sample rate !\n";
|
|
stop();
|
|
break;
|
|
}
|
|
|
|
io.mutex.unlock();
|
|
|
|
emit outMultiplierEvent(io.outputMultiplier);
|
|
}
|
|
|
|
void DataEngine::setMercuryAttenuator(QObject *sender, HamBand band, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
Q_UNUSED(band)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.mercuryAttenuator = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setMercuryAttenuators(QObject *sender, QList<int> attn) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.mercuryAttenuators = attn;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setDither(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.dither = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setRandom(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.random = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::set10MhzSource(QObject *sender, int source) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.control_out[1] = io.control_out[1] & 0xF3;
|
|
io.control_out[1] = io.control_out[1] | (source << 2);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::set122_88MhzSource(QObject *sender, int source) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.control_out[1] = io.control_out[1] & 0xEF;
|
|
io.control_out[1] = io.control_out[1] | (source << 4);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setMicSource(QObject *sender, int source) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.control_out[1] = io.control_out[1] & 0x7F;
|
|
io.control_out[1] = io.control_out[1] | (source << 7);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setMercuryClass(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.rxClass = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setMercuryTiming(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
io.timing = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setAlexConfiguration(quint16 conf) {
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.alexConfig = conf;
|
|
DATA_ENGINE_DEBUG << "Alex Configuration = " << io.ccTx.alexConfig;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setAlexStates(HamBand band, const QList<int> &states) {
|
|
|
|
Q_UNUSED (band)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.alexStates = states;
|
|
DATA_ENGINE_DEBUG << "Alex States = " << io.ccTx.alexStates;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setPennyOCEnabled(bool value) {
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.pennyOCenabled = value;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setRxJ6Pins(const QList<int> &list) {
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.rxJ6pinList = list;
|
|
io.mutex.unlock();
|
|
|
|
}
|
|
|
|
void DataEngine::setTxJ6Pins(const QList<int> &list) {
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.txJ6pinList = list;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setRcveIQSignal(QObject *sender, int value) {
|
|
|
|
emit rcveIQEvent(sender, value);
|
|
}
|
|
|
|
void DataEngine::setPenelopeVersion(QObject *sender, int version) {
|
|
|
|
emit penelopeVersionInfoEvent(sender, version);
|
|
}
|
|
|
|
void DataEngine::setHwIOVersion(QObject *sender, int version) {
|
|
|
|
emit hwIOVersionInfoEvent(sender, version);
|
|
}
|
|
|
|
void DataEngine::setNumberOfRx(QObject *sender, int value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
if (io.receivers == value) return;
|
|
|
|
io.mutex.lock();
|
|
io.receivers = value;
|
|
io.mutex.unlock();
|
|
//io.control_out[4] &= 0xc7;
|
|
//io.control_out[4] |= (value - 1) << 3;
|
|
|
|
DATA_ENGINE_DEBUG << "number of receivers set to " << QString::number(value);
|
|
}
|
|
|
|
void DataEngine::setTimeStamp(QObject *sender, bool value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
if (io.timeStamp == value) return;
|
|
|
|
io.mutex.lock();
|
|
io.timeStamp = value;
|
|
io.mutex.unlock();
|
|
//io.control_out[4] &= 0xc7;
|
|
io.control_out[4] |= value << 6;
|
|
|
|
if (value)
|
|
DATA_ENGINE_DEBUG << "set time stamp on";
|
|
else
|
|
DATA_ENGINE_DEBUG << "set time stamp off";
|
|
}
|
|
|
|
void DataEngine::setRxSocketState(int rx, const char* prop, QString str) {
|
|
|
|
RX[rx]->setProperty(prop, str);
|
|
set->setRxList(RX);
|
|
}
|
|
|
|
void DataEngine::setRxPeerAddress(int rx, QHostAddress address) {
|
|
|
|
RX[rx]->setPeerAddress(address);
|
|
set->setRxList(RX);
|
|
}
|
|
|
|
void DataEngine::setRx(int rx) {
|
|
|
|
io.mutex.lock();
|
|
RX[rx]->setReceiver(rx);
|
|
set->setRxList(RX);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setRxClient(int rx, int client) {
|
|
|
|
io.mutex.lock();
|
|
RX[rx]->setClient(client);
|
|
set->setRxList(RX);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setClientConnected(QObject* sender, int rx) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
if (!io.clientList.contains(rx)) {
|
|
|
|
io.clientList.append(rx);
|
|
io.audio_rx = rx;
|
|
|
|
m_AudioRcvrThread->quit();
|
|
m_AudioRcvrThread->wait();
|
|
m_AudioRcvrThread->start();
|
|
}
|
|
else {
|
|
|
|
io.sendIQ_toggle = true;
|
|
io.rcveIQ_toggle = false;
|
|
m_AudioRcvrThread->start();
|
|
}
|
|
}
|
|
|
|
void DataEngine::setClientConnected(bool value) {
|
|
|
|
m_clientConnected = value;
|
|
}
|
|
|
|
void DataEngine::setClientDisconnected(int client) {
|
|
|
|
Q_UNUSED(client)
|
|
/*if (m_clientConnected) {
|
|
|
|
m_AudioRcvrThread->quit();
|
|
m_AudioRcvrThread->wait();
|
|
if (!m_AudioRcvrThread->isRunning())
|
|
DATA_ENGINE_DEBUG << "audio receiver thread stopped.";
|
|
|
|
m_clientConnected = false;
|
|
}
|
|
sync_toggle = true;
|
|
adc_toggle = false;*/
|
|
}
|
|
|
|
//void DataEngine::setAudioInProcessorRunning(bool value) {
|
|
//
|
|
// //m_audioInProcessorRunning = value;
|
|
//}
|
|
|
|
void DataEngine::setAudioReceiver(QObject *sender, int rx) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
emit audioRxEvent(rx);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setIQPort(int rx, int port) {
|
|
|
|
io.mutex.lock();
|
|
RX[rx]->setIQPort(port);
|
|
set->setRxList(RX);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setRxConnectedStatus(QObject* sender, int rx, bool value) {
|
|
|
|
Q_UNUSED(sender)
|
|
|
|
io.mutex.lock();
|
|
RX[rx]->setConnectedStatus(value);
|
|
set->setRxList(RX);
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setHamBand(QObject *sender, int rx, bool byBtn, HamBand band) {
|
|
|
|
Q_UNUSED(sender)
|
|
Q_UNUSED(rx)
|
|
Q_UNUSED(byBtn)
|
|
|
|
io.mutex.lock();
|
|
io.ccTx.currentBand = band;
|
|
io.mutex.unlock();
|
|
}
|
|
|
|
void DataEngine::setFrequency(QObject* sender, int mode, int rx, long frequency) {
|
|
|
|
Q_UNUSED (sender)
|
|
Q_UNUSED (mode)
|
|
|
|
//RX[rx]->setFrequency(frequency);
|
|
RX[rx]->setCtrFrequency(frequency);
|
|
io.rx_freq_change = rx;
|
|
io.tx_freq_change = rx;
|
|
}
|
|
|
|
void DataEngine::loadWavFile(const QString &fileName) {
|
|
|
|
if (m_audioEngine->loadFile(fileName))
|
|
m_soundFileLoaded = true;
|
|
else
|
|
m_soundFileLoaded = false;
|
|
}
|
|
|
|
void DataEngine::suspend() {
|
|
|
|
m_audioEngine->suspend();
|
|
}
|
|
|
|
void DataEngine::startPlayback() {
|
|
|
|
m_audioEngine->startPlayback();
|
|
}
|
|
|
|
void DataEngine::showSettingsDialog() {
|
|
|
|
m_audioEngine->showSettingsDialog();
|
|
}
|
|
|
|
void DataEngine::setAudioFileFormat(QObject *sender, const QAudioFormat &format) {
|
|
|
|
Q_UNUSED (sender)
|
|
Q_UNUSED (format)
|
|
}
|
|
|
|
void DataEngine::setAudioFilePosition(QObject *sender, qint64 position) {
|
|
|
|
Q_UNUSED (sender)
|
|
Q_UNUSED (position)
|
|
}
|
|
|
|
void DataEngine::setAudioFileBuffer(QObject *sender, qint64 position, qint64 length, const QByteArray &buffer) {
|
|
|
|
Q_UNUSED (sender)
|
|
|
|
m_audioFileBufferPosition = position;
|
|
m_audioFileBufferLength = length;
|
|
m_audioFileBuffer = buffer;
|
|
|
|
//DATA_ENGINE_DEBUG << "audio file length" << m_audioFileBufferLength;
|
|
}
|
|
|
|
void DataEngine::setAudioFileBuffer(const QList<qreal> &buffer) {
|
|
|
|
io.inputBuffer = buffer;
|
|
|
|
/*for (int i = 0; i < buffer.length(); i++) {
|
|
|
|
DATA_ENGINE_DEBUG << "i" << i << "audioBuffer" << io.inputBuffer.at(i);
|
|
}*/
|
|
}
|
|
|
|
// *********************************************************************
|
|
// Data processor
|
|
|
|
DataProcessor::DataProcessor(
|
|
DataEngine *de,
|
|
QSDR::_ServerMode serverMode,
|
|
QSDR::_HWInterfaceMode hwMode)
|
|
: QObject()
|
|
, de(de)
|
|
, set(Settings::instance())
|
|
, m_dataProcessorSocket(0)
|
|
, m_serverMode(serverMode)
|
|
, m_hwInterface(hwMode)
|
|
, m_socketConnected(false)
|
|
, m_setNetworkDeviceHeader(true)
|
|
, m_chirpGateBit(true)
|
|
, m_chirpBit(false)
|
|
, m_chirpStart(false)
|
|
, m_bytes(0)
|
|
, m_offset(0)
|
|
, m_length(0)
|
|
, m_rxSamples(0)
|
|
, m_chirpSamples(0)
|
|
, m_chirpStartSample(0)
|
|
, m_idx(IO_HEADER_SIZE)
|
|
, m_sendState(0)
|
|
, m_stopped(false)
|
|
{
|
|
m_IQSequence = 0L;
|
|
m_sequenceHi = 0L;
|
|
|
|
m_IQDatagram.resize(0);
|
|
|
|
m_SyncChangedTime.start();
|
|
m_ADCChangedTime.start();
|
|
|
|
m_fwCount = 0;
|
|
|
|
m_sendSequence = 0L;
|
|
m_oldSendSequence = 0L;
|
|
|
|
m_deviceSendDataSignature.resize(4);
|
|
m_deviceSendDataSignature[0] = (char)0xEF;
|
|
m_deviceSendDataSignature[1] = (char)0xFE;
|
|
m_deviceSendDataSignature[2] = (char)0x01;
|
|
m_deviceSendDataSignature[3] = (char)0x02;
|
|
|
|
//socket = new QUdpSocket();
|
|
m_deviceAddress = set->getCurrentMetisCard().ip_address;
|
|
}
|
|
|
|
DataProcessor::~DataProcessor() {
|
|
}
|
|
|
|
void DataProcessor::stop() {
|
|
|
|
m_stopped = true;
|
|
}
|
|
|
|
void DataProcessor::initDataProcessorSocket() {
|
|
|
|
m_dataProcessorSocket = new QUdpSocket();
|
|
|
|
/*m_dataProcessorSocket->bind(QHostAddress(set->getHPSDRDeviceLocalAddr()),
|
|
23000,
|
|
QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
|
|
|
|
int newBufferSize = 64 * 1024;
|
|
|
|
if (::setsockopt(m_dataProcessorSocket->socketDescriptor(), SOL_SOCKET,
|
|
SO_RCVBUF, (char *)&newBufferSize, sizeof(newBufferSize)) == -1) {
|
|
|
|
DATA_ENGINE_DEBUG << "initDataProcessorSocket error setting m_dataProcessorSocket buffer size.";
|
|
}*/
|
|
|
|
//m_dataProcessorSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
|
|
//m_dataProcessorSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
|
|
|
|
CHECKED_CONNECT(
|
|
m_dataProcessorSocket,
|
|
SIGNAL(error(QAbstractSocket::SocketError)),
|
|
this,
|
|
SLOT(displayDataProcessorSocketError(QAbstractSocket::SocketError)));
|
|
}
|
|
|
|
void DataProcessor::displayDataProcessorSocketError(QAbstractSocket::SocketError error) {
|
|
|
|
DATA_PROCESSOR_DEBUG << "data processor socket error: " << error;
|
|
}
|
|
|
|
void DataProcessor::processDeviceData() {
|
|
|
|
//if (m_serverMode == QSDR::ExternalDSP)
|
|
// initDataProcessorSocket();
|
|
|
|
DATA_PROCESSOR_DEBUG << "Data Processor thread: " << this->thread();
|
|
forever {
|
|
|
|
//m_dataEngine->processInputBuffer(m_dataEngine->io.iq_queue.dequeue());
|
|
QByteArray buf = de->io.iq_queue.dequeue();
|
|
//de->processInputBuffer(buf.left(BUFFER_SIZE/2));
|
|
//de->processInputBuffer(buf.right(BUFFER_SIZE/2));
|
|
|
|
processInputBuffer(buf.left(BUFFER_SIZE/2));
|
|
processInputBuffer(buf.right(BUFFER_SIZE/2));
|
|
|
|
if (de->io.iq_queue.isFull()) {
|
|
DATA_PROCESSOR_DEBUG << "IQ queue full!";
|
|
}
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
if (m_stopped) {
|
|
m_stopped = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if (m_serverMode == QSDR::ExternalDSP) {
|
|
//
|
|
// disconnect(this);
|
|
// m_dataProcessorSocket->close();
|
|
// delete m_dataProcessorSocket;
|
|
// m_dataProcessorSocket = NULL;
|
|
//
|
|
// m_socketConnected = false;
|
|
// }
|
|
}
|
|
|
|
void DataProcessor::processData() {
|
|
|
|
forever {
|
|
|
|
de->processFileBuffer(de->io.data_queue.dequeue());
|
|
|
|
m_mutex.lock();
|
|
if (m_stopped) {
|
|
m_stopped = false;
|
|
m_mutex.unlock();
|
|
break;
|
|
}
|
|
m_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
void DataProcessor::externalDspProcessing(int rx) {
|
|
|
|
// keep UDP packets < 512 bytes
|
|
// 8 bytes sequency number, 2 bytes offset, 2 bytes length, 500 bytes data
|
|
|
|
if (!m_socketConnected) {
|
|
|
|
m_dataProcessorSocket->connectToHost(de->RX[rx]->getPeerAddress(), de->RX[rx]->getIQPort());
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
//int newBufferSize = 64 * 1024;
|
|
int newBufferSize = 16 * 1024;
|
|
|
|
if (::setsockopt(m_dataProcessorSocket->socketDescriptor(), SOL_SOCKET,
|
|
SO_RCVBUF, (char *)&newBufferSize, sizeof(newBufferSize)) == -1) {
|
|
|
|
DATA_PROCESSOR_DEBUG << "externalDspProcessing error setting m_dataProcessorSocket buffer size.";
|
|
}
|
|
#endif
|
|
|
|
m_socketConnected = true;
|
|
}
|
|
|
|
#ifndef __linux__
|
|
m_sequenceHi = 0L;
|
|
#endif
|
|
|
|
/*QUdpSocket socket;
|
|
CHECKED_CONNECT(&socket,
|
|
SIGNAL(error(QAbstractSocket::SocketError)),
|
|
this,
|
|
SLOT(displayDataProcessorSocketError(QAbstractSocket::SocketError)));*/
|
|
|
|
m_offset = 0;
|
|
//m_IQDatagram.append(reinterpret_cast<const char*>(&m_dataEngine->rxList[rx]->input_buffer), sizeof(m_dataEngine->rxList[rx]->input_buffer));
|
|
m_IQDatagram.append(reinterpret_cast<const char*>(&de->RX[rx]->inBuf), sizeof(de->RX[rx]->inBuf));
|
|
|
|
m_IQDatagram.append(reinterpret_cast<const char*>(&de->RX[rx]->inBuf), sizeof(de->RX[rx]->inBuf));
|
|
|
|
while (m_offset < m_IQDatagram.size()) {
|
|
|
|
m_length = m_IQDatagram.size() - m_offset;
|
|
|
|
if (m_length > 500)
|
|
m_length = 500;
|
|
|
|
QByteArray datagram;
|
|
datagram += QByteArray(reinterpret_cast<const char*>(&m_IQSequence), sizeof(m_IQSequence));
|
|
datagram += QByteArray(reinterpret_cast<const char*>(&m_sequenceHi), sizeof(m_sequenceHi));
|
|
datagram += QByteArray(reinterpret_cast<const char*>(&m_offset), sizeof(m_offset));
|
|
datagram += QByteArray(reinterpret_cast<const char*>(&m_length), sizeof(m_length));
|
|
datagram += m_IQDatagram.mid(m_offset, m_length);
|
|
|
|
if (m_dataProcessorSocket->write(datagram) < 0)
|
|
/*if (m_dataProcessorSocket->writeDatagram(datagram,
|
|
m_dataEngine->rxList[rx]->getPeerAddress(),
|
|
m_dataEngine->rxList[rx]->getIQPort()) < 0)*/
|
|
//if (socket.writeDatagram(datagram,
|
|
// m_dataEngine->rxList[rx]->getPeerAddress(),
|
|
// m_dataEngine->rxList[rx]->getIQPort()) < 0)
|
|
{
|
|
if (!de->io.sendIQ_toggle) { // toggles the sendIQ signal
|
|
|
|
de->set->setSendIQ(2);
|
|
de->io.sendIQ_toggle = true;
|
|
}
|
|
|
|
DATA_ENGINE_DEBUG << "externalDspProcessing error sending data to client:"
|
|
<< m_dataProcessorSocket->errorString();
|
|
}
|
|
else {
|
|
|
|
//socket.flush();
|
|
if (de->io.sendIQ_toggle) { // toggles the sendIQ signal
|
|
|
|
de->set->setSendIQ(1);
|
|
de->io.sendIQ_toggle = false;
|
|
}
|
|
}
|
|
m_offset += m_length;
|
|
}
|
|
m_IQDatagram.resize(0);
|
|
m_IQSequence++;
|
|
}
|
|
|
|
void DataProcessor::externalDspProcessingBig(int rx) {
|
|
|
|
m_IQDatagram.append(reinterpret_cast<const char*>(&de->RX[rx]->in), sizeof(de->RX[rx]->in));
|
|
|
|
if (m_dataProcessorSocket->writeDatagram(m_IQDatagram.data(),
|
|
m_IQDatagram.size(),
|
|
de->RX[rx]->getPeerAddress(),
|
|
de->RX[rx]->getIQPort()) < 0)
|
|
|
|
{
|
|
if (!de->io.sendIQ_toggle) { // toggles the sendIQ signal
|
|
|
|
de->set->setSendIQ(2);
|
|
de->io.sendIQ_toggle = true;
|
|
}
|
|
|
|
DATA_PROCESSOR_DEBUG << "error sending data to client:" << m_dataProcessorSocket->errorString();
|
|
}
|
|
else {
|
|
|
|
m_dataProcessorSocket->flush();
|
|
if (de->io.sendIQ_toggle) { // toggles the sendIQ signal
|
|
|
|
de->set->setSendIQ(1);
|
|
de->io.sendIQ_toggle = false;
|
|
}
|
|
}
|
|
m_IQDatagram.resize(0);
|
|
}
|
|
|
|
void DataProcessor::processInputBuffer(const QByteArray &buffer) {
|
|
|
|
//DATA_PROCESSOR_DEBUG << "processInputBuffer: " << this->thread();
|
|
int s = 0;
|
|
|
|
if (buffer.at(s++) == SYNC && buffer.at(s++) == SYNC && buffer.at(s++) == SYNC)
|
|
{
|
|
// extract C&C bytes
|
|
decodeCCBytes(buffer.mid(3, 5));
|
|
s += 5;
|
|
|
|
switch (de->io.receivers)
|
|
{
|
|
case 1: m_maxSamples = 512-0; break;
|
|
case 2: m_maxSamples = 512-0; break;
|
|
case 3: m_maxSamples = 512-4; break;
|
|
case 4: m_maxSamples = 512-10; break;
|
|
case 5: m_maxSamples = 512-24; break;
|
|
case 6: m_maxSamples = 512-10; break;
|
|
case 7: m_maxSamples = 512-20; break;
|
|
case 8: m_maxSamples = 512-4; break;
|
|
case 9: m_maxSamples = 512-0; break;
|
|
case 10: m_maxSamples = 512-8; break;
|
|
case 11: m_maxSamples = 512-28; break;
|
|
case 12: m_maxSamples = 512-60; break;
|
|
case 13: m_maxSamples = 512-24; break;
|
|
case 14: m_maxSamples = 512-74; break;
|
|
case 15: m_maxSamples = 512-44; break;
|
|
case 16: m_maxSamples = 512-14; break;
|
|
}
|
|
|
|
// extract the samples
|
|
while (s < m_maxSamples)
|
|
{
|
|
// extract each of the receivers
|
|
for (int r = 0; r < de->io.receivers; r++)
|
|
{
|
|
m_leftSample = (int)(( signed char) buffer.at(s++)) << 16;
|
|
m_leftSample += (int)((unsigned char) buffer.at(s++)) << 8;
|
|
m_leftSample += (int)((unsigned char) buffer.at(s++));
|
|
m_rightSample = (int)(( signed char) buffer.at(s++)) << 16;
|
|
m_rightSample += (int)((unsigned char) buffer.at(s++)) << 8;
|
|
m_rightSample += (int)((unsigned char) buffer.at(s++));
|
|
|
|
m_lsample = (float)(m_leftSample / 8388607.0f);
|
|
m_rsample = (float)(m_rightSample / 8388607.0f);
|
|
|
|
/*if (m_serverMode == QSDR::ChirpWSPR &&
|
|
m_chirpInititalized &&
|
|
m_chirpSamples < io.samplerate)
|
|
{
|
|
chirpData << m_lsample;
|
|
chirpData << m_rsample;
|
|
}*/
|
|
|
|
if (de->RX.at(r)->qtdsp) {
|
|
|
|
de->RX[r]->inBuf[m_rxSamples].re = m_lsample; // 24 bit sample
|
|
de->RX[r]->inBuf[m_rxSamples].im = m_rsample; // 24 bit sample
|
|
}
|
|
}
|
|
|
|
m_micSample = (int)((signed char) buffer.at(s++)) << 8;
|
|
|
|
// extract chirp signal time stamp
|
|
//m_chirpBit = (buffer.at(s) & 0x01);// == 0x01;
|
|
|
|
m_micSample += (int)((unsigned char) buffer.at(s++));
|
|
m_micSample_float = (float) m_micSample / 32767.0f * de->io.mic_gain; // 16 bit sample
|
|
|
|
// add to buffer
|
|
de->io.mic_left_buffer[m_rxSamples] = m_micSample_float;
|
|
de->io.mic_right_buffer[m_rxSamples] = 0.0f;
|
|
|
|
////m_chirpSamples++;
|
|
|
|
//if (m_serverMode == QSDR::ChirpWSPR && m_chirpInititalized)
|
|
//{
|
|
// if (m_chirpBit)
|
|
// {
|
|
// if (m_chirpGateBit)
|
|
// {
|
|
// // we've found the rising edge of the GPS 1PPS signal, so we set the samples
|
|
// // counter back to zero in order to have a simple and precise synchronisation
|
|
// // with the local chirp.
|
|
// io.networkIOMutex.lock();
|
|
// DATA_ENGINE_DEBUG << "GPS 1 PPS";
|
|
// io.networkIOMutex.unlock();
|
|
|
|
// // remove the last sample (real and imag) and enqueue the buffer
|
|
// chirpData.removeLast();
|
|
// chirpData.removeLast();
|
|
// io.chirp_queue.enqueue(chirpData);
|
|
|
|
// // empty the buffer and add the last sample, which is the starting point of the chirp
|
|
// m_chirpSamples = 0;
|
|
// chirpData.clear();
|
|
|
|
// chirpData << m_lsample;
|
|
// chirpData << m_rsample;
|
|
|
|
// m_chirpStart = true;
|
|
// m_chirpStartSample = m_rxSamples;
|
|
// m_chirpGateBit = false;
|
|
// }
|
|
// }
|
|
// else
|
|
// m_chirpGateBit = true;
|
|
//}
|
|
m_rxSamples++;
|
|
m_chirpSamples++;
|
|
|
|
// when we have enough rx samples we start the DSP processing.
|
|
if (m_rxSamples == BUFFER_SIZE) {
|
|
|
|
for (int r = 0; r < de->io.receivers; r++) {
|
|
|
|
if (de->RX.at(r)->qtdsp) {
|
|
|
|
QMetaObject::invokeMethod(de->RX.at(r), "dspProcessing", Qt::DirectConnection);// Qt::QueuedConnection);
|
|
}
|
|
}
|
|
m_rxSamples = 0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (m_SyncChangedTime.elapsed() > 10) {
|
|
|
|
set->setProtocolSync(2);
|
|
m_SyncChangedTime.restart();
|
|
}
|
|
}
|
|
}
|
|
|
|
void DataProcessor::decodeCCBytes(const QByteArray &buffer) {
|
|
|
|
de->io.ccRx.ptt = (bool)((buffer.at(0) & 0x01) == 0x01);
|
|
de->io.ccRx.dash = (bool)((buffer.at(0) & 0x02) == 0x02);
|
|
de->io.ccRx.dot = (bool)((buffer.at(0) & 0x04) == 0x04);
|
|
de->io.ccRx.lt2208 = (bool)((buffer.at(1) & 0x01) == 0x01);
|
|
|
|
de->io.ccRx.roundRobin = (uchar)(buffer.at(0) >> 3);
|
|
|
|
switch (de->io.ccRx.roundRobin) // cycle through C0
|
|
{
|
|
case 0:
|
|
|
|
if (de->io.ccRx.lt2208) // check ADC signal
|
|
{
|
|
if (m_ADCChangedTime.elapsed() > 50)
|
|
{
|
|
set->setADCOverflow(2);
|
|
m_ADCChangedTime.restart();
|
|
}
|
|
}
|
|
|
|
//qDebug() << "CC: " << io.ccRx.roundRobin;
|
|
if (m_hwInterface == QSDR::Hermes)
|
|
{
|
|
de->io.ccRx.hermesI01 = (bool)((buffer.at(1) & 0x02) == 0x02);
|
|
de->io.ccRx.hermesI02 = (bool)((buffer.at(1) & 0x04) == 0x04);
|
|
de->io.ccRx.hermesI03 = (bool)((buffer.at(1) & 0x08) == 0x08);
|
|
de->io.ccRx.hermesI04 = (bool)((buffer.at(1) & 0x10) == 0x10);
|
|
//qDebug() << "Hermes IO 1: " << io.ccRx.hermesI01
|
|
// << "2: " << io.ccRx.hermesI02
|
|
// << "3: " << io.ccRx.hermesI03
|
|
// << "4: " << io.ccRx.hermesI04;
|
|
}
|
|
|
|
if (m_fwCount < 100)
|
|
{
|
|
if (m_hwInterface == QSDR::Metis)
|
|
{
|
|
if (de->io.ccRx.devices.mercuryFWVersion != buffer.at(2))
|
|
{
|
|
de->io.ccRx.devices.mercuryFWVersion = buffer.at(2);
|
|
set->setMercuryVersion(de->io.ccRx.devices.mercuryFWVersion);
|
|
de->io.networkIOMutex.lock();
|
|
DATA_PROCESSOR_DEBUG << "Mercury firmware version: " << qPrintable(QString::number(buffer.at(2)));
|
|
de->io.networkIOMutex.unlock();
|
|
}
|
|
|
|
if (de->io.ccRx.devices.penelopeFWVersion != buffer.at(3))
|
|
{
|
|
de->io.ccRx.devices.penelopeFWVersion = buffer.at(3);
|
|
de->io.ccRx.devices.pennylaneFWVersion = buffer.at(3);
|
|
set->setPenelopeVersion(de->io.ccRx.devices.penelopeFWVersion);
|
|
set->setPennyLaneVersion(de->io.ccRx.devices.penelopeFWVersion);
|
|
de->io.networkIOMutex.lock();
|
|
DATA_PROCESSOR_DEBUG << "Penelope/Pennylane firmware version: " << qPrintable(QString::number(buffer.at(3)));
|
|
de->io.networkIOMutex.unlock();
|
|
}
|
|
|
|
if (de->io.ccRx.devices.metisFWVersion != buffer.at(4))
|
|
{
|
|
de->io.ccRx.devices.metisFWVersion = buffer.at(4);
|
|
set->setMetisVersion(de->io.ccRx.devices.metisFWVersion);
|
|
de->io.networkIOMutex.lock();
|
|
DATA_PROCESSOR_DEBUG << "Metis firmware version: " << qPrintable(QString::number(buffer.at(4)));
|
|
de->io.networkIOMutex.unlock();
|
|
}
|
|
}
|
|
else if (set->getHWInterface() == QSDR::Hermes) {
|
|
|
|
if (de->io.ccRx.devices.hermesFWVersion != buffer.at(4)) {
|
|
|
|
de->io.ccRx.devices.hermesFWVersion = buffer.at(4);
|
|
set->setHermesVersion(de->io.ccRx.devices.hermesFWVersion);
|
|
de->io.networkIOMutex.lock();
|
|
DATA_ENGINE_DEBUG << "Hermes firmware version: " << qPrintable(QString::number(buffer.at(4)));
|
|
de->io.networkIOMutex.unlock();
|
|
}
|
|
}
|
|
m_fwCount++;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
|
|
//qDebug() << "CC: " << io.ccRx.roundRobin;
|
|
// forward power
|
|
if (set->getPenelopePresence() || (m_hwInterface == QSDR::Hermes)) { // || set->getPennyLanePresence()
|
|
|
|
de->io.ccRx.ain5 = (quint16)((quint16)(buffer.at(1) << 8) + (quint16)buffer.at(2));
|
|
|
|
de->io.penelopeForwardVolts = (qreal)(3.3 * (qreal)de->io.ccRx.ain5 / 4095.0);
|
|
de->io.penelopeForwardPower = (qreal)(de->io.penelopeForwardVolts * de->io.penelopeForwardVolts / 0.09);
|
|
}
|
|
//qDebug() << "penelopeForwardVolts: " << io.penelopeForwardVolts << "penelopeForwardPower" << io.penelopeForwardPower;
|
|
|
|
if (set->getAlexPresence()) { //|| set->getApolloPresence()) {
|
|
|
|
de->io.ccRx.ain1 = (quint16)((quint16)(buffer.at(3) << 8) + (quint16)buffer.at(4));
|
|
|
|
de->io.alexForwardVolts = (qreal)(3.3 * (qreal)de->io.ccRx.ain1 / 4095.0);
|
|
de->io.alexForwardPower = (qreal)(de->io.alexForwardVolts * de->io.alexForwardVolts / 0.09);
|
|
}
|
|
//qDebug() << "alexForwardVolts: " << io.alexForwardVolts << "alexForwardPower" << io.alexForwardPower;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
//qDebug() << "CC: " << io.ccRx.roundRobin;
|
|
// reverse power
|
|
if (set->getAlexPresence()) { //|| set->getApolloPresence()) {
|
|
|
|
de->io.ccRx.ain2 = (quint16)((quint16)(buffer.at(1) << 8) + (quint16)buffer.at(2));
|
|
|
|
de->io.alexReverseVolts = (qreal)(3.3 * (qreal)de->io.ccRx.ain2 / 4095.0);
|
|
de->io.alexReversePower = (qreal)(de->io.alexReverseVolts * de->io.alexReverseVolts / 0.09);
|
|
}
|
|
//qDebug() << "alexReverseVolts: " << io.alexReverseVolts << "alexReversePower" << io.alexReversePower;
|
|
|
|
if (set->getPenelopePresence() || (m_hwInterface == QSDR::Hermes)) { // || set->getPennyLanePresence() {
|
|
|
|
de->io.ccRx.ain3 = (quint16)((quint16)(buffer.at(3) << 8) + (quint16)buffer.at(4));
|
|
de->io.ain3Volts = (qreal)(3.3 * (double)de->io.ccRx.ain3 / 4095.0);
|
|
}
|
|
//qDebug() << "ain3Volts: " << io.ain3Volts;
|
|
break;
|
|
|
|
case 3:
|
|
|
|
//qDebug() << "CC: " << io.ccRx.roundRobin;
|
|
|
|
if (set->getPenelopePresence() || (m_hwInterface == QSDR::Hermes)) { // || set->getPennyLanePresence() {
|
|
|
|
de->io.ccRx.ain4 = (quint16)((quint16)(buffer.at(1) << 8) + (quint16)buffer.at(2));
|
|
de->io.ccRx.ain6 = (quint16)((quint16)(buffer.at(3) << 8) + (quint16)buffer.at(4));
|
|
|
|
de->io.ain4Volts = (qreal)(3.3 * (qreal)de->io.ccRx.ain4 / 4095.0);
|
|
|
|
if (set->getHWInterface() == QSDR::Hermes) // read supply volts applied to board
|
|
de->io.supplyVolts = (qreal)((qreal)de->io.ccRx.ain6 / 186.0f);
|
|
}
|
|
//qDebug() << "ain4Volts: " << io.ain4Volts << "supplyVolts" << io.supplyVolts;
|
|
break;
|
|
|
|
//case 4:
|
|
|
|
// more than 1 Mercury module (currently not usable)
|
|
//qDebug() << "CC: " << io.ccRx.roundRobin;
|
|
//switch (io.receivers) {
|
|
|
|
// case 1:
|
|
// io.ccRx.mercury1_LT2208 = (bool)((buffer.at(1) & 0x02) == 0x02);
|
|
// //qDebug() << "mercury1_LT2208: " << io.ccRx.mercury1_LT2208;
|
|
// break;
|
|
|
|
// case 2:
|
|
// io.ccRx.mercury1_LT2208 = (bool)((buffer.at(1) & 0x02) == 0x02);
|
|
// io.ccRx.mercury2_LT2208 = (bool)((buffer.at(2) & 0x02) == 0x02);
|
|
// //qDebug() << "mercury1_LT2208: " << io.ccRx.mercury1_LT2208 << "mercury2_LT2208" << io.ccRx.mercury2_LT2208;
|
|
// break;
|
|
|
|
// case 3:
|
|
// io.ccRx.mercury1_LT2208 = (bool)((buffer.at(1) & 0x02) == 0x02);
|
|
// io.ccRx.mercury2_LT2208 = (bool)((buffer.at(2) & 0x02) == 0x02);
|
|
// io.ccRx.mercury3_LT2208 = (bool)((buffer.at(3) & 0x02) == 0x02);
|
|
// //qDebug() << "mercury1_LT2208: " << io.ccRx.mercury1_LT2208 << "mercury2_LT2208" << io.ccRx.mercury2_LT2208;
|
|
// //qDebug() << "mercury3_LT2208: " << io.ccRx.mercury3_LT2208;
|
|
// break;
|
|
|
|
// case 4:
|
|
// io.ccRx.mercury1_LT2208 = (bool)((buffer.at(1) & 0x02) == 0x02);
|
|
// io.ccRx.mercury2_LT2208 = (bool)((buffer.at(2) & 0x02) == 0x02);
|
|
// io.ccRx.mercury3_LT2208 = (bool)((buffer.at(3) & 0x02) == 0x02);
|
|
// io.ccRx.mercury4_LT2208 = (bool)((buffer.at(4) & 0x02) == 0x02);
|
|
// //qDebug() << "mercury1_LT2208: " << io.ccRx.mercury1_LT2208 << "mercury2_LT2208" << io.ccRx.mercury2_LT2208;
|
|
// //qDebug() << "mercury3_LT2208: " << io.ccRx.mercury3_LT2208 << "mercury4_LT2208" << io.ccRx.mercury4_LT2208;
|
|
// break;
|
|
//}
|
|
//break;
|
|
} // end switch cycle through C0
|
|
}
|
|
|
|
void DataProcessor::setOutputBuffer(int rx, const CPX &buffer) {
|
|
|
|
if (rx == de->io.currentReceiver) {
|
|
processOutputBuffer(buffer);
|
|
}
|
|
}
|
|
|
|
void DataProcessor::processOutputBuffer(const CPX &buffer) {
|
|
|
|
//DATA_PROCESSOR_DEBUG << "processOutputBuffer: " << this->thread();
|
|
|
|
qint16 leftRXSample;
|
|
qint16 rightRXSample;
|
|
qint16 leftTXSample;
|
|
qint16 rightTXSample;
|
|
|
|
// process the output
|
|
for (int j = 0; j < BUFFER_SIZE; j += de->io.outputMultiplier) {
|
|
|
|
leftRXSample = (qint16)(buffer.at(j).re * 32767.0f);
|
|
rightRXSample = (qint16)(buffer.at(j).im * 32767.0f);
|
|
|
|
leftTXSample = 0;
|
|
rightTXSample = 0;
|
|
|
|
de->io.output_buffer[m_idx++] = leftRXSample >> 8;
|
|
de->io.output_buffer[m_idx++] = leftRXSample;
|
|
de->io.output_buffer[m_idx++] = rightRXSample >> 8;
|
|
de->io.output_buffer[m_idx++] = rightRXSample;
|
|
de->io.output_buffer[m_idx++] = leftTXSample >> 8;
|
|
de->io.output_buffer[m_idx++] = leftTXSample;
|
|
de->io.output_buffer[m_idx++] = rightTXSample >> 8;
|
|
de->io.output_buffer[m_idx++] = rightTXSample;
|
|
|
|
if (m_idx == IO_BUFFER_SIZE) {
|
|
|
|
//if (de->m_audioBuffer.length() == 1024) {
|
|
|
|
// //m_audioEngine->setAudioBuffer(this, m_audioBuffer);
|
|
// de->m_audioBuffer.resize(0);
|
|
//}
|
|
// set the C&C bytes
|
|
encodeCCBytes();
|
|
|
|
switch (m_hwInterface) {
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
|
|
de->io.audioDatagram.resize(IO_BUFFER_SIZE);
|
|
de->io.audioDatagram = QByteArray::fromRawData((const char *)&de->io.output_buffer, IO_BUFFER_SIZE);
|
|
|
|
//if (m_dataIOThreadRunning) {
|
|
// de->m_dataIO->writeData();
|
|
//}
|
|
|
|
de->m_dataIO->sendAudio(de->io.output_buffer); //RRK
|
|
|
|
writeData();
|
|
break;
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
break;
|
|
}
|
|
m_idx = IO_HEADER_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DataProcessor::encodeCCBytes() {
|
|
|
|
de->io.output_buffer[0] = SYNC;
|
|
de->io.output_buffer[1] = SYNC;
|
|
de->io.output_buffer[2] = SYNC;
|
|
|
|
de->io.mutex.lock();
|
|
switch (m_sendState) {
|
|
|
|
case 0:
|
|
|
|
uchar rxAnt;
|
|
uchar rxOut;
|
|
uchar ant;
|
|
|
|
de->io.control_out[0] = 0x0; // C0
|
|
de->io.control_out[1] = 0x0; // C1
|
|
de->io.control_out[2] = 0x0; // C2
|
|
de->io.control_out[3] = 0x0; // C3
|
|
de->io.control_out[4] = 0x0; // C4
|
|
|
|
// C0
|
|
// 0 0 0 0 0 0 0 0
|
|
// |
|
|
// +------------ MOX (1 = active, 0 = inactive)
|
|
|
|
// set C1
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + +------------ Speed (00 = 48kHz, 01 = 96kHz, 10 = 192kHz)
|
|
// | | | | + +---------------- 10MHz Ref. (00 = Atlas/Excalibur, 01 = Penelope, 10 = Mercury)*
|
|
// | | | +-------------------- 122.88MHz source (0 = Penelope, 1 = Mercury)*
|
|
// | + +---------------------- Config (00 = nil, 01 = Penelope, 10 = Mercury, 11 = both)*
|
|
// +-------------------------- Mic source (0 = Janus, 1 = Penelope)*
|
|
//
|
|
// * Ignored by Hermes
|
|
|
|
de->io.control_out[1] |= de->io.speed; // sample rate
|
|
|
|
de->io.control_out[1] &= 0x03; // 0 0 0 0 0 0 1 1
|
|
de->io.control_out[1] |= de->io.ccTx.clockByte;
|
|
|
|
// set C2
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | |
|
|
// | | +------------ Mode (1 = Class E, 0 = All other modes)
|
|
// +---------- +-------------- Open Collector Outputs on Penelope or Hermes (bit 6...bit 0)
|
|
|
|
de->io.control_out[2] = de->io.rxClass;
|
|
|
|
if (de->io.ccTx.pennyOCenabled) {
|
|
|
|
de->io.control_out[2] &= 0x1; // 0 0 0 0 0 0 0 1
|
|
|
|
if (de->io.ccTx.currentBand != (HamBand) gen) {
|
|
|
|
if (de->io.ccTx.mox || de->io.ccTx.ptt)
|
|
de->io.control_out[2] |= (de->io.ccTx.txJ6pinList.at(de->io.ccTx.currentBand) >> 1) << 1;
|
|
else
|
|
de->io.control_out[2] |= (de->io.ccTx.rxJ6pinList.at(de->io.ccTx.currentBand) >> 1) << 1;
|
|
}
|
|
}
|
|
|
|
|
|
// set C3
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + +------------ Alex Attenuator (00 = 0dB, 01 = 10dB, 10 = 20dB, 11 = 30dB)
|
|
// | | | | | +---------------- Preamp On/Off (0 = Off, 1 = On)
|
|
// | | | | +------------------ LT2208 Dither (0 = Off, 1 = On)
|
|
// | | | + ------------------- LT2208 Random (0= Off, 1 = On)
|
|
// | + + --------------------- Alex Rx Antenna (00 = none, 01 = Rx1, 10 = Rx2, 11 = XV)
|
|
// + ------------------------- Alex Rx out (0 = off, 1 = on). Set if Alex Rx Antenna > 00.
|
|
|
|
|
|
rxAnt = 0x07 & (de->io.ccTx.alexStates.at(de->io.ccTx.currentBand) >> 2);
|
|
rxOut = (rxAnt > 0) ? 1 : 0;
|
|
|
|
de->io.control_out[3] = (de->io.ccTx.alexStates.at(de->io.ccTx.currentBand) >> 7);
|
|
|
|
de->io.control_out[3] &= 0xFB; // 1 1 1 1 1 0 1 1
|
|
de->io.control_out[3] |= (de->io.ccTx.mercuryAttenuator << 2);
|
|
|
|
de->io.control_out[3] &= 0xF7; // 1 1 1 1 0 1 1 1
|
|
de->io.control_out[3] |= (de->io.ccTx.dither << 3);
|
|
|
|
de->io.control_out[3] &= 0xEF; // 1 1 1 0 1 1 1 1
|
|
de->io.control_out[3] |= (de->io.ccTx.random << 4);
|
|
|
|
de->io.control_out[3] &= 0x9F; // 1 0 0 1 1 1 1 1
|
|
de->io.control_out[3] |= rxAnt << 5;
|
|
|
|
de->io.control_out[3] &= 0x7F; // 0 1 1 1 1 1 1 1
|
|
de->io.control_out[3] |= rxOut << 7;
|
|
|
|
// set C4
|
|
//
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | + + ----------- Alex Tx relay (00 = Tx1, 01= Tx2, 10 = Tx3)
|
|
// | | | | | + --------------- Duplex (0 = off, 1 = on)
|
|
// + + + + +------------------ Number of Receivers (000 = 1, 11111 = 32)
|
|
|
|
//RRK removed 4HL
|
|
// | +------------------------ Time stamp - 1PPS on LSB of Mic data (0 = off, 1 = on)
|
|
// +-------------------------- Common Mercury Frequency (0 = independent frequencies to Mercury
|
|
// Boards, 1 = same frequency to all Mercury boards)
|
|
|
|
if (de->io.ccTx.mox || de->io.ccTx.ptt)
|
|
ant = (de->io.ccTx.alexStates.at(de->io.ccTx.currentBand) >> 5);
|
|
else
|
|
ant = de->io.ccTx.alexStates.at(de->io.ccTx.currentBand);
|
|
|
|
de->io.control_out[4] |= (ant != 0) ? ant-1 : ant;
|
|
|
|
de->io.control_out[4] &= 0xFB; // 1 1 1 1 1 0 1 1
|
|
de->io.control_out[4] |= de->io.ccTx.duplex << 2;
|
|
|
|
de->io.control_out[4] &= 0x07; // 0 0 0 0 0 1 1 1
|
|
de->io.control_out[4] |= (de->io.receivers - 1) << 3;
|
|
|
|
//RRK removed 4HL
|
|
//de->io.control_out[4] &= 0xBF; // 1 0 1 1 1 1 1 1
|
|
//de->io.control_out[4] |= de->io.ccTx.timeStamp << 6;
|
|
|
|
//de->io.control_out[4] &= 0x7F; // 0 1 1 1 1 1 1 1
|
|
//de->io.control_out[4] |= de->io.ccTx.commonMercuryFrequencies << 7;
|
|
|
|
// fill the out buffer with the C&C bytes
|
|
for (int i = 0; i < 5; i++)
|
|
de->io.output_buffer[i+3] = de->io.control_out[i];
|
|
|
|
m_sendState = 1;
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// C0
|
|
// 0 0 0 0 0 0 1 x C1, C2, C3, C4 NCO Frequency in Hz for Transmitter, Apollo ATU
|
|
// (32 bit binary representation - MSB in C1)
|
|
|
|
de->io.output_buffer[3] = 0x2; // C0
|
|
|
|
if (de->io.tx_freq_change >= 0) {
|
|
|
|
de->io.output_buffer[4] = de->RX.at(de->io.tx_freq_change)->getCtrFrequency() >> 24;
|
|
de->io.output_buffer[5] = de->RX.at(de->io.tx_freq_change)->getCtrFrequency() >> 16;
|
|
de->io.output_buffer[6] = de->RX.at(de->io.tx_freq_change)->getCtrFrequency() >> 8;
|
|
de->io.output_buffer[7] = de->RX.at(de->io.tx_freq_change)->getCtrFrequency();
|
|
|
|
de->io.tx_freq_change = -1;
|
|
}
|
|
|
|
m_sendState = de->io.ccTx.duplex ? 2 : 3;
|
|
break;
|
|
|
|
case 2:
|
|
|
|
// C0 = 0 0 0 0 0 1 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver_1
|
|
// C0 = 0 0 0 0 0 1 1 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _2
|
|
// C0 = 0 0 0 0 1 0 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _3
|
|
// C0 = 0 0 0 0 1 0 1 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _4
|
|
// C0 = 0 0 0 0 1 1 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _5
|
|
// C0 = 0 0 0 0 1 1 1 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _6
|
|
// C0 = 0 0 0 1 0 0 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _7
|
|
// C0 = 0 0 1 0 0 1 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _8 // Was 0 0 0 1 0 0 1 x
|
|
// C0 = 0 0 1 1 0 1 0 x C1, C2, C3, C4 NCO Frequency in Hz for Receiver _16
|
|
|
|
if (de->io.rx_freq_change >= 0) {
|
|
|
|
de->io.output_buffer[3] = (de->io.rx_freq_change < 7) ? (de->io.rx_freq_change + 2) << 1
|
|
: (de->io.rx_freq_change + 11) << 1;
|
|
//RRK removed 4HL de->io.output_buffer[3] = (de->io.rx_freq_change + 2) << 1;
|
|
de->io.output_buffer[4] = de->RX.at(de->io.rx_freq_change)->getCtrFrequency() >> 24;
|
|
de->io.output_buffer[5] = de->RX.at(de->io.rx_freq_change)->getCtrFrequency() >> 16;
|
|
de->io.output_buffer[6] = de->RX.at(de->io.rx_freq_change)->getCtrFrequency() >> 8;
|
|
de->io.output_buffer[7] = de->RX.at(de->io.rx_freq_change)->getCtrFrequency();
|
|
|
|
de->io.rx_freq_change = -1;
|
|
}
|
|
|
|
m_sendState = 3;
|
|
break;
|
|
|
|
case 3:
|
|
|
|
de->io.control_out[0] = 0x12; // 0 0 0 1 0 0 1 0
|
|
de->io.control_out[1] = 0x0; // C1
|
|
de->io.control_out[2] = 0x0; // C2
|
|
de->io.control_out[3] = 0x0; // C3
|
|
de->io.control_out[4] = 0x0; // C4
|
|
|
|
// C1
|
|
// 0 0 0 0 0 0 0 0
|
|
// | |
|
|
// +-------------+------------ Hermes/PennyLane Drive Level (0-255) (ignored by Penelope)
|
|
|
|
|
|
// C2
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | | |
|
|
// | | | | | | | +------------ Hermes/Metis Penelope Mic boost (0 = 0dB, 1 = 20dB)
|
|
// | | | | | | +-------------- Metis/Penelope or PennyLane Mic/Line-in (0 = mic, 1 = Line-in)
|
|
// | | | | | +---------------- Hermes - Enable/disable Apollo filter (0 = disable, 1 = enable)
|
|
// | | | | +------------------ Hermes - Enable/disable Apollo tuner (0 = disable, 1 = enable)
|
|
// | | | +-------------------- Hermes - Apollo auto tune (0 = end, 1 = start)
|
|
// | | +---------------------- Hermes - select filter board (0 = Alex, 1 = Apollo)
|
|
// | +------------------------ Alex - manual HPF/LPF filter select (0 = disable, 1 = enable)
|
|
// +-------------------------- VNA mode (0 = off, 1 = on)
|
|
|
|
// Alex configuration:
|
|
//
|
|
// manual 0
|
|
|
|
de->io.control_out[2] &= 0xBF; // 1 0 1 1 1 1 1 1
|
|
de->io.control_out[2] |= (de->io.ccTx.alexConfig & 0x01) << 6;
|
|
|
|
// C3
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | |
|
|
// | | | | | | +------------ Alex - select 13MHz HPF (0 = disable, 1 = enable)*
|
|
// | | | | | +-------------- Alex - select 20MHz HPF (0 = disable, 1 = enable)*
|
|
// | | | | +---------------- Alex - select 9.5MHz HPF (0 = disable, 1 = enable)*
|
|
// | | | +------------------ Alex - select 6.5MHz HPF (0 = disable, 1 = enable)*
|
|
// | | +-------------------- Alex - select 1.5MHz HPF (0 = disable, 1 = enable)*
|
|
// | +---------------------- Alex - Bypass all HPFs (0 = disable, 1 = enable)*
|
|
// +------------------------ Alex - 6M low noise amplifier (0 = disable, 1 = enable)*
|
|
//
|
|
// *Only valid when Alex - manual HPF/LPF filter select is enabled
|
|
|
|
de->io.control_out[3] &= 0xFE; // 1 1 1 1 1 1 1 0
|
|
// HPF 13 MHz: 1 0 0 0 0 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x40) >> 6;
|
|
|
|
de->io.control_out[3] &= 0xFD; // 1 1 1 1 1 1 0 1
|
|
// HPF 20 MHz: 1 0 0 0 0 0 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x80) >> 6;
|
|
|
|
de->io.control_out[3] &= 0xFB; // 1 1 1 1 1 0 1 1
|
|
// HPF 9.5 MHz: 1 0 0 0 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x20) >> 3;
|
|
|
|
de->io.control_out[3] &= 0xF7; // 1 1 1 1 0 1 1 1
|
|
// HPF 6.5 MHz: 1 0 0 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x10) >> 1;
|
|
|
|
de->io.control_out[3] &= 0xEF; // 1 1 1 0 1 1 1 1
|
|
// HPF 1.5 MHz: 1 0 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x08) << 1;
|
|
|
|
de->io.control_out[3] &= 0xDF; // 1 1 0 1 1 1 1 1
|
|
// bypass all: 1 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x02) << 4;
|
|
|
|
de->io.control_out[3] &= 0xBF; // 1 0 1 1 1 1 1 1
|
|
// 6m BPF/LNA: 1 0 0
|
|
de->io.control_out[3] |= (de->io.ccTx.alexConfig & 0x04) << 4;
|
|
|
|
de->io.control_out[3] &= 0x7F; // 0 1 1 1 1 1 1 1
|
|
de->io.control_out[3] |= ((int)de->io.ccTx.vnaMode) << 7;
|
|
|
|
// C4
|
|
// 0 0 0 0 0 0 0 0
|
|
// | | | | | | |
|
|
// | | | | | | +------------ Alex - select 30/20m LPF (0 = disable, 1 = enable)*
|
|
// | | | | | +-------------- Alex - select 60/40m LPF (0 = disable, 1 = enable)*
|
|
// | | | | +---------------- Alex - select 80m LPF (0 = disable, 1 = enable)*
|
|
// | | | +------------------ Alex - select 160m LPF (0 = disable, 1 = enable)*
|
|
// | | +-------------------- Alex - select 6m LPF (0 = disable, 1 = enable)*
|
|
// | +---------------------- Alex - select 12/10m LPF (0 = disable, 1 = enable)*
|
|
// +------------------------ Alex - select 17/15m LPF (0 = disable, 1 = enable)*
|
|
//
|
|
// *Only valid when Alex - manual HPF/LPF filter select is enabled
|
|
|
|
de->io.control_out[4] &= 0xFE; // 1 1 1 1 1 1 1 0
|
|
// LPF 30/20m: 1 0 0 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x800) >> 11;
|
|
|
|
de->io.control_out[4] &= 0xFD; // 1 1 1 1 1 1 0 1
|
|
// LPF 60/40m: 1 0 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x400) >> 9;
|
|
|
|
de->io.control_out[4] &= 0xFB; // 1 1 1 1 1 0 1 1
|
|
// LPF 80m: 1 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x200) >> 7;
|
|
|
|
de->io.control_out[4] &= 0xF7; // 1 1 1 1 0 1 1 1
|
|
// LPF 160m: 1 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x100) >> 5;
|
|
|
|
de->io.control_out[4] &= 0xEF; // 1 1 1 0 1 1 1 1
|
|
// LPF 6m: 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x4000) >> 10;
|
|
|
|
de->io.control_out[4] &= 0xDF; // 1 1 0 1 1 1 1 1
|
|
// LPF 12/10m : 1 0 0 0 0 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x2000) >> 8;
|
|
|
|
de->io.control_out[4] &= 0xBF; // 1 0 1 1 1 1 1 1
|
|
// LPF 17/15m: 1 0 0 0 0 0 0 0 0 0 0 0 0
|
|
de->io.control_out[4] |= (de->io.ccTx.alexConfig & 0x1000) >> 6;
|
|
|
|
// fill the out buffer with the C&C bytes
|
|
for (int i = 0; i < 5; i++)
|
|
de->io.output_buffer[i+3] = de->io.control_out[i];
|
|
|
|
// round finished
|
|
m_sendState = 0;
|
|
break;
|
|
}
|
|
de->io.mutex.unlock();
|
|
|
|
|
|
/*switch (m_hwInterface) {
|
|
|
|
case QSDR::Metis:
|
|
case QSDR::Hermes:
|
|
|
|
io.audioDatagram.resize(IO_BUFFER_SIZE);
|
|
io.audioDatagram = QByteArray::fromRawData((const char *)&io.output_buffer, IO_BUFFER_SIZE);
|
|
|
|
if (m_dataIOThreadRunning) {
|
|
m_dataIO->writeData();
|
|
}
|
|
break;
|
|
|
|
case QSDR::NoInterfaceMode:
|
|
break;
|
|
}*/
|
|
}
|
|
|
|
void DataProcessor::writeData() {
|
|
|
|
if (m_setNetworkDeviceHeader) {
|
|
|
|
m_outDatagram.resize(0);
|
|
m_outDatagram += m_deviceSendDataSignature;
|
|
|
|
QByteArray seq(reinterpret_cast<const char*>(&m_sendSequence), sizeof(m_sendSequence));
|
|
|
|
m_outDatagram += seq;
|
|
m_outDatagram += de->io.audioDatagram;
|
|
|
|
m_sendSequence++;
|
|
m_setNetworkDeviceHeader = false;
|
|
}
|
|
else {
|
|
|
|
//QUdpSocket socket;
|
|
//DATA_PROCESSOR_DEBUG << "writeData: " << this->thread();
|
|
m_outDatagram += de->io.audioDatagram;
|
|
|
|
if (de->sendSocket->writeDatagram(m_outDatagram, m_deviceAddress, DEVICE_PORT) < 0) {
|
|
DATA_PROCESSOR_DEBUG << "error sending data to device: " << de->sendSocket->errorString();
|
|
}
|
|
|
|
//if (m_sendSequence%100 == 0)
|
|
// DATAIO_DEBUG << m_sendSequence;
|
|
|
|
if (m_sendSequence != m_oldSendSequence + 1) {
|
|
DATA_PROCESSOR_DEBUG << "output sequence error: old = " << m_oldSendSequence << "; new =" << m_sendSequence;
|
|
}
|
|
|
|
m_oldSendSequence = m_sendSequence;
|
|
m_setNetworkDeviceHeader = true;
|
|
}
|
|
}
|
|
|
|
|
|
// *********************************************************************
|
|
// Audio out processor
|
|
|
|
AudioOutProcessor::AudioOutProcessor(DataEngine *de, QSDR::_ServerMode serverMode)
|
|
: QObject()
|
|
, m_dataEngine(de)
|
|
, m_serverMode(serverMode)
|
|
, m_stopped(false)
|
|
{
|
|
m_IQDatagram.resize(0);
|
|
}
|
|
|
|
AudioOutProcessor::~AudioOutProcessor() {
|
|
}
|
|
|
|
void AudioOutProcessor::stop() {
|
|
|
|
m_stopped = true;
|
|
}
|
|
|
|
void AudioOutProcessor::processDeviceData() {
|
|
|
|
forever {
|
|
|
|
//m_dataEngine->processInputBuffer(m_dataEngine->io.iq_queue.dequeue());
|
|
//DATA_ENGINE_DEBUG << "IQ queue length:" << m_dataEngine->io.iq_queue.count();
|
|
//DATA_ENGINE_DEBUG << "iq_queue length:" << m_dataEngine->io.iq_queue.dequeue().length();
|
|
|
|
m_mutex.lock();
|
|
if (m_stopped) {
|
|
m_stopped = false;
|
|
m_mutex.unlock();
|
|
break;
|
|
}
|
|
m_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
void AudioOutProcessor::processData() {
|
|
|
|
forever {
|
|
|
|
//m_dataEngine->processFileBuffer(m_dataEngine->io.data_queue.dequeue());
|
|
|
|
m_mutex.lock();
|
|
if (m_stopped) {
|
|
m_stopped = false;
|
|
m_mutex.unlock();
|
|
break;
|
|
}
|
|
m_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
|
|
// *********************************************************************
|
|
// Wide band data processor
|
|
|
|
WideBandDataProcessor::WideBandDataProcessor(THPSDRParameter *ioData, QSDR::_ServerMode serverMode, int size)
|
|
: QObject()
|
|
, io(ioData)
|
|
, set(Settings::instance())
|
|
, m_serverMode(serverMode)
|
|
, m_size(size)
|
|
, m_bytes(0)
|
|
, m_wbSpectrumAveraging(true)
|
|
, m_stopped(false)
|
|
{
|
|
m_WBDatagram.resize(0);
|
|
|
|
switch (m_serverMode) {
|
|
|
|
case QSDR::SDRMode:
|
|
|
|
wbFFT = new QFFT(m_size);
|
|
|
|
cpxWBIn.resize(m_size);
|
|
cpxWBOut.resize(m_size);
|
|
|
|
io->wbWindow.resize(m_size);
|
|
io->wbWindow.fill(0.0f);
|
|
|
|
QFilter::MakeWindow(12, m_size, (float *)io->wbWindow.data()); // 12 = BLACKMANHARRIS_WINDOW
|
|
|
|
wbAverager = new DualModeAverager(-1, m_size/2);
|
|
|
|
break;
|
|
|
|
//case QSDR::ExternalDSP:
|
|
case QSDR::ChirpWSPR:
|
|
case QSDR::ChirpWSPRFile:
|
|
break;
|
|
|
|
case QSDR::NoServerMode:
|
|
case QSDR::DemoMode:
|
|
break;
|
|
}
|
|
}
|
|
|
|
WideBandDataProcessor::~WideBandDataProcessor() {
|
|
|
|
delete wbFFT;
|
|
|
|
if (wbAverager) {
|
|
|
|
delete wbAverager;
|
|
}
|
|
|
|
cpxWBIn.clear();
|
|
cpxWBOut.clear();
|
|
}
|
|
|
|
void WideBandDataProcessor::stop() {
|
|
|
|
//mutex.lock();
|
|
m_stopped = true;
|
|
//mutex.unlock();
|
|
}
|
|
|
|
void WideBandDataProcessor::processWideBandData() {
|
|
|
|
forever {
|
|
|
|
processWideBandInputBuffer(io->wb_queue.dequeue());
|
|
|
|
m_mutex.lock();
|
|
if (m_stopped) {
|
|
m_stopped = false;
|
|
m_mutex.unlock();
|
|
break;
|
|
}
|
|
m_mutex.unlock();
|
|
}
|
|
}
|
|
|
|
void WideBandDataProcessor::processWideBandInputBuffer(const QByteArray &buffer) {
|
|
|
|
int size;
|
|
|
|
//if (m_mercuryFW > 32 || m_hermesFW > 16)
|
|
if (io->mercuryFW > 32 || io->hermesFW > 11)
|
|
size = 2 * BIGWIDEBANDSIZE;
|
|
else
|
|
size = 2 * SMALLWIDEBANDSIZE;
|
|
|
|
qint64 length = buffer.length();
|
|
if (buffer.length() != size) {
|
|
|
|
WIDEBAND_PROCESSOR_DEBUG << "wrong wide band buffer length: " << length;
|
|
return;
|
|
}
|
|
|
|
int s;
|
|
float sample;
|
|
float norm = 1.0f / (4 * size);
|
|
|
|
for (int i = 0; i < length; i += 2) {
|
|
|
|
s = (int)((qint8 ) buffer.at(i+1)) << 8;
|
|
s += (int)((quint8) buffer.at(i));
|
|
sample = (float)(s * norm);
|
|
|
|
cpxWBIn[i/2].re = sample * io->wbWindow.at(i/2);
|
|
cpxWBIn[i/2].im = sample * io->wbWindow.at(i/2);
|
|
}
|
|
|
|
wbFFT->DoFFTWForward(cpxWBIn, cpxWBOut, size/2);
|
|
|
|
// averaging
|
|
QVector<float> specBuf(size/4);
|
|
|
|
m_mutex.lock();
|
|
if (m_wbSpectrumAveraging) {
|
|
|
|
for (int i = 0; i < size/4; i++)
|
|
specBuf[i] = (float)(10.0 * log10(MagCPX(cpxWBOut.at(i)) + 1.5E-45));
|
|
|
|
wbAverager->ProcessDBAverager(specBuf, specBuf);
|
|
m_mutex.unlock();
|
|
}
|
|
else {
|
|
|
|
for (int i = 0; i < size/4; i++)
|
|
specBuf[i] = (float)(10.0 * log10(MagCPX(cpxWBOut.at(i)) + 1.5E-45));
|
|
|
|
m_mutex.unlock();
|
|
}
|
|
|
|
//set->setWidebandSpectrumBuffer(specBuf);
|
|
emit wbSpectrumBufferChanged(specBuf);
|
|
}
|
|
|
|
void WideBandDataProcessor::setWbSpectrumAveraging(QObject* sender, int rx, bool value) {
|
|
|
|
Q_UNUSED (sender)
|
|
|
|
if (rx != -1) return;
|
|
|
|
m_mutex.lock();
|
|
m_wbSpectrumAveraging = value;
|
|
m_mutex.unlock();
|
|
}
|