Compare commits

...

10 commits

Author SHA1 Message Date
Jan Käberich 6a7b985fa9 Refactor Calkit::fromFile without the need for move semantics
Some checks failed
Build / PC_Application_Ubuntu (push) Has been cancelled
Build / PC_Application_RPi5 (push) Has been cancelled
Build / PC_Application_Windows (push) Has been cancelled
Build / PC_Application_OSX (push) Has been cancelled
Build / PC_Application_OSX_13 (push) Has been cancelled
Build / Embedded_Firmware (push) Has been cancelled
HIL_Tests / Get_Repository (push) Has been cancelled
Unit_Tests / Tests (push) Has been cancelled
HIL_Tests / PC_Application_RPi5 (push) Has been cancelled
HIL_Tests / Embedded_Firmware (push) Has been cancelled
HIL_Tests / HIL (push) Has been cancelled
2025-12-02 16:52:34 +01:00
Jan Käberich 7bdbcde9be SCPI API for editing calibration kit standards 2025-12-02 15:29:47 +01:00
Jan Käberich fbef4b364f fix ambiguous conversion error 2025-12-02 13:50:21 +01:00
Jan Käberich da1228c9b1 Document and fix deembedding SCPI API 2025-12-02 13:43:21 +01:00
Jan Käberich 3aac724298 prevent the combination of log sweep and start frequency = 0 2025-12-02 11:36:36 +01:00
Jan Käberich 0f9510e7a0 save number of averages in setup files 2025-12-02 11:30:16 +01:00
Jan Käberich 65bc247a65 SCPI API for querying the sweep cursor 2025-12-02 11:22:54 +01:00
Jan Käberich d0c8a1ae8e SCPI API for setting/reading calkit meta data 2025-12-02 11:05:51 +01:00
Jan Käberich 9923038d6c allow spaces in arguments of SCPI commands 2025-12-02 10:33:04 +01:00
Jan Käberich 72d547eb08 SCPI: support lower case for calkit file names 2025-12-02 10:08:06 +01:00
18 changed files with 816 additions and 62 deletions

View file

