Add noise figure plugin

This commit is contained in:
Jon Beniston 2021-06-09 16:17:59 +01:00
parent 016ec01826
commit 97669585ad
48 changed files with 6136 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -18,6 +18,7 @@ add_subdirectory(demodchirpchat)
add_subdirectory(demodvorsc)
add_subdirectory(demodpacket)
add_subdirectory(demodais)
add_subdirectory(noisefigure)
if(DAB_FOUND AND ZLIB_FOUND AND FAAD_FOUND)
add_subdirectory(demoddab)

View File

@ -0,0 +1,65 @@
project(noisefigure)
set(noisefigure_SOURCES
noisefigure.cpp
noisefiguresettings.cpp
noisefigurebaseband.cpp
noisefiguresink.cpp
noisefigureplugin.cpp
noisefigurewebapiadapter.cpp
)
set(noisefigure_HEADERS
noisefigure.h
noisefiguresettings.h
noisefigurebaseband.h
noisefiguresink.h
noisefigureplugin.h
noisefigurewebapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${Boost_INCLUDE_DIRS}
)
if(NOT SERVER_MODE)
set(noisefigure_SOURCES
${noisefigure_SOURCES}
noisefiguregui.cpp
noisefiguregui.ui
noisefigurecontroldialog.ui
noisefigurecontroldialog.cpp
noisefigureenrdialog.ui
noisefigureenrdialog.cpp
)
set(noisefigure_HEADERS
${noisefigure_HEADERS}
noisefiguregui.h
noisefigurecontroldialog.h
noisefigureenrdialog.h
)
set(TARGET_NAME noisefigure)
set(TARGET_LIB "Qt5::Widgets" Qt5::Charts)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME noisefiguresrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${noisefigure_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})

View File

@ -0,0 +1,755 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 <boost/math/interpolators/barycentric_rational.hpp>
#include "noisefigure.h"
#include <QTimer>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include <QProcess>
#include <complex.h>
#include "SWGChannelSettings.h"
#include "SWGChannelReport.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "device/deviceapi.h"
#include "feature/feature.h"
#include "util/db.h"
#include "channel/channelwebapiutils.h"
#include "maincore.h"
MESSAGE_CLASS_DEFINITION(NoiseFigure::MsgConfigureNoiseFigure, Message)
MESSAGE_CLASS_DEFINITION(NoiseFigure::MsgPowerMeasurement, Message)
MESSAGE_CLASS_DEFINITION(NoiseFigure::MsgNFMeasurement, Message)
MESSAGE_CLASS_DEFINITION(NoiseFigure::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(NoiseFigure::MsgFinished, Message)
const char * const NoiseFigure::m_channelIdURI = "sdrangel.channel.noisefigure";
const char * const NoiseFigure::m_channelId = "NoiseFigure";
NoiseFigure::NoiseFigure(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink),
m_deviceAPI(deviceAPI),
m_basebandSampleRate(0),
m_state(IDLE)
{
setObjectName(m_channelId);
m_basebandSink = new NoiseFigureBaseband(this);
m_basebandSink->setMessageQueueToChannel(getInputMessageQueue());
m_basebandSink->setChannel(this);
m_basebandSink->moveToThread(&m_thread);
applySettings(m_settings, true);
m_deviceAPI->addChannelSink(this);
m_deviceAPI->addChannelSinkAPI(this);
m_networkManager = new QNetworkAccessManager();
connect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
}
NoiseFigure::~NoiseFigure()
{
qDebug("NoiseFigure::~NoiseFigure");
disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*)));
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this);
if (m_basebandSink->isRunning()) {
stop();
}
delete m_basebandSink;
}
uint32_t NoiseFigure::getNumberOfDeviceStreams() const
{
return m_deviceAPI->getNbSourceStreams();
}
void NoiseFigure::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst)
{
(void) firstOfBurst;
m_basebandSink->feed(begin, end);
}
void NoiseFigure::start()
{
qDebug("NoiseFigure::start");
m_basebandSink->reset();
m_basebandSink->startWork();
m_thread.start();
DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency);
m_basebandSink->getInputMessageQueue()->push(dspMsg);
NoiseFigureBaseband::MsgConfigureNoiseFigureBaseband *msg = NoiseFigureBaseband::MsgConfigureNoiseFigureBaseband::create(m_settings, true);
m_basebandSink->getInputMessageQueue()->push(msg);
}
void NoiseFigure::stop()
{
qDebug("NoiseFigure::stop");
m_basebandSink->stopWork();
m_thread.quit();
m_thread.wait();
}
void NoiseFigure::openVISADevice()
{
m_visa.openDefault();
m_session = m_visa.open(m_settings.m_visaDevice);
}
void NoiseFigure::closeVISADevice()
{
m_visa.close(m_session);
m_visa.closeDefault();
}
void NoiseFigure::processVISA(QStringList commands)
{
if (m_session > 0)
{
for (int i = 0; i < commands.size(); i++)
{
QString command = commands[i].trimmed();
if (!command.isEmpty() && !command.startsWith("#")) // Allow # to comment out lines
{
qDebug() << "VISA ->: " << command;
QByteArray bytes = QString("%1\n").arg(command).toLatin1();
char *cmd = bytes.data();
m_visa.viPrintf(m_session, cmd);
if (command.endsWith("?"))
{
char buf[1024] = "";
m_visa.viScanf(m_session, "%t", buf);
qDebug() << "VISA <-: " << buf;
}
}
}
}
}
// Calculate ENR at specified frequency
double NoiseFigure::calcENR(double frequency)
{
int size = m_settings.m_enr.size();
if (size >= 2)
{
int order = size > 3 ? 3 : size - 1;
std::vector<double> x(size);
std::vector<double> y(size);
for (int i = 0; i < size; i++)
{
x[i] = m_settings.m_enr[i]->m_frequency;
y[i] = m_settings.m_enr[i]->m_enr;
}
boost::math::barycentric_rational<double> interpolant(std::move(x), std::move(y), order);
double enr = interpolant(frequency);
qDebug() << "ENR at " << frequency << " interpolated to " << enr;
return enr;
}
else if (size == 1)
{
return m_settings.m_enr[0]->m_enr;
}
else
{
return 0.0;
}
}
// FSM for running measurements over multiple frequencies
void NoiseFigure::nextState()
{
switch (m_state)
{
case IDLE:
if (m_settings.m_enr.size() < 1)
{
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgFinished::create("ENRs has not been specified"));
}
return;
}
m_step = 0;
if (m_settings.m_frequencySpec == NoiseFigureSettings::LIST)
{
// Create list of frequencies from string
QRegExp separator("[( |,|\t|)]");
QStringList freqStrings = m_settings.m_frequencies.trimmed().split(separator);
m_freqs.clear();
for (int i = 0; i < freqStrings.size(); i++)
{
QString freqString = freqStrings[i].trimmed();
if (!freqString.isEmpty())
{
bool ok;
double freq = freqString.toDouble(&ok);
if (ok) {
m_freqs.append(freq * 1e6);
} else {
qDebug() << "NoiseFigure::nextState: Invalid frequency: " << freqString;
}
}
}
if (m_freqs.size() == 0)
{
qDebug() << "NoiseFigure::nextState: No frequencies in list";
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgFinished::create("No frequencies in list"));
}
return;
}
// Set start frequency and number of frequencies to step through
m_measurementFrequency = m_freqs[0];
m_steps = m_freqs.size();
}
else
{
if (m_settings.m_stopFrequency < m_settings.m_startFrequency)
{
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgFinished::create("Stop frequency must be greater or equal to start frequency"));
}
return;
}
// Set start frequency and number of frequencies to step through
m_measurementFrequency = m_settings.m_startFrequency * 1.e6;
if (m_settings.m_frequencySpec == NoiseFigureSettings::RANGE) {
m_steps = m_settings.m_steps;
} else {
m_steps = (m_settings.m_stopFrequency - m_settings.m_startFrequency) / m_settings.m_step + 1;
}
}
m_state = SET_FREQUENCY;
QTimer::singleShot(0, this, SLOT(nextState()));
break;
case SET_FREQUENCY:
// Set radio centre frequency
if (ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), m_measurementFrequency))
{
qDebug() << "NoiseFigure::nextState: Set center frequency: " << m_measurementFrequency;
m_state = POWER_ON;
QTimer::singleShot(100, this, SLOT(nextState()));
} else
{
qDebug() << "NoiseFigure::nextState: Unable to set center frequency: " << m_measurementFrequency;
}
break;
case POWER_ON:
// Power on noise source
powerOn();
QTimer::singleShot(m_settings.m_powerDelay * 1000.0, this, SLOT(nextState()));
m_state = MEASURE_ON;
break;
case MEASURE_ON:
// Start measurement of power when noise source is on
qDebug() << "NoiseFigure::nextState: Starting on measurement";
m_basebandSink->startMeasurement();
break;
case POWER_OFF:
// Power off noise source
powerOff();
QTimer::singleShot(m_settings.m_powerDelay * 1000.0, this, SLOT(nextState()));
m_state = MEASURE_OFF;
break;
case MEASURE_OFF:
// Start measurement of power when noise source is off
qDebug() << "NoiseFigure::nextState: Starting off measurement";
m_basebandSink->startMeasurement();
break;
case COMPLETE:
// Calculate noise figure and temperature using Y-factor method
double y = m_onPower - m_offPower;
double enr = calcENR(m_measurementFrequency/1e6);
double nf = 10.0*log10(pow(10.0, enr/10.0)/(pow(10.0, y/10.0)-1.0));
double temp = 290.0*(pow(10.0, nf/10.0)-1.0);
// Send result to GUI
if (getMessageQueueToGUI())
{
MsgNFMeasurement *msg = MsgNFMeasurement::create(m_measurementFrequency/1e6, nf, temp, y, enr);
getMessageQueueToGUI()->push(msg);
}
m_step++;
if (m_step >= m_steps)
{
// All frequencies measured
closeVISADevice();
m_state = IDLE;
if (getMessageQueueToGUI()) {
getMessageQueueToGUI()->push(MsgFinished::create());
}
}
else
{
// Move to next frequency
if (m_settings.m_frequencySpec == NoiseFigureSettings::LIST) {
m_measurementFrequency = m_freqs[m_step];
} else if (m_settings.m_frequencySpec == NoiseFigureSettings::RANGE) {
m_measurementFrequency += 1e6 * (m_settings.m_stopFrequency - m_settings.m_startFrequency) / (m_settings.m_steps - 1);
} else {
m_measurementFrequency += m_settings.m_step * 1e6;
}
m_state = SET_FREQUENCY;
QTimer::singleShot(0, this, SLOT(nextState()));
}
break;
}
}
void NoiseFigure::powerOn()
{
QString command = m_settings.m_powerOnCommand.trimmed();
if (!command.isEmpty()) {
QProcess::execute(command);
}
QStringList commands = m_settings.m_powerOnSCPI.split("\n");
processVISA(commands);
}
void NoiseFigure::powerOff()
{
QStringList commands = m_settings.m_powerOffSCPI.split("\n");
processVISA(commands);
QString command = m_settings.m_powerOffCommand.trimmed();
if (!command.isEmpty()) {
QProcess::execute(command);
}
}
bool NoiseFigure::handleMessage(const Message& cmd)
{
if (MsgConfigureNoiseFigure::match(cmd))
{
MsgConfigureNoiseFigure& cfg = (MsgConfigureNoiseFigure&) cmd;
qDebug() << "NoiseFigure::handleMessage: MsgConfigureNoiseFigure";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
m_basebandSampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
// Forward to the sink
DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy
qDebug() << "NoiseFigure::handleMessage: DSPSignalNotification";
m_basebandSink->getInputMessageQueue()->push(rep);
// Forward to GUI if any
if (m_guiMessageQueue)
{
rep = new DSPSignalNotification(notif);
m_guiMessageQueue->push(rep);
}
return true;
}
else if (MsgPowerMeasurement::match(cmd))
{
MsgPowerMeasurement& report = (MsgPowerMeasurement&)cmd;
if (m_state == MEASURE_ON)
{
m_onPower = report.getPower();
m_state = POWER_OFF;
nextState();
}
else if (m_state == MEASURE_OFF)
{
m_offPower = report.getPower();
m_state = COMPLETE;
nextState();
}
return true;
}
else if (MsgStartStop::match(cmd))
{
if (m_state == IDLE)
{
openVISADevice();
QTimer::singleShot(0, this, SLOT(nextState()));
}
else
{
// Set maximum step so test stops after current measurement
m_step = m_steps;
}
return true;
}
else
{
return false;
}
}
void NoiseFigure::applySettings(const NoiseFigureSettings& settings, bool force)
{
qDebug() << "NoiseFigure::applySettings:"
<< " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset
<< " m_fftSize: " << settings.m_fftSize
<< " m_fftCount: " << settings.m_fftCount
<< " m_frequencySpec: " << settings.m_frequencySpec
<< " m_startFrequency: " << settings.m_startFrequency
<< " m_stopFrequency: " << settings.m_stopFrequency
<< " m_steps: " << settings.m_steps
<< " m_step: " << settings.m_step
<< " m_frequencies: " << settings.m_frequencies
<< " m_visaDevice: " << settings.m_visaDevice
<< " m_powerOnSCPI: " << settings.m_powerOnSCPI
<< " m_powerOffSCPI: " << settings.m_powerOffSCPI
<< " m_powerOnCommand: " << settings.m_powerOnCommand
<< " m_powerOffCommand: " << settings.m_powerOffCommand
<< " m_powerDelay: " << settings.m_powerDelay
<< " m_enr: " << settings.m_enr
<< " m_streamIndex: " << settings.m_streamIndex
<< " m_useReverseAPI: " << settings.m_useReverseAPI
<< " m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< " m_reverseAPIPort: " << settings.m_reverseAPIPort
<< " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< " force: " << force;
QList<QString> reverseAPIKeys;
if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) {
reverseAPIKeys.append("inputFrequencyOffset");
}
if (m_settings.m_streamIndex != settings.m_streamIndex)
{
if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex);
m_deviceAPI->addChannelSink(this, settings.m_streamIndex);
m_deviceAPI->addChannelSinkAPI(this);
}
reverseAPIKeys.append("streamIndex");
}
NoiseFigureBaseband::MsgConfigureNoiseFigureBaseband *msg = NoiseFigureBaseband::MsgConfigureNoiseFigureBaseband::create(settings, force);
m_basebandSink->getInputMessageQueue()->push(msg);
if (settings.m_useReverseAPI)
{
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) ||
(m_settings.m_reverseAPIChannelIndex != settings.m_reverseAPIChannelIndex);
webapiReverseSendSettings(reverseAPIKeys, settings, fullUpdate || force);
}
m_settings = settings;
}
QByteArray NoiseFigure::serialize() const
{
return m_settings.serialize();
}
bool NoiseFigure::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureNoiseFigure *msg = MsgConfigureNoiseFigure::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureNoiseFigure *msg = MsgConfigureNoiseFigure::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
int NoiseFigure::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setNoiseFigureSettings(new SWGSDRangel::SWGNoiseFigureSettings());
response.getNoiseFigureSettings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int NoiseFigure::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
NoiseFigureSettings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureNoiseFigure *msg = MsgConfigureNoiseFigure::create(settings, force);
m_inputMessageQueue.push(msg);
qDebug("NoiseFigure::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureNoiseFigure *msgToGUI = MsgConfigureNoiseFigure::create(settings, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
void NoiseFigure::webapiUpdateChannelSettings(
NoiseFigureSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("inputFrequencyOffset")) {
settings.m_inputFrequencyOffset = response.getNoiseFigureSettings()->getInputFrequencyOffset();
}
if (channelSettingsKeys.contains("fftSize")) {
settings.m_fftSize = response.getNoiseFigureSettings()->getFftSize();
}
if (channelSettingsKeys.contains("frequencySpec")) {
settings.m_frequencySpec = (NoiseFigureSettings::FrequencySpec)response.getNoiseFigureSettings()->getFrequencySpec();
}
if (channelSettingsKeys.contains("startFrequency")) {
settings.m_startFrequency = response.getNoiseFigureSettings()->getStartFrequency();
}
if (channelSettingsKeys.contains("stopFrequency")) {
settings.m_stopFrequency = response.getNoiseFigureSettings()->getStopFrequency();
}
if (channelSettingsKeys.contains("steps")) {
settings.m_steps = response.getNoiseFigureSettings()->getSteps();
}
if (channelSettingsKeys.contains("step")) {
settings.m_step = response.getNoiseFigureSettings()->getStep();
}
if (channelSettingsKeys.contains("frequencies")) {
settings.m_frequencies = *response.getNoiseFigureSettings()->getFrequencies();
}
if (channelSettingsKeys.contains("visaDevice")) {
settings.m_visaDevice = *response.getNoiseFigureSettings()->getVisaDevice();
}
if (channelSettingsKeys.contains("powerOnSCPI")) {
settings.m_powerOnSCPI = *response.getNoiseFigureSettings()->getPowerOnScpi();
}
if (channelSettingsKeys.contains("powerOffSCPI")) {
settings.m_powerOffSCPI = *response.getNoiseFigureSettings()->getPowerOffScpi();
}
if (channelSettingsKeys.contains("powerOnCommand")) {
settings.m_powerOnCommand = *response.getNoiseFigureSettings()->getPowerOnCommand();
}
if (channelSettingsKeys.contains("powerOffCommand")) {
settings.m_powerOffCommand = *response.getNoiseFigureSettings()->getPowerOffCommand();
}
if (channelSettingsKeys.contains("powerDelay")) {
settings.m_powerDelay = response.getNoiseFigureSettings()->getPowerDelay();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getNoiseFigureSettings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getNoiseFigureSettings()->getTitle();
}
if (channelSettingsKeys.contains("streamIndex")) {
settings.m_streamIndex = response.getNoiseFigureSettings()->getStreamIndex();
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getNoiseFigureSettings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getNoiseFigureSettings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getNoiseFigureSettings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getNoiseFigureSettings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getNoiseFigureSettings()->getReverseApiChannelIndex();
}
}
void NoiseFigure::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const NoiseFigureSettings& settings)
{
response.getNoiseFigureSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
response.getNoiseFigureSettings()->setRgbColor(settings.m_rgbColor);
if (response.getNoiseFigureSettings()->getTitle()) {
*response.getNoiseFigureSettings()->getTitle() = settings.m_title;
} else {
response.getNoiseFigureSettings()->setTitle(new QString(settings.m_title));
}
response.getNoiseFigureSettings()->setStreamIndex(settings.m_streamIndex);
response.getNoiseFigureSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getNoiseFigureSettings()->getReverseApiAddress()) {
*response.getNoiseFigureSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getNoiseFigureSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getNoiseFigureSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getNoiseFigureSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getNoiseFigureSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
}
void NoiseFigure::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const NoiseFigureSettings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->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 swgChannelSettings;
}
void NoiseFigure::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const NoiseFigureSettings& settings,
bool force
)
{
swgChannelSettings->setDirection(0); // Single sink (Rx)
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("NoiseFigure"));
swgChannelSettings->setNoiseFigureSettings(new SWGSDRangel::SWGNoiseFigureSettings());
SWGSDRangel::SWGNoiseFigureSettings *swgNoiseFigureSettings = swgChannelSettings->getNoiseFigureSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("inputFrequencyOffset") || force) {
swgNoiseFigureSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset);
}
if (channelSettingsKeys.contains("fftSize") || force) {
swgNoiseFigureSettings->setFftSize(settings.m_fftSize);
}
if (channelSettingsKeys.contains("fftCount") || force) {
swgNoiseFigureSettings->setFftCount(settings.m_fftCount);
}
if (channelSettingsKeys.contains("frequencySpec") || force) {
swgNoiseFigureSettings->setFrequencySpec((int)settings.m_frequencySpec);
}
if (channelSettingsKeys.contains("stopFrequency") || force) {
swgNoiseFigureSettings->setStopFrequency(settings.m_stopFrequency);
}
if (channelSettingsKeys.contains("steps") || force) {
swgNoiseFigureSettings->setSteps(settings.m_steps);
}
if (channelSettingsKeys.contains("step") || force) {
swgNoiseFigureSettings->setStep(settings.m_step);
}
if (channelSettingsKeys.contains("frequencies") || force) {
swgNoiseFigureSettings->setFrequencies(new QString(settings.m_frequencies));
}
if (channelSettingsKeys.contains("visaDevice") || force) {
swgNoiseFigureSettings->setVisaDevice(new QString(settings.m_visaDevice));
}
if (channelSettingsKeys.contains("powerOnSCPI") || force) {
swgNoiseFigureSettings->setPowerOnScpi(new QString(settings.m_powerOnSCPI));
}
if (channelSettingsKeys.contains("powerOffSCPI") || force) {
swgNoiseFigureSettings->setPowerOffScpi(new QString(settings.m_powerOffSCPI));
}
if (channelSettingsKeys.contains("powerOnCommand") || force) {
swgNoiseFigureSettings->setPowerOnCommand(new QString(settings.m_powerOnCommand));
}
if (channelSettingsKeys.contains("powerOffCommand") || force) {
swgNoiseFigureSettings->setPowerOffCommand(new QString(settings.m_powerOffCommand));
}
if (channelSettingsKeys.contains("powerDelay") || force) {
swgNoiseFigureSettings->setPowerDelay(settings.m_powerDelay);
}
if (channelSettingsKeys.contains("rgbColor") || force) {
swgNoiseFigureSettings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgNoiseFigureSettings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("streamIndex") || force) {
swgNoiseFigureSettings->setStreamIndex(settings.m_streamIndex);
}
}
void NoiseFigure::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "NoiseFigure::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("NoiseFigure::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

