This commit is contained in:
Jan Käberich 2021-12-13 16:06:03 +01:00
commit c5cbfddbfe
79 changed files with 3275 additions and 1308 deletions

View File

@ -1,5 +1,31 @@
# Changelog
## v1.2.1
Mostly bugfixes along with the occasional new feature.
- Calibration:
- File format changed to json
- Multiple measurements can be taken/deleted at the same time
- Calibration kit allows separate male/female standards
- configurable Z0 for short/open
- SCPI commands:
- load/save calibration files
- export to touchstone file format directly
- additional command for reading trace data
- fix typo in documentation
- UI improvements:
- Additional Y-axis options: Reactance/Real/Imaginary
- Graphs look a bit nicer
- Configurable line width for graphs
- finally added an application logo
- General bugfixes, among others:
- PLL divider calculation fixed for certain frequencies
- Improved USB buffer handling
- Better error handling when opening invalid files
- Various bugs when adding/deleting markers
- graph autoscaling with invisible traces
## v1.2.0
- Additional SCPI commands

View File

@ -241,11 +241,11 @@ This section contains general device commands, available regardless of the curre
\subsubsection{DEVice:MODE}
\event{Switches the device to the specified mode}{DEVice:MODE <mode>}{<mode>:\\ \hspace{1cm} VNA: set to vector analyzer\\ \hspace{1cm} GEN: set to signal generator\\ \hspace{1cm} SA: set to spectrum analyzer}
\begin{example}
:MODE VNA
:DEV:MODE VNA
\end{example}
\query{Queries the currently active mode}{DEVice:MODE?}{None}{<mode>:\\ \hspace{1cm} VNA: set to vector analyzer\\ \hspace{1cm} GEN: set to signal generator\\ \hspace{1cm} SA: set to spectrum analyzer}
\begin{example}
:MODE?
:DEV:MODE?
VNA
\end{example}

View File

