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 # 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 ## v1.2.0
- Additional SCPI commands - Additional SCPI commands

View File

@ -241,11 +241,11 @@ This section contains general device commands, available regardless of the curre
\subsubsection{DEVice:MODE} \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} \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} \begin{example}
:MODE VNA :DEV:MODE VNA
\end{example} \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} \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} \begin{example}
:MODE? :DEV:MODE?
VNA VNA
\end{example} \end{example}

View File

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

View File

@ -1,4 +1,5 @@
# LibreVNA ![LibreVNA](Software/PC_Application/resources/banner.png)
**100kHz to 6GHz VNA** **100kHz to 6GHz VNA**
This is the improved version of my [first attempt](https://www.github.com/jankae/VNA) at a 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 d = new QDialog();
auto ui = new Ui::AddAmplitudePointsDialog(); auto ui = new Ui::AddAmplitudePointsDialog();
ui->setupUi(d); ui->setupUi(d);
connect(d, &QDialog::finished, [=](){
delete ui;
});
ui->frequency->setUnit("Hz"); ui->frequency->setUnit("Hz");
ui->frequency->setPrefixes(" kMG"); ui->frequency->setPrefixes(" kMG");
ui->startFreq->setUnit("Hz"); ui->startFreq->setUnit("Hz");
@ -356,6 +359,9 @@ void AmplitudeCalDialog::AutomaticMeasurementDialog()
automatic.dialog = new QDialog(this); automatic.dialog = new QDialog(this);
auto ui = new Ui::AutomaticAmplitudeDialog(); auto ui = new Ui::AutomaticAmplitudeDialog();
ui->setupUi(automatic.dialog); ui->setupUi(automatic.dialog);
connect(automatic.dialog, &QDialog::finished, [=](){
delete ui;
});
automatic.progress = ui->progress; automatic.progress = ui->progress;
ui->explanation->setText(info); ui->explanation->setText(info);
ui->status->setText("Gathering information about "+otherCal+" Calibration..."); ui->status->setText("Gathering information about "+otherCal+" Calibration...");

View File

@ -26,6 +26,8 @@ Calibration::Calibration()
measurements[Measurement::Line].datapoints = vector<Protocol::Datapoint>(); measurements[Measurement::Line].datapoints = vector<Protocol::Datapoint>();
type = Type::None; type = Type::None;
port1Standard = port2Standard = PortStandard::Male;
throughZeroLength = false;
} }
Calibration::Standard Calibration::getPort1Standard(Calibration::Measurement m) 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"; qDebug() << "Constructing error terms for" << TypeToString(type) << "calibration";
bool isTRL = type == Type::TRL; bool isTRL = type == Type::TRL;
double kit_minFreq = kit.minFreq(isTRL); bool uses_male = true;
double kit_maxFreq = kit.maxFreq(isTRL); bool uses_female = true;
if(minFreq < kit_minFreq || maxFreq > kit_maxFreq) { if(!kit.checkIfValid(minFreq, maxFreq, isTRL, uses_male, uses_female)) {
// TODO adjust for male/female standards
// Calkit does not support complete calibration range // Calkit does not support complete calibration range
QString msg = QString("The calibration kit does not support the complete span.\n\n") 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) + "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."; + "Please adjust the calibration kit or the span and take the calibration measurements again.";
InformationBox::ShowError("Unable to perform calibration", msg); InformationBox::ShowError("Unable to perform calibration", msg);
qWarning() << msg; qWarning() << msg;
return false; 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) { switch(type) {
case Type::Port1SOL: constructPort1SOL(); break; case Type::Port1SOL: constructPort1SOL(); break;
case Type::Port2SOL: constructPort2SOL(); 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 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 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 // Forward calibration
computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load); computeSOL(S11_short, S11_open, S11_load, p.fe00, p.fe11, p.fe10e01, actual.Open, actual.Short, actual.Load);
p.fe30 = S21_isolation; p.fe30 = S21_isolation;
// See page 18 of https://www.rfmentor.com/sites/default/files/NA_Error_Models_and_Cal_Methods.pdf // 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 // 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; 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) 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); / ((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; 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 // 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); computeSOL(S22_short, S22_open, S22_load, p.re33, p.re22, p.re23e32, actual.Open, actual.Short, actual.Load);
p.re03 = S12_isolation; p.re03 = S12_isolation;
p.re11 = ((S22_through - p.re33)*(1.0 - p.re22 * actual.ThroughS22)-actual.ThroughS22*p.re23e32) 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); / ((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; 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); 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_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); auto S11_load = complex<double>(measurements[Measurement::Port1Load].datapoints[i].real_S11, measurements[Measurement::Port1Load].datapoints[i].imag_S11);
// OSL port1 // 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 // 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); 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 // All other calibration coefficients to ideal values
p.fex = 0.0;
p.fe30 = 0.0; p.fe30 = 0.0;
p.fe22 = 0.0; p.fe22 = 0.0;
p.fe10e32 = 1.0; p.fe10e32 = 1.0;
@ -223,6 +248,7 @@ void Calibration::constructPort1SOL()
p.re22 = 0.0; p.re22 = 0.0;
p.re23e32 = 1.0; p.re23e32 = 1.0;
p.re03 = 0.0; p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0; p.re11 = 0.0;
p.re23e01 = 1.0; p.re23e01 = 1.0;
points.push_back(p); 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_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); auto S22_load = complex<double>(measurements[Measurement::Port2Load].datapoints[i].real_S22, measurements[Measurement::Port2Load].datapoints[i].imag_S22);
// OSL port2 // 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 // 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); 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 // All other calibration coefficients to ideal values
p.fex = 0.0;
p.fe30 = 0.0; p.fe30 = 0.0;
p.fe22 = 0.0; p.fe22 = 0.0;
p.fe10e32 = 1.0; p.fe10e32 = 1.0;
@ -251,6 +278,7 @@ void Calibration::constructPort2SOL()
p.fe11 = 0.0; p.fe11 = 0.0;
p.fe10e01 = 1.0; p.fe10e01 = 1.0;
p.re03 = 0.0; p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0; p.re11 = 0.0;
p.re23e01 = 1.0; p.re23e01 = 1.0;
points.push_back(p); points.push_back(p);
@ -271,11 +299,13 @@ void Calibration::constructTransmissionNormalization()
p.re23e01 = S12_through / actual.ThroughS12; p.re23e01 = S12_through / actual.ThroughS12;
// All other calibration coefficients to ideal values // All other calibration coefficients to ideal values
p.fe30 = 0.0; p.fe30 = 0.0;
p.fex = 0.0;
p.fe22 = 0.0; p.fe22 = 0.0;
p.fe00 = 0.0; p.fe00 = 0.0;
p.fe11 = 0.0; p.fe11 = 0.0;
p.fe10e01 = 1.0; p.fe10e01 = 1.0;
p.re03 = 0.0; p.re03 = 0.0;
p.rex = 0.0;
p.re11 = 0.0; p.re11 = 0.0;
p.re33 = 0.0; p.re33 = 0.0;
p.re22 = 0.0; p.re22 = 0.0;
@ -375,6 +405,7 @@ void Calibration::constructTRL()
p.fe10e32 = S_B.m21; p.fe10e32 = S_B.m21;
// no isolation measurement available // no isolation measurement available
p.fe30 = 0.0; p.fe30 = 0.0;
p.fex = 0.0;
// Reverse coefficients, normalize for S12 = 1.0 // Reverse coefficients, normalize for S12 = 1.0
// => det(T)/T22 = 1.0 // => det(T)/T22 = 1.0
@ -395,6 +426,7 @@ void Calibration::constructTRL()
p.re33 = S_B.m22; p.re33 = S_B.m22;
// no isolation measurement available // no isolation measurement available
p.re03 = 0.0; p.re03 = 0.0;
p.rex = 0.0;
points.push_back(p); points.push_back(p);
} }
@ -774,11 +806,23 @@ bool Calibration::openFromFile(QString filename)
} }
try { try {
file >> *this; nlohmann::json j;
file >> j;
fromJSON(j);
} catch(exception e) { } catch(exception e) {
InformationBox::ShowError("File parsing error", e.what()); // json parsing failed, probably using a legacy file format
qWarning() << "Calibration file parsing failed: " << e.what(); try {
return false; 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 this->currentCalFile = filename; // if all ok, remember this
@ -802,7 +846,7 @@ bool Calibration::saveToFile(QString filename)
auto calibration_file = filename + ".cal"; auto calibration_file = filename + ".cal";
ofstream file; ofstream file;
file.open(calibration_file.toStdString()); file.open(calibration_file.toStdString());
file << *this; file << setw(1) << toJSON();
auto calkit_file = filename + ".calkit"; auto calkit_file = filename + ".calkit";
qDebug() << "Saving associated calibration kit to file" << calkit_file; qDebug() << "Saving associated calibration kit to file" << calkit_file;
@ -839,6 +883,16 @@ QString Calibration::descriptiveCalName(){
return tmp; return tmp;
} }
bool Calibration::getThroughZeroLength() const
{
return throughZeroLength;
}
void Calibration::setThroughZeroLength(bool value)
{
throughZeroLength = value;
}
double Calibration::getMinFreq(){ double Calibration::getMinFreq(){
return this->minFreq; return this->minFreq;
} }
@ -848,6 +902,97 @@ double Calibration::getMaxFreq(){
int Calibration::getNumPoints(){ int Calibration::getNumPoints(){
return this->points.size(); 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(){ QString Calibration::getCurrentCalibrationFile(){
return this->currentCalFile; return this->currentCalFile;
} }
@ -872,6 +1017,11 @@ ostream& operator<<(ostream &os, const Calibration &c)
istream& operator >>(istream &in, 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; std::string line;
while(getline(in, line)) { while(getline(in, line)) {
QString qLine = QString::fromStdString(line).simplified(); 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.fe11 = low->fe11 * (1 - alpha) + high->fe11 * alpha;
ret.fe22 = low->fe22 * (1 - alpha) + high->fe22 * alpha; ret.fe22 = low->fe22 * (1 - alpha) + high->fe22 * alpha;
ret.fe30 = low->fe30 * (1 - alpha) + high->fe30 * 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.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.re11 = low->re11 * (1 - alpha) + high->re11 * alpha;
ret.re22 = low->re22 * (1 - alpha) + high->re22 * alpha; ret.re22 = low->re22 * (1 - alpha) + high->re22 * alpha;
ret.re33 = low->re33 * (1 - alpha) + high->re33 * 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; 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) 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); return (measured - directivity) / (measured * match - directivity * match + tracking);
@ -1017,6 +1175,26 @@ void Calibration::setCalibrationKit(const Calkit &value)
kit = 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 Calibration::Type Calibration::getType() const
{ {
return type; return type;

View File

@ -11,8 +11,9 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <QDateTime> #include <QDateTime>
#include <savable.h>
class Calibration class Calibration : public Savable
{ {
public: public:
Calibration(); Calibration();
@ -108,6 +109,23 @@ public:
Calkit& getCalibrationKit(); Calkit& getCalibrationKit();
void setCalibrationKit(const Calkit &value); 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: private:
void construct12TermPoints(); void construct12TermPoints();
void constructPort1SOL(); void constructPort1SOL();
@ -120,9 +138,9 @@ private:
public: public:
double frequency; double frequency;
// Forward error terms // 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 // 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); Point getCalibrationPoint(Protocol::Datapoint &d);
/* /*
@ -140,6 +158,15 @@ private:
std::complex<double> o_c = std::complex<double>(1.0, 0), 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> s_c = std::complex<double>(-1.0, 0),
std::complex<double> l_c = std::complex<double>(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> correctSOL(std::complex<double> measured,
std::complex<double> directivity, std::complex<double> directivity,
std::complex<double> match, std::complex<double> match,
@ -157,14 +184,10 @@ private:
Calkit kit; Calkit kit;
QString descriptiveCalName(); QString descriptiveCalName();
private:
QString currentCalFile; QString currentCalFile;
public:
QString getCurrentCalibrationFile(); PortStandard port1Standard, port2Standard;
double getMinFreq(); bool throughZeroLength;
double getMaxFreq();
int getNumPoints();
}; };
#endif // CALIBRATION_H #endif // CALIBRATION_H

View File

@ -21,16 +21,91 @@ CalibrationTraceDialog::CalibrationTraceDialog(Calibration *cal, double f_min, d
model = new MeasurementModel(cal, measurements); model = new MeasurementModel(cal, measurements);
ui->tableView->setModel(model); ui->tableView->setModel(model);
ui->tableView->setColumnWidth(0, 100); ui->tableView->setColumnWidth(0, 100);
ui->tableView->setColumnWidth(1, 350); ui->tableView->setColumnWidth(1, 80);
ui->tableView->setColumnWidth(2, 320); ui->tableView->setColumnWidth(2, 350);
ui->tableView->setColumnWidth(3, 160); ui->tableView->setColumnWidth(3, 320);
ui->tableView->setColumnWidth(4, 160);
UpdateCalibrationStatus(); 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 // Check calibration kit span
if(type != Calibration::Type::None) { if(type != Calibration::Type::None) {
auto kit = cal->getCalibrationKit(); auto kit = cal->getCalibrationKit();
auto isTRL = type == Calibration::Type::TRL; 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. " 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."); "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() void CalibrationTraceDialog::on_bDelete_clicked()
{ {
auto measurement = measurements[ui->tableView->currentIndex().row()]; auto selected = ui->tableView->selectionModel()->selectedRows();
cal->clearMeasurement(measurement); for(auto s : selected) {
model->measurementUpdated(measurement); cal->clearMeasurement(measurements[s.row()]);
model->measurementUpdated(measurements[s.row()]);
}
UpdateCalibrationStatus(); UpdateCalibrationStatus();
} }

View File

@ -16,80 +16,180 @@
<property name="modal"> <property name="modal">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<item> <item>
<widget class="QTableView" name="tableView"> <widget class="QGroupBox" name="port1Standards">
<property name="selectionBehavior"> <property name="title">
<enum>QAbstractItemView::SelectRows</enum> <string>Port 1 Standards</string>
</property> </property>
<attribute name="horizontalHeaderCascadingSectionResizes"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<bool>true</bool> <item>
</attribute> <widget class="QRadioButton" name="port1Male">
<attribute name="horizontalHeaderShowSortIndicator" stdset="0"> <property name="text">
<bool>false</bool> <string>Male</string>
</attribute> </property>
<attribute name="horizontalHeaderStretchLastSection"> <attribute name="buttonGroup">
<bool>true</bool> <string notr="true">port1Group</string>
</attribute> </attribute>
<attribute name="verticalHeaderVisible"> </widget>
<bool>false</bool> </item>
</attribute> <item>
<attribute name="verticalHeaderHighlightSections"> <widget class="QRadioButton" name="port1Female">
<bool>false</bool> <property name="text">
</attribute> <string>Female</string>
</property>
<attribute name="buttonGroup">
<string notr="true">port1Group</string>
</attribute>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <widget class="QGroupBox" name="port2Standards">
<item> <property name="title">
<widget class="QPushButton" name="bMeasure"> <string>Port 2 Standards</string>
<property name="text"> </property>
<string>Measure</string> <layout class="QHBoxLayout" name="horizontalLayout_3">
</property> <item>
<property name="icon"> <widget class="QRadioButton" name="port2Male">
<iconset theme="media-playback-start" resource="../icons.qrc"> <property name="text">
<normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset> <string>Male</string>
</property> </property>
</widget> <attribute name="buttonGroup">
</item> <string notr="true">port2Group</string>
<item> </attribute>
<widget class="QPushButton" name="bDelete"> </widget>
<property name="text"> </item>
<string>Delete</string> <item>
</property> <widget class="QRadioButton" name="port2Female">
<property name="icon"> <property name="text">
<iconset theme="edit-delete" resource="../icons.qrc"> <string>Female</string>
<normaloff>:/icons/trash.png</normaloff>:/icons/trash.png</iconset> </property>
</property> <attribute name="buttonGroup">
</widget> <string notr="true">port2Group</string>
</item> </attribute>
<item> </widget>
<spacer name="horizontalSpacer"> </item>
<property name="orientation"> </layout>
<enum>Qt::Horizontal</enum> </widget>
</property> </item>
<property name="sizeHint" stdset="0"> <item>
<size> <widget class="QGroupBox" name="throughStandard">
<width>40</width> <property name="title">
<height>20</height> <string>Through Standard</string>
</size> </property>
</property> <layout class="QHBoxLayout" name="horizontalLayout_4">
</spacer> <item>
</item> <widget class="QRadioButton" name="throughCalkit">
<item> <property name="text">
<widget class="QPushButton" name="bApply"> <string>From calibration kit</string>
<property name="text"> </property>
<string>Apply Calibration</string> <attribute name="buttonGroup">
</property> <string notr="true">throughGroup</string>
<property name="icon"> </attribute>
<iconset resource="../icons.qrc"> </widget>
<normaloff>:/icons/ok.png</normaloff>:/icons/ok.png</iconset> </item>
</property> <item>
</widget> <widget class="QRadioButton" name="throughZero">
</item> <property name="text">
</layout> <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">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderHighlightSections">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="bMeasure">
<property name="text">
<string>Measure</string>
</property>
<property name="icon">
<iconset theme="media-playback-start" resource="../icons.qrc">
<normaloff>:/icons/play.png</normaloff>:/icons/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bDelete">
<property name="text">
<string>Delete</string>
</property>
<property name="icon">
<iconset theme="edit-delete" resource="../icons.qrc">
<normaloff>:/icons/trash.png</normaloff>:/icons/trash.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="bApply">
<property name="text">
<string>Apply Calibration</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/ok.png</normaloff>:/icons/ok.png</iconset>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -99,4 +199,9 @@
<include location="../icons.qrc"/> <include location="../icons.qrc"/>
</resources> </resources>
<connections/> <connections/>
<buttongroups>
<buttongroup name="port1Group"/>
<buttongroup name="port2Group"/>
<buttongroup name="throughGroup"/>
</buttongroups>
</ui> </ui>

View File

@ -14,9 +14,12 @@ using json = nlohmann::json;
using namespace std; using namespace std;
Calkit::Calkit() Calkit::Calkit()
: ts_open(nullptr), : ts_open_m(nullptr),
ts_short(nullptr), ts_short_m(nullptr),
ts_load(nullptr), ts_load_m(nullptr),
ts_open_f(nullptr),
ts_short_f(nullptr),
ts_load_f(nullptr),
ts_through(nullptr), ts_through(nullptr),
ts_cached(false) ts_cached(false)
{ {
@ -128,39 +131,39 @@ Calkit Calkit::fromFile(QString filename)
// legacy file format, return to beginning of file // legacy file format, return to beginning of file
file.clear(); file.clear();
file.seekg(0); file.seekg(0);
c.SOLT.Open.useMeasurements = readLine(file).toInt(); c.SOLT.open_m.useMeasurements = readLine(file).toInt();
c.SOLT.Short.useMeasurements = readLine(file).toInt(); c.SOLT.short_m.useMeasurements = readLine(file).toInt();
c.SOLT.Load.useMeasurements = readLine(file).toInt(); c.SOLT.load_m.useMeasurements = readLine(file).toInt();
c.SOLT.Through.useMeasurements = readLine(file).toInt(); c.SOLT.Through.useMeasurements = readLine(file).toInt();
c.SOLT.Open.Z0 = readLine(file).toDouble(); c.SOLT.open_m.Z0 = readLine(file).toDouble();
c.SOLT.Open.delay = readLine(file).toDouble(); c.SOLT.open_m.delay = readLine(file).toDouble();
c.SOLT.Open.loss = readLine(file).toDouble(); c.SOLT.open_m.loss = readLine(file).toDouble();
c.SOLT.Open.C0 = readLine(file).toDouble(); c.SOLT.open_m.C0 = readLine(file).toDouble();
c.SOLT.Open.C1 = readLine(file).toDouble(); c.SOLT.open_m.C1 = readLine(file).toDouble();
c.SOLT.Open.C2 = readLine(file).toDouble(); c.SOLT.open_m.C2 = readLine(file).toDouble();
c.SOLT.Open.C3 = readLine(file).toDouble(); c.SOLT.open_m.C3 = readLine(file).toDouble();
c.SOLT.Short.Z0 = readLine(file).toDouble(); c.SOLT.short_m.Z0 = readLine(file).toDouble();
c.SOLT.Short.delay = readLine(file).toDouble(); c.SOLT.short_m.delay = readLine(file).toDouble();
c.SOLT.Short.loss = readLine(file).toDouble(); c.SOLT.short_m.loss = readLine(file).toDouble();
c.SOLT.Short.L0 = readLine(file).toDouble(); c.SOLT.short_m.L0 = readLine(file).toDouble();
c.SOLT.Short.L1 = readLine(file).toDouble(); c.SOLT.short_m.L1 = readLine(file).toDouble();
c.SOLT.Short.L2 = readLine(file).toDouble(); c.SOLT.short_m.L2 = readLine(file).toDouble();
c.SOLT.Short.L3 = readLine(file).toDouble(); c.SOLT.short_m.L3 = readLine(file).toDouble();
c.SOLT.Load.Z0 = readLine(file).toDouble(); c.SOLT.load_m.Z0 = readLine(file).toDouble();
c.SOLT.Through.Z0 = readLine(file).toDouble(); c.SOLT.Through.Z0 = readLine(file).toDouble();
c.SOLT.Through.delay = readLine(file).toDouble(); c.SOLT.Through.delay = readLine(file).toDouble();
c.SOLT.Through.loss = readLine(file).toDouble(); c.SOLT.Through.loss = readLine(file).toDouble();
if(c.SOLT.Open.useMeasurements) { if(c.SOLT.open_m.useMeasurements) {
c.SOLT.Open.file = readLine(file); c.SOLT.open_m.file = readLine(file);
c.SOLT.Open.Sparam = readLine(file).toInt(); c.SOLT.open_m.Sparam = readLine(file).toInt();
} }
if(c.SOLT.Short.useMeasurements) { if(c.SOLT.short_m.useMeasurements) {
c.SOLT.Short.file = readLine(file); c.SOLT.short_m.file = readLine(file);
c.SOLT.Short.Sparam = readLine(file).toInt(); c.SOLT.short_m.Sparam = readLine(file).toInt();
} }
if(c.SOLT.Load.useMeasurements) { if(c.SOLT.load_m.useMeasurements) {
c.SOLT.Load.file = readLine(file); c.SOLT.load_m.file = readLine(file);
c.SOLT.Load.Sparam = readLine(file).toInt(); c.SOLT.load_m.Sparam = readLine(file).toInt();
} }
if(c.SOLT.Through.useMeasurements) { if(c.SOLT.Through.useMeasurements) {
c.SOLT.Through.file = readLine(file); 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.minFreq = readLine(file).toDouble();
c.TRL.Line.maxFreq = 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" InformationBox::ShowMessage("Loading calkit file", "The file \"" + filename + "\" is stored in a deprecated"
" calibration kit format. Future versions of this application might not support" " calibration kit format. Future versions of this application might not support"
" it anymore. Please save the calibration kit to update to the new format"); " 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(); 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> { 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 // 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; 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(); fillTouchstoneCache();
class SOLT ref; class SOLT ref;
if(SOLT.Load.useMeasurements) { if(Load.useMeasurements) {
ref.Load = ts_load->interpolate(frequency).S[0]; ref.Load = ts_load->interpolate(frequency).S[0];
} else { } else {
auto imp_load = complex<double>(SOLT.Load.Z0, 0); auto imp_load = complex<double>(Load.Z0, 0);
// Add parallel capacitor to impedance // Add parallel capacitor to impedance
if(SOLT.Load.Cparallel > 0) { if(Load.Cparallel > 0) {
auto imp_C = complex<double>(0, -1.0 / (frequency * 2 * M_PI * SOLT.Load.Cparallel)); auto imp_C = complex<double>(0, -1.0 / (frequency * 2 * M_PI * Load.Cparallel));
imp_load = (imp_load * imp_C) / (imp_load + imp_C); imp_load = (imp_load * imp_C) / (imp_load + imp_C);
} }
// add series inductor to impedance // 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; imp_load += imp_L;
ref.Load = (imp_load - complex<double>(50.0)) / (imp_load + complex<double>(50.0)); 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]; ref.Open = ts_open->interpolate(frequency).S[0];
} else { } else {
// calculate fringing capacitance for open // 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 // convert to impedance
if (Cfringing == 0) { if (Cfringing == 0) {
// special case to avoid issues with infinity // 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)); 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 = (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]; ref.Short = ts_short->interpolate(frequency).S[0];
} else { } else {
// calculate inductance for short // 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 // convert to impedance
auto imp_short = complex<double>(0, frequency * 2 * M_PI * Lseries); 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 = (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) { if(SOLT.Through.useMeasurements) {
@ -300,47 +317,91 @@ class Calkit::TRL Calkit::toTRL(double)
return trl; return trl;
} }
double Calkit::minFreq(bool trl) double Calkit::minFreqTRL()
{ {
if(trl) { return TRL.Line.minFreq;
return TRL.Line.minFreq;
} else {
fillTouchstoneCache();
double min = 0;
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) {
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
}
if(ts->minFreq() > min) {
min = ts->minFreq();
}
}
return min;
}
} }
double Calkit::maxFreq(bool trl) double Calkit::maxFreqTRL()
{ {
if(trl) { return TRL.Line.maxFreq;
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) {
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
}
if(ts->minFreq() > min) {
min = ts->minFreq();
}
}
return min;
}
double Calkit::maxFreqSOLT(bool male_standards)
{
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) {
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
}
if(ts->maxFreq() < max) {
max = ts->maxFreq();
}
}
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 { } else {
fillTouchstoneCache(); if(include_male) {
double max = std::numeric_limits<double>::max(); auto min_male = minFreqSOLT(true);
array<Touchstone*, 4> ts_list = {ts_open, ts_short, ts_load, ts_through}; auto max_male = maxFreqSOLT(true);
// find the highest minimum frequency in all measurement files if(min_male > min_supported) {
for(auto ts : ts_list) { min_supported = min_male;
if(!ts) {
// this calibration standard is defined by coefficients, no minimum frequency
continue;
} }
if(ts->maxFreq() < max) { if(max_male > max_supported) {
max = ts->maxFreq(); max_supported = max_male;
} }
} }
return max; 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) 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) { for(auto f : filenames) {
if(f->isEmpty()) { if(f->isEmpty()) {
continue; continue;
@ -366,7 +427,7 @@ void Calkit::TransformPathsToRelative(QFileInfo d)
void Calkit::TransformPathsToAbsolute(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) { for(auto f : filenames) {
if(f->isEmpty()) { if(f->isEmpty()) {
continue; continue;
@ -382,12 +443,18 @@ void Calkit::TransformPathsToAbsolute(QFileInfo d)
void Calkit::clearTouchstoneCache() void Calkit::clearTouchstoneCache()
{ {
delete ts_open; delete ts_open_m;
ts_open = nullptr; ts_open_m = nullptr;
delete ts_short; delete ts_short_m;
ts_short = nullptr; ts_short_m = nullptr;
delete ts_load; delete ts_load_m;
ts_load = nullptr; 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; delete ts_through;
ts_through = nullptr; ts_through = nullptr;
ts_cached = false; ts_cached = false;
@ -398,20 +465,35 @@ void Calkit::fillTouchstoneCache()
if(ts_cached) { if(ts_cached) {
return; return;
} }
if(SOLT.Open.useMeasurements) { if(SOLT.open_m.useMeasurements) {
ts_open = new Touchstone(1); ts_open_m = new Touchstone(1);
*ts_open = Touchstone::fromFile(SOLT.Open.file.toStdString()); *ts_open_m = Touchstone::fromFile(SOLT.open_m.file.toStdString());
ts_open->reduceTo1Port(SOLT.Open.Sparam); ts_open_m->reduceTo1Port(SOLT.open_m.Sparam);
} }
if(SOLT.Short.useMeasurements) { if(SOLT.short_m.useMeasurements) {
ts_short = new Touchstone(1); ts_short_m = new Touchstone(1);
*ts_short = Touchstone::fromFile(SOLT.Short.file.toStdString()); *ts_short_m = Touchstone::fromFile(SOLT.short_m.file.toStdString());
ts_short->reduceTo1Port(SOLT.Short.Sparam); ts_short_m->reduceTo1Port(SOLT.short_m.Sparam);
} }
if(SOLT.Load.useMeasurements) { if(SOLT.load_m.useMeasurements) {
ts_load = new Touchstone(1); ts_load_m = new Touchstone(1);
*ts_load = Touchstone::fromFile(SOLT.Load.file.toStdString()); *ts_load_m = Touchstone::fromFile(SOLT.load_m.file.toStdString());
ts_load->reduceTo1Port(SOLT.Load.Sparam); 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) { if(SOLT.Through.useMeasurements) {
ts_through = new Touchstone(2); ts_through = new Touchstone(2);

View File

@ -41,10 +41,14 @@ public:
void toFile(QString filename); void toFile(QString filename);
static Calkit fromFile(QString filename); static Calkit fromFile(QString filename);
void edit(std::function<void(void)> done = nullptr); 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); TRL toTRL(double frequency);
double minFreq(bool trl = false); double minFreqTRL();
double maxFreq(bool trl = false); 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; bool isTRLReflectionShort() const;
private: private:
@ -54,30 +58,34 @@ private:
QString manufacturer, serialnumber, description; QString manufacturer, serialnumber, description;
// SOLT standard definitions // SOLT standard definitions
struct { struct {
struct { using Open = struct {
double Z0, delay, loss, C0, C1, C2, C3; double Z0, delay, loss, C0, C1, C2, C3;
QString file; QString file;
bool useMeasurements; bool useMeasurements;
int Sparam; int Sparam;
} Open; };
struct { Open open_m, open_f;
using Short = struct {
double Z0, delay, loss, L0, L1, L2, L3; double Z0, delay, loss, L0, L1, L2, L3;
QString file; QString file;
bool useMeasurements; bool useMeasurements;
int Sparam; int Sparam;
} Short; };
struct { Short short_m, short_f;
using Load = struct {
double Z0, delay, Cparallel, Lseries; double Z0, delay, Cparallel, Lseries;
QString file; QString file;
bool useMeasurements; bool useMeasurements;
int Sparam; int Sparam;
} Load; };
Load load_m, load_f;
struct { struct {
double Z0, delay, loss; double Z0, delay, loss;
QString file; QString file;
bool useMeasurements; bool useMeasurements;
int Sparam1, Sparam2; int Sparam1, Sparam2;
} Through; } Through;
bool separate_male_female;
} SOLT; } SOLT;
struct { struct {
struct { struct {
@ -92,7 +100,9 @@ private:
} TRL; } TRL;
bool startDialogWithSOLT; 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; bool ts_cached;
using JSONDescription = struct _jsondescr { using JSONDescription = struct _jsondescr {
@ -100,40 +110,67 @@ private:
QString name; QString name;
QVariant def; QVariant def;
}; };
const std::array<JSONDescription, 43> json_descr = {{ const std::array<JSONDescription, 71> json_descr = {{
{&manufacturer, "Manufacturer", ""}, {&manufacturer, "Manufacturer", ""},
{&serialnumber, "Serialnumber", ""}, {&serialnumber, "Serialnumber", ""},
{&description, "Description", ""}, {&description, "Description", ""},
{&SOLT.Open.Z0, "SOLT/Open/Param/Z0", 50.0}, {&SOLT.open_m.Z0, "SOLT/Open/Param/Z0", 50.0},
{&SOLT.Open.delay, "SOLT/Open/Param/Delay", 0.0}, {&SOLT.open_m.delay, "SOLT/Open/Param/Delay", 0.0},
{&SOLT.Open.loss, "SOLT/Open/Param/Loss", 0.0}, {&SOLT.open_m.loss, "SOLT/Open/Param/Loss", 0.0},
{&SOLT.Open.C0, "SOLT/Open/Param/C0", 0.0}, {&SOLT.open_m.C0, "SOLT/Open/Param/C0", 0.0},
{&SOLT.Open.C1, "SOLT/Open/Param/C1", 0.0}, {&SOLT.open_m.C1, "SOLT/Open/Param/C1", 0.0},
{&SOLT.Open.C2, "SOLT/Open/Param/C2", 0.0}, {&SOLT.open_m.C2, "SOLT/Open/Param/C2", 0.0},
{&SOLT.Open.C3, "SOLT/Open/Param/C3", 0.0}, {&SOLT.open_m.C3, "SOLT/Open/Param/C3", 0.0},
{&SOLT.Open.useMeasurements, "SOLT/Open/Measurements/Use", false}, {&SOLT.open_m.useMeasurements, "SOLT/Open/Measurements/Use", false},
{&SOLT.Open.file, "SOLT/Open/Measurements/File", ""}, {&SOLT.open_m.file, "SOLT/Open/Measurements/File", ""},
{&SOLT.Open.Sparam, "SOLT/Open/Measurements/Port", 0}, {&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_m.Z0, "SOLT/Short/Param/Z0", 50.0},
{&SOLT.Short.delay, "SOLT/Short/Param/Delay", 0.0}, {&SOLT.short_m.delay, "SOLT/Short/Param/Delay", 0.0},
{&SOLT.Short.loss, "SOLT/Short/Param/Loss", 0.0}, {&SOLT.short_m.loss, "SOLT/Short/Param/Loss", 0.0},
{&SOLT.Short.L0, "SOLT/Short/Param/L0", 0.0}, {&SOLT.short_m.L0, "SOLT/Short/Param/L0", 0.0},
{&SOLT.Short.L1, "SOLT/Short/Param/L1", 0.0}, {&SOLT.short_m.L1, "SOLT/Short/Param/L1", 0.0},
{&SOLT.Short.L2, "SOLT/Short/Param/L2", 0.0}, {&SOLT.short_m.L2, "SOLT/Short/Param/L2", 0.0},
{&SOLT.Short.L3, "SOLT/Short/Param/L3", 0.0}, {&SOLT.short_m.L3, "SOLT/Short/Param/L3", 0.0},
{&SOLT.Short.useMeasurements, "SOLT/Short/Measurements/Use", false}, {&SOLT.short_m.useMeasurements, "SOLT/Short/Measurements/Use", false},
{&SOLT.Short.file, "SOLT/Short/Measurements/File", ""}, {&SOLT.short_m.file, "SOLT/Short/Measurements/File", ""},
{&SOLT.Short.Sparam, "SOLT/Short/Measurements/Port", 0}, {&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_m.Z0, "SOLT/Load/Param/Z0", 50.0},
{&SOLT.Load.delay, "SOLT/Load/Param/Delay", 0.0}, {&SOLT.load_m.delay, "SOLT/Load/Param/Delay", 0.0},
{&SOLT.Load.Cparallel, "SOLT/Load/Param/C", 0.0}, {&SOLT.load_m.Cparallel, "SOLT/Load/Param/C", 0.0},
{&SOLT.Load.Lseries, "SOLT/Load/Param/L", 0.0}, {&SOLT.load_m.Lseries, "SOLT/Load/Param/L", 0.0},
{&SOLT.Load.useMeasurements, "SOLT/Load/Measurements/Use", false}, {&SOLT.load_m.useMeasurements, "SOLT/Load/Measurements/Use", false},
{&SOLT.Load.file, "SOLT/Load/Measurements/File", ""}, {&SOLT.load_m.file, "SOLT/Load/Measurements/File", ""},
{&SOLT.Load.Sparam, "SOLT/Load/Measurements/Port", 0}, {&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.Z0, "SOLT/Through/Param/Z0", 50.0},
{&SOLT.Through.delay, "SOLT/Through/Param/Delay", 0.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.Sparam1, "SOLT/Through/Measurements/Port1", 0},
{&SOLT.Through.Sparam2, "SOLT/Through/Measurements/Port2", 1}, {&SOLT.Through.Sparam2, "SOLT/Through/Measurements/Port2", 1},
{&SOLT.separate_male_female, "SOLT/SeparateMaleFemale", false},
{&TRL.Through.Z0, "TRL/Through/Z0", 50.0}, {&TRL.Through.Z0, "TRL/Through/Z0", 50.0},
{&TRL.Reflection.isShort, "TRL/Reflect/isShort", false}, {&TRL.Reflection.isShort, "TRL/Reflect/isShort", false},
{&TRL.Line.delay, "TRL/Line/Delay", 74.0}, {&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_parC->setPrefixes("fpnum ");
ui->load_serL->setUnit("H"); ui->load_serL->setUnit("H");
ui->load_serL->setPrefixes("fpnum "); 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->through_Z0->setUnit("Ω");
ui->TRL_through_Z0->setUnit("Ω"); ui->TRL_through_Z0->setUnit("Ω");
@ -59,6 +82,24 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
editKit.clearTouchstoneCache(); editKit.clearTouchstoneCache();
ownKit = editKit; 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(); updateEntries();
connect(ui->TRL_line_min, &SIUnitEdit::valueChanged, [=](double newval){ 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()) { if(ui->load_measurement->isChecked() && !ui->load_touchstone->getStatus()) {
ok = false; 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()) { if(ui->through_measurement->isChecked() && !ui->through_touchstone->getStatus()) {
ok = false; ok = false;
} }
@ -95,6 +145,9 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->open_touchstone, &TouchstoneImport::statusChanged, UpdateStatus); connect(ui->open_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->short_touchstone, &TouchstoneImport::statusChanged, UpdateStatus); connect(ui->short_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->load_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->through_touchstone, &TouchstoneImport::statusChanged, UpdateStatus);
connect(ui->OpenType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) { 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) { connect(ui->LoadType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus(); 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) { connect(ui->ThroughType, qOverload<int>(&QButtonGroup::buttonClicked), [=](int) {
UpdateStatus(); UpdateStatus();
}); });
@ -152,49 +214,83 @@ void CalkitDialog::parseEntries()
ownKit.description = ui->description->toPlainText(); ownKit.description = ui->description->toPlainText();
// type // type
ownKit.SOLT.Open.useMeasurements = ui->open_measurement->isChecked(); ownKit.SOLT.open_m.useMeasurements = ui->open_measurement->isChecked();
ownKit.SOLT.Short.useMeasurements = ui->short_measurement->isChecked(); ownKit.SOLT.short_m.useMeasurements = ui->short_measurement->isChecked();
ownKit.SOLT.Load.useMeasurements = ui->load_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(); ownKit.SOLT.Through.useMeasurements = ui->through_measurement->isChecked();
// coefficients // coefficients
ownKit.SOLT.Open.Z0 = ui->open_Z0->value(); ownKit.SOLT.open_m.Z0 = ui->open_Z0->value();
ownKit.SOLT.Open.delay = ui->open_delay->value(); ownKit.SOLT.open_m.delay = ui->open_delay->value();
ownKit.SOLT.Open.loss = ui->open_loss->value(); ownKit.SOLT.open_m.loss = ui->open_loss->value();
ownKit.SOLT.Open.C0 = ui->open_C0->value(); ownKit.SOLT.open_m.C0 = ui->open_C0->value();
ownKit.SOLT.Open.C1 = ui->open_C1->value(); ownKit.SOLT.open_m.C1 = ui->open_C1->value();
ownKit.SOLT.Open.C2 = ui->open_C2->value(); ownKit.SOLT.open_m.C2 = ui->open_C2->value();
ownKit.SOLT.Open.C3 = ui->open_C3->value(); ownKit.SOLT.open_m.C3 = ui->open_C3->value();
ownKit.SOLT.Short.Z0 = ui->short_Z0->value(); ownKit.SOLT.short_m.Z0 = ui->short_Z0->value();
ownKit.SOLT.Short.delay = ui->short_delay->value(); ownKit.SOLT.short_m.delay = ui->short_delay->value();
ownKit.SOLT.Short.loss = ui->short_loss->value(); ownKit.SOLT.short_m.loss = ui->short_loss->value();
ownKit.SOLT.Short.L0 = ui->short_L0->value(); ownKit.SOLT.short_m.L0 = ui->short_L0->value();
ownKit.SOLT.Short.L1 = ui->short_L1->value(); ownKit.SOLT.short_m.L1 = ui->short_L1->value();
ownKit.SOLT.Short.L2 = ui->short_L2->value(); ownKit.SOLT.short_m.L2 = ui->short_L2->value();
ownKit.SOLT.Short.L3 = ui->short_L3->value(); ownKit.SOLT.short_m.L3 = ui->short_L3->value();
ownKit.SOLT.Load.Z0 = ui->load_Z0->value(); ownKit.SOLT.load_m.Z0 = ui->load_Z0->value();
ownKit.SOLT.Load.delay = ui->load_delay->value(); ownKit.SOLT.load_m.delay = ui->load_delay->value();
ownKit.SOLT.Load.Cparallel = ui->load_parC->value(); ownKit.SOLT.load_m.Cparallel = ui->load_parC->value();
ownKit.SOLT.Load.Lseries = ui->load_serL->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.Z0 = ui->through_Z0->value();
ownKit.SOLT.Through.delay = ui->through_delay->value(); ownKit.SOLT.Through.delay = ui->through_delay->value();
ownKit.SOLT.Through.loss = ui->through_loss->value(); ownKit.SOLT.Through.loss = ui->through_loss->value();
ownKit.SOLT.separate_male_female = ui->cbStandardDefinition->currentIndex() == 1;
// file // file
ownKit.SOLT.Open.file = ui->open_touchstone->getFilename(); ownKit.SOLT.open_m.file = ui->open_touchstone->getFilename();
ownKit.SOLT.Short.file = ui->short_touchstone->getFilename(); ownKit.SOLT.short_m.file = ui->short_touchstone->getFilename();
ownKit.SOLT.Load.file = ui->load_touchstone->getFilename(); ownKit.SOLT.load_m.file = ui->load_touchstone->getFilename();
ownKit.SOLT.Through.file = ui->through_touchstone->getFilename(); ownKit.SOLT.Through.file = ui->through_touchstone->getFilename();
ownKit.SOLT.Open.Sparam = ui->open_touchstone->getPorts()[0]; ownKit.SOLT.open_m.Sparam = ui->open_touchstone->getPorts()[0];
ownKit.SOLT.Short.Sparam = ui->short_touchstone->getPorts()[0]; ownKit.SOLT.short_m.Sparam = ui->short_touchstone->getPorts()[0];
ownKit.SOLT.Load.Sparam = ui->load_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.Sparam1 = ui->through_touchstone->getPorts()[0];
ownKit.SOLT.Through.Sparam2 = ui->through_touchstone->getPorts()[1]; 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 // TRL
ownKit.TRL.Through.Z0 = ui->TRL_through_Z0->value(); ownKit.TRL.Through.Z0 = ui->TRL_through_Z0->value();
ownKit.TRL.Reflection.isShort = ui->TRL_R_short->isChecked(); ownKit.TRL.Reflection.isShort = ui->TRL_R_short->isChecked();
@ -212,70 +308,124 @@ void CalkitDialog::updateEntries()
ui->description->setPlainText(ownKit.description); ui->description->setPlainText(ownKit.description);
// Coefficients // Coefficients
ui->open_Z0->setValueQuiet(ownKit.SOLT.Open.Z0); ui->open_Z0->setValueQuiet(ownKit.SOLT.open_m.Z0);
ui->open_delay->setValueQuiet(ownKit.SOLT.Open.delay); ui->open_delay->setValueQuiet(ownKit.SOLT.open_m.delay);
ui->open_loss->setValueQuiet(ownKit.SOLT.Open.loss); ui->open_loss->setValueQuiet(ownKit.SOLT.open_m.loss);
ui->open_C0->setValueQuiet(ownKit.SOLT.Open.C0); ui->open_C0->setValueQuiet(ownKit.SOLT.open_m.C0);
ui->open_C1->setValueQuiet(ownKit.SOLT.Open.C1); ui->open_C1->setValueQuiet(ownKit.SOLT.open_m.C1);
ui->open_C2->setValueQuiet(ownKit.SOLT.Open.C2); ui->open_C2->setValueQuiet(ownKit.SOLT.open_m.C2);
ui->open_C3->setValueQuiet(ownKit.SOLT.Open.C3); ui->open_C3->setValueQuiet(ownKit.SOLT.open_m.C3);
ui->short_Z0->setValueQuiet(ownKit.SOLT.Short.Z0); ui->short_Z0->setValueQuiet(ownKit.SOLT.short_m.Z0);
ui->short_delay->setValueQuiet(ownKit.SOLT.Short.delay); ui->short_delay->setValueQuiet(ownKit.SOLT.short_m.delay);
ui->short_loss->setValueQuiet(ownKit.SOLT.Short.loss); ui->short_loss->setValueQuiet(ownKit.SOLT.short_m.loss);
ui->short_L0->setValueQuiet(ownKit.SOLT.Short.L0); ui->short_L0->setValueQuiet(ownKit.SOLT.short_m.L0);
ui->short_L1->setValueQuiet(ownKit.SOLT.Short.L1); ui->short_L1->setValueQuiet(ownKit.SOLT.short_m.L1);
ui->short_L2->setValueQuiet(ownKit.SOLT.Short.L2); ui->short_L2->setValueQuiet(ownKit.SOLT.short_m.L2);
ui->short_L3->setValueQuiet(ownKit.SOLT.Short.L3); ui->short_L3->setValueQuiet(ownKit.SOLT.short_m.L3);
ui->load_Z0->setValueQuiet(ownKit.SOLT.Load.Z0); ui->load_Z0->setValueQuiet(ownKit.SOLT.load_m.Z0);
ui->load_delay->setValueQuiet(ownKit.SOLT.Load.delay); ui->load_delay->setValueQuiet(ownKit.SOLT.load_m.delay);
ui->load_parC->setValueQuiet(ownKit.SOLT.Load.Cparallel); ui->load_parC->setValueQuiet(ownKit.SOLT.load_m.Cparallel);
ui->load_serL->setValueQuiet(ownKit.SOLT.Load.Lseries); 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_Z0->setValueQuiet(ownKit.SOLT.Through.Z0);
ui->through_delay->setValueQuiet(ownKit.SOLT.Through.delay); ui->through_delay->setValueQuiet(ownKit.SOLT.Through.delay);
ui->through_loss->setValueQuiet(ownKit.SOLT.Through.loss); ui->through_loss->setValueQuiet(ownKit.SOLT.Through.loss);
// Measurements // Measurements
ui->open_touchstone->setFile(ownKit.SOLT.Open.file); ui->open_touchstone->setFile(ownKit.SOLT.open_m.file);
ui->open_touchstone->selectPort(0, ownKit.SOLT.Open.Sparam); ui->open_touchstone->selectPort(0, ownKit.SOLT.open_m.Sparam);
ui->short_touchstone->setFile(ownKit.SOLT.Short.file); ui->short_touchstone->setFile(ownKit.SOLT.short_m.file);
ui->short_touchstone->selectPort(0, ownKit.SOLT.Short.Sparam); ui->short_touchstone->selectPort(0, ownKit.SOLT.short_m.Sparam);
ui->load_touchstone->setFile(ownKit.SOLT.Load.file); ui->load_touchstone->setFile(ownKit.SOLT.load_m.file);
ui->load_touchstone->selectPort(0, ownKit.SOLT.Load.Sparam); 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->setFile(ownKit.SOLT.Through.file);
ui->through_touchstone->selectPort(0, ownKit.SOLT.Through.Sparam1); ui->through_touchstone->selectPort(0, ownKit.SOLT.Through.Sparam1);
ui->through_touchstone->selectPort(1, ownKit.SOLT.Through.Sparam2); ui->through_touchstone->selectPort(1, ownKit.SOLT.Through.Sparam2);
// Type // Type
if (ownKit.SOLT.Open.useMeasurements) { if (ownKit.SOLT.open_m.useMeasurements) {
ui->open_measurement->click(); ui->open_measurement->click();
} else { } else {
ui->open_coefficients->click(); ui->open_coefficients->click();
} }
if (ownKit.SOLT.Short.useMeasurements) { if (ownKit.SOLT.short_m.useMeasurements) {
ui->short_measurement->click(); ui->short_measurement->click();
} else { } else {
ui->short_coefficients->click(); ui->short_coefficients->click();
} }
if (ownKit.SOLT.Load.useMeasurements) { if (ownKit.SOLT.load_m.useMeasurements) {
ui->load_measurement->click(); ui->load_measurement->click();
} else { } else {
ui->load_coefficients->click(); 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) { if (ownKit.SOLT.Through.useMeasurements) {
ui->through_measurement->click(); ui->through_measurement->click();
} else { } else {
ui->through_coefficients->click(); ui->through_coefficients->click();
} }
if (ownKit.SOLT.separate_male_female) {
ui->cbStandardDefinition->setCurrentIndex(1);
} else {
ui->cbStandardDefinition->setCurrentIndex(0);
}
// TRL // TRL
ui->TRL_through_Z0->setValueQuiet(ownKit.TRL.Through.Z0); ui->TRL_through_Z0->setValueQuiet(ownKit.TRL.Through.Z0);
if(ownKit.TRL.Reflection.isShort) { if(ownKit.TRL.Reflection.isShort) {

View File

@ -20,7 +20,7 @@ public:
explicit CalkitDialog(Calkit &c, QWidget *parent = nullptr); explicit CalkitDialog(Calkit &c, QWidget *parent = nullptr);
~CalkitDialog(); ~CalkitDialog();
private: private:
void parseEntries(); void parseEntries();
void updateEntries(); void updateEntries();
Ui::CalkitDialog *ui; Ui::CalkitDialog *ui;

File diff suppressed because it is too large Load Diff

View File

@ -19,21 +19,46 @@ int MeasurementModel::rowCount(const QModelIndex &) const
int MeasurementModel::columnCount(const QModelIndex &) const int MeasurementModel::columnCount(const QModelIndex &) const
{ {
return ColIndexLast; return (int) ColIndex::Last;
} }
QVariant MeasurementModel::data(const QModelIndex &index, int role) const QVariant MeasurementModel::data(const QModelIndex &index, int role) const
{ {
auto info = cal->getMeasurementInfo(measurements[index.row()]); auto info = cal->getMeasurementInfo(measurements[index.row()]);
if(role == Qt::DisplayRole) { if(role == Qt::DisplayRole) {
switch(index.column()) { switch((ColIndex) index.column()) {
case ColIndexName: case ColIndex::Name:
return info.name; return info.name;
break; 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; return info.prerequisites;
break; break;
case ColIndexData: case ColIndex::Data:
if(info.points > 0) { if(info.points > 0) {
QString data = QString::number(info.points); QString data = QString::number(info.points);
data.append(" points from "); data.append(" points from ");
@ -45,16 +70,17 @@ QVariant MeasurementModel::data(const QModelIndex &index, int role) const
return "Not available"; return "Not available";
} }
break; break;
case ColIndexDate: case ColIndex::Date:
return info.timestamp.toString("dd.MM.yyyy hh:mm:ss"); return info.timestamp.toString("dd.MM.yyyy hh:mm:ss");
break; break;
} }
} else if(role == Qt::SizeHintRole) { } else if(role == Qt::SizeHintRole) {
switch(index.column()) { switch((ColIndex) index.column()) {
case ColIndexName: return 200; break; case ColIndex::Name: return 200; break;
case ColIndexDescription: return 500; break; case ColIndex::Gender: return 150; break;
case ColIndexData: return 300; break; case ColIndex::Description: return 500; break;
case ColIndexDate: return 300; break; case ColIndex::Data: return 300; break;
case ColIndex::Date: return 300; break;
default: return QVariant(); 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 QVariant MeasurementModel::headerData(int section, Qt::Orientation orientation, int role) const
{ {
if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) {
switch(section) { switch((ColIndex) section) {
case ColIndexName: return "Type"; break; case ColIndex::Name: return "Type"; break;
case ColIndexDescription: return "Prerequisites"; break; case ColIndex::Gender: return "Gender"; break;
case ColIndexData: return "Statistics"; break; case ColIndex::Description: return "Prerequisites"; break;
case ColIndexDate: return "Timestamp"; break; case ColIndex::Data: return "Statistics"; break;
case ColIndex::Date: return "Timestamp"; break;
default: return QVariant(); break; default: return QVariant(); break;
} }
} else { } else {
@ -83,6 +110,11 @@ void MeasurementModel::measurementUpdated(Calibration::Measurement m)
auto it = std::find(measurements.begin(), measurements.end(), m); auto it = std::find(measurements.begin(), measurements.end(), m);
if(it != measurements.end()) { if(it != measurements.end()) {
int row = it - measurements.begin(); 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 Q_OBJECT
public: public:
enum class ColIndex {
Name,
Gender,
Description,
Data,
Date,
Last
};
MeasurementModel(Calibration *cal, std::vector<Calibration::Measurement> measurements); MeasurementModel(Calibration *cal, std::vector<Calibration::Measurement> measurements);
int rowCount(const QModelIndex &parent = QModelIndex()) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@ -20,15 +29,9 @@ public:
public slots: public slots:
void measurementUpdated(Calibration::Measurement m); void measurementUpdated(Calibration::Measurement m);
void genderUpdated();
private: private:
enum {
ColIndexName,
ColIndexDescription,
ColIndexData,
ColIndexDate,
ColIndexLast
};
Calibration *cal; Calibration *cal;
std::vector<Calibration::Measurement> measurements; std::vector<Calibration::Measurement> measurements;
}; };

View File

@ -218,6 +218,7 @@ SOURCES += \
Traces/tracewidget.cpp \ Traces/tracewidget.cpp \
Traces/tracexyplot.cpp \ Traces/tracexyplot.cpp \
Traces/xyplotaxisdialog.cpp \ Traces/xyplotaxisdialog.cpp \
Util/util.cpp \
VNA/Deembedding/deembedding.cpp \ VNA/Deembedding/deembedding.cpp \
VNA/Deembedding/deembeddingdialog.cpp \ VNA/Deembedding/deembeddingdialog.cpp \
VNA/Deembedding/deembeddingoption.cpp \ VNA/Deembedding/deembeddingoption.cpp \
@ -298,10 +299,11 @@ FORMS += \
DISTFILES += DISTFILES +=
RESOURCES += \ RESOURCES += \
icons.qrc icons.qrc \
resources/librevna.qrc
CONFIG += c++17 CONFIG += c++17
REVISION = $$system(git rev-parse HEAD) REVISION = $$system(git rev-parse HEAD)
DEFINES += GITHASH=\\"\"$$REVISION\\"\" 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 DEFINES -= _UNICODE UNICODE

View File

@ -265,6 +265,13 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window)
// Set initial sweep settings // Set initial sweep settings
auto pref = Preferences::getInstance(); auto pref = Preferences::getInstance();
if(pref.Acquisition.useMedianAveraging) {
average.setMode(Averaging::Mode::Median);
} else {
average.setMode(Averaging::Mode::Mean);
}
if(pref.Startup.RememberSweepSettings) { if(pref.Startup.RememberSweepSettings) {
LoadSweepSettings(); LoadSweepSettings();
} else { } else {
@ -315,7 +322,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
tracking["enabled"] = settings.trackingGenerator ? true : false; tracking["enabled"] = settings.trackingGenerator ? true : false;
tracking["port"] = settings.trackingGeneratorPort ? 2 : 1; tracking["port"] = settings.trackingGeneratorPort ? 2 : 1;
tracking["offset"] = settings.trackingGeneratorOffset; tracking["offset"] = settings.trackingGeneratorOffset;
tracking["power"] = settings.trackingPower; tracking["power"] = (double) settings.trackingPower / 100.0; // convert to dBm
sweep["trackingGenerator"] = tracking; sweep["trackingGenerator"] = tracking;
if(normalize.active) { if(normalize.active) {
@ -413,6 +420,11 @@ using namespace std;
void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d) 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); d = average.process(d);
if(normalize.measuring) { if(normalize.measuring) {
@ -451,6 +463,11 @@ void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d)
UpdateAverageCount(); UpdateAverageCount();
markerModel->updateMarkers(); 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() void SpectrumAnalyzer::SettingsChanged()
@ -1065,6 +1082,11 @@ void SpectrumAnalyzer::updateGraphColors()
emit graphColorsChanged(); emit graphColorsChanged();
} }
void SpectrumAnalyzer::setAveragingMode(Averaging::Mode mode)
{
average.setMode(mode);
}
QString SpectrumAnalyzer::WindowToString(SpectrumAnalyzer::Window w) QString SpectrumAnalyzer::WindowToString(SpectrumAnalyzer::Window w)
{ {
switch(w) { switch(w) {

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,7 @@
#include <QKeyEvent> #include <QKeyEvent>
#include <QMenu> #include <QMenu>
#include <QPainter>
MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) : MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
QWidget(parent), QWidget(parent),
@ -12,6 +13,16 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
model(model) model(model)
{ {
ui->setupUi(this); 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->setModel(&model);
ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexTrace, new MarkerTraceDelegate); ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexTrace, new MarkerTraceDelegate);
ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexType, new MarkerTypeDelegate); ui->treeView->setItemDelegateForColumn(MarkerModel::ColIndexType, new MarkerTypeDelegate);
@ -47,7 +58,7 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
} }
// multiple markers selected, execute group context menu // multiple markers selected, execute group context menu
QMenu menu; QMenu menu;
auto createGroup = new QAction("Link selected"); auto createGroup = new QAction("Link selected", &menu);
connect(createGroup, &QAction::triggered, [&](){ connect(createGroup, &QAction::triggered, [&](){
auto g = model.createMarkerGroup(); auto g = model.createMarkerGroup();
// assign markers to group // assign markers to group
@ -57,7 +68,7 @@ MarkerWidget::MarkerWidget(MarkerModel &model, QWidget *parent) :
}); });
menu.addAction(createGroup); menu.addAction(createGroup);
if(anyInGroup) { if(anyInGroup) {
auto removeGroup = new QAction("Break Links"); auto removeGroup = new QAction("Break Links", &menu);
connect(removeGroup, &QAction::triggered, [&](){ connect(removeGroup, &QAction::triggered, [&](){
// remove selected markers from groups if they are already assigned to one // remove selected markers from groups if they are already assigned to one
for(auto m : selected) { for(auto m : selected) {
@ -123,6 +134,18 @@ void MarkerWidget::on_bAdd_clicked()
model.addMarker(marker); 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) bool MarkerWidget::eventFilter(QObject *, QEvent *event)
{ {
if (event->type() == QEvent::KeyPress) { 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 on_bAdd_clicked();
void updatePersistentEditors(); void updatePersistentEditors();
void on_bAddAll_clicked();
private: private:
bool eventFilter(QObject *obj, QEvent *event) override; bool eventFilter(QObject *obj, QEvent *event) override;
Ui::MarkerWidget *ui; Ui::MarkerWidget *ui;

View File

@ -44,7 +44,27 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <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>
<property name="text"> <property name="text">
<string/> <string/>
@ -64,7 +84,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Delete</string> <string>Delete marker</string>
</property> </property>
<property name="text"> <property name="text">
<string/> <string/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -74,6 +74,9 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
ret.explanationWidget = new QWidget(); ret.explanationWidget = new QWidget();
auto ui = new Ui::TimeDomainGatingExplanationWidget; auto ui = new Ui::TimeDomainGatingExplanationWidget;
ui->setupUi(ret.explanationWidget); ui->setupUi(ret.explanationWidget);
connect(ret.explanationWidget, &QWidget::destroyed, [=](){
delete ui;
});
} }
break; break;
default: default:
@ -84,7 +87,14 @@ TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
TraceMath::Data TraceMath::getSample(unsigned int index) 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) double TraceMath::getStepResponse(unsigned int index)

View File

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

View File

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

View File

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

View File

@ -17,22 +17,6 @@
using namespace std; 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) TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
: TracePlot(model, parent) : TracePlot(model, parent)
{ {
@ -333,8 +317,11 @@ void TraceXYPlot::updateContextMenu()
bool TraceXYPlot::dropSupported(Trace *t) bool TraceXYPlot::dropSupported(Trace *t)
{ {
Q_UNUSED(t) if(domainMatch(t) && !supported(t)) {
// all kind of traces can be dropped, the graph will be reconfigured to support the dropped trace if required // correct domain configured but Y axis do not match, prevent drop
return false;
}
// either directly compatible or domain change required
return true; return true;
} }
@ -457,7 +444,7 @@ void TraceXYPlot::draw(QPainter &p)
if(!t->isVisible()) { if(!t->isVisible()) {
continue; continue;
} }
pen = QPen(t->color(), 1); pen = QPen(t->color(), pref.Graphs.lineWidth);
pen.setCosmetic(true); pen.setCosmetic(true);
if(i == 1) { if(i == 1) {
pen.setStyle(Qt::DotLine); pen.setStyle(Qt::DotLine);
@ -705,6 +692,9 @@ void TraceXYPlot::updateAxisTicks()
double max = std::numeric_limits<double>::lowest(); double max = std::numeric_limits<double>::lowest();
double min = std::numeric_limits<double>::max(); double min = std::numeric_limits<double>::max();
for(auto t : tracesAxis[i]) { for(auto t : tracesAxis[i]) {
if(!t->isVisible()) {
continue;
}
unsigned int samples = t->size(); unsigned int samples = t->size();
for(unsigned int j=0;j<samples;j++) { for(unsigned int j=0;j<samples;j++) {
auto point = traceToCoordinate(t, j, YAxis[i].type); auto point = traceToCoordinate(t, j, YAxis[i].type);
@ -740,10 +730,14 @@ void TraceXYPlot::updateAxisTicks()
min -= range * 0.05; min -= range * 0.05;
max += range * 0.05; max += range * 0.05;
} }
YAxis[i].rangeMin = min; } else {
YAxis[i].rangeMax = max; // max/min still at default values, no valid samples are available for this axis, use default range
YAxis[i].rangeDiv = createAutomaticTicks(YAxis[i].ticks, min, max, 8); max = 1.0;
min = -1.0;
} }
YAxis[i].rangeMin = min;
YAxis[i].rangeMax = max;
YAxis[i].rangeDiv = createAutomaticTicks(YAxis[i].ticks, min, max, 8);
} }
} }
} }
@ -816,12 +810,14 @@ QString TraceXYPlot::AxisTypeToName(TraceXYPlot::YAxisType type)
case YAxisType::Capacitance: return "Capacitance"; case YAxisType::Capacitance: return "Capacitance";
case YAxisType::Inductance: return "Inductance"; case YAxisType::Inductance: return "Inductance";
case YAxisType::QualityFactor: return "Quality Factor"; case YAxisType::QualityFactor: return "Quality Factor";
case YAxisType::GroupDelay: return "Group delay";
case YAxisType::ImpulseReal: return "Impulse Response (Real)"; case YAxisType::ImpulseReal: return "Impulse Response (Real)";
case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)"; case YAxisType::ImpulseMag: return "Impulse Response (Magnitude)";
case YAxisType::Step: return "Step Response"; case YAxisType::Step: return "Step Response";
case YAxisType::Impedance: return "Impedance"; 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) 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) { switch(XAxis.type) {
case XAxisType::Frequency: case XAxisType::Frequency:
if(t->outputType() != Trace::DataType::Frequency) { return t->outputType() == Trace::DataType::Frequency;
return false;
}
break;
case XAxisType::Distance: case XAxisType::Distance:
case XAxisType::Time: case XAxisType::Time:
if(t->outputType() != Trace::DataType::Time) { return t->outputType() == Trace::DataType::Time;
return false;
}
break;
case XAxisType::Power: case XAxisType::Power:
if(t->outputType() != Trace::DataType::Power) { return t->outputType() == Trace::DataType::Power;
return false; case XAxisType::Last:
} return false;
break; }
default: return false;
break; }
bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
{
if(!domainMatch(t)) {
return false;
} }
switch(type) { switch(type) {
@ -890,6 +885,11 @@ bool TraceXYPlot::supported(Trace *t, TraceXYPlot::YAxisType type)
return false; return false;
} }
break; break;
case YAxisType::GroupDelay:
if(t->isReflection()) {
return false;
}
break;
default: default:
break; break;
} }
@ -939,6 +939,38 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, TraceXYPlo
case YAxisType::QualityFactor: case YAxisType::QualityFactor:
ret.setY(Util::SparamToQualityFactor(data.y)); ret.setY(Util::SparamToQualityFactor(data.y));
break; 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: case YAxisType::ImpulseReal:
ret.setY(real(data.y)); ret.setY(real(data.y));
break; break;
@ -1096,6 +1128,7 @@ QString TraceXYPlot::AxisUnit(TraceXYPlot::YAxisType type)
case TraceXYPlot::YAxisType::ImpulseMag: return "db"; case TraceXYPlot::YAxisType::ImpulseMag: return "db";
case TraceXYPlot::YAxisType::Step: return ""; case TraceXYPlot::YAxisType::Step: return "";
case TraceXYPlot::YAxisType::Impedance: return "Ohm"; case TraceXYPlot::YAxisType::Impedance: return "Ohm";
case TraceXYPlot::YAxisType::GroupDelay: return "s";
default: return ""; default: return "";
} }
} }

View File

@ -26,6 +26,7 @@ public:
Capacitance, Capacitance,
Inductance, Inductance,
QualityFactor, QualityFactor,
GroupDelay,
// TDR options // TDR options
ImpulseReal, ImpulseReal,
ImpulseMag, ImpulseMag,
@ -33,7 +34,7 @@ public:
Impedance, Impedance,
Last, Last,
}; };
static const std::set<YAxisType> YAxisTypes;
enum class XAxisType { enum class XAxisType {
Frequency, Frequency,
Time, Time,
@ -80,6 +81,7 @@ private:
YAxisType YAxisTypeFromName(QString name); YAxisType YAxisTypeFromName(QString name);
XAxisMode AxisModeFromName(QString name); XAxisMode AxisModeFromName(QString name);
void enableTraceAxis(Trace *t, int axis, bool enabled); void enableTraceAxis(Trace *t, int axis, bool enabled);
bool domainMatch(Trace *t);
bool supported(Trace *t) override; bool supported(Trace *t) override;
bool supported(Trace *t, YAxisType type); bool supported(Trace *t, YAxisType type);
QPointF traceToCoordinate(Trace *t, unsigned int sample, 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 type = (TraceXYPlot::XAxisType) XAxisIndex;
auto supported = supportedYAxis(type); 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 enable = supported.count(t) > 0;
auto index = (int) t; auto index = (int) t;
enableComboBoxItem(ui->Y1type, index, enable); 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::Capacitance);
ret.insert(TraceXYPlot::YAxisType::Inductance); ret.insert(TraceXYPlot::YAxisType::Inductance);
ret.insert(TraceXYPlot::YAxisType::QualityFactor); ret.insert(TraceXYPlot::YAxisType::QualityFactor);
ret.insert(TraceXYPlot::YAxisType::GroupDelay);
break; break;
case TraceXYPlot::XAxisType::Time: case TraceXYPlot::XAxisType::Time:
case TraceXYPlot::XAxisType::Distance: 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 <complex>
#include <math.h> #include <math.h>
#include <limits> #include <limits>
#include <vector>
#include <QColor> #include <QColor>
@ -55,6 +56,11 @@ namespace Util {
auto brightness = q.redF() * 0.299 + q.greenF() * 0.587 + q.blueF() * 0.114; auto brightness = q.redF() * 0.299 + q.greenF() * 0.587 + q.blueF() * 0.114;
return brightness > 0.6 ? Qt::black : Qt::white; 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 #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; auto ui = new Ui_DeembeddingMeasurementDialog;
measurementUI = ui; measurementUI = ui;
ui->setupUi(measurementDialog); ui->setupUi(measurementDialog);
connect(measurementDialog, &QDialog::finished, [=](){
delete ui;
});
// add the trace selector // add the trace selector
set<unsigned int> skip; set<unsigned int> skip;
@ -106,7 +109,8 @@ void Deembedding::startMeasurementDialog(bool S11, bool S12, bool S21, bool S22)
Deembedding::Deembedding(TraceModel &tm) Deembedding::Deembedding(TraceModel &tm)
: tm(tm), : tm(tm),
measuring(false) measuring(false),
sweepPoints(0)
{ {
} }

View File

@ -1,6 +1,8 @@
#include "matchingnetwork.h" #include "matchingnetwork.h"
#include "ui_matchingnetworkdialog.h" #include "ui_matchingnetworkdialog.h"
#include "unit.h"
#include "CustomWidgets/informationbox.h"
#include <QDialog> #include <QDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
@ -11,6 +13,7 @@
#include <QMimeData> #include <QMimeData>
#include <algorithm> #include <algorithm>
#include <QDebug> #include <QDebug>
#include <QFileDialog>
using namespace std; using namespace std;
@ -68,6 +71,9 @@ void MatchingNetwork::edit()
auto dialog = new QDialog(); auto dialog = new QDialog();
auto ui = new Ui::MatchingNetworkDialog(); auto ui = new Ui::MatchingNetworkDialog();
ui->setupUi(dialog); ui->setupUi(dialog);
connect(dialog, &QDialog::finished, [=](){
delete ui;
});
dialog->setModal(true); dialog->setModal(true);
graph = new QWidget(); graph = new QWidget();
@ -83,24 +89,25 @@ void MatchingNetwork::edit()
ui->lParallelC->installEventFilter(this); ui->lParallelC->installEventFilter(this);
ui->lParallelL->installEventFilter(this); ui->lParallelL->installEventFilter(this);
ui->lParallelR->installEventFilter(this); ui->lParallelR->installEventFilter(this);
ui->lDefinedThrough->installEventFilter(this);
layout->setContentsMargins(0,0,0,0); layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0); layout->setSpacing(0);
layout->addStretch(1); layout->addStretch(1);
auto p1 = new QWidget(); auto p1 = new QWidget();
p1->setMinimumSize(portWidth, 150); p1->setMinimumSize(portWidth, 151);
p1->setMaximumSize(portWidth, 150); p1->setMaximumSize(portWidth, 151);
p1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); p1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
p1->setStyleSheet("image: url(:/icons/port1.svg);"); p1->setStyleSheet("image: url(:/icons/port1.png);");
auto DUT = new QWidget(); auto DUT = new QWidget();
DUT->setMinimumSize(DUTWidth, 150); DUT->setMinimumSize(DUTWidth, 151);
DUT->setMaximumSize(DUTWidth, 150); DUT->setMaximumSize(DUTWidth, 151);
DUT->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); DUT->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
DUT->setStyleSheet("image: url(:/icons/DUT.svg);"); DUT->setStyleSheet("image: url(:/icons/DUT.png);");
auto p2 = new QWidget(); auto p2 = new QWidget();
p2->setMinimumSize(portWidth, 150); p2->setMinimumSize(portWidth, 151);
p2->setMaximumSize(portWidth, 150); p2->setMaximumSize(portWidth, 151);
p2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); p2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
p2->setStyleSheet("image: url(:/icons/port2.svg);"); p2->setStyleSheet("image: url(:/icons/port2.png);");
layout->addWidget(p1); layout->addWidget(p1);
for(auto w : p1Network) { for(auto w : p1Network) {
layout->addWidget(w); layout->addWidget(w);
@ -136,27 +143,31 @@ void MatchingNetwork::edit()
nlohmann::json MatchingNetwork::toJSON() nlohmann::json MatchingNetwork::toJSON()
{ {
nlohmann::json j; nlohmann::json j;
for(int i=0;i<2;i++) { nlohmann::json jn1, jn2;
auto network = i==0 ? p1Network : p2Network; for(auto c : p1Network) {
nlohmann::json jn; nlohmann::json jc;
for(auto c : network) { jc["component"] = c->getName().toStdString();
nlohmann::json jc; jc["params"] = c->toJSON();
jc["component"] = c->getName().toStdString(); jn1.push_back(jc);
jc["params"] = c->toJSON();
jn.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; return j;
} }
void MatchingNetwork::fromJSON(nlohmann::json j) void MatchingNetwork::fromJSON(nlohmann::json j)
{ {
for(int i=0;i<2;i++) { p1Network.clear();
auto jn = j[i]; p2Network.clear();
auto &network = i==0 ? p1Network : p2Network; if(j.contains("port1")) {
network.clear(); for(auto jc : j["port1"]) {
for(auto jc : jn) {
if(!jc.contains("component")) { if(!jc.contains("component")) {
continue; continue;
} }
@ -165,9 +176,23 @@ void MatchingNetwork::fromJSON(nlohmann::json j)
continue; continue;
} }
c->fromJSON(jc["params"]); 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(); matching.clear();
} }
@ -329,8 +354,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
dropComponent = (MatchingComponent*) dropPtr; dropComponent = (MatchingComponent*) dropPtr;
dragEvent->acceptProposedAction(); dragEvent->acceptProposedAction();
insertIndicator = new QWidget(); insertIndicator = new QWidget();
insertIndicator->setMinimumSize(2, 150); insertIndicator->setMinimumSize(2, imageHeight);
insertIndicator->setMaximumSize(2, 150); insertIndicator->setMaximumSize(2, imageHeight);
insertIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); insertIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
insertIndicator->setStyleSheet("background-color:red;"); insertIndicator->setStyleSheet("background-color:red;");
updateInsertIndicator(dragEvent->pos().x()); updateInsertIndicator(dragEvent->pos().x());
@ -368,6 +393,8 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelL); dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelL);
} else if(object->objectName() == "lParallelR") { } else if(object->objectName() == "lParallelR") {
dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelR); dragComponent = new MatchingComponent(MatchingComponent::Type::ParallelR);
} else if(object->objectName() == "lDefinedThrough") {
dragComponent = new MatchingComponent(MatchingComponent::Type::DefinedThrough);
} else { } else {
dragComponent = nullptr; dragComponent = nullptr;
} }
@ -397,18 +424,21 @@ bool MatchingNetwork::eventFilter(QObject *object, QEvent *event)
MatchingComponent::MatchingComponent(Type type) MatchingComponent::MatchingComponent(Type type)
{ {
this->type = type; this->type = type;
setMinimumSize(150, 150); eValue = nullptr;
setMaximumSize(150, 150); touchstone = nullptr;
touchstoneLabel = nullptr;
setMinimumSize(151, 151);
setMaximumSize(151, 151);
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 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); setFocusPolicy(Qt::FocusPolicy::ClickFocus);
switch(type) { switch(type) {
case Type::SeriesR: case Type::SeriesR:
case Type::SeriesL: case Type::SeriesL:
case Type::SeriesC: { case Type::SeriesC: {
eValue = new SIUnitEdit();
eValue->setPrecision(4);
eValue->setPrefixes("fpnum k");
connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged);
auto layout = new QVBoxLayout(); auto layout = new QVBoxLayout();
layout->addWidget(eValue); layout->addWidget(eValue);
setLayout(layout); setLayout(layout);
@ -417,13 +447,16 @@ MatchingComponent::MatchingComponent(Type type)
case Type::ParallelR: case Type::ParallelR:
case Type::ParallelL: case Type::ParallelL:
case Type::ParallelC: { case Type::ParallelC: {
eValue = new SIUnitEdit();
eValue->setPrecision(4);
eValue->setPrefixes("fpnum k");
connect(eValue, &SIUnitEdit::valueChanged, this, &MatchingComponent::valueChanged);
auto layout = new QVBoxLayout(); auto layout = new QVBoxLayout();
layout->addWidget(eValue); layout->addWidget(eValue);
layout->addStretch(1); layout->addStretch(1);
layout->setContentsMargins(9, 5, 9, 9); layout->setContentsMargins(9, 5, 9, 9);
setLayout(layout); setLayout(layout);
} }
break;
default: default:
break; break;
} }
@ -431,38 +464,58 @@ MatchingComponent::MatchingComponent(Type type)
case Type::SeriesR: case Type::SeriesR:
eValue->setUnit("Ω"); eValue->setUnit("Ω");
eValue->setValue(50); eValue->setValue(50);
setStyleSheet("image: url(:/icons/seriesR.svg);"); setStyleSheet("image: url(:/icons/seriesR.png);");
break; break;
case Type::SeriesL: case Type::SeriesL:
eValue->setUnit("H"); eValue->setUnit("H");
eValue->setValue(1e-9); eValue->setValue(1e-9);
setStyleSheet("image: url(:/icons/seriesL.svg);"); setStyleSheet("image: url(:/icons/seriesL.png);");
break; break;
case Type::SeriesC: case Type::SeriesC:
eValue->setUnit("F"); eValue->setUnit("F");
eValue->setValue(1e-12); eValue->setValue(1e-12);
setStyleSheet("image: url(:/icons/seriesC.svg);"); setStyleSheet("image: url(:/icons/seriesC.png);");
break; break;
case Type::ParallelR: case Type::ParallelR:
eValue->setUnit("Ω"); eValue->setUnit("Ω");
eValue->setValue(50); eValue->setValue(50);
setStyleSheet("image: url(:/icons/parallelR.svg);"); setStyleSheet("image: url(:/icons/parallelR.png);");
break; break;
case Type::ParallelL: case Type::ParallelL:
eValue->setUnit("H"); eValue->setUnit("H");
eValue->setValue(1e-9); eValue->setValue(1e-9);
setStyleSheet("image: url(:/icons/parallelL.svg);"); setStyleSheet("image: url(:/icons/parallelL.png);");
break; break;
case Type::ParallelC: case Type::ParallelC:
eValue->setUnit("F"); eValue->setUnit("F");
eValue->setValue(1e-12); 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; break;
default: default:
break; break;
} }
} }
MatchingComponent::~MatchingComponent()
{
delete eValue;
delete touchstone;
delete touchstoneLabel;
}
ABCDparam MatchingComponent::parameters(double freq) ABCDparam MatchingComponent::parameters(double freq)
{ {
switch(type) { 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); return ABCDparam(1.0, 0.0, 1.0/complex<double>(0, freq * 2 * M_PI * eValue->value()), 1.0);
case Type::ParallelC: case Type::ParallelC:
return ABCDparam(1.0, 0.0, 1.0/complex<double>(0, -1.0 / (freq * 2 * M_PI * eValue->value())), 1.0); 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: default:
return ABCDparam(1.0, 0.0, 0.0, 1.0); 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) void MatchingComponent::MatchingComponent::setValue(double v)
{ {
eValue->setValue(v); if(eValue) {
eValue->setValue(v);
}
} }
MatchingComponent *MatchingComponent::createFromName(QString name) MatchingComponent *MatchingComponent::createFromName(QString name)
@ -507,13 +571,73 @@ QString MatchingComponent::getName()
nlohmann::json MatchingComponent::toJSON() nlohmann::json MatchingComponent::toJSON()
{ {
nlohmann::json j; nlohmann::json j;
j["value"] = eValue->value(); 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; return j;
} }
void MatchingComponent::fromJSON(nlohmann::json j) void MatchingComponent::fromJSON(nlohmann::json j)
{ {
eValue->setValue(j.value("value", 1e-12)); 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) QString MatchingComponent::typeToName(MatchingComponent::Type type)
@ -525,6 +649,7 @@ QString MatchingComponent::typeToName(MatchingComponent::Type type)
case Type::ParallelR: return "ParallelR"; case Type::ParallelR: return "ParallelR";
case Type::ParallelL: return "ParallelL"; case Type::ParallelL: return "ParallelL";
case Type::ParallelC: return "ParallelC"; case Type::ParallelC: return "ParallelC";
case Type::DefinedThrough: return "Touchstone Through";
default: return ""; default: return "";
} }
} }

View File

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

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>772</width> <width>772</width>
<height>442</height> <height>443</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -131,20 +131,8 @@
<property name="title"> <property name="title">
<string>Matching Components</string> <string>Matching Components</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_10"> <layout class="QGridLayout" name="gridLayout">
<property name="leftMargin"> <item row="0" column="0" colspan="2">
<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="label_9"> <widget class="QLabel" name="label_9">
<property name="font"> <property name="font">
<font> <font>
@ -159,369 +147,423 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="1" column="0">
<layout class="QGridLayout" name="gridLayout"> <widget class="QFrame" name="frame">
<item row="0" column="0"> <property name="sizePolicy">
<widget class="QFrame" name="frame"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<property name="sizePolicy"> <horstretch>0</horstretch>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <verstretch>0</verstretch>
<horstretch>0</horstretch> </sizepolicy>
<verstretch>0</verstretch> </property>
</sizepolicy> <property name="minimumSize">
</property> <size>
<property name="minimumSize"> <width>80</width>
<size> <height>80</height>
<width>80</width> </size>
<height>80</height> </property>
</size> <property name="frameShape">
</property> <enum>QFrame::StyledPanel</enum>
<property name="frameShape"> </property>
<enum>QFrame::StyledPanel</enum> <property name="frameShadow">
</property> <enum>QFrame::Raised</enum>
<property name="frameShadow"> </property>
<enum>QFrame::Raised</enum> <layout class="QVBoxLayout" name="verticalLayout_2">
</property> <property name="spacing">
<layout class="QVBoxLayout" name="verticalLayout_2"> <number>0</number>
<property name="spacing"> </property>
<number>0</number> <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="lSeriesC">
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/seriesC.svg);</string>
</property> </property>
<property name="leftMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="topMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <string>Series C</string>
</property> </property>
<property name="bottomMargin"> <property name="alignment">
<number>0</number> <set>Qt::AlignCenter</set>
</property> </property>
<item> </widget>
<widget class="QLabel" name="lSeriesC"> </item>
<property name="styleSheet"> </layout>
<string notr="true">border-image: url(:/icons/seriesC.svg);</string> </widget>
</property> </item>
<property name="frameShape"> <item row="1" column="1">
<enum>QFrame::NoFrame</enum> <widget class="QFrame" name="frame_2">
</property> <property name="sizePolicy">
<property name="frameShadow"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<enum>QFrame::Plain</enum> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="text"> </sizepolicy>
<string>Series C</string> </property>
</property> <property name="minimumSize">
<property name="alignment"> <size>
<set>Qt::AlignCenter</set> <width>80</width>
</property> <height>80</height>
</widget> </size>
</item> </property>
</layout> <property name="frameShape">
</widget> <enum>QFrame::StyledPanel</enum>
</item> </property>
<item row="0" column="1"> <property name="frameShadow">
<widget class="QFrame" name="frame_2"> <enum>QFrame::Raised</enum>
<property name="sizePolicy"> </property>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <layout class="QVBoxLayout" name="verticalLayout_3">
<horstretch>0</horstretch> <property name="spacing">
<verstretch>0</verstretch> <number>0</number>
</sizepolicy> </property>
</property> <property name="leftMargin">
<property name="minimumSize"> <number>0</number>
<size> </property>
<width>80</width> <property name="topMargin">
<height>80</height> <number>0</number>
</size> </property>
</property> <property name="rightMargin">
<property name="frameShape"> <number>0</number>
<enum>QFrame::StyledPanel</enum> </property>
</property> <property name="bottomMargin">
<property name="frameShadow"> <number>0</number>
<enum>QFrame::Raised</enum> </property>
</property> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <widget class="QLabel" name="lSeriesL">
<property name="spacing"> <property name="styleSheet">
<number>0</number> <string notr="true">border-image: url(:/icons/seriesL.svg);</string>
</property> </property>
<property name="leftMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="topMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <string>Series L</string>
</property> </property>
<property name="bottomMargin"> <property name="alignment">
<number>0</number> <set>Qt::AlignCenter</set>
</property> </property>
<item> </widget>
<widget class="QLabel" name="lSeriesL"> </item>
<property name="styleSheet"> </layout>
<string notr="true">border-image: url(:/icons/seriesL.svg);</string> </widget>
</property> </item>
<property name="frameShape"> <item row="1" column="2">
<enum>QFrame::NoFrame</enum> <widget class="QFrame" name="frame_3">
</property> <property name="sizePolicy">
<property name="frameShadow"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<enum>QFrame::Plain</enum> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="text"> </sizepolicy>
<string>Series L</string> </property>
</property> <property name="minimumSize">
<property name="alignment"> <size>
<set>Qt::AlignCenter</set> <width>80</width>
</property> <height>80</height>
</widget> </size>
</item> </property>
</layout> <property name="frameShape">
</widget> <enum>QFrame::StyledPanel</enum>
</item> </property>
<item row="0" column="2"> <property name="frameShadow">
<widget class="QFrame" name="frame_3"> <enum>QFrame::Raised</enum>
<property name="sizePolicy"> </property>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <layout class="QVBoxLayout" name="verticalLayout_4">
<horstretch>0</horstretch> <property name="spacing">
<verstretch>0</verstretch> <number>0</number>
</sizepolicy> </property>
</property> <property name="leftMargin">
<property name="minimumSize"> <number>0</number>
<size> </property>
<width>80</width> <property name="topMargin">
<height>80</height> <number>0</number>
</size> </property>
</property> <property name="rightMargin">
<property name="frameShape"> <number>0</number>
<enum>QFrame::StyledPanel</enum> </property>
</property> <property name="bottomMargin">
<property name="frameShadow"> <number>0</number>
<enum>QFrame::Raised</enum> </property>
</property> <item>
<layout class="QVBoxLayout" name="verticalLayout_4"> <widget class="QLabel" name="lSeriesR">
<property name="spacing"> <property name="styleSheet">
<number>0</number> <string notr="true">border-image: url(:/icons/seriesR.svg);</string>
</property> </property>
<property name="leftMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="topMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <string>Series R</string>
</property> </property>
<property name="bottomMargin"> <property name="alignment">
<number>0</number> <set>Qt::AlignCenter</set>
</property> </property>
<item> </widget>
<widget class="QLabel" name="lSeriesR"> </item>
<property name="styleSheet"> </layout>
<string notr="true">border-image: url(:/icons/seriesR.svg);</string> </widget>
</property> </item>
<property name="frameShape"> <item row="1" column="3">
<enum>QFrame::NoFrame</enum> <widget class="QFrame" name="frame_7">
</property> <property name="sizePolicy">
<property name="frameShadow"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<enum>QFrame::Plain</enum> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="text"> </sizepolicy>
<string>Series R</string> </property>
</property> <property name="minimumSize">
<property name="alignment"> <size>
<set>Qt::AlignCenter</set> <width>80</width>
</property> <height>80</height>
</widget> </size>
</item> </property>
</layout> <property name="frameShape">
</widget> <enum>QFrame::StyledPanel</enum>
</item> </property>
<item row="1" column="0"> <property name="frameShadow">
<widget class="QFrame" name="frame_4"> <enum>QFrame::Raised</enum>
<property name="sizePolicy"> </property>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <layout class="QVBoxLayout" name="verticalLayout_10">
<horstretch>0</horstretch> <property name="spacing">
<verstretch>0</verstretch> <number>0</number>
</sizepolicy> </property>
</property> <property name="leftMargin">
<property name="minimumSize"> <number>0</number>
<size> </property>
<width>80</width> <property name="topMargin">
<height>80</height> <number>0</number>
</size> </property>
</property> <property name="rightMargin">
<property name="maximumSize"> <number>0</number>
<size> </property>
<width>80</width> <property name="bottomMargin">
<height>80</height> <number>0</number>
</size> </property>
</property> <item>
<property name="frameShape"> <widget class="QLabel" name="lDefinedThrough">
<enum>QFrame::StyledPanel</enum> <property name="styleSheet">
</property> <string notr="true">border-image: url(:/icons/definedThrough.svg);</string>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property> </property>
<property name="leftMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="topMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <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>
<property name="bottomMargin"> <property name="alignment">
<number>0</number> <set>Qt::AlignHCenter|Qt::AlignTop</set>
</property> </property>
<item> </widget>
<widget class="QLabel" name="lParallelC"> </item>
<property name="styleSheet"> </layout>
<string notr="true">border-image: url(:/icons/parallelC.svg);</string> </widget>
</property> </item>
<property name="frameShape"> <item row="2" column="0">
<enum>QFrame::NoFrame</enum> <widget class="QFrame" name="frame_4">
</property> <property name="sizePolicy">
<property name="frameShadow"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<enum>QFrame::Plain</enum> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="text"> </sizepolicy>
<string>Parallel C</string> </property>
</property> <property name="minimumSize">
<property name="alignment"> <size>
<set>Qt::AlignHCenter|Qt::AlignTop</set> <width>80</width>
</property> <height>80</height>
</widget> </size>
</item> </property>
</layout> <property name="maximumSize">
</widget> <size>
</item> <width>80</width>
<item row="1" column="1"> <height>80</height>
<widget class="QFrame" name="frame_5"> </size>
<property name="sizePolicy"> </property>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <property name="frameShape">
<horstretch>0</horstretch> <enum>QFrame::StyledPanel</enum>
<verstretch>0</verstretch> </property>
</sizepolicy> <property name="frameShadow">
</property> <enum>QFrame::Raised</enum>
<property name="minimumSize"> </property>
<size> <layout class="QVBoxLayout" name="verticalLayout_5">
<width>80</width> <property name="spacing">
<height>80</height> <number>0</number>
</size> </property>
</property> <property name="leftMargin">
<property name="frameShape"> <number>0</number>
<enum>QFrame::StyledPanel</enum> </property>
</property> <property name="topMargin">
<property name="frameShadow"> <number>0</number>
<enum>QFrame::Raised</enum> </property>
</property> <property name="rightMargin">
<layout class="QVBoxLayout" name="verticalLayout_6"> <number>0</number>
<property name="spacing"> </property>
<number>0</number> <property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lParallelC">
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/parallelC.svg);</string>
</property> </property>
<property name="leftMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="topMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="rightMargin"> <property name="text">
<number>0</number> <string>Parallel C</string>
</property> </property>
<property name="bottomMargin"> <property name="alignment">
<number>0</number> <set>Qt::AlignHCenter|Qt::AlignTop</set>
</property> </property>
<item> </widget>
<widget class="QLabel" name="lParallelL"> </item>
<property name="minimumSize"> </layout>
<size> </widget>
<width>0</width> </item>
<height>0</height> <item row="2" column="1">
</size> <widget class="QFrame" name="frame_5">
</property> <property name="sizePolicy">
<property name="styleSheet"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<string notr="true">border-image: url(:/icons/parallelL.svg);</string> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="frameShape"> </sizepolicy>
<enum>QFrame::NoFrame</enum> </property>
</property> <property name="minimumSize">
<property name="frameShadow"> <size>
<enum>QFrame::Plain</enum> <width>80</width>
</property> <height>80</height>
<property name="text"> </size>
<string>Parallel L</string> </property>
</property> <property name="frameShape">
<property name="alignment"> <enum>QFrame::StyledPanel</enum>
<set>Qt::AlignHCenter|Qt::AlignTop</set> </property>
</property> <property name="frameShadow">
</widget> <enum>QFrame::Raised</enum>
</item> </property>
</layout> <layout class="QVBoxLayout" name="verticalLayout_6">
</widget> <property name="spacing">
</item> <number>0</number>
<item row="1" column="2"> </property>
<widget class="QFrame" name="frame_6"> <property name="leftMargin">
<property name="sizePolicy"> <number>0</number>
<sizepolicy hsizetype="Fixed" vsizetype="Fixed"> </property>
<horstretch>0</horstretch> <property name="topMargin">
<verstretch>0</verstretch> <number>0</number>
</sizepolicy> </property>
</property> <property name="rightMargin">
<property name="minimumSize"> <number>0</number>
<size> </property>
<width>80</width> <property name="bottomMargin">
<height>80</height> <number>0</number>
</size> </property>
</property> <item>
<property name="frameShape"> <widget class="QLabel" name="lParallelL">
<enum>QFrame::StyledPanel</enum> <property name="minimumSize">
</property> <size>
<property name="frameShadow"> <width>0</width>
<enum>QFrame::Raised</enum> <height>0</height>
</property> </size>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="spacing">
<number>0</number>
</property> </property>
<property name="leftMargin"> <property name="styleSheet">
<number>0</number> <string notr="true">border-image: url(:/icons/parallelL.svg);</string>
</property> </property>
<property name="topMargin"> <property name="frameShape">
<number>0</number> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="rightMargin"> <property name="frameShadow">
<number>0</number> <enum>QFrame::Plain</enum>
</property> </property>
<property name="bottomMargin"> <property name="text">
<number>0</number> <string>Parallel L</string>
</property> </property>
<item> <property name="alignment">
<widget class="QLabel" name="lParallelR"> <set>Qt::AlignHCenter|Qt::AlignTop</set>
<property name="styleSheet"> </property>
<string notr="true">border-image: url(:/icons/parallelR.svg);</string> </widget>
</property> </item>
<property name="frameShape"> </layout>
<enum>QFrame::NoFrame</enum> </widget>
</property> </item>
<property name="frameShadow"> <item row="2" column="2">
<enum>QFrame::Plain</enum> <widget class="QFrame" name="frame_6">
</property> <property name="sizePolicy">
<property name="text"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<string>Parallel R</string> <horstretch>0</horstretch>
</property> <verstretch>0</verstretch>
<property name="alignment"> </sizepolicy>
<set>Qt::AlignHCenter|Qt::AlignTop</set> </property>
</property> <property name="minimumSize">
</widget> <size>
</item> <width>80</width>
</layout> <height>80</height>
</widget> </size>
</item> </property>
</layout> <property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<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="lParallelR">
<property name="styleSheet">
<string notr="true">border-image: url(:/icons/parallelR.svg);</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="text">
<string>Parallel R</string>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
</layout> </layout>
</widget> </widget>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -78,6 +78,8 @@ AppWindow::AppWindow(QWidget *parent)
// qDebug().setVerbosity(0); // qDebug().setVerbosity(0);
qDebug() << "Application start"; qDebug() << "Application start";
this->setWindowIcon(QIcon(":/app/logo.png"));
parser.setApplicationDescription(qlibrevnaApp->applicationName()); parser.setApplicationDescription(qlibrevnaApp->applicationName());
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
@ -202,6 +204,14 @@ AppWindow::AppWindow(QWidget *parent)
vna->updateGraphColors(); 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, [=](){ connect(ui->actionAbout, &QAction::triggered, [=](){

View File

@ -5,6 +5,7 @@ using namespace std;
Averaging::Averaging() Averaging::Averaging()
{ {
averages = 1; averages = 1;
mode = Mode::Mean;
} }
void Averaging::reset(unsigned int points) void Averaging::reset(unsigned int points)
@ -45,18 +46,57 @@ Protocol::Datapoint Averaging::process(Protocol::Datapoint d)
deque->pop_front(); deque->pop_front();
} }
// calculate average switch(mode) {
complex<double> sum[4]; case Mode::Mean: {
for(auto s : *deque) { // calculate average
sum[0] += s[0]; complex<double> sum[4];
sum[1] += s[1]; for(auto s : *deque) {
sum[2] += s[2]; sum[0] += s[0];
sum[3] += s[3]; sum[1] += s[1];
sum[2] += s[2];
sum[3] += s[3];
}
S11 = sum[0] / (double) (deque->size());
S12 = sum[1] / (double) (deque->size());
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;
} }
S11 = sum[0] / (double) (deque->size());
S12 = sum[1] / (double) (deque->size());
S21 = sum[2] / (double) (deque->size());
S22 = sum[3] / (double) (deque->size());
} }
d.real_S11 = S11.real(); d.real_S11 = S11.real();
@ -90,16 +130,40 @@ Protocol::SpectrumAnalyzerResult Averaging::process(Protocol::SpectrumAnalyzerRe
deque->pop_front(); deque->pop_front();
} }
// calculate average switch(mode) {
complex<double> sum[4]; case Mode::Mean: {
for(auto s : *deque) { // calculate average
sum[0] += s[0]; complex<double> sum[2];
sum[1] += s[1]; for(auto s : *deque) {
sum[2] += s[2]; sum[0] += s[0];
sum[3] += s[3]; sum[1] += s[1];
}
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;
} }
d.port1 = abs(sum[0] / (double) (deque->size()));
d.port2 = abs(sum[1] / (double) (deque->size()));
} }
return d; return d;
@ -122,3 +186,13 @@ unsigned int Averaging::currentSweep()
return 0; 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 class Averaging
{ {
public: public:
enum class Mode {
Mean,
Median
};
Averaging(); Averaging();
void reset(unsigned int points); void reset(unsigned int points);
void setAverages(unsigned int a); 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 // 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 // Returned values are in range 0 (when no data has been added yet) to averages
unsigned int currentSweep(); unsigned int currentSweep();
Mode getMode() const;
void setMode(const Mode &value);
private: private:
std::vector<std::deque<std::array<std::complex<double>, 4>>> avg; std::vector<std::deque<std::array<std::complex<double>, 4>>> avg;
int maxPoints; int maxPoints;
unsigned int averages; unsigned int averages;
Mode mode;
}; };
#endif // AVERAGING_H #endif // AVERAGING_H

View File

@ -50,5 +50,13 @@
<file>icons/down.png</file> <file>icons/down.png</file>
<file>icons/up.png</file> <file>icons/up.png</file>
<file>icons/chainlink.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> </qresource>
</RCC> </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"?> <?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"> <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> <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"> <clipPath id="clip1">
<path d="M 28 28 L 56.691406 28 L 56.691406 29 L 28 29 Z M 28 28 "/> <path d="M 28 28 L 56.691406 28 L 56.691406 29 L 28 29 Z M 28 28 "/>
</clipPath> </clipPath>
@ -12,6 +32,17 @@
<g clip-path="url(#clip1)" clip-rule="nonzero"> <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)"/> <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>
<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"> <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)"/> <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> </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"?> <?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"> <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> <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"> <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 "/> <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> </clipPath>
</defs> </defs>
<g id="surface1"> <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)"/> <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"> <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)"/> <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> </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.harmonicMixing = ui->AcquisitionUseHarmonic->isChecked();
p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked(); p->Acquisition.useDFTinSAmode = ui->AcquisitionUseDFT->isChecked();
p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value(); p->Acquisition.RBWLimitForDFT = ui->AcquisitionDFTlimitRBW->value();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Graphs.Color.background = ui->GraphsColorBackground->getColor(); p->Graphs.Color.background = ui->GraphsColorBackground->getColor();
p->Graphs.Color.axis = ui->GraphsColorAxis->getColor(); p->Graphs.Color.axis = ui->GraphsColorAxis->getColor();
p->Graphs.Color.Ticks.Background.enabled = ui->GraphsColorTicksBackgroundEnabled->isChecked(); 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.domainChangeBehavior = (GraphDomainChangeBehavior) ui->GraphsDomainChangeBehavior->currentIndex();
p->Graphs.markerBehavior.showDataOnGraphs = ui->GraphsShowMarkerData->isChecked(); p->Graphs.markerBehavior.showDataOnGraphs = ui->GraphsShowMarkerData->isChecked();
p->Graphs.markerBehavior.showAllData = ui->GraphsShowAllMarkerData->isChecked(); p->Graphs.markerBehavior.showAllData = ui->GraphsShowAllMarkerData->isChecked();
p->Graphs.lineWidth = ui->GraphsLineWidth->value();
p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked(); p->SCPIServer.enabled = ui->SCPIServerEnabled->isChecked();
p->SCPIServer.port = ui->SCPIServerPort->value(); p->SCPIServer.port = ui->SCPIServerPort->value();
accept(); accept();
@ -199,6 +201,7 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionUseHarmonic->setChecked(p->Acquisition.harmonicMixing); ui->AcquisitionUseHarmonic->setChecked(p->Acquisition.harmonicMixing);
ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode); ui->AcquisitionUseDFT->setChecked(p->Acquisition.useDFTinSAmode);
ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT); ui->AcquisitionDFTlimitRBW->setValue(p->Acquisition.RBWLimitForDFT);
ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0);
ui->GraphsColorBackground->setColor(p->Graphs.Color.background); ui->GraphsColorBackground->setColor(p->Graphs.Color.background);
ui->GraphsColorAxis->setColor(p->Graphs.Color.axis); ui->GraphsColorAxis->setColor(p->Graphs.Color.axis);
@ -208,6 +211,8 @@ void PreferencesDialog::setInitialGUIState()
ui->GraphsDomainChangeBehavior->setCurrentIndex((int) p->Graphs.domainChangeBehavior); ui->GraphsDomainChangeBehavior->setCurrentIndex((int) p->Graphs.domainChangeBehavior);
ui->GraphsShowMarkerData->setChecked(p->Graphs.markerBehavior.showDataOnGraphs); ui->GraphsShowMarkerData->setChecked(p->Graphs.markerBehavior.showDataOnGraphs);
ui->GraphsShowAllMarkerData->setChecked(p->Graphs.markerBehavior.showAllData); ui->GraphsShowAllMarkerData->setChecked(p->Graphs.markerBehavior.showAllData);
ui->GraphsLineWidth->setValue(p->Graphs.lineWidth);
ui->SCPIServerEnabled->setChecked(p->SCPIServer.enabled); ui->SCPIServerEnabled->setChecked(p->SCPIServer.enabled);
ui->SCPIServerPort->setValue(p->SCPIServer.port); ui->SCPIServerPort->setValue(p->SCPIServer.port);

View File

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

View File

@ -637,6 +637,36 @@
</layout> </layout>
</widget> </widget>
</item> </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> <item>
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer_2">
<property name="orientation"> <property name="orientation">
@ -739,6 +769,20 @@
</layout> </layout>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
</item> </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; 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 #ifndef TOUCHSTONE_H
#define TOUCHSTONE_H #define TOUCHSTONE_H
#include "savable.h"
#include <complex> #include <complex>
#include <vector> #include <vector>
#include <string> #include <string>
#include <QString> #include <QString>
class Touchstone class Touchstone : public Savable
{ {
public: public:
enum class Scale { enum class Scale {
@ -29,6 +31,7 @@ public:
}; };
Touchstone(unsigned int m_ports); Touchstone(unsigned int m_ports);
virtual ~Touchstone(){};
void AddDatapoint(Datapoint p); void AddDatapoint(Datapoint p);
void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary); void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
std::stringstream toString(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; } unsigned int ports() { return m_ports; }
QString getFilename() const; QString getFilename() const;
virtual nlohmann::json toJSON();
virtual void fromJSON(nlohmann::json j);
private: private:
unsigned int m_ports; unsigned int m_ports;
std::vector<Datapoint> m_datapoints; std::vector<Datapoint> m_datapoints;

View File

@ -189,7 +189,7 @@
<listOptionValue builtIn="false" value="FW_MAJOR=1"/> <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"/> <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-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"/> <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-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"/> <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); 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) { if(approx.denom == 1) {
// M value must be at least 2 // M value must be at least 2
approx.denom = 2; approx.denom = 2;

View File

@ -300,6 +300,10 @@ void SA::Work() {
if(!s.SignalID || signalIDstep >= signalIDsteps - 1) { if(!s.SignalID || signalIDstep >= signalIDsteps - 1) {
// this measurement point is done, handle result according to detector // this measurement point is done, handle result according to detector
for(uint16_t i=0;i<DFTpoints;i++) { 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; uint16_t binIndex = (pointCnt + i) / binSize;
uint32_t pointInBin = (pointCnt + i) % binSize; uint32_t pointInBin = (pointCnt + i) % binSize;
bool lastPointInBin = pointInBin >= binSize - 1; bool lastPointInBin = pointInBin >= binSize - 1;
@ -386,7 +390,7 @@ void SA::Work() {
Communication::Send(packet); Communication::Send(packet);
} }
if(pointCnt < points - DFTpoints) { if(pointCnt + DFTpoints < points) {
pointCnt += DFTpoints; pointCnt += DFTpoints;
} else { } else {
pointCnt = 0; 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 // Configure LO2 for the changed IF1. This is not necessary right now but it will generate
// the correct clock settings // the correct clock settings
last_LO2 = actualFirstIF - HW::IF2; 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), 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 { } else {
// last entry in IF table, revert LO2 to default // last entry in IF table, revert LO2 to default
last_LO2 = HW::IF1 - HW::IF2; last_LO2 = HW::IF1 - HW::IF2;

View File

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