View File

@ -0,0 +1,283 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGURE_H
#define INCLUDE_NOISEFIGURE_H
#include <vector>
#include <QNetworkRequest>
#include <QUdpSocket>
#include <QThread>
#include <QDateTime>
#include "dsp/basebandsamplesink.h"
#include "channel/channelapi.h"
#include "util/message.h"
#include "util/visa.h"
#include "noisefigurebaseband.h"
#include "noisefiguresettings.h"
class QNetworkAccessManager;
class QNetworkReply;
class QThread;
class DeviceAPI;
class NoiseFigure : public BasebandSampleSink, public ChannelAPI {
Q_OBJECT
public:
class MsgConfigureNoiseFigure : public Message {
MESSAGE_CLASS_DECLARATION
public:
const NoiseFigureSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureNoiseFigure* create(const NoiseFigureSettings& settings, bool force)
{
return new MsgConfigureNoiseFigure(settings, force);
}
private:
NoiseFigureSettings m_settings;
bool m_force;
MsgConfigureNoiseFigure(const NoiseFigureSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgPowerMeasurement : public Message {
MESSAGE_CLASS_DECLARATION
public:
double getPower() const { return m_power; }
static MsgPowerMeasurement* create(double power)
{
return new MsgPowerMeasurement(power);
}
private:
double m_power;
MsgPowerMeasurement(double power) :
Message(),
m_power(power)
{
}
};
class MsgNFMeasurement : public Message {
MESSAGE_CLASS_DECLARATION
public:
double getFrequency() const { return m_frequency; }
double getNF() const { return m_nf; }
double getTemp() const { return m_temp; }
double getY() const { return m_y; }
double getENR() const { return m_enr; }
static MsgNFMeasurement* create(double frequency, double nf, double temp, double y, double enr)
{
return new MsgNFMeasurement(frequency, nf, temp, y, enr);
}
private:
double m_frequency; // In MHz
double m_nf; // In dB
double m_temp; // In Kelvin
double m_y; // In dB
double m_enr; // In dB
MsgNFMeasurement(double frequency, double nf, double temp, double y, double enr) :
Message(),
m_frequency(frequency),
m_nf(nf),
m_temp(temp),
m_y(y),
m_enr(enr)
{
}
};
// Sent from GUI to start of stop a measurement
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgStartStop* create()
{
return new MsgStartStop();
}
private:
MsgStartStop() :
Message()
{
}
};
// Sent to GUI to indicate measurements have finished
class MsgFinished : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getErrorMessage() const { return m_errorMessage; }
static MsgFinished* create()
{
QString noError;
return new MsgFinished(noError);
}
static MsgFinished* create(const QString& errorMessage)
{
return new MsgFinished(errorMessage);
}
private:
QString m_errorMessage;
MsgFinished(const QString& errorMessage) :
Message(),
m_errorMessage(errorMessage)
{
}
};
NoiseFigure(DeviceAPI *deviceAPI);
virtual ~NoiseFigure();
virtual void destroy() { delete this; }
using BasebandSampleSink::feed;
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
virtual void stop();
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual const QString& getURI() const { return getName(); }
virtual void getTitle(QString& title) { title = m_settings.m_title; }
virtual qint64 getCenterFrequency() const { return 0; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 1; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return 0;
}
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const NoiseFigureSettings& settings);
static void webapiUpdateChannelSettings(
NoiseFigureSettings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
double getMagSq() const { return m_basebandSink->getMagSq(); }
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_basebandSink->getMagSqLevels(avg, peak, nbSamples);
}
/* void setMessageQueueToGUI(MessageQueue* queue) override {
ChannelAPI::setMessageQueueToGUI(queue);
m_basebandSink->setMessageQueueToGUI(queue);
}*/
void openVISADevice();
void closeVISADevice();
void processVISA(QStringList commands);
void powerOn();
void powerOff();
double calcENR(double frequency);
uint32_t getNumberOfDeviceStreams() const;
static const char * const m_channelIdURI;
static const char * const m_channelId;
private:
DeviceAPI *m_deviceAPI;
QThread m_thread;
NoiseFigureBaseband* m_basebandSink;
NoiseFigureSettings m_settings;
int m_basebandSampleRate; //!< stored from device message used when starting baseband sink
qint64 m_centerFrequency;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void applySettings(const NoiseFigureSettings& settings, bool force = false);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const NoiseFigureSettings& settings, bool force);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const NoiseFigureSettings& settings,
bool force
);
enum State {
IDLE,
SET_FREQUENCY,
POWER_ON,
MEASURE_ON,
POWER_OFF,
MEASURE_OFF,
COMPLETE
} m_state;
double m_measurementFrequency; // In Hz
QList<double> m_freqs; // In Hz
int m_step; // Current frequency step
int m_steps; // Number of frequencies to test
double m_onPower;
double m_offPower;
ViSession m_session;
VISA m_visa;
private slots:
void networkManagerFinished(QNetworkReply *reply);
void nextState();
};
#endif // INCLUDE_NOISEFIGURE_H

View File

@ -0,0 +1,170 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "dsp/downchannelizer.h"
#include "noisefigurebaseband.h"
MESSAGE_CLASS_DEFINITION(NoiseFigureBaseband::MsgConfigureNoiseFigureBaseband, Message)
NoiseFigureBaseband::NoiseFigureBaseband(NoiseFigure *aisDemod) :
m_sink(aisDemod),
m_running(false),
m_mutex(QMutex::Recursive)
{
qDebug("NoiseFigureBaseband::NoiseFigureBaseband");
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000));
m_channelizer = new DownChannelizer(&m_sink);
}
NoiseFigureBaseband::~NoiseFigureBaseband()
{
m_inputMessageQueue.clear();
delete m_channelizer;
}
void NoiseFigureBaseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_inputMessageQueue.clear();
m_sampleFifo.reset();
}
void NoiseFigureBaseband::startWork()
{
QMutexLocker mutexLocker(&m_mutex);
QObject::connect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&NoiseFigureBaseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_running = true;
}
void NoiseFigureBaseband::stopWork()
{
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
QObject::disconnect(
&m_sampleFifo,
&SampleSinkFifo::dataReady,
this,
&NoiseFigureBaseband::handleData
);
m_running = false;
}
void NoiseFigureBaseband::setChannel(ChannelAPI *channel)
{
m_sink.setChannel(channel);
}
void NoiseFigureBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_sampleFifo.write(begin, end);
}
void NoiseFigureBaseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0))
{
SampleVector::iterator part1begin;
SampleVector::iterator part1end;
SampleVector::iterator part2begin;
SampleVector::iterator part2end;
std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end);
// first part of FIFO data
if (part1begin != part1end) {
m_channelizer->feed(part1begin, part1end);
}
// second part of FIFO data (used when block wraps around)
if(part2begin != part2end) {
m_channelizer->feed(part2begin, part2end);
}
m_sampleFifo.readCommit((unsigned int) count);
}
}
void NoiseFigureBaseband::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool NoiseFigureBaseband::handleMessage(const Message& cmd)
{
if (MsgConfigureNoiseFigureBaseband::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureNoiseFigureBaseband& cfg = (MsgConfigureNoiseFigureBaseband&) cmd;
qDebug() << "NoiseFigureBaseband::handleMessage: MsgConfigureNoiseFigureBaseband";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
qDebug() << "NoiseFigureBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
setBasebandSampleRate(notif.getSampleRate());
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
m_channelizer->setChannelization(m_channelizer->getBasebandSampleRate(), 0);
return true;
}
else
{
return false;
}
}
void NoiseFigureBaseband::applySettings(const NoiseFigureSettings& settings, bool force)
{
m_sink.applySettings(settings, force);
m_settings = settings;
}
void NoiseFigureBaseband::setBasebandSampleRate(int sampleRate)
{
m_channelizer->setBasebandSampleRate(sampleRate);
m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset());
}

View File

@ -0,0 +1,98 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGUREBASEBAND_H
#define INCLUDE_NOISEFIGUREBASEBAND_H
#include <QObject>
#include <QMutex>
#include "dsp/samplesinkfifo.h"
#include "dsp/scopevis.h"
#include "util/message.h"
#include "util/messagequeue.h"
#include "noisefiguresink.h"
class DownChannelizer;
class ChannelAPI;
class NoiseFigure;
class NoiseFigureBaseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureNoiseFigureBaseband : public Message {
MESSAGE_CLASS_DECLARATION
public:
const NoiseFigureSettings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureNoiseFigureBaseband* create(const NoiseFigureSettings& settings, bool force)
{
return new MsgConfigureNoiseFigureBaseband(settings, force);
}
private:
NoiseFigureSettings m_settings;
bool m_force;
MsgConfigureNoiseFigureBaseband(const NoiseFigureSettings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
NoiseFigureBaseband(NoiseFigure *aisDemod);
~NoiseFigureBaseband();
void reset();
void startWork();
void stopWork();
void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void getMagSqLevels(double& avg, double& peak, int& nbSamples) {
m_sink.getMagSqLevels(avg, peak, nbSamples);
}
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); }
void setBasebandSampleRate(int sampleRate);
void setChannel(ChannelAPI *channel);
double getMagSq() const { return m_sink.getMagSq(); }
bool isRunning() const { return m_running; }
void startMeasurement() { m_sink.startMeasurement(); }
private:
SampleSinkFifo m_sampleFifo;
DownChannelizer *m_channelizer;
NoiseFigureSink m_sink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
NoiseFigureSettings m_settings;
bool m_running;
QMutex m_mutex;
bool handleMessage(const Message& cmd);
void calculateOffset(NoiseFigureSink *sink);
void applySettings(const NoiseFigureSettings& settings, bool force = false);
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_NOISEFIGUREBASEBAND_H

