Merge branch 'master' of github.com:jankae/LibreVNA

This commit is contained in:
Jan Käberich 2022-04-05 14:10:34 +02:00
commit 4243e64f7f
45 changed files with 710 additions and 400 deletions

View file

@ -253,7 +253,7 @@ void AmplitudeCalDialog::RemoveAllPoints()
bool AmplitudeCalDialog::AddPoint(AmplitudeCalDialog::CorrectionPoint &p)
{
if (points.size() >= Device::Info().limits_maxAmplitudePoints) {
if (points.size() >= Device::Info(dev).limits_maxAmplitudePoints) {
// already at limit
return false;
}
@ -299,8 +299,8 @@ void AmplitudeCalDialog::AddPointDialog()
ui->stopFreq->setUnit("Hz");
ui->stopFreq->setPrefixes(" kMG");
ui->frequency->setValue(1000000000.0);
ui->startFreq->setValue(Device::Info().limits_minFreq);
ui->stopFreq->setValue(Device::Info().limits_maxFreq);
ui->startFreq->setValue(Device::Info(dev).limits_minFreq);
ui->stopFreq->setValue(Device::Info(dev).limits_maxFreq);
connect(ui->singlePoint, &QRadioButton::toggled, [=](bool single) {
ui->stopFreq->setEnabled(!single);
ui->startFreq->setEnabled(!single);

View file

@ -120,16 +120,6 @@ static constexpr Protocol::DeviceInfo defaultInfo = {
.FW_minor = 0,
.FW_patch = 0,
.HW_Revision = '0',
.extRefAvailable = 0,
.extRefInUse = 0,
.FPGA_configured = 0,
.source_locked = 0,
.LO1_locked = 0,
.ADC_overload = 0,
.unlevel = 0,
.temp_source = 0,
.temp_LO1 = 0,
.temp_MCU = 0,
.limits_minFreq = 0,
.limits_maxFreq = 6000000000,
.limits_minIFBW = 10,
@ -143,14 +133,25 @@ static constexpr Protocol::DeviceInfo defaultInfo = {
.limits_maxFreqHarmonic = 18000000000,
};
Protocol::DeviceInfo Device::lastInfo = defaultInfo;
static constexpr Protocol::DeviceStatusV1 defaultStatusV1 = {
.extRefAvailable = 0,
.extRefInUse = 0,
.FPGA_configured = 0,
.source_locked = 0,
.LO1_locked = 0,
.ADC_overload = 0,
.unlevel = 0,
.temp_source = 0,
.temp_LO1 = 0,
.temp_MCU = 0,
};
Device::Device(QString serial)
{
lastInfo = defaultInfo;
info = defaultInfo;
m_handle = nullptr;
lastInfoValid = false;
infoValid = false;
libusb_init(&m_context);
#if LIBUSB_API_VERSION >= 0x01000106
libusb_set_option(m_context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
@ -261,10 +262,10 @@ bool Device::Configure(Protocol::SpectrumAnalyzerSettings settings)
return SendPacket(p);
}
bool Device::SetManual(Protocol::ManualControl manual)
bool Device::SetManual(Protocol::ManualControlV1 manual)
{
Protocol::PacketInfo p;
p.type = Protocol::PacketType::ManualControl;
p.type = Protocol::PacketType::ManualControlV1;
p.manual = manual;
return SendPacket(p);
}
@ -393,25 +394,48 @@ void Device::SearchDevices(std::function<bool (libusb_device_handle *, QString)>
const Protocol::DeviceInfo &Device::Info()
{
return lastInfo;
return info;
}
const Protocol::DeviceInfo &Device::Info(Device *dev)
{
if(dev) {
return dev->Info();
} else {
return defaultInfo;
}
}
Protocol::DeviceStatusV1 &Device::StatusV1()
{
return status.v1;
}
const Protocol::DeviceStatusV1 &Device::StatusV1(Device *dev)
{
if(dev) {
return dev->StatusV1();
} else {
return defaultStatusV1;
}
}
QString Device::getLastDeviceInfoString()
{
QString ret;
if(!lastInfoValid) {
if(!infoValid) {
ret.append("No device information available yet");
} else {
ret.append("HW Rev.");
ret.append(lastInfo.HW_Revision);
ret.append(" FW "+QString::number(lastInfo.FW_major)+"."+QString::number(lastInfo.FW_minor)+"."+QString::number(lastInfo.FW_patch));
ret.append(" Temps: "+QString::number(lastInfo.temp_source)+"°C/"+QString::number(lastInfo.temp_LO1)+"°C/"+QString::number(lastInfo.temp_MCU)+"°C");
ret.append(info.HW_Revision);
ret.append(" FW "+QString::number(info.FW_major)+"."+QString::number(info.FW_minor)+"."+QString::number(info.FW_patch));
ret.append(" Temps: "+QString::number(status.v1.temp_source)+"°C/"+QString::number(status.v1.temp_LO1)+"°C/"+QString::number(status.v1.temp_MCU)+"°C");
ret.append(" Reference:");
if(lastInfo.extRefInUse) {
if(status.v1.extRefInUse) {
ret.append("External");
} else {
ret.append("Internal");
if(lastInfo.extRefAvailable) {
if(status.v1.extRefAvailable) {
ret.append(" (External available)");
}
}
@ -431,8 +455,8 @@ void Device::ReceivedData()
case Protocol::PacketType::Datapoint:
emit DatapointReceived(packet.datapoint);
break;
case Protocol::PacketType::Status:
emit ManualStatusReceived(packet.status);
case Protocol::PacketType::ManualStatusV1:
emit ManualStatusReceived(packet.manualStatusV1);
break;
case Protocol::PacketType::SpectrumAnalyzerResult:
emit SpectrumResultReceived(packet.spectrumResult);
@ -443,15 +467,19 @@ void Device::ReceivedData()
break;
case Protocol::PacketType::DeviceInfo:
if(packet.info.ProtocolVersion != Protocol::Version) {
if(!lastInfoValid) {
if(!infoValid) {
emit NeedsFirmwareUpdate(packet.info.ProtocolVersion, Protocol::Version);
}
} else {
lastInfo = packet.info;
info = packet.info;
}
lastInfoValid = true;
infoValid = true;
emit DeviceInfoUpdated();
break;
case Protocol::PacketType::DeviceStatusV1:
status.v1 = packet.statusV1;
emit DeviceStatusUpdated();
break;
case Protocol::PacketType::Ack:
emit AckReceived();
emit receivedAnswer(TransmissionResult::Ack);

View file

@ -13,7 +13,7 @@
#include <QTimer>
Q_DECLARE_METATYPE(Protocol::Datapoint);
Q_DECLARE_METATYPE(Protocol::ManualStatus);
Q_DECLARE_METATYPE(Protocol::ManualStatusV1);
Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult);
Q_DECLARE_METATYPE(Protocol::AmplitudeCorrectionPoint);
@ -61,23 +61,27 @@ public:
bool SendPacket(const Protocol::PacketInfo& packet, std::function<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 500);
bool Configure(Protocol::SweepSettings settings, std::function<void(TransmissionResult)> cb = nullptr);
bool Configure(Protocol::SpectrumAnalyzerSettings settings);
bool SetManual(Protocol::ManualControl manual);
bool SetManual(Protocol::ManualControlV1 manual);
bool SetIdle();
bool SendFirmwareChunk(Protocol::FirmwarePacket &fw);
bool SendCommandWithoutPayload(Protocol::PacketType type);
QString serial() const;
static const Protocol::DeviceInfo& Info();
const Protocol::DeviceInfo& Info();
static const Protocol::DeviceInfo& Info(Device *dev);
Protocol::DeviceStatusV1& StatusV1();
static const Protocol::DeviceStatusV1& StatusV1(Device *dev);
QString getLastDeviceInfoString();
// Returns serial numbers of all connected devices
static std::set<QString> GetDevices();
signals:
void DatapointReceived(Protocol::Datapoint);
void ManualStatusReceived(Protocol::ManualStatus);
void ManualStatusReceived(Protocol::ManualStatusV1);
void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult);
void AmplitudeCorrectionPointReceived(Protocol::AmplitudeCorrectionPoint);
void FrequencyCorrectionReceived(float ppm);
void DeviceInfoUpdated();
void DeviceStatusUpdated();
void ConnectionLost();
void AckReceived();
void NackReceived();
@ -122,8 +126,11 @@ private:
QString m_serial;
bool m_connected;
std::thread *m_receiveThread;
static Protocol::DeviceInfo lastInfo;
bool lastInfoValid;
Protocol::DeviceInfo info;
bool infoValid;
union {
Protocol::DeviceStatusV1 v1;
} status;
};
#endif // DEVICE_H

View file

@ -580,7 +580,7 @@ double ManualControlDialog::getRefPhase()
return ui->refphase->text().toDouble();
}
void ManualControlDialog::NewStatus(Protocol::ManualStatus status)
void ManualControlDialog::NewStatus(Protocol::ManualStatusV1 status)
{
// ADC values
ui->port1min->setText(QString::number(status.port1min));
@ -616,7 +616,7 @@ void ManualControlDialog::NewStatus(Protocol::ManualStatus status)
void ManualControlDialog::UpdateDevice()
{
Protocol::ManualControl m;
Protocol::ManualControlV1 m;
// Source highband
m.SourceHighCE = ui->SourceCE->isChecked();
m.SourceHighRFEN = ui->SourceRFEN->isChecked();

View file

@ -103,7 +103,7 @@ public:
double getRefPhase();
public slots:
void NewStatus(Protocol::ManualStatus status);
void NewStatus(Protocol::ManualStatusV1 status);
private:
void UpdateDevice();

View file

@ -2,11 +2,10 @@
#include <QSettings>
Generator::Generator(AppWindow *window)
: Mode(window, "Signal Generator")
, SCPINode("GENerator")
Generator::Generator(AppWindow *window, QString name)
: Mode(window, name, "GENerator")
{
central = new SignalgeneratorWidget(window);
central = new SignalgeneratorWidget(window->getDevice(), window);
auto pref = Preferences::getInstance();

View file

@ -5,13 +5,15 @@
#include "signalgenwidget.h"
#include "scpi.h"
class Generator : public Mode, public SCPINode
class Generator : public Mode
{
public:
Generator(AppWindow *window);
Generator(AppWindow *window, QString name = "Signal Generator");
void deactivate() override;
void initializeDevice() override;
virtual Type getType() override { return Type::SG;}
// Nothing to do for now
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;

View file

@ -2,9 +2,10 @@
#include "ui_signalgenwidget.h"
SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
SignalgeneratorWidget::SignalgeneratorWidget(Device *&dev, QWidget *parent) :
QWidget(parent),
ui(new Ui::SignalgeneratorWidget)
ui(new Ui::SignalgeneratorWidget),
dev(dev)
{
ui->setupUi(this);
ui->frequency->setUnit("Hz");
@ -31,16 +32,16 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
ui->steps->setPrecision(0);
connect(ui->frequency, &SIUnitEdit::valueChanged, [=](double newval) {
if(newval < Device::Info().limits_minFreq) {
newval = Device::Info().limits_minFreq;
} else if (newval > Device::Info().limits_maxFreq) {
newval = Device::Info().limits_maxFreq;
if(newval < Device::Info(dev).limits_minFreq) {
newval = Device::Info(dev).limits_minFreq;
} else if (newval > Device::Info(dev).limits_maxFreq) {
newval = Device::Info(dev).limits_maxFreq;
}
ui->frequency->setValueQuiet(newval);
if (newval < ui->span->value()/2)
ui->span->setValueQuiet(newval/2);
if (newval + ui->span->value()/2 > Device::Info().limits_maxFreq)
ui->span->setValueQuiet((Device::Info().limits_maxFreq - newval)*2);
if (newval + ui->span->value()/2 > Device::Info(dev).limits_maxFreq)
ui->span->setValueQuiet((Device::Info(dev).limits_maxFreq - newval)*2);
newval = ui->frequency->value() - ui->span->value()/2;
ui->current->setValueQuiet(newval);
emit SettingsChanged();
@ -49,8 +50,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
connect(ui->span, &SIUnitEdit::valueChanged, [=](double newval) {
if(newval < 0 ) {
newval = 0;
} else if (newval > Device::Info().limits_maxFreq - Device::Info().limits_minFreq) {
newval = Device::Info().limits_maxFreq - Device::Info().limits_minFreq;
} else if (newval > Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq) {
newval = Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq;
}
ui->span->setValueQuiet(newval);
@ -59,8 +60,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
ui->frequency->setValueQuiet(ui->span->value()/2);
}
newF = ui->frequency->value() + ui->span->value()/2;
if (newF > Device::Info().limits_maxFreq) {
ui->frequency->setValueQuiet(Device::Info().limits_maxFreq - ui->span->value()/2);
if (newF > Device::Info(dev).limits_maxFreq) {
ui->frequency->setValueQuiet(Device::Info(dev).limits_maxFreq - ui->span->value()/2);
}
newval = ui->frequency->value() - ui->span->value()/2;
@ -71,8 +72,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) :
connect(ui->current, &SIUnitEdit::valueChanged, [=](double newval) {
if(newval < 0 ) {
newval = 0;
} else if (newval > Device::Info().limits_maxFreq - Device::Info().limits_minFreq) {
newval = Device::Info().limits_maxFreq - Device::Info().limits_minFreq;
} else if (newval > Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq) {
newval = Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq;
}
ui->current->setValueQuiet(newval);
emit SettingsChanged();

View file

@ -15,7 +15,7 @@ class SignalgeneratorWidget : public QWidget, public Savable
Q_OBJECT
public:
explicit SignalgeneratorWidget(QWidget *parent = nullptr);
explicit SignalgeneratorWidget(Device*&dev, QWidget *parent = nullptr);
~SignalgeneratorWidget();
Protocol::GeneratorSettings getDeviceStatus();
@ -36,6 +36,7 @@ protected:
private:
Ui::SignalgeneratorWidget *ui;
int m_timerId;
Device*&dev;
};
#endif // SIGNALGENERATOR_H

View file

@ -45,9 +45,8 @@
#include <fstream>
#include <QDateTime>
SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
: Mode(window, "Spectrum Analyzer"),
SCPINode("SA"),
SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
: Mode(window, name, "SA"),
central(new TileWidget(traceModel, window))
{
averages = 1;
@ -422,6 +421,10 @@ using namespace std;
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
{
if(Mode::getActiveMode() != this) {
return;
}
if(d.pointNum >= settings.pointNum) {
qWarning() << "Ignoring point with too large point number (" << d.pointNum << ")";
return;
@ -555,14 +558,14 @@ void SpectrumAnalyzer::SetStopFreq(double freq)
void SpectrumAnalyzer::SetCenterFreq(double freq)
{
auto old_span = settings.f_stop - settings.f_start;
if (freq - old_span / 2 <= Device::Info().limits_minFreq) {
if (freq - old_span / 2 <= Device::Info(window->getDevice()).limits_minFreq) {
// would shift start frequency below minimum
settings.f_start = 0;
settings.f_stop = 2 * freq;
} else if(freq + old_span / 2 >= Device::Info().limits_maxFreq) {
} else if(freq + old_span / 2 >= Device::Info(window->getDevice()).limits_maxFreq) {
// would shift stop frequency above maximum
settings.f_start = 2 * freq - Device::Info().limits_maxFreq;
settings.f_stop = Device::Info().limits_maxFreq;
settings.f_start = 2 * freq - Device::Info(window->getDevice()).limits_maxFreq;
settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq;
} else {
settings.f_start = freq - old_span / 2;
settings.f_stop = freq + old_span / 2;
@ -573,14 +576,14 @@ void SpectrumAnalyzer::SetCenterFreq(double freq)
void SpectrumAnalyzer::SetSpan(double span)
{
auto old_center = (settings.f_start + settings.f_stop) / 2;
if(old_center < Device::Info().limits_minFreq + span / 2) {
if(old_center < Device::Info(window->getDevice()).limits_minFreq + span / 2) {
// would shift start frequency below minimum
settings.f_start = Device::Info().limits_minFreq;
settings.f_stop = Device::Info().limits_minFreq + span;
} else if(old_center > Device::Info().limits_maxFreq - span / 2) {
settings.f_start = Device::Info(window->getDevice()).limits_minFreq;
settings.f_stop = Device::Info(window->getDevice()).limits_minFreq + span;
} else if(old_center > Device::Info(window->getDevice()).limits_maxFreq - span / 2) {
// would shift stop frequency above maximum
settings.f_start = Device::Info().limits_maxFreq - span;
settings.f_stop = Device::Info().limits_maxFreq;
settings.f_start = Device::Info(window->getDevice()).limits_maxFreq - span;
settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq;
} else {
settings.f_start = old_center - span / 2;
settings.f_stop = settings.f_start + span;
@ -590,8 +593,8 @@ void SpectrumAnalyzer::SetSpan(double span)
void SpectrumAnalyzer::SetFullSpan()
{
settings.f_start = Device::Info().limits_minFreq;
settings.f_stop = Device::Info().limits_maxFreq;
settings.f_start = Device::Info(window->getDevice()).limits_minFreq;
settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq;
ConstrainAndUpdateFrequencies();
}
@ -619,10 +622,10 @@ void SpectrumAnalyzer::SpanZoomOut()
void SpectrumAnalyzer::SetRBW(double bandwidth)
{
if(bandwidth > Device::Info().limits_maxRBW) {
bandwidth = Device::Info().limits_maxRBW;
} else if(bandwidth < Device::Info().limits_minRBW) {
bandwidth = Device::Info().limits_minRBW;
if(bandwidth > Device::Info(window->getDevice()).limits_maxRBW) {
bandwidth = Device::Info(window->getDevice()).limits_maxRBW;
} else if(bandwidth < Device::Info(window->getDevice()).limits_minRBW) {
bandwidth = Device::Info(window->getDevice()).limits_minRBW;
}
settings.RBW = bandwidth;
emit RBWChanged(settings.RBW);
@ -690,10 +693,10 @@ void SpectrumAnalyzer::SetTGPort(int port)
void SpectrumAnalyzer::SetTGLevel(double level)
{
if(level > Device::Info().limits_cdbm_max / 100.0) {
level = Device::Info().limits_cdbm_max / 100.0;
} else if(level < Device::Info().limits_cdbm_min / 100.0) {
level = Device::Info().limits_cdbm_min / 100.0;
if(level > Device::Info(window->getDevice()).limits_cdbm_max / 100.0) {
level = Device::Info(window->getDevice()).limits_cdbm_max / 100.0;
} else if(level < Device::Info(window->getDevice()).limits_cdbm_min / 100.0) {
level = Device::Info(window->getDevice()).limits_cdbm_min / 100.0;
}
emit TGLevelChanged(level);
settings.trackingPower = level * 100;
@ -1015,24 +1018,24 @@ void SpectrumAnalyzer::UpdateAverageCount()
void SpectrumAnalyzer::ConstrainAndUpdateFrequencies()
{
if(settings.f_stop > Device::Info().limits_maxFreq) {
settings.f_stop = Device::Info().limits_maxFreq;
if(settings.f_stop > Device::Info(window->getDevice()).limits_maxFreq) {
settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq;
}
if(settings.f_start > settings.f_stop) {
settings.f_start = settings.f_stop;
}
if(settings.f_start < Device::Info().limits_minFreq) {
settings.f_start = Device::Info().limits_minFreq;
if(settings.f_start < Device::Info(window->getDevice()).limits_minFreq) {
settings.f_start = Device::Info(window->getDevice()).limits_minFreq;
}
bool trackingOffset_limited = false;
if(settings.f_stop + settings.trackingGeneratorOffset > Device::Info().limits_maxFreq) {
if(settings.f_stop + settings.trackingGeneratorOffset > Device::Info(window->getDevice()).limits_maxFreq) {
trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_maxFreq - settings.f_stop;
settings.trackingGeneratorOffset = Device::Info(window->getDevice()).limits_maxFreq - settings.f_stop;
}
if((long) settings.f_start + settings.trackingGeneratorOffset < (long) Device::Info().limits_minFreq) {
if((long) settings.f_start + settings.trackingGeneratorOffset < (long) Device::Info(window->getDevice()).limits_minFreq) {
trackingOffset_limited = true;
settings.trackingGeneratorOffset = Device::Info().limits_minFreq - settings.f_start;
settings.trackingGeneratorOffset = Device::Info(window->getDevice()).limits_minFreq - settings.f_start;
}
if(trackingOffset_limited) {
InformationBox::ShowMessage("Warning", "The selected tracking generator offset is not reachable for all frequencies with the current span. "

View file

@ -12,15 +12,17 @@
#include <QComboBox>
#include <QCheckBox>
class SpectrumAnalyzer : public Mode, public SCPINode
class SpectrumAnalyzer : public Mode
{
Q_OBJECT
public:
SpectrumAnalyzer(AppWindow *window);
SpectrumAnalyzer(AppWindow *window, QString name = "Spectrum Analyzer");
void deactivate() override;
void initializeDevice() override;
virtual Type getType() override { return Type::SA;}
// Only save/load user changeable stuff, no need to save the widgets/mode name etc.
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;

View file

@ -51,9 +51,8 @@
#include <QErrorMessage>
#include <QDebug>
VNA::VNA(AppWindow *window)
: Mode(window, "Vector Network Analyzer"),
SCPINode("VNA"),
VNA::VNA(AppWindow *window, QString name)
: Mode(window, name, "VNA"),
deembedding(traceModel),
deembedding_active(false),
central(new TileWidget(traceModel))
@ -790,6 +789,11 @@ using namespace std;
void VNA::NewDatapoint(Protocol::Datapoint d)
{
if(Mode::getActiveMode() != this) {
// ignore
return;
}
if(changingSettings) {
// already setting new sweep settings, ignore incoming points from old settings
return;
@ -997,14 +1001,14 @@ void VNA::SetStopFreq(double freq)
void VNA::SetCenterFreq(double freq)
{
auto old_span = settings.Freq.stop - settings.Freq.start;
if (freq - old_span / 2 <= Device::Info().limits_minFreq) {
if (freq - old_span / 2 <= Device::Info(window->getDevice()).limits_minFreq) {
// would shift start frequency below minimum
settings.Freq.start = 0;
settings.Freq.stop = 2 * freq;
} else if(freq + old_span / 2 >= Device::Info().limits_maxFreq) {
} else if(freq + old_span / 2 >= Device::Info(window->getDevice()).limits_maxFreq) {
// would shift stop frequency above maximum
settings.Freq.start = 2 * freq - Device::Info().limits_maxFreq;
settings.Freq.stop = Device::Info().limits_maxFreq;
settings.Freq.start = 2 * freq - Device::Info(window->getDevice()).limits_maxFreq;
settings.Freq.stop = Device::Info(window->getDevice()).limits_maxFreq;
} else {
settings.Freq.start = freq - old_span / 2;
settings.Freq.stop = freq + old_span / 2;
@ -1014,12 +1018,12 @@ void VNA::SetCenterFreq(double freq)
void VNA::SetSpan(double span)
{
auto maxFreq = Preferences::getInstance().Acquisition.harmonicMixing ? Device::Info().limits_maxFreqHarmonic : Device::Info().limits_maxFreq;
auto maxFreq = Preferences::getInstance().Acquisition.harmonicMixing ? Device::Info(window->getDevice()).limits_maxFreqHarmonic : Device::Info(window->getDevice()).limits_maxFreq;
auto old_center = (settings.Freq.start + settings.Freq.stop) / 2;
if(old_center < Device::Info().limits_minFreq + span / 2) {
if(old_center < Device::Info(window->getDevice()).limits_minFreq + span / 2) {
// would shift start frequency below minimum
settings.Freq.start = Device::Info().limits_minFreq;
settings.Freq.stop = Device::Info().limits_minFreq + span;
settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq;
settings.Freq.stop = Device::Info(window->getDevice()).limits_minFreq + span;
} else if(old_center > maxFreq - span / 2) {
// would shift stop frequency above maximum
settings.Freq.start = maxFreq - span;
@ -1033,8 +1037,8 @@ void VNA::SetSpan(double span)
void VNA::SetFullSpan()
{
settings.Freq.start = Device::Info().limits_minFreq;
settings.Freq.stop = Device::Info().limits_maxFreq;
settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq;
settings.Freq.stop = Device::Info(window->getDevice()).limits_maxFreq;
ConstrainAndUpdateFrequencies();
}
@ -1072,10 +1076,10 @@ void VNA::SetLogSweep(bool log)
void VNA::SetSourceLevel(double level)
{
if(level > Device::Info().limits_cdbm_max / 100.0) {
level = Device::Info().limits_cdbm_max / 100.0;
} else if(level < Device::Info().limits_cdbm_min / 100.0) {
level = Device::Info().limits_cdbm_min / 100.0;
if(level > Device::Info(window->getDevice()).limits_cdbm_max / 100.0) {
level = Device::Info(window->getDevice()).limits_cdbm_max / 100.0;
} else if(level < Device::Info(window->getDevice()).limits_cdbm_min / 100.0) {
level = Device::Info(window->getDevice()).limits_cdbm_min / 100.0;
}
emit sourceLevelChanged(level);
settings.Freq.excitation_power = level;
@ -1105,15 +1109,15 @@ void VNA::SetPowerSweepFrequency(double freq)
void VNA::SetPoints(unsigned int points)
{
unsigned int maxPoints = Preferences::getInstance().Acquisition.allowSegmentedSweep ? UINT16_MAX : Device::Info().limits_maxPoints;
unsigned int maxPoints = Preferences::getInstance().Acquisition.allowSegmentedSweep ? UINT16_MAX : Device::Info(window->getDevice()).limits_maxPoints;
if(points > maxPoints) {
points = maxPoints;
} else if (points < 2) {
points = 2;
}
if (points > Device::Info().limits_maxPoints) {
if (points > Device::Info(window->getDevice()).limits_maxPoints) {
// needs segmented sweep
settings.segments = ceil((double) points / Device::Info().limits_maxPoints);
settings.segments = ceil((double) points / Device::Info(window->getDevice()).limits_maxPoints);
settings.activeSegment = 0;
} else {
// can fit all points into one segment
@ -1127,10 +1131,10 @@ void VNA::SetPoints(unsigned int points)
void VNA::SetIFBandwidth(double bandwidth)
{
if(bandwidth > Device::Info().limits_maxIFBW) {
bandwidth = Device::Info().limits_maxIFBW;
} else if(bandwidth < Device::Info().limits_minIFBW) {
bandwidth = Device::Info().limits_minIFBW;
if(bandwidth > Device::Info(window->getDevice()).limits_maxIFBW) {
bandwidth = Device::Info(window->getDevice()).limits_maxIFBW;
} else if(bandwidth < Device::Info(window->getDevice()).limits_minIFBW) {
bandwidth = Device::Info(window->getDevice()).limits_minIFBW;
}
settings.bandwidth = bandwidth;
emit IFBandwidthChanged(settings.bandwidth);
@ -1478,9 +1482,9 @@ void VNA::ConstrainAndUpdateFrequencies()
auto pref = Preferences::getInstance();
double maxFreq;
if(pref.Acquisition.harmonicMixing) {
maxFreq = Device::Info().limits_maxFreqHarmonic;
maxFreq = Device::Info(window->getDevice()).limits_maxFreqHarmonic;
} else {
maxFreq = Device::Info().limits_maxFreq;
maxFreq = Device::Info(window->getDevice()).limits_maxFreq;
}
if(settings.Freq.stop > maxFreq) {
settings.Freq.stop = maxFreq;
@ -1488,8 +1492,8 @@ void VNA::ConstrainAndUpdateFrequencies()
if(settings.Freq.start > settings.Freq.stop) {
settings.Freq.start = settings.Freq.stop;
}
if(settings.Freq.start < Device::Info().limits_minFreq) {
settings.Freq.start = Device::Info().limits_minFreq;
if(settings.Freq.start < Device::Info(window->getDevice()).limits_minFreq) {
settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq;
}
emit startFreqChanged(settings.Freq.start);
emit stopFreqChanged(settings.Freq.stop);

View file

@ -13,17 +13,19 @@
#include <QWidget>
#include <functional>
class VNA : public Mode, public SCPINode
class VNA : public Mode
{
Q_OBJECT
public:
VNA(AppWindow *window);
VNA(AppWindow *window, QString name = "Vector Network Analyzer");
void deactivate() override;
void initializeDevice() override;
void deviceDisconnected() override;
void shutdown() override;
virtual Type getType() override { return Type::VNA;}
// Only save/load user changeable stuff, no need to save the widgets/mode name etc.
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;

View file

@ -134,9 +134,9 @@ AppWindow::AppWindow(QWidget *parent)
// Create GUI modes
central = new QStackedWidget;
setCentralWidget(central);
vna = new VNA(this);
generator = new Generator(this);
spectrumAnalyzer = new SpectrumAnalyzer(this);
auto vna = new VNA(this);
auto generator = new Generator(this);
auto spectrumAnalyzer = new SpectrumAnalyzer(this);
// UI connections
connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList);
@ -235,7 +235,7 @@ AppWindow::AppWindow(QWidget *parent)
vna->activate();
qRegisterMetaType<Protocol::Datapoint>("Datapoint");
qRegisterMetaType<Protocol::ManualStatus>("Manual");
qRegisterMetaType<Protocol::ManualStatusV1>("ManualV1");
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumAnalyzerResult");
qRegisterMetaType<Protocol::AmplitudeCorrectionPoint>("AmplitudeCorrection");
@ -278,9 +278,9 @@ void AppWindow::closeEvent(QCloseEvent *event)
if(pref.Startup.UseSetupFile && pref.Startup.AutosaveSetupFile) {
SaveSetup(pref.Startup.SetupFile);
}
vna->shutdown();
generator->shutdown();
spectrumAnalyzer->shutdown();
for(auto m : Mode::getModes()) {
m->shutdown();
}
delete device;
QSettings settings;
settings.setValue("geometry", saveGeometry());
@ -309,7 +309,7 @@ bool AppWindow::ConnectToDevice(QString serial)
UpdateStatusBar(AppWindow::DeviceStatusBar::Connected);
connect(device, &Device::LogLineReceived, &deviceLog, &DeviceLog::addLine);
connect(device, &Device::ConnectionLost, this, &AppWindow::DeviceConnectionLost);
connect(device, &Device::DeviceInfoUpdated, this, &AppWindow::DeviceInfoUpdated);
connect(device, &Device::DeviceStatusUpdated, this, &AppWindow::DeviceStatusUpdated);
connect(device, &Device::NeedsFirmwareUpdate, this, &AppWindow::DeviceNeedsUpdate);
ui->actionDisconnect->setEnabled(true);
ui->actionManual_Control->setEnabled(true);
@ -465,7 +465,7 @@ void AppWindow::SetupSCPI()
}
return "";
}, [=](QStringList) -> QString {
switch(Device::Info().extRefInUse) {
switch(Device::StatusV1(getDevice()).extRefInUse) {
case 0: return "INT";
case 1: return "EXT";
default: return "ERROR";
@ -475,88 +475,89 @@ void AppWindow::SetupSCPI()
if (params.size() != 1) {
return "ERROR";
}
Mode *mode = nullptr;
if (params[0] == "VNA") {
vna->activate();
mode = Mode::findFirstOfType(Mode::Type::VNA);
} else if(params[0] == "GEN") {
generator->activate();
mode = Mode::findFirstOfType(Mode::Type::SG);
} else if(params[0] == "SA") {
spectrumAnalyzer->activate();
mode = Mode::findFirstOfType(Mode::Type::SA);
} else {
return "INVALID MDOE";
}
return "";
}, [=](QStringList) -> QString {
auto active = Mode::getActiveMode();
if(active == vna) {
return "VNA";
} else if(active == generator) {
return "GEN";
} else if(active == spectrumAnalyzer) {
return "SA";
if(mode) {
mode->activate();
return "";
} else {
return "ERROR";
}
}, [=](QStringList) -> QString {
auto active = Mode::getActiveMode();
if(active) {
switch(active->getType()) {
case Mode::Type::VNA: return "VNA";
case Mode::Type::SG: return "SG";
case Mode::Type::SA: return "SA";
}
}
return "ERROR";
}));
auto scpi_status = new SCPINode("STAtus");
scpi_dev->add(scpi_status);
scpi_status->add(new SCPICommand("UNLOcked", nullptr, [=](QStringList){
bool locked = Device::Info().source_locked && Device::Info().LO1_locked;
bool locked = Device::StatusV1(getDevice()).source_locked && Device::StatusV1(getDevice()).LO1_locked;
return locked ? "FALSE" : "TRUE";
}));
scpi_status->add(new SCPICommand("ADCOVERload", nullptr, [=](QStringList){
return Device::Info().ADC_overload ? "TRUE" : "FALSE";
return Device::StatusV1(getDevice()).ADC_overload ? "TRUE" : "FALSE";
}));
scpi_status->add(new SCPICommand("UNLEVel", nullptr, [=](QStringList){
return Device::Info().unlevel ? "TRUE" : "FALSE";
return Device::StatusV1(getDevice()).unlevel ? "TRUE" : "FALSE";
}));
auto scpi_info = new SCPINode("INFo");
scpi_dev->add(scpi_info);
scpi_info->add(new SCPICommand("FWREVision", nullptr, [=](QStringList){
return QString::number(Device::Info().FW_major)+"."+QString::number(Device::Info().FW_minor)+"."+QString::number(Device::Info().FW_patch);
return QString::number(Device::Info(getDevice()).FW_major)+"."+QString::number(Device::Info(getDevice()).FW_minor)+"."+QString::number(Device::Info(getDevice()).FW_patch);
}));
scpi_info->add(new SCPICommand("HWREVision", nullptr, [=](QStringList){
return QString(Device::Info().HW_Revision);
return QString(Device::Info(getDevice()).HW_Revision);
}));
scpi_info->add(new SCPICommand("TEMPeratures", nullptr, [=](QStringList){
return QString::number(Device::Info().temp_source)+"/"+QString::number(Device::Info().temp_LO1)+"/"+QString::number(Device::Info().temp_MCU);
return QString::number(Device::StatusV1(getDevice()).temp_source)+"/"+QString::number(Device::StatusV1(getDevice()).temp_LO1)+"/"+QString::number(Device::StatusV1(getDevice()).temp_MCU);
}));
auto scpi_limits = new SCPINode("LIMits");
scpi_info->add(scpi_limits);
scpi_limits->add(new SCPICommand("MINFrequency", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_minFreq);
return QString::number(Device::Info(getDevice()).limits_minFreq);
}));
scpi_limits->add(new SCPICommand("MAXFrequency", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_maxFreq);
return QString::number(Device::Info(getDevice()).limits_maxFreq);
}));
scpi_limits->add(new SCPICommand("MINIFBW", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_minIFBW);
return QString::number(Device::Info(getDevice()).limits_minIFBW);
}));
scpi_limits->add(new SCPICommand("MAXIFBW", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_maxIFBW);
return QString::number(Device::Info(getDevice()).limits_maxIFBW);
}));
scpi_limits->add(new SCPICommand("MAXPoints", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_maxPoints);
return QString::number(Device::Info(getDevice()).limits_maxPoints);
}));
scpi_limits->add(new SCPICommand("MINPOWer", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_cdbm_min / 100.0);
return QString::number(Device::Info(getDevice()).limits_cdbm_min / 100.0);
}));
scpi_limits->add(new SCPICommand("MAXPOWer", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_cdbm_max / 100.0);
return QString::number(Device::Info(getDevice()).limits_cdbm_max / 100.0);
}));
scpi_limits->add(new SCPICommand("MINRBW", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_minRBW);
return QString::number(Device::Info(getDevice()).limits_minRBW);
}));
scpi_limits->add(new SCPICommand("MAXRBW", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_maxRBW);
return QString::number(Device::Info(getDevice()).limits_maxRBW);
}));
scpi_limits->add(new SCPICommand("MAXHARMonicfrequency", nullptr, [=](QStringList){
return QString::number(Device::Info().limits_maxFreqHarmonic);
return QString::number(Device::Info(getDevice()).limits_maxFreqHarmonic);
}));
scpi.add(vna);
scpi.add(generator);
scpi.add(spectrumAnalyzer);
auto scpi_manual = new SCPINode("MANual");
scpi_manual->add(new SCPICommand("STArt",[=](QStringList) -> QString {
StartManualControl();
@ -798,6 +799,11 @@ void AppWindow::StopTCPServer()
server = nullptr;
}
SCPI* AppWindow::getSCPI()
{
return &scpi;
}
int AppWindow::UpdateDeviceList()
{
deviceActionGroup->setExclusive(true);
@ -918,7 +924,7 @@ void AppWindow::DeviceNeedsUpdate(int reported, int expected)
}
}
void AppWindow::DeviceInfoUpdated()
void AppWindow::DeviceStatusUpdated()
{
UpdateStatusBar(DeviceStatusBar::Updated);
}
@ -963,10 +969,18 @@ void AppWindow::SaveSetup(QString filename)
nlohmann::json AppWindow::SaveSetup()
{
nlohmann::json j;
j["activeMode"] = Mode::getActiveMode()->getName().toStdString();
j["VNA"] = vna->toJSON();
j["Generator"] = generator->toJSON();
j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON();
nlohmann::json jm;
for(auto m : Mode::getModes()) {
nlohmann::json jmode;
jmode["type"] = Mode::TypeToName(m->getType()).toStdString();
jmode["name"] = m->getName().toStdString();
jmode["settings"] = m->toJSON();
jm.push_back(jmode);
}
j["Modes"] = jm;
if(Mode::getActiveMode()) {
j["activeMode"] = Mode::getActiveMode()->getName().toStdString();
}
nlohmann::json ref;
ref["Mode"] = toolbars.reference.type->currentText().toStdString();
ref["Output"] = toolbars.reference.outFreq->currentText().toStdString();
@ -1003,20 +1017,36 @@ void AppWindow::LoadSetup(nlohmann::json j)
toolbars.reference.type->setCurrentText(QString::fromStdString(j["Reference"].value("Mode", "Int")));
toolbars.reference.outFreq->setCurrentText(QString::fromStdString(j["Reference"].value("Output", "Off")));
}
while(Mode::getModes().size() > 0) {
delete Mode::getModes()[0];
}
// old style VNA/Generator/Spectrum Analyzer settings
if(j.contains("VNA")) {
auto vna = new VNA(this);
vna->fromJSON(j["VNA"]);
}
if(j.contains("Generator")) {
auto generator = new Generator(this);
generator->fromJSON(j["Generator"]);
}
if(j.contains("SpectrumAnalyzer")) {
auto spectrumAnalyzer = new SpectrumAnalyzer(this);
spectrumAnalyzer->fromJSON(j["SpectrumAnalyzer"]);
}
if(j.contains("Modes")) {
for(auto jm : j["Modes"]) {
auto type = Mode::TypeFromName(QString::fromStdString(jm.value("type", "Invalid")));
if(type != Mode::Type::Last && jm.contains("settings")) {
auto m = Mode::createNew(this, QString::fromStdString(jm.value("name", "")), type);
m->fromJSON(jm["settings"]);
}
}
}
// activate the correct mode
QString modeName = QString::fromStdString(j.value("activeMode", ""));
std::vector<Mode*> modes = {vna, generator, spectrumAnalyzer};
for(auto m : modes) {
for(auto m : Mode::getModes()) {
if(m->getName() == modeName) {
m->activate();
break;
@ -1024,7 +1054,7 @@ void AppWindow::LoadSetup(nlohmann::json j)
}
}
Device *AppWindow::getDevice() const
Device *&AppWindow::getDevice()
{
return device;
}
@ -1104,14 +1134,12 @@ void AppWindow::UpdateStatusBar(DeviceStatusBar status)
break;
case DeviceStatusBar::Updated:
lDeviceInfo.setText(device->getLastDeviceInfoString());
lADCOverload.setVisible(device->Info().ADC_overload);
lUnlevel.setVisible(device->Info().unlevel);
lUnlock.setVisible(!device->Info().LO1_locked || !device->Info().source_locked);
lADCOverload.setVisible(device->StatusV1().ADC_overload);
lUnlevel.setVisible(device->StatusV1().unlevel);
lUnlock.setVisible(!device->StatusV1().LO1_locked || !device->StatusV1().source_locked);
break;
default:
// invalid status
break;
}
}

View file

@ -41,13 +41,15 @@ public:
Ui::MainWindow *getUi() const;
QStackedWidget *getCentral() const;
Device *getDevice() const;
Device*&getDevice();
const QString& getAppVersion() const;
const QString& getAppGitHash() const;
static bool showGUI();
SCPI* getSCPI();
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
@ -59,7 +61,7 @@ private slots:
void UpdateAcquisitionFrequencies();
void StartFirmwareUpdateDialog();
void DeviceNeedsUpdate(int reported, int expected);
void DeviceInfoUpdated();
void DeviceStatusUpdated();
void SourceCalibrationDialog();
void ReceiverCalibrationDialog();
void FrequencyCalibrationDialog();
@ -100,11 +102,6 @@ private:
ManualControlDialog *manual;
// Modes
VNA *vna;
Generator *generator;
SpectrumAnalyzer *spectrumAnalyzer;
// Status bar widgets
QLabel lConnectionStatus;
QLabel lDeviceInfo;

View file

@ -1,44 +1,115 @@
#include "mode.h"
#include "Generator/generator.h"
#include "VNA/vna.h"
#include "SpectrumAnalyzer/spectrumanalyzer.h"
#include "CustomWidgets/informationbox.h"
#include "ui_main.h"
#include <QPushButton>
#include <QSettings>
#include <QDebug>
#include <QFileDialog>
#include <QInputDialog>
std::vector<Mode*> Mode::modes;
Mode* Mode::activeMode = nullptr;
QTabBar* Mode::tabbar = nullptr;
QWidget* Mode::cornerWidget = nullptr;
QButtonGroup* Mode::modeButtonGroup = nullptr;
//QButtonGroup* Mode::modeButtonGroup = nullptr;
Mode::Mode(AppWindow *window, QString name)
Mode::Mode(AppWindow *window, QString name, QString SCPIname)
: QObject(window),
SCPINode(SCPIname),
window(window),
name(name),
central(nullptr)
{
{
if(!nameAllowed(name)) {
throw std::runtime_error("Unable to create mode, name already taken");
}
// Create mode switch button
auto modeSwitch = new QPushButton(name);
modeSwitch->setCheckable(true);
modeSwitch->setMaximumHeight(window->menuBar()->height());
if(!cornerWidget) {
// this is the first created mode, initialize corner widget and set this mode as active
modeSwitch->setChecked(true);
cornerWidget = new QWidget;
cornerWidget = new QWidget();
cornerWidget->setLayout(new QHBoxLayout);
cornerWidget->layout()->setSpacing(0);
cornerWidget->layout()->setMargin(0);
cornerWidget->layout()->setContentsMargins(0,0,0,0);
window->menuBar()->setCornerWidget(cornerWidget);
modeButtonGroup = new QButtonGroup;
// window->menuBar()->setMaximumHeight(window->menuBar()->height());
}
cornerWidget->layout()->addWidget(modeSwitch);
modeButtonGroup->addButton(modeSwitch);
cornerWidget->setMaximumHeight(window->menuBar()->height());
connect(modeSwitch, &QPushButton::clicked, [=](){
activate();
});
tabbar = new QTabBar;
tabbar->setTabsClosable(true);
tabbar->setStyleSheet("QTabBar::tab { height: "+QString::number(window->menuBar()->height())+"px;}");
cornerWidget->layout()->addWidget(tabbar);
auto bAdd = new QPushButton();
QIcon icon;
QString iconThemeName = QString::fromUtf8("list-add");
if (QIcon::hasThemeIcon(iconThemeName)) {
icon = QIcon::fromTheme(iconThemeName);
} else {
icon.addFile(QString::fromUtf8(":/icons/add.png"), QSize(), QIcon::Normal, QIcon::Off);
}
bAdd->setIcon(icon);
auto mAdd = new QMenu();
for(unsigned int i=0;i<(int) Type::Last;i++) {
auto type = (Type) i;
auto action = new QAction(TypeToName(type));
mAdd->addAction(action);
connect(action, &QAction::triggered, [=](){
bool ok;
QString text = QInputDialog::getText(window, "Create new "+TypeToName(type)+" tab",
"Name:", QLineEdit::Normal,
TypeToName(type), &ok);
if(ok) {
if(!nameAllowed(text)) {
InformationBox::ShowError("Name collision", "Unable to create tab, no duplicate names allowed");
} else {
auto mode = Mode::createNew(window, text, type);
mode->activate();
}
}
});
}
bAdd->setMenu(mAdd);
bAdd->setMaximumHeight(window->menuBar()->height());
bAdd->setMaximumWidth(40);
cornerWidget->layout()->addWidget(bAdd);
window->menuBar()->setCornerWidget(cornerWidget);
connect(tabbar, &QTabBar::currentChanged, [=](int index){
modes[index]->activate();
});
connect(tabbar, &QTabBar::tabCloseRequested, [=](int index){
delete modes[index];
});
}
modes.push_back(this);
tabbar->blockSignals(true);
tabbar->insertTab(tabbar->count(), name);
tabbar->blockSignals(false);
window->getSCPI()->add(this);
}
Mode::~Mode()
{
window->getSCPI()->remove(this);
if(activeMode == this) {
deactivate();
}
auto index = findTabIndex();
tabbar->blockSignals(true);
tabbar->removeTab(index);
tabbar->blockSignals(false);
modes.erase(modes.begin() + index);
if(modes.size() > 0) {
modes[tabbar->currentIndex()]->activate();
}
window->getCentral()->removeWidget(central);
}
void Mode::activate()
@ -89,14 +160,10 @@ void Mode::activate()
}
activeMode = this;
// force activation of correct pushbutton in case the mode switch was done via script/setup load.
// This will trigger a second activation of this mode in the signal of the button, but since it is
// force activation of correct tab in case the mode switch was done via script/setup load.
// This will trigger a second activation of this mode in the signal of the tab bar, but since it is
// already the active mode, this function will just return -> no recursion
for(auto b : modeButtonGroup->buttons()) {
if(b->text() == name) {
b->click();
}
}
tabbar->setCurrentIndex(findTabIndex());
if(window->getDevice()) {
initializeDevice();
@ -131,6 +198,9 @@ void Mode::deactivate()
}
qDebug() << "Deactivated mode" << name;
if(window->getDevice()) {
window->getDevice()->SetIdle();
}
activeMode = nullptr;
}
@ -139,6 +209,26 @@ Mode *Mode::getActiveMode()
return activeMode;
}
QString Mode::TypeToName(Mode::Type t)
{
switch(t) {
case Type::VNA: return "Vector Network Analyzer";
case Type::SG: return "Signal Generator";
case Type::SA: return "Spectrum Analyzer";
default: return "Invalid";
}
}
Mode::Type Mode::TypeFromName(QString s)
{
for(unsigned int i=0;i<(int)Type::Last;i++) {
if(s == TypeToName((Type) i)) {
return (Type) i;
}
}
return Type::Last;
}
void Mode::saveSreenshot()
{
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, QFileDialog::DontUseNativeDialog);
@ -153,6 +243,27 @@ void Mode::saveSreenshot()
central->grab().save(filename);
}
Mode *Mode::createNew(AppWindow *window, QString name, Mode::Type t)
{
switch(t) {
case Type::VNA: return new VNA(window, name);
case Type::SG: return new Generator(window, name);
case Type::SA: return new SpectrumAnalyzer(window, name);
default: return nullptr;
}
}
bool Mode::nameAllowed(QString name)
{
for(auto m : modes) {
if(m->getName() == name) {
// name already taken, no duplicates allowed
return false;
}
}
return true;
}
void Mode::finalize(QWidget *centralWidget)
{
central = centralWidget;
@ -176,6 +287,27 @@ void Mode::finalize(QWidget *centralWidget)
}
}
int Mode::findTabIndex()
{
auto it = std::find(modes.begin(), modes.end(), this);
return it - modes.begin();
}
std::vector<Mode *> Mode::getModes()
{
return modes;
}
Mode *Mode::findFirstOfType(Mode::Type t)
{
for(auto m : modes) {
if(m->getType() == t) {
return m;
}
}
return nullptr;
}
void Mode::setStatusbarMessage(QString msg)
{
statusbarMsg = msg;
@ -188,3 +320,14 @@ QString Mode::getName() const
{
return name;
}
void Mode::setName(const QString &value)
{
if(!nameAllowed(value)) {
// unable to use this name
return;
}
name = value;
tabbar->setTabText(findTabIndex(), name);
}

View file

@ -3,30 +3,50 @@
#include "appwindow.h"
#include "savable.h"
#include "scpi.h"
#include <QString>
#include <QWidget>
#include <QButtonGroup>
#include <QToolBar>
#include <QTabBar>
#include <QDockWidget>
#include <set>
class Mode : public QObject, public Savable
class Mode : public QObject, public Savable, public SCPINode
{
Q_OBJECT
public:
Mode(AppWindow *window, QString name);
enum class Type {
VNA,
SG,
SA,
Last,
};
Mode(AppWindow *window, QString name, QString SCPIname);
~Mode();
virtual void activate(); // derived classes must call Mode::activate before doing anything
virtual void deactivate(); // derived classes must call Mode::deactivate before returning
virtual void shutdown(){}; // called when the application is about to exit
QString getName() const;
void setName(const QString &value);
static Mode *getActiveMode();
static QString TypeToName(Type t);
static Type TypeFromName(QString s);
virtual Type getType() = 0;
virtual void initializeDevice() = 0;
virtual void deviceDisconnected(){};
virtual void saveSreenshot();
static Mode *createNew(AppWindow *window, QString name, Type t);
static bool nameAllowed(QString name);
static std::vector<Mode *> getModes();
static Mode* findFirstOfType(Type t);
signals:
void statusbarMessage(QString msg);
protected:
@ -39,10 +59,13 @@ protected:
std::set<QDockWidget*> docks;
private:
int findTabIndex();
static std::vector<Mode*> modes;
static Mode *activeMode;
static QTabBar *tabbar;
static QWidget *cornerWidget;
static QButtonGroup *modeButtonGroup;
const QString name;
// static QButtonGroup *modeButtonGroup;
QString name;
QString statusbarMsg;
QWidget *central;
};

View file

@ -106,6 +106,17 @@ bool SCPINode::add(SCPINode *node)
return true;
}
bool SCPINode::remove(SCPINode *node)
{
auto it = std::find(subnodes.begin(), subnodes.end(), node);
if(it != subnodes.end()) {
subnodes.erase(it);
return true;
} else {
return false;
}
}
bool SCPINode::add(SCPICommand *cmd)
{
if(nameCollision(cmd->name())) {

View file

@ -31,6 +31,7 @@ public:
name(name){}
bool add(SCPINode *node);
bool remove(SCPINode *node);
bool add(SCPICommand *cmd);
private: