mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-01 21:54:55 -04:00
Metis MISO: initial commit with 4 receivers
This commit is contained in:
@@ -12,5 +12,6 @@ if(ENABLE_XTRX AND LIBXTRX_FOUND)
|
||||
add_subdirectory(xtrxmimo)
|
||||
endif()
|
||||
|
||||
add_subdirectory(metismiso)
|
||||
add_subdirectory(testmi)
|
||||
add_subdirectory(testmosync)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
project(metismiso)
|
||||
|
||||
set(metismiso_SOURCES
|
||||
metismiso.cpp
|
||||
metismisoplugin.cpp
|
||||
metismisoudphandler.cpp
|
||||
metismisosettings.cpp
|
||||
metismisowebapiadapter.cpp
|
||||
metismisodecimators.cpp
|
||||
)
|
||||
|
||||
set(metismiso_HEADERS
|
||||
metismiso.h
|
||||
metismisoplugin.h
|
||||
metismisoudphandler.h
|
||||
metismisosettings.h
|
||||
metismisowebapiadapter.h
|
||||
metismisodecimators.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${CMAKE_SOURCE_DIR}/devices
|
||||
)
|
||||
|
||||
if (NOT SERVER_MODE)
|
||||
set (metismiso_SOURCES
|
||||
${metismiso_SOURCES}
|
||||
metismisogui.cpp
|
||||
metismisogui.ui
|
||||
)
|
||||
set(metismiso_HEADERS
|
||||
${metismiso_HEADERS}
|
||||
metismisogui.h
|
||||
)
|
||||
set(TARGET_NAME mimometismiso)
|
||||
set(TARGET_LIB "Qt5::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME mimometismisosrv)
|
||||
set(TARGET_LIB "")
|
||||
set(TARGET_LIB_GUI "")
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
|
||||
endif()
|
||||
|
||||
add_library(${TARGET_NAME} SHARED
|
||||
${metismiso_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${TARGET_NAME}
|
||||
Qt5::Core
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
swagger
|
||||
metisdevice
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
@@ -0,0 +1,706 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QBuffer>
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "SWGDeviceState.h"
|
||||
#include "SWGMetisMISOSettings.h"
|
||||
|
||||
#include "device/deviceapi.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspdevicemimoengine.h"
|
||||
#include "dsp/devicesamplesource.h"
|
||||
#include "metis/devicemetis.h"
|
||||
|
||||
#include "metismisoudphandler.h"
|
||||
#include "metismiso.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(MetisMISO::MsgConfigureMetisMISO, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MetisMISO::MsgStartStop, Message)
|
||||
|
||||
|
||||
MetisMISO::MetisMISO(DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_settings(),
|
||||
m_udpHandler(&m_sampleMIFifo, deviceAPI),
|
||||
m_deviceDescription("MetisMISO"),
|
||||
m_running(false),
|
||||
m_masterTimer(deviceAPI->getMasterTimer())
|
||||
{
|
||||
m_mimoType = MIMOHalfSynchronous;
|
||||
m_sampleMIFifo.init(4, 96000 * 4);
|
||||
m_deviceAPI->setNbSourceStreams(4);
|
||||
m_deviceAPI->setNbSinkStreams(1);
|
||||
int deviceSequence = m_deviceAPI->getSamplingDeviceSequence();
|
||||
const DeviceMetisScan::DeviceScan *deviceScan = DeviceMetis::instance().getDeviceScanAt(deviceSequence);
|
||||
m_udpHandler.setMetisAddress(deviceScan->m_address, deviceScan->m_port);
|
||||
m_networkManager = new QNetworkAccessManager();
|
||||
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
}
|
||||
|
||||
MetisMISO::~MetisMISO()
|
||||
{
|
||||
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
|
||||
delete m_networkManager;
|
||||
|
||||
if (m_running) {
|
||||
stopRx();
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISO::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MetisMISO::init()
|
||||
{
|
||||
applySettings(m_settings, true);
|
||||
}
|
||||
|
||||
bool MetisMISO::startRx()
|
||||
{
|
||||
qDebug("MetisMISO::startRx");
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (!m_running) {
|
||||
startMetis();
|
||||
}
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
applySettings(m_settings, true);
|
||||
m_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetisMISO::startTx()
|
||||
{
|
||||
qDebug("MetisMISO::startTx");
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (!m_running) {
|
||||
startMetis();
|
||||
}
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
applySettings(m_settings, true);
|
||||
m_running = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetisMISO::stopRx()
|
||||
{
|
||||
qDebug("MetisMISO::stopRx");
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (m_running) {
|
||||
stopMetis();
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void MetisMISO::stopTx()
|
||||
{
|
||||
qDebug("MetisMISO::stopTx");
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
if (m_running) {
|
||||
stopMetis();
|
||||
}
|
||||
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void MetisMISO::startMetis()
|
||||
{
|
||||
MetisMISOUDPHandler::MsgStartStop *message = MetisMISOUDPHandler::MsgStartStop::create(true);
|
||||
m_udpHandler.getInputMessageQueue()->push(message);
|
||||
}
|
||||
|
||||
void MetisMISO::stopMetis()
|
||||
{
|
||||
MetisMISOUDPHandler::MsgStartStop *message = MetisMISOUDPHandler::MsgStartStop::create(false);
|
||||
m_udpHandler.getInputMessageQueue()->push(message);
|
||||
}
|
||||
|
||||
QByteArray MetisMISO::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool MetisMISO::deserialize(const QByteArray& data)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (!m_settings.deserialize(data))
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
success = false;
|
||||
}
|
||||
|
||||
MsgConfigureMetisMISO* message = MsgConfigureMetisMISO::create(m_settings, true);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureMetisMISO* messageToGUI = MsgConfigureMetisMISO::create(m_settings, true);
|
||||
m_guiMessageQueue->push(messageToGUI);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
const QString& MetisMISO::getDeviceDescription() const
|
||||
{
|
||||
return m_deviceDescription;
|
||||
}
|
||||
|
||||
int MetisMISO::getSourceSampleRate(int index) const
|
||||
{
|
||||
if (index < 3) {
|
||||
return MetisMISOSettings::getSampleRateFromIndex(m_settings.m_sampleRateIndex);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
quint64 MetisMISO::getSourceCenterFrequency(int index) const
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case 0:
|
||||
return m_settings.m_rx1CenterFrequency;
|
||||
break;
|
||||
case 1:
|
||||
return m_settings.m_rx2CenterFrequency;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISO::setSourceCenterFrequency(qint64 centerFrequency, int index)
|
||||
{
|
||||
MetisMISOSettings settings = m_settings; // note: calls copy constructor
|
||||
|
||||
if (index < 2)
|
||||
{
|
||||
if (index == 0) {
|
||||
settings.m_rx1CenterFrequency = centerFrequency;
|
||||
} else {
|
||||
settings.m_rx2CenterFrequency = centerFrequency;
|
||||
}
|
||||
|
||||
MsgConfigureMetisMISO* message = MsgConfigureMetisMISO::create(settings, false);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue)
|
||||
{
|
||||
MsgConfigureMetisMISO* messageToGUI = MsgConfigureMetisMISO::create(settings, false);
|
||||
m_guiMessageQueue->push(messageToGUI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MetisMISO::handleMessage(const Message& message)
|
||||
{
|
||||
if (MsgConfigureMetisMISO::match(message))
|
||||
{
|
||||
MsgConfigureMetisMISO& conf = (MsgConfigureMetisMISO&) message;
|
||||
qDebug() << "MetisMISO::handleMessage: MsgConfigureMetisMISO";
|
||||
|
||||
bool success = applySettings(conf.getSettings(), conf.getForce());
|
||||
|
||||
if (!success)
|
||||
{
|
||||
qDebug("MetisMISO::handleMessage: config error");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgStartStop::match(message))
|
||||
{
|
||||
MsgStartStop& cmd = (MsgStartStop&) message;
|
||||
qDebug() << "MetisMISO::handleMessage: MsgStartStop: " << (cmd.getStartStop() ? "start" : "stop");
|
||||
|
||||
if (cmd.getStartStop())
|
||||
{
|
||||
if (m_deviceAPI->initDeviceEngine())
|
||||
{
|
||||
m_deviceAPI->startDeviceEngine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceAPI->stopDeviceEngine();
|
||||
}
|
||||
|
||||
if (m_settings.m_useReverseAPI) {
|
||||
webapiReverseSendStartStop(cmd.getStartStop());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetisMISO::applySettings(const MetisMISOSettings& settings, bool force)
|
||||
{
|
||||
QList<QString> reverseAPIKeys;
|
||||
|
||||
qDebug() << "MetisMISO::applySettings: "
|
||||
<< " m_nbReceivers:" << settings.m_nbReceivers
|
||||
<< " m_rx1CenterFrequency:" << settings.m_rx1CenterFrequency
|
||||
<< " m_rx2CenterFrequency:" << settings.m_rx2CenterFrequency
|
||||
<< " m_rx3CenterFrequency:" << settings.m_rx3CenterFrequency
|
||||
<< " m_rx3CenterFrequency:" << settings.m_rx4CenterFrequency
|
||||
<< " m_txCenterFrequency:" << settings.m_txCenterFrequency
|
||||
<< " m_sampleRateIndex:" << settings.m_sampleRateIndex
|
||||
<< " m_log2Decim:" << settings.m_log2Decim
|
||||
<< " m_preamp:" << settings.m_preamp
|
||||
<< " m_random:" << settings.m_random
|
||||
<< " m_dither:" << settings.m_dither
|
||||
<< " m_duplex:" << settings.m_duplex
|
||||
<< " m_dcBlock:" << settings.m_dcBlock
|
||||
<< " m_iqCorrection:" << settings.m_iqCorrection
|
||||
<< " m_useReverseAPI: " << settings.m_useReverseAPI
|
||||
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
|
||||
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
|
||||
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex;
|
||||
|
||||
bool propagateSettings = false;
|
||||
|
||||
if ((m_settings.m_nbReceivers != settings.m_nbReceivers) || force)
|
||||
{
|
||||
reverseAPIKeys.append("nbReceivers");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx1CenterFrequency != settings.m_rx1CenterFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("rx1CenterFrequency");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx2CenterFrequency != settings.m_rx2CenterFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("rx2CenterFrequency");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx3CenterFrequency != settings.m_rx3CenterFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("rx3CenterFrequency");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx4CenterFrequency != settings.m_rx4CenterFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("rx4CenterFrequency");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_txCenterFrequency != settings.m_txCenterFrequency) || force)
|
||||
{
|
||||
reverseAPIKeys.append("txCenterFrequency");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) || force)
|
||||
{
|
||||
reverseAPIKeys.append("sampleRateIndex");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
reverseAPIKeys.append("log2Decim");
|
||||
propagateSettings = true;
|
||||
}
|
||||
|
||||
if ((m_settings.m_dcBlock != settings.m_dcBlock) || force) {
|
||||
reverseAPIKeys.append("dcBlock");
|
||||
}
|
||||
|
||||
if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) {
|
||||
reverseAPIKeys.append("iqCorrection");
|
||||
}
|
||||
|
||||
if ((m_settings.m_dcBlock != settings.m_dcBlock) ||
|
||||
(m_settings.m_iqCorrection != settings.m_iqCorrection) || force)
|
||||
{
|
||||
m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection, 0);
|
||||
m_deviceAPI->configureCorrections(settings.m_dcBlock, settings.m_iqCorrection, 1);
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx1CenterFrequency != settings.m_rx1CenterFrequency) ||
|
||||
(m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) ||
|
||||
(m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
int devSampleRate = (1<<settings.m_sampleRateIndex) * 48000;
|
||||
int sampleRate = devSampleRate / (1<<settings.m_log2Decim);
|
||||
DSPMIMOSignalNotification *engineRx1Notif = new DSPMIMOSignalNotification(
|
||||
sampleRate, settings.m_rx1CenterFrequency, true, 0);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineRx1Notif);
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx2CenterFrequency != settings.m_rx2CenterFrequency) ||
|
||||
(m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) ||
|
||||
(m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
int devSampleRate = (1<<settings.m_sampleRateIndex) * 48000;
|
||||
int sampleRate = devSampleRate / (1<<settings.m_log2Decim);
|
||||
DSPMIMOSignalNotification *engineRx2Notif = new DSPMIMOSignalNotification(
|
||||
sampleRate, settings.m_rx2CenterFrequency, true, 1);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineRx2Notif);
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx3CenterFrequency != settings.m_rx3CenterFrequency) ||
|
||||
(m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) ||
|
||||
(m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
int devSampleRate = (1<<settings.m_sampleRateIndex) * 48000;
|
||||
int sampleRate = devSampleRate / (1<<settings.m_log2Decim);
|
||||
DSPMIMOSignalNotification *engineRx3Notif = new DSPMIMOSignalNotification(
|
||||
sampleRate, settings.m_rx3CenterFrequency, true, 2);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineRx3Notif);
|
||||
}
|
||||
|
||||
if ((m_settings.m_rx4CenterFrequency != settings.m_rx4CenterFrequency) ||
|
||||
(m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) ||
|
||||
(m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
int devSampleRate = (1<<settings.m_sampleRateIndex) * 48000;
|
||||
int sampleRate = devSampleRate / (1<<settings.m_log2Decim);
|
||||
DSPMIMOSignalNotification *engineRx4Notif = new DSPMIMOSignalNotification(
|
||||
sampleRate, settings.m_rx4CenterFrequency, true, 3);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineRx4Notif);
|
||||
}
|
||||
|
||||
if ((m_settings.m_txCenterFrequency != settings.m_txCenterFrequency) ||
|
||||
(m_settings.m_sampleRateIndex != settings.m_sampleRateIndex) ||
|
||||
(m_settings.m_log2Decim != settings.m_log2Decim) || force)
|
||||
{
|
||||
int devSampleRate = (1<<settings.m_sampleRateIndex) * 48000;
|
||||
int sampleRate = devSampleRate / (1<<settings.m_log2Decim);
|
||||
DSPMIMOSignalNotification *engineTxNotif = new DSPMIMOSignalNotification(
|
||||
sampleRate, settings.m_txCenterFrequency, false, 0);
|
||||
m_deviceAPI->getDeviceEngineInputMessageQueue()->push(engineTxNotif);
|
||||
}
|
||||
|
||||
if (propagateSettings) {
|
||||
m_udpHandler.applySettings(settings);
|
||||
}
|
||||
|
||||
if (settings.m_useReverseAPI)
|
||||
{
|
||||
qDebug("MetisMISO::applySettings: call webapiReverseSendSettings");
|
||||
bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) ||
|
||||
(m_settings.m_reverseAPIAddress != settings.m_reverseAPIAddress) ||
|
||||
(m_settings.m_reverseAPIPort != settings.m_reverseAPIPort) ||
|
||||
(m_settings.m_reverseAPIDeviceIndex != settings.m_reverseAPIDeviceIndex);
|
||||
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
return true;
|
||||
}
|
||||
|
||||
int MetisMISO::webapiRunGet(
|
||||
int subsystemIndex,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
if (subsystemIndex == 0)
|
||||
{
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState()); // Rx only
|
||||
return 200;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = QString("Subsystem index invalid: expect 0 (Rx) only");
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
|
||||
int MetisMISO::webapiRun(
|
||||
bool run,
|
||||
int subsystemIndex,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
if (subsystemIndex == 0)
|
||||
{
|
||||
m_deviceAPI->getDeviceEngineStateStr(*response.getState()); // Rx only
|
||||
MsgStartStop *message = MsgStartStop::create(run);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgStartStop *msgToGUI = MsgStartStop::create(run);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
return 200;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = QString("Subsystem index invalid: expect 0 (Rx) only");
|
||||
return 404;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int MetisMISO::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setMetisMisoSettings(new SWGSDRangel::SWGMetisMISOSettings());
|
||||
response.getMetisMisoSettings()->init();
|
||||
webapiFormatDeviceSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int MetisMISO::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
MetisMISOSettings settings = m_settings;
|
||||
webapiUpdateDeviceSettings(settings, deviceSettingsKeys, response);
|
||||
|
||||
MsgConfigureMetisMISO *msg = MsgConfigureMetisMISO::create(settings, force);
|
||||
m_inputMessageQueue.push(msg);
|
||||
|
||||
if (m_guiMessageQueue) // forward to GUI if any
|
||||
{
|
||||
MsgConfigureMetisMISO *msgToGUI = MsgConfigureMetisMISO::create(settings, force);
|
||||
m_guiMessageQueue->push(msgToGUI);
|
||||
}
|
||||
|
||||
webapiFormatDeviceSettings(response, settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
void MetisMISO::webapiUpdateDeviceSettings(
|
||||
MetisMISOSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response)
|
||||
{
|
||||
if (deviceSettingsKeys.contains("rx1CenterFrequency")) {
|
||||
settings.m_rx1CenterFrequency = response.getMetisMisoSettings()->getRx1CenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("rx2CenterFrequency")) {
|
||||
settings.m_rx2CenterFrequency = response.getMetisMisoSettings()->getRx2CenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("rx3CenterFrequency")) {
|
||||
settings.m_rx3CenterFrequency = response.getMetisMisoSettings()->getRx3CenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("txCenterFrequency")) {
|
||||
settings.m_txCenterFrequency = response.getMetisMisoSettings()->getTxCenterFrequency();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleRateIndex")) {
|
||||
settings.m_sampleRateIndex = response.getMetisMisoSettings()->getSampleRateIndex();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("preamp")) {
|
||||
settings.m_preamp = response.getMetisMisoSettings()->getPreamp() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("random")) {
|
||||
settings.m_random = response.getMetisMisoSettings()->getRandom() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dither")) {
|
||||
settings.m_dither = response.getMetisMisoSettings()->getDither() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("duplex")) {
|
||||
settings.m_duplex = response.getMetisMisoSettings()->getDuplex() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dcBlock")) {
|
||||
settings.m_dcBlock = response.getMetisMisoSettings()->getDcBlock() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqCorrection")) {
|
||||
settings.m_iqCorrection = response.getMetisMisoSettings()->getIqCorrection() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("useReverseAPI")) {
|
||||
settings.m_useReverseAPI = response.getMetisMisoSettings()->getUseReverseApi() != 0;
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIAddress")) {
|
||||
settings.m_reverseAPIAddress = *response.getMetisMisoSettings()->getReverseApiAddress();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIPort")) {
|
||||
settings.m_reverseAPIPort = response.getMetisMisoSettings()->getReverseApiPort();
|
||||
}
|
||||
if (deviceSettingsKeys.contains("reverseAPIDeviceIndex")) {
|
||||
settings.m_reverseAPIDeviceIndex = response.getMetisMisoSettings()->getReverseApiDeviceIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISO::webapiFormatDeviceSettings(SWGSDRangel::SWGDeviceSettings& response, const MetisMISOSettings& settings)
|
||||
{
|
||||
response.getMetisMisoSettings()->setRx1CenterFrequency(settings.m_rx1CenterFrequency);
|
||||
response.getMetisMisoSettings()->setRx2CenterFrequency(settings.m_rx2CenterFrequency);
|
||||
response.getMetisMisoSettings()->setRx3CenterFrequency(settings.m_rx3CenterFrequency);
|
||||
response.getMetisMisoSettings()->setTxCenterFrequency(settings.m_txCenterFrequency);
|
||||
response.getMetisMisoSettings()->setSampleRateIndex(settings.m_sampleRateIndex);
|
||||
response.getMetisMisoSettings()->setPreamp(settings.m_preamp ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setRandom(settings.m_random ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setDither(settings.m_dither ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setDuplex(settings.m_duplex ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setDcBlock(settings.m_dcBlock ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
|
||||
response.getMetisMisoSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
|
||||
|
||||
if (response.getMetisMisoSettings()->getReverseApiAddress()) {
|
||||
*response.getMetisMisoSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
|
||||
} else {
|
||||
response.getMetisMisoSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
|
||||
}
|
||||
|
||||
response.getMetisMisoSettings()->setReverseApiPort(settings.m_reverseAPIPort);
|
||||
response.getMetisMisoSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
|
||||
}
|
||||
|
||||
void MetisMISO::webapiReverseSendSettings(const QList<QString>& deviceSettingsKeys, const MetisMISOSettings& settings, bool force)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(2); // MIMO
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("MetisMISO"));
|
||||
swgDeviceSettings->setMetisMisoSettings(new SWGSDRangel::SWGMetisMISOSettings());
|
||||
SWGSDRangel::SWGMetisMISOSettings *swgMetisMISOSettings = swgDeviceSettings->getMetisMisoSettings();
|
||||
|
||||
if (deviceSettingsKeys.contains("rx1CenterFrequency") || force) {
|
||||
swgMetisMISOSettings->setRx1CenterFrequency(settings.m_rx1CenterFrequency);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("rx2CenterFrequency") || force) {
|
||||
swgMetisMISOSettings->setRx2CenterFrequency(settings.m_rx2CenterFrequency);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("rx3CenterFrequency") || force) {
|
||||
swgMetisMISOSettings->setRx3CenterFrequency(settings.m_rx3CenterFrequency);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("txCenterFrequency") || force) {
|
||||
swgMetisMISOSettings->setTxCenterFrequency(settings.m_txCenterFrequency);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("sampleRateIndex") || force) {
|
||||
swgMetisMISOSettings->setSampleRateIndex(settings.m_sampleRateIndex);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("preamp") || force) {
|
||||
swgMetisMISOSettings->setPreamp(settings.m_preamp ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("random") || force) {
|
||||
swgMetisMISOSettings->setRandom(settings.m_random ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dither") || force) {
|
||||
swgMetisMISOSettings->setDither(settings.m_dither ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("duplex") || force) {
|
||||
swgMetisMISOSettings->setDuplex(settings.m_duplex ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("dcBlock") || force) {
|
||||
swgMetisMISOSettings->setDcBlock(settings.m_dcBlock ? 1 : 0);
|
||||
}
|
||||
if (deviceSettingsKeys.contains("iqCorrection") || force) {
|
||||
swgMetisMISOSettings->setIqCorrection(settings.m_iqCorrection ? 1 : 0);
|
||||
}
|
||||
|
||||
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/settings")
|
||||
.arg(settings.m_reverseAPIAddress)
|
||||
.arg(settings.m_reverseAPIPort)
|
||||
.arg(settings.m_reverseAPIDeviceIndex);
|
||||
m_networkRequest.setUrl(QUrl(channelSettingsURL));
|
||||
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QBuffer *buffer = new QBuffer();
|
||||
buffer->open((QBuffer::ReadWrite));
|
||||
buffer->write(swgDeviceSettings->asJson().toUtf8());
|
||||
buffer->seek(0);
|
||||
|
||||
// Always use PATCH to avoid passing reverse API settings
|
||||
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
|
||||
buffer->setParent(reply);
|
||||
|
||||
delete swgDeviceSettings;
|
||||
}
|
||||
|
||||
void MetisMISO::webapiReverseSendStartStop(bool start)
|
||||
{
|
||||
SWGSDRangel::SWGDeviceSettings *swgDeviceSettings = new SWGSDRangel::SWGDeviceSettings();
|
||||
swgDeviceSettings->setDirection(2); // MIMO
|
||||
swgDeviceSettings->setOriginatorIndex(m_deviceAPI->getDeviceSetIndex());
|
||||
swgDeviceSettings->setDeviceHwType(new QString("MetisMISO"));
|
||||
|
||||
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/device/run")
|
||||
.arg(m_settings.m_reverseAPIAddress)
|
||||
.arg(m_settings.m_reverseAPIPort)
|
||||
.arg(m_settings.m_reverseAPIDeviceIndex);
|
||||
m_networkRequest.setUrl(QUrl(channelSettingsURL));
|
||||
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||
|
||||
QBuffer *buffer = new QBuffer();
|
||||
buffer->open((QBuffer::ReadWrite));
|
||||
buffer->write(swgDeviceSettings->asJson().toUtf8());
|
||||
buffer->seek(0);
|
||||
QNetworkReply *reply;
|
||||
|
||||
if (start) {
|
||||
reply = m_networkManager->sendCustomRequest(m_networkRequest, "POST", buffer);
|
||||
} else {
|
||||
reply = m_networkManager->sendCustomRequest(m_networkRequest, "DELETE", buffer);
|
||||
}
|
||||
|
||||
buffer->setParent(reply);
|
||||
delete swgDeviceSettings;
|
||||
}
|
||||
|
||||
void MetisMISO::networkManagerFinished(QNetworkReply *reply)
|
||||
{
|
||||
QNetworkReply::NetworkError replyError = reply->error();
|
||||
|
||||
if (replyError)
|
||||
{
|
||||
qWarning() << "MetisMISO::networkManagerFinished:"
|
||||
<< " error(" << (int) replyError
|
||||
<< "): " << replyError
|
||||
<< ": " << reply->errorString();
|
||||
}
|
||||
else
|
||||
{
|
||||
QString answer = reply->readAll();
|
||||
answer.chop(1); // remove last \n
|
||||
qDebug("MetisMISO::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISO_H_
|
||||
#define _METISMISO_METISMISO_H_
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
#include <QTimer>
|
||||
#include <QNetworkRequest>
|
||||
#include <QThread>
|
||||
|
||||
#include "dsp/devicesamplemimo.h"
|
||||
#include "metismisoudphandler.h"
|
||||
#include "metismisosettings.h"
|
||||
|
||||
class DeviceAPI;
|
||||
class MetisMISOWorker;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
class MetisMISO : public DeviceSampleMIMO {
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgConfigureMetisMISO : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const MetisMISOSettings& getSettings() const { return m_settings; }
|
||||
bool getForce() const { return m_force; }
|
||||
|
||||
static MsgConfigureMetisMISO* create(const MetisMISOSettings& settings, bool force)
|
||||
{
|
||||
return new MsgConfigureMetisMISO(settings, force);
|
||||
}
|
||||
|
||||
private:
|
||||
MetisMISOSettings m_settings;
|
||||
bool m_force;
|
||||
|
||||
MsgConfigureMetisMISO(const MetisMISOSettings& settings, bool force) :
|
||||
Message(),
|
||||
m_settings(settings),
|
||||
m_force(force)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgStartStop : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getStartStop() const { return m_startStop; }
|
||||
|
||||
static MsgStartStop* create(bool startStop) {
|
||||
return new MsgStartStop(startStop);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_startStop;
|
||||
|
||||
MsgStartStop(bool startStop) :
|
||||
Message(),
|
||||
m_startStop(startStop)
|
||||
{ }
|
||||
};
|
||||
|
||||
MetisMISO(DeviceAPI *deviceAPI);
|
||||
virtual ~MetisMISO();
|
||||
virtual void destroy();
|
||||
|
||||
virtual void init();
|
||||
virtual bool startRx();
|
||||
virtual void stopRx();
|
||||
virtual bool startTx();
|
||||
virtual void stopTx();
|
||||
|
||||
virtual QByteArray serialize() const;
|
||||
virtual bool deserialize(const QByteArray& data);
|
||||
|
||||
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
|
||||
virtual const QString& getDeviceDescription() const;
|
||||
|
||||
virtual int getSourceSampleRate(int index) const;
|
||||
virtual void setSourceSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
|
||||
virtual quint64 getSourceCenterFrequency(int index) const;
|
||||
virtual void setSourceCenterFrequency(qint64 centerFrequency, int index);
|
||||
|
||||
virtual int getSinkSampleRate(int index) const { return 0; (void) index; }
|
||||
virtual void setSinkSampleRate(int sampleRate, int index) { (void) sampleRate; (void) index; }
|
||||
virtual quint64 getSinkCenterFrequency(int index) const { return 0; (void) index; }
|
||||
virtual void setSinkCenterFrequency(qint64 centerFrequency, int index) { (void) centerFrequency; (void) index; }
|
||||
|
||||
virtual quint64 getMIMOCenterFrequency() const { return getSourceCenterFrequency(0); }
|
||||
virtual unsigned int getMIMOSampleRate() const { return getSourceSampleRate(0); }
|
||||
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiRunGet(
|
||||
int subsystemIndex,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiRun(
|
||||
bool run,
|
||||
int subsystemIndex,
|
||||
SWGSDRangel::SWGDeviceState& response,
|
||||
QString& errorMessage);
|
||||
|
||||
static void webapiFormatDeviceSettings(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
const MetisMISOSettings& settings);
|
||||
|
||||
static void webapiUpdateDeviceSettings(
|
||||
MetisMISOSettings& settings,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response);
|
||||
|
||||
private:
|
||||
struct DeviceSettingsKeys
|
||||
{
|
||||
QList<QString> m_commonSettingsKeys;
|
||||
QList<QList<QString>> m_streamsSettingsKeys;
|
||||
};
|
||||
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QMutex m_mutex;
|
||||
MetisMISOSettings m_settings;
|
||||
MetisMISOUDPHandler m_udpHandler;
|
||||
QString m_deviceDescription;
|
||||
bool m_running;
|
||||
const QTimer& m_masterTimer;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
||||
void startMetis();
|
||||
void stopMetis();
|
||||
bool applySettings(const MetisMISOSettings& settings, bool force);
|
||||
void webapiReverseSendSettings(const QList<QString>& deviceSettingsKeys, const MetisMISOSettings& settings, bool force);
|
||||
void webapiReverseSendStartStop(bool start);
|
||||
|
||||
private slots:
|
||||
void networkManagerFinished(QNetworkReply *reply);
|
||||
};
|
||||
|
||||
#endif // _METISMISO_METISMISO_H_
|
||||
@@ -0,0 +1,85 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "metismisodecimators.h"
|
||||
|
||||
MetisMISODecimators::MetisMISODecimators()
|
||||
{
|
||||
resetCounters();
|
||||
m_wSampleVector.resize(1);
|
||||
}
|
||||
void MetisMISODecimators::resetCounters()
|
||||
{
|
||||
std::fill(m_counters, m_counters+MetisMISOSettings::m_maxReceivers, 0);
|
||||
}
|
||||
|
||||
int MetisMISODecimators::decimate2(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex)
|
||||
{
|
||||
if (streamIndex < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleI;
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleQ;
|
||||
|
||||
if (m_counters[streamIndex] >= 8)
|
||||
{
|
||||
SampleVector::iterator it = result.begin();
|
||||
m_decimatorsIQ[streamIndex].decimate2_cen(&it, m_accumulators[streamIndex], 8);
|
||||
m_counters[streamIndex] = 0;
|
||||
return 2; // 2 samples available
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MetisMISODecimators::decimate4(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex)
|
||||
{
|
||||
if (streamIndex < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleI;
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleQ;
|
||||
|
||||
if (m_counters[streamIndex] >= 16)
|
||||
{
|
||||
SampleVector::iterator it = result.begin();
|
||||
m_decimatorsIQ[streamIndex].decimate4_cen(&it, m_accumulators[streamIndex], 16);
|
||||
m_counters[streamIndex] = 0;
|
||||
return 2; // 2 samples available
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MetisMISODecimators::decimate8(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex)
|
||||
{
|
||||
if (streamIndex < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleI;
|
||||
m_accumulators[streamIndex][m_counters[streamIndex]++] = sampleQ;
|
||||
|
||||
if (m_counters[streamIndex] >= 16)
|
||||
{
|
||||
SampleVector::iterator it = result.begin();
|
||||
m_decimatorsIQ[streamIndex].decimate8_cen(&it, m_accumulators[streamIndex], 16);
|
||||
m_counters[streamIndex] = 0;
|
||||
return 1; // 1 sample available
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// Decimators adapters specific to Metis //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISODECIMATORS_H_
|
||||
#define _METISMISO_METISMISODECIMATORS_H_
|
||||
|
||||
#include "dsp/decimators.h"
|
||||
|
||||
#include "metismisosettings.h"
|
||||
|
||||
class MetisMISODecimators
|
||||
{
|
||||
public:
|
||||
MetisMISODecimators();
|
||||
|
||||
int decimate2(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex);
|
||||
int decimate4(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex);
|
||||
int decimate8(qint32 sampleI, qint32 sampleQ, SampleVector& result, unsigned int streamIndex);
|
||||
void resetCounters();
|
||||
|
||||
private:
|
||||
|
||||
qint32 m_accumulators[MetisMISOSettings::m_maxReceivers][256*2];
|
||||
int m_counters[MetisMISOSettings::m_maxReceivers];
|
||||
Decimators<qint32, qint32, SDR_RX_SAMP_SZ, 24, true> m_decimatorsIQ[MetisMISOSettings::m_maxReceivers];
|
||||
Decimators<qint32, qint32, SDR_RX_SAMP_SZ, 24, false> m_decimatorsQI[MetisMISOSettings::m_maxReceivers];
|
||||
SampleVector m_wSampleVector;
|
||||
};
|
||||
|
||||
|
||||
#endif // _METISMISO_METISMISODECIMATORS_H_
|
||||
@@ -0,0 +1,517 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "device/deviceuiset.h"
|
||||
#include "gui/colormapper.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "gui/basicdevicesettingsdialog.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "dsp/dspdevicemimoengine.h"
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "util/db.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
#include "ui_metismisogui.h"
|
||||
#include "metismisogui.h"
|
||||
|
||||
MetisMISOGui::MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::MetisMISOGui),
|
||||
m_deviceUISet(deviceUISet),
|
||||
m_settings(),
|
||||
m_doApplySettings(true),
|
||||
m_forceSettings(true),
|
||||
m_sampleMIMO(nullptr),
|
||||
m_tickCount(0),
|
||||
m_lastEngineState(DeviceAPI::StNotStarted)
|
||||
{
|
||||
qDebug("MetisMISOGui::MetisMISOGui");
|
||||
m_sampleMIMO = m_deviceUISet->m_deviceAPI->getSampleMIMO();
|
||||
m_streamIndex = 0;
|
||||
m_spectrumStreamIndex = 0;
|
||||
m_rxSampleRate = 48000;
|
||||
|
||||
ui->setupUi(this);
|
||||
ui->centerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->centerFrequency->setValueRange(7, 0, 61440000);
|
||||
|
||||
displaySettings();
|
||||
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||
m_statusTimer.start(500);
|
||||
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()), Qt::QueuedConnection);
|
||||
m_sampleMIMO->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
|
||||
CRightClickEnabler *startStopRightClickEnabler = new CRightClickEnabler(ui->startStop);
|
||||
connect(startStopRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
|
||||
}
|
||||
|
||||
MetisMISOGui::~MetisMISOGui()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MetisMISOGui::destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void MetisMISOGui::setName(const QString& name)
|
||||
{
|
||||
setObjectName(name);
|
||||
}
|
||||
|
||||
QString MetisMISOGui::getName() const
|
||||
{
|
||||
return objectName();
|
||||
}
|
||||
|
||||
void MetisMISOGui::resetToDefaults()
|
||||
{
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
qint64 MetisMISOGui::getCenterFrequency() const
|
||||
{
|
||||
if (m_streamIndex == 0) {
|
||||
return m_settings.m_rx1CenterFrequency;
|
||||
} else if (m_streamIndex == 1) {
|
||||
return m_settings.m_rx2CenterFrequency;
|
||||
} else if (m_streamIndex == 2) {
|
||||
return m_settings.m_rx3CenterFrequency;
|
||||
} else if (m_streamIndex == 3) {
|
||||
return m_settings.m_rx4CenterFrequency;
|
||||
} else if (m_streamIndex == 4) {
|
||||
return m_settings.m_txCenterFrequency;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::setCenterFrequency(qint64 centerFrequency)
|
||||
{
|
||||
if (m_streamIndex == 0) {
|
||||
m_settings.m_rx1CenterFrequency = centerFrequency;
|
||||
} else if (m_streamIndex == 1) {
|
||||
m_settings.m_rx2CenterFrequency = centerFrequency;
|
||||
} else if (m_streamIndex == 2) {
|
||||
m_settings.m_rx3CenterFrequency = centerFrequency;
|
||||
} else if (m_streamIndex == 3) {
|
||||
m_settings.m_rx4CenterFrequency = centerFrequency;
|
||||
} else if (m_streamIndex == 4) {
|
||||
m_settings.m_txCenterFrequency = centerFrequency;
|
||||
} else {
|
||||
m_settings.m_txCenterFrequency = 0;
|
||||
}
|
||||
|
||||
displaySettings();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
QByteArray MetisMISOGui::serialize() const
|
||||
{
|
||||
return m_settings.serialize();
|
||||
}
|
||||
|
||||
bool MetisMISOGui::deserialize(const QByteArray& data)
|
||||
{
|
||||
if(m_settings.deserialize(data)) {
|
||||
displaySettings();
|
||||
m_forceSettings = true;
|
||||
sendSettings();
|
||||
return true;
|
||||
} else {
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_startStop_toggled(bool checked)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
MetisMISO::MsgStartStop *message = MetisMISO::MsgStartStop::create(checked);
|
||||
m_sampleMIMO->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_streamIndex_currentIndexChanged(int index)
|
||||
{
|
||||
if (ui->streamLock->isChecked())
|
||||
{
|
||||
m_spectrumStreamIndex = index;
|
||||
|
||||
if (m_spectrumStreamIndex < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
m_deviceUISet->m_spectrum->setDisplayedStream(true, index);
|
||||
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(true, m_spectrumStreamIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceUISet->m_spectrum->setDisplayedStream(false, 0);
|
||||
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, 0);
|
||||
}
|
||||
|
||||
ui->spectrumSource->blockSignals(true);
|
||||
ui->spectrumSource->setCurrentIndex(index);
|
||||
ui->spectrumSource->blockSignals(false);
|
||||
}
|
||||
|
||||
m_streamIndex = index;
|
||||
displayFrequency();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_spectrumSource_currentIndexChanged(int index)
|
||||
{
|
||||
m_spectrumStreamIndex = index;
|
||||
|
||||
if (m_spectrumStreamIndex < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
m_deviceUISet->m_spectrum->setDisplayedStream(true, index);
|
||||
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(true, m_spectrumStreamIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_deviceUISet->m_spectrum->setDisplayedStream(false, 0);
|
||||
m_deviceUISet->m_deviceAPI->setSpectrumSinkInput(false, 0);
|
||||
}
|
||||
|
||||
updateSpectrum();
|
||||
|
||||
if (ui->streamLock->isChecked())
|
||||
{
|
||||
ui->streamIndex->blockSignals(true);
|
||||
ui->streamIndex->setCurrentIndex(index);
|
||||
ui->streamIndex->blockSignals(false);
|
||||
m_streamIndex = index;
|
||||
displayFrequency();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_streamLock_toggled(bool checked)
|
||||
{
|
||||
if (checked && (ui->streamIndex->currentIndex() != ui->spectrumSource->currentIndex())) {
|
||||
ui->spectrumSource->setCurrentIndex(ui->streamIndex->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_centerFrequency_changed(quint64 value)
|
||||
{
|
||||
if (m_streamIndex == 0) {
|
||||
m_settings.m_rx1CenterFrequency = value * 1000;
|
||||
} else if (m_streamIndex == 1) {
|
||||
m_settings.m_rx2CenterFrequency = value * 1000;
|
||||
} else if (m_streamIndex == 2) {
|
||||
m_settings.m_rx3CenterFrequency = value * 1000;
|
||||
} else if (m_streamIndex == 3) {
|
||||
m_settings.m_rx4CenterFrequency = value * 1000;
|
||||
} else if (m_streamIndex == 4) {
|
||||
m_settings.m_txCenterFrequency = value * 1000;
|
||||
} else {
|
||||
m_settings.m_txCenterFrequency = 0;
|
||||
}
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_samplerateIndex_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_sampleRateIndex = index < 0 ? 0 : index > 3 ? 3 : index;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_log2Decim_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_log2Decim = index < 0 ? 0 : index > 3 ? 3 : index;
|
||||
displaySampleRate();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_dcBlock_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_dcBlock = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_iqCorrection_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_iqCorrection = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_preamp_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_preamp = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_random_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_random = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_dither_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_dither = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_duplex_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_duplex = checked;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::on_nbRxIndex_currentIndexChanged(int index)
|
||||
{
|
||||
m_settings.m_nbReceivers = index + 1;
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void MetisMISOGui::displaySettings()
|
||||
{
|
||||
blockApplySettings(true);
|
||||
|
||||
ui->streamIndex->setCurrentIndex(m_streamIndex);
|
||||
ui->spectrumSource->setCurrentIndex(m_spectrumStreamIndex);
|
||||
ui->nbRxIndex->setCurrentIndex(m_settings.m_nbReceivers - 1);
|
||||
ui->samplerateIndex->setCurrentIndex(m_settings.m_sampleRateIndex);
|
||||
ui->dcBlock->setChecked(m_settings.m_dcBlock);
|
||||
ui->iqCorrection->setChecked(m_settings.m_iqCorrection);
|
||||
ui->preamp->setChecked(m_settings.m_preamp);
|
||||
ui->random->setChecked(m_settings.m_random);
|
||||
ui->dither->setChecked(m_settings.m_dither);
|
||||
ui->duplex->setChecked(m_settings.m_duplex);
|
||||
displayFrequency();
|
||||
displaySampleRate();
|
||||
updateSpectrum();
|
||||
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
void MetisMISOGui::sendSettings()
|
||||
{
|
||||
if(!m_updateTimer.isActive()) {
|
||||
m_updateTimer.start(100);
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::updateHardware()
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
MetisMISO::MsgConfigureMetisMISO* message = MetisMISO::MsgConfigureMetisMISO::create(m_settings, m_forceSettings);
|
||||
m_sampleMIMO->getInputMessageQueue()->push(message);
|
||||
m_forceSettings = false;
|
||||
m_updateTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::updateStatus()
|
||||
{
|
||||
int state = m_deviceUISet->m_deviceAPI->state();
|
||||
|
||||
if(m_lastEngineState != state)
|
||||
{
|
||||
switch(state)
|
||||
{
|
||||
case DeviceAPI::StNotStarted:
|
||||
ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
break;
|
||||
case DeviceAPI::StIdle:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
|
||||
break;
|
||||
case DeviceAPI::StRunning:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
||||
break;
|
||||
case DeviceAPI::StError:
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
QMessageBox::information(this, tr("Message"), m_deviceUISet->m_deviceAPI->errorMessage());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_lastEngineState = state;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetisMISOGui::handleMessage(const Message& message)
|
||||
{
|
||||
if (MetisMISO::MsgConfigureMetisMISO::match(message))
|
||||
{
|
||||
qDebug("MetisMISOGui::handleMessage: MsgConfigureMetisMISO");
|
||||
const MetisMISO::MsgConfigureMetisMISO& cfg = (MetisMISO::MsgConfigureMetisMISO&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
displaySettings();
|
||||
return true;
|
||||
}
|
||||
else if (MetisMISO::MsgStartStop::match(message))
|
||||
{
|
||||
qDebug("MetisMISOGui::handleMessage: MsgStartStop");
|
||||
MetisMISO::MsgStartStop& notif = (MetisMISO::MsgStartStop&) message;
|
||||
blockApplySettings(true);
|
||||
ui->startStop->setChecked(notif.getStartStop());
|
||||
blockApplySettings(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
if (DSPMIMOSignalNotification::match(*message))
|
||||
{
|
||||
DSPMIMOSignalNotification* notif = (DSPMIMOSignalNotification*) message;
|
||||
int istream = notif->getIndex();
|
||||
bool sourceOrSink = notif->getSourceOrSink();
|
||||
qint64 frequency = notif->getCenterFrequency();
|
||||
|
||||
if (sourceOrSink)
|
||||
{
|
||||
m_rxSampleRate = notif->getSampleRate();
|
||||
|
||||
if (istream == 0) {
|
||||
m_settings.m_rx1CenterFrequency = frequency;
|
||||
} else if (istream == 1) {
|
||||
m_settings.m_rx2CenterFrequency = frequency;
|
||||
} else if (istream == 2) {
|
||||
m_settings.m_rx3CenterFrequency = frequency;
|
||||
} else if (istream == 3) {
|
||||
m_settings.m_rx4CenterFrequency = frequency;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settings.m_txCenterFrequency = frequency;
|
||||
}
|
||||
|
||||
qDebug("MetisMISOGui::handleInputMessages: DSPMIMOSignalNotification: %s stream: %d m_rxSampleRate:%d, CenterFrequency:%llu",
|
||||
sourceOrSink ? "source" : "sink",
|
||||
istream,
|
||||
m_rxSampleRate,
|
||||
frequency);
|
||||
|
||||
displayFrequency();
|
||||
updateSpectrum();
|
||||
|
||||
delete message;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (handleMessage(*message))
|
||||
{
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOGui::displayFrequency()
|
||||
{
|
||||
qint64 centerFrequency;
|
||||
|
||||
if (m_streamIndex == 0) {
|
||||
centerFrequency = m_settings.m_rx1CenterFrequency;
|
||||
} else if (m_streamIndex == 1) {
|
||||
centerFrequency = m_settings.m_rx2CenterFrequency;
|
||||
} else if (m_streamIndex == 2) {
|
||||
centerFrequency = m_settings.m_rx3CenterFrequency;
|
||||
} else if (m_streamIndex == 3) {
|
||||
centerFrequency = m_settings.m_rx4CenterFrequency;
|
||||
} else if (m_streamIndex == 4) {
|
||||
centerFrequency = m_settings.m_txCenterFrequency;
|
||||
} else {
|
||||
centerFrequency = 0;
|
||||
}
|
||||
|
||||
ui->centerFrequency->setValue(centerFrequency / 1000);
|
||||
}
|
||||
|
||||
void MetisMISOGui::displaySampleRate()
|
||||
{
|
||||
int deviceSampleRate = 48000 * (1<<m_settings.m_sampleRateIndex);
|
||||
int sampleRate = deviceSampleRate / (1<<m_settings.m_log2Decim);
|
||||
ui->deviceRateText->setText(tr("%1k").arg((float) sampleRate / 1000));
|
||||
}
|
||||
|
||||
void MetisMISOGui::updateSpectrum()
|
||||
{
|
||||
qint64 centerFrequency;
|
||||
|
||||
if (m_spectrumStreamIndex == 0) {
|
||||
centerFrequency = m_settings.m_rx1CenterFrequency;
|
||||
} else if (m_spectrumStreamIndex == 1) {
|
||||
centerFrequency = m_settings.m_rx2CenterFrequency;
|
||||
} else if (m_spectrumStreamIndex == 2) {
|
||||
centerFrequency = m_settings.m_rx3CenterFrequency;
|
||||
} else if (m_spectrumStreamIndex == 3) {
|
||||
centerFrequency = m_settings.m_rx4CenterFrequency;
|
||||
} else if (m_spectrumStreamIndex == 4) {
|
||||
centerFrequency = m_settings.m_txCenterFrequency;
|
||||
} else {
|
||||
centerFrequency = 0;
|
||||
}
|
||||
|
||||
m_deviceUISet->getSpectrum()->setSampleRate(m_rxSampleRate);
|
||||
m_deviceUISet->getSpectrum()->setCenterFrequency(centerFrequency);
|
||||
}
|
||||
|
||||
void MetisMISOGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
{
|
||||
BasicDeviceSettingsDialog dialog(this);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
|
||||
|
||||
dialog.move(p);
|
||||
dialog.exec();
|
||||
|
||||
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
|
||||
|
||||
sendSettings();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISOGUI_H_
|
||||
#define _METISMISO_METISMISOGUI_H_
|
||||
|
||||
#include <plugin/plugininstancegui.h>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "util/messagequeue.h"
|
||||
|
||||
#include "metismisosettings.h"
|
||||
#include "metismiso.h"
|
||||
|
||||
class DeviceUISet;
|
||||
|
||||
namespace Ui {
|
||||
class MetisMISOGui;
|
||||
}
|
||||
|
||||
class MetisMISOGui : public QWidget, public PluginInstanceGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MetisMISOGui(DeviceUISet *deviceUISet, QWidget* parent = 0);
|
||||
virtual ~MetisMISOGui();
|
||||
virtual void destroy();
|
||||
|
||||
void setName(const QString& name);
|
||||
QString getName() const;
|
||||
|
||||
void resetToDefaults();
|
||||
virtual qint64 getCenterFrequency() const;
|
||||
virtual void setCenterFrequency(qint64 centerFrequency);
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
virtual bool handleMessage(const Message& message);
|
||||
|
||||
private:
|
||||
Ui::MetisMISOGui* ui;
|
||||
|
||||
DeviceUISet* m_deviceUISet;
|
||||
MetisMISOSettings m_settings;
|
||||
int m_streamIndex; //!< Current stream index being dealt with
|
||||
int m_spectrumStreamIndex; //!< Index of the stream displayed on main spectrum
|
||||
int m_rxSampleRate;
|
||||
QTimer m_updateTimer;
|
||||
QTimer m_statusTimer;
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
DeviceSampleMIMO* m_sampleMIMO;
|
||||
std::size_t m_tickCount;
|
||||
std::vector<int> m_deviceSampleRates;
|
||||
std::vector<quint64> m_deviceCenterFrequencies; //!< Center frequency in device
|
||||
int m_lastEngineState;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
void blockApplySettings(bool block) { m_doApplySettings = !block; }
|
||||
void displaySettings();
|
||||
void displayFrequency();
|
||||
void displaySampleRate();
|
||||
void updateSpectrum();
|
||||
void sendSettings();
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
void on_streamIndex_currentIndexChanged(int index);
|
||||
void on_spectrumSource_currentIndexChanged(int index);
|
||||
void on_streamLock_toggled(bool checked);
|
||||
void on_startStop_toggled(bool checked);
|
||||
void on_centerFrequency_changed(quint64 value);
|
||||
void on_samplerateIndex_currentIndexChanged(int index);
|
||||
void on_log2Decim_currentIndexChanged(int index);
|
||||
void on_dcBlock_toggled(bool checked);
|
||||
void on_iqCorrection_toggled(bool checked);
|
||||
void on_preamp_toggled(bool checked);
|
||||
void on_random_toggled(bool checked);
|
||||
void on_dither_toggled(bool checked);
|
||||
void on_duplex_toggled(bool checked);
|
||||
void on_nbRxIndex_currentIndexChanged(int index);
|
||||
void openDeviceSettingsDialog(const QPoint& p);
|
||||
void updateStatus();
|
||||
void updateHardware();
|
||||
};
|
||||
|
||||
#endif // _METISMISO_METISMISOGUI_H_
|
||||
@@ -0,0 +1,606 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MetisMISOGui</class>
|
||||
<widget class="QWidget" name="MetisMISOGui">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>200</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>360</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
<weight>50</weight>
|
||||
<italic>false</italic>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Metis MISO</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="streamLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Stream</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="streamIndex">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select stream to which settings apply (frequency only)</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tx</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="spectrumSourceLabel">
|
||||
<property name="text">
|
||||
<string>Spectrum</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="spectrumSource">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Select stream for main spectrum source</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx0</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Rx3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Tx</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="streamLock">
|
||||
<property name="toolTip">
|
||||
<string>Lock spectrum display to stream selection</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/unlocked.png</normaloff>
|
||||
<normalon>:/locked.png</normalon>:/unlocked.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_freq">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="deviceUILayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="deviceButtonsLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="startStop">
|
||||
<property name="toolTip">
|
||||
<string>start/stop acquisition</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/play.png</normaloff>
|
||||
<normalon>:/stop.png</normalon>:/play.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="deviceRateLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="deviceRateText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>58</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>I/Q sample rate kS/s</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0000.00k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ValueDial" name="centerFrequency" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
<pointsize>20</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Tuner center frequency in kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="freqUnits">
|
||||
<property name="text">
|
||||
<string> kHz</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="autoCorrectionsLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="samplerateLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SR</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="samplerateIndex">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Modulation</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>48k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>96k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>192k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>384k</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="decimationLabel">
|
||||
<property name="text">
|
||||
<string>Dec</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="log2Decim">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>8</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="autoCorrLabel">
|
||||
<property name="text">
|
||||
<string>Corr</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="dcBlock">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DC</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="iqCorrection">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>45</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IQ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="switchesLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="preamp">
|
||||
<property name="toolTip">
|
||||
<string>Toggle preamplifier</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PRE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="random">
|
||||
<property name="toolTip">
|
||||
<string>Toggle LT2208 Random</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>RAN</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="dither">
|
||||
<property name="toolTip">
|
||||
<string>Toggle LT2208 Dither</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DITH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="duplex">
|
||||
<property name="toolTip">
|
||||
<string>Toggle duplex</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>DUP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nbRxLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>#Rx</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="nbRxIndex">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Number of active receivers</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>1</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>2</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>3</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>4</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>5</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>6</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>7</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ValueDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ButtonSwitch</class>
|
||||
<extends>QToolButton</extends>
|
||||
<header>gui/buttonswitch.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,139 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "metis/devicemetis.h"
|
||||
#include "util/simpleserializer.h"
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
#include "metismiso.h"
|
||||
#else
|
||||
#include "metismisogui.h"
|
||||
#endif
|
||||
#include "metismisoplugin.h"
|
||||
#include "metismisowebapiadapter.h"
|
||||
|
||||
const PluginDescriptor MetisMISOPlugin::m_pluginDescriptor = {
|
||||
QString("MetisMISO"),
|
||||
QString("Metis MISO"),
|
||||
QString("5.10.0"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
QString("https://github.com/f4exb/sdrangel")
|
||||
};
|
||||
|
||||
const QString MetisMISOPlugin::m_hardwareID = "MetisMISO";
|
||||
const QString MetisMISOPlugin::m_deviceTypeID = METISMISO_DEVICE_TYPE_ID;
|
||||
|
||||
MetisMISOPlugin::MetisMISOPlugin(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
const PluginDescriptor& MetisMISOPlugin::getPluginDescriptor() const
|
||||
{
|
||||
return m_pluginDescriptor;
|
||||
}
|
||||
|
||||
void MetisMISOPlugin::initPlugin(PluginAPI* pluginAPI)
|
||||
{
|
||||
pluginAPI->registerSampleMIMO(m_deviceTypeID, this);
|
||||
}
|
||||
|
||||
void MetisMISOPlugin::enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices)
|
||||
{
|
||||
if (listedHwIds.contains(m_hardwareID)) { // check if it was done
|
||||
return;
|
||||
}
|
||||
|
||||
DeviceMetis::instance().enumOriginDevices(m_hardwareID, originDevices);
|
||||
listedHwIds.append(m_hardwareID);
|
||||
}
|
||||
|
||||
PluginInterface::SamplingDevices MetisMISOPlugin::enumSampleMIMO(const OriginDevices& originDevices)
|
||||
{
|
||||
SamplingDevices result;
|
||||
|
||||
for (OriginDevices::const_iterator it = originDevices.begin(); it != originDevices.end(); ++it)
|
||||
{
|
||||
if (it->hardwareId == m_hardwareID)
|
||||
{
|
||||
result.append(SamplingDevice(
|
||||
it->displayableName,
|
||||
it->hardwareId,
|
||||
m_deviceTypeID,
|
||||
it->serial,
|
||||
it->sequence,
|
||||
PluginInterface::SamplingDevice::PhysicalDevice,
|
||||
PluginInterface::SamplingDevice::StreamMIMO,
|
||||
1, // MIMO is always considered as a single device
|
||||
0)
|
||||
);
|
||||
qDebug("MetisMISOPlugin::enumSampleMIMO: enumerated Metis device #%d", it->sequence);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SERVER_MODE
|
||||
PluginInstanceGUI* MetisMISOPlugin::createSampleMIMOPluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
(void) sourceId;
|
||||
(void) widget;
|
||||
(void) deviceUISet;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
PluginInstanceGUI* MetisMISOPlugin::createSampleMIMOPluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet)
|
||||
{
|
||||
if (sourceId == m_deviceTypeID) {
|
||||
MetisMISOGui* gui = new MetisMISOGui(deviceUISet);
|
||||
*widget = gui;
|
||||
return gui;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DeviceSampleMIMO *MetisMISOPlugin::createSampleMIMOPluginInstance(const QString& mimoId, DeviceAPI *deviceAPI)
|
||||
{
|
||||
if (mimoId == m_deviceTypeID)
|
||||
{
|
||||
MetisMISO* input = new MetisMISO(deviceAPI);
|
||||
return input;
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceWebAPIAdapter *MetisMISOPlugin::createDeviceWebAPIAdapter() const
|
||||
{
|
||||
return new MetisMISOWebAPIAdapter();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMIMOPLUGIN_H
|
||||
#define _METISMISO_METISMIMOPLUGIN_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "plugin/plugininterface.h"
|
||||
|
||||
class PluginAPI;
|
||||
|
||||
#define METISMISO_DEVICE_TYPE_ID "sdrangel.samplemimo.metismiso"
|
||||
|
||||
class MetisMISOPlugin : public QObject, public PluginInterface {
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(PluginInterface)
|
||||
Q_PLUGIN_METADATA(IID METISMISO_DEVICE_TYPE_ID)
|
||||
|
||||
public:
|
||||
explicit MetisMISOPlugin(QObject* parent = NULL);
|
||||
|
||||
const PluginDescriptor& getPluginDescriptor() const;
|
||||
void initPlugin(PluginAPI* pluginAPI);
|
||||
|
||||
virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices);
|
||||
virtual SamplingDevices enumSampleMIMO(const OriginDevices& originDevices);
|
||||
virtual PluginInstanceGUI* createSampleMIMOPluginInstanceGUI(
|
||||
const QString& sourceId,
|
||||
QWidget **widget,
|
||||
DeviceUISet *deviceUISet);
|
||||
virtual DeviceSampleMIMO* createSampleMIMOPluginInstance(const QString& sourceId, DeviceAPI *deviceAPI);
|
||||
virtual DeviceWebAPIAdapter* createDeviceWebAPIAdapter() const;
|
||||
|
||||
static const QString m_hardwareID;
|
||||
static const QString m_deviceTypeID;
|
||||
|
||||
private:
|
||||
static const PluginDescriptor m_pluginDescriptor;
|
||||
};
|
||||
|
||||
#endif // _METISMISO_METISMIMOPLUGIN_H
|
||||
@@ -0,0 +1,155 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QtGlobal>
|
||||
#include "util/simpleserializer.h"
|
||||
#include "metismisosettings.h"
|
||||
|
||||
MetisMISOSettings::MetisMISOSettings()
|
||||
{
|
||||
resetToDefaults();
|
||||
}
|
||||
|
||||
MetisMISOSettings::MetisMISOSettings(const MetisMISOSettings& other)
|
||||
{
|
||||
m_nbReceivers = other.m_nbReceivers;
|
||||
m_rx1CenterFrequency = other.m_rx1CenterFrequency;
|
||||
m_rx2CenterFrequency = other.m_rx2CenterFrequency;
|
||||
m_rx3CenterFrequency = other.m_rx3CenterFrequency;
|
||||
m_rx4CenterFrequency = other.m_rx4CenterFrequency;
|
||||
m_txCenterFrequency = other.m_txCenterFrequency;
|
||||
m_sampleRateIndex = other.m_sampleRateIndex;
|
||||
m_log2Decim = other.m_log2Decim;
|
||||
m_preamp = other.m_preamp;
|
||||
m_random = other.m_random;
|
||||
m_dither = other.m_dither;
|
||||
m_duplex = other.m_duplex;
|
||||
m_dcBlock = other.m_dcBlock;
|
||||
m_iqCorrection = other.m_iqCorrection;
|
||||
m_useReverseAPI = other.m_useReverseAPI;
|
||||
m_reverseAPIAddress = other.m_reverseAPIAddress;
|
||||
m_reverseAPIPort = other.m_reverseAPIPort;
|
||||
m_reverseAPIDeviceIndex = other.m_reverseAPIDeviceIndex;
|
||||
}
|
||||
|
||||
void MetisMISOSettings::resetToDefaults()
|
||||
{
|
||||
m_nbReceivers = 1;
|
||||
m_rx1CenterFrequency = 7074000;
|
||||
m_rx2CenterFrequency = 7074000;
|
||||
m_rx3CenterFrequency = 7074000;
|
||||
m_rx4CenterFrequency = 7074000;
|
||||
m_txCenterFrequency = 7074000;
|
||||
m_sampleRateIndex = 0; // 48000 kS/s
|
||||
m_log2Decim = 0;
|
||||
m_preamp = false;
|
||||
m_random = false;
|
||||
m_dither = false;
|
||||
m_duplex = false;
|
||||
m_dcBlock = false;
|
||||
m_iqCorrection = false;
|
||||
m_useReverseAPI = false;
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
}
|
||||
|
||||
QByteArray MetisMISOSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeU32(1, m_nbReceivers);
|
||||
s.writeU64(2, m_rx1CenterFrequency);
|
||||
s.writeU64(3, m_rx2CenterFrequency);
|
||||
s.writeU64(4, m_rx3CenterFrequency);
|
||||
s.writeU64(5, m_rx4CenterFrequency);
|
||||
s.writeU64(6, m_txCenterFrequency);
|
||||
s.writeU32(7, m_sampleRateIndex);
|
||||
s.writeU32(8, m_log2Decim);
|
||||
s.writeBool(9, m_preamp);
|
||||
s.writeBool(10, m_random);
|
||||
s.writeBool(11, m_dither);
|
||||
s.writeBool(12, m_duplex);
|
||||
s.writeBool(13, m_dcBlock);
|
||||
s.writeBool(14, m_iqCorrection);
|
||||
s.writeBool(15, m_useReverseAPI);
|
||||
s.writeString(16, m_reverseAPIAddress);
|
||||
s.writeU32(17, m_reverseAPIPort);
|
||||
s.writeU32(18, m_reverseAPIDeviceIndex);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool MetisMISOSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid())
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
int intval;
|
||||
uint32_t utmp;
|
||||
|
||||
d.readU32(1, &m_nbReceivers, 1);
|
||||
d.readU64(2, &m_rx1CenterFrequency, 7074000);
|
||||
d.readU64(3, &m_rx2CenterFrequency, 7074000);
|
||||
d.readU64(4, &m_rx3CenterFrequency, 7074000);
|
||||
d.readU64(5, &m_rx4CenterFrequency, 7074000);
|
||||
d.readU64(6, &m_txCenterFrequency, 7074000);
|
||||
d.readU32(7, &m_sampleRateIndex, 0);
|
||||
d.readU32(8, &m_log2Decim, 0);
|
||||
d.readBool(8, &m_preamp, false);
|
||||
d.readBool(9, &m_random, false);
|
||||
d.readBool(10, &m_dither, false);
|
||||
d.readBool(11, &m_duplex, false);
|
||||
d.readBool(12, &m_dcBlock, false);
|
||||
d.readBool(13, &m_iqCorrection, false);
|
||||
d.readBool(14, &m_useReverseAPI, false);
|
||||
d.readString(15, &m_reverseAPIAddress, "127.0.0.1");
|
||||
d.readU32(16, &utmp, 0);
|
||||
|
||||
if ((utmp > 1023) && (utmp < 65535)) {
|
||||
m_reverseAPIPort = utmp;
|
||||
} else {
|
||||
m_reverseAPIPort = 8888;
|
||||
}
|
||||
|
||||
d.readU32(17, &utmp, 0);
|
||||
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
resetToDefaults();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int MetisMISOSettings::getSampleRateFromIndex(unsigned int index)
|
||||
{
|
||||
if (index < 3) {
|
||||
return (1<<index) * 48000;
|
||||
} else {
|
||||
return 48000;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISOSETTINGS_H_
|
||||
#define _METISMISO_METISMISOSETTINGS_H_
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct MetisMISOSettings {
|
||||
unsigned int m_nbReceivers;
|
||||
quint64 m_rx1CenterFrequency;
|
||||
quint64 m_rx2CenterFrequency;
|
||||
quint64 m_rx3CenterFrequency;
|
||||
quint64 m_rx4CenterFrequency;
|
||||
quint64 m_txCenterFrequency;
|
||||
unsigned int m_sampleRateIndex;
|
||||
unsigned int m_log2Decim;
|
||||
bool m_preamp;
|
||||
bool m_random;
|
||||
bool m_dither;
|
||||
bool m_duplex;
|
||||
bool m_dcBlock;
|
||||
bool m_iqCorrection;
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
uint16_t m_reverseAPIPort;
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
|
||||
static const int m_maxReceivers = 4;
|
||||
|
||||
MetisMISOSettings();
|
||||
MetisMISOSettings(const MetisMISOSettings& other);
|
||||
void resetToDefaults();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
|
||||
static int getSampleRateFromIndex(unsigned int index);
|
||||
};
|
||||
|
||||
|
||||
#endif /* _METISMISO_METISMISOSETTINGS_H_ */
|
||||
@@ -0,0 +1,557 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "dsp/samplemififo.h"
|
||||
#include "metismisoudphandler.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(MetisMISOUDPHandler::MsgStartStop, Message)
|
||||
|
||||
MetisMISOUDPHandler::MetisMISOUDPHandler(SampleMIFifo *sampleFifo, DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_socket(nullptr),
|
||||
m_metisAddress(QHostAddress::LocalHost),
|
||||
m_metisPort(9090),
|
||||
m_running(false),
|
||||
m_dataConnected(false),
|
||||
m_sampleFifo(sampleFifo),
|
||||
m_sampleCount(0),
|
||||
m_messageQueueToGUI(nullptr),
|
||||
m_mutex(QMutex::Recursive),
|
||||
m_commandBase(0),
|
||||
m_receiveSequence(0),
|
||||
m_receiveSequenceError(0)
|
||||
{
|
||||
setNbReceivers(m_settings.m_nbReceivers);
|
||||
|
||||
for (unsigned int i = 0; i < MetisMISOSettings::m_maxReceivers; i++) {
|
||||
m_convertBuffer[i].resize(1024, Sample{0,0});
|
||||
}
|
||||
|
||||
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleMessages()));
|
||||
}
|
||||
|
||||
MetisMISOUDPHandler::~MetisMISOUDPHandler()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::start()
|
||||
{
|
||||
qDebug("MetisMISOUDPHandler::start");
|
||||
|
||||
m_rxFrame = 0;
|
||||
m_txFrame = 0;
|
||||
m_sendSequence = -1;
|
||||
m_offset = 8;
|
||||
m_receiveSequence = 0;
|
||||
|
||||
if (m_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_dataConnected)
|
||||
{
|
||||
if (m_socket.bind(QHostAddress::AnyIPv4, 10001, QUdpSocket::ShareAddress))
|
||||
{
|
||||
qDebug("MetisMISOUDPHandler::start: bind host socket OK");
|
||||
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
|
||||
m_dataConnected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning("MetisMISOUDPHandler::start: cannot bind host socket");
|
||||
m_dataConnected = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Start Metis
|
||||
unsigned char buffer[64];
|
||||
|
||||
buffer[0] = (unsigned char) 0xEF;
|
||||
buffer[1] = (unsigned char) 0XFE;
|
||||
buffer[2] = (unsigned char) 0x04;
|
||||
buffer[3] = (unsigned char) 0x01;
|
||||
std::fill(&buffer[4], &buffer[64], 0);
|
||||
|
||||
if (m_socket.writeDatagram((const char*) buffer, sizeof(buffer), m_metisAddress, m_metisPort) < 0)
|
||||
{
|
||||
qDebug() << "MetisMISOUDPHandler::start: writeDatagram start command failed " << m_socket.errorString();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "MetisMISOUDPHandler::start: writeDatagram start command" << m_metisAddress.toString() << ":" << m_metisPort;
|
||||
}
|
||||
|
||||
m_socket.flush();
|
||||
|
||||
// send 2 frames with control data
|
||||
|
||||
sendNullBuffer();
|
||||
sendNullBuffer(); // TODO: on the next send frequencies
|
||||
|
||||
m_running = true;
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::stop()
|
||||
{
|
||||
qDebug("MetisMISOUDPHandler::stop");
|
||||
|
||||
if (!m_running) {
|
||||
return;
|
||||
}
|
||||
|
||||
// stop Metis
|
||||
unsigned char buffer[64];
|
||||
|
||||
buffer[0] = (unsigned char) 0xEF;
|
||||
buffer[1] = (unsigned char) 0XFE;
|
||||
buffer[2] = (unsigned char) 0x04;
|
||||
buffer[3] = (unsigned char) 0x00;
|
||||
std::fill(&buffer[4], &buffer[64], 0);
|
||||
|
||||
if (m_dataConnected)
|
||||
{
|
||||
disconnect(&m_socket, SIGNAL(readyRead()), this, SLOT(dataReadyRead()));
|
||||
m_dataConnected = false;
|
||||
}
|
||||
|
||||
|
||||
if (m_socket.writeDatagram((const char*)buffer, sizeof(buffer), m_metisAddress,m_metisPort) < 0)
|
||||
{
|
||||
qDebug() << "MetisMISOUDPHandler::stop: writeDatagram failed " << m_socket.errorString();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug()<<"MetisMISOUDPHandler::stop: writeDatagram stop command" << m_metisAddress.toString() << ":" << m_metisPort;
|
||||
}
|
||||
|
||||
m_socket.flush();
|
||||
m_socket.close();
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::setNbReceivers(unsigned int nbReceivers)
|
||||
{
|
||||
m_nbReceivers = nbReceivers;
|
||||
|
||||
switch(m_nbReceivers)
|
||||
{
|
||||
case 1: m_bMax = 512-0; break;
|
||||
case 2: m_bMax = 512-0; break;
|
||||
case 3: m_bMax = 512-4; break;
|
||||
case 4: m_bMax = 512-10; break;
|
||||
case 5: m_bMax = 512-24; break;
|
||||
case 6: m_bMax = 512-10; break;
|
||||
case 7: m_bMax = 512-20; break;
|
||||
case 8: m_bMax = 512-4; break;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < MetisMISOSettings::m_maxReceivers; i++) {
|
||||
m_convertBuffer[i].resize(1024, Sample{0,0});
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::applySettings(const MetisMISOSettings& settings)
|
||||
{
|
||||
if (m_settings.m_nbReceivers != settings.m_nbReceivers)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
int nbReceivers = settings.m_nbReceivers < 1 ?
|
||||
1 : settings.m_nbReceivers > MetisMISOSettings::m_maxReceivers ?
|
||||
MetisMISOSettings::m_maxReceivers : settings.m_nbReceivers;
|
||||
setNbReceivers(nbReceivers);
|
||||
}
|
||||
|
||||
if (m_settings.m_log2Decim != settings.m_log2Decim)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_decimators.resetCounters();
|
||||
}
|
||||
|
||||
m_settings = settings;
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::handleMessages()
|
||||
{
|
||||
Message* message;
|
||||
|
||||
while ((message = m_inputMessageQueue.pop()) != 0)
|
||||
{
|
||||
if (handleMessage(*message)) {
|
||||
delete message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MetisMISOUDPHandler::handleMessage(const Message& message)
|
||||
{
|
||||
if (MsgStartStop::match(message))
|
||||
{
|
||||
const MsgStartStop& cmd = (const MsgStartStop&) message;
|
||||
|
||||
if (cmd.getStartStop()) {
|
||||
start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::sendMetisBuffer(int ep, unsigned char* buffer)
|
||||
{
|
||||
if (m_offset == 8) // header and first HPSDR frame
|
||||
{
|
||||
m_sendSequence++;
|
||||
m_outputBuffer[0] = 0xEF;
|
||||
m_outputBuffer[1] = 0xFE;
|
||||
m_outputBuffer[2] = 0x01;
|
||||
m_outputBuffer[3] = ep;
|
||||
m_outputBuffer[4] = (m_sendSequence>>24) & 0xFF;
|
||||
m_outputBuffer[5] = (m_sendSequence>>16) & 0xFF;
|
||||
m_outputBuffer[6] = (m_sendSequence>>8) & 0xFF;
|
||||
m_outputBuffer[7] = (m_sendSequence) & 0xFF;
|
||||
std::copy(buffer, buffer+512, &m_outputBuffer[m_offset]); // copy the buffer over
|
||||
m_offset = 520;
|
||||
}
|
||||
else // second HPSDR frame and send
|
||||
{
|
||||
std::copy(buffer, buffer+512, &m_outputBuffer[m_offset]); // copy the buffer over
|
||||
m_offset = 8;
|
||||
|
||||
// send the buffer
|
||||
if (m_socket.writeDatagram((const char*) m_outputBuffer, sizeof(m_outputBuffer), m_metisAddress, m_metisPort) < 0)
|
||||
{
|
||||
qDebug() << "MetisMISOUDPHandler::sendMetisBuffer: writeDatagram failed " << m_socket.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
m_socket.flush();
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::dataReadyRead()
|
||||
{
|
||||
QHostAddress metisAddress;
|
||||
quint16 metisPort;
|
||||
unsigned char receiveBuffer[1032];
|
||||
qint64 length;
|
||||
|
||||
long sequence;
|
||||
|
||||
if ((length = m_socket.readDatagram((char*) &receiveBuffer, (qint64) sizeof(receiveBuffer), &metisAddress, &metisPort)) != 1032)
|
||||
{
|
||||
qDebug() << "MetisMISOUDPHandler::dataReadyRead: readDatagram failed " << m_socket.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
sendNullBuffer(); // TODO: for now a null payload is sent
|
||||
sendNullBuffer();
|
||||
|
||||
if (receiveBuffer[0] == 0xEF && receiveBuffer[1] == 0xFE)
|
||||
{
|
||||
// valid frame
|
||||
switch(receiveBuffer[2])
|
||||
{
|
||||
case 1: // IQ data
|
||||
switch (receiveBuffer[3])
|
||||
{
|
||||
case 4: // EP4 data
|
||||
break;
|
||||
case 6: // EP6 data
|
||||
sequence = ((receiveBuffer[4] & 0xFF)<<24) + ((receiveBuffer[5] & 0xFF)<<16) + ((receiveBuffer[6] & 0xFF)<<8) +(receiveBuffer[7] & 0xFF);
|
||||
if (m_receiveSequence == 0)
|
||||
{
|
||||
m_receiveSequence = sequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_receiveSequence++;
|
||||
|
||||
if (m_receiveSequence != sequence)
|
||||
{
|
||||
//qDebug()<<"Sequence error: expected "<<receive_sequence<<" got "<<sequence;
|
||||
m_receiveSequence = sequence;
|
||||
m_receiveSequenceError++;
|
||||
}
|
||||
}
|
||||
processIQBuffer(&receiveBuffer[8]);
|
||||
processIQBuffer(&receiveBuffer[520]);
|
||||
break;
|
||||
default:
|
||||
qDebug() << "MetisMISOUDPHandler::dataReadyRead: invalid EP" << receiveBuffer[3];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qDebug() << "MetisMISOUDPHandler::dataReadyRead: expected data packet (1) got " << receiveBuffer[2];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
qDebug()<<"expected EFFE";
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::sendNullBuffer()
|
||||
{
|
||||
unsigned char buffer[512];
|
||||
|
||||
if (m_txFrame % (m_settings.m_sampleRateIndex+1) == 0)
|
||||
{
|
||||
int commandIndex = 2*m_commandBase; // command rotation
|
||||
|
||||
buffer[0] = (unsigned char) 0x7F;
|
||||
buffer[1] = (unsigned char) 0x7F;
|
||||
buffer[2] = (unsigned char) 0x7F;
|
||||
buffer[3] = commandIndex; // C0
|
||||
|
||||
int commandValue = getCommandValue(commandIndex);
|
||||
|
||||
buffer[4]= commandValue>>24; // C1
|
||||
buffer[5]= (commandValue>>16) & 0xFF; // C2
|
||||
buffer[6]= (commandValue>>8) & 0xFF; // C3
|
||||
buffer[7]= commandValue & 0xFF; // C4
|
||||
|
||||
if (m_commandBase < 15) { // base count 0 to 16
|
||||
m_commandBase++;
|
||||
} else {
|
||||
m_commandBase = 0;
|
||||
}
|
||||
|
||||
std::fill(&buffer[8], &buffer[512], 0);
|
||||
sendMetisBuffer(2, buffer);
|
||||
}
|
||||
|
||||
m_txFrame++;
|
||||
}
|
||||
|
||||
int MetisMISOUDPHandler::getCommandValue(int commandIndex)
|
||||
{
|
||||
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
|
||||
|
||||
if (commandIndex == 0)
|
||||
{
|
||||
c1 = m_settings.m_sampleRateIndex & 0x03;
|
||||
c3 = m_settings.m_preamp ? 0x04 : 0;
|
||||
c3 += m_settings.m_dither ? 0x08 : 0;
|
||||
c3 += m_settings.m_random ? 0x10 : 0;
|
||||
c4 = m_settings.m_duplex ? 0x04 : 0;
|
||||
c4 += (((m_nbReceivers-1) & 0x07)<<3);
|
||||
return (c1<<24) + (c3<<8) + c4;
|
||||
}
|
||||
else if (commandIndex == 2)
|
||||
{
|
||||
return m_settings.m_txCenterFrequency;
|
||||
}
|
||||
else if (commandIndex == 4)
|
||||
{
|
||||
return m_settings.m_rx1CenterFrequency;
|
||||
}
|
||||
else if (commandIndex == 6)
|
||||
{
|
||||
return m_settings.m_rx2CenterFrequency;
|
||||
}
|
||||
else if (commandIndex == 8)
|
||||
{
|
||||
return m_settings.m_rx3CenterFrequency;
|
||||
}
|
||||
else if (commandIndex == 10)
|
||||
{
|
||||
return m_settings.m_rx4CenterFrequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MetisMISOUDPHandler::processIQBuffer(unsigned char* buffer)
|
||||
{
|
||||
int b = 0;
|
||||
int b_max;
|
||||
int r;
|
||||
int sampleI, sampleQ, sampleMic;
|
||||
|
||||
|
||||
if (buffer[b++]==0x7F && buffer[b++]==0x7F && buffer[b++]==0x7F)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
|
||||
// extract control bytes
|
||||
m_controlIn[0] = buffer[b++];
|
||||
m_controlIn[1] = buffer[b++];
|
||||
m_controlIn[2] = buffer[b++];
|
||||
m_controlIn[3] = buffer[b++];
|
||||
m_controlIn[4] = buffer[b++];
|
||||
|
||||
// extract PTT, DOT and DASH
|
||||
m_ptt = (m_controlIn[0] & 0x01) == 0x01;
|
||||
m_dash = (m_controlIn[0] & 0x02) == 0x02;
|
||||
m_dot = (m_controlIn[0] & 0x04) == 0x04;
|
||||
|
||||
switch((m_controlIn[0]>>3) & 0x1F)
|
||||
{
|
||||
case 0:
|
||||
m_lt2208ADCOverflow = m_controlIn[1] & 0x01;
|
||||
m_IO1 = (m_controlIn[1] & 0x02) ? 0 : 1;
|
||||
m_IO2 = (m_controlIn[1] & 0x04) ? 0 : 1;
|
||||
m_IO3 = (m_controlIn[1] & 0x08) ? 0 : 1;
|
||||
// {
|
||||
// int nbRx = (m_controlIn[4]>>3) & 0x07;
|
||||
// nbRx++;
|
||||
// qDebug("MetisMISOUDPHandler::processIQBuffer: nbRx: %d", nbRx);
|
||||
// }
|
||||
if (m_mercurySoftwareVersion != m_controlIn[2])
|
||||
{
|
||||
m_mercurySoftwareVersion = m_controlIn[2];
|
||||
qDebug("MetisMISOUDPHandler::processIQBuffer: Mercury Software version: %d (0x%0X)",
|
||||
m_mercurySoftwareVersion, m_mercurySoftwareVersion);
|
||||
}
|
||||
|
||||
if (m_penelopeSoftwareVersion != m_controlIn[3])
|
||||
{
|
||||
m_penelopeSoftwareVersion = m_controlIn[3];
|
||||
qDebug("MetisMISOUDPHandler::processIQBuffer: Penelope Software version: %d (0x%0X)",
|
||||
m_penelopeSoftwareVersion, m_penelopeSoftwareVersion);
|
||||
}
|
||||
|
||||
if (m_ozySoftwareVersion != m_controlIn[4])
|
||||
{
|
||||
m_ozySoftwareVersion = m_controlIn[4];
|
||||
qDebug("MetisMISOUDPHandler::processIQBuffer: Ozy Software version: %d (0x%0X)",
|
||||
m_ozySoftwareVersion, m_ozySoftwareVersion);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
m_forwardPower = (m_controlIn[1]<<8) + m_controlIn[2]; // from Penelope or Hermes
|
||||
m_alexForwardPower = (m_controlIn[3]<<8) + m_controlIn[4]; // from Alex or Apollo
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_alexForwardPower = (m_controlIn[1]<<8) + m_controlIn[2]; // from Alex or Apollo
|
||||
m_AIN3 = (m_controlIn[3]<<8) + m_controlIn[4]; // from Pennelope or Hermes
|
||||
break;
|
||||
|
||||
case 3:
|
||||
m_AIN4 = (m_controlIn[1]<<8) + m_controlIn[2]; // from Pennelope or Hermes
|
||||
m_AIN6 = (m_controlIn[3]<<8) + m_controlIn[4]; // from Pennelope or Hermes
|
||||
break;
|
||||
}
|
||||
|
||||
// extract the samples
|
||||
while (b < m_bMax)
|
||||
{
|
||||
int samplesAdded = 0;
|
||||
|
||||
// extract each of the receivers
|
||||
for (r = 0; r < m_nbReceivers; r++)
|
||||
{
|
||||
if (SDR_RX_SAMP_SZ == 16)
|
||||
{
|
||||
sampleQ = (int)((signed char)buffer[b++]) << 8;
|
||||
sampleQ += (int)((unsigned char)buffer[b++]);
|
||||
b++;
|
||||
sampleI = (int)((signed char)buffer[b++]) << 8;
|
||||
sampleI += (int)((unsigned char)buffer[b++]);
|
||||
b++;
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleQ = (int)((signed char)buffer[b++]) << 16;
|
||||
sampleQ += (int)((unsigned char)buffer[b++]) << 8;
|
||||
sampleQ += (int)((unsigned char)buffer[b++]);
|
||||
sampleI = (int)((signed char)buffer[b++]) << 16;
|
||||
sampleI += (int)((unsigned char)buffer[b++]) << 8;
|
||||
sampleI += (int)((unsigned char)buffer[b++]);
|
||||
}
|
||||
|
||||
if (r < MetisMISOSettings::m_maxReceivers)
|
||||
{
|
||||
if (m_settings.m_log2Decim == 0) // no decimation - direct conversion
|
||||
{
|
||||
m_convertBuffer[r][m_sampleCount] = Sample{sampleI, sampleQ};
|
||||
samplesAdded = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
SampleVector v(2, Sample{0, 0});
|
||||
|
||||
switch (m_settings.m_log2Decim)
|
||||
{
|
||||
case 1:
|
||||
samplesAdded = m_decimators.decimate2(sampleI, sampleQ, v, r);
|
||||
break;
|
||||
case 2:
|
||||
samplesAdded = m_decimators.decimate4(sampleI, sampleQ, v, r);
|
||||
break;
|
||||
case 3:
|
||||
samplesAdded = m_decimators.decimate8(sampleI, sampleQ, v, r);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (samplesAdded != 0)
|
||||
{
|
||||
std::copy(
|
||||
v.begin(),
|
||||
v.begin() + samplesAdded,
|
||||
&m_convertBuffer[r][m_sampleCount]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sampleMic = (int)((signed char) buffer[b++]) << 8;
|
||||
sampleMic += (int)((unsigned char)buffer[b++]);
|
||||
|
||||
if (samplesAdded != 0)
|
||||
{
|
||||
m_sampleCount += samplesAdded;
|
||||
|
||||
if (m_sampleCount >= 1024)
|
||||
{
|
||||
std::vector<SampleVector::const_iterator> vbegin;
|
||||
|
||||
for (unsigned int channel = 0; channel < MetisMISOSettings::m_maxReceivers; channel++) {
|
||||
vbegin.push_back(m_convertBuffer[channel].begin());
|
||||
}
|
||||
|
||||
m_sampleFifo->writeSync(vbegin, 1024);
|
||||
m_sampleCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug()<<"MetisMISOUDPHandler::processIQBuffer: SYNC Error";
|
||||
}
|
||||
|
||||
m_rxFrame++;
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISOUDPHANDLER_H_
|
||||
#define _METISMISO_METISMISOUDPHANDLER_H_
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QHostAddress>
|
||||
#include <QMutex>
|
||||
|
||||
#include "dsp/decimators.h"
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/message.h"
|
||||
|
||||
#include "metismisosettings.h"
|
||||
#include "metismisodecimators.h"
|
||||
|
||||
class SampleMIFifo;
|
||||
class DeviceAPI;
|
||||
|
||||
class MetisMISOUDPHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
class MsgStartStop : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getStartStop() const { return m_startStop; }
|
||||
|
||||
static MsgStartStop* create(bool startStop) {
|
||||
return new MsgStartStop(startStop);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool m_startStop;
|
||||
|
||||
MsgStartStop(bool startStop) :
|
||||
Message(),
|
||||
m_startStop(startStop)
|
||||
{ }
|
||||
};
|
||||
|
||||
MetisMISOUDPHandler(SampleMIFifo* sampleFifo, DeviceAPI *deviceAPI);
|
||||
~MetisMISOUDPHandler();
|
||||
void setMessageQueueToGUI(MessageQueue *queue) { m_messageQueueToGUI = queue; }
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
void start();
|
||||
void stop();
|
||||
void setMetisAddress(const QHostAddress& address, quint16 port) {
|
||||
m_metisAddress = address;
|
||||
m_metisPort = port;
|
||||
}
|
||||
void setNbReceivers(unsigned int nbReceivers);
|
||||
void applySettings(const MetisMISOSettings& settings);
|
||||
|
||||
public slots:
|
||||
void dataReadyRead();
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QUdpSocket m_socket;
|
||||
QHostAddress m_metisAddress;
|
||||
quint16 m_metisPort;
|
||||
bool m_running;
|
||||
bool m_dataConnected;
|
||||
SampleMIFifo *m_sampleFifo;
|
||||
SampleVector m_convertBuffer[MetisMISOSettings::m_maxReceivers];
|
||||
int m_sampleCount;
|
||||
MessageQueue *m_messageQueueToGUI;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
MetisMISOSettings m_settings;
|
||||
QMutex m_mutex;
|
||||
MetisMISODecimators m_decimators;
|
||||
|
||||
unsigned long m_sendSequence;
|
||||
int m_offset;
|
||||
int m_commandBase;
|
||||
unsigned long m_rxFrame;
|
||||
unsigned long m_txFrame;
|
||||
unsigned char m_outputBuffer[1032]; //!< buffer to send
|
||||
unsigned char m_metisBuffer[512]; //!< current HPSDR frame
|
||||
int metisBufferIndex;
|
||||
unsigned long m_receiveSequence;
|
||||
int m_receiveSequenceError;
|
||||
unsigned char m_controlIn[5];
|
||||
unsigned int m_nbReceivers;
|
||||
int m_bMax;
|
||||
bool m_ptt;
|
||||
bool m_dash;
|
||||
bool m_dot;
|
||||
bool m_lt2208ADCOverflow;
|
||||
int m_IO1;
|
||||
int m_IO2;
|
||||
int m_IO3;
|
||||
int m_mercurySoftwareVersion;
|
||||
int m_penelopeSoftwareVersion;
|
||||
int m_ozySoftwareVersion;
|
||||
int m_forwardPower;
|
||||
int m_alexForwardPower;
|
||||
int m_AIN3;
|
||||
int m_AIN4;
|
||||
int m_AIN6;
|
||||
|
||||
void sendMetisBuffer(int ep, unsigned char* buffer);
|
||||
bool handleMessage(const Message& message);
|
||||
void sendNullBuffer();
|
||||
int getCommandValue(int commandIndex);
|
||||
void processIQBuffer(unsigned char* buffer);
|
||||
|
||||
private slots:
|
||||
void handleMessages();
|
||||
};
|
||||
|
||||
#endif // _METISMISO_METISMISOUDPHANDLER_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// Implementation of static web API adapters used for preset serialization and //
|
||||
// deserialization //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SWGDeviceSettings.h"
|
||||
#include "metismiso.h"
|
||||
#include "metismisowebapiadapter.h"
|
||||
|
||||
MetisMISOWebAPIAdapter::MetisMISOWebAPIAdapter()
|
||||
{}
|
||||
|
||||
MetisMISOWebAPIAdapter::~MetisMISOWebAPIAdapter()
|
||||
{}
|
||||
|
||||
int MetisMISOWebAPIAdapter::webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
response.setMetisMisoSettings(new SWGSDRangel::SWGMetisMISOSettings());
|
||||
response.getMetisMisoSettings()->init();
|
||||
MetisMISO::webapiFormatDeviceSettings(response, m_settings);
|
||||
return 200;
|
||||
}
|
||||
|
||||
int MetisMISOWebAPIAdapter::webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage)
|
||||
{
|
||||
(void) errorMessage;
|
||||
MetisMISO::webapiUpdateDeviceSettings(m_settings, deviceSettingsKeys, response);
|
||||
return 200;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// Implementation of static web API adapters used for preset serialization and //
|
||||
// deserialization //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// 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 V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _METISMISO_METISMISOWEBAPIADAPTER_H_
|
||||
#define _METISMISO_METISMISOWEBAPIADAPTER_H_
|
||||
|
||||
#include "device/devicewebapiadapter.h"
|
||||
#include "metismisosettings.h"
|
||||
|
||||
class MetisMISOWebAPIAdapter : public DeviceWebAPIAdapter
|
||||
{
|
||||
public:
|
||||
MetisMISOWebAPIAdapter();
|
||||
virtual ~MetisMISOWebAPIAdapter();
|
||||
virtual QByteArray serialize() { return m_settings.serialize(); }
|
||||
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
|
||||
|
||||
virtual int webapiSettingsGet(
|
||||
SWGSDRangel::SWGDeviceSettings& response,
|
||||
QString& errorMessage);
|
||||
|
||||
virtual int webapiSettingsPutPatch(
|
||||
bool force,
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response, // query + response
|
||||
QString& errorMessage);
|
||||
|
||||
private:
|
||||
MetisMISOSettings m_settings;
|
||||
};
|
||||
|
||||
#endif // _METISMISO_METISMISOWEBAPIADAPTER_H_
|
||||
@@ -0,0 +1,137 @@
|
||||
<h1>Test source input plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
This is a v5 only plugin.
|
||||
|
||||
This input sample source plugin is an internal continuous wave generator that can be used to carry out test of software internals.
|
||||
|
||||
<h2>Build</h2>
|
||||
|
||||
The plugin is present in the core of the software and thus is always present in the list of sources.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||

|
||||
|
||||
<h3>1: Common stream parameters</h3>
|
||||
|
||||

|
||||
|
||||
<h4>1.1: Frequency</h4>
|
||||
|
||||
This is the center frequency of reception in kHz.
|
||||
|
||||
<h4>1.2: Start/Stop</h4>
|
||||
|
||||
Device start / stop button.
|
||||
|
||||
- Blue triangle icon: device is ready and can be started
|
||||
- Green square icon: device is running and can be stopped
|
||||
- Magenta (or pink) square icon: an error occurred. In the case the device was accidentally disconnected you may click on the icon, plug back in and start again.
|
||||
|
||||
<h4>1.3: Record</h4>
|
||||
|
||||
- Left click: record baseband I/Q stream toggle button
|
||||
- Right click: choose record file
|
||||
|
||||
<h4>1.4: Stream sample rate</h4>
|
||||
|
||||
Baseband I/Q sample rate in kS/s. This is the device to host sample rate (3) divided by the decimation factor (4).
|
||||
|
||||
<h3>2: Various options</h3>
|
||||
|
||||

|
||||
|
||||
<h4>2.1: Auto corrections</h4>
|
||||
|
||||
This combo box control the local DSP auto correction options:
|
||||
|
||||
- **None**: no correction
|
||||
- **DC**: auto remove DC component
|
||||
- **DC+IQ**: auto remove DC component and correct I/Q balance.
|
||||
|
||||
<h4>2.2: Decimation factor</h4>
|
||||
|
||||
The I/Q stream from the generator is downsampled by a power of two before being sent to the passband. Possible values are increasing powers of two: 1 (no decimation), 2, 4, 8, 16, 32. This exercises the decimation chain.
|
||||
|
||||
This exercises the decimation chain.
|
||||
|
||||
<h4>2.3: Baseband center frequency position relative the center frequency</h4>
|
||||
|
||||
- **Cen**: the decimation operation takes place around the center frequency Fs
|
||||
- **Inf**: the decimation operation takes place around Fs - Fc.
|
||||
- **Sup**: the decimation operation takes place around Fs + Fc.
|
||||
|
||||
With SR as the sample rate before decimation Fc is calculated as:
|
||||
|
||||
- if decimation n is 4 or lower: Fc = SR/2^(log2(n)-1). The device center frequency is on the side of the baseband. You need a RF filter bandwidth at least twice the baseband.
|
||||
- if decimation n is 8 or higher: Fc = SR/n. The device center frequency is half the baseband away from the side of the baseband. You need a RF filter bandwidth at least 3 times the baseband.
|
||||
|
||||
<h3>2.4: Sample size</h3>
|
||||
|
||||
This is the sample size in number of bits. It corresponds to the actual sample size used by the devices supported:
|
||||
|
||||
- **8**: RTL-SDR, HackRF
|
||||
- **12**: Airspy, BladeRF, LimeSDR, PlutoSDR, SDRplay
|
||||
- **16**: Airspy HF+, FCD Pro, FCD Pro+
|
||||
|
||||
<h3>3: Sample rate</h3>
|
||||
|
||||
This controls the generator sample rate in samples per second.
|
||||
|
||||
<h3>4: Modulation</h4>
|
||||
|
||||
- **No**: No modulation
|
||||
- **AM**: Amplitude modulation (AM)
|
||||
- **FM**: Frequency modulation (FM)
|
||||
- **P0**: Pattern 0 is a binary pattern
|
||||
- Pulse width: 150 samples
|
||||
- Sync pattern: 010 at full amplitude
|
||||
- Binary pattern LSB first on 3 bits from 0 to 7 at 0.3 amplitude
|
||||
- **P1**: Pattern 1 is a sawtooth pattern
|
||||
- Pulse width: 1000 samples
|
||||
- Starts at full amplitude then amplitude decreases linearly down to zero
|
||||
- **P2**: Pattern 2 is a 50% duty cycle square pattern
|
||||
- Pulse width: 1000 samples
|
||||
- Starts with a full amplitude pulse then down to zero for the duration of one pulse
|
||||
|
||||
<h3>5: Modulating tone frequency</h3>
|
||||
|
||||
This controls the modulating tone frequency in kHz in 10 Hz steps.
|
||||
|
||||
<h3>6: Carrier shift from center frequency</h3>
|
||||
|
||||
Use this control to set the offset of the carrier from the center frequency of reception.
|
||||
|
||||
<h3>7: AM modulation factor</h3>
|
||||
|
||||
This controls the AM modulation factor from 0 to 99%
|
||||
|
||||
<h3>8: FM deviation</h3>
|
||||
|
||||
This controls the frequency modulation deviation in kHz in 100 Hz steps. It cannot exceed the sample rate.
|
||||
|
||||
<h3>9: Amplitude coarse control</h3>
|
||||
|
||||
This slider controls the number of amplitude bits by steps of 100 bits. The total number of amplitude bits appear on the right.
|
||||
|
||||
<h3>10: Amplitude fine control</h3>
|
||||
|
||||
This slider controls the number of amplitude bits by steps of 1 bit. The signal power in dB relative to the maximum power (full bit range) appear on the right.
|
||||
|
||||
<h3>11: DC bias</h3>
|
||||
|
||||
Use this slider to give a DC component in percentage of maximum amplitude.
|
||||
|
||||
<h3>12: I bias</h3>
|
||||
|
||||
Use this slider to give an in-phase (I) bias in percentage of maximum amplitude.
|
||||
|
||||
<h3>13: Q bias</h3>
|
||||
|
||||
Use this slider to give an quadrature-phase (Q) bias in percentage of maximum amplitude.
|
||||
|
||||
<h3>14: Phase imbalance</h3>
|
||||
|
||||
Use this slider to introduce a phase imbalance in percentage of full period (continuous wave) or percentage of I signal injected in Q (AM, FM).
|
||||
Reference in New Issue
Block a user