View File

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 "util/visa.h"
#include "noisefigurecontroldialog.h"
NoiseFigureControlDialog::NoiseFigureControlDialog(NoiseFigureSettings *settings, QWidget* parent) :
QDialog(parent),
m_settings(settings),
ui(new Ui::NoiseFigureControlDialog)
{
ui->setupUi(this);
ui->powerOnCommand->setText(settings->m_powerOnCommand);
ui->powerOffCommand->setText(settings->m_powerOffCommand);
ui->device->setText(settings->m_visaDevice);
ui->powerOnSCPI->setPlainText(settings->m_powerOnSCPI);
ui->powerOffSCPI->setPlainText(settings->m_powerOffSCPI);
ui->delay->setValue(settings->m_powerDelay);
VISA visa;
if (!visa.isAvailable())
{
ui->device->setEnabled(false);
ui->powerOnSCPI->setEnabled(false);
ui->powerOffSCPI->setEnabled(false);
}
}
NoiseFigureControlDialog::~NoiseFigureControlDialog()
{
delete ui;
}
void NoiseFigureControlDialog::accept()
{
m_settings->m_powerOnCommand = ui->powerOnCommand->text();
m_settings->m_powerOffCommand = ui->powerOffCommand->text();
m_settings->m_visaDevice = ui->device->text();
m_settings->m_powerOnSCPI = ui->powerOnSCPI->toPlainText();
m_settings->m_powerOffSCPI = ui->powerOffSCPI->toPlainText();
m_settings->m_powerDelay = ui->delay->value();
QDialog::accept();
}

View File

@ -0,0 +1,40 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGURECONTROLDIALOG_H
#define INCLUDE_NOISEFIGURECONTROLDIALOG_H
#include "ui_NoiseFigureControlDialog.h"
#include "noisefiguresettings.h"
class NoiseFigureControlDialog : public QDialog {
Q_OBJECT
public:
explicit NoiseFigureControlDialog(NoiseFigureSettings *settings, QWidget* parent = 0);
~NoiseFigureControlDialog();
NoiseFigureSettings *m_settings;
private slots:
void accept();
private:
Ui::NoiseFigureControlDialog* ui;
};
#endif // INCLUDE_NOISEFIGURECONTROLDIALOG_H

View File

@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NoiseFigureControlDialog</class>
<widget class="QDialog" name="NoiseFigureControlDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>514</width>
<height>531</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Noise Source Control Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="programsBox">
<property name="title">
<string>Programs / scripts</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="powerOnCommandLabel">
<property name="text">
<string>Power on</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="powerOnCommand">
<property name="toolTip">
<string>Program or script to execute to turn on the noise source's power</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="powerOffCommand">
<property name="toolTip">
<string>Program or script to execute to turn off the noise source's power</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="powerOffCommandLabel">
<property name="text">
<string>Power off</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="visaBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>VISA / SCPI</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="deviceLabel">
<property name="text">
<string>Device</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="device">
<property name="toolTip">
<string>VISA device address to send SCPI commands to.
E.g:
USB0::0x1234::0xabcd::DP8C1234::0::INSTR
TCPIP0::192.168.0.10::inst0::INSTR</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="powerOnSCPILabel">
<property name="text">
<string>Power On SCPI</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="powerOnSCPI">
<property name="toolTip">
<string>SCPI commands to power on noise source.
Lines beginning with # are comments</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="powerOffSCPILabel">
<property name="text">
<string>Power Off SCPI</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="powerOffSCPI">
<property name="toolTip">
<string>SCPI commands to power off noise source</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="timingBox">
<property name="title">
<string>Timing</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="delayLabel">
<property name="text">
<string>Delay in seconds betwen power on/off and measurement</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="delay">
<property name="toolTip">
<string>Delay in seconds between the noise source being powered on or off and when the measurement starts</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>powerOnCommand</tabstop>
<tabstop>powerOffCommand</tabstop>
<tabstop>device</tabstop>
<tabstop>powerOnSCPI</tabstop>
<tabstop>powerOffSCPI</tabstop>
<tabstop>delay</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NoiseFigureControlDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NoiseFigureControlDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,84 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 "noisefigureenrdialog.h"
NoiseFigureENRDialog::NoiseFigureENRDialog(NoiseFigureSettings *settings, QWidget* parent) :
QDialog(parent),
m_settings(settings),
ui(new Ui::NoiseFigureENRDialog)
{
ui->setupUi(this);
ui->enr->sortByColumn(0, Qt::AscendingOrder);
for (int i = 0; i < m_settings->m_enr.size(); i++) {
addRow( m_settings->m_enr[i]->m_frequency, m_settings->m_enr[i]->m_enr);
}
}
NoiseFigureENRDialog::~NoiseFigureENRDialog()
{
delete ui;
}
void NoiseFigureENRDialog::accept()
{
QDialog::accept();
qDeleteAll(m_settings->m_enr);
m_settings->m_enr.clear();
ui->enr->sortByColumn(0, Qt::AscendingOrder);
for (int i = 0; i < ui->enr->rowCount(); i++)
{
QTableWidgetItem *freqItem = ui->enr->item(i, ENR_COL_FREQ);
QTableWidgetItem *enrItem = ui->enr->item(i, ENR_COL_ENR);
double freqValue = freqItem->data(Qt::DisplayRole).toDouble();
double enrValue = enrItem->data(Qt::DisplayRole).toDouble();
NoiseFigureSettings::ENR *enr = new NoiseFigureSettings::ENR(freqValue, enrValue);
m_settings->m_enr.append(enr);
}
}
void NoiseFigureENRDialog::addRow(double freq, double enr)
{
ui->enr->setSortingEnabled(false);
int row = ui->enr->rowCount();
ui->enr->setRowCount(row + 1);
QTableWidgetItem *freqItem = new QTableWidgetItem();
QTableWidgetItem *enrItem = new QTableWidgetItem();
ui->enr->setItem(row, ENR_COL_FREQ, freqItem);
ui->enr->setItem(row, ENR_COL_ENR, enrItem);
freqItem->setData(Qt::DisplayRole, freq);
enrItem->setData(Qt::DisplayRole, enr);
ui->enr->setSortingEnabled(true);
}
void NoiseFigureENRDialog::on_addRow_clicked()
{
addRow(0.0, 0.0);
}
void NoiseFigureENRDialog::on_deleteRow_clicked()
{
QModelIndexList indexList = ui->enr->selectionModel()->selectedRows();
if (!indexList.isEmpty())
{
int row = indexList.at(0).row();
ui->enr->removeRow(row);
}
}

View File