@ -123,6 +123,7 @@
\usepackage{xifthen}
\newcommand{\vna}{LibreVNA}
\newcommand{\gui}{\vna{}-GUI}
\newcommand{\subsubsubsection}[1]{\paragraph{#1}\mbox{}\\\\}
\newcommand{\screenshot}[2]{\begin{center}
\includegraphics[width=#1\textwidth]{Screenshots/#2}
@ -462,6 +463,21 @@ If single sweep is enabled, the acquisition is stopped when the required number
\item Issue the command again (i.e. VNA:ACQ:SINGLE TRUE always triggers a new sweep)
\end{itemize}
\subsubsection{VNA:ACQuisition:FREQuency}
\query{Returns the current frequency of the sweep}{VNA:ACQuisition:FREQuency?}{None}{<Frequency in Hz>}
This command only returns valid data when a sweep is running and the the sweep is a frequency sweep.
\subsubsection{VNA:ACQuisition:POWer}
\query{Returns the current power of the sweep}{VNA:ACQuisition:POWer?}{None}{<Power in dBm>}
This command only returns valid data when a sweep is running and the the sweep is a power sweep.
\subsubsection{VNA:ACQuisition:TIME}
\query{Returns the current time of the sweep}{VNA:ACQuisition:TIME?}{None}{<Time in seconds>}
This command only returns valid data when a sweep is running and the the sweep is a zero-span sweep.
\subsubsection{VNA:STIMulus:LVL}
\event{Sets the output power of the stimulus signal when sweep type is frequency}{VNA:STIMulus:LVL}{<power>, in dBm}
\query{Queries the currently selected output power}{VNA:STIMulus:LVL?}{None}{power in dBm}
@ -601,6 +617,9 @@ If no de-embedding is configured for the selected trace, enabling the de-embeddi
\hspace{1cm}LOAD\\
\hspace{1cm}THROUGH\\
\hspace{1cm}ISOLATION\\
\hspace{1cm}SLIDINGLOAD\\
\hspace{1cm}REFLECT\\
\hspace{1cm}LINE\\
{[<standard>]}, calibration kit standard name, optional\\}
\subsubsection{VNA:CALibration:TYPE}
@ -609,7 +628,10 @@ If no de-embedding is configured for the selected trace, enabling the de-embeddi
\hspace{1cm}SHORT\\
\hspace{1cm}LOAD\\
\hspace{1cm}THROUGH\\
\hspace{1cm}ISOLATION\\}
\hspace{1cm}ISOLATION\\
\hspace{1cm}SLIDINGLOAD\\
\hspace{1cm}REFLECT\\
\hspace{1cm}LINE\\}
\subsubsection{VNA:CALibration:PORT}
\event{Sets the port for the specified measurement}{VNA:CALibration:PORT}{<measurement number> <port number>}
@ -637,6 +659,23 @@ Important points when saving/loading calibration files through SCPI commands:
\subsubsection{VNA:CALibration:LOAD}
\query{Loads a calibration file}{VNA:CALibration:LOAD?}{<filename>}{TRUE or FALSE}
\subsubsection{VNA:CALibration:KIT:MANufacturer}
\event{Sets the manufacturer name of the calibration kit}{VNA:CALibration:KIT:MANufacturer}{<manufacturer>}
\query{Returns the manufacturer name of the calibration kit}{VNA:CALibration:KIT:MANufacturer?}{None}{<manufacturer>}
\subsubsection{VNA:CALibration:KIT:SERial}
\event{Sets the serial number of the calibration kit}{VNA:CALibration:KIT:SERial}{<serial number>}
\query{Returns the serial number of the calibration kit}{VNA:CALibration:KIT:SERial?}{None}{<serial number>}
\subsubsection{VNA:CALibration:KIT:DESCription}
\event{Sets the description of the calibration kit}{VNA:CALibration:KIT:DESCription}{<description>}
\query{Returns the description of the calibration kit}{VNA:CALibration:KIT:DESCription?}{None}{<description>}
\subsubsection{VNA:CALibration:KIT:FILEname}
\query{Returns the filename of the calibration kit}{VNA:CALibration:KIT:FILEname?}{None}{<filename>}
The filename is only available if the calibration kit was loaded from a dedicated file. If it was loaded as part of a calibration file or has not been loaded since the GUI started, no filename will be returned.
\subsubsection{VNA:CALibration:KIT:SAVE}
\event{Saves the active calibration kit to a file}{VNA:CALibration:KIT:SAVE}{<filename>}
Important points when saving/loading calibration kit files through SCPI commands:
@ -648,6 +687,343 @@ Important points when saving/loading calibration kit files through SCPI commands
\subsubsection{VNA:CALibration:KIT:LOAD}
\query{Loads a calibration kit file}{VNA:CALibration:KIT:LOAD?}{<filename>}{TRUE or FALSE}
\subsubsection{Common calibration kit standards commands}
The calibration kit supports an arbitrary amount of standards. This requires a flexible SCPI API which adjusts to the currently configured standards.
A few calibration kit standard commands are always available and they can be used to create and delete calibration standards. Every created calibration standard adds their own commands. They can be accessed by including the calibration standard number in the command (shown as ``x'' in the following commands).
\subsubsubsection{VNA:CALibration:KIT:STAndard:CLEAR}
\event{Resets the calibration kit to default values (ideal standards)}{VNA:CALibration:KIT:STAndard:CLEAR}{None}
\subsubsubsection{VNA:CALibration:KIT:STAndard:NUMber}
\query{Returns the number of configured calibration standards}{VNA:CALibration:KIT:STAndard:NUMber?}{None}{<number of calibration standards>}
\subsubsubsection{VNA:CALibration:KIT:STAndard:TYPE}
\query{Returns the type of a created calibration standard}{VNA:CALibration:KIT:STAndard:TYPE?}{<x> Number of calibration standard}{Type of standard, one of:\\
\hspace{1cm}Open\\
\hspace{1cm}Short\\
\hspace{1cm}Load\\
\hspace{1cm}Reflect\\
\hspace{1cm}Through\\
\hspace{1cm}Line\\}
\subsubsubsection{VNA:CALibration:KIT:STAndard:NEW}
\event{Adds a new calibration standard}{VNA:CALibration:KIT:STAndard:NEW}{<type> Type of calibration standard, one of:\\
\hspace{1cm}Open\\
\hspace{1cm}Short\\
\hspace{1cm}Load\\
\hspace{1cm}Reflect\\
\hspace{1cm}Through\\
\hspace{1cm}Line\\
<name> User selectable name of new standard}
\subsubsubsection{VNA:CALibration:KIT:STAndard:DELete}
\event{Removes a calibration standard}{VNA:CALibration:KIT:STAndard:DELete}{<x> Number of standard to delete}
\subsubsection{OPEN standard commands}
The following commands are only valid for a calibration standard of type ``Open''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:Z0}
\event{Sets the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0}{<Z> Characteristic impedance in Ohm}
\query{Queries the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0?}{None}{<Z> Characteristic impedance in Ohm}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:DELAY}
\event{Sets the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY}{<delay> Delay in \unit{\pico\second}}
\query{Queries the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY?}{None}{<delay> Delay in \unit{\pico\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:LOSS}
\event{Sets the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS}{<loss> Loss in \unit{\giga\ohm\per\second}}
\query{Queries the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS?}{None}{<loss> Loss in \unit{\giga\ohm\per\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:C0}
\event{Sets the fringing capacitance polynom term 0 of the standard}{VNA:CALibration:KIT:STAndard:x:C0}{<C0> in \num{d-15}\unit{\farad}}
\query{Queries the fringing capacitance polynom term 0 of the standard}{VNA:CALibration:KIT:STAndard:x:C0?}{None}{<C0> in \num{d-15}\unit{\farad}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:C1}
\event{Sets the fringing capacitance polynom term 1 of the standard}{VNA:CALibration:KIT:STAndard:x:C1}{<C1> in \num{d-27}\unit{\farad\per\hertz}}
\query{Queries the fringing capacitance polynom term 1 of the standard}{VNA:CALibration:KIT:STAndard:x:C1?}{None}{<C1> in \num{d-27}\unit{\farad\per\hertz}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:C2}
\event{Sets the fringing capacitance polynom term 2 of the standard}{VNA:CALibration:KIT:STAndard:x:C2}{<C2> in \num{d-36}\unit{\farad\per\hertz\squared}}
\query{Queries the fringing capacitance polynom term 2 of the standard}{VNA:CALibration:KIT:STAndard:x:C2?}{None}{<C2> in \num{d-36}\unit{\farad\per\hertz\squared}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:C3}
\event{Sets the fringing capacitance polynom term 3 of the standard}{VNA:CALibration:KIT:STAndard:x:C3}{<C3> in \num{d-45}\unit{\farad\per\hertz\cubed}}
\query{Queries the fringing capacitance polynom term 3 of the standard}{VNA:CALibration:KIT:STAndard:x:C3?}{None}{<C3> in \num{d-45}\unit{\farad\per\hertz\cubed}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:FILE}
\event{Loads the coefficients for the calibration standard from a touchstone file}{VNA:CALibration:KIT:STAndard:x:FILE}{<filename>\\<port> sets the port from the file which is used to define the standard, optional}
\subsubsection{SHORT standard commands}
The following commands are only valid for a calibration standard of type ``Short''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:Z0}
\event{Sets the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0}{<Z> Characteristic impedance in Ohm}
\query{Queries the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0?}{None}{<Z> Characteristic impedance in Ohm}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:DELAY}
\event{Sets the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY}{<delay> Delay in \unit{\pico\second}}
\query{Queries the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY?}{None}{<delay> Delay in \unit{\pico\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:LOSS}
\event{Sets the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS}{<loss> Loss in \unit{\giga\ohm\per\second}}
\query{Queries the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS?}{None}{<loss> Loss in \unit{\giga\ohm\per\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:L0}
\event{Sets the residual inductance polynom term 0 of the standard}{VNA:CALibration:KIT:STAndard:x:L0}{<L0> in \num{d-12}\unit{\henry}}
\query{Queries the residual inductance polynom term 0 of the standard}{VNA:CALibration:KIT:STAndard:x:L0?}{None}{<L0> in \num{d-12}\unit{\henry}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:L1}
\event{Sets the residual inductance polynom term 1 of the standard}{VNA:CALibration:KIT:STAndard:x:L1}{<L1> in \num{d-24}\unit{\henry\per\hertz}}
\query{Queries the residual inductance polynom term 1 of the standard}{VNA:CALibration:KIT:STAndard:x:L1?}{None}{<L1> in \num{d-24}\unit{\henry\per\hertz}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:L2}
\event{Sets the residual inductance polynom term 2 of the standard}{VNA:CALibration:KIT:STAndard:x:L2}{<L2> in \num{d-33}\unit{\henry\per\hertz\squared}}
\query{Queries the residual inductance polynom term 2 of the standard}{VNA:CALibration:KIT:STAndard:x:L2?}{None}{<L2> in \num{d-33}\unit{\henry\per\hertz\squared}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:L3}
\event{Sets the residual inductance polynom term 3 of the standard}{VNA:CALibration:KIT:STAndard:x:L3}{<L3> in \num{d-42}\unit{\henry\per\hertz\cubed}}
\query{Queries the residual inductance polynom term 3 of the standard}{VNA:CALibration:KIT:STAndard:x:L3?}{None}{<L3> in \num{d-42}\unit{\henry\per\hertz\cubed}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:FILE}
\event{Loads the coefficients for the calibration standard from a touchstone file}{VNA:CALibration:KIT:STAndard:x:FILE}{<filename>\\<port> sets the port from the file which is used to define the standard, optional}
\subsubsection{LOAD standard commands}
The following commands are only valid for a calibration standard of type ``Load''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:RESistance}
\event{Sets the resistance of the standard}{VNA:CALibration:KIT:STAndard:x:RESistance}{<R> in \unit{\ohm}}
\query{Queries the resistance of the standard}{VNA:CALibration:KIT:STAndard:x:RESistance?}{None}{<R> in \unit{\ohm}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:Z0}
\event{Sets the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0}{<Z> Characteristic impedance in Ohm}
\query{Queries the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0?}{None}{<Z> Characteristic impedance in Ohm}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:DELAY}
\event{Sets the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY}{<delay> Delay in \unit{\pico\second}}
\query{Queries the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY?}{None}{<delay> Delay in \unit{\pico\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:LOSS}
\event{Sets the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS}{<loss> Loss in \unit{\giga\ohm\per\second}}
\query{Queries the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS?}{None}{<loss> Loss in \unit{\giga\ohm\per\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:CARallel}
\event{Sets the residual capacitance polynom of the standard}{VNA:CALibration:KIT:STAndard:x:CARallel}{<C> in \unit{\farad}}
\query{Queries the residual capacitance of the standard}{VNA:CALibration:KIT:STAndard:x:CARallel?}{None}{<C> in \unit{\farad}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:LSERies}
\event{Sets the residual inductance of the standard}{VNA:CALibration:KIT:STAndard:x:LSERies}{<L> in \unit{\henry}}
\query{Queries the residual inductance of the standard}{VNA:CALibration:KIT:STAndard:x:LSERies?}{None}{<L> in \unit{\henry}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:CFIRST}
\event{Sets the order of residual capacitance/inductance}{VNA:CALibration:KIT:STAndard:x:CFIRST}{TRUE or FALSE}
\query{Queries the order of residual capacitance/inductance}{VNA:CALibration:KIT:STAndard:x:CFIRST?}{None}{TRUE or FALSE}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:FILE}
\event{Loads the coefficients for the calibration standard from a touchstone file}{VNA:CALibration:KIT:STAndard:x:FILE}{<filename>\\<port> sets the port from the file which is used to define the standard, optional}
\subsubsection{REFLECT standard commands}
The following commands are only valid for a calibration standard of type ``Reflect''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:SHORT}
\event{Sets the type of reflection standard}{VNA:CALibration:KIT:STAndard:x:SHORT}{TRUE for short standard or FALSE for open standard}
\query{Queries the type of reflection standard}{VNA:CALibration:KIT:STAndard:x:SHORT?}{None}{TRUE for short standard or FALSE for open standard}
\subsubsection{THROUGH standard commands}
The following commands are only valid for a calibration standard of type ``Through''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:Z0}
\event{Sets the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0}{<Z> Characteristic impedance in Ohm}
\query{Queries the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0?}{None}{<Z> Characteristic impedance in Ohm}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:DELAY}
\event{Sets the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY}{<delay> Delay in \unit{\pico\second}}
\query{Queries the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY?}{None}{<delay> Delay in \unit{\pico\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:LOSS}
\event{Sets the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS}{<loss> Loss in \unit{\giga\ohm\per\second}}
\query{Queries the loss of the standard}{VNA:CALibration:KIT:STAndard:x:LOSS?}{None}{<loss> Loss in \unit{\giga\ohm\per\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:FILE}
\event{Loads the coefficients for the calibration standard from a touchstone file}{VNA:CALibration:KIT:STAndard:x:FILE}{<filename>\\
<port1> selects the first port from the file which is used to define the standard\\
<port1> selects the second port from the file which is used to define the standard}
\subsubsection{LINE standard commands}
The following commands are only valid for a calibration standard of type ``Line''. The ``x'' in the command names must be replaced with the calibration standard number.
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:NAME}
\event{Sets the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME}{<name> User selectable name}
\query{Queries the name of the standard}{VNA:CALibration:KIT:STAndard:x:NAME?}{None}{<name> User selectable name}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:Z0}
\event{Sets the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0}{<Z> Characteristic impedance in Ohm}
\query{Queries the characteristic impedance of the standard}{VNA:CALibration:KIT:STAndard:x:Z0?}{None}{<Z> Characteristic impedance in Ohm}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:DELAY}
\event{Sets the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY}{<delay> Delay in \unit{\pico\second}}
\query{Queries the delay of the standard}{VNA:CALibration:KIT:STAndard:x:DELAY?}{None}{<delay> Delay in \unit{\pico\second}}
\subsubsubsection{VNA:CALibration:KIT:STAndard:x:FILE}
\event{Loads the coefficients for the calibration standard from a touchstone file}{VNA:CALibration:KIT:STAndard:x:FILE}{<filename>\\
<port1> selects the first port from the file which is used to define the standard\\
<port1> selects the second port from the file which is used to define the standard}
\subsubsection{Common Deembedding Commands}
The VNA supports various deembedding options which can also be configured through the SCPI API. Because the deembedding system is flexible and supports any number of deembedding options at the same time, the SCPI API is also flexible. Instead of fixed commands, the available commands change depending on the configured deembedding options.
A few deembedding commands are always available and they can be used to create, delete and swap deembedding options. Every created deembedding option adds their own commands. They can be accessed by including the option number in the command (shown as ``x'' in the following commands).
\subsubsubsection{VNA:DEEMBedding:NUMber}
\query{Returns the number of configured deembedding options}{VNA:DEEMBedding:NUMber?}{None}{<number of deembedding options>}
\subsubsubsection{VNA:DEEMBedding:TYPE}
\query{Returns the type of a configured deembedding option}{VNA:DEEMBedding:TYPE?}{<x> Number of deembedding option}{Type of option, one of:\\
\hspace{1cm}Port\_Extension\\
\hspace{1cm}2xThru\\
\hspace{1cm}Matching\_Network\\
\hspace{1cm}Impedance\_Renormalization\\}
\subsubsubsection{VNA:DEEMBedding:NEW}
\event{Adds a new deembedding option}{VNA:DEEMBedding:NEW}{<type> Type of option, one of:\\
\hspace{1cm}Port\_Extension\\
\hspace{1cm}2xThru\\
\hspace{1cm}Matching\_Network\\
\hspace{1cm}Impedance\_Renormalization\\}
\subsubsubsection{VNA:DEEMBedding:DELete}
\event{Removes a deembedding option}{VNA:DEEMBedding:DELete}{<x> Number of option to delete}
\subsubsubsection{VNA:DEEMBedding:SWAP}
\event{Swaps the order of two deembedding options}{VNA:DEEMBedding:SWAP}{<x1> Number of option 1 to swap\\<x2> Number of option 2 to swap}
\subsubsubsection{VNA:DEEMBedding:CLEAR}
\event{Delete all deembedding options}{VNA:DEEMBedding:CLEAR}{None}
\subsubsection{Port Extension Deembedding Commands}
The following commands are only valid for a deembedding option of type ``Port\_Extension''. The ``x'' in the command names must be replaced with the option number.
\subsubsubsection{VNA:DEEMBedding:x:PORT}
\event{Sets the port}{VNA:DEEMBedding:x:PORT}{<port>, 1...n}
\query{Queries the selected port}{VNA:DEEMBedding:x:PORT?}{None}{<port>, 1...n}
\subsubsubsection{VNA:DEEMBedding:x:DELAY}
\event{Sets the delay of the port extension}{VNA:DEEMBedding:x:DELAY}{<delay> Delay in seconds}
\query{Queries the delay of the port extension}{VNA:DEEMBedding:x:DELAY?}{None}{<delay> Delay in seconds}
\subsubsubsection{VNA:DEEMBedding:x:DCLOSS}
\event{Sets the DC loss of the port extension}{VNA:DEEMBedding:x:DCLOSS}{<loss> Loss at DC in dB}
\query{Queries the DC loss of the port extension}{VNA:DEEMBedding:x:DCLOSS?}{None}{<loss> Loss at DC in dB}
\subsubsubsection{VNA:DEEMBedding:x:LOSS}
\event{Sets the loss of the port extension at the configured frequency}{VNA:DEEMBedding:x:LOSS}{<loss> Loss at frequency in dB}
\query{Queries the loss of the port extension at the configured frequency}{VNA:DEEMBedding:x:LOSS?}{None}{<loss> Loss at frequency in dB}
\subsubsubsection{VNA:DEEMBedding:x:FREQuency}
\event{Sets the frequency for the specified loss of the port extension}{VNA:DEEMBedding:x:FREQuency}{<freq> Frequency for loss in Hz}
\query{Queries the frequency for the specified loss of the port extension}{VNA:DEEMBedding:x:FREQuency?}{None}{<freq> Frequency for loss in Hz}
\subsubsection{2xThru Deembedding Commands}
The 2xThru deembedding does not have a SCPI API so far because it requires the user to manually make specific connections and take additional measurements.
\subsubsection{Matching Network Deembedding Commands}
The following commands are only valid for a deembedding option of type ``Matching\_Network''. The ``x'' in the command names must be replaced with the option number.
Similarly to the deembedding options, the matching network itself is also flexible and requires a flexible API as well. A similar approach is used where components in the matching network are given a number. This number is shown as ``y'' in the following commands.
\subsubsubsection{VNA:DEEMBedding:x:PORT}
\event{Sets the port at which the matching network is added}{VNA:DEEMBedding:x:PORT}{<port>, 1...n}
\query{Queries the port at which the matching network is added}{VNA:DEEMBedding:x:PORT?}{None}{<port>, 1...n}
\subsubsubsection{VNA:DEEMBedding:x:ADD}
\event{Selects whether the network is added or removed during the deembedding}{VNA:DEEMBedding:x:ADD}{TRUE if the effect of the network should be added. FALSE if the effect of the\\
\hspace{1cm}network should be subtracted}
\query{Queries whether the network is added or removed during the deembedding}{VNA:DEEMBedding:x:ADD?}{None}{TRUE or FALSE}
\subsubsubsection{VNA:DEEMBedding:x:NUMber}
\query{Returns the number of configured matching network components}{VNA:DEEMBedding:x:NUMber?}{None}{<number of matching network components>}
\subsubsubsection{VNA:DEEMBedding:x:TYPE}
\query{Returns the type of a configured matching network component}{VNA:DEEMBedding:x:TYPE? <y>}{<y> Number of matching component}{Type of component, one of:\\
\hspace{1cm}SeriesR\\
\hspace{1cm}SeriesL\\
\hspace{1cm}SeriesC\\
\hspace{1cm}ParallelR\\
\hspace{1cm}ParallelL\\
\hspace{1cm}ParallelC\\
\hspace{1cm}Touchstone\_Through\\
\hspace{1cm}Touchstone\_Shunt\\}
\subsubsubsection{VNA:DEEMBedding:x:NEW}
\event{Adds a new matching network component}{VNA:DEEMBedding:x:NEW}{<type> Type of component, one of:\\
\hspace{1cm}SeriesR\\
\hspace{1cm}SeriesL\\
\hspace{1cm}SeriesC\\
\hspace{1cm}ParallelR\\
\hspace{1cm}ParallelL\\
\hspace{1cm}ParallelC\\
\hspace{1cm}Touchstone\_Through\\
\hspace{1cm}Touchstone\_Shunt\\
<pos> position at which the component will be added. A value of 0 adds the\\
\hspace{1cm}component closest to the port. This parameter is optional. If it is omitted,\\
\hspace{1cm}the component is added closest to the DUT}
\subsubsubsection{VNA:DEEMBedding:x:DELete}
\event{Removes a matching network component}{VNA:DEEMBedding:x:DELete}{<y> Number of component to delete}
\subsubsubsection{VNA:DEEMBedding:x:CLEAR}
\event{Removes all matching network components}{VNA:DEEMBedding:x:DELete}{None}
\subsubsection{Impedance Renormalization Deembedding Commands}
The following commands are only valid for a deembedding option of type ``Impedance\_Renormalization''. The ``x'' in the command names must be replaced with the option number.
\subsubsubsection{VNA:DEEMBedding:x:IMPedance}
\event{Sets the target impedance}{VNA:DEEMBedding:x:IMPedance}{<impedance> Impedance in Ohm}
\query{Queries the target impedance}{VNA:DEEMBedding:x:IMPedance?}{None}{<impedance> Impedance in Ohm}
\subsubsubsection{VNA:DEEMBedding:x:y:VALue}
\event{Sets the value of a matching network component}{VNA:DEEMBedding:x:y:VALue}{<value>, either in Ohm, Farad or Henry}
\query{Queries the value of a matching network component}{VNA:DEEMBedding:x:y:VALue?}{None}{<port>, either in Ohm, Farad or Henry}
This command is only available for a component of these types:
\begin{itemize}
\item SeriesR
\item SeriesL
\item SeriesC
\item ParallelR
\item ParallelL
\item ParallelC
\end{itemize}
\subsubsubsection{VNA:DEEMBedding:x:y:FILE}
\event{Loads a matching network component values from a Touchstone file}{VNA:DEEMBedding:x:y:FILE}{<filename>}
This command is only available for a component of these types:
\begin{itemize}
\item Touchstone\_Through
\item Touchstone\_Shunt
\end{itemize}
\subsection{Signal Generator Commands}
These commands change or query signal generator settings. Although most of them are available regardless of the current device mode, they usually only have an effect once the generator mode is active.
@ -760,6 +1136,16 @@ If single sweep is enabled, the acquisition is stopped when the required number
\event{Enables/disables signal identification}{SA:ACQuisition:SIGid}{<enabled>, option are TRUE, FALSE, 1 or 0}
\query{Queries whether signal identification is enabled}{SA:ACQuisition:SIGid?}{None}{TRUE or FALSE}
\subsubsection{SA:ACQuisition:FREQuency}
\query{Returns the current frequency of the sweep}{SA:ACQuisition:FREQuency?}{None}{<Frequency in Hz>}
This command only returns valid data when a sweep is running and the the sweep is a frequency sweep.
\subsubsection{SA:ACQuisition:TIME}
\query{Returns the current time of the sweep}{SA:ACQuisition:TIME?}{None}{<Time in seconds>}
This command only returns valid data when a sweep is running and the the sweep is a zero-span sweep.
\subsubsection{SA:TRACKing:ENable}
\event{Enables/disables the tracking generator}{SA:TRACKing:ENable}{<enabled>, option are TRUE, FALSE, 1 or 0}
\query{Queries whether tracking generator is enabled}{SA:TRACKing:ENable?}{None}{TRUE or FALSE}

