diff --git a/Documentation/UserManual/ProgrammingGuide.pdf b/Documentation/UserManual/ProgrammingGuide.pdf index 7ccece1..46b2e6d 100644 Binary files a/Documentation/UserManual/ProgrammingGuide.pdf and b/Documentation/UserManual/ProgrammingGuide.pdf differ diff --git a/Documentation/UserManual/ProgrammingGuide.tex b/Documentation/UserManual/ProgrammingGuide.tex index 79b7599..1654b32 100644 --- a/Documentation/UserManual/ProgrammingGuide.tex +++ b/Documentation/UserManual/ProgrammingGuide.tex @@ -501,22 +501,44 @@ $$ S_{11}...S_{1n},S_{21}...S_{2n},...,S_{n1}...S_{nn} $$ \event{Sets the storage type of a trace}{VNA:TRACe:TYPE}{, either by name or by index\\, options are OVERWRITE, MAXHOLD or MINHOLD} \query{Queries the storage type of a trace}{VNA:TRACe:TYPE?}{, either by name or by index}{OVERWRITE, MAXHOLD or MINHOLD} -\subsubsection{VNA:CALibration:TYPE} -\event{Sets the calibration type. This command fails if the required measurements have not been taken yet}{VNA:CALibration:TYPE}{, options are NONE, PORT\_1, PORT\_2, SOLT, NORMALIZE or TRL} -\query{Queries the currently active calibration type}{VNA:CALibration:TYPE?}{None}{NONE, PORT\_1, PORT\_2, SOLT, NORMALIZE or TRL} +\subsubsection{VNA:CALibration:ACTivate} +\event{Activates a specific calibration. This command fails if the required measurements have not been taken yet}{VNA:CALibration:ACTivate}{} +\query{Queries the currently available calibration types}{VNA:CALibration:ACTivate?}{None}{comma-separated list of available calibration types} -\subsubsection{VNA:CALibration:MEASure} -\event{Starts a calibration measurement. This command fails if no device is connected, the VNA mode is not active or a calibration measurement is already in progress.}{VNA:CALibration:MEASure}{, options are:\\ -\hspace{1cm}PORT\_1\_OPEN\\ -\hspace{1cm}PORT\_1\_SHORT\\ -\hspace{1cm}PORT\_1\_LOAD\\ -\hspace{1cm}PORT\_2\_OPEN\\ -\hspace{1cm}PORT\_2\_SHORT\\ -\hspace{1cm}PORT\_2\_LOAD\\ +\subsubsection{VNA:CALibration:NUMber} +\query{Queries the number of available calibration measurements}{VNA:CALibration:NUMber?}{None}{} + +\subsubsection{VNA:CALibration:RESET} +\event{Resets the calibration. Deactivates the calibration and deletes all measurements.}{VNA:CALibration:RESET}{None} + +\subsubsection{VNA:CALibration:ADD} +\event{Adds a new empty calibration measurement.}{VNA:CALibration:ADD}{ Measurement type, one of:\\ +\hspace{1cm}OPEN\\ +\hspace{1cm}SHORT\\ +\hspace{1cm}LOAD\\ \hspace{1cm}THROUGH\\ \hspace{1cm}ISOLATION\\ -\hspace{1cm}LINE\\ -} +{[]}, calibration kit standard name, optional\\} + +\subsubsection{VNA:CALibration:TYPE} +\query{Returns the type of the specified measurement}{VNA:CALibration:TYPE?}{}{Measurement type, one of:\\ +\hspace{1cm}OPEN\\ +\hspace{1cm}SHORT\\ +\hspace{1cm}LOAD\\ +\hspace{1cm}THROUGH\\ +\hspace{1cm}ISOLATION\\} + +\subsubsection{VNA:CALibration:PORT} +\event{Sets the port for the specified measurement}{VNA:CALibration:PORT}{ } +\query{Returns the port for the specified measurement}{VNA:CALibration:PORT?}{}{} + +\subsubsection{VNA:CALibration:STANDARD} +\event{Sets the calibration standard which will be used for the specified measurement}{VNA:CALibration:STANDARD}{ } +\query{Returns the standard name for the specified measurement}{VNA:CALibration:STANDARD?}{}{Name of used calibration standard (from calibration kit)} + +\subsubsection{VNA:CALibration:MEASure} +\event{Starts a calibration measurement. This command fails if no device is connected, the VNA mode is not active or a calibration measurement is already in progress.}{VNA:CALibration:MEASure}{,,...} +Any number of measurements can be specified (by their number). These measurements will be taken simultaneously. This only works if they are measuring different ports (e.g. measure SHORT on port 1 and OPEN on port 2). If colliding measurements are specified (e.g. SHORT on port 1 and LOAD on port 1), an error is returned and no measurements are started. \subsubsection{VNA:CALibration:BUSY} \query{Queries whether a calibration measurement is ongoing}{VNA:CALibration:BUSY?}{None}{TRUE or FALSE} diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index b7bf04d..0bca1b2 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -36,8 +36,258 @@ bool operator==(const Calibration::CalType &lhs, const Calibration::CalType &rhs } Calibration::Calibration() + : SCPINode("CALibration") { caltype.type = Type::None; + unsavedChanges = false; + + // create SCPI commands + add(new SCPICommand("ACTivate", [=](QStringList params) -> QString { + if(params.size() != 1) { + return SCPI::getResultName(SCPI::Result::Error); + } else { + auto availableCals = getAvailableCalibrations(); + for(auto caltype : availableCals) { + if(caltype.getShortString().compare(params[0], Qt::CaseInsensitive) == 0) { + // found a match + // check if calibration can be activated + if(canCompute(caltype)) { + compute(caltype); + } else { + return SCPI::getResultName(SCPI::Result::Error); + } + } + } + // if we get here, the supplied parameter did not match any of the available calibrations + return SCPI::getResultName(SCPI::Result::Error); + } + return SCPI::getResultName(SCPI::Result::Empty); + }, [=](QStringList) -> QString { + auto availableCals = getAvailableCalibrations(); + if(availableCals.size() == 0) { + return SCPI::getResultName(SCPI::Result::Empty); + } + auto ret = availableCals[0].getShortString(); + for(unsigned int i=1;i QString { + return QString::number(measurements.size()); + })); + add(new SCPICommand("RESET", [=](QStringList) -> QString { + reset(); + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); + add(new SCPICommand("ADD", [=](QStringList params) -> QString { + if(params.size() < 1) { + // no measurement type specified + return SCPI::getResultName(SCPI::Result::Error); + } else { + // parse measurement type + auto type = CalibrationMeasurement::Base::TypeFromString(params[0]); + auto newMeas = newMeasurement(type); + if(!newMeas) { + // failed to create this type of measurement + return SCPI::getResultName(SCPI::Result::Error); + } + if(params.size() == 2) { + // standard name given + CalStandard::Virtual *standard = nullptr; + for(auto s : newMeas->supportedStandards()) { + if(s->getName().compare(params[1], Qt::CaseInsensitive) == 0) { + // use this standard + standard = s; + } + } + if(!standard) { + // specified standard not available + return SCPI::getResultName(SCPI::Result::Error); + } + newMeas->setStandard(standard); + } + measurements.push_back(newMeas); + return SCPI::getResultName(SCPI::Result::Empty); + } + }, nullptr)); + add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString { + if(params.size() < 1) { + // no measurement number specified + return SCPI::getResultName(SCPI::Result::Error); + } else { + bool okay; + unsigned int number = params[0].toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } + return CalibrationMeasurement::Base::TypeToString(measurements[number]->getType()); + } + })); + add(new SCPICommand("PORT", [=](QStringList params) -> QString { + if(params.size() < 1) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } else { + bool okay; + unsigned int number = params[0].toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } + auto meas = measurements[number]; + auto onePort = dynamic_cast(meas); + if(onePort) { + if(params.size() != 2) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } + bool okay; + int number = params[1].toInt(&okay); + if(!okay || number < 1 || number > VirtualDevice::getInfo(VirtualDevice::getConnected()).ports) { + // invalid port specified + return SCPI::getResultName(SCPI::Result::Error); + } + onePort->setPort(number); + return SCPI::getResultName(SCPI::Result::Empty); + } + auto twoPort = dynamic_cast(meas); + if(twoPort) { + if(params.size() != 3) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } + bool okay1; + int port1 = params[1].toInt(&okay1); + bool okay2; + int port2 = params[2].toInt(&okay2); + if(!okay1 || !okay2 || port1 < 1 || port2 > VirtualDevice::getInfo(VirtualDevice::getConnected()).ports + || port2 < 1 || port2 > VirtualDevice::getInfo(VirtualDevice::getConnected()).ports) { + // invalid port specified + return SCPI::getResultName(SCPI::Result::Error); + } + twoPort->setPort1(port1); + twoPort->setPort2(port2); + return SCPI::getResultName(SCPI::Result::Empty); + } + return SCPI::getResultName(SCPI::Result::Error); + } + }, [=](QStringList params) -> QString { + if(params.size() != 1) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } else { + bool okay; + unsigned int number = params[0].toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } + auto meas = measurements[number]; + auto onePort = dynamic_cast(meas); + if(onePort) { + return QString::number(onePort->getPort()); + } + auto twoPort = dynamic_cast(meas); + if(twoPort) { + return QString::number(twoPort->getPort1()) + " " + QString::number(twoPort->getPort2()); + } + return SCPI::getResultName(SCPI::Result::Error); + } + })); + add(new SCPICommand("STANDARD", [=](QStringList params) -> QString { + if(params.size() != 2) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } else { + bool okay; + unsigned int number = params[0].toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } + auto meas = measurements[number]; + for(auto s : meas->supportedStandards()) { + if(s->getName().compare(params[1], Qt::CaseInsensitive) == 0) { + // use this standard + meas->setStandard(s); + return SCPI::getResultName(SCPI::Result::Empty); + } + } + // specified standard not available + return SCPI::getResultName(SCPI::Result::Error); + } + }, [=](QStringList params) -> QString { + if(params.size() != 1) { + // invalid number of parameters + return SCPI::getResultName(SCPI::Result::Error); + } else { + bool okay; + unsigned int number = params[0].toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } + auto s = measurements[number]->getStandard(); + if(s) { + s->getName(); + } else { + // no standard set + return "None"; + } + } + })); + add(new SCPICommand("MEASure", [=](QStringList params) -> QString { + if(params.size() < 1 ) { + // no measurement specified + // TODO check if measurement is already running + return SCPI::getResultName(SCPI::Result::Error); + } else { + // assemble list of measurements to take + std::set m; + for(QString p : params) { + bool okay = false; + unsigned int number = p.toUInt(&okay); + if(!okay || number >= measurements.size()) { + // invalid measurement specified + return SCPI::getResultName(SCPI::Result::Error); + } else { + m.insert(measurements[number]); + } + } + if(CalibrationMeasurement::Base::canMeasureSimultaneously(m)) { + emit startMeasurements(m); + return SCPI::getResultName(SCPI::Result::Empty); + } else { + // can't start these measurements simultaneously + return SCPI::getResultName(SCPI::Result::Error); + } + } + }, nullptr)); + add(new SCPICommand("SAVE", [=](QStringList params) -> QString { + if(params.size() != 1 || getCaltype().type == Calibration::Type::None) { + // no filename given or no calibration active + return SCPI::getResultName(SCPI::Result::Error); + } + if(!toFile(params[0])) { + // some error when writing the calibration file + return SCPI::getResultName(SCPI::Result::Error); + } + return SCPI::getResultName(SCPI::Result::Empty); + }, nullptr)); + add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString { + if(params.size() != 1) { + // no filename given or no calibration active + return SCPI::getResultName(SCPI::Result::False); + } + if(!fromFile(params[0])) { + // some error when loading the calibration file + return SCPI::getResultName(SCPI::Result::False); + } + return SCPI::getResultName(SCPI::Result::True); + })); } QString Calibration::TypeToString(Calibration::Type type) @@ -284,7 +534,7 @@ void Calibration::edit() ui->createDefault->blockSignals(false); return; } - measurements.clear(); + deleteMeasurements(); } createDefaultMeasurements((DefaultMeasurements) (ui->createDefault->currentIndex() - 1)); updateMeasurementTable(); @@ -845,6 +1095,7 @@ bool Calibration::toFile(QString filename) kit.toFile(calkit_file); this->currentCalFile = calibration_file; // if all ok, remember this + unsavedChanges = false; return true; } @@ -888,6 +1139,7 @@ bool Calibration::fromFile(QString filename) return false; } + unsavedChanges = false; return true; } @@ -1012,10 +1264,7 @@ bool Calibration::compute(Calibration::CalType type) void Calibration::reset() { - for(auto m : measurements) { - delete m; - } - measurements.clear(); + deleteMeasurements(); deactivate(); } @@ -1033,6 +1282,7 @@ void Calibration::addMeasurements(std::set m, co for(auto meas : m) { meas->addPoint(data); } + unsavedChanges = true; } void Calibration::clearMeasurements(std::set m) @@ -1116,6 +1366,14 @@ void Calibration::createDefaultMeasurements(Calibration::DefaultMeasurements dm) } } +void Calibration::deleteMeasurements() +{ + for(auto m : measurements) { + delete m; + } + measurements.clear(); +} + bool Calibration::hasFrequencyOverlap(std::vector m, double *startFreq, double *stopFreq, int *points) { double minResolution = std::numeric_limits::max(); diff --git a/Software/PC_Application/Calibration/calibration.h b/Software/PC_Application/Calibration/calibration.h index 6bdb940..f6d8c25 100644 --- a/Software/PC_Application/Calibration/calibration.h +++ b/Software/PC_Application/Calibration/calibration.h @@ -5,8 +5,9 @@ #include "calibrationmeasurement.h" #include "calkit.h" #include "Traces/trace.h" +#include "scpi.h" -class Calibration : public QObject, public Savable +class Calibration : public QObject, public Savable, public SCPINode { Q_OBJECT @@ -115,6 +116,7 @@ private: }; static QString DefaultMeasurementsToString(DefaultMeasurements dm); void createDefaultMeasurements(DefaultMeasurements dm); + void deleteMeasurements(); bool hasFrequencyOverlap(std::vector m, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); CalibrationMeasurement::Base* findMeasurement(CalibrationMeasurement::Base::Type type, int port1 = 0, int port2 = 0); @@ -145,6 +147,8 @@ private: QString descriptiveCalName(); QString currentCalFile; + + bool unsavedChanges; }; #endif // CALIBRATION2_H diff --git a/Software/PC_Application/Calibration/calibrationmeasurement.cpp b/Software/PC_Application/Calibration/calibrationmeasurement.cpp index 2e10ce3..35d616c 100644 --- a/Software/PC_Application/Calibration/calibrationmeasurement.cpp +++ b/Software/PC_Application/Calibration/calibrationmeasurement.cpp @@ -95,7 +95,7 @@ QString CalibrationMeasurement::Base::TypeToString(CalibrationMeasurement::Base: CalibrationMeasurement::Base::Type CalibrationMeasurement::Base::TypeFromString(QString s) { for(int i=0;i<(int) Type::Last;i++) { - if(TypeToString((Type) i) == s) { + if(TypeToString((Type) i).compare(s, Qt::CaseInsensitive) == 0) { return (Type) i; } } @@ -188,6 +188,11 @@ QDateTime CalibrationMeasurement::Base::getTimestamp() const return timestamp; } +CalStandard::Virtual* CalibrationMeasurement::Base::getStandard() const +{ + return standard; +} + double CalibrationMeasurement::OnePort::minFreq() { if(points.size() > 0) { diff --git a/Software/PC_Application/Calibration/calibrationmeasurement.h b/Software/PC_Application/Calibration/calibrationmeasurement.h index d407cc8..985417c 100644 --- a/Software/PC_Application/Calibration/calibrationmeasurement.h +++ b/Software/PC_Application/Calibration/calibrationmeasurement.h @@ -54,6 +54,8 @@ public: static bool canMeasureSimultaneously(std::set measurements); QDateTime getTimestamp() const; + CalStandard::Virtual *getStandard() const; + protected: signals: void standardChanged(CalStandard::Virtual* newStandard); @@ -101,7 +103,7 @@ protected: signals: void portChanged(int p); protected: - int port; + int port; // starts at 1 std::vector points; }; @@ -182,7 +184,7 @@ signals: void port2Changed(int p); void reverseStandardChanged(bool r); protected: - int port1, port2; + int port1, port2; // starts at 1 bool reverseStandard; // Set to true if standard is defined with ports swapped std::vector points; }; diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 189eb7d..6836e8d 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -1403,77 +1403,10 @@ void VNA::SetupSCPI() return QString::number(settings.Power.frequency, 'f', 0); })); SCPINode::add(traceWidget); - auto scpi_cal = new SCPINode("CALibration"); - SCPINode::add(scpi_cal); - scpi_cal->add(new SCPICommand("TYPE", [=](QStringList params) -> QString { - if(params.size() != 1) { - return SCPI::getResultName(SCPI::Result::Error); - } else { - auto availableCals = cal.getAvailableCalibrations(); - for(auto caltype : availableCals) { - if(caltype.getShortString().compare(params[0], Qt::CaseInsensitive) == 0) { - // found a match - // check if calibration can be activated - if(cal.canCompute(caltype)) { - ApplyCalibration(caltype); - } else { - return SCPI::getResultName(SCPI::Result::Error); - } - } - } - // if we get here, the supplied parameter did not match any of the available calibrations - return SCPI::getResultName(SCPI::Result::Error); - } - return SCPI::getResultName(SCPI::Result::Empty); - }, [=](QStringList) -> QString { - auto ret = cal.getCaltype().getShortString(); - return ret; - })); - scpi_cal->add(new SCPICommand("MEASure", [=](QStringList params) -> QString { - if(params.size() != 1 || CalibrationMeasurementActive() || !window->getDevice() || isActive != true) { - // no measurement specified, still busy or invalid mode - return SCPI::getResultName(SCPI::Result::Error); - } else { - // TODO -// auto meas = Calibration::MeasurementFromString(params[0].replace('_', ' ')); -// if(meas == Calibration::Measurement::Last) { -// // failed to parse string -// return SCPI::getResultName(SCPI::Result::Error); -// } else { -// std::set m; -// m.insert(meas); -//// StartCalibrationMeasurements(m); -// } - } - return SCPI::getResultName(SCPI::Result::Empty); - }, nullptr)); - scpi_cal->add(new SCPICommand("BUSy", nullptr, [=](QStringList) -> QString { + SCPINode::add(&cal); + cal.add(new SCPICommand("BUSy", nullptr, [=](QStringList) -> QString { return CalibrationMeasurementActive() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); })); - scpi_cal->add(new SCPICommand("SAVE", [=](QStringList params) -> QString { - if(params.size() != 1 || cal.getCaltype().type == Calibration::Type::None) { - // no filename given or no calibration active - return SCPI::getResultName(SCPI::Result::Error); - } - if(!cal.toFile(params[0])) { - // some error when writing the calibration file - return SCPI::getResultName(SCPI::Result::Error); - } - calEdited = false; - return SCPI::getResultName(SCPI::Result::Empty); - }, nullptr)); - scpi_cal->add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString { - if(params.size() != 1) { - // no filename given or no calibration active - return SCPI::getResultName(SCPI::Result::False); - } - if(!cal.fromFile(params[0])) { - // some error when loading the calibration file - return SCPI::getResultName(SCPI::Result::False); - } - calEdited = false; - return SCPI::getResultName(SCPI::Result::True); - })); } void VNA::ConstrainAndUpdateFrequencies()