@ -0,0 +1,48 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGUREENRDIALOG_H
#define INCLUDE_NOISEFIGUREENRDIALOG_H
#include "ui_NoiseFigureENRDialog.h"
#include "noisefiguresettings.h"
class NoiseFigureENRDialog : public QDialog {
Q_OBJECT
public:
explicit NoiseFigureENRDialog(NoiseFigureSettings *settings, QWidget* parent = 0);
~NoiseFigureENRDialog();
NoiseFigureSettings *m_settings;
enum ENRCol {
ENR_COL_FREQ,
ENR_COL_ENR
};
private slots:
void accept();
void on_addRow_clicked();
void on_deleteRow_clicked();
private:
Ui::NoiseFigureENRDialog* ui;
void addRow(double freq, double enr);
};
#endif // INCLUDE_NOISEFIGUREENRDIALOG_H

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NoiseFigureENRDialog</class>
<widget class="QDialog" name="NoiseFigureENRDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>284</width>
<height>496</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Excess Noise Ratio</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="enrLabel">
<property name="text">
<string>Enter calibrated ENRs for noise source</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="enr">
<property name="toolTip">
<string>ENR (Excess Noise Ratio) vs frequency table. Interpolation is used at frequencies not listed in the table.</string>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Freq (MHz)</string>
</property>
</column>
<column>
<property name="text">
<string>ENR (dB)</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="buttonsLayout">
<item>
<widget class="QToolButton" name="addRow">
<property name="toolTip">
<string>Add row</string>
</property>
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteRow">
<property name="toolTip">
<string>Delete row</string>
</property>
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<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>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
<include location="../demodapt/icons.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>NoiseFigureENRDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>NoiseFigureENRDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,775 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 <QMessageBox>
#include <QAction>
#include <QClipboard>
#include "noisefiguregui.h"
#include "device/deviceuiset.h"
#include "dsp/dspengine.h"
#include "dsp/dspcommands.h"
#include "ui_noisefiguregui.h"
#include "plugin/pluginapi.h"
#include "util/simpleserializer.h"
#include "util/db.h"
#include "gui/basicchannelsettingsdialog.h"
#include "gui/devicestreamselectiondialog.h"
#include "gui/crightclickenabler.h"
#include "maincore.h"
#include "noisefigure.h"
#include "noisefiguresink.h"
#include "noisefigurecontroldialog.h"
#include "noisefigureenrdialog.h"
// Deligate for table to control precision used to display floating point values
class DecimalDelegate : public QStyledItemDelegate {
public:
DecimalDelegate(int precision = 2) :
m_precision(precision)
{
}
virtual QString displayText(const QVariant &value, const QLocale &locale) const override
{
return QString::number(value.toDouble(), 'f', m_precision);
}
private:
int m_precision;
};
void NoiseFigureGUI::resizeTable()
{
// Fill table with a row of dummy data that will size the columns nicely
// Trailing spaces are for sort arrow
int row = ui->results->rowCount();
ui->results->setRowCount(row + 1);
ui->results->setItem(row, RESULTS_COL_FREQ, new QTableWidgetItem("2000.000"));
ui->results->setItem(row, RESULTS_COL_NF, new QTableWidgetItem("10.00"));
ui->results->setItem(row, RESULTS_COL_TEMP, new QTableWidgetItem("10000"));
ui->results->setItem(row, RESULTS_COL_Y, new QTableWidgetItem("10.00"));
ui->results->setItem(row, RESULTS_COL_ENR, new QTableWidgetItem("10.00"));
ui->results->resizeColumnsToContents();
ui->results->removeRow(row);
}
void NoiseFigureGUI::measurementReceived(NoiseFigure::MsgNFMeasurement& report)
{
ui->results->setSortingEnabled(false);
int row = ui->results->rowCount();
ui->results->setRowCount(row + 1);
QTableWidgetItem *freqItem = new QTableWidgetItem();
QTableWidgetItem *nfItem = new QTableWidgetItem();
QTableWidgetItem *tempItem = new QTableWidgetItem();
QTableWidgetItem *yItem = new QTableWidgetItem();
QTableWidgetItem *enrItem = new QTableWidgetItem();
ui->results->setItem(row, RESULTS_COL_FREQ, freqItem);
ui->results->setItem(row, RESULTS_COL_NF, nfItem);
ui->results->setItem(row, RESULTS_COL_TEMP, tempItem);
ui->results->setItem(row, RESULTS_COL_Y, yItem);
ui->results->setItem(row, RESULTS_COL_ENR, enrItem);
freqItem->setData(Qt::DisplayRole, report.getFrequency());
nfItem->setData(Qt::DisplayRole, report.getNF());
tempItem->setData(Qt::DisplayRole, report.getTemp());
yItem->setData(Qt::DisplayRole, report.getY());
enrItem->setData(Qt::DisplayRole, report.getENR());
ui->results->setSortingEnabled(true);
plotChart();
}
void NoiseFigureGUI::plotChart()
{
QChart *oldChart = m_chart;
m_chart = new QChart();
m_chart->layout()->setContentsMargins(0, 0, 0, 0);
m_chart->setMargins(QMargins(1, 1, 1, 1));
m_chart->setTheme(QChart::ChartThemeDark);
// Create reference data series
QLineSeries *ref = nullptr;
if ((m_refData.size() > 0) && (ui->chartSelect->currentIndex() < m_refCols-1)) {
ref = new QLineSeries();
for (int i = 0; i < m_refData.size() / m_refCols; i++) {
ref->append(m_refData[i*m_refCols], m_refData[i*m_refCols+ui->chartSelect->currentIndex()+1]);
}
QFileInfo fi(m_refFilename);
ref->setName(fi.completeBaseName());
} else {
m_chart->legend()->hide();
}
// Create measurement data series
QLineSeries *series = new QLineSeries();
series->setName("Measurement");
for (int i = 0; i < ui->results->rowCount(); i++)
{
double freq = ui->results->item(i, RESULTS_COL_FREQ)->data(Qt::DisplayRole).toDouble();
double val = ui->results->item(i, ui->chartSelect->currentIndex() + RESULTS_COL_NF)->data(Qt::DisplayRole).toDouble();
series->append(freq, val);
}
QValueAxis *xAxis = new QValueAxis();
QValueAxis *yAxis = new QValueAxis();
m_chart->addAxis(xAxis, Qt::AlignBottom);
m_chart->addAxis(yAxis, Qt::AlignLeft);
xAxis->setTitleText("Frequency (MHz)");
yAxis->setTitleText(ui->chartSelect->currentText());
m_chart->addSeries(series);
series->attachAxis(xAxis);
series->attachAxis(yAxis);
if (ref)
{
m_chart->addSeries(ref);
ref->attachAxis(xAxis);
ref->attachAxis(yAxis);
}
ui->chart->setChart(m_chart);
delete oldChart;
}
void NoiseFigureGUI::on_chartSelect_currentIndexChanged(int index)
{
(void) index;
plotChart();
}
// Columns in table reordered
void NoiseFigureGUI::results_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
{
(void) oldVisualIndex;
m_settings.m_resultsColumnIndexes[logicalIndex] = newVisualIndex;
}
// Column in table resized (when hidden size is 0)
void NoiseFigureGUI::results_sectionResized(int logicalIndex, int oldSize, int newSize)
{
(void) oldSize;
m_settings.m_resultsColumnSizes[logicalIndex] = newSize;
}
// Right click in table header - show column select menu
void NoiseFigureGUI::resultsColumnSelectMenu(QPoint pos)
{
resultsMenu->popup(ui->results->horizontalHeader()->viewport()->mapToGlobal(pos));
}
// Hide/show column when menu selected
void NoiseFigureGUI::resultsColumnSelectMenuChecked(bool checked)
{
(void) checked;
QAction* action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
int idx = action->data().toInt(nullptr);
ui->results->setColumnHidden(idx, !action->isChecked());
}
}
// Create column select menu item
QAction *NoiseFigureGUI::createCheckableItem(QString &text, int idx, bool checked, const char *slot)
{
QAction *action = new QAction(text, this);
action->setCheckable(true);
action->setChecked(checked);
action->setData(QVariant(idx));
connect(action, SIGNAL(triggered()), this, slot);
return action;
}
NoiseFigureGUI* NoiseFigureGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
NoiseFigureGUI* gui = new NoiseFigureGUI(pluginAPI, deviceUISet, rxChannel);
return gui;
}
void NoiseFigureGUI::destroy()
{
delete this;
}
void NoiseFigureGUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray NoiseFigureGUI::serialize() const
{
return m_settings.serialize();
}
bool NoiseFigureGUI::deserialize(const QByteArray& data)
{
if(m_settings.deserialize(data)) {
displaySettings();
applySettings(true);
return true;
} else {
resetToDefaults();
return false;
}
}
// Call when sample rate or FFT size changes
void NoiseFigureGUI::updateBW()
{
// We use a single bin for measurement
double bw = m_basebandSampleRate / (double)m_settings.m_fftSize;
ui->rfBWText->setText(QString("%1k").arg(bw / 1000.0, 0, 'f', 1));
m_channelMarker.setBandwidth(bw);
// TODO: Could display total measurement time
}
bool NoiseFigureGUI::handleMessage(const Message& message)
{
if (NoiseFigure::MsgConfigureNoiseFigure::match(message))
{
qDebug("NoiseFigureGUI::handleMessage: NoiseFigure::MsgConfigureNoiseFigure");
const NoiseFigure::MsgConfigureNoiseFigure& cfg = (NoiseFigure::MsgConfigureNoiseFigure&) message;
m_settings = cfg.getSettings();
blockApplySettings(true);
displaySettings();
blockApplySettings(false);
return true;
}
else if (DSPSignalNotification::match(message))
{
DSPSignalNotification& notif = (DSPSignalNotification&) message;
m_basebandSampleRate = notif.getSampleRate();
updateBW();
return true;
}
else if (NoiseFigure::MsgNFMeasurement::match(message))
{
NoiseFigure::MsgNFMeasurement& report = (NoiseFigure::MsgNFMeasurement&) message;
measurementReceived(report);
return true;
}
else if (NoiseFigure::MsgFinished::match(message))
{
NoiseFigure::MsgFinished& report = (NoiseFigure::MsgFinished&) message;
ui->startStop->setChecked(false);
m_runningTest = false;
QString errorMessage = report.getErrorMessage();
if (!errorMessage.isEmpty()) {
QMessageBox::critical(this, "Noise Figure", errorMessage);
}
return true;
}
return false;
}
void NoiseFigureGUI::handleInputMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void NoiseFigureGUI::channelMarkerChangedByCursor()
{
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void NoiseFigureGUI::channelMarkerHighlightedByCursor()
{
setHighlighted(m_channelMarker.getHighlighted());
}
void NoiseFigureGUI::on_deltaFrequency_changed(qint64 value)
{
m_channelMarker.setCenterFrequency(value);
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
applySettings();
}
void NoiseFigureGUI::on_fftCount_valueChanged(int value)
{
m_settings.m_fftCount = 10000 * value;
ui->fftCountText->setText(QString("%1k").arg(m_settings.m_fftCount / 1000));
applySettings();
}
void NoiseFigureGUI::updateFreqWidgets()
{
bool range = m_settings.m_frequencySpec == NoiseFigureSettings::RANGE;
bool step = m_settings.m_frequencySpec == NoiseFigureSettings::STEP;
bool list = m_settings.m_frequencySpec == NoiseFigureSettings::LIST;
ui->startLabel->setVisible(range || step);
ui->start->setVisible(range || step);
ui->stopLabel->setVisible(range || step);
ui->stop->setVisible(range || step);
ui->stepsLabel->setVisible(range);
ui->steps->setVisible(range);
ui->stepLabel->setVisible(step);
ui->step->setVisible(step);
ui->frequenciesLabel->setVisible(list);
ui->frequencies->setVisible(list);
}
void NoiseFigureGUI::on_frequencySpec_currentIndexChanged(int index)
{
m_settings.m_frequencySpec = (NoiseFigureSettings::FrequencySpec)index;
updateFreqWidgets();
applySettings();
}
void NoiseFigureGUI::on_start_valueChanged(double value)
{
m_settings.m_startFrequency = value;
applySettings();
}
void NoiseFigureGUI::on_stop_valueChanged(double value)
{
m_settings.m_stopFrequency = value;
applySettings();
}
void NoiseFigureGUI::on_steps_valueChanged(int value)
{
m_settings.m_steps = value;
applySettings();
}
void NoiseFigureGUI::on_step_valueChanged(double value)
{
m_settings.m_step = value;
applySettings();
}
void NoiseFigureGUI::on_frequencies_editingFinished()
{
m_settings.m_frequencies = ui->frequencies->text().trimmed();
applySettings();
}
void NoiseFigureGUI::on_fftSize_currentIndexChanged(int index)
{
m_settings.m_fftSize = 1 << (index + 6);
updateBW();
applySettings();
}
void NoiseFigureGUI::on_startStop_clicked()
{
// Check we have at least on ENR value
if (m_settings.m_enr.size() < 1)
{
QMessageBox::critical(this, "Noise Figure", "You must enter the ENR of the noise source for at least one frequency");
return;
}
// Clear current results if starting a test
if (!m_runningTest)
{
on_clearResults_clicked();
m_runningTest = true;
}
// Send message to start/stop test
NoiseFigure::MsgStartStop* message = NoiseFigure::MsgStartStop::create();
m_noiseFigure->getInputMessageQueue()->push(message);
}
// Save results in table to a CSV file
void NoiseFigureGUI::on_saveResults_clicked()
{
// Get filename to save to
QFileDialog fileDialog(nullptr, "Select file to save results to", "", "*.csv");
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
if (fileDialog.exec())
{
QStringList fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
qDebug() << "NoiseFigureGUI: Saving results to " << fileNames;
QFile file(fileNames[0]);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(this, "Noise Figure", QString("Failed to open file %1").arg(fileNames[0]));
return;
}
QTextStream out(&file);
// Create a CSV file from the values in the table
out << "Frequency (MHz),NF (dB),Noise Temp (K),Y (dB),ENR (dB)\n";
for (int i = 0; i < ui->results->rowCount(); i++)
{
for (int j = 0; j < NOISEFIGURE_COLUMNS; j++)
{
double val = ui->results->item(i,j)->data(Qt::DisplayRole).toDouble();
out << val << ",";
}
out << "\n";
}
}
}
}
// Open .csv containing reference data to plot
void NoiseFigureGUI::on_openReference_clicked()
{
QFileDialog fileDialog(nullptr, "Open file to plot", "", "*.csv");
if (fileDialog.exec())
{
QStringList fileNames = fileDialog.selectedFiles();
if (fileNames.size() > 0)
{
m_refFilename = fileNames[0];
QFile file(m_refFilename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::critical(this, "Noise Figure", QString("Failed to open file %1").arg(m_refFilename));
return;
}
QTextStream in(&file);
// Parse CSV file
QString headerLine = in.readLine();
m_refCols = headerLine.split(",").size();
m_refData.clear();
QString line;
while (!(line = in.readLine()).isNull())
{
QStringList cols = line.split(",");
for (int i = 0; i < m_refCols; i++)
{
if (i < cols.size()) {
bool ok;
m_refData.append(cols[i].toDouble(&ok));
if (!ok) {
qDebug() << "NoiseFigureGUI::on_openReference_clicked: Error parsing " << cols[i] << " as a double";
}
} else {
m_refData.append(0.0);
}
}
}
plotChart();
}
}
}
void NoiseFigureGUI::on_clearReference_clicked()
{
m_refFilename = "";
m_refData.clear();
m_refCols = 0;
plotChart();
}
void NoiseFigureGUI::on_clearResults_clicked()
{
ui->results->setRowCount(0);
plotChart();
}
void NoiseFigureGUI::on_enr_clicked()
{
NoiseFigureENRDialog dialog(&m_settings);
if (dialog.exec() == QDialog::Accepted)
{
applySettings();
}
}
void NoiseFigureGUI::on_control_clicked()
{
NoiseFigureControlDialog dialog(&m_settings);
if (dialog.exec() == QDialog::Accepted)
{
applySettings();
}
}
void NoiseFigureGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
}
void NoiseFigureGUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, 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.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.move(p);
dialog.exec();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
else if ((m_contextMenuType == ContextMenuStreamSettings) && (m_deviceUISet->m_deviceMIMOEngine))
{
DeviceStreamSelectionDialog dialog(this);
dialog.setNumberOfStreams(m_noiseFigure->getNumberOfDeviceStreams());
dialog.setStreamIndex(m_settings.m_streamIndex);
dialog.move(p);
dialog.exec();
m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
m_channelMarker.clearStreamIndexes();
m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
displayStreamIndex();
applySettings();
}
resetContextMenuType();
}
NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::NoiseFigureGUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_channelMarker(this),
m_doApplySettings(true),
m_basebandSampleRate(1000000),
m_tickCount(0),
m_runningTest(false),
m_chart(nullptr)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_noiseFigure = reinterpret_cast<NoiseFigure*>(rxChannel);
m_noiseFigure->setMessageQueueToGUI(getInputMessageQueue());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
m_channelMarker.blockSignals(true);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setBandwidth(m_basebandSampleRate / m_settings.m_fftSize);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle("Noise Figure");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
setTitleColor(m_channelMarker.getColor());
m_settings.setChannelMarker(&m_channelMarker);
m_deviceUISet->addChannelMarker(&m_channelMarker);
m_deviceUISet->addRollupWidget(this);
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
// Resize the table using dummy data
resizeTable();
// Allow user to reorder columns
ui->results->horizontalHeader()->setSectionsMovable(true);
// Allow user to sort table by clicking on headers
ui->results->setSortingEnabled(true);
// Add context menu to allow hiding/showing of columns
resultsMenu = new QMenu(ui->results);
for (int i = 0; i < ui->results->horizontalHeader()->count(); i++)
{
QString text = ui->results->horizontalHeaderItem(i)->text();
resultsMenu->addAction(createCheckableItem(text, i, true, SLOT(resultsColumnSelectMenuChecked())));
}
ui->results->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->results->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(resultsColumnSelectMenu(QPoint)));
// Get signals when columns change
connect(ui->results->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(results_sectionMoved(int, int, int)));
connect(ui->results->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(results_sectionResized(int, int, int)));
ui->results->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->results, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customContextMenuRequested(QPoint)));
ui->results->setItemDelegateForColumn(RESULTS_COL_NF, new DecimalDelegate(2));
ui->results->setItemDelegateForColumn(RESULTS_COL_TEMP, new DecimalDelegate(0));
ui->results->setItemDelegateForColumn(RESULTS_COL_Y, new DecimalDelegate(2));
ui->results->setItemDelegateForColumn(RESULTS_COL_ENR, new DecimalDelegate(2));
displaySettings();
applySettings(true);
}
void NoiseFigureGUI::customContextMenuRequested(QPoint pos)
{
QTableWidgetItem *item = ui->results->itemAt(pos);
if (item)
{
QMenu* tableContextMenu = new QMenu(ui->results);
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
QAction* copyAction = new QAction("Copy", tableContextMenu);
const QString text = item->text();
connect(copyAction, &QAction::triggered, this, [text]()->void {
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(text);
});
tableContextMenu->addAction(copyAction);
tableContextMenu->popup(ui->results->viewport()->mapToGlobal(pos));
}
}
NoiseFigureGUI::~NoiseFigureGUI()
{
delete ui;
}
void NoiseFigureGUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void NoiseFigureGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
NoiseFigure::MsgConfigureNoiseFigure* message = NoiseFigure::MsgConfigureNoiseFigure::create( m_settings, force);
m_noiseFigure->getInputMessageQueue()->push(message);
}
}
void NoiseFigureGUI::displaySettings()
{
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->fftCountText->setText(QString("%1k").arg(m_settings.m_fftCount / 1000));
ui->fftCount->setValue(m_settings.m_fftCount / 10000);
ui->frequencySpec->setCurrentIndex((int)m_settings.m_frequencySpec);
updateFreqWidgets();
ui->start->setValue(m_settings.m_startFrequency);
ui->stop->setValue(m_settings.m_stopFrequency);
ui->steps->setValue(m_settings.m_steps);
ui->step->setValue(m_settings.m_step);
ui->frequencies->setText(m_settings.m_frequencies);
ui->fftSize->setCurrentIndex(log2(m_settings.m_fftSize) - 6);
updateBW();
displayStreamIndex();
// Order and size columns
QHeaderView *header = ui->results->horizontalHeader();
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++)
{
bool hidden = m_settings.m_resultsColumnSizes[i] == 0;
header->setSectionHidden(i, hidden);
resultsMenu->actions().at(i)->setChecked(!hidden);
if (m_settings.m_resultsColumnSizes[i] > 0) {
ui->results->setColumnWidth(i, m_settings.m_resultsColumnSizes[i]);
}
header->moveSection(header->visualIndex(i), m_settings.m_resultsColumnIndexes[i]);
}
blockApplySettings(false);
}
void NoiseFigureGUI::displayStreamIndex()
{
if (m_deviceUISet->m_deviceMIMOEngine) {
setStreamIndicator(tr("%1").arg(m_settings.m_streamIndex));
} else {
setStreamIndicator("S"); // single channel indicator
}
}
void NoiseFigureGUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);
}
void NoiseFigureGUI::enterEvent(QEvent*)
{
m_channelMarker.setHighlighted(true);
}
void NoiseFigureGUI::tick()
{
double magsqAvg, magsqPeak;
int nbMagsqSamples;
m_noiseFigure->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
double powDbAvg = CalcDb::dbPower(magsqAvg);
double powDbPeak = CalcDb::dbPower(magsqPeak);
ui->channelPowerMeter->levelChanged(
(100.0f + powDbAvg) / 100.0f,
(100.0f + powDbPeak) / 100.0f,
nbMagsqSamples);
if (m_tickCount % 4 == 0) {
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
}
m_tickCount++;
}

View File

@ -0,0 +1,141 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGUREGUI_H
#define INCLUDE_NOISEFIGUREGUI_H
#include <QTableWidgetItem>
#include <QHBoxLayout>
#include <QMenu>
#include <QtCharts>
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "util/messagequeue.h"
#include "noisefiguresettings.h"
#include "noisefigure.h"
class PluginAPI;
class DeviceUISet;
class BasebandSampleSink;
class NoiseFigure;
class NoiseFigureGUI;
namespace Ui {
class NoiseFigureGUI;
}
using namespace QtCharts;
class NoiseFigureGUI : public ChannelGUI {
Q_OBJECT
public:
static NoiseFigureGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
public slots:
void channelMarkerChangedByCursor();
void channelMarkerHighlightedByCursor();
private:
Ui::NoiseFigureGUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
NoiseFigureSettings m_settings;
bool m_doApplySettings;
NoiseFigure* m_noiseFigure;
int m_basebandSampleRate;
uint32_t m_tickCount;
bool m_runningTest;
MessageQueue m_inputMessageQueue;
QMenu *resultsMenu; //<! Column select context menu
QMenu *copyMenu; //<! Copy menu
QChart *m_chart;
QString m_refFilename; //<! Reference data to plot on chart
QVector<double> m_refData;
int m_refCols;
explicit NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~NoiseFigureGUI();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void displaySettings();
void displayStreamIndex();
bool handleMessage(const Message& message);
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
void resizeTable();
QAction *createCheckableItem(QString& text, int idx, bool checked, const char *slot);
void updateBW();
void updateFreqWidgets();
void measurementReceived(NoiseFigure::MsgNFMeasurement& report);
void plotChart();
enum MessageCol {
RESULTS_COL_FREQ,
RESULTS_COL_NF,
RESULTS_COL_TEMP,
RESULTS_COL_Y,
RESULTS_COL_ENR
};
private slots:
void on_deltaFrequency_changed(qint64 value);
void on_fftCount_valueChanged(int value);
void on_frequencySpec_currentIndexChanged(int index);
void on_start_valueChanged(double value);
void on_stop_valueChanged(double value);
void on_steps_valueChanged(int value);
void on_step_valueChanged(double value);
void on_frequencies_editingFinished();
void on_fftSize_currentIndexChanged(int index);
void on_startStop_clicked();
void on_saveResults_clicked();
void on_clearResults_clicked();
void on_enr_clicked();
void on_control_clicked();
void on_chartSelect_currentIndexChanged(int index);
void on_openReference_clicked();
void on_clearReference_clicked();
void results_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void results_sectionResized(int logicalIndex, int oldSize, int newSize);
void resultsColumnSelectMenu(QPoint pos);
void resultsColumnSelectMenuChecked(bool checked = false);
void customContextMenuRequested(QPoint point);
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
};
#endif // INCLUDE_NOISEFIGUREGUI_H

View File