View file

@ -1708,10 +1708,8 @@ bool Calibration::fromFile(QString filename)
}
calkit_file.append(".calkit");
qDebug() << "Associated calibration kit expected in" << calkit_file;
try {
kit = Calkit::fromFile(calkit_file);
} catch (runtime_error &e) {
qDebug() << "Parsing of calibration kit failed while opening calibration file: " << e.what() << " (ignore for calibration format >= 3)";
if(!kit.fromFile(calkit_file)) {
qDebug() << "Parsing of calibration kit failed while opening calibration file (ignore for calibration format >= 3)";
}
ifstream file;

View file

@ -16,13 +16,47 @@ using json = nlohmann::json;
using namespace std;
Calkit::Calkit()
: SCPINode("KIT")
: SCPINode("KIT"), scpi_std("STAndard")
{
// set default values
filename = "";
for(auto e : descr) {
e.var.setValue(e.def);
}
add(new SCPICommand("MANufacturer", [=](QStringList params) -> QString {
if(params.size() != 1 ) {
// no new value given
return SCPI::getResultName(SCPI::Result::Error);
}
manufacturer = params[0];
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
return manufacturer;
}, false));
add(new SCPICommand("SERial", [=](QStringList params) -> QString {
if(params.size() != 1 ) {
// no new value given
return SCPI::getResultName(SCPI::Result::Error);
}
serialnumber = params[0];
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
return serialnumber;
}, false));
add(new SCPICommand("DESCription", [=](QStringList params) -> QString {
if(params.size() != 1 ) {
// no new value given
return SCPI::getResultName(SCPI::Result::Error);
}
description = params[0];
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
return description;
}, false));
add(new SCPICommand("FILEname", nullptr, [=](QStringList) -> QString {
return filename;
}));
add(new SCPICommand("SAVE", [=](QStringList params) -> QString {
if(params.size() != 1 ) {
// no filename given or no calibration active
@ -30,19 +64,65 @@ Calkit::Calkit()
}
toFile(params[0]);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
}, nullptr, false));
add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString {
if(params.size() != 1) {
// no filename given or no calibration active
return SCPI::getResultName(SCPI::Result::False);
}
try {
*this = fromFile(params[0]);
if(this->fromFile(params[0])) {
return SCPI::getResultName(SCPI::Result::True);
} catch (runtime_error &e) {
} else {
return SCPI::getResultName(SCPI::Result::False);
}
}, false));
scpi_std.add(new SCPICommand("CLEAR", [=](QStringList params) -> QString {
Q_UNUSED(params);
setIdealDefault();
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
scpi_std.add(new SCPICommand("NUMber", nullptr, [=](QStringList params) -> QString {
Q_UNUSED(params);
return QString::number(standards.size());
}));
scpi_std.add(new SCPICommand("NEW", [=](QStringList params) -> QString {
if(params.size() != 2) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto type = CalStandard::Virtual::TypeFromString(params[0]);
if(type == CalStandard::Virtual::Type::Last) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto s = CalStandard::Virtual::create(type);
if(!s) {
return SCPI::getResultName(SCPI::Result::Error);
}
s->setName(params[1]);
addStandard(s);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr, false));
scpi_std.add(new SCPICommand("DELete", [=](QStringList params) -> QString {
unsigned long long index;
if(!SCPI::paramToULongLong(params, 0, index)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(index < 1 || index > standards.size()) {
return SCPI::getResultName(SCPI::Result::Error);
}
removeStandard(standards[index-1]);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
scpi_std.add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString {
unsigned long long index = 0;
if(!SCPI::paramToULongLong(params, 0, index)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(index < 1 || index > standards.size()) {
return SCPI::getResultName(SCPI::Result::Error);
}
return CalStandard::Virtual::TypeToString(standards[index-1]->getType()).replace(" ", "_");
}));
add(&scpi_std);
}
void Calkit::toFile(QString filename)
@ -57,6 +137,7 @@ void Calkit::toFile(QString filename)
file.open(filename.toStdString());
file << setw(4) << toJSON() << endl;
file.close();
this->filename = filename;
}
static QString readLine(ifstream &file) {
@ -65,27 +146,29 @@ static QString readLine(ifstream &file) {
return QString::fromStdString(line).simplified();
}
Calkit Calkit::fromFile(QString filename)
bool Calkit::fromFile(QString filename)
{
qDebug() << "Opening calkit to file" << filename;
qDebug() << "Opening calkit from file" << filename;
auto c = Calkit();
ifstream file;
file.open(filename.toStdString());
if(!file.is_open()) {
throw runtime_error("Unable to open file");
qWarning() << "Unable to open file: " << filename;
return false;
}
json j;
try {
file >> j;
} catch (exception &e) {
throw runtime_error("JSON parsing error: " + string(e.what()));
qWarning() << "JSON parsing error: " << QString(e.what());
return false;
}
c.clearStandards();
clearStandards();
this->filename = "";
if(j.contains("standards")) {
qDebug() << "new JSON format detected";
c.fromJSON(j);
fromJSON(j);
} else {
// older format is used
struct {
@ -133,7 +216,7 @@ Calkit Calkit::fromFile(QString filename)
if(j.contains("SOLT")) {
qDebug() << "old JSON format detected";
// calkit file uses json format, parse
Savable::parseJSON(j, c.descr);
Savable::parseJSON(j, descr);
const std::vector<Savable::SettingDescription> descr_deprecated = {{
{&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0},
{&SOLT.open_m.delay, "SOLT.Open.Param.Delay", 0.0},
@ -286,7 +369,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.open_m.file.toStdString());
open_m->setMeasurement(ts, SOLT.open_m.Sparam);
}
c.addStandard(open_m);
addStandard(open_m);
if(SOLT.separate_male_female) {
auto open_f = new CalStandard::Open("Default female standard", SOLT.open_f.Z0, SOLT.open_f.delay, SOLT.open_f.loss, SOLT.open_f.C0, SOLT.open_f.C1, SOLT.open_f.C2, SOLT.open_f.C3);
if(SOLT.open_f.useMeasurements) {
@ -294,7 +377,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.open_f.file.toStdString());
open_m->setMeasurement(ts, SOLT.open_f.Sparam);
}
c.addStandard(open_f);
addStandard(open_f);
}
auto short_m = new CalStandard::Short(SOLT.separate_male_female ? "Default male standard" : "Default standard", SOLT.short_m.Z0, SOLT.short_m.delay, SOLT.short_m.loss, SOLT.short_m.L0, SOLT.short_m.L1, SOLT.short_m.L2, SOLT.short_m.L3);
@ -303,7 +386,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.short_m.file.toStdString());
short_m->setMeasurement(ts, SOLT.short_m.Sparam);
}
c.addStandard(short_m);
addStandard(short_m);
if(SOLT.separate_male_female) {
auto short_f = new CalStandard::Short("Default female standard", SOLT.short_f.Z0, SOLT.short_f.delay, SOLT.short_f.loss, SOLT.short_f.L0, SOLT.short_f.L1, SOLT.short_f.L2, SOLT.short_f.L3);
if(SOLT.short_f.useMeasurements) {
@ -311,7 +394,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.short_f.file.toStdString());
short_m->setMeasurement(ts, SOLT.short_f.Sparam);
}
c.addStandard(short_f);
addStandard(short_f);
}
auto load_m = new CalStandard::Load(SOLT.separate_male_female ? "Default male standard" : "Default standard", SOLT.load_m.Z0, SOLT.load_m.delay, 0.0, SOLT.load_m.resistance, SOLT.load_m.Cparallel, SOLT.load_m.Lseries, SOLT.loadModelCFirst);
@ -320,7 +403,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.load_m.file.toStdString());
load_m->setMeasurement(ts, SOLT.load_m.Sparam);
}
c.addStandard(load_m);
addStandard(load_m);
if(SOLT.separate_male_female) {
auto load_f = new CalStandard::Load("Default female standard", SOLT.load_m.Z0, SOLT.load_f.delay, 0.0, SOLT.load_f.resistance, SOLT.load_f.Cparallel, SOLT.load_f.Lseries, SOLT.loadModelCFirst);
if(SOLT.load_f.useMeasurements) {
@ -328,7 +411,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.load_f.file.toStdString());
load_m->setMeasurement(ts, SOLT.load_f.Sparam);
}
c.addStandard(load_f);
addStandard(load_f);
}
auto through = new CalStandard::Through("Default standard", SOLT.Through.Z0, SOLT.Through.delay, SOLT.Through.loss);
@ -337,7 +420,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.Through.file.toStdString());
through->setMeasurement(ts, SOLT.Through.Sparam1, SOLT.Through.Sparam2);
}
c.addStandard(through);
addStandard(through);
InformationBox::ShowMessage("Loading calkit file", "The file \"" + filename + "\" is stored in a deprecated"
" calibration kit format. Future versions of this application might not support"
@ -345,8 +428,10 @@ Calkit Calkit::fromFile(QString filename)
}
file.close();
this->filename = filename;
updateSCPINames();
return c;
return true;
}
void Calkit::edit(std::function<void (void)> updateCal)
@ -364,10 +449,24 @@ void Calkit::edit(std::function<void (void)> updateCal)
void Calkit::clearStandards()
{
for(auto s : standards) {
delete s;
while(standards.size() > 0) {
removeStandard(standards[0]);
}
}
void Calkit::updateSCPINames()
{
// Need to remove all standards from the subnode list first, otherwise
// name changes wouldn't work due to temporarily name collisions
for(auto &s : standards) {
scpi_std.remove(s);
}
unsigned int i=1;
for(auto &s : standards) {
s->changeName(QString::number(i));
scpi_std.add(s);
i++;
}
standards.clear();
}
std::vector<CalStandard::Virtual *> Calkit::getStandards() const
@ -391,6 +490,14 @@ void Calkit::addStandard(CalStandard::Virtual *s)
}
}
standards.push_back(s);
updateSCPINames();
}
void Calkit::removeStandard(CalStandard::Virtual *s)
{
standards.erase(std::remove(standards.begin(), standards.end(), s), standards.end());
delete s;
updateSCPINames();
}
nlohmann::json Calkit::toJSON()
@ -411,6 +518,7 @@ nlohmann::json Calkit::toJSON()
void Calkit::fromJSON(nlohmann::json j)
{
clearStandards();
filename = "";
Savable::parseJSON(j, descr);
for(auto js : j["standards"]) {
if(!js.contains("type") || !js.contains("params")) {
@ -426,6 +534,7 @@ void Calkit::fromJSON(nlohmann::json j)
s->fromJSON(js["params"]);
addStandard(s);
}
updateSCPINames();
}
void Calkit::setIdealDefault()
@ -437,4 +546,5 @@ void Calkit::setIdealDefault()
addStandard(new CalStandard::Short("Ideal Short Standard", 50.0, 0, 0, 0, 0, 0, 0));
addStandard(new CalStandard::Load("Ideal Load Standard", 50.0, 0, 0, 50.0, 0, 0));
addStandard(new CalStandard::Through("Ideal Through Standard", 50.0, 0, 0));
updateSCPINames();
}

View file

@ -19,15 +19,8 @@ class Calkit : public Savable, public SCPINode
friend class LibreCALDialog;
public:
Calkit();
Calkit(const Calkit&) = default;
Calkit& operator=(const Calkit& other)
{
this->manufacturer = other.manufacturer;
this->serialnumber = other.serialnumber;
this->description = other.description;
this->standards = other.standards;
return *this;
}
Calkit(const Calkit&) = delete;
Calkit& operator= (const Calkit&) = delete;
class SOLT {
public:
@ -44,11 +37,12 @@ public:
};
void toFile(QString filename);
static Calkit fromFile(QString filename);
bool fromFile(QString filename);
void edit(std::function<void(void)> updateCal = nullptr);
std::vector<CalStandard::Virtual *> getStandards() const;
void addStandard(CalStandard::Virtual* s);
void removeStandard(CalStandard::Virtual* s);
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
@ -57,7 +51,10 @@ public:
private:
void clearStandards();
void updateSCPINames();
QString manufacturer, serialnumber, description;
QString filename;
SCPINode scpi_std;
std::vector<CalStandard::Virtual*> standards;
const std::vector<Savable::SettingDescription> descr = {{

View file

@ -27,8 +27,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->bDelete, &QPushButton::clicked, [=](){
auto row = ui->list->currentRow();
if(row >= 0) {
delete kit.standards[row];
kit.standards.erase(kit.standards.begin() + row);
kit.removeStandard(kit.standards[row]);
updateStandardList();
}
});
@ -39,6 +38,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
swap(kit.standards[row], kit.standards[row-1]);
ui->list->setCurrentRow(row-1);
updateStandardList();
kit.updateSCPINames();
}
});
@ -48,6 +48,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
swap(kit.standards[row], kit.standards[row+1]);
ui->list->setCurrentRow(row+1);
updateStandardList();
kit.updateSCPINames();
}
});
@ -93,7 +94,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.calkit = QFileInfo(filename).path();
try {
kit = Calkit::fromFile(filename);
kit.fromFile(filename);
} catch (runtime_error &e) {
InformationBox::ShowError("Error", "The calibration kit file could not be parsed (" + QString(e.what()) + ")");
qWarning() << "Parsing of calibration kit failed while opening calibration file: " << e.what();

View file

@ -12,6 +12,7 @@ using namespace std;
using namespace CalStandard;
Virtual::Virtual(QString name) :
SCPINode(name),
name(name),
minFreq(std::numeric_limits<double>::lowest()),
maxFreq(std::numeric_limits<double>::max())
@ -60,7 +61,7 @@ QString Virtual::TypeToString(Virtual::Type type)
Virtual::Type Virtual::TypeFromString(QString s)
{
for(int i=0;i<(int) Type::Last;i++) {
if(TypeToString((Type) i) == s) {
if(TypeToString((Type) i).compare(s, Qt::CaseInsensitive) == 0) {
return (Type) i;
}
}
@ -101,6 +102,11 @@ void Virtual::setName(const QString &value)
name = value;
}
void Virtual::setupSCPI()
{
addStringParameter("NAME", name);
}
void OnePort::setMeasurement(const Touchstone &ts, int port)
{
if(!touchstone) {
@ -142,10 +148,35 @@ void OnePort::fromJSON(nlohmann::json j)
}
}
void OnePort::setupSCPI()
{
add(new SCPICommand("FILE", [=](QStringList params) -> QString {
if(params.size() < 1) {
return SCPI::getResultName(SCPI::Result::Error);
}
try {
auto ts = Touchstone::fromFile(params[0].toStdString());
unsigned long long index = 0;
if(params.size() == 2) {
if(!SCPI::paramToULongLong(params, 1, index)) {
return SCPI::getResultName(SCPI::Result::Error);
}
}
setMeasurement(ts, index-1);
return SCPI::getResultName(SCPI::Result::Empty);
} catch(const std::exception& e) {
// failed to load file
return SCPI::getResultName(SCPI::Result::Error);
}
}, nullptr, false));
Virtual::setupSCPI();
}
Open::Open()
{
Z0 = 50.0;
delay = loss = C0 = C1 = C2 = C3 = 0.0;
setupSCPI();
}
std::complex<double> Open::toS11(double freq)
@ -263,10 +294,23 @@ void Open::fromJSON(nlohmann::json j)
C3 = j.value("C3", 0.0);
}
void Open::setupSCPI()
{
addDoubleParameter("Z0", Z0, true, true, [=](){clearMeasurement();});
addDoubleParameter("DELAY", delay, true, true, [=](){clearMeasurement();});
addDoubleParameter("LOSS", loss, true, true, [=](){clearMeasurement();});
addDoubleParameter("C0", C0, true, true, [=](){clearMeasurement();});
addDoubleParameter("C1", C1, true, true, [=](){clearMeasurement();});
addDoubleParameter("C2", C2, true, true, [=](){clearMeasurement();});
addDoubleParameter("C3", C3, true, true, [=](){clearMeasurement();});
OnePort::setupSCPI();
}
Short::Short()
{
Z0 = 50.0;
delay = loss = L0 = L1 = L2 = L3 = 0.0;
setupSCPI();
}
std::complex<double> Short::toS11(double freq)
@ -378,12 +422,25 @@ void Short::fromJSON(nlohmann::json j)
L3 = j.value("L3", 0.0);
}
void Short::setupSCPI()
{
addDoubleParameter("Z0", Z0, true, true, [=](){clearMeasurement();});
addDoubleParameter("DELAY", delay, true, true, [=](){clearMeasurement();});
addDoubleParameter("LOSS", loss, true, true, [=](){clearMeasurement();});
addDoubleParameter("L0", L0, true, true, [=](){clearMeasurement();});
addDoubleParameter("L1", L1, true, true, [=](){clearMeasurement();});
addDoubleParameter("L2", L2, true, true, [=](){clearMeasurement();});
addDoubleParameter("L3", L3, true, true, [=](){clearMeasurement();});
OnePort::setupSCPI();
}
Load::Load()
{
Z0 = 50.0;
resistance = 50.0;
delay = Cparallel = Lseries = 0;
Cfirst = true;
setupSCPI();
}
std::complex<double> Load::toS11(double freq)
@ -519,6 +576,18 @@ void Load::fromJSON(nlohmann::json j)
Cfirst = j.value("Cfirst", true);
}
void Load::setupSCPI()
{
addDoubleParameter("Z0", Z0, true, true, [=](){clearMeasurement();});
addDoubleParameter("DELAY", delay, true, true, [=](){clearMeasurement();});
addDoubleParameter("LOSS", loss, true, true, [=](){clearMeasurement();});
addDoubleParameter("RESistance", resistance, true, true, [=](){clearMeasurement();});
addDoubleParameter("CPARallel", Cparallel, true, true, [=](){clearMeasurement();});
addDoubleParameter("LSERies", Lseries, true, true, [=](){clearMeasurement();});
addBoolParameter("CFIRST", Cfirst, true, true, [=](){clearMeasurement();});
OnePort::setupSCPI();
}
void TwoPort::setMeasurement(const Touchstone &ts, int port1, int port2)
{
if(!touchstone) {
@ -560,11 +629,37 @@ void TwoPort::fromJSON(nlohmann::json j)
}
}
void TwoPort::setupSCPI()
{
add(new SCPICommand("FILE", [=](QStringList params) -> QString {
if(params.size() != 3) {
return SCPI::getResultName(SCPI::Result::Error);
}
try {
auto ts = Touchstone::fromFile(params[0].toStdString());
unsigned long long index1, index2 = 0;
if(!SCPI::paramToULongLong(params, 1, index1)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(!SCPI::paramToULongLong(params, 2, index1)) {
return SCPI::getResultName(SCPI::Result::Error);
}
setMeasurement(ts, index1-1, index2-1);
return SCPI::getResultName(SCPI::Result::Empty);
} catch(const std::exception& e) {
// failed to load file
return SCPI::getResultName(SCPI::Result::Error);
}
}, nullptr, false));
Virtual::setupSCPI();
}
Through::Through()
{
Z0 = 50.0;
delay = 0.0;
loss = 0.0;
setupSCPI();
}
Sparam Through::toSparam(double freq)
@ -675,9 +770,18 @@ void Through::fromJSON(nlohmann::json j)
loss = j.value("loss", 0.0);
}
void Through::setupSCPI()
{
addDoubleParameter("Z0", Z0, true, true, [=](){clearMeasurement();});
addDoubleParameter("DELAY", delay, true, true, [=](){clearMeasurement();});
addDoubleParameter("LOSS", loss, true, true, [=](){clearMeasurement();});
TwoPort::setupSCPI();
}
Reflect::Reflect()
{
isShort = true;
setupSCPI();
}
std::complex<double> Reflect::toS11(double freq)
@ -724,10 +828,17 @@ bool Reflect::getIsShort() const
return isShort;
}
void Reflect::setupSCPI()
{
addBoolParameter("SHORT", isShort, true, true, [=](){clearMeasurement();});
OnePort::setupSCPI();
}
Line::Line()
{
Z0 = 50.0;
setDelay(0.0);
setupSCPI();
}
Sparam Line::toSparam(double freq)
@ -800,3 +911,10 @@ void Line::setDelay(double delay)
minFreq = 1.0 / delay * 20 / 360;
maxFreq = 1.0 / delay * 160 / 360;
}
void Line::setupSCPI()
{
addDoubleParameter("Z0", Z0, true, true, [=](){clearMeasurement();});
addDoubleParameter("DELAY", delay, true, true, [=](){clearMeasurement();});
TwoPort::setupSCPI();
}

View file

@ -4,6 +4,7 @@
#include "savable.h"
#include "touchstone.h"
#include "Tools/parameters.h"
#include "scpi.h"
#include <complex>
#include <functional>
@ -11,7 +12,7 @@
namespace CalStandard
{
class Virtual : public QObject, public Savable
class Virtual : public QObject, public Savable, public SCPINode
{
Q_OBJECT
public:
@ -53,6 +54,7 @@ signals:
void deleted();
protected:
void setupSCPI();
QString name;
double minFreq;
double maxFreq;
@ -75,6 +77,7 @@ public:
virtual void fromJSON(nlohmann::json j) override;
protected:
void setupSCPI();
Touchstone *touchstone;
};
@ -85,7 +88,7 @@ class Open : public OnePort
public:
Open();
Open(QString name, double Z0, double delay, double loss, double C0, double C1, double C2, double C3)
: OnePort(name), Z0(Z0), delay(delay), loss(loss), C0(C0), C1(C1), C2(C2), C3(C3){}
: OnePort(name), Z0(Z0), delay(delay), loss(loss), C0(C0), C1(C1), C2(C2), C3(C3){setupSCPI();}
virtual std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -93,6 +96,7 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
private:
void setupSCPI();
double Z0, delay, loss, C0, C1, C2, C3;
};
@ -101,7 +105,7 @@ class Short : public OnePort
public:
Short();
Short(QString name, double Z0, double delay, double loss, double L0, double L1, double L2, double L3)
: OnePort(name), Z0(Z0), delay(delay), loss(loss), L0(L0), L1(L1), L2(L2), L3(L3){}
: OnePort(name), Z0(Z0), delay(delay), loss(loss), L0(L0), L1(L1), L2(L2), L3(L3){setupSCPI();}
virtual std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -109,6 +113,7 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
private:
void setupSCPI();
double Z0, delay, loss, L0, L1, L2, L3;
};
@ -117,7 +122,7 @@ class Load : public OnePort
public:
Load();
Load(QString name, double Z0, double delay, double loss, double resistance, double Cparallel, double Lseries, bool Cfirst = true)
: OnePort(name), Z0(Z0), delay(delay), loss(loss), resistance(resistance), Cparallel(Cparallel), Lseries(Lseries), Cfirst(Cfirst){}
: OnePort(name), Z0(Z0), delay(delay), loss(loss), resistance(resistance), Cparallel(Cparallel), Lseries(Lseries), Cfirst(Cfirst){setupSCPI();}
virtual std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -125,6 +130,7 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
private:
void setupSCPI();
double Z0, delay, loss, resistance, Cparallel, Lseries;
bool Cfirst;
};
@ -144,6 +150,7 @@ public:
bool getIsShort() const;
private:
void setupSCPI();
bool isShort;
};
@ -164,6 +171,7 @@ public:
virtual void fromJSON(nlohmann::json j) override;
protected:
void setupSCPI();
Touchstone *touchstone;
};
@ -172,7 +180,7 @@ class Through : public TwoPort
public:
Through();
Through(QString name, double Z0, double delay, double loss)
: TwoPort(name), Z0(Z0), delay(delay), loss(loss){}
: TwoPort(name), Z0(Z0), delay(delay), loss(loss){setupSCPI();}
virtual Sparam toSparam(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -180,6 +188,7 @@ public:
virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override;
private:
void setupSCPI();
double Z0, delay, loss;
};
@ -188,7 +197,7 @@ class Line : public TwoPort
public:
Line();
Line(QString name, double Z0, double delay)
: TwoPort(name), Z0(Z0), delay(delay){}
: TwoPort(name), Z0(Z0), delay(delay){setupSCPI();}
virtual Sparam toSparam(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -197,6 +206,7 @@ public:
virtual void fromJSON(nlohmann::json j) override;
private:
void setDelay(double delay);
void setupSCPI();
double Z0, delay;
};

View file

@ -64,6 +64,9 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
normalize.measure = nullptr;
normalize.enable = nullptr;
lastFreq = 0.0;
lastTime = 0.0;
configurationTimer.setSingleShot(true);
connect(&configurationTimer, &QTimer::timeout, this, [=](){
ConfigureDevice();
@ -396,6 +399,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
freq["stop"] = settings.freqStop;
sweep["frequency"] = freq;
sweep["single"] = singleSweep;
sweep["averages"] = averages;
nlohmann::json acq;
acq["RBW"] = settings.RBW;
acq["window"] = WindowToString((DeviceDriver::SASettings::Window) settings.window).toStdString();
@ -502,6 +506,7 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
EnableNormalization(correctSize);
}
SetSingleSweep(sweep.value("single", singleSweep));
SetAveraging(sweep.value("averages", averages));
}
}
@ -584,6 +589,9 @@ void SpectrumAnalyzer::NewDatapoint(DeviceDriver::SAMeasurement m)
qWarning() << "Got point" << m_avg.pointNum << "but last received point was" << lastPoint << "("<<(m_avg.pointNum-lastPoint-1)<<"missed points)";
}
lastPoint = m_avg.pointNum;
lastFreq = m_avg.frequency;
lastTime = (double) m_avg.us / 1000000;
}
void SpectrumAnalyzer::SettingsChanged()
@ -1102,6 +1110,12 @@ void SpectrumAnalyzer::SetupSCPI()
}, [=](QStringList) -> QString {
return singleSweep ? SCPI::getResultName(SCPI::Result::True): SCPI::getResultName(SCPI::Result::False);
}));
scpi_acq->add(new SCPICommand("FREQuency", nullptr, [=](QStringList) -> QString {
return QString::number(lastFreq);
}));
scpi_acq->add(new SCPICommand("TIME", nullptr, [=](QStringList) -> QString {
return QString::number(lastTime);
}));
auto scpi_tg = new SCPINode("TRACKing");
SCPINode::add(scpi_tg);
scpi_tg->add(new SCPICommand("ENable", [=](QStringList params) -> QString {

View file

@ -132,6 +132,10 @@ private:
QList<QAction*> importActions;
QList<QAction*> exportActions;
// meta data from the last received datapoint
double lastFreq;
double lastTime;
signals:
void dataChanged();
void startFreqChanged(double freq);

View file

@ -127,6 +127,31 @@ Deembedding::Deembedding(TraceModel &tm)
addOption(option);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("DELete", [=](QStringList params) -> QString {
unsigned long long index;
if(!SCPI::paramToULongLong(params, 0, index)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(index < 1 || index > options.size()) {
return SCPI::getResultName(SCPI::Result::Error);
}
removeOption(index-1);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("SWAP", [=](QStringList params) -> QString {
unsigned long long index1, index2;
if(!SCPI::paramToULongLong(params, 0, index1)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(!SCPI::paramToULongLong(params, 1, index2)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(index1 < 1 || index1 > options.size() || index2 < 1 || index2 > options.size()) {
return SCPI::getResultName(SCPI::Result::Error);
}
swapOptions(index1-1, index2-1);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("CLEAR", [=](QStringList params) -> QString {
Q_UNUSED(params);
clear();
@ -209,12 +234,12 @@ void Deembedding::addOption(DeembeddingOption *option)
emit optionAdded();
}
void Deembedding::swapOptions(unsigned int index)
void Deembedding::swapOptions(unsigned int index1, unsigned int index2)
{
if(index + 1 >= options.size()) {
if(index1 > options.size() || index2 > options.size() || index1 == index2) {
return;
}
std::swap(options[index], options[index+1]);
std::swap(options[index1], options[index2]);
updateSCPINames();
}

View file

@ -27,7 +27,7 @@ public:
void removeOption(unsigned int index);
void addOption(DeembeddingOption* option);
void swapOptions(unsigned int index);
void swapOptions(unsigned int index1, unsigned int index2);
void clear();
bool isMeasuring();

View file

@ -44,12 +44,12 @@ DeembeddingDialog::DeembeddingDialog(Deembedding *d, QWidget *parent) :
});
connect(ui->bMoveUp, &QPushButton::clicked, [=](){
auto index = ui->view->currentIndex();
d->swapOptions(index.row() - 1);
d->swapOptions(index.row() - 1, index.row());
ui->view->setCurrentIndex(index.sibling(index.row() - 1, 0));
});
connect(ui->bMoveDown, &QPushButton::clicked, [=](){
auto index = ui->view->currentIndex();
d->swapOptions(index.row());
d->swapOptions(index.row(), index.row() + 1);
ui->view->setCurrentIndex(index.sibling(index.row() + 1, 0));
});

View file

@ -56,6 +56,17 @@ MatchingNetwork::MatchingNetwork()
addComponent(index, c);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("DELete", [=](QStringList params) -> QString {
unsigned long long index;
if(!SCPI::paramToULongLong(params, 0, index)) {
return SCPI::getResultName(SCPI::Result::Error);
}
if(index < 1 || index > network.size()) {
return SCPI::getResultName(SCPI::Result::Error);
}
removeComponent(index-1);
return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr));
add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString {
unsigned long long index = 0;
if(!SCPI::paramToULongLong(params, 0, index)) {
@ -267,6 +278,7 @@ void MatchingNetwork::fromJSON(nlohmann::json j)
}
addNetwork = j.value("addNetwork", true);
matching.clear();
updateSCPINames();
}
void MatchingNetwork::clearNetwork()
@ -643,7 +655,7 @@ MatchingComponent::MatchingComponent(Type type)
// failed to load file
return SCPI::getResultName(SCPI::Result::Error);
}
}, nullptr));
}, nullptr, false));
break;
default:
break;
@ -787,6 +799,7 @@ void MatchingComponent::updateTouchstoneLabel()
return;
}
QFont font = touchstoneLabel->font();
touchstoneLabel->setStyleSheet("color: black");
font.setPointSize(10);
touchstoneLabel->setFont(font);
if(touchstone->points() == 0) {

View file

@ -69,6 +69,10 @@ VNA::VNA(AppWindow *window, QString name)
calWaitFirst = false;
calDialog = nullptr;
lastFreq = 0.0;
lastPower = 0.0;
lastTime = 0.0;
changingSettings = false;
settings.sweepType = SweepType::Frequency;
settings.zerospan = false;
@ -880,6 +884,7 @@ nlohmann::json VNA::toJSON()
sweep["power"] = power;
sweep["points"] = settings.npoints;
sweep["IFBW"] = settings.bandwidth;
sweep["averages"] = averages;
j["sweep"] = sweep;
j["traces"] = traceModel.toJSON();
@ -917,6 +922,7 @@ void VNA::fromJSON(nlohmann::json j)
// restore sweep settings, keep current value as default in case of missing entry
SetPoints(sweep.value("points", settings.npoints));
SetIFBandwidth(sweep.value("IFBW", settings.bandwidth));
SetAveraging(sweep.value("averages", averages));
if(sweep.contains("frequency")) {
auto freq = sweep["frequency"];
SetStartFreq(freq.value("start", settings.Freq.start));
@ -1015,6 +1021,10 @@ void VNA::NewDatapoint(DeviceDriver::VNAMeasurement m)
}
}
lastFreq = m_avg.frequency;
lastPower = m_avg.dBm;
lastTime = (double) m_avg.us / 1000000;
window->addStreamingData(m_avg, AppWindow::VNADataType::Raw, settings.zerospan);
if(average.settled()) {
@ -1246,6 +1256,7 @@ void VNA::SetLogSweep(bool log)
{
if(settings.Freq.logSweep != log) {
settings.Freq.logSweep = log;
ConstrainAndUpdateFrequencies();
emit logSweepChanged(log);
SettingsChanged();
}
@ -1592,6 +1603,15 @@ void VNA::SetupSCPI()
}, [=](QStringList) -> QString {
return singleSweep ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
scpi_acq->add(new SCPICommand("FREQuency", nullptr, [=](QStringList) -> QString {
return QString::number(lastFreq);
}));
scpi_acq->add(new SCPICommand("POWer", nullptr, [=](QStringList) -> QString {
return QString::number(lastPower);
}));
scpi_acq->add(new SCPICommand("TIME", nullptr, [=](QStringList) -> QString {
return QString::number(lastTime);
}));
auto scpi_stim = new SCPINode("STIMulus");
SCPINode::add(scpi_stim);
scpi_stim->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
@ -1627,6 +1647,12 @@ void VNA::SetupSCPI()
void VNA::ConstrainAndUpdateFrequencies()
{
if(settings.sweepType == SweepType::Frequency && settings.Freq.logSweep) {
if(settings.Freq.start <= 0) {
// start frequency must be positive, force it to 1 Hz
settings.Freq.start = 1.0;
}
}
if(settings.Freq.stop > DeviceDriver::getInfo(window->getDevice()).Limits.VNA.maxFreq) {
settings.Freq.stop = DeviceDriver::getInfo(window->getDevice()).Limits.VNA.maxFreq;
}

View file

@ -201,6 +201,10 @@ private:
// Statistics for detecting missing points and sweep time
QDateTime lastStart;
int lastPoint;
// meta data from the last received datapoint
double lastFreq;
double lastPower;
double lastTime;
signals:
void deviceInitialized();

View file

@ -297,7 +297,7 @@ bool SCPINode::addDoubleParameter(QString name, double &param, bool gettable, bo
return SCPI::getResultName(SCPI::Result::Error);
}
} : (std::function<QString(QStringList)>) nullptr;
auto query = gettable ? [=](QStringList params) -> QString {
auto query = gettable ? [&param](QStringList params) -> QString {
Q_UNUSED(params)
return QString::number(param);
} : (std::function<QString(QStringList)>) nullptr;
@ -344,6 +344,26 @@ bool SCPINode::addBoolParameter(QString name, bool &param, bool gettable, bool s
return add(new SCPICommand(name, cmd, query));
}
bool SCPINode::addStringParameter(QString name, QString &param, bool gettable, bool settable, std::function<void ()> setCallback)
{
auto cmd = settable ? [&param, setCallback](QStringList params) -> QString {
if(params.size() == 1) {
param = params[0];
if(setCallback) {
setCallback();
}
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return SCPI::getResultName(SCPI::Result::Error);
}
} : (std::function<QString(QStringList)>) nullptr;
auto query = gettable ? [=](QStringList params) -> QString {
Q_UNUSED(params)
return param;
} : (std::function<QString(QStringList)>) nullptr;
return add(new SCPICommand(name, cmd, query));
}
bool SCPINode::changeName(QString newname)
{
if(newname == name) {
@ -548,16 +568,43 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
return SCPI::getResultName(SCPI::Result::Error);
} else {
// no more levels, search for command
auto params = cmd.split(" ");
auto cmd = params.front();
QStringList params;
params.append("");
bool inQuotes = false;
for(unsigned int i=0;i<cmd.length();i++) {
if(cmd[i] == '\\') {
// escape character, ignore
continue;
}
// check if we are starting/stopping quotes
if(cmd[i] == '"' || cmd[i] == '\'') {
// check whether quotes are escaped
if(i == 0 || cmd[i-1] != '\\') {
inQuotes = !inQuotes;
continue;
}
}
if(inQuotes) {
params.back().append(cmd[i]);
} else {
// not in quotes, handle splitting by space
if(cmd[i] == ' ') {
if(params.back().length() > 0)
params.append("");
} else {
params.back().append(cmd[i]);
}
}
}
// remove command name
params.pop_front();
bool isQuery = false;
if (cmd[cmd.size()-1]=='?') {
if (cmdName[cmdName.size()-1]=='?') {
isQuery = true;
cmd.chop(1);
cmdName.chop(1);
}
for(auto c : commands) {
if(SCPI::match(c->leafName(), cmd.toUpper())) {
if(SCPI::match(c->leafName(), cmdName.toUpper())) {
// save current node in case of non-root for the next command
lastNode = this;
if(c->convertToUppercase()) {

View file

@ -44,6 +44,7 @@ public:
bool addDoubleParameter(QString name, double &param, bool gettable = true, bool settable = true, std::function<void(void)> setCallback = nullptr);
bool addUnsignedIntParameter(QString name, unsigned int &param, bool gettable = true, bool settable = true, std::function<void(void)> setCallback = nullptr);
bool addBoolParameter(QString name, bool &param, bool gettable = true, bool settable = true, std::function<void(void)> setCallback = nullptr);
bool addStringParameter(QString name, QString &param, bool gettable = true, bool settable = true, std::function<void(void)> setCallback = nullptr);
bool changeName(QString newname);
QString leafName() {return name.split(":").back();}