@ -311,7 +311,7 @@ architecture Behavioral of top is
);
END COMPONENT;
signal clk160 : std_logic;
signal clk_pll : std_logic;
signal clk_locked : std_logic;
signal inv_clk_locked : std_logic;
signal int_reset : std_logic;
@ -453,7 +453,7 @@ begin
-- Clock in ports
CLK_IN1 => CLK,
-- Clock out ports
CLK_OUT1 => clk160,
CLK_OUT1 => clk_pll,
-- Status and control signals
RESET => RESET,
LOCKED => clk_locked
@ -464,7 +464,7 @@ begin
Inst_ResetDelay: ResetDelay
GENERIC MAP(CLK_DELAY => 100)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
IN_RESET => inv_clk_locked,
OUT_RESET => int_reset
);
@ -472,42 +472,42 @@ begin
Sync_AUX1 : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => MCU_AUX1,
SYNC_OUT => aux1_sync
);
Sync_AUX2 : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => MCU_AUX2,
SYNC_OUT => aux2_sync
);
Sync_AUX3 : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => MCU_AUX3,
SYNC_OUT => aux3_sync
);
Sync_LO_LD : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => LO1_LD,
SYNC_OUT => lo_ld_sync
);
Sync_SOURCE_LD : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => SOURCE_LD,
SYNC_OUT => source_ld_sync
);
Sync_NSS : Synchronizer
GENERIC MAP(stages => 2)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
SYNC_IN => MCU_NSS,
SYNC_OUT => nss_sync
);
@ -516,7 +516,7 @@ begin
Source: MAX2871
GENERIC MAP(CLK_DIV => 10)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
REG4 => source_reg_4,
REG3 => source_reg_3,
@ -531,7 +531,7 @@ begin
LO1: MAX2871
GENERIC MAP(CLK_DIV => 10)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
REG4 => lo_reg_4,
REG3 => lo_reg_3,
@ -550,7 +550,7 @@ begin
GENERIC MAP(CLK_DIV => 2,
CONVCYCLES => 77)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
START => adc_trigger_sample,
READY => adc_port1_ready,
@ -566,7 +566,7 @@ begin
GENERIC MAP(CLK_DIV => 2,
CONVCYCLES => 77)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
START => adc_trigger_sample,
READY => open, -- synchronous ADCs, ready indicated by port 1 ADC
@ -582,7 +582,7 @@ begin
GENERIC MAP(CLK_DIV => 2,
CONVCYCLES => 77)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
START => adc_trigger_sample,
READY => open, -- synchronous ADCs, ready indicated by port 1 ADC
@ -597,7 +597,7 @@ begin
Windower: Windowing PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => sampling_start,
WINDOW_TYPE => sampling_window,
PORT1_RAW => adc_port1_data,
@ -614,7 +614,7 @@ begin
Sampler: Sampling
GENERIC MAP(CLK_CYCLES_PRE_DONE => 0)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => sweep_reset,
ADC_PRESCALER => sampling_prescaler,
PHASEINC => sampling_phaseinc,
@ -639,7 +639,7 @@ begin
sweep_reset <= not aux3_sync;
SweepModule: Sweep PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => sweep_reset,
NPOINTS => sweep_points,
CONFIG_ADDRESS => sweep_config_address,
@ -703,7 +703,7 @@ begin
source_unlocked <= not source_ld_sync;
SPI: SPICommands PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => int_reset,
SCLK => MCU_SCK,
MOSI => MCU_MOSI,
@ -755,7 +755,7 @@ begin
SA_DFT: DFT GENERIC MAP(BINS => 96)
PORT MAP(
CLK => clk160,
CLK => clk_pll,
RESET => dft_reset,
PORT1 => port1_windowed,
PORT2 => port2_windowed,
@ -770,12 +770,12 @@ begin
ConfigMem : SweepConfigMem
PORT MAP (
clka => clk160,
clka => clk_pll,
ena => '1',
wea => sweep_config_write,
addra => sweep_config_write_address,
dina => sweep_config_write_data,
clkb => clk160,
clkb => clk_pll,
addrb => sweep_config_address,
doutb => sweep_config_data
);

View File

@ -1,4 +1,5 @@
# LibreVNA
![LibreVNA](Software/PC_Application/resources/banner.png)
**100kHz to 6GHz VNA**
This is the improved version of my [first attempt](https://www.github.com/jankae/VNA) at a VNA.

View File

@ -289,6 +289,9 @@ void AmplitudeCalDialog::AddPointDialog()
auto d = new QDialog();
auto ui = new Ui::AddAmplitudePointsDialog();
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->frequency->setUnit("Hz");
ui->frequency->setPrefixes(" kMG");
ui->startFreq->setUnit("Hz");
@ -356,6 +359,9 @@ void AmplitudeCalDialog::AutomaticMeasurementDialog()
automatic.dialog = new QDialog(this);
auto ui = new Ui::AutomaticAmplitudeDialog();
ui->setupUi(automatic.dialog);
connect(automatic.dialog, &QDialog::finished, [=](){
delete ui;
});
automatic.progress = ui->progress;
ui->explanation->setText(info);
ui->status->setText("Gathering information about "+otherCal+" Calibration...");

View File

@ -26,6 +26,8 @@ Calibration::Calibration()
measurements[Measurement::Line].datapoints = vector<Protocol::Datapoint>();
type = Type::None;
port1Standard = port2Standard = PortStandard::Male;
throughZeroLength = false;
}
Calibration::Standard Calibration::getPort1Standard(Calibration::Measurement m)
@ -122,18 +124,28 @@ bool Calibration::constructErrorTerms(Calibration::Type type)
}
qDebug() << "Constructing error terms for" << TypeToString(type) << "calibration";
bool isTRL = type == Type::TRL;
double kit_minFreq = kit.minFreq(isTRL);
double kit_maxFreq = kit.maxFreq(isTRL);
if(minFreq < kit_minFreq || maxFreq > kit_maxFreq) {
bool uses_male = true;
bool uses_female = true;
if(!kit.checkIfValid(minFreq, maxFreq, isTRL, uses_male, uses_female)) {
// TODO adjust for male/female standards
// Calkit does not support complete calibration range
QString msg = QString("The calibration kit does not support the complete span.\n\n")
+ "The measured calibration data covers " + Unit::ToString(minFreq, "Hz", " kMG", 4) + " to " + Unit::ToString(maxFreq, "Hz", " kMG", 4)
+ ", however the calibration kit is only valid from " + Unit::ToString(kit_minFreq, "Hz", " kMG", 4) + " to " + Unit::ToString(kit_maxFreq, "Hz", " kMG", 4) + ".\n\n"
+ ", however the calibration kit does not support the whole frequency range.\n\n"
+ "Please adjust the calibration kit or the span and take the calibration measurements again.";
InformationBox::ShowError("Unable to perform calibration", msg);
qWarning() << msg;
return false;
}
// check calkit standards and adjust if necessary
if(!kit.hasSeparateMaleFemaleStandards()) {
port1Standard = PortStandard::Male;
port2Standard = PortStandard::Male;
}
if(port1Standard == port2Standard) {
// unable to use zero-length through
throughZeroLength = false;
}
switch(type) {
case Type::Port1SOL: constructPort1SOL(); break;
case Type::Port2SOL: constructPort2SOL(); break;
@ -181,22 +193,34 @@ void Calibration::construct12TermPoints()
auto S22_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S22, measurements[Measurement::Through].datapoints[i].imag_S22);
auto S12_through = complex<double>(measurements[Measurement::Through].datapoints[i].real_S12, measurements[Measurement::Through].datapoints[i].imag_S12);
auto actual = kit.toSOLT(p.frequency);
auto actual = kit.toSOLT(p.frequency, port1Standard == PortStandard::Male);
// Forward calibration
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
p.fe30 = S21_isolation;
// See page 18 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
// Formulas for S11M and S21M solved for e22 and e10e32
if (throughZeroLength) {
// use ideal through
actual.ThroughS11 = 0.0;
actual.ThroughS12 = 1.0;
actual.ThroughS21 = 1.0;
actual.ThroughS22 = 0.0;
}
auto deltaS = actual.ThroughS11*actual.ThroughS22 - actual.ThroughS21 * actual.ThroughS12;
p.fe22 = ((S11_through - p.fe00)*(1.0 - p.fe11 * actual.ThroughS11)-actual.ThroughS11*p.fe10e01)
/ ((S11_through - p.fe00)*(actual.ThroughS22-p.fe11*deltaS)-deltaS*p.fe10e01);
p.fe10e32 = (S21_through - p.fe30)*(1.0 - p.fe11*actual.ThroughS11 - p.fe22*actual.ThroughS22 + p.fe11*p.fe22*deltaS) / actual.ThroughS21;
// Reverse calibration
actual = kit.toSOLT(p.frequency, port2Standard == PortStandard::Male);
computeSOL(S22_short, S22_open, S22_load, p.re33, p.re22, p.re23e32, actual.Open, actual.Short, actual.Load);
p.re03 = S12_isolation;
p.re11 = ((S22_through - p.re33)*(1.0 - p.re22 * actual.ThroughS22)-actual.ThroughS22*p.re23e32)
/ ((S22_through - p.re33)*(actual.ThroughS11-p.re22*deltaS)-deltaS*p.re23e32);
p.re23e01 = (S12_through - p.re03)*(1.0 - p.re11*actual.ThroughS11 - p.re22*actual.ThroughS22 + p.re11*p.re22*deltaS) / actual.ThroughS12;
points.push_back(p);
}
}
@ -212,10 +236,11 @@ void Calibration::constructPort1SOL()
auto S11_short = complex<double>(measurements[Measurement::Port1Short].datapoints[i].real_S11, measurements[Measurement::Port1Short].datapoints[i].imag_S11);
auto S11_load = complex<double>(measurements[Measurement::Port1Load].datapoints[i].real_S11, measurements[Measurement::Port1Load].datapoints[i].imag_S11);
// OSL port1
auto actual = kit.toSOLT(p.frequency);
auto actual = kit.toSOLT(p.frequency, port1Standard == PortStandard::Male);
// See page 13 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
// All other calibration coefficients to ideal values
p.fex = 0.0;
p.fe30 = 0.0;
p.fe22 = 0.0;
p.fe10e32 = 1.0;
@ -223,6 +248,7 @@ void Calibration::constructPort1SOL()
p.re22 = 0.0;
p.re23e32 = 1.0;
p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0;
p.re23e01 = 1.0;
points.push_back(p);
@ -240,10 +266,11 @@ void Calibration::constructPort2SOL()
auto S22_short = complex<double>(measurements[Measurement::Port2Short].datapoints[i].real_S22, measurements[Measurement::Port2Short].datapoints[i].imag_S22);
auto S22_load = complex<double>(measurements[Measurement::Port2Load].datapoints[i].real_S22, measurements[Measurement::Port2Load].datapoints[i].imag_S22);
// OSL port2
auto actual = kit.toSOLT(p.frequency);
auto actual = kit.toSOLT(p.frequency, port1Standard == PortStandard::Male);
// See page 19 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf
computeSOL(S22_short, S22_open, S22_load, p.re33, p.re22, p.re23e32, actual.Open, actual.Short, actual.Load);
// All other calibration coefficients to ideal values
p.fex = 0.0;
p.fe30 = 0.0;
p.fe22 = 0.0;
p.fe10e32 = 1.0;
@ -251,6 +278,7 @@ void Calibration::constructPort2SOL()
p.fe11 = 0.0;
p.fe10e01 = 1.0;
p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0;
p.re23e01 = 1.0;
points.push_back(p);
@ -271,11 +299,13 @@ void Calibration::constructTransmissionNormalization()
p.re23e01 = S12_through / actual.ThroughS12;
// All other calibration coefficients to ideal values
p.fe30 = 0.0;
p.fex = 0.0;
p.fe22 = 0.0;
p.fe00 = 0.0;
p.fe11 = 0.0;
p.fe10e01 = 1.0;
p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0;
p.re33 = 0.0;
p.re22 = 0.0;
@ -375,6 +405,7 @@ void Calibration::constructTRL()
p.fe10e32 = S_B.m21;
// no isolation measurement available
p.fe30 = 0.0;
p.fex = 0.0;
// Reverse coefficients, normalize for S12 = 1.0
// => det(T)/T22 = 1.0
@ -395,6 +426,7 @@ void Calibration::constructTRL()
p.re33 = S_B.m22;
// no isolation measurement available
p.re03 = 0.0;
p.rex = 0.0;
points.push_back(p);
}
@ -774,12 +806,24 @@ bool Calibration::openFromFile(QString filename)
}
try {
nlohmann::json j;
file >> j;
fromJSON(j);
} catch(exception e) {
// json parsing failed, probably using a legacy file format
try {
file.clear();
file.seekg(0);
file >> *this;
InformationBox::ShowMessage("Loading calibration file", "The file \"" + filename + "\" is stored in a deprecated"
" calibration format. Future versions of this application might not support"
" it anymore. Please save the calibration to update to the new format");
} catch(exception e) {
InformationBox::ShowError("File parsing error", e.what());
qWarning() << "Calibration file parsing failed: " << e.what();
return false;
}
}
this->currentCalFile = filename; // if all ok, remember this
return true;
@ -802,7 +846,7 @@ bool Calibration::saveToFile(QString filename)
auto calibration_file = filename + ".cal";
ofstream file;
file.open(calibration_file.toStdString());
file << *this;
file << setw(1) << toJSON();
auto calkit_file = filename + ".calkit";
qDebug() << "Saving associated calibration kit to file" << calkit_file;
@ -839,6 +883,16 @@ QString Calibration::descriptiveCalName(){
return tmp;
}
bool Calibration::getThroughZeroLength() const
{
return throughZeroLength;
}
void Calibration::setThroughZeroLength(bool value)
{
throughZeroLength = value;
}
double Calibration::getMinFreq(){
return this->minFreq;
}
@ -848,6 +902,97 @@ double Calibration::getMaxFreq(){
int Calibration::getNumPoints(){
return this->points.size();
}
nlohmann::json Calibration::toJSON()
{
nlohmann::json j;
nlohmann::json j_measurements;
for(auto m : measurements) {
if(m.second.datapoints.size() > 0) {
nlohmann::json j_measurement;
j_measurement["name"] = MeasurementToString(m.first).toStdString();
j_measurement["timestamp"] = m.second.timestamp.toSecsSinceEpoch();
nlohmann::json j_points;
for(auto p : m.second.datapoints) {
nlohmann::json j_point;
j_point["frequency"] = p.frequency;
j_point["S11_real"] = p.real_S11;
j_point["S11_imag"] = p.imag_S11;
j_point["S12_real"] = p.real_S12;
j_point["S12_imag"] = p.imag_S12;
j_point["S21_real"] = p.real_S21;
j_point["S21_imag"] = p.imag_S21;
j_point["S22_real"] = p.real_S22;
j_point["S22_imag"] = p.imag_S22;
j_points.push_back(j_point);
}
j_measurement["points"] = j_points;
j_measurements.push_back(j_measurement);
}
}
j["measurements"] = j_measurements;
j["type"] = TypeToString(getType()).toStdString();
j["port1StandardMale"] = port1Standard == PortStandard::Male;
j["port2StandardMale"] = port2Standard == PortStandard::Male;
j["throughZeroLength"] = throughZeroLength;
return j;
}
void Calibration::fromJSON(nlohmann::json j)
{
clearMeasurements();
resetErrorTerms();
port1Standard = j.value("port1StandardMale", true) ? PortStandard::Male : PortStandard::Female;
port2Standard = j.value("port2StandardMale", true) ? PortStandard::Male : PortStandard::Female;
throughZeroLength = j.value("throughZeroLength", false);
if(j.contains("measurements")) {
// grab measurements
for(auto j_m : j["measurements"]) {
if(!j_m.contains("name")) {
throw runtime_error("Measurement without name given");
}
auto m = MeasurementFromString(QString::fromStdString(j_m["name"]));
if(m == Measurement::Last) {
throw runtime_error("Measurement name unknown: "+std::string(j_m["name"]));
}
// get timestamp
measurements[m].timestamp = QDateTime::fromSecsSinceEpoch(j_m.value("timestamp", 0));
// extract points
if(!j_m.contains("points")) {
throw runtime_error("Measurement "+MeasurementToString(m).toStdString()+" does not contain any points");
}
int pointNum = 0;
for(auto j_p : j_m["points"]) {
Protocol::Datapoint p;
p.pointNum = pointNum++;
p.frequency = j_p.value("frequency", 0.0);
p.real_S11 = j_p.value("S11_real", 0.0);
p.imag_S11 = j_p.value("S11_imag", 0.0);
p.real_S12 = j_p.value("S12_real", 0.0);
p.imag_S12 = j_p.value("S12_imag", 0.0);
p.real_S21 = j_p.value("S21_real", 0.0);
p.imag_S21 = j_p.value("S21_imag", 0.0);
p.real_S22 = j_p.value("S22_real", 0.0);
p.imag_S22 = j_p.value("S22_imag", 0.0);
measurements[m].datapoints.push_back(p);
}
}
}
// got all measurements, construct calibration according to type
if(j.contains("type")) {
auto t = TypeFromString(QString::fromStdString(j["type"]));
if(t == Type::Last) {
throw runtime_error("Calibration type unknown: "+std::string(j["type"]));
}
if(calculationPossible(t)) {
constructErrorTerms(t);
} else {
throw runtime_error("Incomplete calibration data, the requested calibration could not be performed.");
}
}
}
QString Calibration::getCurrentCalibrationFile(){
return this->currentCalFile;
}
@ -872,6 +1017,11 @@ ostream& operator<<(ostream &os, const Calibration &c)
istream& operator >>(istream &in, Calibration &c)
{
// old file format did not contain port standard gender, set default
c.port1Standard = Calibration::PortStandard::Male;
c.port2Standard = Calibration::PortStandard::Male;
c.throughZeroLength = false;
std::string line;
while(getline(in, line)) {
QString qLine = QString::fromStdString(line).simplified();
@ -978,7 +1128,9 @@ Calibration::Point Calibration::getCalibrationPoint(Protocol::Datapoint &d)
ret.fe11 = low->fe11 * (1 - alpha) + high->fe11 * alpha;
ret.fe22 = low->fe22 * (1 - alpha) + high->fe22 * alpha;
ret.fe30 = low->fe30 * (1 - alpha) + high->fe30 * alpha;
ret.fex = low->fex * (1 - alpha) + high->fex * alpha;
ret.re03 = low->re03 * (1 - alpha) + high->re03 * alpha;
ret.rex = low->rex * (1 - alpha) + high->rex * alpha;
ret.re11 = low->re11 * (1 - alpha) + high->re11 * alpha;
ret.re22 = low->re22 * (1 - alpha) + high->re22 * alpha;
ret.re33 = low->re33 * (1 - alpha) + high->re33 * alpha;
@ -1002,6 +1154,12 @@ void Calibration::computeSOL(std::complex<double> s_m, std::complex<double> o_m,
tracking = directivity * match - delta;
}
void Calibration::computeIsolation(std::complex<double> x0_m, std::complex<double> x1_m, std::complex<double> reverse_match, std::complex<double> reverse_tracking, std::complex<double> reverse_directivity, std::complex<double> x0, std::complex<double> x1, std::complex<double> &internal_isolation, std::complex<double> &external_isolation)
{
external_isolation = (x1_m - x0_m)*(1.0 - reverse_match * (x1 - x0) + x1*x0*reverse_match*reverse_match) / (reverse_tracking * (x1 - x0));
internal_isolation = x0_m - external_isolation*(reverse_directivity + reverse_tracking*x0 / (1.0 - x0*reverse_match));
}
std::complex<double> Calibration::correctSOL(std::complex<double> measured, std::complex<double> directivity, std::complex<double> match, std::complex<double> tracking)
{
return (measured - directivity) / (measured * match - directivity * match + tracking);
@ -1017,6 +1175,26 @@ void Calibration::setCalibrationKit(const Calkit &value)
kit = value;
}
void Calibration::setPortStandard(int port, Calibration::PortStandard standard)
{
if(port == 1) {
port1Standard = standard;
} else if(port == 2) {
port2Standard = standard;
}
}
Calibration::PortStandard Calibration::getPortStandard(int port)
{
if(port == 1) {
return port1Standard;
} else if(port == 2) {
return port2Standard;
} else {
return PortStandard::Male;
}
}
Calibration::Type Calibration::getType() const
{
return type;

View File

@ -11,8 +11,9 @@
#include <iostream>
#include <iomanip>
#include <QDateTime>
#include <savable.h>
class Calibration
class Calibration : public Savable
{
public:
Calibration();
@ -108,6 +109,23 @@ public:
Calkit& getCalibrationKit();
void setCalibrationKit(const Calkit &value);
enum class PortStandard {
Male,
Female,
};
void setPortStandard(int port, PortStandard standard);
PortStandard getPortStandard(int port);
bool getThroughZeroLength() const;
void setThroughZeroLength(bool value);
QString getCurrentCalibrationFile();
double getMinFreq();
double getMaxFreq();
int getNumPoints();
nlohmann::json toJSON() override;
void fromJSON(nlohmann::json j) override;
private:
void construct12TermPoints();
void constructPort1SOL();
@ -120,9 +138,9 @@ private:
public:
double frequency;
// Forward error terms
std::complex<double> fe00, fe11, fe10e01, fe10e32, fe22, fe30;
std::complex<double> fe00, fe11, fe10e01, fe10e32, fe22, fe30, fex;
// Reverse error terms
std::complex<double> re33, re11, re23e32, re23e01, re22, re03;
std::complex<double> re33, re11, re23e32, re23e01, re22, re03, rex;
};
Point getCalibrationPoint(Protocol::Datapoint &d);
/*
@ -140,6 +158,15 @@ private:
std::complex<double> o_c = std::complex<double>(1.0, 0),
std::complex<double> s_c = std::complex<double>(-1.0, 0),
std::complex<double> l_c = std::complex<double>(0, 0));
void computeIsolation(std::complex<double> x0_m,
std::complex<double> x1_m,
std::complex<double> reverse_match,
std::complex<double> reverse_tracking,
std::complex<double> reverse_directivity,
std::complex<double> x0,
std::complex<double> x1,
std::complex<double> &internal_isolation,
std::complex<double> &external_isolation);
std::complex<double> correctSOL(std::complex<double> measured,
std::complex<double> directivity,
std::complex<double> match,
@ -157,14 +184,10 @@ private:
Calkit kit;
QString descriptiveCalName();
private:
QString currentCalFile;
public:
QString getCurrentCalibrationFile();
double getMinFreq();
double getMaxFreq();
int getNumPoints();
PortStandard port1Standard, port2Standard;
bool throughZeroLength;
};
#endif // CALIBRATION_H

View File

@ -21,16 +21,91 @@ CalibrationTraceDialog::CalibrationTraceDialog(Calibration *cal, double f_min, d
model = new MeasurementModel(cal, measurements);
ui->tableView->setModel(model);
ui->tableView->setColumnWidth(0, 100);
ui->tableView->setColumnWidth(1, 350);
ui->tableView->setColumnWidth(2, 320);
ui->tableView->setColumnWidth(3, 160);
ui->tableView->setColumnWidth(1, 80);
ui->tableView->setColumnWidth(2, 350);
ui->tableView->setColumnWidth(3, 320);
ui->tableView->setColumnWidth(4, 160);
UpdateCalibrationStatus();
auto updateThroughStandardUI = [=](){
if(cal->getPortStandard(1) == cal->getPortStandard(2)) {
// same gender on both ports, can't use zero length through
ui->throughCalkit->click();
ui->throughZero->setEnabled(false);
ui->throughCalkit->setEnabled(false);
} else {
// user may select option for through
ui->throughZero->setEnabled(true);
ui->throughCalkit->setEnabled(true);
}
model->genderUpdated();
};
connect(ui->port1Group, qOverload<int>(&QButtonGroup::buttonClicked), [=](){
if(ui->port1Male->isChecked()) {
cal->setPortStandard(1, Calibration::PortStandard::Male);
} else {
cal->setPortStandard(1, Calibration::PortStandard::Female);
}
updateThroughStandardUI();
UpdateCalibrationStatus();
});
connect(ui->port2Group, qOverload<int>(&QButtonGroup::buttonClicked), [=](){
if(ui->port2Male->isChecked()) {
cal->setPortStandard(2, Calibration::PortStandard::Male);
} else {
cal->setPortStandard(2, Calibration::PortStandard::Female);
}
updateThroughStandardUI();
UpdateCalibrationStatus();
});
connect(ui->throughGroup, qOverload<int>(&QButtonGroup::buttonClicked), [=](){
if(ui->throughZero->isChecked()) {
cal->setThroughZeroLength(true);
} else {
cal->setThroughZeroLength(false);
}
UpdateCalibrationStatus();
});
// hide selector if calkit does not have separate male/female standards
if(!cal->getCalibrationKit().hasSeparateMaleFemaleStandards()) {
ui->port1Standards->hide();
ui->port2Standards->hide();
ui->throughStandard->hide();
ui->tableView->hideColumn((int) MeasurementModel::ColIndex::Gender);
// default selection is male
ui->port1Male->click();
ui->port2Male->click();
ui->throughCalkit->click();
} else {
// separate standards defined
if(cal->getPortStandard(1) == Calibration::PortStandard::Male) {
ui->port1Male->setChecked(true);
} else {
ui->port1Female->setChecked(true);
}
if(cal->getPortStandard(2) == Calibration::PortStandard::Male) {
ui->port2Male->setChecked(true);
} else {
ui->port2Female->setChecked(true);
}
if(cal->getThroughZeroLength()) {
ui->throughZero->setChecked(true);
} else {
ui->throughCalkit->setChecked(true);
}
updateThroughStandardUI();
}
// Check calibration kit span
if(type != Calibration::Type::None) {
auto kit = cal->getCalibrationKit();
auto isTRL = type == Calibration::Type::TRL;
if(kit.minFreq(isTRL) > f_min || kit.maxFreq(isTRL) < f_max) {
if(isTRL && (kit.minFreqTRL() > f_min || kit.maxFreqTRL() < f_max)) {
// TODO check SOLT frequency range depending on selected male/female kit
InformationBox::ShowMessage("Warning", "The calibration kit does not completely cover the currently selected span. "
"Applying a calibration will not be possible for any measurements taken with these settings.");
}
@ -70,9 +145,11 @@ void CalibrationTraceDialog::UpdateCalibrationStatus()
void CalibrationTraceDialog::on_bDelete_clicked()
{
auto measurement = measurements[ui->tableView->currentIndex().row()];
cal->clearMeasurement(measurement);
model->measurementUpdated(measurement);
auto selected = ui->tableView->selectionModel()->selectedRows();
for(auto s : selected) {
cal->clearMeasurement(measurements[s.row()]);
model->measurementUpdated(measurements[s.row()]);
}
UpdateCalibrationStatus();
}

View File

@ -16,9 +16,111 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QGroupBox" name="port1Standards">
<property name="title">
<string>Port 1 Standards</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="port1Male">
<property name="text">
<string>Male</string>
</property>
<attribute name="buttonGroup">
<string notr="true">port1Group</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="port1Female">
<property name="text">
<string>Female</string>
</property>
<attribute name="buttonGroup">
<string notr="true">port1Group</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="port2Standards">
<property name="title">
<string>Port 2 Standards</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QRadioButton" name="port2Male">
<property name="text">
<string>Male</string>
</property>
<attribute name="buttonGroup">
<string notr="true">port2Group</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="port2Female">
<property name="text">
<string>Female</string>
</property>
<attribute name="buttonGroup">
<string notr="true">port2Group</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="throughStandard">
<property name="title">
<string>Through Standard</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QRadioButton" name="throughCalkit">
<property name="text">
<string>From calibration kit</string>
</property>
<attribute name="buttonGroup">
<string notr="true">throughGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="throughZero">
<property name="text">
<string>Zero-length through</string>
</property>
<attribute name="buttonGroup">
<string notr="true">throughGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="tableView">
<property name="selectionBehavior">
@ -92,11 +194,14 @@
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../icons.qrc"/>
</resources>
<connections/>
<buttongroups>
<buttongroup name="port1Group"/>
<buttongroup name="port2Group"/>
<buttongroup name="throughGroup"/>
</buttongroups>
</ui>

View File

@ -14,9 +14,12 @@ using json = nlohmann::json;
using namespace std;
Calkit::Calkit()
: ts_open(nullptr),
ts_short(nullptr),
ts_load(nullptr),
: ts_open_m(nullptr),
ts_short_m(nullptr),
ts_load_m(nullptr),
ts_open_f(nullptr),
ts_short_f(nullptr),
ts_load_f(nullptr),
ts_through(nullptr),
ts_cached(false)
{
@ -128,39 +131,39 @@ Calkit Calkit::fromFile(QString filename)
// legacy file format, return to beginning of file
file.clear();
file.seekg(0);
c.SOLT.Open.useMeasurements = readLine(file).toInt();
c.SOLT.Short.useMeasurements = readLine(file).toInt();
c.SOLT.Load.useMeasurements = readLine(file).toInt();
c.SOLT.open_m.useMeasurements = readLine(file).toInt();
c.SOLT.short_m.useMeasurements = readLine(file).toInt();
c.SOLT.load_m.useMeasurements = readLine(file).toInt();
c.SOLT.Through.useMeasurements = readLine(file).toInt();
c.SOLT.Open.Z0 = readLine(file).toDouble();
c.SOLT.Open.delay = readLine(file).toDouble();
c.SOLT.Open.loss = readLine(file).toDouble();
c.SOLT.Open.C0 = readLine(file).toDouble();
c.SOLT.Open.C1 = readLine(file).toDouble();
c.SOLT.Open.C2 = readLine(file).toDouble();
c.SOLT.Open.C3 = readLine(file).toDouble();
c.SOLT.Short.Z0 = readLine(file).toDouble();
c.SOLT.Short.delay = readLine(file).toDouble();
c.SOLT.Short.loss = readLine(file).toDouble();
c.SOLT.Short.L0 = readLine(file).toDouble();
c.SOLT.Short.L1 = readLine(file).toDouble();
c.SOLT.Short.L2 = readLine(file).toDouble();
c.SOLT.Short.L3 = readLine(file).toDouble();
c.SOLT.Load.Z0 = readLine(file).toDouble();
c.SOLT.open_m.Z0 = readLine(file).toDouble();
c.SOLT.open_m.delay = readLine(file).toDouble();
c.SOLT.open_m.loss = readLine(file).toDouble();
c.SOLT.open_m.C0 = readLine(file).toDouble();
c.SOLT.open_m.C1 = readLine(file).toDouble();
c.SOLT.open_m.C2 = readLine(file).toDouble();
c.SOLT.open_m.C3 = readLine(file).toDouble();
c.SOLT.short_m.Z0 = readLine(file).toDouble();
c.SOLT.short_m.delay = readLine(file).toDouble();
c.SOLT.short_m.loss = readLine(file).toDouble();
c.SOLT.short_m.L0 = readLine(file).toDouble();
c.SOLT.short_m.L1 = readLine(file).toDouble();
c.SOLT.short_m.L2 = readLine(file).toDouble();
c.SOLT.short_m.L3 = readLine(file).toDouble();
c.SOLT.load_m.Z0 = readLine(file).toDouble();
c.SOLT.Through.Z0 = readLine(file).toDouble();
c.SOLT.Through.delay = readLine(file).toDouble();
c.SOLT.Through.loss = readLine(file).toDouble();
if(c.SOLT.Open.useMeasurements) {
c.SOLT.Open.file = readLine(file);
c.SOLT.Open.Sparam = readLine(file).toInt();
if(c.SOLT.open_m.useMeasurements) {
c.SOLT.open_m.file = readLine(file);
c.SOLT.open_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.Short.useMeasurements) {
c.SOLT.Short.file = readLine(file);
c.SOLT.Short.Sparam = readLine(file).toInt();
if(c.SOLT.short_m.useMeasurements) {
c.SOLT.short_m.file = readLine(file);
c.SOLT.short_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.Load.useMeasurements) {
c.SOLT.Load.file = readLine(file);
c.SOLT.Load.Sparam = readLine(file).toInt();
if(c.SOLT.load_m.useMeasurements) {
c.SOLT.load_m.file = readLine(file);
c.SOLT.load_m.Sparam = readLine(file).toInt();
}
if(c.SOLT.Through.useMeasurements) {
c.SOLT.Through.file = readLine(file);
@ -173,6 +176,8 @@ Calkit Calkit::fromFile(QString filename)
c.TRL.Line.minFreq = readLine(file).toDouble();
c.TRL.Line.maxFreq = readLine(file).toDouble();
c.SOLT.separate_male_female = false;
InformationBox::ShowMessage("Loading calkit file", "The file \"" + filename + "\" is stored in a deprecated"
" calibration kit format. Future versions of this application might not support"
" it anymore. Please save the calibration kit to update to the new format");
@ -199,7 +204,12 @@ void Calkit::edit(std::function<void (void)> done)
dialog->show();
}
class Calkit::SOLT Calkit::toSOLT(double frequency)
bool Calkit::hasSeparateMaleFemaleStandards()
{
return SOLT.separate_male_female;
}
class Calkit::SOLT Calkit::toSOLT(double frequency, bool male_standards)
{
auto addTransmissionLine = [](complex<double> termination_reflection, double offset_impedance, double offset_delay, double offset_loss, double frequency) -> complex<double> {
// nomenclature and formulas from https://loco.lab.asu.edu/loco-memos/edges_reports/report_20130807.pdf
@ -221,29 +231,36 @@ class Calkit::SOLT Calkit::toSOLT(double frequency)
return Gamma_i;
};
auto Load = male_standards ? SOLT.load_m : SOLT.load_f;
auto Short = male_standards ? SOLT.short_m : SOLT.short_f;
auto Open = male_standards ? SOLT.open_m : SOLT.open_f;
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
fillTouchstoneCache();
class SOLT ref;
if(SOLT.Load.useMeasurements) {
if(Load.useMeasurements) {
ref.Load = ts_load->interpolate(frequency).S[0];
} else {
auto imp_load = complex<double>(SOLT.Load.Z0, 0);
auto imp_load = complex<double>(Load.Z0, 0);
// Add parallel capacitor to impedance
if(SOLT.Load.Cparallel > 0) {
auto imp_C = complex<double>(0, -1.0 / (frequency * 2 * M_PI * SOLT.Load.Cparallel));
if(Load.Cparallel > 0) {
auto imp_C = complex<double>(0, -1.0 / (frequency * 2 * M_PI * Load.Cparallel));
imp_load = (imp_load * imp_C) / (imp_load + imp_C);
}
// add series inductor to impedance
auto imp_L = complex<double>(0, frequency * 2 * M_PI * SOLT.Load.Lseries);
auto imp_L = complex<double>(0, frequency * 2 * M_PI * Load.Lseries);
imp_load += imp_L;
ref.Load = (imp_load - complex<double>(50.0)) / (imp_load + complex<double>(50.0));
ref.Load = addTransmissionLine(ref.Load, SOLT.Load.Z0, SOLT.Load.delay*1e-12, 0, frequency);
ref.Load = addTransmissionLine(ref.Load, Load.Z0, Load.delay*1e-12, 0, frequency);
}
if(SOLT.Open.useMeasurements) {
if(Open.useMeasurements) {
ref.Open = ts_open->interpolate(frequency).S[0];
} else {
// calculate fringing capacitance for open
double Cfringing = SOLT.Open.C0 * 1e-15 + SOLT.Open.C1 * 1e-27 * frequency + SOLT.Open.C2 * 1e-36 * pow(frequency, 2) + SOLT.Open.C3 * 1e-45 * pow(frequency, 3);
double Cfringing = Open.C0 * 1e-15 + Open.C1 * 1e-27 * frequency + Open.C2 * 1e-36 * pow(frequency, 2) + Open.C3 * 1e-45 * pow(frequency, 3);
// convert to impedance
if (Cfringing == 0) {
// special case to avoid issues with infinity
@ -252,18 +269,18 @@ class Calkit::SOLT Calkit::toSOLT(double frequency)
auto imp_open = complex<double>(0, -1.0 / (frequency * 2 * M_PI * Cfringing));
ref.Open = (imp_open - complex<double>(50.0)) / (imp_open + complex<double>(50.0));
}
ref.Open = addTransmissionLine(ref.Open, SOLT.Open.Z0, SOLT.Open.delay*1e-12, SOLT.Open.loss*1e9, frequency);
ref.Open = addTransmissionLine(ref.Open, Open.Z0, Open.delay*1e-12, Open.loss*1e9, frequency);
}
if(SOLT.Short.useMeasurements) {
if(Short.useMeasurements) {
ref.Short = ts_short->interpolate(frequency).S[0];
} else {
// calculate inductance for short
double Lseries = SOLT.Short.L0 * 1e-12 + SOLT.Short.L1 * 1e-24 * frequency + SOLT.Short.L2 * 1e-33 * pow(frequency, 2) + SOLT.Short.L3 * 1e-42 * pow(frequency, 3);
double Lseries = Short.L0 * 1e-12 + Short.L1 * 1e-24 * frequency + Short.L2 * 1e-33 * pow(frequency, 2) + Short.L3 * 1e-42 * pow(frequency, 3);
// convert to impedance
auto imp_short = complex<double>(0, frequency * 2 * M_PI * Lseries);
ref.Short = (imp_short - complex<double>(50.0)) / (imp_short + complex<double>(50.0));
ref.Short = addTransmissionLine(ref.Short, SOLT.Short.Z0, SOLT.Short.delay*1e-12, SOLT.Short.loss*1e9, frequency);
ref.Short = addTransmissionLine(ref.Short, Short.Z0, Short.delay*1e-12, Short.loss*1e9, frequency);
}
if(SOLT.Through.useMeasurements) {
@ -300,13 +317,23 @@ class Calkit::TRL Calkit::toTRL(double)
return trl;
}
double Calkit::minFreq(bool trl)
double Calkit::minFreqTRL()
{
if(trl) {
return TRL.Line.minFreq;
} else {
}
double Calkit::maxFreqTRL()
{
return TRL.Line.maxFreq;
}
double Calkit::minFreqSOLT(bool male_standards)
{
fillTouchstoneCache();
double min = 0;
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through};
// find the highest minimum frequency in all measurement files
for(auto ts : ts_list) {
@ -319,16 +346,15 @@ double Calkit::minFreq(bool trl)
}
}
return min;
}
}
double Calkit::maxFreq(bool trl)
double Calkit::maxFreqSOLT(bool male_standards)
{
if(trl) {
return TRL.Line.maxFreq;
} else {
fillTouchstoneCache();
double max = std::numeric_limits<double>::max();
auto ts_load = male_standards ? ts_load_m : ts_load_f;
auto ts_short = male_standards ? ts_short_m : ts_short_f;
auto ts_open = male_standards ? ts_open_m : ts_open_f;
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through};
// find the highest minimum frequency in all measurement files
for(auto ts : ts_list) {
@ -341,6 +367,41 @@ double Calkit::maxFreq(bool trl)
}
}
return max;
}
bool Calkit::checkIfValid(double min_freq, double max_freq, bool isTRL, bool include_male, bool include_female)
{
auto min_supported = std::numeric_limits<double>::min();
auto max_supported = std::numeric_limits<double>::max();
if(isTRL) {
min_supported = TRL.Line.minFreq;
max_supported = TRL.Line.maxFreq;
} else {
if(include_male) {
auto min_male = minFreqSOLT(true);
auto max_male = maxFreqSOLT(true);
if(min_male > min_supported) {
min_supported = min_male;
}
if(max_male > max_supported) {
max_supported = max_male;
}
}
if(include_female) {
auto min_female = minFreqSOLT(false);
auto max_female = maxFreqSOLT(false);
if(min_female > min_supported) {
min_supported = min_female;
}
if(max_female > max_supported) {
max_supported = max_female;
}
}
}
if(min_supported <= min_freq && max_supported >= max_freq) {
return true;
} else {
return false;
}
}
@ -351,7 +412,7 @@ bool Calkit::isTRLReflectionShort() const
void Calkit::TransformPathsToRelative(QFileInfo d)
{
vector<QString*> filenames = {&SOLT.Short.file, &SOLT.Open.file, &SOLT.Load.file, &SOLT.Through.file};
vector<QString*> filenames = {&SOLT.short_m.file, &SOLT.open_m.file, &SOLT.load_m.file, &SOLT.short_f.file, &SOLT.open_f.file, &SOLT.load_f.file, &SOLT.Through.file};
for(auto f : filenames) {
if(f->isEmpty()) {
continue;
@ -366,7 +427,7 @@ void Calkit::TransformPathsToRelative(QFileInfo d)
void Calkit::TransformPathsToAbsolute(QFileInfo d)
{
vector<QString*> filenames = {&SOLT.Short.file, &SOLT.Open.file, &SOLT.Load.file, &SOLT.Through.file};
vector<QString*> filenames = {&SOLT.short_m.file, &SOLT.open_m.file, &SOLT.load_m.file, &SOLT.short_f.file, &SOLT.open_f.file, &SOLT.load_f.file, &SOLT.Through.file};
for(auto f : filenames) {
if(f->isEmpty()) {
continue;
@ -382,12 +443,18 @@ void Calkit::TransformPathsToAbsolute(QFileInfo d)
void Calkit::clearTouchstoneCache()
{
delete ts_open;
ts_open = nullptr;
delete ts_short;
ts_short = nullptr;
delete ts_load;
ts_load = nullptr;
delete ts_open_m;
ts_open_m = nullptr;
delete ts_short_m;
ts_short_m = nullptr;
delete ts_load_m;
ts_load_m = nullptr;
delete ts_open_f;
ts_open_f = nullptr;
delete ts_short_f;
ts_short_f = nullptr;
delete ts_load_f;
ts_load_f = nullptr;
delete ts_through;
ts_through = nullptr;
ts_cached = false;
@ -398,20 +465,35 @@ void Calkit::fillTouchstoneCache()
if(ts_cached) {
return;
}
if(SOLT.Open.useMeasurements) {
ts_open = new Touchstone(1);
*ts_open = Touchstone::fromFile(SOLT.Open.file.toStdString());
ts_open->reduceTo1Port(SOLT.Open.Sparam);
if(SOLT.open_m.useMeasurements) {
ts_open_m = new Touchstone(1);
*ts_open_m = Touchstone::fromFile(SOLT.open_m.file.toStdString());
ts_open_m->reduceTo1Port(SOLT.open_m.Sparam);
}
if(SOLT.Short.useMeasurements) {
ts_short = new Touchstone(1);
*ts_short = Touchstone::fromFile(SOLT.Short.file.toStdString());
ts_short->reduceTo1Port(SOLT.Short.Sparam);
if(SOLT.short_m.useMeasurements) {
ts_short_m = new Touchstone(1);
*ts_short_m = Touchstone::fromFile(SOLT.short_m.file.toStdString());
ts_short_m->reduceTo1Port(SOLT.short_m.Sparam);
}
if(SOLT.Load.useMeasurements) {
ts_load = new Touchstone(1);
*ts_load = Touchstone::fromFile(SOLT.Load.file.toStdString());
ts_load->reduceTo1Port(SOLT.Load.Sparam);
if(SOLT.load_m.useMeasurements) {
ts_load_m = new Touchstone(1);
*ts_load_m = Touchstone::fromFile(SOLT.load_m.file.toStdString());
ts_load_m->reduceTo1Port(SOLT.load_m.Sparam);
}
if(SOLT.open_f.useMeasurements) {
ts_open_f = new Touchstone(1);
*ts_open_f = Touchstone::fromFile(SOLT.open_f.file.toStdString());
ts_open_f->reduceTo1Port(SOLT.open_f.Sparam);
}
if(SOLT.short_f.useMeasurements) {
ts_short_f = new Touchstone(1);
*ts_short_f = Touchstone::fromFile(SOLT.short_f.file.toStdString());
ts_short_f->reduceTo1Port(SOLT.short_f.Sparam);
}
if(SOLT.load_f.useMeasurements) {
ts_load_f = new Touchstone(1);
*ts_load_f = Touchstone::fromFile(SOLT.load_f.file.toStdString());
ts_load_f->reduceTo1Port(SOLT.load_f.Sparam);
}
if(SOLT.Through.useMeasurements) {
ts_through = new Touchstone(2);

View File

@ -41,10 +41,14 @@ public:
void toFile(QString filename);
static Calkit fromFile(QString filename);
void edit(std::function<void(void)> done = nullptr);
SOLT toSOLT(double frequency);
bool hasSeparateMaleFemaleStandards();
SOLT toSOLT(double frequency, bool male_standards = true);
TRL toTRL(double frequency);
double minFreq(bool trl = false);
double maxFreq(bool trl = false);
double minFreqTRL();
double maxFreqTRL();
double minFreqSOLT(bool male_standards = true);
double maxFreqSOLT(bool male_standards = true);
bool checkIfValid(double min_freq, double max_freq, bool isTRL, bool include_male, bool include_female);
bool isTRLReflectionShort() const;
private:
@ -54,30 +58,34 @@ private:
QString manufacturer, serialnumber, description;
// SOLT standard definitions
struct {
struct {
using Open = struct {
double Z0, delay, loss, C0, C1, C2, C3;
QString file;
bool useMeasurements;
int Sparam;
} Open;
struct {
};
Open open_m, open_f;
using Short = struct {
double Z0, delay, loss, L0, L1, L2, L3;
QString file;
bool useMeasurements;
int Sparam;
} Short;
struct {
};
Short short_m, short_f;
using Load = struct {
double Z0, delay, Cparallel, Lseries;
QString file;
bool useMeasurements;
int Sparam;
} Load;
};
Load load_m, load_f;
struct {
double Z0, delay, loss;
QString file;
bool useMeasurements;
int Sparam1, Sparam2;
} Through;
bool separate_male_female;
} SOLT;
struct {
struct {
@ -92,7 +100,9 @@ private:
} TRL;
bool startDialogWithSOLT;
Touchstone *ts_open, *ts_short, *ts_load, *ts_through;
Touchstone *ts_open_m, *ts_short_m, *ts_load_m;
Touchstone *ts_open_f, *ts_short_f, *ts_load_f;
Touchstone *ts_through;
bool ts_cached;
using JSONDescription = struct _jsondescr {
@ -100,40 +110,67 @@ private:
QString name;
QVariant def;
};
const std::array<JSONDescription, 43> json_descr = {{
const std::array<JSONDescription, 71> json_descr = {{
{&manufacturer, "Manufacturer", ""},
{&serialnumber, "Serialnumber", ""},
{&description, "Description", ""},
{&SOLT.Open.Z0, "SOLT/Open/Param/Z0", 50.0},
{&SOLT.Open.delay, "SOLT/Open/Param/Delay", 0.0},
{&SOLT.Open.loss, "SOLT/Open/Param/Loss", 0.0},
{&SOLT.Open.C0, "SOLT/Open/Param/C0", 0.0},
{&SOLT.Open.C1, "SOLT/Open/Param/C1", 0.0},
{&SOLT.Open.C2, "SOLT/Open/Param/C2", 0.0},
{&SOLT.Open.C3, "SOLT/Open/Param/C3", 0.0},
{&SOLT.Open.useMeasurements, "SOLT/Open/Measurements/Use", false},
{&SOLT.Open.file, "SOLT/Open/Measurements/File", ""},
{&SOLT.Open.Sparam, "SOLT/Open/Measurements/Port", 0},
{&SOLT.open_m.Z0, "SOLT/Open/Param/Z0", 50.0},
{&SOLT.open_m.delay, "SOLT/Open/Param/Delay", 0.0},
{&SOLT.open_m.loss, "SOLT/Open/Param/Loss", 0.0},
{&SOLT.open_m.C0, "SOLT/Open/Param/C0", 0.0},
{&SOLT.open_m.C1, "SOLT/Open/Param/C1", 0.0},
{&SOLT.open_m.C2, "SOLT/Open/Param/C2", 0.0},
{&SOLT.open_m.C3, "SOLT/Open/Param/C3", 0.0},
{&SOLT.open_m.useMeasurements, "SOLT/Open/Measurements/Use", false},
{&SOLT.open_m.file, "SOLT/Open/Measurements/File", ""},
{&SOLT.open_m.Sparam, "SOLT/Open/Measurements/Port", 0},
{&SOLT.open_f.Z0, "SOLT/Open/Param/Z0_Female", 50.0},
{&SOLT.open_f.delay, "SOLT/Open/Param/Delay_Female", 0.0},
{&SOLT.open_f.loss, "SOLT/Open/Param/Loss_Female", 0.0},
{&SOLT.open_f.C0, "SOLT/Open/Param/C0_Female", 0.0},
{&SOLT.open_f.C1, "SOLT/Open/Param/C1_Female", 0.0},
{&SOLT.open_f.C2, "SOLT/Open/Param/C2_Female", 0.0},
{&SOLT.open_f.C3, "SOLT/Open/Param/C3_Female", 0.0},
{&SOLT.open_f.useMeasurements, "SOLT/Open/Measurements/Use_Female", false},
{&SOLT.open_f.file, "SOLT/Open/Measurements/File_Female", ""},
{&SOLT.open_f.Sparam, "SOLT/Open/Measurements/Port_Female", 0},
{&SOLT.Short.Z0, "SOLT/Short/Param/Z0", 50.0},
{&SOLT.Short.delay, "SOLT/Short/Param/Delay", 0.0},
{&SOLT.Short.loss, "SOLT/Short/Param/Loss", 0.0},
{&SOLT.Short.L0, "SOLT/Short/Param/L0", 0.0},
{&SOLT.Short.L1, "SOLT/Short/Param/L1", 0.0},
{&SOLT.Short.L2, "SOLT/Short/Param/L2", 0.0},
{&SOLT.Short.L3, "SOLT/Short/Param/L3", 0.0},
{&SOLT.Short.useMeasurements, "SOLT/Short/Measurements/Use", false},
{&SOLT.Short.file, "SOLT/Short/Measurements/File", ""},
{&SOLT.Short.Sparam, "SOLT/Short/Measurements/Port", 0},
{&SOLT.short_m.Z0, "SOLT/Short/Param/Z0", 50.0},
{&SOLT.short_m.delay, "SOLT/Short/Param/Delay", 0.0},
{&SOLT.short_m.loss, "SOLT/Short/Param/Loss", 0.0},
{&SOLT.short_m.L0, "SOLT/Short/Param/L0", 0.0},
{&SOLT.short_m.L1, "SOLT/Short/Param/L1", 0.0},
{&SOLT.short_m.L2, "SOLT/Short/Param/L2", 0.0},
{&SOLT.short_m.L3, "SOLT/Short/Param/L3", 0.0},
{&SOLT.short_m.useMeasurements, "SOLT/Short/Measurements/Use", false},
{&SOLT.short_m.file, "SOLT/Short/Measurements/File", ""},
{&SOLT.short_m.Sparam, "SOLT/Short/Measurements/Port", 0},
{&SOLT.short_f.Z0, "SOLT/Short/Param/Z0_Female", 50.0},
{&SOLT.short_f.delay, "SOLT/Short/Param/Delay_Female", 0.0},
{&SOLT.short_f.loss, "SOLT/Short/Param/Loss_Female", 0.0},
{&SOLT.short_f.L0, "SOLT/Short/Param/L0_Female", 0.0},
{&SOLT.short_f.L1, "SOLT/Short/Param/L1_Female", 0.0},
{&SOLT.short_f.L2, "SOLT/Short/Param/L2_Female", 0.0},
{&SOLT.short_f.L3, "SOLT/Short/Param/L3_Female", 0.0},
{&SOLT.short_f.useMeasurements, "SOLT/Short/Measurements/Use_Female", false},
{&SOLT.short_f.file, "SOLT/Short/Measurements/File_Female", ""},
{&SOLT.short_f.Sparam, "SOLT/Short/Measurements/Port_Female", 0},
{&SOLT.Load.Z0, "SOLT/Load/Param/Z0", 50.0},
{&SOLT.Load.delay, "SOLT/Load/Param/Delay", 0.0},
{&SOLT.Load.Cparallel, "SOLT/Load/Param/C", 0.0},
{&SOLT.Load.Lseries, "SOLT/Load/Param/L", 0.0},
{&SOLT.Load.useMeasurements, "SOLT/Load/Measurements/Use", false},
{&SOLT.Load.file, "SOLT/Load/Measurements/File", ""},
{&SOLT.Load.Sparam, "SOLT/Load/Measurements/Port", 0},
{&SOLT.load_m.Z0, "SOLT/Load/Param/Z0", 50.0},
{&SOLT.load_m.delay, "SOLT/Load/Param/Delay", 0.0},
{&SOLT.load_m.Cparallel, "SOLT/Load/Param/C", 0.0},
{&SOLT.load_m.Lseries, "SOLT/Load/Param/L", 0.0},
{&SOLT.load_m.useMeasurements, "SOLT/Load/Measurements/Use", false},
{&SOLT.load_m.file, "SOLT/Load/Measurements/File", ""},
{&SOLT.load_m.Sparam, "SOLT/Load/Measurements/Port", 0},
{&SOLT.load_f.Z0, "SOLT/Load/Param/Z0_Female", 50.0},
{&SOLT.load_f.delay, "SOLT/Load/Param/Delay_Female", 0.0},
{&SOLT.load_f.Cparallel, "SOLT/Load/Param/C_Female", 0.0},
{&SOLT.load_f.Lseries, "SOLT/Load/Param/L_Female", 0.0},
{&SOLT.load_f.useMeasurements, "SOLT/Load/Measurements/Use_Female", false},
{&SOLT.load_f.file, "SOLT/Load/Measurements/File_Female", ""},
{&SOLT.load_f.Sparam, "SOLT/Load/Measurements/Port_Female", 0},
{&SOLT.Through.Z0, "SOLT/Through/Param/Z0", 50.0},
{&SOLT.Through.delay, "SOLT/Through/Param/Delay", 0.0},
@ -143,6 +180,8 @@ private:
{&SOLT.Through.Sparam1, "SOLT/Through/Measurements/Port1", 0},
{&SOLT.Through.Sparam2, "SOLT/Through/Measurements/Port2", 1},
{&SOLT.separate_male_female, "SOLT/SeparateMaleFemale", false},
{&TRL.Through.Z0, "TRL/Through/Z0", 50.0},
{&TRL.Reflection.isShort, "TRL/Reflect/isShort", false},
{&TRL.Line.delay, "TRL/Line/Delay", 74.0},

View File

@ -47,6 +47,29 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
ui->load_parC->setPrefixes("fpnum ");
ui->load_serL->setUnit("H");
ui->load_serL->setPrefixes("fpnum ");
// Same setup for female standards
ui->OpenType_f->setId(ui->open_coefficients_f, 0);
ui->OpenType_f->setId(ui->open_measurement_f, 1);
ui->ShortType_f->setId(ui->short_coefficients_f, 0);
ui->ShortType_f->setId(ui->short_measurement_f, 1);
ui->LoadType_f->setId(ui->load_coefficients_f, 0);
ui->LoadType_f->setId(ui->load_measurement_f, 1);
ui->open_touchstone_f->setPorts(1);
ui->short_touchstone_f->setPorts(1);
ui->load_touchstone_f->setPorts(1);
ui->short_Z0_f->setUnit("Ω");
ui->open_Z0_f->setUnit("Ω");
ui->load_Z0_f->setUnit("Ω");
ui->load_parC_f->setUnit("F");
ui->load_parC_f->setPrefixes("fpnum ");
ui->load_serL_f->setUnit("H");
ui->load_serL_f->setPrefixes("fpnum ");
ui->through_Z0->setUnit("Ω");
ui->TRL_through_Z0->setUnit("Ω");
@ -59,6 +82,24 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
editKit.clearTouchstoneCache();
ownKit = editKit;
connect(ui->cbStandardDefinition, qOverload<int>(&QComboBox::currentIndexChanged), [=](int index){
if (index == 0) {
// common definition, hide tab bars, set all to male tab
ui->mf_short->setCurrentIndex(0);
ui->mf_short->tabBar()->hide();
ui->mf_open->setCurrentIndex(0);
ui->mf_open->tabBar()->hide();
ui->mf_load->setCurrentIndex(0);
ui->mf_load->tabBar()->hide();
} else {
// separate definitions for male/female standards
ui->mf_short->tabBar()->show();
ui->mf_open->tabBar()->show();
ui->mf_load->tabBar()->show();
}
});
updateEntries();
connect(ui->TRL_line_min, &SIUnitEdit::valueChanged, [=](double newval){
@ -85,6 +126,15 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
if(ui->load_measurement->isChecked() && !ui->load_touchstone->getStatus()) {
ok = false;
}
if(ui->open_measurement_f->isChecked() && !ui->open_touchstone_f->getStatus()) {
ok = false;
}
if(ui->short_measurement_f->isChecked() && !ui->short_touchstone_f->getStatus()) {
ok = false;
}
if(ui->load_measurement_f->isChecked() && !ui->load_touchstone_f->getStatus()) {
ok = false;
}
if(ui->through_measurement->isChecked() && !ui->through_touchstone->getStatus()) {
ok = false;
}
@ -95,6 +145,9 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->open_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->short_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->load_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->open_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->short_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->load_touchstone_f, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->through_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->OpenType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
@ -106,6 +159,15 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->LoadType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->OpenType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->ShortType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->LoadType_f, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
connect(ui->ThroughType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus();
});
@ -152,49 +214,83 @@ void CalkitDialog::parseEntries()
ownKit.description = ui->description->toPlainText();
// type
ownKit.SOLT.Open.useMeasurements = ui->open_measurement->isChecked();
ownKit.SOLT.Short.useMeasurements = ui->short_measurement->isChecked();
ownKit.SOLT.Load.useMeasurements = ui->load_measurement->isChecked();
ownKit.SOLT.open_m.useMeasurements = ui->open_measurement->isChecked();
ownKit.SOLT.short_m.useMeasurements = ui->short_measurement->isChecked();
ownKit.SOLT.load_m.useMeasurements = ui->load_measurement->isChecked();
ownKit.SOLT.open_f.useMeasurements = ui->open_measurement_f->isChecked();
ownKit.SOLT.short_f.useMeasurements = ui->short_measurement_f->isChecked();
ownKit.SOLT.load_f.useMeasurements = ui->load_measurement_f->isChecked();
ownKit.SOLT.Through.useMeasurements = ui->through_measurement->isChecked();
// coefficients
ownKit.SOLT.Open.Z0 = ui->open_Z0->value();
ownKit.SOLT.Open.delay = ui->open_delay->value();
ownKit.SOLT.Open.loss = ui->open_loss->value();
ownKit.SOLT.Open.C0 = ui->open_C0->value();
ownKit.SOLT.Open.C1 = ui->open_C1->value();
ownKit.SOLT.Open.C2 = ui->open_C2->value();
ownKit.SOLT.Open.C3 = ui->open_C3->value();
ownKit.SOLT.open_m.Z0 = ui->open_Z0->value();
ownKit.SOLT.open_m.delay = ui->open_delay->value();
ownKit.SOLT.open_m.loss = ui->open_loss->value();
ownKit.SOLT.open_m.C0 = ui->open_C0->value();
ownKit.SOLT.open_m.C1 = ui->open_C1->value();
ownKit.SOLT.open_m.C2 = ui->open_C2->value();
ownKit.SOLT.open_m.C3 = ui->open_C3->value();
ownKit.SOLT.Short.Z0 = ui->short_Z0->value();
ownKit.SOLT.Short.delay = ui->short_delay->value();
ownKit.SOLT.Short.loss = ui->short_loss->value();
ownKit.SOLT.Short.L0 = ui->short_L0->value();
ownKit.SOLT.Short.L1 = ui->short_L1->value();
ownKit.SOLT.Short.L2 = ui->short_L2->value();
ownKit.SOLT.Short.L3 = ui->short_L3->value();
ownKit.SOLT.short_m.Z0 = ui->short_Z0->value();
ownKit.SOLT.short_m.delay = ui->short_delay->value();
ownKit.SOLT.short_m.loss = ui->short_loss->value();
ownKit.SOLT.short_m.L0 = ui->short_L0->value();
ownKit.SOLT.short_m.L1 = ui->short_L1->value();
ownKit.SOLT.short_m.L2 = ui->short_L2->value();
ownKit.SOLT.short_m.L3 = ui->short_L3->value();
ownKit.SOLT.Load.Z0 = ui->load_Z0->value();
ownKit.SOLT.Load.delay = ui->load_delay->value();
ownKit.SOLT.Load.Cparallel = ui->load_parC->value();
ownKit.SOLT.Load.Lseries = ui->load_serL->value();
ownKit.SOLT.load_m.Z0 = ui->load_Z0->value();
ownKit.SOLT.load_m.delay = ui->load_delay->value();
ownKit.SOLT.load_m.Cparallel = ui->load_parC->value();
ownKit.SOLT.load_m.Lseries = ui->load_serL->value();
ownKit.SOLT.open_f.Z0 = ui->open_Z0_f->value();
ownKit.SOLT.open_f.delay = ui->open_delay_f->value();
ownKit.SOLT.open_f.loss = ui->open_loss_f->value();
ownKit.SOLT.open_f.C0 = ui->open_C0_f->value();
ownKit.SOLT.open_f.C1 = ui->open_C1_f->value();
ownKit.SOLT.open_f.C2 = ui->open_C2_f->value();
ownKit.SOLT.open_f.C3 = ui->open_C3_f->value();
ownKit.SOLT.short_f.Z0 = ui->short_Z0_f->value();
ownKit.SOLT.short_f.delay = ui->short_delay_f->value();
ownKit.SOLT.short_f.loss = ui->short_loss_f->value();
ownKit.SOLT.short_f.L0 = ui->short_L0_f->value();
ownKit.SOLT.short_f.L1 = ui->short_L1_f->value();
ownKit.SOLT.short_f.L2 = ui->short_L2_f->value();
ownKit.SOLT.short_f.L3 = ui->short_L3_f->value();
ownKit.SOLT.load_f.Z0 = ui->load_Z0_f->value();
ownKit.SOLT.load_f.delay = ui->load_delay_f->value();
ownKit.SOLT.load_f.Cparallel = ui->load_parC_f->value();
ownKit.SOLT.load_f.Lseries = ui->load_serL_f->value();
ownKit.SOLT.Through.Z0 = ui->through_Z0->value();
ownKit.SOLT.Through.delay = ui->through_delay->value();
ownKit.SOLT.Through.loss = ui->through_loss->value();
ownKit.SOLT.separate_male_female = ui->cbStandardDefinition->currentIndex() == 1;
// file
ownKit.SOLT.Open.file = ui->open_touchstone->getFilename();
ownKit.SOLT.Short.file = ui->short_touchstone->getFilename();
ownKit.SOLT.Load.file = ui->load_touchstone->getFilename();
ownKit.SOLT.open_m.file = ui->open_touchstone->getFilename();
ownKit.SOLT.short_m.file = ui->short_touchstone->getFilename();
ownKit.SOLT.load_m.file = ui->load_touchstone->getFilename();
ownKit.SOLT.Through.file = ui->through_touchstone->getFilename();
ownKit.SOLT.Open.Sparam = ui->open_touchstone->getPorts()[0];
ownKit.SOLT.Short.Sparam = ui->short_touchstone->getPorts()[0];
ownKit.SOLT.Load.Sparam = ui->load_touchstone->getPorts()[0];
ownKit.SOLT.open_m.Sparam = ui->open_touchstone->getPorts()[0];
ownKit.SOLT.short_m.Sparam = ui->short_touchstone->getPorts()[0];
ownKit.SOLT.load_m.Sparam = ui->load_touchstone->getPorts()[0];
ownKit.SOLT.Through.Sparam1 = ui->through_touchstone->getPorts()[0];
ownKit.SOLT.Through.Sparam2 = ui->through_touchstone->getPorts()[1];
ownKit.SOLT.open_f.file = ui->open_touchstone_f->getFilename();
ownKit.SOLT.short_f.file = ui->short_touchstone_f->getFilename();
ownKit.SOLT.load_f.file = ui->load_touchstone_f->getFilename();
ownKit.SOLT.open_f.Sparam = ui->open_touchstone_f->getPorts()[0];
ownKit.SOLT.short_f.Sparam = ui->short_touchstone_f->getPorts()[0];
ownKit.SOLT.load_f.Sparam = ui->load_touchstone_f->getPorts()[0];
// TRL
ownKit.TRL.Through.Z0 = ui->TRL_through_Z0->value();
ownKit.TRL.Reflection.isShort = ui->TRL_R_short->isChecked();
@ -212,70 +308,124 @@ void CalkitDialog::updateEntries()
ui->description->setPlainText(ownKit.description);
// Coefficients
ui->open_Z0->setValueQuiet(ownKit.SOLT.Open.Z0);
ui->open_delay->setValueQuiet(ownKit.SOLT.Open.delay);
ui->open_loss->setValueQuiet(ownKit.SOLT.Open.loss);
ui->open_C0->setValueQuiet(ownKit.SOLT.Open.C0);
ui->open_C1->setValueQuiet(ownKit.SOLT.Open.C1);
ui->open_C2->setValueQuiet(ownKit.SOLT.Open.C2);
ui->open_C3->setValueQuiet(ownKit.SOLT.Open.C3);
ui->open_Z0->setValueQuiet(ownKit.SOLT.open_m.Z0);
ui->open_delay->setValueQuiet(ownKit.SOLT.open_m.delay);
ui->open_loss->setValueQuiet(ownKit.SOLT.open_m.loss);
ui->open_C0->setValueQuiet(ownKit.SOLT.open_m.C0);
ui->open_C1->setValueQuiet(ownKit.SOLT.open_m.C1);
ui->open_C2->setValueQuiet(ownKit.SOLT.open_m.C2);
ui->open_C3->setValueQuiet(ownKit.SOLT.open_m.C3);
ui->short_Z0->setValueQuiet(ownKit.SOLT.Short.Z0);
ui->short_delay->setValueQuiet(ownKit.SOLT.Short.delay);
ui->short_loss->setValueQuiet(ownKit.SOLT.Short.loss);
ui->short_L0->setValueQuiet(ownKit.SOLT.Short.L0);
ui->short_L1->setValueQuiet(ownKit.SOLT.Short.L1);
ui->short_L2->setValueQuiet(ownKit.SOLT.Short.L2);
ui->short_L3->setValueQuiet(ownKit.SOLT.Short.L3);
ui->short_Z0->setValueQuiet(ownKit.SOLT.short_m.Z0);
ui->short_delay->setValueQuiet(ownKit.SOLT.short_m.delay);
ui->short_loss->setValueQuiet(ownKit.SOLT.short_m.loss);
ui->short_L0->setValueQuiet(ownKit.SOLT.short_m.L0);
ui->short_L1->setValueQuiet(ownKit.SOLT.short_m.L1);
ui->short_L2->setValueQuiet(ownKit.SOLT.short_m.L2);
ui->short_L3->setValueQuiet(ownKit.SOLT.short_m.L3);
ui->load_Z0->setValueQuiet(ownKit.SOLT.Load.Z0);
ui->load_delay->setValueQuiet(ownKit.SOLT.Load.delay);
ui->load_parC->setValueQuiet(ownKit.SOLT.Load.Cparallel);
ui->load_serL->setValueQuiet(ownKit.SOLT.Load.Lseries);
ui->load_Z0->setValueQuiet(ownKit.SOLT.load_m.Z0);
ui->load_delay->setValueQuiet(ownKit.SOLT.load_m.delay);
ui->load_parC->setValueQuiet(ownKit.SOLT.load_m.Cparallel);
ui->load_serL->setValueQuiet(ownKit.SOLT.load_m.Lseries);
ui->open_Z0_f->setValueQuiet(ownKit.SOLT.open_f.Z0);
ui->open_delay_f->setValueQuiet(ownKit.SOLT.open_f.delay);
ui->open_loss_f->setValueQuiet(ownKit.SOLT.open_f.loss);
ui->open_C0_f->setValueQuiet(ownKit.SOLT.open_f.C0);
ui->open_C1_f->setValueQuiet(ownKit.SOLT.open_f.C1);
ui->open_C2_f->setValueQuiet(ownKit.SOLT.open_f.C2);
ui->open_C3_f->setValueQuiet(ownKit.SOLT.open_f.C3);
ui->short_Z0_f->setValueQuiet(ownKit.SOLT.short_f.Z0);
ui->short_delay_f->setValueQuiet(ownKit.SOLT.short_f.delay);
ui->short_loss_f->setValueQuiet(ownKit.SOLT.short_f.loss);
ui->short_L0_f->setValueQuiet(ownKit.SOLT.short_f.L0);
ui->short_L1_f->setValueQuiet(ownKit.SOLT.short_f.L1);
ui->short_L2_f->setValueQuiet(ownKit.SOLT.short_f.L2);
ui->short_L3_f->setValueQuiet(ownKit.SOLT.short_f.L3);
ui->load_Z0_f->setValueQuiet(ownKit.SOLT.load_f.Z0);
ui->load_delay_f->setValueQuiet(ownKit.SOLT.load_f.delay);
ui->load_parC_f->setValueQuiet(ownKit.SOLT.load_f.Cparallel);
ui->load_serL_f->setValueQuiet(ownKit.SOLT.load_f.Lseries);
ui->through_Z0->setValueQuiet(ownKit.SOLT.Through.Z0);
ui->through_delay->setValueQuiet(ownKit.SOLT.Through.delay);
ui->through_loss->setValueQuiet(ownKit.SOLT.Through.loss);
// Measurements
ui->open_touchstone->setFile(ownKit.SOLT.Open.file);
ui->open_touchstone->selectPort(0, ownKit.SOLT.Open.Sparam);
ui->open_touchstone->setFile(ownKit.SOLT.open_m.file);
ui->open_touchstone->selectPort(0, ownKit.SOLT.open_m.Sparam);
ui->short_touchstone->setFile(ownKit.SOLT.Short.file);
ui->short_touchstone->selectPort(0, ownKit.SOLT.Short.Sparam);
ui->short_touchstone->setFile(ownKit.SOLT.short_m.file);
ui->short_touchstone->selectPort(0, ownKit.SOLT.short_m.Sparam);
ui->load_touchstone->setFile(ownKit.SOLT.Load.file);
ui->load_touchstone->selectPort(0, ownKit.SOLT.Load.Sparam);
ui->load_touchstone->setFile(ownKit.SOLT.load_m.file);
ui->load_touchstone->selectPort(0, ownKit.SOLT.load_m.Sparam);
ui->open_touchstone_f->setFile(ownKit.SOLT.open_f.file);
ui->open_touchstone_f->selectPort(0, ownKit.SOLT.open_f.Sparam);
ui->short_touchstone_f->setFile(ownKit.SOLT.short_f.file);
ui->short_touchstone_f->selectPort(0, ownKit.SOLT.short_f.Sparam);
ui->load_touchstone_f->setFile(ownKit.SOLT.load_f.file);
ui->load_touchstone_f->selectPort(0, ownKit.SOLT.load_f.Sparam);
ui->through_touchstone->setFile(ownKit.SOLT.Through.file);
ui->through_touchstone->selectPort(0, ownKit.SOLT.Through.Sparam1);
ui->through_touchstone->selectPort(1, ownKit.SOLT.Through.Sparam2);
// Type
if (ownKit.SOLT.Open.useMeasurements) {
if (ownKit.SOLT.open_m.useMeasurements) {
ui->open_measurement->click();
} else {
ui->open_coefficients->click();
}
if (ownKit.SOLT.Short.useMeasurements) {
if (ownKit.SOLT.short_m.useMeasurements) {
ui->short_measurement->click();
} else {
ui->short_coefficients->click();
}
if (ownKit.SOLT.Load.useMeasurements) {
if (ownKit.SOLT.load_m.useMeasurements) {
ui->load_measurement->click();
} else {
ui->load_coefficients->click();
}
if (ownKit.SOLT.open_f.useMeasurements) {
ui->open_measurement_f->click();
} else {
ui->open_coefficients_f->click();
}
if (ownKit.SOLT.short_f.useMeasurements) {
ui->short_measurement_f->click();
} else {
ui->short_coefficients_f->click();
}
if (ownKit.SOLT.load_f.useMeasurements) {
ui->load_measurement_f->click();
} else {
ui->load_coefficients_f->click();
}
if (ownKit.SOLT.Through.useMeasurements) {
ui->through_measurement->click();
} else {
ui->through_coefficients->click();
}
if (ownKit.SOLT.separate_male_female) {
ui->cbStandardDefinition->setCurrentIndex(1);
} else {
ui->cbStandardDefinition->setCurrentIndex(0);
}
// TRL
ui->TRL_through_Z0->setValueQuiet(ownKit.TRL.Through.Z0);
if(ownKit.TRL.Reflection.isShort) {

View File

@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1141</width>
<height>602</height>
<width>1213</width>
<height>626</height>
</rect>
</property>
<property name="sizePolicy">
@ -28,7 +28,7 @@
<property name="sizeGripEnabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<layout class="QVBoxLayout" name="verticalLayout_13" stretch="0,0,1,0">
<item>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="0">
@ -93,9 +93,43 @@
<attribute name="title">
<string>SOLT</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QHBoxLayout" name="horizontalLayout_20">
<item>
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_19" stretch="0,1">
<item>
<widget class="QLabel" name="label_65">
<property name="text">
<string>Standard definition:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbStandardDefinition">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Use one common definition for open/short/load standard</string>
</property>
</item>
<item>
<property name="text">
<string>Use separate male/female definitions for open/short/load standard</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<layout class="QVBoxLayout" name="verticalLayout_22">
<item>
<widget class="QLabel" name="label_8">
<property name="font">
@ -111,6 +145,16 @@
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="mf_short">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Male</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
@ -232,6 +276,137 @@
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>Female</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QRadioButton" name="short_coefficients_f">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ShortType_f</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="short_measurement_f">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">ShortType_f</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="short_stack_f">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_9">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<layout class="QFormLayout" name="formLayout_9">
<item row="1" column="0">
<widget class="QLabel" name="label_43">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="short_delay_f"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_44">
<property name="text">
<string>Offset loss [GΩ/s]: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="short_loss_f"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_45">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L0 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-12&lt;/span&gt;H]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="short_L0_f"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_46">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L1 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-24&lt;/span&gt;H/Hz]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="short_L1_f"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L2 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-33&lt;/span&gt;H/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;2&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="SIUnitEdit" name="short_L2_f"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_48">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;L3 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-42&lt;/span&gt;H/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;3&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="SIUnitEdit" name="short_L3_f"/>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="short_Z0_f">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_49">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_10">
<layout class="QVBoxLayout" name="verticalLayout_14" stretch="0">
<item>
<widget class="TouchstoneImport" name="short_touchstone_f" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
@ -241,7 +416,7 @@
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_23">
<item>
<widget class="QLabel" name="label_7">
<property name="font">
@ -257,6 +432,16 @@
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="mf_open">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_5">
<attribute name="title">
<string>Male</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@ -378,6 +563,137 @@
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_6">
<attribute name="title">
<string>Female</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QRadioButton" name="open_coefficients_f">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">OpenType_f</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="open_measurement_f">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">OpenType_f</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="open_stack_f">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_11">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<layout class="QFormLayout" name="formLayout_10">
<item row="1" column="0">
<widget class="QLabel" name="label_50">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="open_delay_f"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_51">
<property name="text">
<string>Offset loss [GΩ/s]: </string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="open_loss_f"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_52">
<property name="text">
<string>C0 [10&lt;sup&gt;-15&lt;/sup&gt;F]:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="open_C0_f"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_53">
<property name="text">
<string>C1 [10&lt;sup&gt;-27&lt;/sup&gt;F/Hz]:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="SIUnitEdit" name="open_C1_f"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_54">
<property name="text">
<string>C2 [10&lt;sup&gt;-36&lt;/sup&gt;F/Hz&lt;sup&gt;2&lt;/sup&gt;]:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="SIUnitEdit" name="open_C2_f"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_55">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;C3 [10&lt;span style=&quot; vertical-align:super;&quot;&gt;-45&lt;/span&gt;F/Hz&lt;span style=&quot; vertical-align:super;&quot;&gt;3&lt;/span&gt;]:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="SIUnitEdit" name="open_C3_f"/>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="open_Z0_f">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_56">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_12">
<layout class="QVBoxLayout" name="verticalLayout_16" stretch="0">
<item>
<widget class="TouchstoneImport" name="open_touchstone_f" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
@ -387,7 +703,7 @@
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_24">
<item>
<widget class="QLabel" name="label_19">
<property name="font">
@ -403,6 +719,16 @@
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="mf_load">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab_7">
<attribute name="title">
<string>Male</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_21">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
@ -467,6 +793,9 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="load_parC"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
@ -474,9 +803,6 @@
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="load_parC"/>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="load_serL"/>
</item>
@ -494,6 +820,111 @@
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_8">
<attribute name="title">
<string>Female</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_17">
<item>
<widget class="QRadioButton" name="load_coefficients_f">
<property name="text">
<string>Coefficients</string>
</property>
<attribute name="buttonGroup">
<string notr="true">LoadType_f</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="load_measurement_f">
<property name="text">
<string>Measurement file</string>
</property>
<attribute name="buttonGroup">
<string notr="true">LoadType_f</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="load_stack_f">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page_15">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_12">
<item row="0" column="0">
<widget class="QLabel" name="label_61">
<property name="text">
<string>Z0:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="load_Z0_f">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_62">
<property name="text">
<string>Offset delay [ps]:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="load_delay_f"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_63">
<property name="text">
<string>Parallel C:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="load_parC_f"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_64">
<property name="text">
<string>Series L:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="load_serL_f"/>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_16">
<layout class="QVBoxLayout" name="verticalLayout_19" stretch="0">
<item>
<widget class="TouchstoneImport" name="load_touchstone_f" native="true"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
@ -602,12 +1033,14 @@
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>TRL</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0,1,0,1,0">
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0,1,0,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
@ -867,19 +1300,6 @@
</item>
</layout>
</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>
</widget>
</widget>
@ -986,12 +1406,63 @@
</hint>
</hints>
</connection>
<connection>
<sender>ShortType_f</sender>
<signal>buttonClicked(int)</signal>
<receiver>short_stack_f</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>165</x>
<y>442</y>
</hint>
</hints>
</connection>
<connection>
<sender>OpenType_f</sender>
<signal>buttonClicked(int)</signal>
<receiver>open_stack_f</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>466</x>
<y>442</y>
</hint>
</hints>
</connection>
<connection>
<sender>LoadType_f</sender>
<signal>buttonClicked(int)</signal>
<receiver>load_stack_f</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>767</x>
<y>442</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="ThroughType"/>
<buttongroup name="LoadType"/>
<buttongroup name="OpenType"/>
<buttongroup name="ShortType"/>
<buttongroup name="LoadType"/>
<buttongroup name="ShortType_f"/>
<buttongroup name="TRL_Rtype"/>
<buttongroup name="LoadType_f"/>
<buttongroup name="OpenType_f"/>
<buttongroup name="ShortType"/>
<buttongroup name="ThroughType"/>
</buttongroups>
</ui>

View File

@ -19,21 +19,46 @@ int MeasurementModel::rowCount(const QModelIndex &) const
int MeasurementModel::columnCount(const QModelIndex &) const
{
return ColIndexLast;
return (int) ColIndex::Last;
}
QVariant MeasurementModel::data(const QModelIndex &index, int role) const
{
auto info = cal->getMeasurementInfo(measurements[index.row()]);
if(role == Qt::DisplayRole) {
switch(index.column()) {
case ColIndexName:
switch((ColIndex) index.column()) {
case ColIndex::Name:
return info.name;
break;
case ColIndexDescription:
case ColIndex::Gender:
switch(measurements[index.row()]) {
case Calibration::Measurement::Port1Load:
case Calibration::Measurement::Port1Open:
case Calibration::Measurement::Port1Short:
if(cal->getPortStandard(1) == Calibration::PortStandard::Male) {
return "Male";
} else {
return "Female";
}
break;
case Calibration::Measurement::Port2Load:
case Calibration::Measurement::Port2Open:
case Calibration::Measurement::Port2Short:
if(cal->getPortStandard(2) == Calibration::PortStandard::Male) {
return "Male";
} else {
return "Female";
}
break;
default:
return "";
}
break;
case ColIndex::Description:
return info.prerequisites;
break;
case ColIndexData:
case ColIndex::Data:
if(info.points > 0) {
QString data = QString::number(info.points);
data.append(" points from ");
@ -45,16 +70,17 @@ QVariant MeasurementModel::data(const QModelIndex &index, int role) const
return "Not available";
}
break;
case ColIndexDate:
case ColIndex::Date:
return info.timestamp.toString("dd.MM.yyyy hh:mm:ss");
break;
}
} else if(role == Qt::SizeHintRole) {
switch(index.column()) {
case ColIndexName: return 200; break;
case ColIndexDescription: return 500; break;
case ColIndexData: return 300; break;
case ColIndexDate: return 300; break;
switch((ColIndex) index.column()) {
case ColIndex::Name: return 200; break;
case ColIndex::Gender: return 150; break;
case ColIndex::Description: return 500; break;
case ColIndex::Data: return 300; break;
case ColIndex::Date: return 300; break;
default: return QVariant(); break;
}
}
@ -65,11 +91,12 @@ QVariant MeasurementModel::data(const QModelIndex &index, int role) const
QVariant MeasurementModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) {
case ColIndexName: return "Type"; break;
case ColIndexDescription: return "Prerequisites"; break;
case ColIndexData: return "Statistics"; break;
case ColIndexDate: return "Timestamp"; break;
switch((ColIndex) section) {
case ColIndex::Name: return "Type"; break;
case ColIndex::Gender: return "Gender"; break;
case ColIndex::Description: return "Prerequisites"; break;
case ColIndex::Data: return "Statistics"; break;
case ColIndex::Date: return "Timestamp"; break;
default: return QVariant(); break;
}
} else {
@ -83,6 +110,11 @@ void MeasurementModel::measurementUpdated(Calibration::Measurement m)
auto it = std::find(measurements.begin(), measurements.end(), m);
if(it != measurements.end()) {
int row = it - measurements.begin();
emit dataChanged(index(row, 0), index(row, ColIndexLast - 1));
emit dataChanged(index(row, 0), index(row, (int) ColIndex::Last - 1));
}
}
void MeasurementModel::genderUpdated()
{
emit dataChanged(index(0, (int) ColIndex::Gender), index(rowCount() - 1, (int) ColIndex::Gender));
}

View File

@ -11,6 +11,15 @@ class MeasurementModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum class ColIndex {
Name,
Gender,
Description,
Data,
Date,
Last
};
MeasurementModel(Calibration *cal, std::vector<Calibration::Measurement> measurements);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@ -20,15 +29,9 @@ public:
public slots:
void measurementUpdated(Calibration::Measurement m);
void genderUpdated();
private:
enum {
ColIndexName,
ColIndexDescription,
ColIndexData,
ColIndexDate,
ColIndexLast
};
Calibration *cal;
std::vector<Calibration::Measurement> measurements;
};

View File

@ -218,6 +218,7 @@ SOURCES += \
Traces/tracewidget.cpp \
Traces/tracexyplot.cpp \
Traces/xyplotaxisdialog.cpp \
Util/util.cpp \
VNA/Deembedding/deembedding.cpp \
VNA/Deembedding/deembeddingdialog.cpp \
VNA/Deembedding/deembeddingoption.cpp \
@ -298,10 +299,11 @@ FORMS += \
DISTFILES +=
RESOURCES += \
icons.qrc
icons.qrc \
resources/librevna.qrc
CONFIG += c++17
REVISION = $$system(git rev-parse HEAD)
DEFINES += GITHASH=\\"\"$$REVISION\\"\"
DEFINES += FW_MAJOR=1 FW_MINOR=2 FW_PATCH=0 FW_SUFFIX=""#\\"\"-alpha.2\\"\"
DEFINES += FW_MAJOR=1 FW_MINOR=2 FW_PATCH=1 FW_SUFFIX=""#\\"\"-alpha.2\\"\"
DEFINES -= _UNICODE UNICODE

View File

@ -265,6 +265,13 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
// Set initial sweep settings
auto pref = Preferences::getInstance();
if(pref.Acquisition.useMedianAveraging) {
average.setMode(Averaging::Mode::Median);
} else {
average.setMode(Averaging::Mode::Mean);
}
if(pref.Startup.RememberSweepSettings) {
LoadSweepSettings();
} else {
@ -315,7 +322,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
tracking["enabled"] = settings.trackingGenerator ? true : false;
tracking["port"] = settings.trackingGeneratorPort ? 2 : 1;
tracking["offset"] = settings.trackingGeneratorOffset;
tracking["power"] = settings.trackingPower;
tracking["power"] = (double) settings.trackingPower / 100.0; // convert to dBm
sweep["trackingGenerator"] = tracking;
if(normalize.active) {
@ -413,6 +420,11 @@ using namespace std;
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
{
if(d.pointNum >= settings.pointNum) {
qWarning() << "Ignoring point with too large point number (" << d.pointNum << ")";
return;
}
d = average.process(d);
if(normalize.measuring) {
@ -451,6 +463,11 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
UpdateAverageCount();
markerModel->updateMarkers();
}
static unsigned int lastPoint = 0;
if(d.pointNum > 0 && d.pointNum != lastPoint + 1) {
qWarning() << "Got point" << d.pointNum << "but last received point was" << lastPoint << "("<<(d.pointNum-lastPoint-1)<<"missed points)";
}
lastPoint = d.pointNum;
}
void SpectrumAnalyzer::SettingsChanged()
@ -1065,6 +1082,11 @@ void SpectrumAnalyzer::updateGraphColors()
emit graphColorsChanged();
}
void SpectrumAnalyzer::setAveragingMode(Averaging::Mode mode)
{
average.setMode(mode);
}
QString SpectrumAnalyzer::WindowToString(SpectrumAnalyzer::Window w)
{
switch(w) {

View File

@ -26,6 +26,7 @@ public:
virtual void fromJSON(nlohmann::json j) override;
void updateGraphColors();
void setAveragingMode(Averaging::Mode mode);
private:

View File

@ -674,7 +674,7 @@ void Marker::updateContextmenu()
auto typemenu = contextmenu.addMenu("Type");
auto typegroup = new QActionGroup(&contextmenu);
for(auto t : getSupportedTypes()) {
auto setTypeAction = new QAction(typeToString(t));
auto setTypeAction = new QAction(typeToString(t), typemenu);
setTypeAction->setCheckable(true);
if(t == type) {
setTypeAction->setChecked(true);
@ -689,7 +689,7 @@ void Marker::updateContextmenu()
auto table = contextmenu.addMenu("Data Format in Table");
auto tablegroup = new QActionGroup(&contextmenu);
for(auto f : applicableFormats()) {
auto setFormatAction = new QAction(formatToString(f));
auto setFormatAction = new QAction(formatToString(f), table);
setFormatAction->setCheckable(true);
if(f == formatTable) {
setFormatAction->setChecked(true);
@ -703,7 +703,7 @@ void Marker::updateContextmenu()
auto graph = contextmenu.addMenu("Show on Graph");
for(auto f : applicableFormats()) {
auto setFormatAction = new QAction(formatToString(f));
auto setFormatAction = new QAction(formatToString(f), graph);
setFormatAction->setCheckable(true);
if(formatGraph.count(f)) {
setFormatAction->setChecked(true);
@ -753,7 +753,7 @@ void Marker::updateContextmenu()
}
if(group != nullptr) {
// "remove from group" available
auto removeGroup = new QAction("Remove from linked group");
auto removeGroup = new QAction("Remove from linked group", &contextmenu);
connect(removeGroup, &QAction::triggered, [=](){
group->remove(this);
});
@ -765,7 +765,7 @@ void Marker::updateContextmenu()
}
auto deleteAction = new QAction("Delete");
auto deleteAction = new QAction("Delete", &contextmenu);
connect(deleteAction, &QAction::triggered, this, &Marker::deleteLater);
contextmenu.addAction(deleteAction);
}
@ -1560,6 +1560,9 @@ bool Marker::isMovable()
// helper traces are never movable by the user
return false;
}
if(!parentTrace || parentTrace->size() == 0) {
return false;
}
switch(type) {
case Type::Manual:
case Type::Delta:

View File

@ -22,9 +22,7 @@ bool MarkerGroup::add(Marker *m)
connect(m, &Marker::positionChanged, this, &MarkerGroup::markerMoved);
connect(m, &Marker::typeChanged, this, &MarkerGroup::checkMarker);
connect(m, &Marker::domainChanged, this, &MarkerGroup::checkMarker);
connect(m, &Marker::deleted, [=](){
remove(m);
});
connect(m, &Marker::deleted, this, &MarkerGroup::remove);
if(markers.size() > 0) {
m->setPosition((*markers.begin())->getPosition());

View File

@ -17,7 +17,6 @@ public:
~MarkerGroup();
bool add(Marker *m);
bool remove(Marker *m);
unsigned int getNumber() const;
bool applicable(Marker *m);
@ -25,6 +24,9 @@ public:
signals:
void emptied(MarkerGroup*);
public slots:
bool remove(Marker *m);
private:
void markerMoved(double newpos);

View File

@ -5,6 +5,7 @@
#include <QKeyEvent>
#include <QMenu>
#include <QPainter>
MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
QWidget(parent),
@ -12,6 +13,16 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
model(model)
{
ui->setupUi(this);
// some image magic to create a button with three "add" icons (not available as standard icon)
QImage image(44, 44, QImage::Format_ARGB32);
auto origImage = ui->bAddAll->icon().pixmap(22).toImage().convertToFormat(QImage::Format_ARGB32);
QPainter painter(&image);
painter.drawImage(0, 0, origImage);
painter.drawImage(origImage.width(), 0, origImage);
painter.drawImage(origImage.width()/2, origImage.height(), origImage);
ui->bAddAll->setIcon(QIcon(QPixmap::fromImage(image)));
ui->treeView->setModel(&model);
ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexTrace, new MarkerTraceDelegate);
ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexType, new MarkerTypeDelegate);
@ -47,7 +58,7 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
}
// multiple markers selected, execute group context menu
QMenu menu;
auto createGroup = new QAction("Link selected");
auto createGroup = new QAction("Link selected", &menu);
connect(createGroup, &QAction::triggered, [&](){
auto g = model.createMarkerGroup();
// assign markers to group
@ -57,7 +68,7 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
});
menu.addAction(createGroup);
if(anyInGroup) {
auto removeGroup = new QAction("Break Links");
auto removeGroup = new QAction("Break Links", &menu);
connect(removeGroup, &QAction::triggered, [&](){
// remove selected markers from groups if they are already assigned to one
for(auto m : selected) {
@ -123,6 +134,18 @@ void MarkerWidget::on_bAdd_clicked()
model.addMarker(marker);
}
void MarkerWidget::on_bAddAll_clicked()
{
// add a marker for every trace and link them
auto group = model.createMarkerGroup();
for(auto trace : model.getModel().getTraces()) {
auto m = model.createDefaultMarker();
m->assignTrace(trace);
group->add(m);
model.addMarker(m);
}
}
bool MarkerWidget::eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
@ -152,4 +175,3 @@ void MarkerWidget::updatePersistentEditors()
}
}
}

View File

@ -22,6 +22,8 @@ private slots:
void on_bAdd_clicked();
void updatePersistentEditors();
void on_bAddAll_clicked();
private:
bool eventFilter(QObject *obj, QEvent *event) override;
Ui::MarkerWidget *ui;

View File

@ -44,7 +44,27 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>Add</string>
<string>Add marker</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="list-add" resource="../../icons.qrc">
<normaloff>:/icons/add.png</normaloff>:/icons/add.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bAddAll">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Add markers to all traces</string>
</property>
<property name="text">
<string/>
@ -64,7 +84,7 @@
</sizepolicy>
</property>
<property name="toolTip">
<string>Delete</string>
<string>Delete marker</string>
</property>
<property name="text">
<string/>

View File

@ -42,6 +42,9 @@ void Math::DFT::edit()
auto d = new QDialog();
auto ui = new Ui::DFTDialog;
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->windowBox->setLayout(new QVBoxLayout);
ui->windowBox->layout()->addWidget(window.createEditor());
@ -76,6 +79,9 @@ QWidget *Math::DFT::createExplanationWidget()
auto w = new QWidget();
auto ui = new Ui::DFTExplanationWidget;
ui->setupUi(w);
connect(w, &QWidget::destroyed, [=](){
delete ui;
});
return w;
}

View File

@ -37,6 +37,9 @@ void Math::Expression::edit()
auto d = new QDialog();
auto ui = new Ui::ExpressionDialog;
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->expEdit->setText(exp);
connect(ui->buttonBox, &QDialogButtonBox::accepted, [=](){
exp = ui->expEdit->text();
@ -57,6 +60,9 @@ QWidget *Math::Expression::createExplanationWidget()
auto w = new QWidget();
auto ui = new Ui::ExpressionExplanationWidget;
ui->setupUi(w);
connect(w, &QWidget::destroyed, [=](){
delete ui;
});
return w;
}

View File

@ -29,6 +29,9 @@ void MedianFilter::edit()
auto d = new QDialog();
auto ui = new Ui::MedianFilterDialog();
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->kernelSize->setValue(kernelSize);
ui->sortingMethod->setCurrentIndex((int) order);
@ -52,6 +55,9 @@ QWidget *MedianFilter::createExplanationWidget()
auto w = new QWidget();
auto ui = new Ui::MedianFilterExplanationWidget;
ui->setupUi(w);
connect(w, &QWidget::destroyed, [=](){
delete ui;
});
return w;
}

View File

@ -52,6 +52,9 @@ void TDR::edit()
auto d = new QDialog();
auto ui = new Ui::TDRDialog;
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->windowBox->setLayout(new QVBoxLayout);
ui->windowBox->layout()->addWidget(window.createEditor());
@ -119,6 +122,9 @@ QWidget *TDR::createExplanationWidget()
auto w = new QWidget();
auto ui = new Ui::TDRExplanationWidget;
ui->setupUi(w);
connect(w, &QWidget::destroyed, [=](){
delete ui;
});
return w;
}

View File

@ -58,6 +58,9 @@ void Math::TimeGate::edit()
auto d = new QDialog();
auto ui = new Ui::TimeGateDialog();
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->graph->setGate(this);
ui->windowBox->setLayout(new QVBoxLayout);
ui->windowBox->layout()->addWidget(window.createEditor());
@ -113,6 +116,9 @@ QWidget *Math::TimeGate::createExplanationWidget()
auto w = new QWidget();
auto ui = new Ui::TimeGateExplanationWidget;
ui->setupUi(w);
connect(w, &QWidget::destroyed, [=](){
delete ui;
});
return w;
}

View File

@ -74,6 +74,9 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
ret.explanationWidget = new QWidget();
auto ui = new Ui::TimeDomainGatingExplanationWidget;
ui->setupUi(ret.explanationWidget);
connect(ret.explanationWidget, &QWidget::destroyed, [=](){
delete ui;
});
}
break;
default:
@ -84,7 +87,14 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
TraceMath::Data TraceMath::getSample(unsigned int index)
{
return data.at(index);
if(index < data.size()) {
return data[index];
} else {
TraceMath::Data d;
d.x = 0;
d.y = 0;
return d;
}
}
double TraceMath::getStepResponse(unsigned int index)

View File

@ -169,6 +169,9 @@ TraceEditDialog::TraceEditDialog(Trace &t, QWidget *parent) :
auto d = new QDialog();
auto ui = new Ui::NewTraceMathDialog();
ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
for(int i = 0; i < (int) TraceMath::Type::Last;i++) {
auto info = TraceMath::getInfo(static_cast<TraceMath::Type>(i));
ui->list->addItem(info.name);

View File

@ -165,13 +165,12 @@ void TracePlot::paintEvent(QPaintEvent *event)
continue;
}
// Trace name
auto textArea = QRect(x, areaTextTop, width() - x, marginTop);
QFont font = p.font();
font.setPixelSize(12);
p.setFont(font);
p.setPen(t.first->color());
auto space = " ";
auto label = space + t.first->name() + space;
QRectF usedLabelArea = p.boundingRect(textArea, 0, label);
@ -185,9 +184,6 @@ void TracePlot::paintEvent(QPaintEvent *event)
x += usedLabelArea.width()+labelMarginRight;
auto tmarkers = t.first->getMarkers();
font.setPixelSize(12);
p.setFont(font);
for(auto m : tmarkers) {
if(!xCoordinateVisible(m->getPosition())) {
// marker not visible with current plot settings
@ -199,9 +195,9 @@ void TracePlot::paintEvent(QPaintEvent *event)
}
hasMarkerData = true;
// Rounded box
auto space = " ";
auto textArea = QRect(width() - marginRight - marginMarkerData, y, width() - marginRight, y + 100);
auto description = m->getSuffix() + space + m->readablePosition();
auto label = space + QString::number(m->getNumber()) + space;
QRectF textAreaConsumed = p.boundingRect(textArea, 0, label);
QPainterPath pathM;
@ -209,10 +205,14 @@ void TracePlot::paintEvent(QPaintEvent *event)
p.fillPath(pathM, t.first->color());
p.drawPath(pathM);
// Over box
p.setPen(Util::getFontColorFromBackground(t.first->color()));
p.drawText(textArea, 0, label);
// Non-rounded description
auto description = m->getSuffix() + space + m->readablePosition();
p.setPen(t.first->color());
p.drawText(textAreaConsumed.x()+textAreaConsumed.width(), textAreaConsumed.y(), textArea.width(), textArea.height(), 0, description);
p.drawText(width() - marginRight - marginMarkerData + textAreaConsumed.width() + 5, textAreaConsumed.y(), width() - marginRight, textArea.height(), 0, description);
y += textAreaConsumed.height();
for(auto f : m->getGraphDisplayFormats()) {

View File

@ -186,7 +186,7 @@ void TraceSmithChart::draw(QPainter &p) {
// trace marked invisible
continue;
}
pen = QPen(trace->color(), 1);
pen = QPen(trace->color(), pref.Graphs.lineWidth);
pen.setCosmetic(true);
p.setPen(pen);
int nPoints = trace->size();

View File

@ -17,22 +17,6 @@
using namespace std;
const set<TraceXYPlot::YAxisType> TraceXYPlot::YAxisTypes = {TraceXYPlot::YAxisType::Disabled,
TraceXYPlot::YAxisType::Magnitude,
TraceXYPlot::YAxisType::Phase,
TraceXYPlot::YAxisType::VSWR,
TraceXYPlot::YAxisType::Real,
TraceXYPlot::YAxisType::Imaginary,
TraceXYPlot::YAxisType::SeriesR,
TraceXYPlot::YAxisType::Reactance,
TraceXYPlot::YAxisType::Capacitance,
TraceXYPlot::YAxisType::Inductance,
TraceXYPlot::YAxisType::QualityFactor,
TraceXYPlot::YAxisType::ImpulseReal,
TraceXYPlot::YAxisType::ImpulseMag,
TraceXYPlot::YAxisType::Step,
TraceXYPlot::YAxisType::Impedance};
TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
: TracePlot(model, parent)
{
@ -333,8 +317,11 @@ void TraceXYPlot::updateContextMenu()
bool TraceXYPlot::dropSupported(Trace *t)
{
Q_UNUSED(t)
// all kind of traces can be dropped, the graph will be reconfigured to support the dropped trace if required
if(domainMatch(t) && !supported(t)) {
// correct domain configured but Y axis do not match, prevent drop
return false;
}
// either directly compatible or domain change required
return true;
}
@ -457,7 +444,7 @@ void TraceXYPlot::draw(QPainter &p)
if(!t->isVisible()) {
continue;
}
pen = QPen(t->color(), 1);
pen = QPen(t->color(), pref.Graphs.lineWidth);
pen.setCosmetic(true);
if(i == 1) {
pen.setStyle(Qt::DotLine);
@ -705,6 +692,9 @@ void TraceXYPlot::updateAxisTicks()
double max = std::numeric_limits<double>::lowest();
double min = std::numeric_limits<double>::max();
for(auto t : tracesAxis[i]) {
if(!t->isVisible()) {
continue;
}
unsigned int samples = t->size();
for(unsigned int j=0;j<samples;j++) {
auto point = traceToCoordinate(t, j, YAxis[i].type);
@ -740,12 +730,16 @@ void TraceXYPlot::updateAxisTicks()
min -= range * 0.05;
max += range * 0.05;
}
} else {
// max/min still at default values, no valid samples are available for this axis, use default range
max = 1.0;
min = -1.0;
}
YAxis[i].rangeMin = min;
YAxis[i].rangeMax = max;
YAxis[i].rangeDiv = createAutomaticTicks(YAxis[i].ticks, min, max, 8);
}
}
}
}
QString TraceXYPlot::AxisTypeToName(TraceXYPlot::XAxisType type)
@ -816,12 +810,14 @@ QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type)
case YAxisType::Capacitance: return "Capacitance";
case YAxisType::Inductance: return "Inductance";
case YAxisType::QualityFactor: return "Quality Factor";
case YAxisType::GroupDelay: return "Group delay";
case YAxisType::ImpulseReal: return "Impulse Response (Real)";
case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)";
case YAxisType::Step: return "Step Response";
case YAxisType::Impedance: return "Impedance";
default: return "Unknown";
case YAxisType::Last: return "Unknown";
}
return "Missing case";
}
void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
@ -854,27 +850,26 @@ void TraceXYPlot::enableTraceAxis(Trace *t, int axis, bool enabled)
}
}
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
bool TraceXYPlot::domainMatch(Trace *t)
{
switch(XAxis.type) {
case XAxisType::Frequency:
if(t->outputType() != Trace::DataType::Frequency) {
return false;
}
break;
return t->outputType() == Trace::DataType::Frequency;
case XAxisType::Distance:
case XAxisType::Time:
if(t->outputType() != Trace::DataType::Time) {
return false;
}
break;
return t->outputType() == Trace::DataType::Time;
case XAxisType::Power:
if(t->outputType() != Trace::DataType::Power) {
return t->outputType() == Trace::DataType::Power;
case XAxisType::Last:
return false;
}
break;
default:
break;
return false;
}
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
{
if(!domainMatch(t)) {
return false;
}
switch(type) {
@ -890,6 +885,11 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
return false;
}
break;
case YAxisType::GroupDelay:
if(t->isReflection()) {
return false;
}
break;
default:
break;
}
@ -939,6 +939,38 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
case YAxisType::QualityFactor:
ret.setY(Util::SparamToQualityFactor(data.y));
break;
case YAxisType::GroupDelay: {
constexpr int requiredSamples = 5;
if(t->size() < requiredSamples) {
// unable to calculate
ret.setY(0.0);
break;
}
// needs at least some samples before/after current sample for calculating the derivative.
// For samples too far at either end of the trace, return group delay of "inner" trace sample instead
if(sample < requiredSamples / 2) {
return traceToCoordinate(t, requiredSamples / 2, type);
} else if(sample >= t->size() - requiredSamples / 2) {
return traceToCoordinate(t, t->size() - requiredSamples / 2 - 1, type);
} else {
// got enough samples at either end to calculate derivative.
// acquire phases of the required samples
std::vector<double> phases;
phases.reserve(requiredSamples);
for(unsigned int index = sample - requiredSamples / 2;index <= sample + requiredSamples / 2;index++) {
phases.push_back(arg(t->sample(index).y));
}
// make sure there are no phase jumps
Util::unwrapPhase(phases);
// calculate linearRegression to get derivative
double B_0, B_1;
Util::linearRegression(phases, B_0, B_1);
// B_1 now contains the derived phase vs. the sample. Scale by frequency to get group delay
double freq_step = t->sample(sample).x - t->sample(sample - 1).x;
ret.setY(-B_1 / (2.0*M_PI * freq_step));
}
}
break;
case YAxisType::ImpulseReal:
ret.setY(real(data.y));
break;
@ -1096,6 +1128,7 @@ QString TraceXYPlot::AxisUnit(TraceXYPlot::YAxisType type)
case TraceXYPlot::YAxisType::ImpulseMag: return "db";
case TraceXYPlot::YAxisType::Step: return "";
case TraceXYPlot::YAxisType::Impedance: return "Ohm";
case TraceXYPlot::YAxisType::GroupDelay: return "s";
default: return "";
}
}

View File

@ -26,6 +26,7 @@ public:
Capacitance,
Inductance,
QualityFactor,
GroupDelay,
// TDR options
ImpulseReal,
ImpulseMag,
@ -33,7 +34,7 @@ public:
Impedance,
Last,
};
static const std::set<YAxisType> YAxisTypes;
enum class XAxisType {
Frequency,
Time,
@ -80,6 +81,7 @@ private:
YAxisType YAxisTypeFromName(QString name);
XAxisMode AxisModeFromName(QString name);
void enableTraceAxis(Trace *t, int axis, bool enabled);
bool domainMatch(Trace *t);
bool supported(Trace *t) override;
bool supported(Trace *t, YAxisType type);
QPointF traceToCoordinate(Trace *t, unsigned int sample, YAxisType type);

View File

@ -151,7 +151,8 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
{
auto type = (TraceXYPlot::XAxisType) XAxisIndex;
auto supported = supportedYAxis(type);
for(auto t : TraceXYPlot::YAxisTypes) {
for(unsigned int i=0;i<(int) TraceXYPlot::YAxisType::Last;i++) {
auto t = (TraceXYPlot::YAxisType) i;
auto enable = supported.count(t) > 0;
auto index = (int) t;
enableComboBoxItem(ui->Y1type, index, enable);
@ -197,6 +198,7 @@ std::set<TraceXYPlot::YAxisType> XYplotAxisDialog::supportedYAxis(TraceXYPlot::X
ret.insert(TraceXYPlot::YAxisType::Capacitance);
ret.insert(TraceXYPlot::YAxisType::Inductance);
ret.insert(TraceXYPlot::YAxisType::QualityFactor);
ret.insert(TraceXYPlot::YAxisType::GroupDelay);
break;
case TraceXYPlot::XAxisType::Time:
case TraceXYPlot::XAxisType::Distance:

View File

@ -0,0 +1,26 @@
#include "util.h"
void Util::unwrapPhase(std::vector<double> &phase)
{
for (unsigned int i = 1; i < phase.size(); i++) {
double d = phase[i] - phase[i-1];
d = d > M_PI ? d - 2 * M_PI : (d < -M_PI ? d + 2 * M_PI : d);
phase[i] = phase[i-1] + d;
}
}
void Util::linearRegression(const std::vector<double> &input, double &B_0, double &B_1)
{
double x_mean = (input.size() - 1.0) / 2.0;
double y_mean = std::accumulate(input.begin(), input.end(), 0.0) / input.size();
double ss_xy = 0.0;
for(unsigned int i=0;i<input.size();i++) {
ss_xy += input[i] * i;
}
ss_xy -= input.size() * x_mean * y_mean;
int n = input.size() - 1;
double ss_xx = (1.0/6.0) * n * (n + 1) * (2*n + 1) - input.size() * x_mean * x_mean;
B_1 = ss_xy / ss_xx;
B_0 = y_mean - B_1 * x_mean;
}

View File

@ -4,6 +4,7 @@
#include <complex>
#include <math.h>
#include <limits>
#include <vector>
#include <QColor>
@ -55,6 +56,11 @@ namespace Util {
auto brightness = q.redF() * 0.299 + q.greenF() * 0.587 + q.blueF() * 0.114;
return brightness > 0.6 ? Qt::black : Qt::white;
}
void unwrapPhase(std::vector<double> &phase);
// input values are Y coordinates, assumes evenly spaced linear X values from 0 to input.size() - 1
void linearRegression(const std::vector<double> &input, double &B_0, double &B_1);
}
#endif // UTILH_H

View File

@ -34,6 +34,9 @@ void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
auto ui = new Ui_DeembeddingMeasurementDialog;
measurementUI = ui;
ui->setupUi(measurementDialog);
connect(measurementDialog, &QDialog::finished, [=](){
delete ui;
});
// add the trace selector
set<unsigned int> skip;
@ -106,7 +109,8 @@ void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
Deembedding::Deembedding(TraceModel &tm)
: tm(tm),
measuring(false)
measuring(false),
sweepPoints(0)
{
}

View File

@ -1,6 +1,8 @@
#include "matchingnetwork.h"
#include "ui_matchingnetworkdialog.h"
#include "unit.h"
#include "CustomWidgets/informationbox.h"
#include <QDialog>
#include <QHBoxLayout>
@ -11,6 +13,7 @@
#include <QMimeData>
#include <algorithm>
#include <QDebug>
#include <QFileDialog>
using namespace std;
@ -68,6 +71,9 @@ void MatchingNetwork::edit()
auto dialog = new QDialog();
auto ui = new Ui::MatchingNetworkDialog();
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
dialog->setModal(true);
graph = new QWidget();
@ -83,24 +89,25 @@ void MatchingNetwork::edit()
ui->lParallelC->installEventFilter(this);
ui->lParallelL->installEventFilter(this);
ui->lParallelR->installEventFilter(this);
ui->lDefinedThrough->installEventFilter(this);
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0);
layout->addStretch(1);
auto p1 = new QWidget();
p1->setMinimumSize(portWidth, 150);
p1->setMaximumSize(portWidth, 150);
p1->setMinimumSize(portWidth, 151);
p1->setMaximumSize(portWidth, 151);
p1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
p1->setStyleSheet("image: url(:/icons/port1.svg);");
p1->setStyleSheet("image: url(:/icons/port1.png);");
auto DUT = new QWidget();
DUT->setMinimumSize(DUTWidth, 150);
DUT->setMaximumSize(DUTWidth, 150);
DUT->setMinimumSize(DUTWidth, 151);
DUT->setMaximumSize(DUTWidth, 151);
DUT->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
DUT->setStyleSheet("image: url(:/icons/DUT.svg);");
DUT->setStyleSheet("image: url(:/icons/DUT.png);");
auto p2 = new QWidget();
p2->setMinimumSize(portWidth, 150);
p2->setMaximumSize(portWidth, 150);
p2->setMinimumSize(portWidth, 151);
p2->setMaximumSize(portWidth, 151);
p2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
p2->setStyleSheet("image: url(:/icons/port2.svg);");
p2->setStyleSheet("image: url(:/icons/port2.png);");
layout->addWidget(p1);
for(auto w : p1Network) {
layout->addWidget(w);
@ -136,27 +143,31 @@ void MatchingNetwork::edit()
nlohmann::json MatchingNetwork::toJSON()
{
nlohmann::json j;
for(int i=0;i<2;i++) {
auto network = i==0 ? p1Network : p2Network;
nlohmann::json jn;
for(auto c : network) {
nlohmann::json jn1, jn2;
for(auto c : p1Network) {
nlohmann::json jc;
jc["component"] = c->getName().toStdString();
jc["params"] = c->toJSON();
jn.push_back(jc);
jn1.push_back(jc);
}
j.push_back(jn);
for(auto c : p2Network) {
nlohmann::json jc;
jc["component"] = c->getName().toStdString();
jc["params"] = c->toJSON();
jn2.push_back(jc);
}
j["port1"] = jn1;
j["port2"] = jn2;
j["addNetwork"] = addNetwork;
return j;
}
void MatchingNetwork::fromJSON(nlohmann::json j)
{
for(int i=0;i<2;i++) {
auto jn = j[i];
auto &network = i==0 ? p1Network : p2Network;
network.clear();
for(auto jc : jn) {
p1Network.clear();
p2Network.clear();
if(j.contains("port1")) {
for(auto jc : j["port1"]) {
if(!jc.contains("component")) {
continue;
}
@ -165,9 +176,23 @@ void MatchingNetwork::fromJSON(nlohmann::json j)
continue;
}
c->fromJSON(jc["params"]);
network.push_back(c);
p1Network.push_back(c);
}
}
if(j.contains("port2")) {
for(auto jc : j["port2"]) {
if(!jc.contains("component")) {
continue;
}
auto c = MatchingComponent::createFromName(QString::fromStdString(jc["component"]));
if(!c) {
continue;
}
c->fromJSON(jc["params"]);
p2Network.push_back(c);
}
}
addNetwork = j.value("addNetwork", true);
matching.clear();
}
@ -329,8 +354,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
dropComponent = (MatchingComponent*) dropPtr;
dragEvent->acceptProposedAction();
insertIndicator = new QWidget();
insertIndicator->setMinimumSize(2, 150);
insertIndicator->setMaximumSize(2, 150);
insertIndicator->setMinimumSize(2, imageHeight);
insertIndicator->setMaximumSize(2, imageHeight);
insertIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
insertIndicator->setStyleSheet("background-color:red;");
updateInsertIndicator(dragEvent->pos().x());
@ -368,6 +393,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelL);
} else if(object->objectName() == "lParallelR") {
dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelR);
} else if(object->objectName() == "lDefinedThrough") {
dragComponent = new MatchingComponent(MatchingComponent::Type::DefinedThrough);
} else {
dragComponent = nullptr;
}
@ -397,18 +424,21 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
MatchingComponent::MatchingComponent(Type type)
{
this->type = type;
setMinimumSize(150, 150);
setMaximumSize(150, 150);
eValue = nullptr;
touchstone = nullptr;
touchstoneLabel = nullptr;
setMinimumSize(151, 151);
setMaximumSize(151, 151);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
eValue = new SIUnitEdit();
eValue->setPrecision(4);
eValue->setPrefixes("fpnum k");
connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged);
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
switch(type) {
case Type::SeriesR:
case Type::SeriesL:
case Type::SeriesC: {
eValue = new SIUnitEdit();
eValue->setPrecision(4);
eValue->setPrefixes("fpnum k");
connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged);
auto layout = new QVBoxLayout();
layout->addWidget(eValue);
setLayout(layout);
@ -417,13 +447,16 @@ MatchingComponent::MatchingComponent(Type type)
case Type::ParallelR:
case Type::ParallelL:
case Type::ParallelC: {
eValue = new SIUnitEdit();
eValue->setPrecision(4);
eValue->setPrefixes("fpnum k");
connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged);
auto layout = new QVBoxLayout();
layout->addWidget(eValue);
layout->addStretch(1);
layout->setContentsMargins(9, 5, 9, 9);
setLayout(layout);
}
break;
default:
break;
}
@ -431,38 +464,58 @@ MatchingComponent::MatchingComponent(Type type)
case Type::SeriesR:
eValue->setUnit("Ω");
eValue->setValue(50);
setStyleSheet("image: url(:/icons/seriesR.svg);");
setStyleSheet("image: url(:/icons/seriesR.png);");
break;
case Type::SeriesL:
eValue->setUnit("H");
eValue->setValue(1e-9);
setStyleSheet("image: url(:/icons/seriesL.svg);");
setStyleSheet("image: url(:/icons/seriesL.png);");
break;
case Type::SeriesC:
eValue->setUnit("F");
eValue->setValue(1e-12);
setStyleSheet("image: url(:/icons/seriesC.svg);");
setStyleSheet("image: url(:/icons/seriesC.png);");
break;
case Type::ParallelR:
eValue->setUnit("Ω");
eValue->setValue(50);
setStyleSheet("image: url(:/icons/parallelR.svg);");
setStyleSheet("image: url(:/icons/parallelR.png);");
break;
case Type::ParallelL:
eValue->setUnit("H");
eValue->setValue(1e-9);
setStyleSheet("image: url(:/icons/parallelL.svg);");
setStyleSheet("image: url(:/icons/parallelL.png);");
break;
case Type::ParallelC:
eValue->setUnit("F");
eValue->setValue(1e-12);
setStyleSheet("image: url(:/icons/parallelC.svg);");
setStyleSheet("image: url(:/icons/parallelC.png);");
break;
case Type::DefinedThrough: {
touchstone = new Touchstone(2);
touchstoneLabel = new QLabel();
touchstoneLabel->setWordWrap(true);
touchstoneLabel->setAlignment(Qt::AlignCenter);
auto layout = new QVBoxLayout();
layout->addWidget(touchstoneLabel);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
setStyleSheet("image: url(:/icons/definedThrough.png);");
updateTouchstoneLabel();
}
break;
default:
break;
}
}
MatchingComponent::~MatchingComponent()
{
delete eValue;
delete touchstone;
delete touchstoneLabel;
}
ABCDparam MatchingComponent::parameters(double freq)
{
switch(type) {
@ -478,6 +531,15 @@ ABCDparam MatchingComponent::parameters(double freq)
return ABCDparam(1.0, 0.0, 1.0/complex<double>(0, freq * 2 * M_PI * eValue->value()), 1.0);
case Type::ParallelC:
return ABCDparam(1.0, 0.0, 1.0/complex<double>(0, -1.0 / (freq * 2 * M_PI * eValue->value())), 1.0);
case Type::DefinedThrough:
if(touchstone->points() == 0 || freq < touchstone->minFreq() || freq > touchstone->maxFreq()) {
// outside of provided frequency range, pass through unchanged
return ABCDparam(1.0, 0.0, 0.0, 1.0);
} else {
auto d = touchstone->interpolate(freq);
auto S = Sparam(d.S[0], d.S[1], d.S[2], d.S[3]);
return ABCDparam(S, 50.0);
}
default:
return ABCDparam(1.0, 0.0, 0.0, 1.0);
}
@ -485,7 +547,9 @@ ABCDparam MatchingComponent::parameters(double freq)
void MatchingComponent::MatchingComponent::setValue(double v)
{
if(eValue) {
eValue->setValue(v);
}
}
MatchingComponent *MatchingComponent::createFromName(QString name)
@ -507,13 +571,73 @@ QString MatchingComponent::getName()
nlohmann::json MatchingComponent::toJSON()
{
nlohmann::json j;
switch(type) {
case Type::SeriesC:
case Type::SeriesR:
case Type::SeriesL:
case Type::ParallelC:
case Type::ParallelR:
case Type::ParallelL:
j["value"] = eValue->value();
break;
case Type::DefinedThrough:
j["touchstone"] = touchstone->toJSON();
break;
case Type::Last:
break;
}
return j;
}
void MatchingComponent::fromJSON(nlohmann::json j)
{
switch(type) {
case Type::SeriesC:
case Type::SeriesR:
case Type::SeriesL:
case Type::ParallelC:
case Type::ParallelR:
case Type::ParallelL:
eValue->setValue(j.value("value", 1e-12));
break;
case Type::DefinedThrough:
touchstone->fromJSON(j["touchstone"]);
updateTouchstoneLabel();
break;
case Type::Last:
break;
}
}
void MatchingComponent::mouseDoubleClickEvent(QMouseEvent *e)
{
Q_UNUSED(e);
if(type == Type::DefinedThrough) {
// select new touchstone file
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s2p)", nullptr, QFileDialog::DontUseNativeDialog);
if (!filename.isEmpty()) {
try {
*touchstone = Touchstone::fromFile(filename.toStdString());
} catch(const std::exception& e) {
InformationBox::ShowError("Failed to load file", QString("Attempt to load file ended with error: \"") + e.what()+"\"");
}
updateTouchstoneLabel();
}
}
}
void MatchingComponent::updateTouchstoneLabel()
{
if(!touchstone || !touchstoneLabel) {
return;
}
if(touchstone->points() == 0) {
touchstoneLabel->setText("No data. Double-click to select touchstone file");
} else {
QString text = QString::number(touchstone->points()) + " points from "+Unit::ToString(touchstone->minFreq(), "Hz", " kMG", 4)
+ " to "+Unit::ToString(touchstone->maxFreq(), "Hz", " kMG", 4);
touchstoneLabel->setText(text);
}
}
QString MatchingComponent::typeToName(MatchingComponent::Type type)
@ -525,6 +649,7 @@ QString MatchingComponent::typeToName(MatchingComponent::Type type)
case Type::ParallelR: return "ParallelR";
case Type::ParallelL: return "ParallelL";
case Type::ParallelC: return "ParallelC";
case Type::DefinedThrough: return "Touchstone Through";
default: return "";
}
}

View File

@ -5,8 +5,10 @@
#include "deembeddingoption.h"
#include "Tools/parameters.h"
#include "savable.h"
#include "touchstone.h"
#include <QWidget>
#include <QLabel>
#include <vector>
@ -21,11 +23,13 @@ public:
ParallelR,
ParallelL,
ParallelC,
DefinedThrough,
// Add new matching components here, do not explicitly assign values and keep the Last entry at the last position
Last,
};
MatchingComponent(Type type);
~MatchingComponent();
ABCDparam parameters(double freq);
void setValue(double v);
@ -40,7 +44,11 @@ signals:
void deleted(MatchingComponent* m);
protected:
SIUnitEdit *eValue;
Touchstone *touchstone;
QLabel *touchstoneLabel;
private:
void mouseDoubleClickEvent(QMouseEvent *e) override;
void updateTouchstoneLabel();
static QString typeToName(Type type);
Type type;
void keyPressEvent(QKeyEvent *event) override;
@ -62,9 +70,10 @@ public:
nlohmann::json toJSON() override;
void fromJSON(nlohmann::json j) override;
private:
static constexpr int componentWidth = 150;
static constexpr int DUTWidth = 150;
static constexpr int portWidth = 75;
static constexpr int imageHeight = 151;
static constexpr int componentWidth = 151;
static constexpr int DUTWidth = 151;
static constexpr int portWidth = 76;
MatchingComponent *componentAtPosition(int pos);
unsigned int findInsertPosition(int xcoord);
void addComponentAtPosition(int pos, MatchingComponent *c);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>772</width>
<height>442</height>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
@ -131,20 +131,8 @@
<property name="title">
<string>Matching Components</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_9">
<property name="font">
<font>
@ -159,9 +147,7 @@
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -219,7 +205,7 @@
</layout>
</widget>
</item>
<item row="0" column="1">
<item row="1" column="1">
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -277,7 +263,7 @@
</layout>
</widget>
</item>
<item row="0" column="2">
<item row="1" column="2">
<widget class="QFrame" name="frame_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -335,7 +321,65 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<item row="1" column="3">
<widget class="QFrame" name="frame_7">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>80</width>
<height>80</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lDefinedThrough">
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/definedThrough.svg);</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;br/&gt;&lt;br/&gt;Defined&lt;br/&gt;Through&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QFrame" name="frame_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -399,7 +443,7 @@
</layout>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QFrame" name="frame_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -463,7 +507,7 @@
</layout>
</widget>
</item>
<item row="1" column="2">
<item row="2" column="2">
<widget class="QFrame" name="frame_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -522,8 +566,6 @@
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>

View File

@ -81,6 +81,9 @@ void PortExtension::edit()
auto dialog = new QDialog();
ui = new Ui::PortExtensionEditDialog();
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
// set initial values
ui->P1Enabled->setChecked(port1.enabled);

View File

@ -126,6 +126,9 @@ void TwoThru::edit()
auto dialog = new QDialog();
ui = new Ui::TwoThruDialog();
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
ui->Z0->setUnit("Ω");
ui->Z0->setPrecision(4);
ui->Z0->setValue(Z0);

View File

@ -83,6 +83,9 @@ void TraceWidgetVNA::importDialog()
auto dialog = new QDialog();
auto ui = new Ui::s2pImportOptions;
ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
ui->applyCal->setEnabled(calAvailable);
ui->deembed->setEnabled(deembedAvailable);
bool applyCal = false;

View File

@ -53,6 +53,7 @@ VNA::VNA(AppWindow *window)
: Mode(window, "Vector Network Analyzer"),
SCPINode("VNA"),
deembedding(traceModel),
deembedding_active(false),
central(new TileWidget(traceModel))
{
averages = 1;
@ -60,6 +61,7 @@ VNA::VNA(AppWindow *window)
calMeasuring = false;
calDialog.reset();
calEdited = false;
settings.sweepType = SweepType::Frequency;
// Create default traces
auto tS11 = new Trace("S11", Qt::yellow);
@ -530,6 +532,12 @@ VNA::VNA(AppWindow *window)
// Set initial sweep settings
auto pref = Preferences::getInstance();
if(pref.Acquisition.useMedianAveraging) {
average.setMode(Averaging::Mode::Median);
} else {
average.setMode(Averaging::Mode::Mean);
}
if(pref.Startup.RememberSweepSettings) {
LoadSweepSettings();
} else {
@ -545,6 +553,8 @@ VNA::VNA(AppWindow *window)
SetPoints(pref.Startup.DefaultSweep.points);
if(pref.Startup.DefaultSweep.type == "Power Sweep") {
SetSweepType(SweepType::Power);
} else {
SetSweepType(SweepType::Frequency);
}
}
@ -760,6 +770,11 @@ using namespace std;
void VNA::NewDatapoint(Protocol::Datapoint d)
{
if(d.pointNum >= settings.npoints) {
qWarning() << "Ignoring point with too large point number (" << d.pointNum << ")";
return;
}
d = average.process(d);
if(calMeasuring) {
if(average.currentSweep() == averages) {
@ -1461,6 +1476,11 @@ void VNA::updateGraphColors()
emit graphColorsChanged();
}
void VNA::setAveragingMode(Averaging::Mode mode)
{
average.setMode(mode);
}
QString VNA::SweepTypeToString(VNA::SweepType sw)
{
switch(sw) {

View File

@ -29,6 +29,7 @@ public:
virtual void fromJSON(nlohmann::json j) override;
void updateGraphColors();
void setAveragingMode(Averaging::Mode mode);
enum class SweepType {
Frequency = 0,

View File

@ -78,6 +78,8 @@ AppWindow::AppWindow(QWidget *parent)
// qDebug().setVerbosity(0);
qDebug() << "Application start";
this->setWindowIcon(QIcon(":/app/logo.png"));
parser.setApplicationDescription(qlibrevnaApp->applicationName());
parser.addHelpOption();
parser.addVersionOption();
@ -202,6 +204,14 @@ AppWindow::AppWindow(QWidget *parent)
vna->updateGraphColors();
}
// averaging mode may have changed, update for all relevant modes
if(p.Acquisition.useMedianAveraging) {
spectrumAnalyzer->setAveragingMode(Averaging::Mode::Median);
vna->setAveragingMode(Averaging::Mode::Median);
} else {
spectrumAnalyzer->setAveragingMode(Averaging::Mode::Mean);
vna->setAveragingMode(Averaging::Mode::Mean);
}
});
connect(ui->actionAbout, &QAction::triggered, [=](){

View File

@ -5,6 +5,7 @@ using namespace std;
Averaging::Averaging()
{
averages = 1;
mode = Mode::Mean;
}
void Averaging::reset(unsigned int points)
@ -45,6 +46,8 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
deque->pop_front();
}
switch(mode) {
case Mode::Mean: {
// calculate average
complex<double> sum[4];
for(auto s : *deque) {
@ -58,6 +61,43 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
S21 = sum[2] / (double) (deque->size());
S22 = sum[3] / (double) (deque->size());
}
break;
case Mode::Median: {
auto size = deque->size();
// create sorted arrays
std::vector<complex<double>> S11sorted, S12sorted, S21sorted, S22sorted;
S11sorted.reserve(size);
S12sorted.reserve(size);
S21sorted.reserve(size);
S22sorted.reserve(size);
auto comp = [=](const complex<double>&a, const complex<double>&b){
return abs(a) < abs(b);
};
for(auto d : *deque) {
S11sorted.insert(upper_bound(S11sorted.begin(), S11sorted.end(), d[0], comp), d[0]);
S12sorted.insert(upper_bound(S12sorted.begin(), S12sorted.end(), d[1], comp), d[1]);
S21sorted.insert(upper_bound(S21sorted.begin(), S21sorted.end(), d[2], comp), d[2]);
S22sorted.insert(upper_bound(S22sorted.begin(), S22sorted.end(), d[3], comp), d[3]);
}
if(size & 0x01) {
// odd number of samples
S11 = S11sorted[size / 2];
S12 = S12sorted[size / 2];
S21 = S21sorted[size / 2];
S22 = S22sorted[size / 2];
} else {
// even number, use average of middle samples
S11 = (S11sorted[size / 2 - 1] + S11sorted[size / 2]) / 2.0;
S12 = (S12sorted[size / 2 - 1] + S12sorted[size / 2]) / 2.0;
S21 = (S21sorted[size / 2 - 1] + S21sorted[size / 2]) / 2.0;
S22 = (S22sorted[size / 2 - 1] + S22sorted[size / 2]) / 2.0;
}
}
break;
}
}
d.real_S11 = S11.real();
d.imag_S11 = S11.imag();
@ -90,17 +130,41 @@ Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerRe
deque->pop_front();
}
switch(mode) {
case Mode::Mean: {
// calculate average
complex<double> sum[4];
complex<double> sum[2];
for(auto s : *deque) {
sum[0] += s[0];
sum[1] += s[1];
sum[2] += s[2];
sum[3] += s[3];
}
d.port1 = abs(sum[0] / (double) (deque->size()));
d.port2 = abs(sum[1] / (double) (deque->size()));
}
break;
case Mode::Median: {
auto size = deque->size();
// create sorted arrays
std::vector<double> port1, port2;
port1.reserve(size);
port2.reserve(size);
for(auto d : *deque) {
port1.insert(upper_bound(port1.begin(), port1.end(), abs(d[0])), abs(d[0]));
port2.insert(upper_bound(port2.begin(), port2.end(), abs(d[0])), abs(d[0]));
}
if(size & 0x01) {
// odd number of samples
d.port1 = port1[size / 2];
d.port2 = port1[size / 2];
} else {
// even number, use average of middle samples
d.port1 = (port1[size / 2 - 1] + port1[size / 2]) / 2;
d.port2 = (port2[size / 2 - 1] + port2[size / 2]) / 2;
}
}
break;
}
}
return d;
}
@ -122,3 +186,13 @@ unsigned int Averaging::currentSweep()
return 0;
}
}
Averaging::Mode Averaging::getMode() const
{
return mode;
}
void Averaging::setMode(const Mode &value)
{
mode = value;
}

View File

@ -10,6 +10,11 @@
class Averaging
{
public:
enum class Mode {
Mean,
Median
};
Averaging();
void reset(unsigned int points);
void setAverages(unsigned int a);
@ -21,10 +26,14 @@ public:
// Returns the number of the currently active sweep. Value is incremented whenever the the first point of the sweep is added
// Returned values are in range 0 (when no data has been added yet) to averages
unsigned int currentSweep();
Mode getMode() const;
void setMode(const Mode &value);
private:
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
int maxPoints;
unsigned int averages;
Mode mode;
};
#endif // AVERAGING_H

View File

@ -50,5 +50,13 @@
<file>icons/down.png</file>
<file>icons/up.png</file>
<file>icons/chainlink.png</file>
<file>icons/definedThrough.svg</file>
<file>icons/seriesR.png</file>
<file>icons/seriesL.png</file>
<file>icons/seriesC.png</file>
<file>icons/parallelR.png</file>
<file>icons/parallelL.png</file>
<file>icons/parallelC.png</file>
<file>icons/definedThrough.png</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 884 B

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="113.386pt" height="113.386pt" viewBox="0 0 113.386 113.386" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 1.90625 -0.53125 C 1.90625 -0.8125 1.671875 -1.0625 1.390625 -1.0625 C 1.09375 -1.0625 0.859375 -0.8125 0.859375 -0.53125 C 0.859375 -0.234375 1.09375 0 1.390625 0 C 1.671875 0 1.90625 -0.234375 1.90625 -0.53125 Z M 1.90625 -0.53125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 3.890625 -3.734375 C 3.625 -3.71875 3.421875 -3.5 3.421875 -3.28125 C 3.421875 -3.140625 3.515625 -2.984375 3.734375 -2.984375 C 3.953125 -2.984375 4.1875 -3.15625 4.1875 -3.546875 C 4.1875 -4 3.765625 -4.40625 3 -4.40625 C 1.6875 -4.40625 1.3125 -3.390625 1.3125 -2.953125 C 1.3125 -2.171875 2.046875 -2.03125 2.34375 -1.96875 C 2.859375 -1.859375 3.375 -1.75 3.375 -1.203125 C 3.375 -0.953125 3.15625 -0.109375 1.953125 -0.109375 C 1.8125 -0.109375 1.046875 -0.109375 0.8125 -0.640625 C 1.203125 -0.59375 1.453125 -0.890625 1.453125 -1.171875 C 1.453125 -1.390625 1.28125 -1.515625 1.078125 -1.515625 C 0.8125 -1.515625 0.515625 -1.3125 0.515625 -0.859375 C 0.515625 -0.296875 1.09375 0.109375 1.9375 0.109375 C 3.5625 0.109375 3.953125 -1.09375 3.953125 -1.546875 C 3.953125 -1.90625 3.765625 -2.15625 3.640625 -2.265625 C 3.375 -2.546875 3.078125 -2.609375 2.640625 -2.6875 C 2.28125 -2.765625 1.890625 -2.84375 1.890625 -3.296875 C 1.890625 -3.578125 2.125 -4.1875 3 -4.1875 C 3.25 -4.1875 3.75 -4.109375 3.890625 -3.734375 Z M 3.890625 -3.734375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 0.453125 1.21875 C 0.375 1.5625 0.34375 1.625 -0.09375 1.625 C -0.203125 1.625 -0.3125 1.625 -0.3125 1.8125 C -0.3125 1.890625 -0.265625 1.9375 -0.1875 1.9375 C 0.078125 1.9375 0.375 1.90625 0.640625 1.90625 C 0.984375 1.90625 1.3125 1.9375 1.640625 1.9375 C 1.6875 1.9375 1.8125 1.9375 1.8125 1.734375 C 1.8125 1.625 1.71875 1.625 1.578125 1.625 C 1.078125 1.625 1.078125 1.5625 1.078125 1.46875 C 1.078125 1.34375 1.5 -0.28125 1.5625 -0.53125 C 1.6875 -0.234375 1.96875 0.109375 2.484375 0.109375 C 3.640625 0.109375 4.890625 -1.34375 4.890625 -2.8125 C 4.890625 -3.75 4.3125 -4.40625 3.5625 -4.40625 C 3.0625 -4.40625 2.578125 -4.046875 2.25 -3.65625 C 2.15625 -4.203125 1.71875 -4.40625 1.359375 -4.40625 C 0.890625 -4.40625 0.703125 -4.015625 0.625 -3.84375 C 0.4375 -3.5 0.3125 -2.90625 0.3125 -2.875 C 0.3125 -2.765625 0.40625 -2.765625 0.421875 -2.765625 C 0.53125 -2.765625 0.53125 -2.78125 0.59375 -3 C 0.765625 -3.703125 0.96875 -4.1875 1.328125 -4.1875 C 1.5 -4.1875 1.640625 -4.109375 1.640625 -3.734375 C 1.640625 -3.5 1.609375 -3.390625 1.5625 -3.21875 Z M 2.203125 -3.109375 C 2.265625 -3.375 2.546875 -3.65625 2.71875 -3.8125 C 3.078125 -4.109375 3.359375 -4.1875 3.53125 -4.1875 C 3.921875 -4.1875 4.171875 -3.84375 4.171875 -3.25 C 4.171875 -2.65625 3.84375 -1.515625 3.65625 -1.140625 C 3.3125 -0.4375 2.84375 -0.109375 2.46875 -0.109375 C 1.8125 -0.109375 1.6875 -0.9375 1.6875 -1 C 1.6875 -1.015625 1.6875 -1.03125 1.71875 -1.15625 Z M 2.203125 -3.109375 "/>
</symbol>
<symbol overflow="visible" id="glyph1-0">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph1-1">
<path style="stroke:none;" d="M 1.265625 -0.765625 L 2.328125 -1.796875 C 3.875 -3.171875 4.46875 -3.703125 4.46875 -4.703125 C 4.46875 -5.84375 3.578125 -6.640625 2.359375 -6.640625 C 1.234375 -6.640625 0.5 -5.71875 0.5 -4.828125 C 0.5 -4.28125 1 -4.28125 1.03125 -4.28125 C 1.203125 -4.28125 1.546875 -4.390625 1.546875 -4.8125 C 1.546875 -5.0625 1.359375 -5.328125 1.015625 -5.328125 C 0.9375 -5.328125 0.921875 -5.328125 0.890625 -5.3125 C 1.109375 -5.96875 1.65625 -6.328125 2.234375 -6.328125 C 3.140625 -6.328125 3.5625 -5.515625 3.5625 -4.703125 C 3.5625 -3.90625 3.078125 -3.125 2.515625 -2.5 L 0.609375 -0.375 C 0.5 -0.265625 0.5 -0.234375 0.5 0 L 4.203125 0 L 4.46875 -1.734375 L 4.234375 -1.734375 C 4.171875 -1.4375 4.109375 -1 4 -0.84375 C 3.9375 -0.765625 3.28125 -0.765625 3.0625 -0.765625 Z M 1.265625 -0.765625 "/>
</symbol>
</g>
<clipPath id="clip1">
<path d="M 0 28 L 113.386719 28 L 113.386719 29 L 0 29 Z M 0 28 "/>
</clipPath>
</defs>
<g id="surface1">
<g clip-path="url(#clip1)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 0 85.04225 L 40.820312 85.04225 M 72.566406 85.04225 L 113.386719 85.04225 " transform="matrix(1,0,0,-1,0,113.386)"/>
</g>
<path style="fill:none;stroke-width:0.797;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 40.820312 90.991469 L 72.566406 90.991469 L 72.566406 79.089125 L 40.820312 79.089125 Z M 40.820312 90.991469 " transform="matrix(1,0,0,-1,0,113.386)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="47.977" y="30.588"/>
<use xlink:href="#glyph0-2" x="50.74461" y="30.588"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph1-1" x="55.415" y="30.588"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-3" x="60.396" y="30.588"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,6 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="56.693pt" height="113.386pt" viewBox="0 0 56.693 113.386" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 2.265625 -3.15625 L 3.953125 -3.15625 C 5.140625 -3.15625 6.21875 -3.953125 6.21875 -4.953125 C 6.21875 -5.9375 5.234375 -6.8125 3.875 -6.8125 L 0.34375 -6.8125 L 0.34375 -6.5 L 0.59375 -6.5 C 1.359375 -6.5 1.375 -6.390625 1.375 -6.03125 L 1.375 -0.78125 C 1.375 -0.421875 1.359375 -0.3125 0.59375 -0.3125 L 0.34375 -0.3125 L 0.34375 0 C 0.703125 -0.03125 1.4375 -0.03125 1.8125 -0.03125 C 2.1875 -0.03125 2.9375 -0.03125 3.296875 0 L 3.296875 -0.3125 L 3.046875 -0.3125 C 2.28125 -0.3125 2.265625 -0.421875 2.265625 -0.78125 Z M 2.234375 -3.40625 L 2.234375 -6.09375 C 2.234375 -6.4375 2.25 -6.5 2.71875 -6.5 L 3.609375 -6.5 C 5.1875 -6.5 5.1875 -5.4375 5.1875 -4.953125 C 5.1875 -4.484375 5.1875 -3.40625 3.609375 -3.40625 Z M 2.234375 -3.40625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 4.6875 -2.140625 C 4.6875 -3.40625 3.703125 -4.46875 2.5 -4.46875 C 1.25 -4.46875 0.28125 -3.375 0.28125 -2.140625 C 0.28125 -0.84375 1.3125 0.109375 2.484375 0.109375 C 3.6875 0.109375 4.6875 -0.875 4.6875 -2.140625 Z M 2.5 -0.140625 C 2.0625 -0.140625 1.625 -0.34375 1.359375 -0.8125 C 1.109375 -1.25 1.109375 -1.859375 1.109375 -2.21875 C 1.109375 -2.609375 1.109375 -3.140625 1.34375 -3.578125 C 1.609375 -4.03125 2.078125 -4.25 2.484375 -4.25 C 2.921875 -4.25 3.34375 -4.03125 3.609375 -3.59375 C 3.875 -3.171875 3.875 -2.59375 3.875 -2.21875 C 3.875 -1.859375 3.875 -1.3125 3.65625 -0.875 C 3.421875 -0.421875 2.984375 -0.140625 2.5 -0.140625 Z M 2.5 -0.140625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 1.671875 -3.3125 L 1.671875 -4.40625 L 0.28125 -4.296875 L 0.28125 -3.984375 C 0.984375 -3.984375 1.0625 -3.921875 1.0625 -3.421875 L 1.0625 -0.75 C 1.0625 -0.3125 0.953125 -0.3125 0.28125 -0.3125 L 0.28125 0 C 0.671875 -0.015625 1.140625 -0.03125 1.421875 -0.03125 C 1.8125 -0.03125 2.28125 -0.03125 2.6875 0 L 2.6875 -0.3125 L 2.46875 -0.3125 C 1.734375 -0.3125 1.71875 -0.421875 1.71875 -0.78125 L 1.71875 -2.3125 C 1.71875 -3.296875 2.140625 -4.1875 2.890625 -4.1875 C 2.953125 -4.1875 2.984375 -4.1875 3 -4.171875 C 2.96875 -4.171875 2.765625 -4.046875 2.765625 -3.78125 C 2.765625 -3.515625 2.984375 -3.359375 3.203125 -3.359375 C 3.375 -3.359375 3.625 -3.484375 3.625 -3.796875 C 3.625 -4.109375 3.3125 -4.40625 2.890625 -4.40625 C 2.15625 -4.40625 1.796875 -3.734375 1.671875 -3.3125 Z M 1.671875 -3.3125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 1.71875 -3.984375 L 3.15625 -3.984375 L 3.15625 -4.296875 L 1.71875 -4.296875 L 1.71875 -6.125 L 1.46875 -6.125 C 1.46875 -5.3125 1.171875 -4.25 0.1875 -4.203125 L 0.1875 -3.984375 L 1.03125 -3.984375 L 1.03125 -1.234375 C 1.03125 -0.015625 1.96875 0.109375 2.328125 0.109375 C 3.03125 0.109375 3.3125 -0.59375 3.3125 -1.234375 L 3.3125 -1.796875 L 3.0625 -1.796875 L 3.0625 -1.25 C 3.0625 -0.515625 2.765625 -0.140625 2.390625 -0.140625 C 1.71875 -0.140625 1.71875 -1.046875 1.71875 -1.21875 Z M 1.71875 -3.984375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-5">
<path style="stroke:none;" d="M 2.9375 -6.375 C 2.9375 -6.625 2.9375 -6.640625 2.703125 -6.640625 C 2.078125 -6 1.203125 -6 0.890625 -6 L 0.890625 -5.6875 C 1.09375 -5.6875 1.671875 -5.6875 2.1875 -5.953125 L 2.1875 -0.78125 C 2.1875 -0.421875 2.15625 -0.3125 1.265625 -0.3125 L 0.953125 -0.3125 L 0.953125 0 C 1.296875 -0.03125 2.15625 -0.03125 2.5625 -0.03125 C 2.953125 -0.03125 3.828125 -0.03125 4.171875 0 L 4.171875 -0.3125 L 3.859375 -0.3125 C 2.953125 -0.3125 2.9375 -0.421875 2.9375 -0.78125 Z M 2.9375 -6.375 "/>
</symbol>
</g>
<clipPath id="clip1">
<path d="M 28 28 L 56.691406 28 L 56.691406 29 L 28 29 Z M 28 28 "/>
</clipPath>
@ -12,6 +32,17 @@
<g clip-path="url(#clip1)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.347656 85.04225 L 56.695312 85.04225 " transform="matrix(1,0,0,-1,0,113.386)"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="10.597" y="20.975"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-2" x="17.098593" y="20.975"/>
<use xlink:href="#glyph0-3" x="22.079893" y="20.975"/>
<use xlink:href="#glyph0-4" x="25.982243" y="20.975"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-5" x="33.174244" y="20.975"/>
</g>
<g clip-path="url(#clip2)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.797;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.347656 85.04225 C 28.347656 87.233656 26.570312 89.007094 24.378906 89.007094 C 22.1875 89.007094 20.410156 87.233656 20.410156 85.04225 C 20.410156 82.850844 22.1875 81.0735 24.378906 81.0735 C 26.570312 81.0735 28.347656 82.850844 28.347656 85.04225 Z M 28.347656 85.04225 " transform="matrix(1,0,0,-1,0,113.386)"/>
</g>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -1,12 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="56.693pt" height="113.386pt" viewBox="0 0 56.693 113.386" version="1.1">
<defs>
<g>
<symbol overflow="visible" id="glyph0-0">
<path style="stroke:none;" d=""/>
</symbol>
<symbol overflow="visible" id="glyph0-1">
<path style="stroke:none;" d="M 2.265625 -3.15625 L 3.953125 -3.15625 C 5.140625 -3.15625 6.21875 -3.953125 6.21875 -4.953125 C 6.21875 -5.9375 5.234375 -6.8125 3.875 -6.8125 L 0.34375 -6.8125 L 0.34375 -6.5 L 0.59375 -6.5 C 1.359375 -6.5 1.375 -6.390625 1.375 -6.03125 L 1.375 -0.78125 C 1.375 -0.421875 1.359375 -0.3125 0.59375 -0.3125 L 0.34375 -0.3125 L 0.34375 0 C 0.703125 -0.03125 1.4375 -0.03125 1.8125 -0.03125 C 2.1875 -0.03125 2.9375 -0.03125 3.296875 0 L 3.296875 -0.3125 L 3.046875 -0.3125 C 2.28125 -0.3125 2.265625 -0.421875 2.265625 -0.78125 Z M 2.234375 -3.40625 L 2.234375 -6.09375 C 2.234375 -6.4375 2.25 -6.5 2.71875 -6.5 L 3.609375 -6.5 C 5.1875 -6.5 5.1875 -5.4375 5.1875 -4.953125 C 5.1875 -4.484375 5.1875 -3.40625 3.609375 -3.40625 Z M 2.234375 -3.40625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-2">
<path style="stroke:none;" d="M 4.6875 -2.140625 C 4.6875 -3.40625 3.703125 -4.46875 2.5 -4.46875 C 1.25 -4.46875 0.28125 -3.375 0.28125 -2.140625 C 0.28125 -0.84375 1.3125 0.109375 2.484375 0.109375 C 3.6875 0.109375 4.6875 -0.875 4.6875 -2.140625 Z M 2.5 -0.140625 C 2.0625 -0.140625 1.625 -0.34375 1.359375 -0.8125 C 1.109375 -1.25 1.109375 -1.859375 1.109375 -2.21875 C 1.109375 -2.609375 1.109375 -3.140625 1.34375 -3.578125 C 1.609375 -4.03125 2.078125 -4.25 2.484375 -4.25 C 2.921875 -4.25 3.34375 -4.03125 3.609375 -3.59375 C 3.875 -3.171875 3.875 -2.59375 3.875 -2.21875 C 3.875 -1.859375 3.875 -1.3125 3.65625 -0.875 C 3.421875 -0.421875 2.984375 -0.140625 2.5 -0.140625 Z M 2.5 -0.140625 "/>
</symbol>
<symbol overflow="visible" id="glyph0-3">
<path style="stroke:none;" d="M 1.671875 -3.3125 L 1.671875 -4.40625 L 0.28125 -4.296875 L 0.28125 -3.984375 C 0.984375 -3.984375 1.0625 -3.921875 1.0625 -3.421875 L 1.0625 -0.75 C 1.0625 -0.3125 0.953125 -0.3125 0.28125 -0.3125 L 0.28125 0 C 0.671875 -0.015625 1.140625 -0.03125 1.421875 -0.03125 C 1.8125 -0.03125 2.28125 -0.03125 2.6875 0 L 2.6875 -0.3125 L 2.46875 -0.3125 C 1.734375 -0.3125 1.71875 -0.421875 1.71875 -0.78125 L 1.71875 -2.3125 C 1.71875 -3.296875 2.140625 -4.1875 2.890625 -4.1875 C 2.953125 -4.1875 2.984375 -4.1875 3 -4.171875 C 2.96875 -4.171875 2.765625 -4.046875 2.765625 -3.78125 C 2.765625 -3.515625 2.984375 -3.359375 3.203125 -3.359375 C 3.375 -3.359375 3.625 -3.484375 3.625 -3.796875 C 3.625 -4.109375 3.3125 -4.40625 2.890625 -4.40625 C 2.15625 -4.40625 1.796875 -3.734375 1.671875 -3.3125 Z M 1.671875 -3.3125 "/>
</symbol>
<symbol overflow="visible" id="glyph0-4">
<path style="stroke:none;" d="M 1.71875 -3.984375 L 3.15625 -3.984375 L 3.15625 -4.296875 L 1.71875 -4.296875 L 1.71875 -6.125 L 1.46875 -6.125 C 1.46875 -5.3125 1.171875 -4.25 0.1875 -4.203125 L 0.1875 -3.984375 L 1.03125 -3.984375 L 1.03125 -1.234375 C 1.03125 -0.015625 1.96875 0.109375 2.328125 0.109375 C 3.03125 0.109375 3.3125 -0.59375 3.3125 -1.234375 L 3.3125 -1.796875 L 3.0625 -1.796875 L 3.0625 -1.25 C 3.0625 -0.515625 2.765625 -0.140625 2.390625 -0.140625 C 1.71875 -0.140625 1.71875 -1.046875 1.71875 -1.21875 Z M 1.71875 -3.984375 "/>
</symbol>
<symbol overflow="visible" id="glyph0-5">
<path style="stroke:none;" d="M 1.265625 -0.765625 L 2.328125 -1.796875 C 3.875 -3.171875 4.46875 -3.703125 4.46875 -4.703125 C 4.46875 -5.84375 3.578125 -6.640625 2.359375 -6.640625 C 1.234375 -6.640625 0.5 -5.71875 0.5 -4.828125 C 0.5 -4.28125 1 -4.28125 1.03125 -4.28125 C 1.203125 -4.28125 1.546875 -4.390625 1.546875 -4.8125 C 1.546875 -5.0625 1.359375 -5.328125 1.015625 -5.328125 C 0.9375 -5.328125 0.921875 -5.328125 0.890625 -5.3125 C 1.109375 -5.96875 1.65625 -6.328125 2.234375 -6.328125 C 3.140625 -6.328125 3.5625 -5.515625 3.5625 -4.703125 C 3.5625 -3.90625 3.078125 -3.125 2.515625 -2.5 L 0.609375 -0.375 C 0.5 -0.265625 0.5 -0.234375 0.5 0 L 4.203125 0 L 4.46875 -1.734375 L 4.234375 -1.734375 C 4.171875 -1.4375 4.109375 -1 4 -0.84375 C 3.9375 -0.765625 3.28125 -0.765625 3.0625 -0.765625 Z M 1.265625 -0.765625 "/>
</symbol>
</g>
<clipPath id="clip1">
<path d="M 24.378906 20.410156 L 40.25 20.410156 L 40.25 26.757812 L 24.378906 26.757812 Z M 33.902344 26.757812 L 40.25 26.757812 L 40.25 29.933594 L 33.902344 29.933594 Z M 24.378906 29.933594 L 40.25 29.933594 L 40.25 36.28125 L 24.378906 36.28125 Z M 24.378906 29.933594 "/>
</clipPath>
</defs>
<g id="surface1">
<path style="fill:none;stroke-width:0.3985;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.347656 85.04225 L 0 85.04225 " transform="matrix(1,0,0,-1,0,113.386)"/>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-1" x="18.533" y="20.975"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-2" x="25.034593" y="20.975"/>
<use xlink:href="#glyph0-3" x="30.015893" y="20.975"/>
<use xlink:href="#glyph0-4" x="33.918243" y="20.975"/>
</g>
<g style="fill:rgb(0%,0%,0%);fill-opacity:1;">
<use xlink:href="#glyph0-5" x="41.120207" y="20.975"/>
</g>
<g clip-path="url(#clip1)" clip-rule="nonzero">
<path style="fill:none;stroke-width:0.797;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 28.347656 85.04225 C 28.347656 87.233656 30.125 89.007094 32.316406 89.007094 C 34.507812 89.007094 36.28125 87.233656 36.28125 85.04225 C 36.28125 82.850844 34.507812 81.0735 32.316406 81.0735 C 30.125 81.0735 28.347656 82.850844 28.347656 85.04225 Z M 28.347656 85.04225 " transform="matrix(1,0,0,-1,0,113.386)"/>
</g>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 808 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

View File

@ -134,6 +134,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->Acquisition.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked();
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
p->Graphs.Color.axis = ui->GraphsColorAxis->getColor();
p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked();
@ -142,6 +143,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
p->Graphs.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex();
p->Graphs.markerBehavior.showDataOnGraphs = ui->GraphsShowMarkerData->isChecked();
p->Graphs.markerBehavior.showAllData = ui->GraphsShowAllMarkerData->isChecked();
p->Graphs.lineWidth = ui->GraphsLineWidth->value();
p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked();
p->SCPIServer.port = ui->SCPIServerPort->value();
accept();
@ -199,6 +201,7 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionUseHarmonic->setChecked(p->Acquisition.harmonicMixing);
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0);
ui->GraphsColorBackground->setColor(p->Graphs.Color.background);
ui->GraphsColorAxis->setColor(p->Graphs.Color.axis);
@ -208,6 +211,8 @@ void PreferencesDialog::setInitialGUIState()
ui->GraphsDomainChangeBehavior->setCurrentIndex((int) p->Graphs.domainChangeBehavior);
ui->GraphsShowMarkerData->setChecked(p->Graphs.markerBehavior.showDataOnGraphs);
ui->GraphsShowAllMarkerData->setChecked(p->Graphs.markerBehavior.showAllData);
ui->GraphsLineWidth->setValue(p->Graphs.lineWidth);
ui->SCPIServerEnabled->setChecked(p->SCPIServer.enabled);
ui->SCPIServerPort->setValue(p->SCPIServer.port);

View File

@ -66,6 +66,7 @@ public:
bool harmonicMixing;
bool useDFTinSAmode;
double RBWLimitForDFT;
bool useMedianAveraging;
} Acquisition;
struct {
struct {
@ -84,6 +85,7 @@ public:
bool showDataOnGraphs;
bool showAllData;
} markerBehavior;
double lineWidth;
} Graphs;
struct {
bool enabled;
@ -100,7 +102,7 @@ private:
QString name;
QVariant def;
};
const std::array<SettingDescription, 37> descr = {{
const std::array<SettingDescription, 39> descr = {{
{&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true},
{&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false},
{&Startup.DefaultSweep.type, "Startup.DefaultSweep.type", "Frequency"},
@ -128,6 +130,7 @@ private:
{&Acquisition.harmonicMixing, "Acquisition.harmonicMixing", false},
{&Acquisition.useDFTinSAmode, "Acquisition.useDFTinSAmode", true},
{&Acquisition.RBWLimitForDFT, "Acquisition.RBWLimitForDFT", 3000.0},
{&Acquisition.useMedianAveraging, "Acquisition.useMedianAveraging", false},
{&Graphs.Color.background, "Graphs.Color.background", QColor(Qt::black)},
{&Graphs.Color.axis, "Graphs.Color.axis", QColor(Qt::white)},
{&Graphs.Color.Ticks.Background.enabled, "Graphs.Color.Ticks.Background.enabled", true},
@ -136,6 +139,7 @@ private:
{&Graphs.domainChangeBehavior, "Graphs.domainChangeBehavior", GraphDomainChangeBehavior::AdjustGraphs},
{&Graphs.markerBehavior.showDataOnGraphs, "Graphs.markerBehavior.ShowDataOnGraphs", true},
{&Graphs.markerBehavior.showAllData, "Graphs.markerBehavior.ShowAllData", false},
{&Graphs.lineWidth, "Graphs.lineWidth", 1.0},
{&SCPIServer.enabled, "SCPIServer.enabled", true},
{&SCPIServer.port, "SCPIServer.port", 19542},
}};

View File

@ -637,6 +637,36 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="title">
<string>Common</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Averaging mode:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="AcquisitionAveragingMode">
<item>
<property name="text">
<string>Mean</string>
</property>
</item>
<item>
<property name="text">
<string>Median</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -739,6 +769,20 @@
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Line Width:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="GraphsLineWidth">
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Name=LibreVNA GUI
Comment=Vector Network Analyzer for LibreVNA board
Exec=/opt/LibreVNA-GUI
Terminal=false
Type=Application
StartupNotify=true
Categories=Science;Engineering;Electronics
Icon=librevna.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/app">
<file alias="logo.png">librevna.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="59.531mm" height="59.531mm" version="1.1" viewBox="0 0 59.531 59.531" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g transform="translate(-86.897 77.607)">
<g stroke="#000" stroke-linecap="round" stroke-width="1.2965">
<path d="m116.66-18.917a29.116 29.021 0 0 1-29.116-28.895 29.116 29.021 0 0 1 28.863-29.146 29.116 29.021 0 0 1 29.367 28.642 29.116 29.021 0 0 1-28.608 29.395" fill="#a38300" stop-color="#000000" style="paint-order:stroke fill markers"/>
<path d="m99.476-57.91a23.403 23.48 90 0 1 33.096-1.5564 23.403 23.48 90 0 1 1.7052 32.981 23.403 23.48 90 0 1-33.081 1.8428 23.403 23.48 90 0 1-1.9924-32.965" fill="#cca300" fill-opacity=".87843" stop-color="#000000" stroke-opacity=".95332" style="paint-order:stroke fill markers"/>
<path d="m116.83-19.035a16.741 16.687 0 0 1-16.741-16.614 16.741 16.687 0 0 1 16.596-16.759 16.741 16.687 0 0 1 16.885 16.469 16.741 16.687 0 0 1-16.449 16.902" fill="#a38300" stop-color="#000000" stroke-opacity=".76863" style="paint-order:stroke fill markers"/>
<path d="m116.81-19.039a10.405 10.371 0 0 1-10.405-10.326 10.405 10.371 0 0 1 10.315-10.416 10.405 10.371 0 0 1 10.495 10.236 10.405 10.371 0 0 1-10.224 10.505" fill="#cca300" fill-opacity=".87843" stop-color="#000000" stroke-opacity=".76906" style="paint-order:stroke fill markers"/>
</g>
<g transform="matrix(.30769 0 0 .30669 84.778 -76.546)" fill="#e3dbdb" stroke-width=".38977" aria-label="LibreVNA">
<path d="m63.347 39.076-10.914 8.5033-12.145-15.588 3.1199-2.4307 9.7144 12.469 7.7942-6.0726z"/>
<path d="m59.611 19.98q0.25742 0.45602 0.30095 0.95364 0.05554 0.49085-0.07512 0.96022-0.13743 0.45736-0.44384 0.86769-0.30118 0.39155-0.7572 0.64897-0.45602 0.25742-0.95887 0.31973-0.49763 0.04354-0.96022-0.07512-0.45736-0.13743-0.86092-0.43184-0.39155-0.30118-0.64897-0.7572-0.25064-0.44402-0.30618-0.93487-0.05031-0.50963 0.07512-0.96022 0.13066-0.46936 0.43184-0.86092 0.3064-0.41033 0.76242-0.66774 0.45602-0.25742 0.95365-0.30095 0.50285-0.06231 0.97222 0.06835 0.46259 0.11866 0.86092 0.43184 0.40355 0.2944 0.65419 0.73842zm8.6623 16.663-3.3001 1.8629-7.255-12.853 3.3001-1.8629z"/>
<path d="m81.913 22.848q0.6097 1.6258 0.55813 3.1757-0.05158 1.55-0.64347 2.8758-0.59674 1.3129-1.6886 2.3258-1.079 1.0081-2.5499 1.5597-1.458 0.5468-2.9516 0.48877-1.4806-0.06287-2.8113-0.66767-1.3177-0.60964-2.371-1.7306-1.0581-1.1338-1.6581-2.7338l-4.6066-12.284 3.5354-1.3259 2.4195 6.4515q0.14837-0.46774 0.44191-0.90161 0.30644-0.43871 0.7032-0.79355 0.40966-0.35968 0.879-0.63872 0.4645-0.29194 0.94191-0.47098 1.4709-0.55164 2.9564-0.47586 1.4935 0.05803 2.8161 0.68057 1.3226 0.62254 2.3806 1.7564 1.0532 1.1209 1.6484 2.708zm-3.5483 1.3307q-0.30001-0.79998-0.84034-1.3774-0.53226-0.59514-1.1984-0.91931t-1.4177-0.36609q-0.7387-0.04676-1.4613 0.22422-0.72256 0.27098-1.2467 0.8355-0.51611 0.54678-0.79512 1.2548-0.28385 0.69516-0.29835 1.4806-0.01934 0.77257 0.25164 1.4951 0.30001 0.79998 0.82744 1.3822 0.52742 0.58224 1.1935 0.90641t1.4097 0.38383q0.7516 0.04192 1.4742-0.22906 0.72256-0.27098 1.2483-0.79195 0.53385-0.53871 0.82254-1.221 0.28869-0.68225 0.30319-1.4677 0.0274-0.79031-0.27261-1.5903z"/>
<path d="m90.38 28.992-3.7064 0.64495-2.5302-14.54 0.89604-0.15592 1.5219 1.5116q0.7543-0.97051 1.8191-1.6034 1.0625-0.64647 2.2843-0.85909l3.2719-0.56935 0.64259 3.6928-3.2719 0.56935q-0.58378 0.10159-1.0619 0.40858t-0.7921 0.75328q-0.31399 0.44629-0.44144 0.99999t-0.02586 1.1375z"/>
<path d="m103.82 24.149q0.22057 0.06863 0.44109 0.09593 0.22051 0.01351 0.44099 0.01325 0.55122-6.67e-4 1.0609-0.15287 0.50969-0.1522 0.95033-0.42834 0.45441-0.28994 0.79843-0.68999 0.35779-0.41385 0.57768-0.91021l2.7594 2.7665q-0.52275 0.74478-1.2111 1.3382-0.67452 0.59337-1.4733 1.0078-0.78499 0.41436-1.6667 0.62214-0.8679 0.22154-1.7912 0.22265-1.5572 0.0019-2.9359-0.57523-1.365-0.57713-2.3997-1.6094-1.021-1.0323-1.6153-2.451-0.59429-1.4324-0.59636-3.1412-0.0021-1.7501 0.58869-3.1978 0.59081-1.4477 1.6093-2.4686 1.0323-1.021 2.3959-1.5876 1.3774-0.56666 2.9346-0.56855 0.92329-0.0011 1.8055 0.2183 0.88221 0.21942 1.6682 0.63188 0.79976 0.41245 1.4895 1.018 0.68974 0.59172 1.2143 1.3352zm1.9199-7.7607q-0.26194-0.09615-0.5238-0.12339-0.24808-0.02726-0.52369-0.02693-0.7717 9.33e-4 -1.4604 0.29116-0.67491 0.27643-1.1842 0.8007-0.49546 0.52426-0.78395 1.2687-0.28851 0.73071-0.28739 1.654 2.5e-4 0.20671 0.0144 0.46852 0.0279 0.26179 0.0695 0.53735 0.0554 0.26176 0.12464 0.50972 0.0692 0.24796 0.17968 0.44076z"/>
<path d="m132.01 11.744-10.384 18.022-3.8719-0.80649-2.2976-20.664 4.398 0.91608 1.1692 13.714 6.5611-12.104z"/>
<path d="m144.09 38.576-3.8187-1.8209-1.2794-15.022-5.5991 11.742-3.5699-1.7023 8.5055-17.837 3.8187 1.8209 1.2735 15.035 5.6051-11.755 3.5699 1.7023z"/>
<path d="m161.2 42.054 2.542-3.0299q0.52256-0.62287 0.74031-1.3756 0.23717-0.75438 0.17087-1.5115t-0.43098-1.4588q-0.34526-0.70338-0.96813-1.2259-0.62287-0.52257-1.3861-0.74918-0.74382-0.22831-1.501-0.16201-0.75716 0.0663-1.4694 0.42212-0.69283 0.35412-1.2154 0.97699l-2.542 3.0299zm-2.0452 8.5912-3.0299-2.542 2.542-3.0299-6.0598-5.0839-2.542 3.0299-3.0193-2.5331 7.617-9.0791q1.054-1.2563 2.4502-1.9557 1.4051-0.70993 2.9088-0.8514 1.5143-0.13261 3.0108 0.31346 1.5071 0.45492 2.7634 1.5089 1.2563 1.054 1.9557 2.4502 0.70994 1.4051 0.84254 2.9194 0.13261 1.5143-0.32231 3.0214-0.44606 1.4965-1.5 2.7528z"/>
</g>
<path d="m88.935-56.792a29.116 29.021 0 0 0-1.3876 8.9793 29.116 29.021 0 0 0 29.116 28.895l0.13372-0.0012a28.541 18.773 50.735 0 0 0.27492-3.7453 28.541 18.773 50.735 0 0-23.385-32.659 28.541 18.773 50.735 0 0-4.7521-1.4692z" fill="none" stop-color="#000000" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".9" stroke-width="1.297" style="paint-order:stroke fill markers"/>
<path d="m144.44-56.641a18.773 28.541 39.265 0 0-3.8633 1.2365l-0.4026 0.16609a18.773 28.541 39.265 0 0-23.185 32.777 18.773 28.541 39.265 0 0 0.27586 3.5387 29.116 29.021 0 0 0 28.513-29.393 29.116 29.021 0 0 0-1.3375-8.325z" fill="none" stop-color="#000000" stroke="#000" stroke-linecap="square" stroke-opacity=".9" stroke-width="1.2965" style="paint-order:stroke fill markers"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -384,3 +384,57 @@ QString Touchstone::getFilename() const
{
return filename;
}
nlohmann::json Touchstone::toJSON()
{
nlohmann::json j;
j["ports"] = m_ports;
j["filename"] = filename.toStdString();
if(m_datapoints.size() > 0) {
nlohmann::json json_points;
for(auto d : m_datapoints) {
nlohmann::json point;
point["frequency"] = d.frequency;
nlohmann::json sparams;
for(auto s : d.S) {
nlohmann::json sparam;
sparam["real"] = s.real();
sparam["imag"] = s.imag();
sparams.push_back(sparam);
}
point["Sparams"] = sparams;
json_points.push_back(point);
}
j["datapoints"] = json_points;
}
return j;
}
void Touchstone::fromJSON(nlohmann::json j)
{
m_datapoints.clear();
filename = QString::fromStdString(j.value("filename", ""));
m_ports = j.value("ports", 0);
if(!m_ports || !j.contains("datapoints")) {
return;
}
auto json_points = j["datapoints"];
for(auto point : json_points) {
Datapoint d;
if(!point.contains("frequency") || !point.contains("Sparams")) {
// missing data, abort here
qWarning() << "Touchstone data point does not contain frequency or S parameters";
break;
}
d.frequency = point["frequency"];
if(point["Sparams"].size() != m_ports * m_ports) {
// invalid number of Sparams, abort here
qWarning() << "Invalid number of S parameters, got" << point["Sparams"].size() << "expected" << m_ports*m_ports;
break;
}
for(auto Sparam : point["Sparams"]) {
d.S.push_back(complex<double>(Sparam.value("real", 0.0), Sparam.value("imag", 0.0)));
}
m_datapoints.push_back(d);
}
}

View File

@ -1,12 +1,14 @@
#ifndef TOUCHSTONE_H
#define TOUCHSTONE_H
#include "savable.h"
#include <complex>
#include <vector>
#include <string>
#include <QString>
class Touchstone
class Touchstone : public Savable
{
public:
enum class Scale {
@ -29,6 +31,7 @@ public:
};
Touchstone(unsigned int m_ports);
virtual ~Touchstone(){};
void AddDatapoint(Datapoint p);
void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
std::stringstream toString(Scale unit = Scale::GHz, Format format = Format::RealImaginary);
@ -45,6 +48,9 @@ public:
unsigned int ports() { return m_ports; }
QString getFilename() const;
virtual nlohmann::json toJSON();
virtual void fromJSON(nlohmann::json j);
private:
unsigned int m_ports;
std::vector<Datapoint> m_datapoints;

View File

@ -189,7 +189,7 @@
<listOptionValue builtIn="false" value="FW_MAJOR=1"/>
<listOptionValue builtIn="false" value="FW_PATCH=0"/>
<listOptionValue builtIn="false" value="FW_PATCH=1"/>
<listOptionValue builtIn="false" value="FW_MINOR=2"/>

View File

@ -11,7 +11,7 @@
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1307432939745961523" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1365419078466866483" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
@ -33,7 +33,7 @@
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1307432939745961523" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" console="false" env-hash="1365419078466866483" id="fr.ac6.mcu.ide.build.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="Ac6 SW4 STM32 MCU Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>

View File

@ -190,6 +190,14 @@ bool MAX2871::SetFrequency(uint64_t f) {
auto approx = Algorithm::BestRationalApproximation(fraction, 4095);
if (approx.denom == approx.num) {
// got an impossible result due to floating point limitations(?)
// Set fractional part to zero, increase integer part instead
approx.num = 0;
approx.denom = 2;
N++;
}
if(approx.denom == 1) {
// M value must be at least 2
approx.denom = 2;

View File

@ -300,6 +300,10 @@ void SA::Work() {
if(!s.SignalID || signalIDstep >= signalIDsteps - 1) {
// this measurement point is done, handle result according to detector
for(uint16_t i=0;i<DFTpoints;i++) {
if(pointCnt + i >= points) {
// DFT covered more points than are required for the remaining sweep, can abort here
break;
}
uint16_t binIndex = (pointCnt + i) / binSize;
uint32_t pointInBin = (pointCnt + i) % binSize;
bool lastPointInBin = pointInBin >= binSize - 1;
@ -386,7 +390,7 @@ void SA::Work() {
Communication::Send(packet);
}
if(pointCnt < points - DFTpoints) {
if(pointCnt + DFTpoints < points) {
pointCnt += DFTpoints;
} else {
pointCnt = 0;

View File

@ -180,9 +180,11 @@ bool VNA::Setup(Protocol::SweepSettings s) {
// Configure LO2 for the changed IF1. This is not necessary right now but it will generate
// the correct clock settings
last_LO2 = actualFirstIF - HW::IF2;
LOG_INFO("Changing 2.LO to %lu at point %lu (%lu%06luHz) to reach correct 2.IF frequency",
LOG_INFO("Changing 2.LO to %lu at point %lu (%lu%06luHz) to reach correct 2.IF frequency (1.LO: %lu%06luHz, 1.IF: %lu%06luHz)",
last_LO2, i, (uint32_t ) (freq / 1000000),
(uint32_t ) (freq % 1000000));
(uint32_t ) (freq % 1000000), (uint32_t ) (actualLO1 / 1000000),
(uint32_t ) (actualLO1 % 1000000), (uint32_t ) (actualFirstIF / 1000000),
(uint32_t ) (actualFirstIF % 1000000));
} else {
// last entry in IF table, revert LO2 to default
last_LO2 = HW::IF1 - HW::IF2;

View File

@ -101,7 +101,7 @@ MCU = $(CPU) -mthumb $(FLOAT-ABI) $(FPU)
C_DEFS = \
-DFW_MAJOR=1 \
-DFW_MINOR=2 \
-DFW_PATCH=0 \
-DFW_PATCH=1 \
-DUSE_FULL_LL_DRIVER \
-DHW_REVISION="'B'" \
-D__weak="__attribute__((weak))" \