@ -0,0 +1,868 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NoiseFigureGUI</class>
<widget class="RollupWidget" name="NoiseFigureGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>642</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>352</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="windowTitle">
<string>AIS Demodulator</string>
</property>
<property name="statusTip">
<string/>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>390</width>
<height>151</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>350</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Settings</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="powLayout">
<property name="topMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="deltaFrequencyLabel">
<property name="minimumSize">
<size>
<width>16</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Df</string>
</property>
</widget>
</item>
<item>
<widget class="ValueDialZ" name="deltaFrequency" 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>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Measurement frequency from center in Hz</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="deltaUnits">
<property name="text">
<string>Hz </string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<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>
<layout class="QHBoxLayout" name="channelPowerLayout">
<item>
<widget class="QLabel" name="channelPower">
<property name="toolTip">
<string>Channel power</string>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>0.0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="channelPowerUnits">
<property name="text">
<string> dB</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="powerLayout">
<item>
<widget class="QLabel" name="channelPowerMeterUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<widget class="LevelMeterSignalDB" name="channelPowerMeter" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>24</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>8</pointsize>
</font>
</property>
<property name="toolTip">
<string>Level meter (dB) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="phySettingsLayout">
<item>
<widget class="QLabel" name="fftSizeLabel">
<property name="text">
<string>FFT Size</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fftSize">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>FFT size (number of bins)</string>
</property>
<property name="currentText">
<string>64</string>
</property>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>512</string>
</property>
</item>
<item>
<property name="text">
<string>1k</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWLabel">
<property name="text">
<string>BW</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Measurement bandwidth as determined by width of a single FFT bin</string>
</property>
<property name="text">
<string>10.0k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fftCountLabel">
<property name="text">
<string>FFTs to average</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="fftCount">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Number of FFT outputs to average to calculate power</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fftCountText">
<property name="minimumSize">
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>2.4k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="freqLayout">
<item>
<widget class="QComboBox" name="frequencySpec">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>68</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>How to specify frequencies to measure NF at</string>
</property>
<item>
<property name="text">
<string>Range</string>
</property>
</item>
<item>
<property name="text">
<string>Step</string>
</property>
</item>
<item>
<property name="text">
<string>List</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="startLabel">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="start">
<property name="toolTip">
<string>Start frequency in MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="stopLabel">
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="stop">
<property name="toolTip">
<string>Stop frequency in MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="stepsLabel">
<property name="text">
<string>Steps</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="steps">
<property name="toolTip">
<string>Number of frequencies to measure at between start and stop inclusive</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="stepLabel">
<property name="text">
<string>Step</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="step">
<property name="toolTip">
<string>Frequency step in MHz</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="frequenciesLabel">
<property name="text">
<string>Freqs</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="frequencies">
<property name="toolTip">
<string>List of frequencies in MHz to measure NF at</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="filterLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>Start/stop noise figure measurement</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>
<item>
<widget class="QToolButton" name="saveResults">
<property name="toolTip">
<string>Save results to a .csv file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clearResults">
<property name="toolTip">
<string>Clear results</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</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="QToolButton" name="enr">
<property name="toolTip">
<string>Open ENR dialog</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="control">
<property name="toolTip">
<string>Open noise source control dialog</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/link.png</normaloff>:/link.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="resultsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>450</y>
<width>391</width>
<height>171</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Results</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutTable">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="QTableWidget" name="results">
<property name="toolTip">
<string>Received packets</string>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<column>
<property name="text">
<string>Freq (MHz)</string>
</property>
<property name="toolTip">
<string>Frequency in MHz the noise figure was measured at</string>
</property>
</column>
<column>
<property name="text">
<string>NF (dB)</string>
</property>
<property name="toolTip">
<string>Noise figure in dB</string>
</property>
</column>
<column>
<property name="text">
<string>T (K)</string>
</property>
<property name="toolTip">
<string>Noise temperature in Kelvin</string>
</property>
</column>
<column>
<property name="text">
<string>Y (dB)</string>
</property>
<property name="toolTip">
<string>Y factor in dB</string>
</property>
</column>
<column>
<property name="text">
<string>ENR (dB)</string>
</property>
<property name="toolTip">
<string>Excess noise ratio of the noise source in dB</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="chartContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>160</y>
<width>391</width>
<height>268</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>200</height>
</size>
</property>
<property name="windowTitle">
<string>Chart</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="chartSelect">
<property name="toolTip">
<string>Select data to plot on chart</string>
</property>
<item>
<property name="text">
<string>Noise Figure (dB)</string>
</property>
</item>
<item>
<property name="text">
<string>Noise Temperature (K)</string>
</property>
</item>
<item>
<property name="text">
<string>Y (dB)</string>
</property>
</item>
<item>
<property name="text">
<string>ENR (dB)</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QToolButton" name="openReference">
<property name="toolTip">
<string>Open .csv file with reference data</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/load.png</normaloff>:/load.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clearReference">
<property name="toolTip">
<string>Clear reference data</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QChartView" name="chart">
<property name="minimumSize">
<size>
<width>300</width>
<height>250</height>
</size>
</property>
<property name="toolTip">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
<header>gui/rollupwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LevelMeterSignalDB</class>
<extends>QWidget</extends>
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>QChartView</class>
<extends>QGraphicsView</extends>
<header>QtCharts</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>deltaFrequency</tabstop>
<tabstop>fftSize</tabstop>
<tabstop>fftCount</tabstop>
<tabstop>start</tabstop>
<tabstop>stop</tabstop>
<tabstop>steps</tabstop>
<tabstop>startStop</tabstop>
<tabstop>saveResults</tabstop>
<tabstop>clearResults</tabstop>
<tabstop>enr</tabstop>
<tabstop>control</tabstop>
<tabstop>chartSelect</tabstop>
<tabstop>chart</tabstop>
<tabstop>results</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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"
#ifndef SERVER_MODE
#include "noisefiguregui.h"
#endif
#include "noisefigure.h"
#include "noisefigurewebapiadapter.h"
#include "noisefigureplugin.h"
const PluginDescriptor NoiseFigurePlugin::m_pluginDescriptor = {
NoiseFigure::m_channelId,
QStringLiteral("Noise Figure"),
QStringLiteral("6.13.1"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
NoiseFigurePlugin::NoiseFigurePlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& NoiseFigurePlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void NoiseFigurePlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerRxChannel(NoiseFigure::m_channelIdURI, NoiseFigure::m_channelId, this);
}
void NoiseFigurePlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
NoiseFigure *instance = new NoiseFigure(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* NoiseFigurePlugin::createRxChannelGUI(
DeviceUISet *deviceUISet,
BasebandSampleSink *rxChannel) const
{
(void) deviceUISet;
(void) rxChannel;
return 0;
}
#else
ChannelGUI* NoiseFigurePlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const
{
return NoiseFigureGUI::create(m_pluginAPI, deviceUISet, rxChannel);
}
#endif
ChannelWebAPIAdapter* NoiseFigurePlugin::createChannelWebAPIAdapter() const
{
return new NoiseFigureWebAPIAdapter();
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGUREPLUGIN_H
#define INCLUDE_NOISEFIGUREPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class BasebandSampleSink;
class NoiseFigurePlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channel.noisefigure")
public:
explicit NoiseFigurePlugin(QObject* parent = NULL);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_NOISEFIGUREPLUGIN_H

View File

@ -0,0 +1,222 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 <QColor>
#include <QDataStream>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "noisefiguresettings.h"
NoiseFigureSettings::NoiseFigureSettings() :
m_channelMarker(0)
{
resetToDefaults();
}
#define DEFAULT_FREQUENCIES "430 435 440"
#define DEFAULT_VISA_DEVICE "USB0::0x1AB1::0x0E11::DP8C155102576::0::INSTR"
#define DEFAULT_POWER_ON "#:SOURCE1:VOLTage 28\n:OUTPut:STATe CH1,ON"
#define DEFAULT_POWER_OFF ":OUTPut:STATe CH1,OFF"
void NoiseFigureSettings::resetToDefaults()
{
m_inputFrequencyOffset = 0;
m_fftSize = 64;
m_fftCount = 20000.0f;
m_frequencySpec = RANGE;
m_startFrequency = 430.0;
m_stopFrequency = 440.0;
m_steps = 3;
m_step = 5.0f;
m_frequencies = DEFAULT_FREQUENCIES;
m_visaDevice = DEFAULT_VISA_DEVICE;
m_powerOnSCPI = DEFAULT_POWER_ON;
m_powerOffSCPI = DEFAULT_POWER_OFF;
m_powerOnCommand = "";
m_powerOffCommand = "";
m_powerDelay = 0.5;
qDeleteAll(m_enr);
m_enr << new ENR(1000.0, 15.0);
m_rgbColor = QColor(0, 100, 200).rgb();
m_title = "Noise Figure";
m_streamIndex = 0;
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++)
{
m_resultsColumnIndexes[i] = i;
m_resultsColumnSizes[i] = -1; // Autosize
}
}
QByteArray NoiseFigureSettings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, m_fftSize);
s.writeFloat(3, m_fftCount);
s.writeS32(4, (int)m_frequencySpec);
s.writeDouble(5, m_startFrequency);
s.writeDouble(6, m_stopFrequency);
s.writeS32(7, m_steps);
s.writeDouble(8, m_step);
s.writeString(9, m_frequencies);
s.writeString(10, m_visaDevice);
s.writeString(11, m_powerOnSCPI);
s.writeString(12, m_powerOffSCPI);
s.writeString(13, m_powerOffCommand);
s.writeString(14, m_powerOffCommand);
s.writeDouble(15, m_powerDelay);
s.writeBlob(16, serializeENRs(m_enr));
s.writeU32(17, m_rgbColor);
s.writeString(18, m_title);
if (m_channelMarker) {
s.writeBlob(19, m_channelMarker->serialize());
}
s.writeS32(20, m_streamIndex);
s.writeBool(21, m_useReverseAPI);
s.writeString(22, m_reverseAPIAddress);
s.writeU32(23, m_reverseAPIPort);
s.writeU32(24, m_reverseAPIDeviceIndex);
s.writeU32(25, m_reverseAPIChannelIndex);
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) {
s.writeS32(100 + i, m_resultsColumnIndexes[i]);
}
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) {
s.writeS32(200 + i, m_resultsColumnSizes[i]);
}
return s.final();
}
bool NoiseFigureSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
QByteArray blob;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readS32(2, &m_fftSize, 64);
d.readFloat(3, &m_fftCount, 10000.0f);
d.readS32(4, (int*)&m_frequencySpec, NoiseFigureSettings::RANGE);
d.readDouble(5, &m_startFrequency, 430.0);
d.readDouble(6, &m_stopFrequency, 440.0);
d.readS32(7, &m_steps, 3);
d.readDouble(8, &m_step, 5.0);
d.readString(9, &m_frequencies, DEFAULT_FREQUENCIES);
d.readString(10, &m_visaDevice, DEFAULT_VISA_DEVICE);
d.readString(11, &m_powerOnSCPI, DEFAULT_POWER_ON);
d.readString(12, &m_powerOffSCPI, DEFAULT_POWER_OFF);
d.readString(13, &m_powerOnCommand, "");
d.readString(14, &m_powerOffCommand, "");
d.readDouble(15, &m_powerDelay, 0.5);
d.readBlob(16, &blob);
deserializeENRs(blob, m_enr);
d.readU32(17, &m_rgbColor, QColor(0, 100, 200).rgb());
d.readString(18, &m_title, "Noise Figure");
d.readBlob(19, &bytetmp);
if (m_channelMarker) {
m_channelMarker->deserialize(bytetmp);
}
d.readS32(20, &m_streamIndex, 0);
d.readBool(21, &m_useReverseAPI, false);
d.readString(22, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(23, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(24, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(25, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) {
d.readS32(100 + i, &m_resultsColumnIndexes[i], i);
}
for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) {
d.readS32(200 + i, &m_resultsColumnSizes[i], -1);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}
QDataStream& operator<<(QDataStream& out, const NoiseFigureSettings::ENR* enr)
{
out << enr->m_frequency;
out << enr->m_enr;
return out;
}
QDataStream& operator>>(QDataStream& in, NoiseFigureSettings::ENR*& enr)
{
enr = new NoiseFigureSettings::ENR();
in >> enr->m_frequency;
in >> enr->m_enr;
return in;
}
QByteArray NoiseFigureSettings::serializeENRs(QList<ENR *> enrs) const
{
QByteArray data;
QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly);
(*stream) << enrs;
delete stream;
return data;
}
void NoiseFigureSettings::deserializeENRs(const QByteArray& data, QList<ENR *>& enrs)
{
QDataStream *stream = new QDataStream(data);
(*stream) >> enrs;
delete stream;
}

View File

@ -0,0 +1,95 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGURESETTINGS_H
#define INCLUDE_NOISEFIGURESETTINGS_H
#include <QByteArray>
#include <QString>
#include <QList>
#include "dsp/dsptypes.h"
class Serializable;
// Number of columns in the table
#define NOISEFIGURE_COLUMNS 5
struct NoiseFigureSettings
{
struct ENR
{
double m_frequency; //!< Frequency in MHz
double m_enr; //!< ENR in dB
ENR() :
m_frequency(0.0),
m_enr(0.0)
{ }
ENR(double frequency, double enr) :
m_frequency(frequency),
m_enr(enr)
{ }
};
qint32 m_inputFrequencyOffset;
int m_fftSize;
Real m_fftCount; //!< Number of FFT bins to average
enum FrequencySpec {
RANGE,
STEP,
LIST
} m_frequencySpec;
double m_startFrequency;
double m_stopFrequency;
int m_steps;
double m_step;
QString m_frequencies;
QString m_visaDevice;
QString m_powerOnSCPI;
QString m_powerOffSCPI;
QString m_powerOnCommand;
QString m_powerOffCommand;
double m_powerDelay; //<! Delay in seconds before starting a measurement
QList<ENR *> m_enr;
quint32 m_rgbColor;
QString m_title;
Serializable *m_channelMarker;
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
int m_resultsColumnIndexes[NOISEFIGURE_COLUMNS];//!< How the columns are ordered in the table
int m_resultsColumnSizes[NOISEFIGURE_COLUMNS]; //!< Size of the columns in the table
NoiseFigureSettings();
void resetToDefaults();
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
QByteArray serializeENRs(QList<ENR *> enrs) const;
void deserializeENRs(const QByteArray& data, QList<ENR *>& enrs);
};
#endif /* INCLUDE_NOISEFIGURESETTINGS_H */

View File

@ -0,0 +1,154 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 <complex.h>
#include "dsp/dspengine.h"
#include "noisefigure.h"
#include "noisefiguresink.h"
NoiseFigureSink::NoiseFigureSink(NoiseFigure *noiseFigure) :
m_noiseFigure(noiseFigure),
m_channelSampleRate(48000),
m_fftSequence(-1),
m_fft(nullptr),
m_fftCounter(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_powerSum(0.0),
m_count(0),
m_enabled(false)
{
m_magsq = 0.0;
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, 0, true);
}
NoiseFigureSink::~NoiseFigureSink()
{
}
void NoiseFigureSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
processOneSample(c);
}
}
void NoiseFigureSink::processOneSample(Complex &ci)
{
// Add to FFT input buffer
m_fft->in()[m_fftCounter] = Complex(ci.real() / SDR_RX_SCALEF, ci.imag() / SDR_RX_SCALEF);
m_fftCounter++;
if (m_fftCounter == m_settings.m_fftSize)
{
// Calculate FFT (note no windowing as input should be broadband noise)
m_fft->transform();
m_fftCounter = 0;
// Calculate power in FFT bin selected by input frequency offset
double frequencyResolution = m_channelSampleRate / (double)m_settings.m_fftSize;
int bin;
if (m_settings.m_inputFrequencyOffset >= 0) {
bin = m_settings.m_inputFrequencyOffset / frequencyResolution;
} else {
bin = m_settings.m_fftSize + m_settings.m_inputFrequencyOffset / frequencyResolution;
}
Complex c = m_fft->out()[bin];
Real v = c.real() * c.real() + c.imag() * c.imag();
// Calculate average and peak levels for level meter
Real magsq = v / (m_settings.m_fftSize*m_settings.m_fftSize);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
if (m_enabled)
{
// Average power for measurement
m_powerSum += v;
m_count++;
if (m_count == m_settings.m_fftCount)
{
// Convert average to dB
// This is 10*log10(p/(1/fftSize)^2) optimised to not use log10 in the loop
const Real mult = (10.0f / log2(10.0f)); // ~3.01
Real ofs = 20.0f * log10f(1.0f / m_settings.m_fftSize);
Real avg = mult * log2f(m_powerSum / m_count) + ofs;
// Send NF results to channel
if (getMessageQueueToChannel())
{
NoiseFigure::MsgPowerMeasurement *msg = NoiseFigure::MsgPowerMeasurement::create(avg);
getMessageQueueToChannel()->push(msg);
}
// Prepare for a new measurement
m_powerSum = 0.0;
m_count = 0;
m_enabled = false;
}
}
}
}
void NoiseFigureSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
qDebug() << "NoiseFigureSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
m_channelSampleRate = channelSampleRate;
}
void NoiseFigureSink::applySettings(const NoiseFigureSettings& settings, bool force)
{
qDebug() << "NoiseFigureSink::applySettings:"
<< " force: " << force;
if ((settings.m_fftSize != m_settings.m_fftSize) || force)
{
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0) {
fftFactory->releaseEngine(m_settings.m_fftSize, false, m_fftSequence);
}
m_fftSequence = fftFactory->getEngine(settings.m_fftSize, false, &m_fft);
m_fftCounter = 0;
}
if ((settings.m_fftCount != m_settings.m_fftCount) || force)
{
m_powerSum = 0.0;
m_count = 0;
}
m_settings = settings;
}

View File

@ -0,0 +1,112 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGURESINK_H
#define INCLUDE_NOISEFIGURESINK_H
#include <QVector>
#include "dsp/channelsamplesink.h"
#include "dsp/fftfactory.h"
#include "dsp/fftengine.h"
#include "util/movingaverage.h"
#include "util/messagequeue.h"
#include "noisefiguresettings.h"
#include <vector>
#include <iostream>
#include <fstream>
class ChannelAPI;
class NoiseFigure;
class NoiseFigureSink : public ChannelSampleSink {
public:
NoiseFigureSink(NoiseFigure *aisDemod);
~NoiseFigureSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false);
void applySettings(const NoiseFigureSettings& settings, bool force = false);
void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; }
void setChannel(ChannelAPI *channel) { m_channel = channel; }
void startMeasurement() { m_enabled = true; }
double getMagSq() const { return m_magsq; }
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
{
if (m_magsqCount > 0)
{
m_magsq = m_magsqSum / m_magsqCount;
m_magSqLevelStore.m_magsq = m_magsq;
m_magSqLevelStore.m_magsqPeak = m_magsqPeak;
}
avg = m_magSqLevelStore.m_magsq;
peak = m_magSqLevelStore.m_magsqPeak;
nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount;
m_magsqSum = 0.0f;
m_magsqPeak = 0.0f;
m_magsqCount = 0;
}
private:
struct MagSqLevelsStore
{
MagSqLevelsStore() :
m_magsq(1e-12),
m_magsqPeak(1e-12)
{}
double m_magsq;
double m_magsqPeak;
};
NoiseFigure *m_noiseFigure;
NoiseFigureSettings m_settings;
ChannelAPI *m_channel;
int m_channelSampleRate;
int m_fftSequence;
FFTEngine *m_fft;
int m_fftCounter;
double m_magsq;
double m_magsqSum;
double m_magsqPeak;
int m_magsqCount;
MagSqLevelsStore m_magSqLevelStore;
MessageQueue *m_messageQueueToChannel;
MovingAverageUtil<Real, double, 16> m_movingAverage;
double m_powerSum;
int m_count;
bool m_enabled;
void processOneSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
void sampleToScope(Complex sample);
};
#endif // INCLUDE_NOISEFIGURESINK_H

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 "SWGChannelSettings.h"
#include "noisefigure.h"
#include "noisefigurewebapiadapter.h"
NoiseFigureWebAPIAdapter::NoiseFigureWebAPIAdapter()
{}
NoiseFigureWebAPIAdapter::~NoiseFigureWebAPIAdapter()
{}
int NoiseFigureWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setNoiseFigureSettings(new SWGSDRangel::SWGNoiseFigureSettings());
response.getNoiseFigureSettings()->init();
NoiseFigure::webapiFormatChannelSettings(response, m_settings);
return 200;
}
int NoiseFigureWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force;
(void) errorMessage;
NoiseFigure::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB. //
// Copyright (C) 2020 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_NOISEFIGURE_WEBAPIADAPTER_H
#define INCLUDE_NOISEFIGURE_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "noisefiguresettings.h"
/**
* Standalone API adapter only for the settings
*/
class NoiseFigureWebAPIAdapter : public ChannelWebAPIAdapter {
public:
NoiseFigureWebAPIAdapter();
virtual ~NoiseFigureWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
private:
NoiseFigureSettings m_settings;
};
#endif // INCLUDE_NOISEFIGURE_WEBAPIADAPTER_H

View File

@ -0,0 +1,156 @@
<h1>Noise Figure plugin</h1>
<h2>Introduction</h2>
This plugin can be used to calculate the noise figure (NF) of a receiver (SDR as well as optionally one or more LNAs). It uses the Y-factor method and requires a calibrated noise source.
The noise figure of a receiver is important, as it determines the noise floor: Noise floor (dBm) = 10*log10(1000kT)+NF+10*log10(BW)
The noise figure will vary with frequency and gain settings. Typically, the noise figure will increase with frequency and decrease with gain. When measuring noise figure, AGC should be disabled.
<h2>Interface</h2>
![Noise figure plugin GUI](../../../doc/img/NoiseFigure_plugin.png)
<h3>1: Measurement frequency shift from center</h3>
Use the wheels to adjust the measurement frequency in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2.
Many SDRs have a large DC offset, which can significantly interfer with the NF measurement, so this setting should be used to move the measurement frequency away from the radio's center frequency or any other spurs.
<h3>2: Channel power</h3>
Average total power in dB relative to a +/- 1.0 amplitude signal received in the measurement band.
<h3>3: Level meter in dB</h3>
- top bar (green): average value
- bottom bar (blue green): instantaneous peak value
- tip vertical bar (bright green): peak hold value
<h3>4: FFT size</h3>
Determines the size (number of points) of the FFT used to measure noise power. Only a single output bin of the FFT is used, which is selected by (1).
<h3>5: BW</h3>
Displays the measurement bandwidth in kHz as determined by (2).
<h3>6: FFTs to average</h3>
Determines the number of FFTs that are used to measure the average power. The more values that are used in the average, the more accurate the result will be, at the expense of increase test time.
<h3>7: Frequency range</h3>
Specifies the frequencies to measure the noise figure at. This can be specified as a:
* Range - Specify start frequency in MHz, stop frequency in MHz and the number of steps. 100, 200, 5, would measure at 100MHz, 125MHz, 150MHz, 175MHz and 200MHz.
* Step - Specify start frequency in MHz, stop frequency in MHz and the step frequency in MHz. 100, 200, 25, would measure at 100MHz, 125MHz, 150MHz, 175MHz and 200MHz.
* List - Specify a space or comma separated list of frequencies in MHz. "100 125 150 175 200", would measure at 100MHz, 125MHz, 150MHz, 175MHz and 200MHz.
<h3>8: Start/stop noise figure measurement</h3>
Starts or stops the noise figure measurement. When starting a new measurement, existing results are cleared.
<h3>9: Save results</h3>
Saves the results in the table to a CSV file.
<h3>10: Clear results</h3>
Clears the current results from the table and chart.
<h3>11: Open ENR dialog</h3>
Opens the ENR dialog to allow entering the Excess Noise Ratios (ENRs) for noise source. ENR specifies the difference in noise source power output in dB from when the source is powered off compared to when it is powered on.
This typically varies with frequency, so values should be entered for each frequency. When a measurement is attempted at a frequency for which a value is not specified, an interpolated value will be used.
Barycentric Rational Interpolation is used.
![Noise figure ENR dialog](../../../doc/img/NoiseFigure_plugin_enr.png)
<h3>12: Open Noise Source Control dialog</h3>
Opens the noise source control dialog, to allow setting how the plugin turns the power to the noise source off and on. Two control methods are supported: A program or script can be run to turn the power on or off,
or the VISA library (if available) can be used to send SCPI commands to a programmable power supply or other test equipment. If a VISA library is not found, the VISA and SCPI fields will be disabled.
The delay setting determines how long after the noise source's power is turned on or off, before the noise figure measurement starts. This should be long enough to allow the noise source output to settle, but
not too long so that tests over a large number of frequencies take a long time to run. The ideal value will vary with the noise source and power supply.
![Noise source control dialog](../../../doc/img/NoiseFigure_plugin_control.png)
<h3>13: Results Table</h3>
Displays measurement results.
* Frequency - Frequency of the measurement in MHz.
* NF - Calculated noise figure in dB.
* T - Calculated noise temperature in Kelvin with a reference temperature of 290K.
* Y - Measured Y factor in dB (difference in measured power when the noise source is on and off).
* ENR - Excess noise factor of the noise source in dB.
![Noise figure results table](../../../doc/img/NoiseFigure_plugin_results.png)
<h3>14: Results Chart</h3>
Plots the results (NF, T or Y) vs frequency as a line chart.
<h3>15: Open reference data</h3>
A set of reference data in CSV format can be loaded for comparisons with the measurement results. The first column of the CSV file should contain frequency and the second the noise figure in dB. The first row should contain a header (E.g. "Frequency,NF" allthough the exact text is ignored).
![SDRPlay Duo Noise figure comparison](../../../doc/img/NoiseFigure_plugin_duo_comparison.png)
<h3>16: Clear reference data</h3>
Clears the reference data, so the chart only plots the measured data.
<h2>Usage</h2>
A typical h/w setup for measuring the noise figure of a receiver is shown below:
![H/W setup](../../../doc/img/NoiseFigure_plugin_hw_setup.png)
A DC blocking capacitor at the output of the noise source for SDRs with a bias tee is recommended.
The noise source may be a device from the 346 family (E.g. Keysight 346B or NoiseCom NC346), but also can be a lower cost device that is supplied with accurate ENR calibration data.
(Inaccurate ENR values can significantly effect the calculated NF).
The ENR calibration data indicates the difference in power output when the noise source is powered off compared with when it is powered on. As the first setup step, this calibration data should
be entered in to the ENR dialog (11).
Next, we need to setup how the SDRangel powers on and off the noise source. This is set in the Noise Source Control Dialog (12).
For a 346 device, a programmable power supply outputting 28V would be used. Providing the VISA libraries are installed (see below), we can send SCPI commands to enable and disable the PSU's output.
As an example, for a Rigol DP832, we can set the channel 1 output to be 28V and enable it, with:
:SOURCE1:VOLTage 28
:OUTPut:STATe CH1,ON
And then disable it with:
:OUTPut:STATe CH1,OFF
The final settings needed are the frequencies to measure the NF at. This can be set with (7), to step through a range or a list of specific point frequencies.
To start the measurement, press (8).
<h2>Examples</h2>
Here is a plot comparing the measured noise figure for tuner 1 of a SDRplay Duo, compared to the [datasheet values](https://www.sdrplay.com/wp-content/uploads/2018/06/RSPDuo-Technical-Information-R1P1.pdf).
![SDRPlay Duo NF comparison](../../../doc/img/NoiseFigure_plugin_duo_comparison.png)
Here is a plot comparing measured values for a USRP B210 RF A RX2 port to measured values for the TX/RX port. As we can see, there is lower noise on the RX2 port:
![B210 input NF comparison](../../../doc/img/NoiseFigure_plugin_b210_comparison.png)
Here is a plot comparing measured values for a Nooelec SMArTee XTR on its own, with a Nooelec Sawbird GOES LNA and with a Sawbird+ GOES LNA.
As can be seen, a LNA makes a massive difference (12dB+) to the overall NF, and allows low cost SDRs to get similar NFs to more expensive devices,
as the total NF is primarily determined by the LNA, if it has a decent amount of gain:
![LNA comparison](../../../doc/img/NoiseFigure_plugin_lna_comparison.png)
<h2>VISA libraries</h2>
VISA libraries are available for Windows, Linux and MacOS from:
[Keysight](https://www.keysight.com/us/en/lib/software-detail/computer-software/io-libraries-suite-downloads-2175637.html)
[NI](https://www.ni.com/en-gb/support/downloads/drivers/download.ni-visa.html)

View File

@ -206,6 +206,7 @@ set(sdrbase_SOURCES
util/uid.cpp
util/units.cpp
util/timeutil.cpp
util/visa.cpp
plugin/plugininterface.cpp
plugin/pluginapi.cpp
@ -411,6 +412,7 @@ set(sdrbase_HEADERS
util/uid.h
util/units.h
util/timeutil.h
util/visa.h
webapi/webapiadapter.h
webapi/webapiadapterbase.h

136
sdrbase/util/visa.cpp Normal file
View File

@ -0,0 +1,136 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 "visa.h"
#ifdef _MSC_VER
#include <windows.h>
#else
#include <dlfcn.h>
#endif
VISA::VISA() :
m_defaultRM(0),
viOpenDefaultRM(nullptr),
viOpen(nullptr),
viClose(nullptr),
viPrintf(nullptr),
viScanf(nullptr),
m_available(false)
{
#ifdef _MSC_VER
const char *visaName = "visa32.dll"; // Loads visa64.dll on WIN64
#else
const char *visaName = "visakt32.so"; // Keysight library
#endif
visaLibrary = libraryOpen(visaName);
if (visaLibrary)
{
viOpenDefaultRM = (ViStatus (WINAPI *)(ViPSession)) libraryFunc(visaLibrary, "viOpenDefaultRM");
viOpen = (ViStatus (WINAPI *)(ViSession sesn, ViRsrc name, ViAccessMode mode, ViUInt32 timeout, ViPSession vi)) libraryFunc(visaLibrary, "viOpen");
viClose = (ViStatus (WINAPI *)(ViObject vi)) libraryFunc(visaLibrary, "viClose");
viPrintf = (ViStatus (WINAPI *) (ViSession vi, ViString writeFmt, ...)) libraryFunc(visaLibrary, "viPrintf");
viScanf = (ViStatus (WINAPI *) (ViSession vi, ViString writeFmt, ...)) libraryFunc(visaLibrary, "viScanf");
if (viOpenDefaultRM && viOpen && viClose && viPrintf) {
m_available = true;
}
}
else
{
qDebug() << "VISA::VISA: Unable to load " << visaName;
}
}
ViSession VISA::openDefault()
{
if (isAvailable() && (m_defaultRM == 0))
{
viOpenDefaultRM(&m_defaultRM);
return m_defaultRM;
}
else
{
return m_defaultRM;
}
}
void VISA::closeDefault()
{
if (isAvailable())
{
viClose(m_defaultRM);
m_defaultRM = 0;
}
}
ViSession VISA::open(const QString& device)
{
ViSession session;
if (isAvailable())
{
if (VI_SUCCESS == viOpen(m_defaultRM, device.toLatin1().data(), VI_NULL, VI_NULL, &session))
{
qDebug() << "VISA::open: Opened VISA device: " << device;
return session;
}
else
{
qDebug() << "VISA::open: Failed to open VISA device: " << device;
}
}
return 0;
}
void VISA::close(ViSession session)
{
if (isAvailable()) {
viClose(session);
}
}
#ifdef _MSC_VER
void *VISA::libraryOpen(const char *filename)
{
HMODULE module;
module = LoadLibrary ((LPCSTR)filename);
return module;
}
void *VISA::libraryFunc(void *library, const char *function)
{
return GetProcAddress ((HMODULE)library, function);
}
#else
void *VISA::libraryOpen(const char *filename)
{
return dlopen (filename, RTLD_LAZY);
}
void *VISA::libraryFunc(void *library, const char *function)
{
return dlsym (library, function);
}
#endif

81
sdrbase/util/visa.h Normal file
View File

@ -0,0 +1,81 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE //
// //
// 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 INCLUDE_VISA_H
#define INCLUDE_VISA_H
// Minimal implementation of VISA specification (just the bits we need so far)
// https://www.ivifoundation.org/docs/vpp432_2016-02-26.pdf
#include "export.h"
typedef char ViChar;
typedef ViChar * ViPChar;
typedef signed long ViInt32;
typedef unsigned long ViUInt32;
typedef ViPChar ViString;
typedef ViInt32 ViStatus;
typedef ViUInt32 ViObject;
typedef ViObject ViSession;
typedef ViSession * ViPSession;
typedef ViString ViRsrc;
typedef ViUInt32 ViAccessMode;
#define VI_SUCCESS 0
#define VI_TRUE 1
#define VI_FALSE 0
#define VI_NULL 0
// We dynamically load the visa dll, as most users probably do not have it
// Note: Can't seem to call viOpenDefaultRM/viClose in constructor / destructor of global instance
class SDRBASE_API VISA {
public:
// Default session
ViSession m_defaultRM;
// Function pointers to VISA API for direct calls
ViStatus (*viOpenDefaultRM) (ViPSession vi);
ViStatus (*viOpen) (ViSession sesn, ViRsrc name, ViAccessMode mode, ViUInt32 timeout, ViPSession vi);
ViStatus (*viClose) (ViObject vi);
ViStatus (*viPrintf) (ViSession vi, ViString writeFmt, ...);
ViStatus (*viScanf) (ViSession vi, ViString readFmt, ...);
VISA();
ViSession openDefault();
void closeDefault();
ViSession open(const QString& device);
void close(ViSession session);
// Is the VISA library available
bool isAvailable() const
{
return m_available;
}
private:
bool m_available;
protected:
void *visaLibrary;
void *libraryOpen(const char *filename);
void *libraryFunc(void *library, const char *function);
};
#endif // INCLUDE_VISA_H

View File

@ -3936,6 +3936,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setNfmModSettings(new SWGSDRangel::SWGNFMModSettings());
channelSettings->getNfmModSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "NoiseFigureSettings")
{
channelSettings->setNoiseFigureSettings(new SWGSDRangel::SWGNoiseFigureSettings());
channelSettings->getNoiseFigureSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "LocalSinkSettings")
{
channelSettings->setLocalSinkSettings(new SWGSDRangel::SWGLocalSinkSettings());
@ -4638,6 +4643,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings&
channelSettings.setIeee802154ModSettings(nullptr);
channelSettings.setNfmDemodSettings(nullptr);
channelSettings.setNfmModSettings(nullptr);
channelSettings.setNoiseFigureSettings(nullptr);
channelSettings.setPacketDemodSettings(nullptr);
channelSettings.setPacketModSettings(nullptr);
channelSettings.setRemoteSinkSettings(nullptr);
@ -4666,6 +4672,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan
channelReport.setDsdDemodReport(nullptr);
channelReport.setNfmDemodReport(nullptr);
channelReport.setNfmModReport(nullptr);
channelReport.setNoiseFigureReport(nullptr);
channelReport.setIeee802154ModReport(nullptr);
channelReport.setPacketModReport(nullptr);
channelReport.setRemoteSourceReport(nullptr);

View File

@ -49,6 +49,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channel.nfmdemod", "NFMDemodSettings"},
{"de.maintech.sdrangelove.channel.nfm", "NFMDemodSettings"}, // remap
{"sdrangel.channeltx.modnfm", "NFMModSettings"},
{"sdrangel.channel.noisefigure", "NoiseFigureSettings"},
{"sdrangel.demod.localsink", "LocalSinkSettings"},
{"sdrangel.channel.localsink", "LocalSinkSettings"}, // remap
{"sdrangel.channel.localsource", "LocalSourceSettings"},
@ -147,6 +148,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModSettings"},
{"NFMDemod", "NFMDemodSettings"},
{"NFMMod", "NFMModSettings"},
{"NoiseFigure", "NoiseFigureSettings"},
{"PacketDemod", "PacketDemodSettings"},
{"PacketMod", "PacketModSettings"},
{"LocalSink", "LocalSinkSettings"},

View File

@ -49,6 +49,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodReport"
NFMModReport:
$ref: "http://swgserver:8081/api/swagger/include/NFMMod.yaml#/NFMModReport"
NoiseFigureReport:
$ref: "http://swgserver:8081/api/swagger/include/NoiseFigure.yaml#/NoiseFigureReport"
SSBDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/SSBDemod.yaml#/SSBDemodReport"
RemoteSourceReport:

View File

@ -69,6 +69,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/NFMDemod.yaml#/NFMDemodSettings"
NFMModSettings:
$ref: "http://swgserver:8081/api/swagger/include/NFMMod.yaml#/NFMModSettings"
NoiseFigureSettings:
$ref: "http://swgserver:8081/api/swagger/include/NoiseFigure.yaml#/NoiseFigureSettings"
LocalSinkSettings:
$ref: "http://swgserver:8081/api/swagger/include/LocalSink.yaml#/LocalSinkSettings"
LocalSourceSettings:

View File

@ -0,0 +1,66 @@
NoiseFigureSettings:
description: NoiseFigure
properties:
inputFrequencyOffset:
type: integer
format: int64
fftSize:
type: integer
fftCount:
type: integer
frequencySpec:
type: integer
startFrequency:
type: number
format: float
stopFrequency:
type: number
format: float
steps:
type: integer
step:
type: number
format: float
frequencies:
type: string
visaDevice:
type: string
powerOnSCPI:
type: string
powerOffSCPI:
type: string
powerOnCommand:
type: string
powerOffCommand:
type: string
powerDelay:
type: number
format: float
rgbColor:
type: integer
title:
type: string
streamIndex:
description: MIMO channel. Not relevant when connected to SI (single Rx).
type: integer
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
NoiseFigureReport:
description: NoiseFigure
properties:
channelPowerDB:
description: power received in channel (dB)
type: number
format: float
channelSampleRate:
type: integer

View File

@ -70,6 +70,8 @@ SWGChannelReport::SWGChannelReport() {
m_nfm_demod_report_isSet = false;
nfm_mod_report = nullptr;
m_nfm_mod_report_isSet = false;
noise_figure_report = nullptr;
m_noise_figure_report_isSet = false;
ssb_demod_report = nullptr;
m_ssb_demod_report_isSet = false;
remote_source_report = nullptr;
@ -142,6 +144,8 @@ SWGChannelReport::init() {
m_nfm_demod_report_isSet = false;
nfm_mod_report = new SWGNFMModReport();
m_nfm_mod_report_isSet = false;
noise_figure_report = new SWGNoiseFigureReport();
m_noise_figure_report_isSet = false;
ssb_demod_report = new SWGSSBDemodReport();
m_ssb_demod_report_isSet = false;
remote_source_report = new SWGRemoteSourceReport();
@ -229,6 +233,9 @@ SWGChannelReport::cleanup() {
if(nfm_mod_report != nullptr) {
delete nfm_mod_report;
}
if(noise_figure_report != nullptr) {
delete noise_figure_report;
}
if(ssb_demod_report != nullptr) {
delete ssb_demod_report;
}
@ -317,6 +324,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&nfm_mod_report, pJson["NFMModReport"], "SWGNFMModReport", "SWGNFMModReport");
::SWGSDRangel::setValue(&noise_figure_report, pJson["NoiseFigureReport"], "SWGNoiseFigureReport", "SWGNoiseFigureReport");
::SWGSDRangel::setValue(&ssb_demod_report, pJson["SSBDemodReport"], "SWGSSBDemodReport", "SWGSSBDemodReport");
::SWGSDRangel::setValue(&remote_source_report, pJson["RemoteSourceReport"], "SWGRemoteSourceReport", "SWGRemoteSourceReport");
@ -418,6 +427,9 @@ SWGChannelReport::asJsonObject() {
if((nfm_mod_report != nullptr) && (nfm_mod_report->isSet())){
toJsonValue(QString("NFMModReport"), nfm_mod_report, obj, QString("SWGNFMModReport"));
}
if((noise_figure_report != nullptr) && (noise_figure_report->isSet())){
toJsonValue(QString("NoiseFigureReport"), noise_figure_report, obj, QString("SWGNoiseFigureReport"));
}
if((ssb_demod_report != nullptr) && (ssb_demod_report->isSet())){
toJsonValue(QString("SSBDemodReport"), ssb_demod_report, obj, QString("SWGSSBDemodReport"));
}
@ -665,6 +677,16 @@ SWGChannelReport::setNfmModReport(SWGNFMModReport* nfm_mod_report) {
this->m_nfm_mod_report_isSet = true;
}
SWGNoiseFigureReport*
SWGChannelReport::getNoiseFigureReport() {
return noise_figure_report;
}
void
SWGChannelReport::setNoiseFigureReport(SWGNoiseFigureReport* noise_figure_report) {
this->noise_figure_report = noise_figure_report;
this->m_noise_figure_report_isSet = true;
}
SWGSSBDemodReport*
SWGChannelReport::getSsbDemodReport() {
return ssb_demod_report;
@ -843,6 +865,9 @@ SWGChannelReport::isSet(){
if(nfm_mod_report && nfm_mod_report->isSet()){
isObjectUpdated = true; break;
}
if(noise_figure_report && noise_figure_report->isSet()){
isObjectUpdated = true; break;
}
if(ssb_demod_report && ssb_demod_report->isSet()){
isObjectUpdated = true; break;
}

View File

@ -41,6 +41,7 @@
#include "SWGIEEE_802_15_4_ModReport.h"
#include "SWGNFMDemodReport.h"
#include "SWGNFMModReport.h"
#include "SWGNoiseFigureReport.h"
#include "SWGPacketModReport.h"
#include "SWGRemoteSourceReport.h"
#include "SWGSSBDemodReport.h"
@ -135,6 +136,9 @@ public:
SWGNFMModReport* getNfmModReport();
void setNfmModReport(SWGNFMModReport* nfm_mod_report);
SWGNoiseFigureReport* getNoiseFigureReport();
void setNoiseFigureReport(SWGNoiseFigureReport* noise_figure_report);
SWGSSBDemodReport* getSsbDemodReport();
void setSsbDemodReport(SWGSSBDemodReport* ssb_demod_report);
@ -235,6 +239,9 @@ private:
SWGNFMModReport* nfm_mod_report;
bool m_nfm_mod_report_isSet;
SWGNoiseFigureReport* noise_figure_report;
bool m_noise_figure_report_isSet;
SWGSSBDemodReport* ssb_demod_report;
bool m_ssb_demod_report_isSet;

View File

@ -88,6 +88,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_nfm_demod_settings_isSet = false;
nfm_mod_settings = nullptr;
m_nfm_mod_settings_isSet = false;
noise_figure_settings = nullptr;
m_noise_figure_settings_isSet = false;
local_sink_settings = nullptr;
m_local_sink_settings_isSet = false;
local_source_settings = nullptr;
@ -186,6 +188,8 @@ SWGChannelSettings::init() {
m_nfm_demod_settings_isSet = false;
nfm_mod_settings = new SWGNFMModSettings();
m_nfm_mod_settings_isSet = false;
noise_figure_settings = new SWGNoiseFigureSettings();
m_noise_figure_settings_isSet = false;
local_sink_settings = new SWGLocalSinkSettings();
m_local_sink_settings_isSet = false;
local_source_settings = new SWGLocalSourceSettings();
@ -304,6 +308,9 @@ SWGChannelSettings::cleanup() {
if(nfm_mod_settings != nullptr) {
delete nfm_mod_settings;
}
if(noise_figure_settings != nullptr) {
delete noise_figure_settings;
}
if(local_sink_settings != nullptr) {
delete local_sink_settings;
}
@ -422,6 +429,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&nfm_mod_settings, pJson["NFMModSettings"], "SWGNFMModSettings", "SWGNFMModSettings");
::SWGSDRangel::setValue(&noise_figure_settings, pJson["NoiseFigureSettings"], "SWGNoiseFigureSettings", "SWGNoiseFigureSettings");
::SWGSDRangel::setValue(&local_sink_settings, pJson["LocalSinkSettings"], "SWGLocalSinkSettings", "SWGLocalSinkSettings");
::SWGSDRangel::setValue(&local_source_settings, pJson["LocalSourceSettings"], "SWGLocalSourceSettings", "SWGLocalSourceSettings");
@ -558,6 +567,9 @@ SWGChannelSettings::asJsonObject() {
if((nfm_mod_settings != nullptr) && (nfm_mod_settings->isSet())){
toJsonValue(QString("NFMModSettings"), nfm_mod_settings, obj, QString("SWGNFMModSettings"));
}
if((noise_figure_settings != nullptr) && (noise_figure_settings->isSet())){
toJsonValue(QString("NoiseFigureSettings"), noise_figure_settings, obj, QString("SWGNoiseFigureSettings"));
}
if((local_sink_settings != nullptr) && (local_sink_settings->isSet())){
toJsonValue(QString("LocalSinkSettings"), local_sink_settings, obj, QString("SWGLocalSinkSettings"));
}
@ -907,6 +919,16 @@ SWGChannelSettings::setNfmModSettings(SWGNFMModSettings* nfm_mod_settings) {
this->m_nfm_mod_settings_isSet = true;
}
SWGNoiseFigureSettings*
SWGChannelSettings::getNoiseFigureSettings() {
return noise_figure_settings;
}
void
SWGChannelSettings::setNoiseFigureSettings(SWGNoiseFigureSettings* noise_figure_settings) {
this->noise_figure_settings = noise_figure_settings;
this->m_noise_figure_settings_isSet = true;
}
SWGLocalSinkSettings*
SWGChannelSettings::getLocalSinkSettings() {
return local_sink_settings;
@ -1152,6 +1174,9 @@ SWGChannelSettings::isSet(){
if(nfm_mod_settings && nfm_mod_settings->isSet()){
isObjectUpdated = true; break;
}
if(noise_figure_settings && noise_figure_settings->isSet()){
isObjectUpdated = true; break;
}
if(local_sink_settings && local_sink_settings->isSet()){
isObjectUpdated = true; break;
}

View File

@ -50,6 +50,7 @@
#include "SWGLocalSourceSettings.h"
#include "SWGNFMDemodSettings.h"
#include "SWGNFMModSettings.h"
#include "SWGNoiseFigureSettings.h"
#include "SWGPacketDemodSettings.h"
#include "SWGPacketModSettings.h"
#include "SWGRemoteSinkSettings.h"
@ -173,6 +174,9 @@ public:
SWGNFMModSettings* getNfmModSettings();
void setNfmModSettings(SWGNFMModSettings* nfm_mod_settings);
SWGNoiseFigureSettings* getNoiseFigureSettings();
void setNoiseFigureSettings(SWGNoiseFigureSettings* noise_figure_settings);
SWGLocalSinkSettings* getLocalSinkSettings();
void setLocalSinkSettings(SWGLocalSinkSettings* local_sink_settings);
@ -312,6 +316,9 @@ private:
SWGNFMModSettings* nfm_mod_settings;
bool m_nfm_mod_settings_isSet;
SWGNoiseFigureSettings* noise_figure_settings;
bool m_noise_figure_settings_isSet;
SWGLocalSinkSettings* local_sink_settings;
bool m_local_sink_settings_isSet;

View File

@ -172,6 +172,8 @@
#include "SWGNFMModReport.h"
#include "SWGNFMModSettings.h"
#include "SWGNamedEnum.h"
#include "SWGNoiseFigureReport.h"
#include "SWGNoiseFigureSettings.h"
#include "SWGPERTesterActions.h"
#include "SWGPERTesterActions_aos.h"
#include "SWGPERTesterSettings.h"
@ -748,6 +750,12 @@ namespace SWGSDRangel {
if(QString("SWGNamedEnum").compare(type) == 0) {
return new SWGNamedEnum();
}
if(QString("SWGNoiseFigureReport").compare(type) == 0) {
return new SWGNoiseFigureReport();
}
if(QString("SWGNoiseFigureSettings").compare(type) == 0) {
return new SWGNoiseFigureSettings();
}
if(QString("SWGPERTesterActions").compare(type) == 0) {
return new SWGPERTesterActions();
}

View File

@ -0,0 +1,131 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGNoiseFigureReport.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGNoiseFigureReport::SWGNoiseFigureReport(QString* json) {
init();
this->fromJson(*json);
}
SWGNoiseFigureReport::SWGNoiseFigureReport() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGNoiseFigureReport::~SWGNoiseFigureReport() {
this->cleanup();
}
void
SWGNoiseFigureReport::init() {
channel_power_db = 0.0f;
m_channel_power_db_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGNoiseFigureReport::cleanup() {
}
SWGNoiseFigureReport*
SWGNoiseFigureReport::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGNoiseFigureReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGNoiseFigureReport::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGNoiseFigureReport::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_channel_power_db_isSet){
obj->insert("channelPowerDB", QJsonValue(channel_power_db));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
float
SWGNoiseFigureReport::getChannelPowerDb() {
return channel_power_db;
}
void
SWGNoiseFigureReport::setChannelPowerDb(float channel_power_db) {
this->channel_power_db = channel_power_db;
this->m_channel_power_db_isSet = true;
}
qint32
SWGNoiseFigureReport::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGNoiseFigureReport::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGNoiseFigureReport::isSet(){
bool isObjectUpdated = false;
do{
if(m_channel_power_db_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,64 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGNoiseFigureReport.h
*
* NoiseFigure
*/
#ifndef SWGNoiseFigureReport_H_
#define SWGNoiseFigureReport_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGNoiseFigureReport: public SWGObject {
public:
SWGNoiseFigureReport();
SWGNoiseFigureReport(QString* json);
virtual ~SWGNoiseFigureReport();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGNoiseFigureReport* fromJson(QString &jsonString) override;
float getChannelPowerDb();
void setChannelPowerDb(float channel_power_db);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
float channel_power_db;
bool m_channel_power_db_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGNoiseFigureReport_H_ */

View File

@ -0,0 +1,630 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGNoiseFigureSettings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGNoiseFigureSettings::SWGNoiseFigureSettings(QString* json) {
init();
this->fromJson(*json);
}
SWGNoiseFigureSettings::SWGNoiseFigureSettings() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
fft_size = 0;
m_fft_size_isSet = false;
fft_count = 0;
m_fft_count_isSet = false;
frequency_spec = 0;
m_frequency_spec_isSet = false;
start_frequency = 0.0f;
m_start_frequency_isSet = false;
stop_frequency = 0.0f;
m_stop_frequency_isSet = false;
steps = 0;
m_steps_isSet = false;
step = 0.0f;
m_step_isSet = false;
frequencies = nullptr;
m_frequencies_isSet = false;
visa_device = nullptr;
m_visa_device_isSet = false;
power_on_scpi = nullptr;
m_power_on_scpi_isSet = false;
power_off_scpi = nullptr;
m_power_off_scpi_isSet = false;
power_on_command = nullptr;
m_power_on_command_isSet = false;
power_off_command = nullptr;
m_power_off_command_isSet = false;
power_delay = 0.0f;
m_power_delay_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
SWGNoiseFigureSettings::~SWGNoiseFigureSettings() {
this->cleanup();
}
void
SWGNoiseFigureSettings::init() {
input_frequency_offset = 0L;
m_input_frequency_offset_isSet = false;
fft_size = 0;
m_fft_size_isSet = false;
fft_count = 0;
m_fft_count_isSet = false;
frequency_spec = 0;
m_frequency_spec_isSet = false;
start_frequency = 0.0f;
m_start_frequency_isSet = false;
stop_frequency = 0.0f;
m_stop_frequency_isSet = false;
steps = 0;
m_steps_isSet = false;
step = 0.0f;
m_step_isSet = false;
frequencies = new QString("");
m_frequencies_isSet = false;
visa_device = new QString("");
m_visa_device_isSet = false;
power_on_scpi = new QString("");
m_power_on_scpi_isSet = false;
power_off_scpi = new QString("");
m_power_off_scpi_isSet = false;
power_on_command = new QString("");
m_power_on_command_isSet = false;
power_off_command = new QString("");
m_power_off_command_isSet = false;
power_delay = 0.0f;
m_power_delay_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
stream_index = 0;
m_stream_index_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
}
void
SWGNoiseFigureSettings::cleanup() {
if(frequencies != nullptr) {
delete frequencies;
}
if(visa_device != nullptr) {
delete visa_device;
}
if(power_on_scpi != nullptr) {
delete power_on_scpi;
}
if(power_off_scpi != nullptr) {
delete power_off_scpi;
}
if(power_on_command != nullptr) {
delete power_on_command;
}
if(power_off_command != nullptr) {
delete power_off_command;
}
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
}
SWGNoiseFigureSettings*
SWGNoiseFigureSettings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGNoiseFigureSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", "");
::SWGSDRangel::setValue(&fft_size, pJson["fftSize"], "qint32", "");
::SWGSDRangel::setValue(&fft_count, pJson["fftCount"], "qint32", "");
::SWGSDRangel::setValue(&frequency_spec, pJson["frequencySpec"], "qint32", "");
::SWGSDRangel::setValue(&start_frequency, pJson["startFrequency"], "float", "");
::SWGSDRangel::setValue(&stop_frequency, pJson["stopFrequency"], "float", "");
::SWGSDRangel::setValue(&steps, pJson["steps"], "qint32", "");
::SWGSDRangel::setValue(&step, pJson["step"], "float", "");
::SWGSDRangel::setValue(&frequencies, pJson["frequencies"], "QString", "QString");
::SWGSDRangel::setValue(&visa_device, pJson["visaDevice"], "QString", "QString");
::SWGSDRangel::setValue(&power_on_scpi, pJson["powerOnSCPI"], "QString", "QString");
::SWGSDRangel::setValue(&power_off_scpi, pJson["powerOffSCPI"], "QString", "QString");
::SWGSDRangel::setValue(&power_on_command, pJson["powerOnCommand"], "QString", "QString");
::SWGSDRangel::setValue(&power_off_command, pJson["powerOffCommand"], "QString", "QString");
::SWGSDRangel::setValue(&power_delay, pJson["powerDelay"], "float", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
}
QString
SWGNoiseFigureSettings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGNoiseFigureSettings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_input_frequency_offset_isSet){
obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset));
}
if(m_fft_size_isSet){
obj->insert("fftSize", QJsonValue(fft_size));
}
if(m_fft_count_isSet){
obj->insert("fftCount", QJsonValue(fft_count));
}
if(m_frequency_spec_isSet){
obj->insert("frequencySpec", QJsonValue(frequency_spec));
}
if(m_start_frequency_isSet){
obj->insert("startFrequency", QJsonValue(start_frequency));
}
if(m_stop_frequency_isSet){
obj->insert("stopFrequency", QJsonValue(stop_frequency));
}
if(m_steps_isSet){
obj->insert("steps", QJsonValue(steps));
}
if(m_step_isSet){
obj->insert("step", QJsonValue(step));
}
if(frequencies != nullptr && *frequencies != QString("")){
toJsonValue(QString("frequencies"), frequencies, obj, QString("QString"));
}
if(visa_device != nullptr && *visa_device != QString("")){
toJsonValue(QString("visaDevice"), visa_device, obj, QString("QString"));
}
if(power_on_scpi != nullptr && *power_on_scpi != QString("")){
toJsonValue(QString("powerOnSCPI"), power_on_scpi, obj, QString("QString"));
}
if(power_off_scpi != nullptr && *power_off_scpi != QString("")){
toJsonValue(QString("powerOffSCPI"), power_off_scpi, obj, QString("QString"));
}
if(power_on_command != nullptr && *power_on_command != QString("")){
toJsonValue(QString("powerOnCommand"), power_on_command, obj, QString("QString"));
}
if(power_off_command != nullptr && *power_off_command != QString("")){
toJsonValue(QString("powerOffCommand"), power_off_command, obj, QString("QString"));
}
if(m_power_delay_isSet){
obj->insert("powerDelay", QJsonValue(power_delay));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_stream_index_isSet){
obj->insert("streamIndex", QJsonValue(stream_index));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
return obj;
}
qint64
SWGNoiseFigureSettings::getInputFrequencyOffset() {
return input_frequency_offset;
}
void
SWGNoiseFigureSettings::setInputFrequencyOffset(qint64 input_frequency_offset) {
this->input_frequency_offset = input_frequency_offset;
this->m_input_frequency_offset_isSet = true;
}
qint32
SWGNoiseFigureSettings::getFftSize() {
return fft_size;
}
void
SWGNoiseFigureSettings::setFftSize(qint32 fft_size) {
this->fft_size = fft_size;
this->m_fft_size_isSet = true;
}
qint32
SWGNoiseFigureSettings::getFftCount() {
return fft_count;
}
void
SWGNoiseFigureSettings::setFftCount(qint32 fft_count) {
this->fft_count = fft_count;
this->m_fft_count_isSet = true;
}
qint32
SWGNoiseFigureSettings::getFrequencySpec() {
return frequency_spec;
}
void
SWGNoiseFigureSettings::setFrequencySpec(qint32 frequency_spec) {
this->frequency_spec = frequency_spec;
this->m_frequency_spec_isSet = true;
}
float
SWGNoiseFigureSettings::getStartFrequency() {
return start_frequency;
}
void
SWGNoiseFigureSettings::setStartFrequency(float start_frequency) {
this->start_frequency = start_frequency;
this->m_start_frequency_isSet = true;
}
float
SWGNoiseFigureSettings::getStopFrequency() {
return stop_frequency;
}
void
SWGNoiseFigureSettings::setStopFrequency(float stop_frequency) {
this->stop_frequency = stop_frequency;
this->m_stop_frequency_isSet = true;
}
qint32
SWGNoiseFigureSettings::getSteps() {
return steps;
}
void
SWGNoiseFigureSettings::setSteps(qint32 steps) {
this->steps = steps;
this->m_steps_isSet = true;
}
float
SWGNoiseFigureSettings::getStep() {
return step;
}
void
SWGNoiseFigureSettings::setStep(float step) {
this->step = step;
this->m_step_isSet = true;
}
QString*
SWGNoiseFigureSettings::getFrequencies() {
return frequencies;
}
void
SWGNoiseFigureSettings::setFrequencies(QString* frequencies) {
this->frequencies = frequencies;
this->m_frequencies_isSet = true;
}
QString*
SWGNoiseFigureSettings::getVisaDevice() {
return visa_device;
}
void
SWGNoiseFigureSettings::setVisaDevice(QString* visa_device) {
this->visa_device = visa_device;
this->m_visa_device_isSet = true;
}
QString*
SWGNoiseFigureSettings::getPowerOnScpi() {
return power_on_scpi;
}
void
SWGNoiseFigureSettings::setPowerOnScpi(QString* power_on_scpi) {
this->power_on_scpi = power_on_scpi;
this->m_power_on_scpi_isSet = true;
}
QString*
SWGNoiseFigureSettings::getPowerOffScpi() {
return power_off_scpi;
}
void
SWGNoiseFigureSettings::setPowerOffScpi(QString* power_off_scpi) {
this->power_off_scpi = power_off_scpi;
this->m_power_off_scpi_isSet = true;
}
QString*
SWGNoiseFigureSettings::getPowerOnCommand() {
return power_on_command;
}
void
SWGNoiseFigureSettings::setPowerOnCommand(QString* power_on_command) {
this->power_on_command = power_on_command;
this->m_power_on_command_isSet = true;
}
QString*
SWGNoiseFigureSettings::getPowerOffCommand() {
return power_off_command;
}
void
SWGNoiseFigureSettings::setPowerOffCommand(QString* power_off_command) {
this->power_off_command = power_off_command;
this->m_power_off_command_isSet = true;
}
float
SWGNoiseFigureSettings::getPowerDelay() {
return power_delay;
}
void
SWGNoiseFigureSettings::setPowerDelay(float power_delay) {
this->power_delay = power_delay;
this->m_power_delay_isSet = true;
}
qint32
SWGNoiseFigureSettings::getRgbColor() {
return rgb_color;
}
void
SWGNoiseFigureSettings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGNoiseFigureSettings::getTitle() {
return title;
}
void
SWGNoiseFigureSettings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGNoiseFigureSettings::getStreamIndex() {
return stream_index;
}
void
SWGNoiseFigureSettings::setStreamIndex(qint32 stream_index) {
this->stream_index = stream_index;
this->m_stream_index_isSet = true;
}
qint32
SWGNoiseFigureSettings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGNoiseFigureSettings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGNoiseFigureSettings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGNoiseFigureSettings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGNoiseFigureSettings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGNoiseFigureSettings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGNoiseFigureSettings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGNoiseFigureSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGNoiseFigureSettings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGNoiseFigureSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
bool
SWGNoiseFigureSettings::isSet(){
bool isObjectUpdated = false;
do{
if(m_input_frequency_offset_isSet){
isObjectUpdated = true; break;
}
if(m_fft_size_isSet){
isObjectUpdated = true; break;
}
if(m_fft_count_isSet){
isObjectUpdated = true; break;
}
if(m_frequency_spec_isSet){
isObjectUpdated = true; break;
}
if(m_start_frequency_isSet){
isObjectUpdated = true; break;
}
if(m_stop_frequency_isSet){
isObjectUpdated = true; break;
}
if(m_steps_isSet){
isObjectUpdated = true; break;
}
if(m_step_isSet){
isObjectUpdated = true; break;
}
if(frequencies && *frequencies != QString("")){
isObjectUpdated = true; break;
}
if(visa_device && *visa_device != QString("")){
isObjectUpdated = true; break;
}
if(power_on_scpi && *power_on_scpi != QString("")){
isObjectUpdated = true; break;
}
if(power_off_scpi && *power_off_scpi != QString("")){
isObjectUpdated = true; break;
}
if(power_on_command && *power_on_command != QString("")){
isObjectUpdated = true; break;
}
if(power_off_command && *power_off_command != QString("")){
isObjectUpdated = true; break;
}
if(m_power_delay_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_stream_index_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

View File

@ -0,0 +1,191 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 6.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGNoiseFigureSettings.h
*
* NoiseFigure
*/
#ifndef SWGNoiseFigureSettings_H_
#define SWGNoiseFigureSettings_H_
#include <QJsonObject>
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGNoiseFigureSettings: public SWGObject {
public:
SWGNoiseFigureSettings();
SWGNoiseFigureSettings(QString* json);
virtual ~SWGNoiseFigureSettings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGNoiseFigureSettings* fromJson(QString &jsonString) override;
qint64 getInputFrequencyOffset();
void setInputFrequencyOffset(qint64 input_frequency_offset);
qint32 getFftSize();
void setFftSize(qint32 fft_size);
qint32 getFftCount();
void setFftCount(qint32 fft_count);
qint32 getFrequencySpec();
void setFrequencySpec(qint32 frequency_spec);
float getStartFrequency();
void setStartFrequency(float start_frequency);
float getStopFrequency();
void setStopFrequency(float stop_frequency);
qint32 getSteps();
void setSteps(qint32 steps);
float getStep();
void setStep(float step);
QString* getFrequencies();
void setFrequencies(QString* frequencies);
QString* getVisaDevice();
void setVisaDevice(QString* visa_device);
QString* getPowerOnScpi();
void setPowerOnScpi(QString* power_on_scpi);
QString* getPowerOffScpi();
void setPowerOffScpi(QString* power_off_scpi);
QString* getPowerOnCommand();
void setPowerOnCommand(QString* power_on_command);
QString* getPowerOffCommand();
void setPowerOffCommand(QString* power_off_command);
float getPowerDelay();
void setPowerDelay(float power_delay);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
qint32 getStreamIndex();
void setStreamIndex(qint32 stream_index);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
virtual bool isSet() override;
private:
qint64 input_frequency_offset;
bool m_input_frequency_offset_isSet;
qint32 fft_size;
bool m_fft_size_isSet;
qint32 fft_count;
bool m_fft_count_isSet;
qint32 frequency_spec;
bool m_frequency_spec_isSet;
float start_frequency;
bool m_start_frequency_isSet;
float stop_frequency;
bool m_stop_frequency_isSet;
qint32 steps;
bool m_steps_isSet;
float step;
bool m_step_isSet;
QString* frequencies;
bool m_frequencies_isSet;
QString* visa_device;
bool m_visa_device_isSet;
QString* power_on_scpi;
bool m_power_on_scpi_isSet;
QString* power_off_scpi;
bool m_power_off_scpi_isSet;
QString* power_on_command;
bool m_power_on_command_isSet;
QString* power_off_command;
bool m_power_off_command_isSet;
float power_delay;
bool m_power_delay_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
qint32 stream_index;
bool m_stream_index_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
};
}
#endif /* SWGNoiseFigureSettings_H_ */