Compare commits

...

55 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
Jan Käberich d853571aea move to dedicated driver
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-11-17 16:38:02 +01:00
Jan Käberich 64791e6d4e option to restore default values of device configuration
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-11-15 19:22:15 +01:00
Jan Käberich b6718c57b2 Improve marker handling when at the edge of a graph 2025-11-15 19:03:41 +01:00
Jan Käberich 87ab2fccca install missing svg library for RPi5 build
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-11-14 08:55:57 +01:00
alexey.lysiuk 56dbec82d4 Add image saving to SVG format
This allows to create images independent from screen resolution
Replaced copy-pasted screenshot code with the single implementation
2025-11-12 15:14:37 +02:00
Jan Käberich bbd1bce0b8 accept enter key along with return key when setting values
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-11-05 10:41:56 +01:00
Jan Käberich 937b002dfb Minor UI improvememts
- Add space between number and unit
- Increase width of toolbar items to (hopefully) be fully visible for
  all operating systems and themes
- Adjust order of automatically created calibration measurements to
  match LibreCAL
- Disable edit triggers for calibration measurement table
- Show timestamps of calibration measurements in local time
- Allow starting calibration measurements by double clicking row
2025-11-05 09:19:02 +01:00
Jan Käberich d77215aecb initialize new plots with the correct span
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-10-31 15:42:42 +01:00
Jan Käberich 7acb847a2b update compound device section in manual 2025-10-31 15:06:45 +01:00
Jan Käberich 92088ec646 Enable auto mode comobobox when auto range is active for X axis 2025-10-31 14:26:19 +01:00
Jan Käberich be123420db Configuration for hardware version 0xD0
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-10-28 12:08:13 +01:00
Jan Käberich 273dfa8037 add support for hardware version 0xD0 device status
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-10-24 12:55:09 +02:00
Jan Käberich 6e2079fcfb menu entry to run internal alignment if supported by hardware version
Some checks are pending
Build / PC_Application_Ubuntu (push) Waiting to run
Build / PC_Application_RPi5 (push) Waiting to run
Build / PC_Application_Windows (push) Waiting to run
Build / PC_Application_OSX (push) Waiting to run
Build / PC_Application_OSX_13 (push) Waiting to run
Build / Embedded_Firmware (push) Waiting to run
HIL_Tests / Get_Repository (push) Waiting to run
HIL_Tests / PC_Application_RPi5 (push) Blocked by required conditions
HIL_Tests / Embedded_Firmware (push) Blocked by required conditions
HIL_Tests / HIL (push) Blocked by required conditions
Unit_Tests / Tests (push) Waiting to run
2025-10-23 12:57:49 +02:00
Jan Käberich ed699cfd6a hide device specific actions based on the hardware version
Some checks are pending
Build / PC_Application_Ubuntu (push) Waiting to run
Build / PC_Application_RPi5 (push) Waiting to run
Build / PC_Application_Windows (push) Waiting to run
Build / PC_Application_OSX (push) Waiting to run
Build / PC_Application_OSX_13 (push) Waiting to run
Build / Embedded_Firmware (push) Waiting to run
HIL_Tests / Get_Repository (push) Waiting to run
HIL_Tests / PC_Application_RPi5 (push) Blocked by required conditions
HIL_Tests / Embedded_Firmware (push) Blocked by required conditions
HIL_Tests / HIL (push) Blocked by required conditions
Unit_Tests / Tests (push) Waiting to run
2025-10-23 10:16:33 +02:00
Jan Käberich f51c6a0bce Manual control dialog for prototype 0xD0
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-10-15 10:23:08 +02:00
Jan Käberich 5d00d4786b new calibration type without receiver matching for lossy calibration standards
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-09-22 16:58:49 +02:00
Jan Käberich 3e327e7e6b option to use already existing measurements for calibration 2025-09-22 16:01:38 +02:00
Jan Käberich b2fd49d800 increase resolution for referenced data in 0xE0 manual control dialog 2025-09-17 10:47:27 +02:00
Jan Käberich f327a4b4a0 allow firmware updates for version 0xE0 2025-09-17 10:46:44 +02:00
Jan Käberich 5947584e63 manual control dialog for experimental hardware version 0xE0
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-08-31 18:49:38 +02:00
Jan Käberich 3071d8de72 Set new markers to middle of trace by default
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-08-28 07:59:58 +02:00
Jan Käberich d882bca547 rework editor creation for MarkerTraceDelegate
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-08-22 11:47:48 +02:00
Jan Käberich f0c7f289cb Bugfix: remove not used optional parameter
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-08-17 20:18:38 +02:00
Jan Käberich 8b44421ea3 improve PLL fractional divider algorithm 2025-08-17 19:37:10 +02:00
Jan Käberich 66d5bdd91b Add calibrated parameters table
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-08-15 21:07:34 +02:00
Jan Käberich 10ba138104 Allow manual calibration of incomplete trace sets
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-08-10 17:48:18 +02:00
Jan Käberich 2f018e3867 New calibration type: OSL 2025-08-10 17:17:53 +02:00
Jan Käberich 68e8d29e0c Calibration error term model view 2025-08-10 15:13:24 +02:00
Jan Käberich 1b3c1c4b3b Merge branch 'master' of github.com:jankae/LibreVNA
Some checks are pending
Build / PC_Application_Ubuntu (push) Waiting to run
Build / PC_Application_RPi5 (push) Waiting to run
Build / PC_Application_Windows (push) Waiting to run
Build / PC_Application_OSX (push) Waiting to run
Build / PC_Application_OSX_13 (push) Waiting to run
Build / Embedded_Firmware (push) Waiting to run
HIL_Tests / Get_Repository (push) Waiting to run
HIL_Tests / PC_Application_RPi5 (push) Blocked by required conditions
HIL_Tests / Embedded_Firmware (push) Blocked by required conditions
HIL_Tests / HIL (push) Blocked by required conditions
Unit_Tests / Tests (push) Waiting to run
2025-08-09 19:12:07 +02:00
Jan Käberich 38b4a6ba39 Bugfix: correct PLL configuration at the band switch frequency (25 MHz) 2025-08-09 19:11:32 +02:00
Jan Käberich abe2aefcf9 use PLL A to generate lowband source 2025-08-09 19:10:53 +02:00
Jan Käberich 3483b7baec
Merge pull request #323 from HanfG/master
change LM3370 MPN
2025-07-30 10:39:27 +02:00
hanf 1a21a49dfd change LM3370 MPN to LM3370SD-3021(defualt 1.2v for buck 1) 2025-07-28 01:36:34 +08:00
Jan Käberich 8f689637ff Marker display and handling improvements
- prevent interaction with invisible markers on graph
- helper markers get their own (reduced) context menu
- only update required columns in table when marker data changes
- persistent helper markers for peak table
- New format for peak table markers
2025-07-24 10:32:55 +02:00
Jan Käberich 67cffbb69e fix windows build workflow 2025-07-20 18:30:25 +02:00
Jan Käberich 2f256117db add unit tests for lin/log calibration detection 2025-07-19 13:38:49 +02:00
Jan Käberich 8e917faed4 add support for logarithmically spaced calibration points 2025-07-18 19:18:28 +02:00
Jan Käberich c1705290ff update workflows to use windows-2022 runner 2025-07-16 07:46:47 +02:00
Jan Käberich ceaf412c2a
Merge pull request #317 from alekssadowski95/master
Added README title for SEO and more immediate understanding
2025-07-16 07:36:46 +02:00
Aleksander Sadowski 514fcd180c
Added README title for SEO and more immediate understanding
Signed-off-by: aleksander.sadowski@alsado.de Aleksander Sadowski
2025-07-12 21:43:03 +02:00
Jan Käberich b3eb1735cc option to prevent electronic calibrations while LibreCAL temperature unstable 2025-06-24 08:50:43 +02:00
Jan Käberich 3330cee2ba remember last used load/save paths in preferences 2025-06-23 16:39:18 +02:00
Jan Käberich d8f042dcaa remember touchstone export settings 2025-06-23 16:08:06 +02:00
Jan Käberich d615089afe Merge branch 'master' of github.com:jankae/LibreVNA 2025-06-23 15:25:14 +02:00
Jan Käberich d6020a6a26 Custom expression bugfixes
- Off-by-one error when number of input samples changes
- race condition when closing custom expression edit dialog
- adjust available variables when input data type changes
2025-06-23 15:23:57 +02:00
116 changed files with 5950 additions and 466 deletions

View file

@ -17,7 +17,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev
qtchooser -install qt6 $(which qmake6) qtchooser -install qt6 $(which qmake6)
- name: Get build timestamp - name: Get build timestamp
@ -57,7 +57,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev
- name: Get build timestamp - name: Get build timestamp
id: id_date id: id_date
@ -88,22 +88,22 @@ jobs:
path: Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI path: Software/PC_Application/LibreVNA-GUI/LibreVNA-GUI
PC_Application_Windows: PC_Application_Windows:
runs-on: windows-2019 runs-on: windows-2022
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: '6.2.0' version: '6.2.4'
arch: 'win64_mingw81' arch: 'win64_mingw'
- name: Download libusb - name: Download libusb
run: | run: |
curl -o libusb.7z -L https://github.com/libusb/libusb/releases/download/v1.0.25/libusb-1.0.25.7z curl -o libusb.7z -L https://github.com/libusb/libusb/releases/download/v1.0.25/libusb-1.0.25.7z
7z x libusb.7z -r -olibusb 7z x libusb.7z -r -olibusb
Xcopy /E /I /Y libusb\include ..\Qt\6.2.0\mingw81_64\include Xcopy /E /I /Y libusb\include %QT_ROOT_DIR%\include
Xcopy /E /I /Y libusb\MinGW64\static\libusb-1.0.a Software\PC_Application\LibreVNA-GUI Xcopy /E /I /Y libusb\MinGW64\static\libusb-1.0.a Software\PC_Application\LibreVNA-GUI
shell: cmd shell: cmd
@ -134,10 +134,9 @@ jobs:
cd Software/PC_Application/LibreVNA-GUI/release cd Software/PC_Application/LibreVNA-GUI/release
del *.o *.cpp del *.o *.cpp
windeployqt.exe . windeployqt.exe .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libwinpthread-1.dll . copy %QT_ROOT_DIR%\bin\libwinpthread-1.dll .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libgcc_s_seh-1.dll . copy %QT_ROOT_DIR%\bin\libgcc_s_seh-1.dll .
copy "..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libstdc++-6.dll" . copy %QT_ROOT_DIR%\bin\Qt6OpenGL.dll .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\Qt6OpenGL.dll .
shell: cmd shell: cmd
- name: Upload - name: Upload

View file

@ -19,7 +19,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev qt6-svg-dev
- name: Build application - name: Build application
run: | run: |

View file

@ -17,7 +17,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev zip sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev zip
qtchooser -install qt6 $(which qmake6) qtchooser -install qt6 $(which qmake6)
- name: Get app version - name: Get app version
@ -72,7 +72,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev qt6-svg-dev
- name: Get app version - name: Get app version
id: id_version id: id_version
@ -104,22 +104,22 @@ jobs:
PC_Application_Windows: PC_Application_Windows:
needs: PC_Application_Ubuntu needs: PC_Application_Ubuntu
runs-on: windows-2019 runs-on: windows-2022
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v4
with: with:
version: '6.2.0' version: '6.2.4'
arch: 'win64_mingw81' arch: 'win64_mingw'
- name: Download libusb - name: Download libusb
run: | run: |
curl -o libusb.7z -L https://github.com/libusb/libusb/releases/download/v1.0.25/libusb-1.0.25.7z curl -o libusb.7z -L https://github.com/libusb/libusb/releases/download/v1.0.25/libusb-1.0.25.7z
7z x libusb.7z -r -olibusb 7z x libusb.7z -r -olibusb
Xcopy /E /I /Y libusb\include ..\Qt\6.2.0\mingw81_64\include Xcopy /E /I /Y libusb\include %QT_ROOT_DIR%\include
Xcopy /E /I /Y libusb\MinGW64\static\libusb-1.0.a Software\PC_Application\LibreVNA-GUI Xcopy /E /I /Y libusb\MinGW64\static\libusb-1.0.a Software\PC_Application\LibreVNA-GUI
shell: cmd shell: cmd
@ -145,10 +145,9 @@ jobs:
cd Software/PC_Application/LibreVNA-GUI/release cd Software/PC_Application/LibreVNA-GUI/release
del *.o *.cpp del *.o *.cpp
windeployqt.exe . windeployqt.exe .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libwinpthread-1.dll . copy %QT_ROOT_DIR%\bin\libwinpthread-1.dll .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libgcc_s_seh-1.dll . copy %QT_ROOT_DIR%\bin\libgcc_s_seh-1.dll .
copy "..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\libstdc++-6.dll" . copy %QT_ROOT_DIR%\bin\Qt6OpenGL.dll .
copy ..\..\..\..\..\Qt\6.2.0\mingw81_64\bin\Qt6OpenGL.dll .
shell: cmd shell: cmd
- name: Zip app - name: Zip app

View file

@ -17,7 +17,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev sudo apt-get install -y libusb-1.0-0-dev qt6-tools-dev qt6-base-dev libqt6svg6-dev libgl-dev
qtchooser -install qt6 $(which qmake6) qtchooser -install qt6 $(which qmake6)
- name: Build Tests - name: Build Tests

View file

@ -123,6 +123,7 @@
\usepackage{xifthen} \usepackage{xifthen}
\newcommand{\vna}{LibreVNA} \newcommand{\vna}{LibreVNA}
\newcommand{\gui}{\vna{}-GUI} \newcommand{\gui}{\vna{}-GUI}
\newcommand{\subsubsubsection}[1]{\paragraph{#1}\mbox{}\\\\}
\newcommand{\screenshot}[2]{\begin{center} \newcommand{\screenshot}[2]{\begin{center}
\includegraphics[width=#1\textwidth]{Screenshots/#2} \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) \item Issue the command again (i.e. VNA:ACQ:SINGLE TRUE always triggers a new sweep)
\end{itemize} \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} \subsubsection{VNA:STIMulus:LVL}
\event{Sets the output power of the stimulus signal when sweep type is frequency}{VNA:STIMulus:LVL}{<power>, in dBm} \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} \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}LOAD\\
\hspace{1cm}THROUGH\\ \hspace{1cm}THROUGH\\
\hspace{1cm}ISOLATION\\ \hspace{1cm}ISOLATION\\
\hspace{1cm}SLIDINGLOAD\\
\hspace{1cm}REFLECT\\
\hspace{1cm}LINE\\
{[<standard>]}, calibration kit standard name, optional\\} {[<standard>]}, calibration kit standard name, optional\\}
\subsubsection{VNA:CALibration:TYPE} \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}SHORT\\
\hspace{1cm}LOAD\\ \hspace{1cm}LOAD\\
\hspace{1cm}THROUGH\\ \hspace{1cm}THROUGH\\
\hspace{1cm}ISOLATION\\} \hspace{1cm}ISOLATION\\
\hspace{1cm}SLIDINGLOAD\\
\hspace{1cm}REFLECT\\
\hspace{1cm}LINE\\}
\subsubsection{VNA:CALibration:PORT} \subsubsection{VNA:CALibration:PORT}
\event{Sets the port for the specified measurement}{VNA:CALibration:PORT}{<measurement number> <port number>} \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} \subsubsection{VNA:CALibration:LOAD}
\query{Loads a calibration file}{VNA:CALibration:LOAD?}{<filename>}{TRUE or FALSE} \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} \subsubsection{VNA:CALibration:KIT:SAVE}
\event{Saves the active calibration kit to a file}{VNA:CALibration:KIT:SAVE}{<filename>} \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: 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} \subsubsection{VNA:CALibration:KIT:LOAD}
\query{Loads a calibration kit file}{VNA:CALibration:KIT:LOAD?}{<filename>}{TRUE or FALSE} \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} \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. 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} \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} \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} \subsubsection{SA:TRACKing:ENable}
\event{Enables/disables the tracking generator}{SA:TRACKing:ENable}{<enabled>, option are TRUE, FALSE, 1 or 0} \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} \query{Queries whether tracking generator is enabled}{SA:TRACKing:ENable?}{None}{TRUE or FALSE}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

View file

@ -1410,14 +1410,14 @@ When connected to a compound device and in VNA mode, there is no phase informati
\end{important} \end{important}
\subsection{Creating a compound device} \subsection{Creating a compound device}
Compound devices must be configured in the preferences: \menu[,]{Window,Preferences,Compound Devices} Compound devices must be configured in the preferences:\linebreak\menu[,]{Window,Preferences,Device Drivers,LibreVNA/Compound}
\screenshot{1.0}{CompoundDeviceList.png} \screenshot{1.0}{CompoundDeviceList.png}
Create and remove compound devices with the buttons on the right. Edit an existing compound device by double-clicking it: Create and remove compound devices with the buttons on the right. Edit an existing compound device by double-clicking it:
\screenshot{1.0}{CompoundDeviceEdit.png} \screenshot{1.0}{CompoundDeviceEdit.png}
Required steps when creating a compound device: Required steps when creating a compound device:
\begin{enumerate} \begin{enumerate}
\item Assign a name to the new compound device \item Assign a name to the new compound device
\item Select the synchronization method between devices. At the moment, only USB synchronization is supported but future hardware versions might support faster synchronization via dedicated trigger ports \item Select the synchronization method between devices. At the moment, only GUI synchronization is supported but future hardware versions might support faster synchronization via dedicated trigger ports
\item Drag-and-drop a LibreVNA symbol into the configuration area for every physical device in the compound device \item Drag-and-drop a LibreVNA symbol into the configuration area for every physical device in the compound device
\begin{itemize} \begin{itemize}
\item At least two physical devices must be used \item At least two physical devices must be used

View file

@ -13075,7 +13075,7 @@
(hide yes) (hide yes)
) )
) )
(property "MPN" "LM3370SD-4221" (property "MPN" "LM3370SD-3021"
(at 101.6 190.5 0) (at 101.6 190.5 0)
(effects (effects
(font (font
@ -13970,7 +13970,7 @@
(hide yes) (hide yes)
) )
) )
(property "MPN" "LM3370SD-4221" (property "MPN" "LM3370SD-3021"
(at 101.6 231.14 0) (at 101.6 231.14 0)
(effects (effects
(font (font
@ -16667,7 +16667,7 @@
(hide yes) (hide yes)
) )
) )
(property "MPN" "LM3370SD-4221" (property "MPN" "LM3370SD-3021"
(at 208.28 180.34 0) (at 208.28 180.34 0)
(effects (effects
(font (font

View file

@ -1,3 +1,5 @@
# LibreVNA
![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/Build.yml/badge.svg) ![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/Test.yml/badge.svg) ![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/HIL_Tests.yml/badge.svg) ![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/Build.yml/badge.svg) ![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/Test.yml/badge.svg) ![Build workflow status](https://github.com/jankae/LibreVNA/actions/workflows/HIL_Tests.yml/badge.svg)
![LibreVNA](Software/PC_Application/LibreVNA-GUI/resources/banner.png) ![LibreVNA](Software/PC_Application/LibreVNA-GUI/resources/banner.png)

View file

@ -4,6 +4,7 @@
#include "caldevice.h" #include "caldevice.h"
#include "usbdevice.h" #include "usbdevice.h"
#include "CustomWidgets/informationbox.h" #include "CustomWidgets/informationbox.h"
#include "preferences.h"
#include <set> #include <set>
@ -234,6 +235,14 @@ void LibreCALDialog::updateCalibrationStartStatus()
canStart = validatePortSelection(true); canStart = validatePortSelection(true);
} }
if(canStart) {
if(!Preferences::getInstance().Acquisition.allowUseOfUnstableLibreCALTemp && !device->stabilized()) {
canStart = false;
ui->lCalibrationStatus->setText("LibreCAL temperature unstable");
ui->lCalibrationStatus->setStyleSheet("QLabel { color : red; }");
}
}
ui->start->setEnabled(canStart); ui->start->setEnabled(canStart);
if(canStart) { if(canStart) {
ui->lCalibrationStatus->setText("Ready to start"); ui->lCalibrationStatus->setText("Ready to start");
@ -259,6 +268,7 @@ void LibreCALDialog::updateDeviceStatus()
ui->lDeviceStatus->setText("Heating up, please wait with calibration"); ui->lDeviceStatus->setText("Heating up, please wait with calibration");
ui->lDeviceStatus->setStyleSheet("QLabel { color : orange; }"); ui->lDeviceStatus->setStyleSheet("QLabel { color : orange; }");
} }
updateCalibrationStartStatus();
} }
void LibreCALDialog::determineAutoPorts() void LibreCALDialog::determineAutoPorts()
@ -420,6 +430,7 @@ void LibreCALDialog::stopSweep()
void LibreCALDialog::startCalibration() void LibreCALDialog::startCalibration()
{ {
disableUI(); disableUI();
busy = true;
ui->progressCal->setValue(0); ui->progressCal->setValue(0);
ui->lCalibrationStatus->setText("Creating calibration kit from coefficients..."); ui->lCalibrationStatus->setText("Creating calibration kit from coefficients...");
@ -592,6 +603,7 @@ void LibreCALDialog::startCalibration()
disconnect(cal, &Calibration::measurementsUpdated, this, nullptr); disconnect(cal, &Calibration::measurementsUpdated, this, nullptr);
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None)); setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
enableUI(); enableUI();
busy = false;
break; break;
} }
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None)); setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
@ -609,6 +621,7 @@ void LibreCALDialog::startCalibration()
connect(cal, &Calibration::measurementsAborted, this, [=](){ connect(cal, &Calibration::measurementsAborted, this, [=](){
setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None)); setTerminationOnAllUsedPorts(CalDevice::Standard(CalDevice::Standard::Type::None));
enableUI(); enableUI();
busy = false;
ui->lCalibrationStatus->setText("Ready to start"); ui->lCalibrationStatus->setText("Ready to start");
}); });

View file

@ -6,6 +6,7 @@
#include "Util/util.h" #include "Util/util.h"
#include "LibreCAL/librecaldialog.h" #include "LibreCAL/librecaldialog.h"
#include "preferences.h" #include "preferences.h"
#include "Traces/sparamtraceselectordialog.h"
#include "Tools/Eigen/Dense" #include "Tools/Eigen/Dense"
@ -301,7 +302,9 @@ QString Calibration::TypeToString(Calibration::Type type)
{ {
switch(type) { switch(type) {
case Type::None: return "None"; case Type::None: return "None";
case Type::OSL: return "OSL";
case Type::SOLT: return "SOLT"; case Type::SOLT: return "SOLT";
case Type::SOLTwithoutRxMatch: return "SOLTwithoutRxMatch";
case Type::ThroughNormalization: return "ThroughNormalization"; case Type::ThroughNormalization: return "ThroughNormalization";
case Type::TRL: return "TRL"; case Type::TRL: return "TRL";
case Type::Last: return "Invalid"; case Type::Last: return "Invalid";
@ -407,7 +410,7 @@ void Calibration::correctTraces(std::map<QString, Trace *> traceSet)
} }
} }
void Calibration::edit() void Calibration::edit(TraceModel *traceModel)
{ {
auto d = new QDialog(); auto d = new QDialog();
d->setAttribute(Qt::WA_DeleteOnClose); d->setAttribute(Qt::WA_DeleteOnClose);
@ -509,6 +512,10 @@ void Calibration::edit()
ui->bDelete->setEnabled(ui->table->currentRow() >= 0); ui->bDelete->setEnabled(ui->table->currentRow() >= 0);
ui->bMoveUp->setEnabled(ui->table->currentRow() >= 1); ui->bMoveUp->setEnabled(ui->table->currentRow() >= 1);
ui->bMoveDown->setEnabled(ui->table->currentRow() >= 0 && ui->table->currentRow() < ui->table->rowCount() - 1); ui->bMoveDown->setEnabled(ui->table->currentRow() >= 0 && ui->table->currentRow() < ui->table->rowCount() - 1);
auto selected = ui->table->selectionModel()->selectedRows();
ui->measure->setEnabled(selected.size() > 0);
ui->selectMeasurement->setEnabled(traceModel && selected.size() == 1);
ui->clearMeasurement->setEnabled(selected.size() > 0);
}; };
auto updateMeasurementTable = [=](){ auto updateMeasurementTable = [=](){
@ -526,8 +533,9 @@ void Calibration::edit()
ui->table->setCellWidget(i, 1, measurements[i]->createStandardWidget()); ui->table->setCellWidget(i, 1, measurements[i]->createStandardWidget());
ui->table->setCellWidget(i, 2, measurements[i]->createSettingsWidget()); ui->table->setCellWidget(i, 2, measurements[i]->createSettingsWidget());
ui->table->setItem(i, 3, measurements[i]->getStatisticsItem()); ui->table->setItem(i, 3, measurements[i]->getStatisticsItem());
ui->table->setItem(i, 4, new QTableWidgetItem(measurements[i]->getTimestamp().toString())); ui->table->setItem(i, 4, new QTableWidgetItem(measurements[i]->getTimestamp().toLocalTime().toString()));
} }
ui->table->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->table->selectRow(row); ui->table->selectRow(row);
updateTableEditButtons(); updateTableEditButtons();
}; };
@ -618,6 +626,40 @@ void Calibration::edit()
emit startMeasurements(m); emit startMeasurements(m);
}); });
// double clicking on a row also starts the measurement
connect(ui->table, &QTableWidget::doubleClicked, this, [=](){
emit ui->measure->clicked();
});
connect(ui->selectMeasurement, &QPushButton::clicked, [=](){
auto selected = ui->table->selectionModel()->selectedRows();
if(selected.size() != 1) {
InformationBox::ShowError("Unable to select measurement", "Exactly one measurement must be selected");
return;
}
// figure out which S parameters we need
auto meas = measurements[selected[0].row()];
auto ports = meas->getPorts();
if(ports.size() == 0) {
InformationBox::ShowError("Unable to select measurement", "Selecting measurements for this type of calibration measurement is not supported");
return;
}
auto selector = new SParamTraceSelectorDialog(*traceModel, ports);
connect(selector, &SParamTraceSelectorDialog::tracesSelected, d, [=](std::vector<DeviceDriver::VNAMeasurement> traceMeasurements){
clearMeasurements({meas});
for(const auto &tm : traceMeasurements) {
addMeasurements({meas}, tm);
}
updateMeasurementTable();
updateCalibrationList();
});
selector->show();
});
if(!traceModel) {
// can not select a measurement if no trace model is supplied
ui->selectMeasurement->setEnabled(false);
}
connect(this, &Calibration::measurementsUpdated, d, [=](){ connect(this, &Calibration::measurementsUpdated, d, [=](){
updateMeasurementTable(); updateMeasurementTable();
updateCalibrationList(); updateCalibrationList();
@ -664,7 +706,7 @@ void Calibration::edit()
}); });
}); });
QObject::connect(ui->table, &QTableWidget::currentCellChanged, updateTableEditButtons); QObject::connect(ui->table, &QTableWidget::itemSelectionChanged, updateTableEditButtons);
auto addMenu = new QMenu(); auto addMenu = new QMenu();
for(auto t : CalibrationMeasurement::Base::availableTypes()) { for(auto t : CalibrationMeasurement::Base::availableTypes()) {
@ -708,19 +750,19 @@ Calibration::Point Calibration::createInitializedPoint(double f) {
point.frequency = f; point.frequency = f;
// resize vectors // resize vectors
point.D.resize(caltype.usedPorts.size(), 0.0); point.D.resize(caltype.usedPorts.size(), 0.0);
point.R.resize(caltype.usedPorts.size(), 0.0); point.R.resize(caltype.usedPorts.size(), 1.0);
point.S.resize(caltype.usedPorts.size(), 0.0); point.S.resize(caltype.usedPorts.size(), 0.0);
point.L.resize(caltype.usedPorts.size()); point.L.resize(caltype.usedPorts.size());
point.T.resize(caltype.usedPorts.size()); point.T.resize(caltype.usedPorts.size());
point.I.resize(caltype.usedPorts.size()); point.I.resize(caltype.usedPorts.size());
fill(point.L.begin(), point.L.end(), vector<complex<double>>(caltype.usedPorts.size(), 0.0)); fill(point.L.begin(), point.L.end(), vector<complex<double>>(caltype.usedPorts.size(), 0.0));
fill(point.T.begin(), point.T.end(), vector<complex<double>>(caltype.usedPorts.size(), 0.0)); fill(point.T.begin(), point.T.end(), vector<complex<double>>(caltype.usedPorts.size(), 1.0));
fill(point.I.begin(), point.I.end(), vector<complex<double>>(caltype.usedPorts.size(), 0.0)); fill(point.I.begin(), point.I.end(), vector<complex<double>>(caltype.usedPorts.size(), 0.0));
return point; return point;
} }
Calibration::Point Calibration::computeSOLT(double f) Calibration::Point Calibration::computeOSL(double f)
{ {
Point point = createInitializedPoint(f); Point point = createInitializedPoint(f);
@ -762,6 +804,13 @@ Calibration::Point Calibration::computeSOLT(double f)
auto delta = (l_c * l_m * (o_m - s_m) + o_c * o_m * (s_m - l_m) + s_c * s_m * (l_m - o_m)) / denom; auto delta = (l_c * l_m * (o_m - s_m) + o_c * o_m * (s_m - l_m) + s_c * s_m * (l_m - o_m)) / denom;
point.R[i] = point.D[i] * point.S[i] - delta; point.R[i] = point.D[i] * point.S[i] - delta;
} }
return point;
}
Calibration::Point Calibration::computeSOLT(double f)
{
Point point = computeOSL(f);
// calculate forward match and transmission // calculate forward match and transmission
for(unsigned int i=0;i<caltype.usedPorts.size();i++) { for(unsigned int i=0;i<caltype.usedPorts.size();i++) {
for(unsigned int j=0;j<caltype.usedPorts.size();j++) { for(unsigned int j=0;j<caltype.usedPorts.size();j++) {
@ -801,6 +850,17 @@ Calibration::Point Calibration::computeSOLT(double f)
return point; return point;
} }
Calibration::Point Calibration::computeSOLTwithoutRxMatch(double f) {
// This is very similar to SOLT but it assumes that receiver matching at the VNA is perfect.
// It can be used if the through calibration standard is very lossy which would result in
// very noisy values for the receiver match
auto p = computeSOLT(f);
for(auto &l : p.L) {
fill(l.begin(), l.end(), 0.0);
}
return p;
}
Calibration::Point Calibration::computeThroughNormalization(double f) Calibration::Point Calibration::computeThroughNormalization(double f)
{ {
Point point = createInitializedPoint(f); Point point = createInitializedPoint(f);
@ -1278,6 +1338,117 @@ bool Calibration::validForDevice(QString serial) const
} }
} }
bool Calibration::hasDirectivity(unsigned int port)
{
unsigned int index = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), port) - caltype.usedPorts.begin();
if(points.size() == 0 || index >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(0.0, 0.0);
for(const auto &p : points) {
if(p.D[index] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasReflectionTracking(unsigned int port)
{
unsigned int index = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), port) - caltype.usedPorts.begin();
if(points.size() == 0 || index >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(1.0, 0.0);
for(const auto &p : points) {
if(p.R[index] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasSourceMatch(unsigned int port)
{
unsigned int index = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), port) - caltype.usedPorts.begin();
if(points.size() == 0 || index >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(0.0, 0.0);
for(const auto &p : points) {
if(p.S[index] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasReceiverMatch(unsigned int sourcePort, unsigned int receivePort)
{
unsigned int indexSrc = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), sourcePort) - caltype.usedPorts.begin();
unsigned int indexRcv = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), receivePort) - caltype.usedPorts.begin();
if(points.size() == 0 || indexSrc >= caltype.usedPorts.size() || indexRcv >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(0.0, 0.0);
for(const auto &p : points) {
if(p.L[indexSrc][indexRcv] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasTransmissionTracking(unsigned int sourcePort, unsigned int receivePort)
{
unsigned int indexSrc = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), sourcePort) - caltype.usedPorts.begin();
unsigned int indexRcv = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), receivePort) - caltype.usedPorts.begin();
if(points.size() == 0 || indexSrc >= caltype.usedPorts.size() || indexRcv >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(1.0, 0.0);
for(const auto &p : points) {
if(p.T[indexSrc][indexRcv] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasIsolation(unsigned int sourcePort, unsigned int receivePort)
{
unsigned int indexSrc = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), sourcePort) - caltype.usedPorts.begin();
unsigned int indexRcv = std::find(caltype.usedPorts.begin(), caltype.usedPorts.end(), receivePort) - caltype.usedPorts.begin();
if(points.size() == 0 || indexSrc >= caltype.usedPorts.size() || indexRcv >= caltype.usedPorts.size()) {
// no calibration or it does not contain this port
return false;
}
auto def = std::complex<double>(0.0, 0.0);
for(const auto &p : points) {
if(p.I[indexSrc][indexRcv] != def) {
// at least one point does not match the default value -> we have a valid calibration for this
return true;
}
}
// all points still at default value
return false;
}
bool Calibration::hasUnsavedChanges() const bool Calibration::hasUnsavedChanges() const
{ {
return unsavedChanges; return unsavedChanges;
@ -1487,11 +1658,12 @@ bool Calibration::toFile(QString filename)
{ {
if(filename.isEmpty()) { if(filename.isEmpty()) {
QString fn = descriptiveCalName(); QString fn = descriptiveCalName();
filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", fn, "Calibration files (*.cal)", nullptr, Preferences::QFileDialogOptions()); filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", Preferences::getInstance().UISettings.Paths.cal + "/" + fn, "Calibration files (*.cal)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return false; return false;
} }
Preferences::getInstance().UISettings.Paths.cal = QFileInfo(filename).path();
} }
if(filename.toLower().endsWith(".cal")) { if(filename.toLower().endsWith(".cal")) {
@ -1511,11 +1683,12 @@ bool Calibration::toFile(QString filename)
bool Calibration::fromFile(QString filename) bool Calibration::fromFile(QString filename)
{ {
if(filename.isEmpty()) { if(filename.isEmpty()) {
filename = QFileDialog::getOpenFileName(nullptr, "Load calibration data", "", "Calibration files (*.cal)", nullptr, Preferences::QFileDialogOptions()); filename = QFileDialog::getOpenFileName(nullptr, "Load calibration data", Preferences::getInstance().UISettings.Paths.cal, "Calibration files (*.cal)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return false; return false;
} }
Preferences::getInstance().UISettings.Paths.cal = QFileInfo(filename).path();
} }
// force correct file ending // force correct file ending
@ -1535,10 +1708,8 @@ bool Calibration::fromFile(QString filename)
} }
calkit_file.append(".calkit"); calkit_file.append(".calkit");
qDebug() << "Associated calibration kit expected in" << calkit_file; qDebug() << "Associated calibration kit expected in" << calkit_file;
try { if(!kit.fromFile(calkit_file)) {
kit = Calkit::fromFile(calkit_file); qDebug() << "Parsing of calibration kit failed while opening calibration file (ignore for calibration format >= 3)";
} catch (runtime_error &e) {
qDebug() << "Parsing of calibration kit failed while opening calibration file: " << e.what() << " (ignore for calibration format >= 3)";
} }
ifstream file; ifstream file;
@ -1604,7 +1775,7 @@ std::vector<Calibration::Type> Calibration::getTypes()
return types; return types;
} }
bool Calibration::canCompute(Calibration::CalType type, double *startFreq, double *stopFreq, int *points) bool Calibration::canCompute(Calibration::CalType type, double *startFreq, double *stopFreq, int *points, bool *isLog)
{ {
using RequiredMeasurements = struct { using RequiredMeasurements = struct {
CalibrationMeasurement::Base::Type type; CalibrationMeasurement::Base::Type type;
@ -1615,6 +1786,15 @@ bool Calibration::canCompute(Calibration::CalType type, double *startFreq, doubl
case Type::None: case Type::None:
return true; // Always possible to reset the calibration return true; // Always possible to reset the calibration
case Type::SOLT: case Type::SOLT:
case Type::SOLTwithoutRxMatch:
// through measurements between all ports
for(unsigned int i=1;i<=type.usedPorts.size();i++) {
for(unsigned int j=i+1;j<=type.usedPorts.size();j++) {
required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j});
}
}
[[fallthrough]];
case Type::OSL:
// SOL measurements for every port // SOL measurements for every port
for(auto p : type.usedPorts) { for(auto p : type.usedPorts) {
required.push_back({.type = CalibrationMeasurement::Base::Type::Short, .port1 = p, .port2 = 0}); required.push_back({.type = CalibrationMeasurement::Base::Type::Short, .port1 = p, .port2 = 0});
@ -1627,12 +1807,6 @@ bool Calibration::canCompute(Calibration::CalType type, double *startFreq, doubl
required.push_back({.type = CalibrationMeasurement::Base::Type::Load, .port1 = p, .port2 = 0}); required.push_back({.type = CalibrationMeasurement::Base::Type::Load, .port1 = p, .port2 = 0});
} }
} }
// through measurements between all ports
for(unsigned int i=1;i<=type.usedPorts.size();i++) {
for(unsigned int j=i+1;j<=type.usedPorts.size();j++) {
required.push_back({.type = CalibrationMeasurement::Base::Type::Through, .port1 = i, .port2 = j});
}
}
break; break;
case Type::ThroughNormalization: case Type::ThroughNormalization:
// through measurements between all ports // through measurements between all ports
@ -1674,7 +1848,7 @@ bool Calibration::canCompute(Calibration::CalType type, double *startFreq, doubl
foundMeasurements.push_back(meas); foundMeasurements.push_back(meas);
} }
} }
return hasFrequencyOverlap(foundMeasurements, startFreq, stopFreq, points); return hasFrequencyOverlap(foundMeasurements, startFreq, stopFreq, points, isLog);
} }
return false; return false;
} }
@ -1688,17 +1862,25 @@ bool Calibration::compute(Calibration::CalType type)
} }
double start, stop; double start, stop;
int numPoints; int numPoints;
if(!canCompute(type, &start, &stop, &numPoints)) { bool isLog;
if(!canCompute(type, &start, &stop, &numPoints, &isLog)) {
return false; return false;
} }
caltype = type; caltype = type;
try { try {
points.clear(); points.clear();
for(int i=0;i<numPoints;i++) { for(int i=0;i<numPoints;i++) {
double f = start + (stop - start) * i / (numPoints - 1); double f;
if(!isLog) {
f = start + (stop - start) * i / (numPoints - 1);
} else {
f = start * pow(10.0, i * log10(stop / start) / (numPoints - 1));
}
Point p; Point p;
switch(type.type) { switch(type.type) {
case Type::OSL: p = computeOSL(f); break;
case Type::SOLT: p = computeSOLT(f); break; case Type::SOLT: p = computeSOLT(f); break;
case Type::SOLTwithoutRxMatch: p = computeSOLTwithoutRxMatch(f); break;
case Type::ThroughNormalization: p = computeThroughNormalization(f); break; case Type::ThroughNormalization: p = computeThroughNormalization(f); break;
case Type::TRL: p = computeTRL(f); break; case Type::TRL: p = computeTRL(f); break;
case Type::None: case Type::None:
@ -1726,7 +1908,9 @@ void Calibration::reset()
int Calibration::minimumPorts(Calibration::Type type) int Calibration::minimumPorts(Calibration::Type type)
{ {
switch(type) { switch(type) {
case Type::OSL: return 1;
case Type::SOLT: return 1; case Type::SOLT: return 1;
case Type::SOLTwithoutRxMatch: return 2;
case Type::ThroughNormalization: return 2; case Type::ThroughNormalization: return 2;
case Type::TRL: return 2; case Type::TRL: return 2;
case Type::None: case Type::None:
@ -1791,12 +1975,12 @@ void Calibration::createDefaultMeasurements(Calibration::DefaultMeasurements dm)
{ {
lock_guard<recursive_mutex> guard(access); lock_guard<recursive_mutex> guard(access);
auto createSOL = [=](int port) { auto createSOL = [=](int port) {
auto _short = new CalibrationMeasurement::Short(this);
_short->setPort(port);
measurements.push_back(_short);
auto open = new CalibrationMeasurement::Open(this); auto open = new CalibrationMeasurement::Open(this);
open->setPort(port); open->setPort(port);
measurements.push_back(open); measurements.push_back(open);
auto _short = new CalibrationMeasurement::Short(this);
_short->setPort(port);
measurements.push_back(_short);
auto load = new CalibrationMeasurement::Load(this); auto load = new CalibrationMeasurement::Load(this);
load->setPort(port); load->setPort(port);
measurements.push_back(load); measurements.push_back(load);
@ -1850,11 +2034,13 @@ void Calibration::deleteMeasurements()
measurements.clear(); measurements.clear();
} }
bool Calibration::hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base *> m, double *startFreq, double *stopFreq, int *points) bool Calibration::hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base *> m, double *startFreq, double *stopFreq, int *points, bool *isLog)
{ {
double minResolution = std::numeric_limits<double>::max(); double minResolution = std::numeric_limits<double>::max();
double minFreq = 0; double minFreq = 0;
double maxFreq = std::numeric_limits<double>::max(); double maxFreq = std::numeric_limits<double>::max();
unsigned int logCount = 0;
unsigned int linCount = 0;
for(auto meas : m) { for(auto meas : m) {
if(meas->numPoints() < 2) { if(meas->numPoints() < 2) {
return false; return false;
@ -1869,6 +2055,38 @@ bool Calibration::hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base *
if(resolution < minResolution) { if(resolution < minResolution) {
minResolution = resolution; minResolution = resolution;
} }
// check whether the frequency points are more linear or more logarithmic
double minDiff = std::numeric_limits<double>::max();
double maxDiff = 0;
double minRatio = std::numeric_limits<double>::max();
double maxRatio = 0;
for(unsigned int i=1;i<meas->numPoints();i++) {
double f_prev = meas->getPointFreq(i-1);
double f_next = meas->getPointFreq(i);
double diff = f_next - f_prev;
double ratio = f_next / f_prev;
if(diff < minDiff) {
minDiff = diff;
}
if(diff > maxDiff) {
maxDiff = diff;
}
if(ratio < minRatio) {
minRatio = ratio;
}
if(ratio > maxRatio) {
maxRatio = ratio;
}
}
double diffVariationNormalized = (maxDiff - minDiff) / maxDiff;
double ratioVariationNormalized = (maxRatio - minRatio) / maxRatio;
if(abs(diffVariationNormalized) < abs(ratioVariationNormalized)) {
// more linear
linCount++;
} else {
// more logarithmic
logCount++;
}
} }
if(startFreq) { if(startFreq) {
*startFreq = minFreq; *startFreq = minFreq;
@ -1879,6 +2097,9 @@ bool Calibration::hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base *
if(points) { if(points) {
*points = (maxFreq - minFreq) / minResolution + 1; *points = (maxFreq - minFreq) / minResolution + 1;
} }
if(isLog) {
*isLog = logCount > linCount;
}
if(maxFreq > minFreq) { if(maxFreq > minFreq) {
return true; return true;
} else { } else {

View file

@ -14,12 +14,15 @@ class Calibration : public QObject, public Savable, public SCPINode
Q_OBJECT Q_OBJECT
friend class LibreCALDialog; friend class LibreCALDialog;
friend class CalibrationTests;
public: public:
Calibration(); Calibration();
enum class Type { enum class Type {
None, None,
OSL,
SOLT, SOLT,
SOLTwithoutRxMatch,
ThroughNormalization, ThroughNormalization,
TRL, TRL,
Last, Last,
@ -44,7 +47,7 @@ public:
void correctTraces(std::map<QString, Trace*> traceSet); void correctTraces(std::map<QString, Trace*> traceSet);
// Starts the calibration edit dialog, allowing the user to make/delete measurements // Starts the calibration edit dialog, allowing the user to make/delete measurements
void edit(); void edit(TraceModel *traceModel = nullptr);
Calkit& getKit(); Calkit& getKit();
@ -62,7 +65,7 @@ public:
static std::vector<Type> getTypes(); static std::vector<Type> getTypes();
// Checks whether all measurements for a specific calibration are available. // Checks whether all measurements for a specific calibration are available.
// If pointer to the frequency/points variables are given, the start/stop frequency and number of points the calibration will have after the calculation is stored there // If pointer to the frequency/points variables are given, the start/stop frequency and number of points the calibration will have after the calculation is stored there
bool canCompute(CalType type, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); bool canCompute(CalType type, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr, bool *isLog = nullptr);
// Resets the calibration (deletes all measurements and calculated coefficients) // Resets the calibration (deletes all measurements and calculated coefficients)
void reset(); void reset();
// Returns the minimum number of ports for a given calibration type. // Returns the minimum number of ports for a given calibration type.
@ -98,6 +101,14 @@ public:
QString getValidDevice() const; QString getValidDevice() const;
bool validForDevice(QString serial) const; bool validForDevice(QString serial) const;
// query whether error terms coefficients are available. Port count starts at 1
bool hasDirectivity(unsigned int port);
bool hasReflectionTracking(unsigned int port);
bool hasSourceMatch(unsigned int port);
bool hasReceiverMatch(unsigned int sourcePort, unsigned int receivePort);
bool hasTransmissionTracking(unsigned int sourcePort, unsigned int receivePort);
bool hasIsolation(unsigned int sourcePort, unsigned int receivePort);
public slots: public slots:
// Call once all datapoints of the current span have been added // Call once all datapoints of the current span have been added
void measurementsComplete(); void measurementsComplete();
@ -130,7 +141,7 @@ private:
void createDefaultMeasurements(DefaultMeasurements dm); void createDefaultMeasurements(DefaultMeasurements dm);
void deleteMeasurements(); void deleteMeasurements();
bool hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base*> m, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr); static bool hasFrequencyOverlap(std::vector<CalibrationMeasurement::Base*> m, double *startFreq = nullptr, double *stopFreq = nullptr, int *points = nullptr, bool *isLog = nullptr);
// returns all measurements that match the paramaters // returns all measurements that match the paramaters
std::vector<CalibrationMeasurement::Base*> findMeasurements(CalibrationMeasurement::Base::Type type, int port1 = 0, int port2 = 0); std::vector<CalibrationMeasurement::Base*> findMeasurements(CalibrationMeasurement::Base::Type type, int port1 = 0, int port2 = 0);
// returns the first measurement in the list that matches the parameters // returns the first measurement in the list that matches the parameters
@ -151,7 +162,9 @@ private:
std::vector<Point> points; std::vector<Point> points;
Point createInitializedPoint(double f); Point createInitializedPoint(double f);
Point computeOSL(double f);
Point computeSOLT(double f); Point computeSOLT(double f);
Point computeSOLTwithoutRxMatch(double f);
Point computeThroughNormalization(double f); Point computeThroughNormalization(double f);
Point computeTRL(double f); Point computeTRL(double f);

View file

@ -48,7 +48,7 @@
<item> <item>
<widget class="QTableWidget" name="table"> <widget class="QTableWidget" name="table">
<property name="selectionBehavior"> <property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum> <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property> </property>
<attribute name="horizontalHeaderStretchLastSection"> <attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool> <bool>true</bool>
@ -148,6 +148,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="selectMeasurement">
<property name="text">
<string>Select
Measurement</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="clearMeasurement"> <widget class="QPushButton" name="clearMeasurement">
<property name="text"> <property name="text">
@ -185,7 +193,7 @@ Kit</string>
<item> <item>
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
@ -287,7 +295,7 @@ Kit</string>
<item> <item>
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Orientation::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>

View file

@ -627,6 +627,10 @@ void CalibrationMeasurement::Isolation::addPoint(const DeviceDriver::VNAMeasurem
QString name = meas.first; QString name = meas.first;
unsigned int rcv = name.mid(1, 1).toInt() - 1; unsigned int rcv = name.mid(1, 1).toInt() - 1;
unsigned int src = name.mid(2, 1).toInt() - 1; unsigned int src = name.mid(2, 1).toInt() - 1;
if(rcv > 8 || src > 8) {
// skip
continue;
}
if(rcv >= p.S.size()) { if(rcv >= p.S.size()) {
p.S.resize(rcv + 1); p.S.resize(rcv + 1);
} }

View file

@ -41,6 +41,7 @@ public:
virtual double maxUsableFreq() = 0; virtual double maxUsableFreq() = 0;
virtual double minFreq() = 0; virtual double minFreq() = 0;
virtual double maxFreq() = 0; virtual double maxFreq() = 0;
virtual double getPointFreq(unsigned int p) = 0;
virtual unsigned int numPoints() = 0; virtual unsigned int numPoints() = 0;
virtual bool readyForMeasurement() {return false;} virtual bool readyForMeasurement() {return false;}
virtual bool readyForCalculation() {return false;} virtual bool readyForCalculation() {return false;}
@ -60,6 +61,8 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
virtual std::vector<unsigned int> getPorts() = 0;
static bool canMeasureSimultaneously(std::set<Base *> measurements); static bool canMeasureSimultaneously(std::set<Base *> measurements);
QDateTime getTimestamp() const; QDateTime getTimestamp() const;
@ -88,6 +91,7 @@ public:
virtual double maxUsableFreq() override; virtual double maxUsableFreq() override;
virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();} virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();}
virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;} virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;}
virtual double getPointFreq(unsigned int p) override { return p < points.size() ? points[p].frequency : 0;}
virtual unsigned int numPoints() override {return points.size();} virtual unsigned int numPoints() override {return points.size();}
virtual bool readyForMeasurement() override {return standard != nullptr;} virtual bool readyForMeasurement() override {return standard != nullptr;}
virtual bool readyForCalculation() override {return standard && points.size() > 0;} virtual bool readyForCalculation() override {return standard && points.size() > 0;}
@ -100,6 +104,8 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
virtual std::vector<unsigned int> getPorts() override {return {port};}
class Point { class Point {
public: public:
double frequency; double frequency;
@ -207,6 +213,7 @@ public:
virtual double maxUsableFreq() override; virtual double maxUsableFreq() override;
virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();} virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();}
virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;} virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;}
virtual double getPointFreq(unsigned int p) override { return p < points.size() ? points[p].frequency : 0;}
virtual unsigned int numPoints() override {return points.size();} virtual unsigned int numPoints() override {return points.size();}
virtual bool readyForMeasurement() override {return standard != nullptr;} virtual bool readyForMeasurement() override {return standard != nullptr;}
virtual bool readyForCalculation() override {return standard && points.size() > 0;} virtual bool readyForCalculation() override {return standard && points.size() > 0;}
@ -219,6 +226,8 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
virtual std::vector<unsigned int> getPorts() override {return {port1, port2};}
class Point { class Point {
public: public:
double frequency; double frequency;
@ -281,6 +290,7 @@ public:
virtual double maxUsableFreq() override {return maxFreq();} virtual double maxUsableFreq() override {return maxFreq();}
virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();} virtual double minFreq() override {return points.size() > 0 ? points.front().frequency : std::numeric_limits<double>::max();}
virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;} virtual double maxFreq() override {return points.size() > 0 ? points.back().frequency : 0;}
virtual double getPointFreq(unsigned int p) override { return p < points.size() ? points[p].frequency : 0;}
virtual unsigned int numPoints() override; virtual unsigned int numPoints() override;
virtual bool readyForMeasurement() override {return true;} virtual bool readyForMeasurement() override {return true;}
virtual bool readyForCalculation() override {return points.size() > 0;} virtual bool readyForCalculation() override {return points.size() > 0;}
@ -294,6 +304,8 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
virtual std::vector<unsigned int> getPorts() override {return {};}
class Point { class Point {
public: public:
double frequency; double frequency;

View file

@ -0,0 +1,314 @@
#include "calibrationviewdialog.h"
#include "ui_calibrationviewdialog.h"
#include <QGraphicsSimpleTextItem>
#include <QVector2D>
const QColor CalibrationViewDialog::colorNoCal = Qt::darkRed;
const QColor CalibrationViewDialog::colorHasCal = Qt::darkGreen;
CalibrationViewDialog::CalibrationViewDialog(Calibration *cal, unsigned int ports, QWidget *parent)
: QDialog(parent)
, ui(new Ui::CalibrationViewDialog)
, cal(cal)
, ports(ports)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
ui->port->setMaximum(ports);
scene = new QGraphicsScene();
populateScene();
ui->view->setScene(scene);
connect(ui->port, &QSpinBox::valueChanged, this, &CalibrationViewDialog::populateScene);
}
CalibrationViewDialog::~CalibrationViewDialog()
{
delete ui;
}
void CalibrationViewDialog::populateScene()
{
scene->clear();
auto colorF = QApplication::palette().text().color();
auto colorB = QApplication::palette().base().color();
auto pen = QPen(colorF);
auto drawDot = [this](float x, float y, float size, QPen pen = QPen(), QBrush brush = QBrush()) {
scene->addEllipse(x - size/2, y - size/2, size, size, pen, brush);
};
auto drawText = [this](float x, float y, QString s, QColor color, auto alignH = Qt::AlignCenter, auto alignV = Qt::AlignCenter, float rotation = 0.0f) {
auto text = scene->addText(s);
text->setRotation(rotation);
switch(alignH) {
default:
case Qt::AlignLeft: break;
case Qt::AlignCenter:
case Qt::AlignHCenter: x -= text->boundingRect().bottomRight().x()/2; break;
case Qt::AlignRight: x -= text->boundingRect().bottomRight().x(); break;
}
switch(alignV) {
default:
case Qt::AlignTop: break;
case Qt::AlignCenter:
case Qt::AlignVCenter: y -= text->boundingRect().bottomRight().y()/2; break;
case Qt::AlignBottom: y -= text->boundingRect().bottomRight().y(); break;
}
text->setPos(QPointF(x, y));
text->setDefaultTextColor(color);
};
auto drawPath = [this, drawText](QList<QPointF> vertices, QColor color, QString label = QString(), bool arrow = false) {
// draw lines
for(unsigned int i=1;i<vertices.size();i++) {
scene->addLine(QLineF(vertices[i-1], vertices[i]), color);
if(i == vertices.size() / 2) {
// this is the middle segment, add label and arrow if required
auto midPoint = (vertices[i-1]+vertices[i])/2;
QVector2D direction = QVector2D(vertices[i] - vertices[i-1]);
direction.normalize();
auto ortho = QVector2D(-direction.y(), direction.x());
if(arrow) {
auto poly = QPolygonF({
QPointF(midPoint + direction.toPointF()*arrowLength/2),
QPointF(midPoint - direction.toPointF()*arrowLength/2 + ortho.toPointF()*arrowWidth/2),
QPointF(midPoint - direction.toPointF()*arrowLength/2 - ortho.toPointF()*arrowWidth/2)
});
scene->addPolygon(poly, color, color);
}
if(!label.isEmpty()) {
auto pos = midPoint;
if(label.startsWith("_")) {
label.remove(0, 1);
pos -= ortho.toPointF() * labelDistance;
} else {
pos += ortho.toPointF() * labelDistance;
}
auto alignH = abs(direction.x()) > abs(direction.y()) ? Qt::AlignCenter : Qt::AlignLeft;
auto alignV = abs(direction.y()) > abs(direction.x()) ? Qt::AlignCenter : Qt::AlignTop;
drawText(pos.x(), pos.y(), label, color, alignH, alignV);
}
}
}
};
auto DUTwidth = 2 * ports * pathSpacing;
auto DUTstartX = ports * pathSpacing + 2*boxSpacing;
// set the overall scene size
scene->setSceneRect(0, 0, marginLeft + DUTstartX + DUTwidth + marginRight, marginTop + ports * portHeight + marginBottom);
// create the DUT
// rectangle
scene->addRect(marginLeft + DUTstartX, marginTop, DUTwidth, ports * portHeight, pen);
drawText(marginLeft + DUTstartX + DUTwidth/2, marginTop, "DUT", colorF, Qt::AlignCenter, Qt::AlignTop);
// ports
for(unsigned int i=1;i<=ports;i++) {
// input marker
drawDot(marginLeft + DUTstartX, marginTop + i*portHeight - portHeight/2 + portForwardYOffset, portSize, colorF, colorB);
// output marker
drawDot(marginLeft + DUTstartX, marginTop + i*portHeight - portHeight/2 + portReverseYOffset, portSize, colorF, colorB);
}
// the reflection path
drawPath({
QPointF(marginLeft + DUTstartX + portSize/2, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(marginLeft + DUTstartX + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(marginLeft + DUTstartX + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(marginLeft + DUTstartX + portSize/2, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, colorF, "_S"+QString::number(ui->port->value())+QString::number(ui->port->value()), true);
// the forward transmission paths
float xstart = marginLeft + DUTstartX + pathSpacing;
for(unsigned int i=1;i<=ports;i++) {
if((int) i == ui->port->value()) {
// skip, this is the reflection path
continue;
}
drawDot(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset, junctionSize, colorF, colorF);
drawPath({
QPointF(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
QPointF(marginLeft + DUTstartX + portSize/2, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
}, colorF, QString((int) i > ui->port->value() ? "_" : "") + "S"+QString::number(i)+QString::number(ui->port->value()), true);
xstart += pathSpacing;
}
// the reverse transmission paths
bool first = true;
for(unsigned int i=1;i<=ports;i++) {
if((int) i == ui->port->value()) {
// skip, this is the reflection path
continue;
}
if(first) {
first = false;
drawDot(marginLeft + DUTstartX + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
drawPath({
QPointF(marginLeft + DUTstartX + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, colorF, "", false);
} else {
drawDot(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
}
drawPath({
QPointF(marginLeft + DUTstartX + portSize/2, marginTop + i*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + i*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, colorF, QString((int) i > ui->port->value() ? "" : "_") + "S"+QString::number(ui->port->value())+QString::number(i), true);
xstart += pathSpacing;
}
// isolation, transmission and receiver match paths
xstart = marginLeft;
for(unsigned int i=1;i<=ports;i++) {
if((int) i == ui->port->value()) {
// skip, this is the reflection path
continue;
}
// isolation
drawDot(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset, junctionSize, colorF, colorF);
drawPath({
QPointF(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
}, colorF);
drawPath({
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
}, cal->hasIsolation(ui->port->value(), i) ? colorHasCal : colorNoCal, QString((int) i > ui->port->value() ? "_" : "") + "I"+QString::number(i)+QString::number(ui->port->value()), true);
drawPath({
QPointF(xstart + pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
QPointF(marginLeft, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
}, colorF);
xstart += pathSpacing;
// transmission
drawDot(xstart, marginTop + i*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
drawPath({
QPointF(marginLeft + DUTstartX - portSize / 2, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
QPointF(marginLeft + DUTstartX - pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
}, colorF);
drawPath({
QPointF(marginLeft + DUTstartX - pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
}, cal->hasTransmissionTracking(ui->port->value(), i) ? colorHasCal : colorNoCal, "_T"+QString::number(i)+QString::number(ui->port->value()), true);
// Reveicer match
drawDot(marginLeft + DUTstartX - pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
drawPath({
QPointF(marginLeft + DUTstartX - pathSpacing, marginTop + i*portHeight - portHeight/2 + portReverseYOffset),
QPointF(marginLeft + DUTstartX - pathSpacing, marginTop + i*portHeight - portHeight/2 + portForwardYOffset),
QPointF(marginLeft + DUTstartX - portSize/2, marginTop + i*portHeight - portHeight/2 + portForwardYOffset),
}, cal->hasReceiverMatch(ui->port->value(), i) ? colorHasCal : colorNoCal, "L"+QString::number(i)+QString::number(ui->port->value()), true);
}
// reflection error box
drawDot(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset, junctionSize, colorF, colorF);
drawDot(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset, junctionSize, colorF, colorF);
drawDot(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
drawDot(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset, junctionSize, colorF, colorF);
drawDot(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset, junctionSize, colorF, colorF);
// unity paths
drawPath({
QPointF(xstart, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(marginLeft + DUTstartX - portSize/2, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
}, colorF, "", true);
drawPath({
QPointF(marginLeft + DUTstartX - portSize/2, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, colorF);
drawPath({
QPointF(marginLeft + portSize/2, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, colorF);
// directivity
drawPath({
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, cal->hasDirectivity(ui->port->value()) ? colorHasCal : colorNoCal, "_D"+QString::number(ui->port->value()), true);
// reflection tracking
drawPath({
QPointF(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart + pathSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
}, cal->hasReflectionTracking(ui->port->value()) ? colorHasCal : colorNoCal, "_R"+QString::number(ui->port->value()), true);
// source match
drawPath({
QPointF(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portReverseYOffset),
QPointF(xstart + pathSpacing + boxSpacing, marginTop + ui->port->value()*portHeight - portHeight/2 + portForwardYOffset),
}, cal->hasSourceMatch(ui->port->value()) ? colorHasCal : colorNoCal, "S"+QString::number(ui->port->value()), true);
// create the VNA ports
for(unsigned int i=1;i<=ports;i++) {
// stimulus port
if(i == (unsigned int) ui->port->value()) {
drawDot(marginLeft, marginTop + i*portHeight - portHeight/2 + portForwardYOffset, portSize, colorF, colorB);
drawText(marginLeft - portSize/2, marginTop + i*portHeight - portHeight/2 + portForwardYOffset, "a"+QString::number(i), colorF, Qt::AlignRight, Qt::AlignCenter);
}
// output marker
drawDot(marginLeft, marginTop + i*portHeight - portHeight/2 + portReverseYOffset, portSize, colorF, colorB);
drawText(marginLeft - portSize/2, marginTop + i*portHeight - portHeight/2 + portReverseYOffset, "b"+QString::number(i), colorF, Qt::AlignRight, Qt::AlignCenter);
}
// Fill the measurement correction table
ui->table->clear();
ui->table->setRowCount(ports*ports);
ui->table->setColumnCount(2);
ui->table->setHorizontalHeaderLabels({"Parameter", "Calibration Status"});
for(unsigned int i=1;i<=ports;i++) {
for(unsigned int j=1;j<=ports;j++) {
auto row = (i-1)*ports+j-1;
// add parameter
ui->table->setItem(row, 0, new QTableWidgetItem("S"+QString::number(j)+QString::number(i)));
// check the calibration status
QString status = "Uncalibrated";
if(i == j) {
// check reflection parameters
if(cal->hasSourceMatch(i) && cal->hasDirectivity(i) && cal->hasReflectionTracking(i)) {
// we are calibrated
status = "Calibrated";
// check if we have enhanced responses
QList<int> enhanced;
for(unsigned int k=1;k<=ports;k++) {
if(k==i) {
continue;
}
if(cal->hasReceiverMatch(i, k)) {
enhanced.append(k);
}
}
if(enhanced.size() == 1) {
status += " with enhanced response from port "+QString::number(enhanced[0]);
} else if(enhanced.size() > 1) {
status += " with enhanced response from ports ";
for(unsigned int k=0;k<enhanced.size();k++) {
if(k == enhanced.size() - 1) {
status += " and "+QString::number(enhanced[k]);
} else if(k > 0) {
status += ", ";
}
status += QString::number(enhanced[k]);
}
}
}
} else {
// check transmission calibration
if(cal->hasTransmissionTracking(i, j)) {
// we are calibrated
status = "Calibrated";
// check if we have isolation terms
if(cal->hasIsolation(i, j)) {
status += " with isolation measurement";
}
}
}
// add calibration status
ui->table->setItem(row, 1, new QTableWidgetItem(status));
}
}
ui->table->resizeColumnsToContents();
}

View file

@ -0,0 +1,51 @@
#ifndef CALIBRATIONVIEWDIALOG_H
#define CALIBRATIONVIEWDIALOG_H
#include <QDialog>
#include "calibration.h"
#include <QGraphicsScene>
namespace Ui {
class CalibrationViewDialog;
}
class CalibrationViewDialog : public QDialog
{
Q_OBJECT
public:
explicit CalibrationViewDialog(Calibration *cal, unsigned int ports, QWidget *parent = nullptr);
~CalibrationViewDialog();
private slots:
void populateScene();
private:
static constexpr int marginTop = 10;
static constexpr int marginBottom = 10;
static constexpr int marginLeft = 30;
static constexpr int marginRight = 10;
static constexpr int portHeight = 170;
static constexpr int portForwardYOffset = -50;
static constexpr int portReverseYOffset = 50;
static constexpr int boxSpacing = portReverseYOffset - portForwardYOffset;
static constexpr int portSize = 10;
static constexpr int arrowLength = 15;
static constexpr int arrowWidth = 10;
static constexpr int junctionSize = 6;
static constexpr int labelDistance = 6;
static constexpr int pathSpacing = 40;
static const QColor colorNoCal;
static const QColor colorHasCal;
Ui::CalibrationViewDialog *ui;
Calibration *cal;
unsigned int ports;
QGraphicsScene *scene;
};
#endif // CALIBRATIONVIEWDIALOG_H

View file

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CalibrationViewDialog</class>
<widget class="QDialog" name="CalibrationViewDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1106</width>
<height>665</height>
</rect>
</property>
<property name="windowTitle">
<string>Calibration Error Term Model</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>View error term model when stimulus is at port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="port">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style='color: darkgreen;'&gt;Green&lt;/span&gt; error terms have been calculated from calibration measurements.&lt;/p&gt;&lt;p&gt;&lt;span style='color: darkred;'&gt;Red&lt;/span&gt; error terms are at their default values (either 1 or 0).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QGraphicsView" name="view">
<property name="dragMode">
<enum>QGraphicsView::DragMode::ScrollHandDrag</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;D: Directivity, R: Reflection tracking, S: Source match, L: Receiver match, T: Transmission tracking, I: Isolation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Which measurements are being corrected?</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="table">
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SelectionMode::NoSelection</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CalibrationViewDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>374</x>
<y>509</y>
</hint>
<hint type="destinationlabel">
<x>374</x>
<y>265</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -16,13 +16,47 @@ using json = nlohmann::json;
using namespace std; using namespace std;
Calkit::Calkit() Calkit::Calkit()
: SCPINode("KIT") : SCPINode("KIT"), scpi_std("STAndard")
{ {
// set default values // set default values
filename = "";
for(auto e : descr) { for(auto e : descr) {
e.var.setValue(e.def); 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 { add(new SCPICommand("SAVE", [=](QStringList params) -> QString {
if(params.size() != 1 ) { if(params.size() != 1 ) {
// no filename given or no calibration active // no filename given or no calibration active
@ -30,19 +64,65 @@ Calkit::Calkit()
} }
toFile(params[0]); toFile(params[0]);
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr)); }, nullptr, false));
add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString { add(new SCPICommand("LOAD", nullptr, [=](QStringList params) -> QString {
if(params.size() != 1) { if(params.size() != 1) {
// no filename given or no calibration active // no filename given or no calibration active
return SCPI::getResultName(SCPI::Result::False); return SCPI::getResultName(SCPI::Result::False);
} }
try { if(this->fromFile(params[0])) {
*this = fromFile(params[0]);
return SCPI::getResultName(SCPI::Result::True); return SCPI::getResultName(SCPI::Result::True);
} catch (runtime_error &e) { } else {
return SCPI::getResultName(SCPI::Result::False); 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) void Calkit::toFile(QString filename)
@ -57,6 +137,7 @@ void Calkit::toFile(QString filename)
file.open(filename.toStdString()); file.open(filename.toStdString());
file << setw(4) << toJSON() << endl; file << setw(4) << toJSON() << endl;
file.close(); file.close();
this->filename = filename;
} }
static QString readLine(ifstream &file) { static QString readLine(ifstream &file) {
@ -65,27 +146,29 @@ static QString readLine(ifstream &file) {
return QString::fromStdString(line).simplified(); 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; ifstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());
if(!file.is_open()) { if(!file.is_open()) {
throw runtime_error("Unable to open file"); qWarning() << "Unable to open file: " << filename;
return false;
} }
json j; json j;
try { try {
file >> j; file >> j;
} catch (exception &e) { } 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")) { if(j.contains("standards")) {
qDebug() << "new JSON format detected"; qDebug() << "new JSON format detected";
c.fromJSON(j); fromJSON(j);
} else { } else {
// older format is used // older format is used
struct { struct {
@ -133,7 +216,7 @@ Calkit Calkit::fromFile(QString filename)
if(j.contains("SOLT")) { if(j.contains("SOLT")) {
qDebug() << "old JSON format detected"; qDebug() << "old JSON format detected";
// calkit file uses json format, parse // calkit file uses json format, parse
Savable::parseJSON(j, c.descr); Savable::parseJSON(j, descr);
const std::vector<Savable::SettingDescription> descr_deprecated = {{ const std::vector<Savable::SettingDescription> descr_deprecated = {{
{&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0}, {&SOLT.open_m.Z0, "SOLT.Open.Param.Z0", 50.0},
{&SOLT.open_m.delay, "SOLT.Open.Param.Delay", 0.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()); ts.fromFile(SOLT.open_m.file.toStdString());
open_m->setMeasurement(ts, SOLT.open_m.Sparam); open_m->setMeasurement(ts, SOLT.open_m.Sparam);
} }
c.addStandard(open_m); addStandard(open_m);
if(SOLT.separate_male_female) { 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); 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) { if(SOLT.open_f.useMeasurements) {
@ -294,7 +377,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.open_f.file.toStdString()); ts.fromFile(SOLT.open_f.file.toStdString());
open_m->setMeasurement(ts, SOLT.open_f.Sparam); 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); 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()); ts.fromFile(SOLT.short_m.file.toStdString());
short_m->setMeasurement(ts, SOLT.short_m.Sparam); short_m->setMeasurement(ts, SOLT.short_m.Sparam);
} }
c.addStandard(short_m); addStandard(short_m);
if(SOLT.separate_male_female) { 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); 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) { if(SOLT.short_f.useMeasurements) {
@ -311,7 +394,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.short_f.file.toStdString()); ts.fromFile(SOLT.short_f.file.toStdString());
short_m->setMeasurement(ts, SOLT.short_f.Sparam); 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); 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()); ts.fromFile(SOLT.load_m.file.toStdString());
load_m->setMeasurement(ts, SOLT.load_m.Sparam); load_m->setMeasurement(ts, SOLT.load_m.Sparam);
} }
c.addStandard(load_m); addStandard(load_m);
if(SOLT.separate_male_female) { 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); 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) { if(SOLT.load_f.useMeasurements) {
@ -328,7 +411,7 @@ Calkit Calkit::fromFile(QString filename)
ts.fromFile(SOLT.load_f.file.toStdString()); ts.fromFile(SOLT.load_f.file.toStdString());
load_m->setMeasurement(ts, SOLT.load_f.Sparam); 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); 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()); ts.fromFile(SOLT.Through.file.toStdString());
through->setMeasurement(ts, SOLT.Through.Sparam1, SOLT.Through.Sparam2); 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" 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"
@ -345,8 +428,10 @@ Calkit Calkit::fromFile(QString filename)
} }
file.close(); file.close();
this->filename = filename;
updateSCPINames();
return c; return true;
} }
void Calkit::edit(std::function<void (void)> updateCal) void Calkit::edit(std::function<void (void)> updateCal)
@ -364,10 +449,24 @@ void Calkit::edit(std::function<void (void)> updateCal)
void Calkit::clearStandards() void Calkit::clearStandards()
{ {
for(auto s : standards) { while(standards.size() > 0) {
delete s; 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 std::vector<CalStandard::Virtual *> Calkit::getStandards() const
@ -391,6 +490,14 @@ void Calkit::addStandard(CalStandard::Virtual *s)
} }
} }
standards.push_back(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() nlohmann::json Calkit::toJSON()
@ -411,6 +518,7 @@ nlohmann::json Calkit::toJSON()
void Calkit::fromJSON(nlohmann::json j) void Calkit::fromJSON(nlohmann::json j)
{ {
clearStandards(); clearStandards();
filename = "";
Savable::parseJSON(j, descr); Savable::parseJSON(j, descr);
for(auto js : j["standards"]) { for(auto js : j["standards"]) {
if(!js.contains("type") || !js.contains("params")) { if(!js.contains("type") || !js.contains("params")) {
@ -426,6 +534,7 @@ void Calkit::fromJSON(nlohmann::json j)
s->fromJSON(js["params"]); s->fromJSON(js["params"]);
addStandard(s); addStandard(s);
} }
updateSCPINames();
} }
void Calkit::setIdealDefault() 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::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::Load("Ideal Load Standard", 50.0, 0, 0, 50.0, 0, 0));
addStandard(new CalStandard::Through("Ideal Through Standard", 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; friend class LibreCALDialog;
public: public:
Calkit(); Calkit();
Calkit(const Calkit&) = default; Calkit(const Calkit&) = delete;
Calkit& operator=(const Calkit& other) Calkit& operator= (const Calkit&) = delete;
{
this->manufacturer = other.manufacturer;
this->serialnumber = other.serialnumber;
this->description = other.description;
this->standards = other.standards;
return *this;
}
class SOLT { class SOLT {
public: public:
@ -44,11 +37,12 @@ public:
}; };
void toFile(QString filename); void toFile(QString filename);
static Calkit fromFile(QString filename); bool fromFile(QString filename);
void edit(std::function<void(void)> updateCal = nullptr); void edit(std::function<void(void)> updateCal = nullptr);
std::vector<CalStandard::Virtual *> getStandards() const; std::vector<CalStandard::Virtual *> getStandards() const;
void addStandard(CalStandard::Virtual* s); void addStandard(CalStandard::Virtual* s);
void removeStandard(CalStandard::Virtual* s);
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
@ -57,7 +51,10 @@ public:
private: private:
void clearStandards(); void clearStandards();
void updateSCPINames();
QString manufacturer, serialnumber, description; QString manufacturer, serialnumber, description;
QString filename;
SCPINode scpi_std;
std::vector<CalStandard::Virtual*> standards; std::vector<CalStandard::Virtual*> standards;
const std::vector<Savable::SettingDescription> descr = {{ const std::vector<Savable::SettingDescription> descr = {{

View file

@ -27,8 +27,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
connect(ui->bDelete, &QPushButton::clicked, [=](){ connect(ui->bDelete, &QPushButton::clicked, [=](){
auto row = ui->list->currentRow(); auto row = ui->list->currentRow();
if(row >= 0) { if(row >= 0) {
delete kit.standards[row]; kit.removeStandard(kit.standards[row]);
kit.standards.erase(kit.standards.begin() + row);
updateStandardList(); updateStandardList();
} }
}); });
@ -39,6 +38,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
swap(kit.standards[row], kit.standards[row-1]); swap(kit.standards[row], kit.standards[row-1]);
ui->list->setCurrentRow(row-1); ui->list->setCurrentRow(row-1);
updateStandardList(); updateStandardList();
kit.updateSCPINames();
} }
}); });
@ -48,6 +48,7 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
swap(kit.standards[row], kit.standards[row+1]); swap(kit.standards[row], kit.standards[row+1]);
ui->list->setCurrentRow(row+1); ui->list->setCurrentRow(row+1);
updateStandardList(); updateStandardList();
kit.updateSCPINames();
} }
}); });
@ -89,10 +90,11 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
accept(); accept();
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Open calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(this, "Open calibration kit coefficients", Preferences::getInstance().UISettings.Paths.calkit, "Calibration kit files (*.calkit)", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.calkit = QFileInfo(filename).path();
try { try {
kit = Calkit::fromFile(filename); kit.fromFile(filename);
} catch (runtime_error &e) { } catch (runtime_error &e) {
InformationBox::ShowError("Error", "The calibration kit file could not be parsed (" + QString(e.what()) + ")"); 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(); qWarning() << "Parsing of calibration kit failed while opening calibration file: " << e.what();
@ -103,8 +105,9 @@ CalkitDialog::CalkitDialog(Calkit &c, QWidget *parent) :
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save calibration kit coefficients", "", "Calibration kit files (*.calkit)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(this, "Save calibration kit coefficients", Preferences::getInstance().UISettings.Paths.calkit, "Calibration kit files (*.calkit)", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.calkit = QFileInfo(filename).path();
parseEntries(); parseEntries();
kit.toFile(filename); kit.toFile(filename);
} }

View file

@ -12,6 +12,7 @@ using namespace std;
using namespace CalStandard; using namespace CalStandard;
Virtual::Virtual(QString name) : Virtual::Virtual(QString name) :
SCPINode(name),
name(name), name(name),
minFreq(std::numeric_limits<double>::lowest()), minFreq(std::numeric_limits<double>::lowest()),
maxFreq(std::numeric_limits<double>::max()) maxFreq(std::numeric_limits<double>::max())
@ -60,7 +61,7 @@ QString Virtual::TypeToString(Virtual::Type type)
Virtual::Type Virtual::TypeFromString(QString s) Virtual::Type Virtual::TypeFromString(QString s)
{ {
for(int i=0;i<(int) Type::Last;i++) { 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; return (Type) i;
} }
} }
@ -101,6 +102,11 @@ void Virtual::setName(const QString &value)
name = value; name = value;
} }
void Virtual::setupSCPI()
{
addStringParameter("NAME", name);
}
void OnePort::setMeasurement(const Touchstone &ts, int port) void OnePort::setMeasurement(const Touchstone &ts, int port)
{ {
if(!touchstone) { 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() Open::Open()
{ {
Z0 = 50.0; Z0 = 50.0;
delay = loss = C0 = C1 = C2 = C3 = 0.0; delay = loss = C0 = C1 = C2 = C3 = 0.0;
setupSCPI();
} }
std::complex<double> Open::toS11(double freq) std::complex<double> Open::toS11(double freq)
@ -263,10 +294,23 @@ void Open::fromJSON(nlohmann::json j)
C3 = j.value("C3", 0.0); 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() Short::Short()
{ {
Z0 = 50.0; Z0 = 50.0;
delay = loss = L0 = L1 = L2 = L3 = 0.0; delay = loss = L0 = L1 = L2 = L3 = 0.0;
setupSCPI();
} }
std::complex<double> Short::toS11(double freq) std::complex<double> Short::toS11(double freq)
@ -378,12 +422,25 @@ void Short::fromJSON(nlohmann::json j)
L3 = j.value("L3", 0.0); 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() Load::Load()
{ {
Z0 = 50.0; Z0 = 50.0;
resistance = 50.0; resistance = 50.0;
delay = Cparallel = Lseries = 0; delay = Cparallel = Lseries = 0;
Cfirst = true; Cfirst = true;
setupSCPI();
} }
std::complex<double> Load::toS11(double freq) std::complex<double> Load::toS11(double freq)
@ -519,6 +576,18 @@ void Load::fromJSON(nlohmann::json j)
Cfirst = j.value("Cfirst", true); 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) void TwoPort::setMeasurement(const Touchstone &ts, int port1, int port2)
{ {
if(!touchstone) { 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() Through::Through()
{ {
Z0 = 50.0; Z0 = 50.0;
delay = 0.0; delay = 0.0;
loss = 0.0; loss = 0.0;
setupSCPI();
} }
Sparam Through::toSparam(double freq) Sparam Through::toSparam(double freq)
@ -675,9 +770,18 @@ void Through::fromJSON(nlohmann::json j)
loss = j.value("loss", 0.0); 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() Reflect::Reflect()
{ {
isShort = true; isShort = true;
setupSCPI();
} }
std::complex<double> Reflect::toS11(double freq) std::complex<double> Reflect::toS11(double freq)
@ -724,10 +828,17 @@ bool Reflect::getIsShort() const
return isShort; return isShort;
} }
void Reflect::setupSCPI()
{
addBoolParameter("SHORT", isShort, true, true, [=](){clearMeasurement();});
OnePort::setupSCPI();
}
Line::Line() Line::Line()
{ {
Z0 = 50.0; Z0 = 50.0;
setDelay(0.0); setDelay(0.0);
setupSCPI();
} }
Sparam Line::toSparam(double freq) Sparam Line::toSparam(double freq)
@ -800,3 +911,10 @@ void Line::setDelay(double delay)
minFreq = 1.0 / delay * 20 / 360; minFreq = 1.0 / delay * 20 / 360;
maxFreq = 1.0 / delay * 160 / 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 "savable.h"
#include "touchstone.h" #include "touchstone.h"
#include "Tools/parameters.h" #include "Tools/parameters.h"
#include "scpi.h"
#include <complex> #include <complex>
#include <functional> #include <functional>
@ -11,7 +12,7 @@
namespace CalStandard namespace CalStandard
{ {
class Virtual : public QObject, public Savable class Virtual : public QObject, public Savable, public SCPINode
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -53,6 +54,7 @@ signals:
void deleted(); void deleted();
protected: protected:
void setupSCPI();
QString name; QString name;
double minFreq; double minFreq;
double maxFreq; double maxFreq;
@ -75,6 +77,7 @@ public:
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
protected: protected:
void setupSCPI();
Touchstone *touchstone; Touchstone *touchstone;
}; };
@ -85,7 +88,7 @@ class Open : public OnePort
public: public:
Open(); Open();
Open(QString name, double Z0, double delay, double loss, double C0, double C1, double C2, double C3) 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 std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override; virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -93,6 +96,7 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
private: private:
void setupSCPI();
double Z0, delay, loss, C0, C1, C2, C3; double Z0, delay, loss, C0, C1, C2, C3;
}; };
@ -101,7 +105,7 @@ class Short : public OnePort
public: public:
Short(); Short();
Short(QString name, double Z0, double delay, double loss, double L0, double L1, double L2, double L3) 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 std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override; virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -109,6 +113,7 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
private: private:
void setupSCPI();
double Z0, delay, loss, L0, L1, L2, L3; double Z0, delay, loss, L0, L1, L2, L3;
}; };
@ -117,7 +122,7 @@ class Load : public OnePort
public: public:
Load(); Load();
Load(QString name, double Z0, double delay, double loss, double resistance, double Cparallel, double Lseries, bool Cfirst = true) 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 std::complex<double> toS11(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override; virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -125,6 +130,7 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
private: private:
void setupSCPI();
double Z0, delay, loss, resistance, Cparallel, Lseries; double Z0, delay, loss, resistance, Cparallel, Lseries;
bool Cfirst; bool Cfirst;
}; };
@ -144,6 +150,7 @@ public:
bool getIsShort() const; bool getIsShort() const;
private: private:
void setupSCPI();
bool isShort; bool isShort;
}; };
@ -164,6 +171,7 @@ public:
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
protected: protected:
void setupSCPI();
Touchstone *touchstone; Touchstone *touchstone;
}; };
@ -172,7 +180,7 @@ class Through : public TwoPort
public: public:
Through(); Through();
Through(QString name, double Z0, double delay, double loss) 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 Sparam toSparam(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override; virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -180,6 +188,7 @@ public:
virtual nlohmann::json toJSON() override; virtual nlohmann::json toJSON() override;
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
private: private:
void setupSCPI();
double Z0, delay, loss; double Z0, delay, loss;
}; };
@ -188,7 +197,7 @@ class Line : public TwoPort
public: public:
Line(); Line();
Line(QString name, double Z0, double delay) 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 Sparam toSparam(double freq) override;
virtual void edit(std::function<void(void)> finishedCallback = nullptr) override; virtual void edit(std::function<void(void)> finishedCallback = nullptr) override;
@ -197,6 +206,7 @@ public:
virtual void fromJSON(nlohmann::json j) override; virtual void fromJSON(nlohmann::json j) override;
private: private:
void setDelay(double delay); void setDelay(double delay);
void setupSCPI();
double Z0, delay; double Z0, delay;
}; };

View file

@ -7,7 +7,7 @@ ManualCalibrationDialog::ManualCalibrationDialog(const TraceModel &model, Calibr
ui(new Ui::ManualCalibrationDialog) ui(new Ui::ManualCalibrationDialog)
{ {
ui->setupUi(this); ui->setupUi(this);
auto traceSelector = new SparamTraceSelector(model, cal->getCaltype().usedPorts); auto traceSelector = new SparamTraceSelector(model, cal->getCaltype().usedPorts, true);
ui->verticalLayout->insertWidget(1, traceSelector, 1.0); ui->verticalLayout->insertWidget(1, traceSelector, 1.0);
ui->buttonBox->setEnabled(false); ui->buttonBox->setEnabled(false);
connect(traceSelector, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled); connect(traceSelector, &SparamTraceSelector::selectionValid, ui->buttonBox, &QDialogButtonBox::setEnabled);

View file

@ -64,8 +64,9 @@ void CSVImport::selectTrace(unsigned int index)
void CSVImport::on_browse_clicked() void CSVImport::on_browse_clicked()
{ {
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", Preferences::getInstance().UISettings.Paths.data, "CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions());
if (filename.length() > 0) { if (filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
ui->file->setText(filename); ui->file->setText(filename);
evaluateFile(); evaluateFile();
} }

View file

@ -59,7 +59,7 @@ bool SIUnitEdit::eventFilter(QObject *, QEvent *event)
clearFocus(); clearFocus();
return true; return true;
} }
if(key == Qt::Key_Return) { if(key == Qt::Key_Return || key == Qt::Key_Enter) {
// use new value without prefix // use new value without prefix
parseNewValue(1.0); parseNewValue(1.0);
continueEditing(); continueEditing();

View file

@ -112,8 +112,9 @@ void TouchstoneImport::setFile(QString filename)
void TouchstoneImport::on_browse_clicked() void TouchstoneImport::on_browse_clicked()
{ {
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s1p *.s2p *.s3p *.s4p)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", Preferences::getInstance().UISettings.Paths.data, "Touchstone files (*.s1p *.s2p *.s3p *.s4p)", nullptr, Preferences::QFileDialogOptions());
if (filename.length() > 0) { if (filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
ui->file->setText(filename); ui->file->setText(filename);
} }
} }

View file

@ -0,0 +1,16 @@
#include "harogicb60.h"
HarogicB60::HarogicB60()
{
validUSBIDs.clear();
validUSBIDs.append({0x367F, 0x0200, "B60"});
for(auto &s : specificSettings) {
s.name.replace("LibreVNAUSBDriver", "HarogicB60Driver");
}
}
QString HarogicB60::getDriverName()
{
return "Harogic B60";
}

View file

@ -0,0 +1,19 @@
#ifndef HAROGICB60_H
#define HAROGICB60_H
#include "../LibreVNA/librevnausbdriver.h"
class HarogicB60 : public LibreVNAUSBDriver
{
Q_OBJECT
public:
HarogicB60();
/**
* @brief Returns the driver name. It must be unique across all implemented drivers and is used to identify the driver
* @return driver name
*/
virtual QString getDriverName() override;
};
#endif // HAROGICB60_H

View file

@ -44,11 +44,12 @@ AmplitudeCalDialog::AmplitudeCalDialog(LibreVNADriver *dev, QWidget *parent) :
connect(ui->saveFile, &QPushButton::clicked, [=](){ connect(ui->saveFile, &QPushButton::clicked, [=](){
auto fileEnding = pointType() == Protocol::PacketType::SourceCalPoint ? ".srccal" : ".recvcal"; auto fileEnding = pointType() == Protocol::PacketType::SourceCalPoint ? ".srccal" : ".recvcal";
auto fileFilter = QString("Amplitude calibration files (*")+fileEnding+")"; auto fileFilter = QString("Amplitude calibration files (*")+fileEnding+")";
auto filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", "", fileFilter, nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", Preferences::getInstance().UISettings.Paths.vnacaldata, fileFilter, nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.vnacaldata = QFileInfo(filename).path();
if(!filename.endsWith(fileEnding)) { if(!filename.endsWith(fileEnding)) {
filename.append(fileEnding); filename.append(fileEnding);
} }
@ -70,11 +71,12 @@ AmplitudeCalDialog::AmplitudeCalDialog(LibreVNADriver *dev, QWidget *parent) :
connect(ui->loadFile, &QPushButton::clicked, [=](){ connect(ui->loadFile, &QPushButton::clicked, [=](){
auto fileEnding = pointType() == Protocol::PacketType::SourceCalPoint ? ".srccal" : ".recvcal"; auto fileEnding = pointType() == Protocol::PacketType::SourceCalPoint ? ".srccal" : ".recvcal";
auto fileFilter = QString("Amplitude calibration files (*")+fileEnding+")"; auto fileFilter = QString("Amplitude calibration files (*")+fileEnding+")";
auto filename = QFileDialog::getOpenFileName(nullptr, "Save calibration data", "", fileFilter, nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Save calibration data", Preferences::getInstance().UISettings.Paths.vnacaldata, fileFilter, nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.vnacaldata = QFileInfo(filename).path();
ifstream file; ifstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());
if(!file.is_open()) { if(!file.is_open()) {

View file

@ -1,6 +1,9 @@
#include "deviceconfigurationdialogv1.h" #include "deviceconfigurationdialogv1.h"
#include "ui_deviceconfigurationdialogv1.h" #include "ui_deviceconfigurationdialogv1.h"
#include <QPushButton>
#include "CustomWidgets/informationbox.h"
DeviceConfigurationDialogV1::DeviceConfigurationDialogV1(LibreVNADriver &dev, QWidget *parent) : DeviceConfigurationDialogV1::DeviceConfigurationDialogV1(LibreVNADriver &dev, QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::DeviceConfigurationDialogV1), ui(new Ui::DeviceConfigurationDialogV1),
@ -56,6 +59,10 @@ DeviceConfigurationDialogV1::DeviceConfigurationDialogV1(LibreVNADriver &dev, QW
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [=](){ connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [=](){
reject(); reject();
}); });
connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, [=](){
resetDevice();
this->dev.sendWithoutPayload(Protocol::PacketType::RequestDeviceConfiguration);
});
} }
DeviceConfigurationDialogV1::~DeviceConfigurationDialogV1() DeviceConfigurationDialogV1::~DeviceConfigurationDialogV1()
@ -84,3 +91,12 @@ void DeviceConfigurationDialogV1::updateDevice()
p.deviceConfig.V1.PLLSettlingDelay = ui->PLLSettlingDelay->value(); p.deviceConfig.V1.PLLSettlingDelay = ui->PLLSettlingDelay->value();
dev.SendPacket(p); dev.SendPacket(p);
} }
void DeviceConfigurationDialogV1::resetDevice()
{
dev.sendWithoutPayload(Protocol::PacketType::ResetDeviceConfiguration, [=](LibreVNADriver::TransmissionResult res){
if(res != LibreVNADriver::TransmissionResult::Ack) {
InformationBox::ShowError("Error", "Failed to reset device configuration");
}
});
}

View file

@ -20,6 +20,7 @@ public:
private: private:
void updateGUI(const Protocol::DeviceConfig &c); void updateGUI(const Protocol::DeviceConfig &c);
void updateDevice(); void updateDevice();
void resetDevice();
Ui::DeviceConfigurationDialogV1 *ui; Ui::DeviceConfigurationDialogV1 *ui;
LibreVNADriver &dev; LibreVNADriver &dev;

View file

@ -164,7 +164,7 @@
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons"> <property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set> <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::RestoreDefaults</set>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -0,0 +1,72 @@
#include "deviceconfigurationdialogvd0.h"
#include "ui_deviceconfigurationdialogvd0.h"
DeviceConfigurationDialogVD0::DeviceConfigurationDialogVD0(LibreVNADriver &dev, QWidget *parent) :
QDialog(parent),
ui(new Ui::DeviceConfigurationDialogVD0),
dev(dev)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
emit dev.acquireControl();
auto updateIF = [=]() {
auto ADCrate = ui->ADCRate->value();
ui->IF->setValue(ADCrate * ui->ADCphaseInc->value() / 65536);
};
connect(ui->ADCRate, &SIUnitEdit::valueChanged, this, updateIF);
connect(ui->ADCphaseInc, qOverload<int>(&QSpinBox::valueChanged), this, updateIF);
ui->ADCRate->setUnit("Hz");
ui->ADCRate->setPrefixes(" kM");
ui->ADCRate->setPrecision(5);
ui->IF->setUnit("Hz");
ui->IF->setPrefixes(" kM");
ui->IF->setPrecision(5);
ui->ADCRate->setValue(1496000);
ui->ADCphaseInc->setValue(10240);
updateIF();
connect(&dev, &LibreVNADriver::receivedPacket, this, [=](const Protocol::PacketInfo &p) {
if(p.type == Protocol::PacketType::DeviceConfiguration) {
updateGUI(p.deviceConfig);
}
});
dev.sendWithoutPayload(Protocol::PacketType::RequestDeviceConfiguration);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, [=](){
updateDevice();
accept();
});
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [=](){
reject();
});
}
DeviceConfigurationDialogVD0::~DeviceConfigurationDialogVD0()
{
dev.releaseControl();
delete ui;
}
void DeviceConfigurationDialogVD0::updateGUI(const Protocol::DeviceConfig &c)
{
ui->ADCRate->setValue(c.VD0.ADCrate);
ui->ADCphaseInc->setValue(c.VD0.DFTphaseInc);
ui->PLLSettlingDelay->setValue(c.VD0.PLLSettlingDelay);
}
void DeviceConfigurationDialogVD0::updateDevice()
{
Protocol::PacketInfo p;
p.type = Protocol::PacketType::DeviceConfiguration;
p.deviceConfig.VD0.ADCrate = ui->ADCRate->value();
p.deviceConfig.VD0.DFTphaseInc = ui->ADCphaseInc->value();
p.deviceConfig.VD0.PLLSettlingDelay = ui->PLLSettlingDelay->value();
dev.SendPacket(p);
}

View file

@ -0,0 +1,28 @@
#ifndef DEVICECONFIGURATIONDIALOGVD0_H
#define DEVICECONFIGURATIONDIALOGVD0_H
#include <QDialog>
#include "librevnadriver.h"
namespace Ui {
class DeviceConfigurationDialogVD0;
}
class DeviceConfigurationDialogVD0 : public QDialog
{
Q_OBJECT
public:
explicit DeviceConfigurationDialogVD0(LibreVNADriver &dev, QWidget *parent = nullptr);
~DeviceConfigurationDialogVD0();
private:
void updateGUI(const Protocol::DeviceConfig &c);
void updateDevice();
Ui::DeviceConfigurationDialogVD0 *ui;
LibreVNADriver &dev;
};
#endif // DEVICECONFIGURATIONDIALOGVD0_H

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DeviceConfigurationDialogVD0</class>
<widget class="QDialog" name="DeviceConfigurationDialogVD0">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>421</width>
<height>314</height>
</rect>
</property>
<property name="windowTitle">
<string>Device Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>This dialog contains advanced system settings. It is recommended to leave them at default values unless you know what you are doing.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_15">
<property name="title">
<string>IF and ADC frequencies</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>ADC sample rate:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="SIUnitEdit" name="ADCRate">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>DFT Phase increment:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="ADCphaseInc">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Phase increment per ADC sample. Together with the ADC sample rate this determines the frequency of the second IF&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>10240</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>IF:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="IF">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Other settings</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>PLL settling delay [us]:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="PLLSettlingDelay">
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>60</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -29,11 +29,12 @@ DevicePacketLogView::DevicePacketLogView(QWidget *parent) :
updateTree(); updateTree();
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
QString filename = QFileDialog::getSaveFileName(nullptr, "Load LibreVNA log data", "", "LibreVNA log files (*.vnalog)", nullptr, Preferences::QFileDialogOptions()); QString filename = QFileDialog::getSaveFileName(nullptr, "Save LibreVNA log data", Preferences::getInstance().UISettings.Paths.packetlog, "LibreVNA log files (*.vnalog)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.packetlog = QFileInfo(filename).path();
if(!filename.endsWith(".vnalog")) { if(!filename.endsWith(".vnalog")) {
filename.append(".vnalog"); filename.append(".vnalog");
} }
@ -43,11 +44,12 @@ DevicePacketLogView::DevicePacketLogView(QWidget *parent) :
file.close(); file.close();
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
QString filename = QFileDialog::getOpenFileName(nullptr, "Load LibreVNA log data", "", "LibreVNA log files (*.vnalog)", nullptr, Preferences::QFileDialogOptions()); QString filename = QFileDialog::getOpenFileName(nullptr, "Load LibreVNA log data", Preferences::getInstance().UISettings.Paths.packetlog, "LibreVNA log files (*.vnalog)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.packetlog = QFileInfo(filename).path();
ifstream file; ifstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());
if(!file.is_open()) { if(!file.is_open()) {
@ -217,6 +219,21 @@ void DevicePacketLogView::addEntry(const DevicePacketLog::LogEntry &e)
addInteger(VFE, "MCU temperature", sFE.temp_MCU); addInteger(VFE, "MCU temperature", sFE.temp_MCU);
addDouble(VFE, "eCal temperature", (double) sFE.temp_eCal / 100.0); addDouble(VFE, "eCal temperature", (double) sFE.temp_eCal / 100.0);
addDouble(VFE, "eCal heater power", (double) sFE.power_heater / 1000.0); addDouble(VFE, "eCal heater power", (double) sFE.power_heater / 1000.0);
auto sD0 = e.p->status.VD0;
auto VD0 = new QTreeWidgetItem();
VD0->setData(2, Qt::DisplayRole, "VD0");
item->addChild(VD0);
addBool(VD0, "External reference available", sD0.extRefAvailable);
addBool(VD0, "External reference in use", sD0.extRefInUse);
addBool(VD0, "FPGA configured", sD0.FPGA_configured);
addBool(VD0, "Source locked", sD0.source_locked);
addBool(VD0, "LO locked", sD0.LO_locked);
addBool(VD0, "ADC overload", sD0.ADC_overload);
addBool(VD0, "Unlevel", sD0.unlevel);
addInteger(VD0, "MCU temperature", sD0.temp_MCU);
addInteger(VD0, "Supply voltage", sD0.supply_voltage);
addInteger(VD0, "Supply current", sD0.supply_current);
} }
break; break;
case Protocol::PacketType::DeviceInfo: { case Protocol::PacketType::DeviceInfo: {
@ -309,6 +326,32 @@ void DevicePacketLogView::addEntry(const DevicePacketLog::LogEntry &e)
addEnum(VFE, "Reference gain", s.VFE.RefGain, {"1 V/V", "10 V/V", "20 V/V", "30 V/V", "40 V/V", "60 V/V", "80 V/V", "120 V/V", "157 V/V", "0.25 V/V"}); addEnum(VFE, "Reference gain", s.VFE.RefGain, {"1 V/V", "10 V/V", "20 V/V", "30 V/V", "40 V/V", "60 V/V", "80 V/V", "120 V/V", "157 V/V", "0.25 V/V"});
addEnum(VFE, "eCal state", s.VFE.eCal_state, {"Port", "Open", "Short", "Load"}); addEnum(VFE, "eCal state", s.VFE.eCal_state, {"Port", "Open", "Short", "Load"});
addDouble(VFE, "eCal target temperature", (double) s.VFE.eCal_target / 100.0); addDouble(VFE, "eCal target temperature", (double) s.VFE.eCal_target / 100.0);
auto VD0 = new QTreeWidgetItem();
VD0->setData(2, Qt::DisplayRole, "VD0");
item->addChild(VD0);
addBool(VD0, "High Source chip enable", s.VD0.SourceHighCE);
addInteger(VD0, "High Source power", s.VD0.SourceHighPower);
addEnum(VD0, "High Source lowpass", s.VD0.SourceHighLowpass, {"947 MHz", "1880 MHz", "3550 MHz", "None"});
addDouble(VD0, "High Source frequency", s.VD0.SourceHighFrequency);
addBool(VD0, "Low Source enable", s.VD0.SourceLowEN);
addEnum(VD0, "Low Source power", s.VD0.SourceLowPower, {"2 mA", "4 mA", "6 mA", "8 mA"});
addDouble(VD0, "Low Source frequency", s.VD0.SourceLowFrequency);
addDouble(VD0, "Attenuator", s.VD0.attenuator * 0.25);
addEnum(VD0, "Source band selection", s.VD0.SourceHighband, {"Low Source", "High Source"});
addEnum(VD0, "Port switch", s.VD0.PortSwitch, {"Port 1", "Port 2"});
addBool(VD0, "High LO chip enable", s.VD0.LOHighCE);
addDouble(VD0, "High LO frequency", s.VD0.LOHighFrequency);
addInteger(VD0, "High LO power", s.VD0.LOHighPower);
addBool(VD0, "Low LO enable", s.VD0.LOLowEN);
addEnum(VD0, "Low LO power", s.VD0.LOLowPower, {"2 mA", "4 mA", "6 mA", "8 mA"});
addDouble(VD0, "Low LO frequency", s.VD0.LOLowFrequency);
addEnum(VD0, "LO band selection", s.VD0.LOHighband, {"Low Source", "High Source"});
addBool(VD0, "Port 1 receiver enable", s.VD0.Port1EN);
addBool(VD0, "Port 2 receiver enable", s.VD0.Port2EN);
addBool(VD0, "Reference receiver enable", s.VD0.RefEN);
addInteger(VD0, "Samples", s.VD0.Samples);
addEnum(VD0, "Window type", s.VD0.WindowType, {"None", "Kaiser", "Hann", "Flattop"});
} }
break; break;
case Protocol::PacketType::ManualStatus: { case Protocol::PacketType::ManualStatus: {
@ -362,6 +405,24 @@ void DevicePacketLogView::addEntry(const DevicePacketLog::LogEntry &e)
addBool(VFE, "LO locked", s.VFE.LO_locked); addBool(VFE, "LO locked", s.VFE.LO_locked);
addDouble(VFE, "eCal temperature", (double) s.VFE.temp_eCal / 100.0); addDouble(VFE, "eCal temperature", (double) s.VFE.temp_eCal / 100.0);
addDouble(VFE, "eCal heater power", (double) s.VFE.power_heater / 1000.0); addDouble(VFE, "eCal heater power", (double) s.VFE.power_heater / 1000.0);
auto VD0 = new QTreeWidgetItem();
VD0->setData(2, Qt::DisplayRole, "VD0");
item->addChild(VD0);
addInteger(VD0, "ADC port 1 minimum", s.VD0.port1min);
addInteger(VD0, "ADC port 1 maximum", s.VD0.port1max);
addInteger(VD0, "ADC port 2 minimum", s.VD0.port2min);
addInteger(VD0, "ADC port 2 maximum", s.VD0.port2max);
addInteger(VD0, "ADC reference minimum", s.VD0.refmin);
addInteger(VD0, "ADC reference maximum", s.VD0.refmax);
addDouble(VD0, "Port 1 real", s.VD0.port1real);
addDouble(VD0, "Port 1 imaginary", s.VD0.port1imag);
addDouble(VD0, "Port 2 real", s.VD0.port2real);
addDouble(VD0, "Port 2 imaginary", s.VD0.port2imag);
addDouble(VD0, "Reference real", s.VD0.refreal);
addDouble(VD0, "Reference imaginary", s.VD0.refimag);
addBool(VD0, "Source locked", s.VD0.source_locked);
addBool(VD0, "LO1 locked", s.VD0.LO_locked);
} }
break; break;
case Protocol::PacketType::SpectrumAnalyzerSettings: { case Protocol::PacketType::SpectrumAnalyzerSettings: {
@ -451,6 +512,7 @@ void DevicePacketLogView::addEntry(const DevicePacketLog::LogEntry &e)
addDouble(V1, "1.IF", s1.IF1, "Hz"); addDouble(V1, "1.IF", s1.IF1, "Hz");
addInteger(V1, "ADC prescaler", s1.ADCprescaler); addInteger(V1, "ADC prescaler", s1.ADCprescaler);
addInteger(V1, "DFT phase increment", s1.DFTphaseInc); addInteger(V1, "DFT phase increment", s1.DFTphaseInc);
addInteger(V1, "PLL settling delay", s1.PLLSettlingDelay);
auto sFF = e.p->deviceConfig.VFF; auto sFF = e.p->deviceConfig.VFF;
auto VFF = new QTreeWidgetItem(); auto VFF = new QTreeWidgetItem();
@ -471,6 +533,14 @@ void DevicePacketLogView::addEntry(const DevicePacketLog::LogEntry &e)
addBool(VFE, "PGA autogain", sFE.autogain); addBool(VFE, "PGA autogain", sFE.autogain);
addInteger(VFE, "Port gain", sFE.portGain); addInteger(VFE, "Port gain", sFE.portGain);
addInteger(VFE, "Reference gain", sFE.refGain); addInteger(VFE, "Reference gain", sFE.refGain);
auto sD0 = e.p->deviceConfig.VD0;
auto VD0 = new QTreeWidgetItem();
VD0->setData(2, Qt::DisplayRole, "VD0");
item->addChild(VD0);
addInteger(VD0, "ADC rate", sD0.ADCrate);
addInteger(VD0, "DFT phase increment", sD0.DFTphaseInc);
addInteger(VD0, "PLL settling delay", sD0.PLLSettlingDelay);
} }
break; break;
default: default:

View file

@ -52,8 +52,9 @@ bool FirmwareUpdateDialog::FirmwareUpdate(LibreVNADriver *dev, QString file)
void FirmwareUpdateDialog::on_bFile_clicked() void FirmwareUpdateDialog::on_bFile_clicked()
{ {
ui->bStart->setEnabled(false); ui->bStart->setEnabled(false);
auto filename = QFileDialog::getOpenFileName(nullptr, "Open firmware file", "", "Firmware file (*.vnafw)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Open firmware file", Preferences::getInstance().UISettings.Paths.firmware, "Firmware file (*.vnafw)", nullptr, Preferences::QFileDialogOptions());
if (filename.length() > 0) { if (filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.firmware = QFileInfo(filename).path();
ui->lFile->setText(filename); ui->lFile->setText(filename);
reloadFile(); reloadFile();
} }

View file

@ -3,9 +3,12 @@
#include "manualcontroldialogV1.h" #include "manualcontroldialogV1.h"
#include "manualcontroldialogvff.h" #include "manualcontroldialogvff.h"
#include "manualcontroldialogvfe.h" #include "manualcontroldialogvfe.h"
#include "manualcontroldialogVE0.h"
#include "manualcontroldialogVD0.h"
#include "deviceconfigurationdialogv1.h" #include "deviceconfigurationdialogv1.h"
#include "deviceconfigurationdialogvff.h" #include "deviceconfigurationdialogvff.h"
#include "deviceconfigurationdialogvfe.h" #include "deviceconfigurationdialogvfe.h"
#include "deviceconfigurationdialogvd0.h"
#include "firmwareupdatedialog.h" #include "firmwareupdatedialog.h"
#include "frequencycaldialog.h" #include "frequencycaldialog.h"
#include "sourcecaldialog.h" #include "sourcecaldialog.h"
@ -127,6 +130,12 @@ LibreVNADriver::LibreVNADriver()
case 1: case 1:
manualControlDialog = new ManualControlDialogV1(*this); manualControlDialog = new ManualControlDialogV1(*this);
break; break;
case 0xD0:
manualControlDialog = new ManualControlDialogVD0(*this);
break;
case 0xE0:
manualControlDialog = new ManualControlDialogVE0(*this);
break;
case 0xFE: case 0xFE:
manualControlDialog = new ManualControlDialogVFE(*this); manualControlDialog = new ManualControlDialogVFE(*this);
break; break;
@ -153,6 +162,9 @@ LibreVNADriver::LibreVNADriver()
case 1: case 1:
d = new DeviceConfigurationDialogV1(*this); d = new DeviceConfigurationDialogV1(*this);
break; break;
case 0xD0:
d = new DeviceConfigurationDialogVD0(*this);
break;
case 0xFE: case 0xFE:
d = new DeviceConfigurationDialogVFE(*this); d = new DeviceConfigurationDialogVFE(*this);
break; break;
@ -198,9 +210,26 @@ LibreVNADriver::LibreVNADriver()
}); });
specificActions.push_back(freqcal); specificActions.push_back(freqcal);
sep = new QAction(); auto internalAlignment = new QAction("Run Internal Alignment");
sep->setSeparator(true); connect(internalAlignment, &QAction::triggered, this, [=](){
specificActions.push_back(sep); emit acquireControl();
Protocol::PacketInfo p;
p.type = Protocol::PacketType::PerformAction;
p.performAction.action = Protocol::Action::InternalAlignment;
SendPacket(p, [=](TransmissionResult res){
if(res == TransmissionResult::Ack) {
InformationBox::ShowMessage("Success", "Internal alignment completed");
} else {
InformationBox::ShowError("Error", "Running internal alignment failed");
}
emit releaseControl();
}, 5000);
});
specificActions.push_back(internalAlignment);
auto sep2 = new QAction();
sep2->setSeparator(true);
specificActions.push_back(sep2);
auto log = new QAction("View Packet Log"); auto log = new QAction("View Packet Log");
connect(log, &QAction::triggered, this, [=](){ connect(log, &QAction::triggered, this, [=](){
@ -209,6 +238,14 @@ LibreVNADriver::LibreVNADriver()
}); });
specificActions.push_back(log); specificActions.push_back(log);
// set available actions for each hardware version
availableActions[0x01] = {manual, config, update, sep, srccal, recvcal, freqcal, sep2, log};
availableActions[0xD0] = {manual, config, update, sep, srccal, recvcal, freqcal, sep2, log};
availableActions[0xE0] = {manual, update, sep, srccal, recvcal, freqcal, internalAlignment, sep2, log};
availableActions[0xFD] = {manual, update, sep, srccal, recvcal, freqcal, sep2, log};
availableActions[0xFE] = {manual, config, update, sep, srccal, recvcal, freqcal, sep2, log};
availableActions[0xFF] = {manual, config, update, sep, srccal, recvcal, freqcal, sep2, log};
// Create driver specific commands // Create driver specific commands
specificSCPIcommands.push_back(new SCPICommand("DEVice:INFo:TEMPeratures", nullptr, [=](QStringList) -> QString { specificSCPIcommands.push_back(new SCPICommand("DEVice:INFo:TEMPeratures", nullptr, [=](QStringList) -> QString {
if(!connected) { if(!connected) {
@ -277,6 +314,17 @@ std::set<DeviceDriver::Flag> LibreVNADriver::getFlags()
ret.insert(Flag::Overload); ret.insert(Flag::Overload);
} }
break; break;
case 0xD0:
if(!lastStatus.VD0.source_locked || !lastStatus.VD0.LO_locked) {
ret.insert(Flag::Unlocked);
}
if(lastStatus.VD0.unlevel) {
ret.insert(Flag::Unlevel);
}
if(lastStatus.VD0.ADC_overload) {
ret.insert(Flag::Overload);
}
break;
case 0xFE: case 0xFE:
if(!lastStatus.VFE.source_locked || !lastStatus.VFE.LO_locked) { if(!lastStatus.VFE.source_locked || !lastStatus.VFE.LO_locked) {
ret.insert(Flag::Unlocked); ret.insert(Flag::Unlocked);
@ -322,6 +370,19 @@ QString LibreVNADriver::getStatus()
} }
} }
break; break;
case 0xD0:
ret.append(" Temps MCU: "+QString::number(lastStatus.VD0.temp_MCU)+"°C");
ret.append(" Supply: "+Unit::ToString((float) lastStatus.VD0.supply_voltage / 1000.0, "V", "m ", 3) + " " + Unit::ToString((float) lastStatus.VD0.supply_current / 1000.0, "A", "m ", 3));
ret.append(" Reference:");
if(lastStatus.VD0.extRefInUse) {
ret.append("External");
} else {
ret.append("Internal");
if(lastStatus.VD0.extRefAvailable) {
ret.append(" (External available)");
}
}
break;
case 0xFE: case 0xFE:
ret.append(" MCU Temp: "+QString::number(lastStatus.VFE.temp_MCU)+"°C"); ret.append(" MCU Temp: "+QString::number(lastStatus.VFE.temp_MCU)+"°C");
ret.append(" eCal Temp: "+QString::number(lastStatus.VFE.temp_eCal / 100.0)+"°C"); ret.append(" eCal Temp: "+QString::number(lastStatus.VFE.temp_eCal / 100.0)+"°C");
@ -705,6 +766,8 @@ void LibreVNADriver::handleReceivedPacket(const Protocol::PacketInfo &packet)
info.Limits.SA.maxdBm = (double) packet.info.limits_cdbm_max / 100; info.Limits.SA.maxdBm = (double) packet.info.limits_cdbm_max / 100;
limits_maxAmplitudePoints = packet.info.limits_maxAmplitudePoints; limits_maxAmplitudePoints = packet.info.limits_maxAmplitudePoints;
updateActionVisibility(hardwareVersion);
emit InfoUpdated(); emit InfoUpdated();
} }
break; break;
@ -769,12 +832,35 @@ QString LibreVNADriver::hardwareVersionToString(uint8_t version)
{ {
switch(version) { switch(version) {
case 0x01: return "1"; case 0x01: return "1";
case 0xD0: return "HAR0";
case 0xE0: return "SAP1";
case 0xFE: return "P2"; case 0xFE: return "P2";
case 0xFF: return "PT"; case 0xFF: return "PT";
default: return "Unknown"; default: return "Unknown";
} }
} }
void LibreVNADriver::updateActionVisibility(uint8_t hardwareVersion)
{
// only show actions for the correct hardware version
if(availableActions.contains(hardwareVersion)) {
// hide all actions
for(auto a : specificActions) {
a->setVisible(false);
}
// show the relevant actions
for(auto a : availableActions[hardwareVersion]) {
a->setVisible(true);
}
} else {
// the hardware version is unknown. This should not happen but just in case
// we set all actions to visible
for(auto a : specificActions) {
a->setVisible(true);
}
}
}
unsigned int LibreVNADriver::getProtocolVersion() const unsigned int LibreVNADriver::getProtocolVersion() const
{ {
return protocolVersion; return protocolVersion;
@ -789,6 +875,8 @@ QString LibreVNADriver::getFirmwareMagicString()
{ {
switch(hardwareVersion) { switch(hardwareVersion) {
case 0x01: return "VNA!"; case 0x01: return "VNA!";
case 0xD0: return "VHP1";
case 0xE0: return "VNS1";
case 0xFE: return "VNP2"; case 0xFE: return "VNP2";
case 0xFF: return "VNPT"; case 0xFF: return "VNPT";
default: return "XXXX"; default: return "XXXX";

View file

@ -197,6 +197,7 @@ protected slots:
void handleReceivedPacket(const Protocol::PacketInfo& packet); void handleReceivedPacket(const Protocol::PacketInfo& packet);
protected: protected:
QString hardwareVersionToString(uint8_t version); QString hardwareVersionToString(uint8_t version);
void updateActionVisibility(uint8_t hardwareVersion);
bool connected; bool connected;
unsigned int protocolVersion; unsigned int protocolVersion;
@ -228,6 +229,9 @@ protected:
bool VNASuppressInvalidPeaks; bool VNASuppressInvalidPeaks;
bool VNAAdjustPowerLevel; bool VNAAdjustPowerLevel;
// available actions per hardware version
QMap<uint8_t, QList<QAction*>> availableActions;
QDialog *manualControlDialog; QDialog *manualControlDialog;
}; };

View file

@ -8,16 +8,6 @@
using namespace std; using namespace std;
using USBID = struct {
int VID;
int PID;
};
static constexpr USBID IDs[] = {
{0x0483, 0x564e},
{0x0483, 0x4121},
{0x1209, 0x4121},
};
LibreVNAUSBDriver::LibreVNAUSBDriver() LibreVNAUSBDriver::LibreVNAUSBDriver()
: LibreVNADriver() : LibreVNADriver()
{ {
@ -30,6 +20,10 @@ LibreVNAUSBDriver::LibreVNAUSBDriver()
lastTimestamp = QDateTime::currentDateTime(); lastTimestamp = QDateTime::currentDateTime();
byteCnt = 0; byteCnt = 0;
validUSBIDs.append({0x0483, 0x564e, "VNA"});
validUSBIDs.append({0x0483, 0x4121, "VNA"});
validUSBIDs.append({0x1209, 0x4121, "VNA"});
specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNAUSBDriver.captureRawReceiverValues", false)); specificSettings.push_back(Savable::SettingDescription(&captureRawReceiverValues, "LibreVNAUSBDriver.captureRawReceiverValues", false));
specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNAUSBDriver.harmonicMixing", false)); specificSettings.push_back(Savable::SettingDescription(&harmonicMixing, "LibreVNAUSBDriver.harmonicMixing", false));
specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNAUSBDriver.signalID", true)); specificSettings.push_back(Savable::SettingDescription(&SASignalID, "LibreVNAUSBDriver.signalID", true));
@ -309,15 +303,15 @@ void LibreVNAUSBDriver::SearchDevices(std::function<bool (libusb_device_handle *
continue; continue;
} }
bool correctID = false; int IDindex = -1;
int numIDs = sizeof(IDs)/sizeof(IDs[0]); for(int i=0;i<validUSBIDs.size();i++) {
for(int i=0;i<numIDs;i++) { if(desc.idVendor == validUSBIDs[i].VID && desc.idProduct == validUSBIDs[i].PID) {
if(desc.idVendor == IDs[i].VID && desc.idProduct == IDs[i].PID) { IDindex = i;
correctID = true;
break; break;
} }
} }
if(!correctID) { if(IDindex == -1) {
// invalid VID/PID
continue; continue;
} }
@ -348,7 +342,7 @@ void LibreVNAUSBDriver::SearchDevices(std::function<bool (libusb_device_handle *
if (ret > 0) { if (ret > 0) {
/* managed to read the product string */ /* managed to read the product string */
QString product(c_product); QString product(c_product);
if (product == "VNA") { if (product == validUSBIDs[IDindex].deviceName) {
// this is a match // this is a match
if(!foundCallback(handle, QString(c_serial))) { if(!foundCallback(handle, QString(c_serial))) {
// abort search // abort search

View file

@ -54,7 +54,7 @@ private:
void USBHandleThread(); void USBHandleThread();
// foundCallback is called for every device that is found. If it returns true the search continues, otherwise it is aborted. // foundCallback is called for every device that is found. If it returns true the search continues, otherwise it is aborted.
// When the search is aborted the last found device is still opened // When the search is aborted the last found device is still opened
static void SearchDevices(std::function<bool(libusb_device_handle *handle, QString getSerial)> foundCallback, libusb_context *context, bool ignoreOpenError); void SearchDevices(std::function<bool(libusb_device_handle *handle, QString getSerial)> foundCallback, libusb_context *context, bool ignoreOpenError);
libusb_device_handle *m_handle; libusb_device_handle *m_handle;
libusb_context *m_context; libusb_context *m_context;
@ -82,6 +82,13 @@ private:
QDateTime lastTimestamp; QDateTime lastTimestamp;
unsigned long byteCnt; unsigned long byteCnt;
using USBID = struct {
int VID;
int PID;
QString deviceName;
};
protected:
QList<USBID> validUSBIDs;
}; };
#endif // LIBREVNAUSBDRIVER_H #endif // LIBREVNAUSBDRIVER_H

View file

@ -0,0 +1,812 @@
#include "manualcontroldialogVD0.h"
#include "ui_manualcontroldialogVD0.h"
#include "Util/util.h"
#include <QComboBox>
#include <QDebug>
#include <QButtonGroup>
#include <complex>
using namespace std;
ManualControlDialogVD0::ManualControlDialogVD0(LibreVNADriver &dev, QWidget *parent) :
QDialog(parent),
ui(new Ui::ManualControlDialogVD0),
dev(dev)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
emit dev.acquireControl();
ui->SourceLowFrequency->setUnit("Hz");
ui->SourceLowFrequency->setPrefixes(" kM");
ui->SourceLowFrequency->setPrecision(6);
ui->SourceLowFrequency->setValueQuiet(1000000);
ui->SourceHighFrequency->setUnit("Hz");
ui->SourceHighFrequency->setPrefixes(" kMG");
ui->SourceHighFrequency->setPrecision(6);
ui->SourceHighFrequency->setValueQuiet(1000000000);
ui->IFHigh->setUnit("Hz");
ui->IFHigh->setPrefixes(" kM");
ui->IFHigh->setPrecision(6);
ui->IFLow->setUnit("Hz");
ui->IFLow->setPrefixes(" kM");
ui->IFLow->setPrecision(6);
ui->LOHighFrequency->setUnit("Hz");
ui->LOHighFrequency->setPrefixes(" kMG");
ui->LOHighFrequency->setPrecision(6);
ui->LOLowFrequency->setUnit("Hz");
ui->LOLowFrequency->setPrefixes(" kM");
ui->LOLowFrequency->setPrecision(6);
auto UpdateLOHigh = [=]() {
double sourceFreq;
if (ui->SwitchLowband->isChecked()) {
sourceFreq = ui->SourceLowFrequency->value();
} else {
sourceFreq = ui->SourceHighFrequency->value();
}
if (ui->LOHighFreqType->currentIndex() == 0) {
// fixed IF mode
ui->LOHighFrequency->setValueQuiet(sourceFreq + ui->IFHigh->value());
} else {
// Manual Frequency mode
ui->IFHigh->setValueQuiet(ui->LOHighFrequency->value() - sourceFreq);
}
};
auto UpdateLOLow = [=]() {
double sourceFreq;
if (ui->SwitchLowband->isChecked()) {
sourceFreq = ui->SourceLowFrequency->value();
} else {
sourceFreq = ui->SourceHighFrequency->value();
}
if (ui->LOLowFreqType->currentIndex() == 0) {
// fixed IF mode
ui->LOLowFrequency->setValueQuiet(sourceFreq + ui->IFLow->value());
} else {
// Manual Frequency mode
ui->IFLow->setValueQuiet(ui->LOLowFrequency->value() - sourceFreq);
}
};
connect(ui->IFHigh, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOHigh();
});
connect(ui->LOHighFrequency, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOHigh();
});
connect(ui->IFLow, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOLow();
});
connect(ui->LOLowFrequency, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOLow();
});
connect(ui->SourceSwitchGroup, qOverload<int, bool>(&QButtonGroup::idToggled), [=](int, bool) {
UpdateLOHigh();
UpdateLOLow();
});
connect(ui->SourceLowFrequency, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOHigh();
UpdateLOLow();
});
connect(ui->SourceHighFrequency, &SIUnitEdit::valueChanged, [=](double) {
UpdateLOHigh();
UpdateLOLow();
});
ui->IFHigh->setValue(233750);
ui->IFLow->setValue(233750);
// LO high/low mode switch connections
connect(ui->LOHighFreqType, qOverload<int>(&QComboBox::activated), this, [=](int index) {
switch(index) {
case 0:
ui->LOHighFrequency->setEnabled(false);
ui->IFHigh->setEnabled(true);
break;
case 1:
ui->LOHighFrequency->setEnabled(true);
ui->IFHigh->setEnabled(false);
break;
}
});
connect(ui->LOLowFreqType, qOverload<int>(&QComboBox::activated), this, [=](int index) {
switch(index) {
case 0:
ui->LOLowFrequency->setEnabled(false);
ui->IFLow->setEnabled(true);
break;
case 1:
ui->LOLowFrequency->setEnabled(true);
ui->IFLow->setEnabled(false);
break;
}
});
// Readonly widgets
auto MakeReadOnly = [](QWidget* w) {
w->setAttribute(Qt::WA_TransparentForMouseEvents);
w->setFocusPolicy(Qt::NoFocus);
};
MakeReadOnly(ui->SourceLocked);
MakeReadOnly(ui->LOLocked);
MakeReadOnly(ui->port1min);
MakeReadOnly(ui->port1max);
MakeReadOnly(ui->port1mag);
MakeReadOnly(ui->port1phase);
MakeReadOnly(ui->port1referenced);
MakeReadOnly(ui->port2min);
MakeReadOnly(ui->port2max);
MakeReadOnly(ui->port2mag);
MakeReadOnly(ui->port2phase);
MakeReadOnly(ui->port2referenced);
MakeReadOnly(ui->refmin);
MakeReadOnly(ui->refmax);
MakeReadOnly(ui->refmag);
MakeReadOnly(ui->refphase);
connect(&dev, &LibreVNADriver::receivedPacket, this, [=](const Protocol::PacketInfo &p){
if(p.type == Protocol::PacketType::ManualStatus) {
NewStatus(p.manualStatus);
}
}, Qt::QueuedConnection);
connect(ui->SourceCE, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->LOHighCE, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->SourceLowEnable, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->LOLowEnable, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->Port1Enable, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->Port2Enable, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->RefEnable, &QCheckBox::toggled, this, [=](bool) { UpdateDevice(); });
connect(ui->SourceLowpass, qOverload<int>(&QComboBox::activated), this, [=](int) { UpdateDevice(); });
connect(ui->SourceLowPower, qOverload<int>(&QComboBox::activated), this, [=](int) { UpdateDevice(); });
connect(ui->LOLowPower, qOverload<int>(&QComboBox::activated), this, [=](int) { UpdateDevice(); });
connect(ui->cbWindow, qOverload<int>(&QComboBox::activated), this, [=](int) { UpdateDevice(); });
connect(ui->SourceHighFrequency, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->SourceLowFrequency, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->LOHighFrequency, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->IFHigh, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->LOLowFrequency, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->IFLow, &SIUnitEdit::valueChanged, this, [=](double) { UpdateDevice(); });
connect(ui->PortSwitchGroup, qOverload<int, bool>(&QButtonGroup::idToggled), this, [=](int, bool) { UpdateDevice(); });
connect(ui->SourceSwitchGroup, qOverload<int, bool>(&QButtonGroup::idToggled), this, [=](int, bool) { UpdateDevice(); });
connect(ui->LOSwitchGroup, qOverload<int, bool>(&QButtonGroup::idToggled), this, [=](int, bool) { UpdateDevice(); });
connect(ui->Attenuator, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [=](double) { UpdateDevice(); });
connect(ui->SourceHighPower, qOverload<int>(&QSpinBox::valueChanged), this, [=](int) { UpdateDevice(); });
connect(ui->Samples, qOverload<int>(&QSpinBox::valueChanged), this, [=](double) { UpdateDevice(); });
// Create the SCPI commands
auto addBooleanManualSetting = [=](QString cmd, void(ManualControlDialogVD0::*set)(bool), bool(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, [=](QStringList params) -> QString {
bool enable;
if(!SCPI::paramToBool(params, 0, enable)) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto set_fn = std::bind(set, this, std::placeholders::_1);
set_fn(enable);
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return get_fn() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
};
auto addDoubleManualSetting = [=](QString cmd, void(ManualControlDialogVD0::*set)(double), double(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, [=](QStringList params) -> QString {
double value;
if(!SCPI::paramToDouble(params, 0, value)) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto set_fn = std::bind(set, this, std::placeholders::_1);
set_fn(value);
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return QString::number(get_fn());
}));
};
auto addIntegerManualSetting = [=](QString cmd, void(ManualControlDialogVD0::*set)(int), int(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, [=](QStringList params) -> QString {
double value;
if(!SCPI::paramToDouble(params, 0, value)) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto set_fn = std::bind(set, this, std::placeholders::_1);
set_fn(value);
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return QString::number(get_fn());
}));
};
auto addIntegerManualSettingWithReturnValue = [=](QString cmd, bool(ManualControlDialogVD0::*set)(int), int(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, [=](QStringList params) -> QString {
double value;
if(!SCPI::paramToDouble(params, 0, value)) {
return SCPI::getResultName(SCPI::Result::Error);
}
auto set_fn = std::bind(set, this, std::placeholders::_1);
if(set_fn(value)) {
return SCPI::getResultName(SCPI::Result::Empty);
} else {
return SCPI::getResultName(SCPI::Result::Error);
}
}, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return QString::number(get_fn());
}));
};
auto addIntegerManualQuery = [=](QString cmd, int(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, nullptr, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return QString::number(get_fn());
}));
};
auto addDoubleManualQuery = [=](QString cmd, double(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, nullptr, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return QString::number(get_fn());
}));
};
auto addBooleanManualQuery = [=](QString cmd, bool(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, nullptr, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
return get_fn() ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False);
}));
};
auto addComplexManualQuery = [=](QString cmd, std::complex<double>(ManualControlDialogVD0::*get)(void)) {
commands.push_back(new SCPICommand(cmd, nullptr, [=](QStringList) -> QString {
auto get_fn = std::bind(get, this);
auto res = get_fn();
return QString::number(res.real())+","+QString::number(res.imag());
}));
};
addBooleanManualSetting("MANual:HSRC_CE", &ManualControlDialogVD0::setHighSourceChipEnable, &ManualControlDialogVD0::getHighSourceChipEnable);
addBooleanManualQuery("MANual:HSRC_LOCKed", &ManualControlDialogVD0::getHighSourceLocked);
addIntegerManualSettingWithReturnValue("MANual:HSRC_PWR", &ManualControlDialogVD0::setHighSourcePower, &ManualControlDialogVD0::getHighSourcePower);
addDoubleManualSetting("MANual:HSRC_FREQ", &ManualControlDialogVD0::setHighSourceFrequency, &ManualControlDialogVD0::getHighSourceFrequency);
commands.push_back(new SCPICommand("MANual:HSRC_LPF", [=](QStringList params) -> QString {
long value;
if(!SCPI::paramToLong(params, 0, value)) {
return SCPI::getResultName(SCPI::Result::Error);
}
switch(value) {
case 947:
setHighSourceLPF(ManualControlDialogVD0::LPF::M947);
break;
case 1880:
setHighSourceLPF(ManualControlDialogVD0::LPF::M1880);
break;
case 3500:
setHighSourceLPF(ManualControlDialogVD0::LPF::M3500);
break;
case 0:
setHighSourceLPF(ManualControlDialogVD0::LPF::None);
break;
default:
return SCPI::getResultName(SCPI::Result::Error);
}
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
auto lpf = getHighSourceLPF();
switch(lpf) {
case ManualControlDialogVD0::LPF::M947: return "947";
case ManualControlDialogVD0::LPF::M1880: return "1880";
case ManualControlDialogVD0::LPF::M3500: return "3500";
case ManualControlDialogVD0::LPF::None: return "0";
default: return SCPI::getResultName(SCPI::Result::Error);
}
}));
addBooleanManualSetting("MANual:LSRC_EN", &ManualControlDialogVD0::setLowSourceEnable, &ManualControlDialogVD0::getLowSourceEnable);
addIntegerManualSettingWithReturnValue("MANual:LSRC_PWR", &ManualControlDialogVD0::setLowSourcePower, &ManualControlDialogVD0::getLowSourcePower);
addDoubleManualSetting("MANual:LSRC_FREQ", &ManualControlDialogVD0::setLowSourceFrequency, &ManualControlDialogVD0::getLowSourceFrequency);
addBooleanManualSetting("MANual:BAND_SW", &ManualControlDialogVD0::setHighband, &ManualControlDialogVD0::getHighband);
addDoubleManualSetting("MANual:ATTenuator", &ManualControlDialogVD0::setAttenuator, &ManualControlDialogVD0::getAttenuator);
addIntegerManualSettingWithReturnValue("MANual:PORT_SW", &ManualControlDialogVD0::setPortSwitch, &ManualControlDialogVD0::getPortSwitch);
addBooleanManualSetting("MANual:LOHigh_CE", &ManualControlDialogVD0::setLOHighChipEnable, &ManualControlDialogVD0::getLOHighChipEnable);
addBooleanManualQuery("MANual:LOHigh_LOCKed", &ManualControlDialogVD0::getLOHighLocked);
addDoubleManualSetting("MANual:LOHigh_FREQ", &ManualControlDialogVD0::setLOHighFrequency, &ManualControlDialogVD0::getLOHighFrequency);
addDoubleManualSetting("MANual:IFHigh_FREQ", &ManualControlDialogVD0::setIFHighFrequency, &ManualControlDialogVD0::getIFHighFrequency);
addBooleanManualSetting("MANual:LOLow_EN", &ManualControlDialogVD0::setLOLowEnable, &ManualControlDialogVD0::getLOLowEnable);
addDoubleManualSetting("MANual:LOLow_FREQ", &ManualControlDialogVD0::setLOLowFrequency, &ManualControlDialogVD0::getLOLowFrequency);
addDoubleManualSetting("MANual:IFLow_FREQ", &ManualControlDialogVD0::setIFLowFrequency, &ManualControlDialogVD0::getIFLowFrequency);
addBooleanManualSetting("MANual:PORT1_EN", &ManualControlDialogVD0::setPort1Enable, &ManualControlDialogVD0::getPort1Enable);
addBooleanManualSetting("MANual:PORT2_EN", &ManualControlDialogVD0::setPort2Enable, &ManualControlDialogVD0::getPort2Enable);
addBooleanManualSetting("MANual:REF_EN", &ManualControlDialogVD0::setRefEnable, &ManualControlDialogVD0::getRefEnable);
addIntegerManualSetting("MANual:SAMPLES", &ManualControlDialogVD0::setNumSamples, &ManualControlDialogVD0::getNumSamples);
commands.push_back(new SCPICommand("MANual:WINdow", [=](QStringList params) -> QString {
if(params.size() < 1) {
return SCPI::getResultName(SCPI::Result::Error);
}
if (params[0] == "NONE") {
setWindow(ManualControlDialogVD0::Window::None);
} else if(params[0] == "KAISER") {
setWindow(ManualControlDialogVD0::Window::Kaiser);
} else if(params[0] == "HANN") {
setWindow(ManualControlDialogVD0::Window::Hann);
} else if(params[0] == "FLATTOP") {
setWindow(ManualControlDialogVD0::Window::FlatTop);
} else {
return "INVALID WINDOW";
}
return SCPI::getResultName(SCPI::Result::Empty);
}, [=](QStringList) -> QString {
switch((ManualControlDialogVD0::Window) getWindow()) {
case ManualControlDialogVD0::Window::None: return "NONE";
case ManualControlDialogVD0::Window::Kaiser: return "KAISER";
case ManualControlDialogVD0::Window::Hann: return "HANN";
case ManualControlDialogVD0::Window::FlatTop: return "FLATTOP";
default: return SCPI::getResultName(SCPI::Result::Error);
}
}));
addIntegerManualQuery("MANual:PORT1_MIN", &ManualControlDialogVD0::getPort1MinADC);
addIntegerManualQuery("MANual:PORT1_MAX", &ManualControlDialogVD0::getPort1MaxADC);
addDoubleManualQuery("MANual:PORT1_MAG", &ManualControlDialogVD0::getPort1Magnitude);
addDoubleManualQuery("MANual:PORT1_PHAse", &ManualControlDialogVD0::getPort1Phase);
addComplexManualQuery("MANual:PORT1_REFerenced", &ManualControlDialogVD0::getPort1Referenced);
addIntegerManualQuery("MANual:PORT2_MIN", &ManualControlDialogVD0::getPort2MinADC);
addIntegerManualQuery("MANual:PORT2_MAX", &ManualControlDialogVD0::getPort2MaxADC);
addDoubleManualQuery("MANual:PORT2_MAG", &ManualControlDialogVD0::getPort2Magnitude);
addDoubleManualQuery("MANual:PORT2_PHAse", &ManualControlDialogVD0::getPort2Phase);
addComplexManualQuery("MANual:PORT2_REFerenced", &ManualControlDialogVD0::getPort2Referenced);
addIntegerManualQuery("MANual:REF_MIN", &ManualControlDialogVD0::getRefMinADC);
addIntegerManualQuery("MANual:REF_MAX", &ManualControlDialogVD0::getRefMaxADC);
addDoubleManualQuery("MANual:REF_MAG", &ManualControlDialogVD0::getRefMagnitude);
addDoubleManualQuery("MANual:REF_PHAse", &ManualControlDialogVD0::getRefPhase);
for(auto c : commands) {
emit dev.addSCPICommand(c);
}
UpdateDevice();
}
ManualControlDialogVD0::~ManualControlDialogVD0()
{
for(auto c : commands) {
emit dev.removeSCPICommand(c);
}
emit dev.releaseControl();
delete ui;
}
void ManualControlDialogVD0::setHighSourceChipEnable(bool enable)
{
ui->SourceCE->setChecked(enable);
}
bool ManualControlDialogVD0::getHighSourceChipEnable()
{
return ui->SourceCE->isChecked();
}
bool ManualControlDialogVD0::getHighSourceLocked()
{
return ui->SourceLocked->isChecked();
}
bool ManualControlDialogVD0::setHighSourcePower(int dBm)
{
if(dBm < 0 || dBm > ui->SourceHighPower->maximum()) {
return false;
} else {
ui->SourceHighPower->setValue(dBm);
return true;
}
}
int ManualControlDialogVD0::getHighSourcePower()
{
return ui->SourceHighPower->value();
}
void ManualControlDialogVD0::setHighSourceFrequency(double f)
{
ui->SourceHighFrequency->setValue(f);
}
double ManualControlDialogVD0::getHighSourceFrequency()
{
return ui->SourceHighFrequency->value();
}
void ManualControlDialogVD0::setHighSourceLPF(ManualControlDialogVD0::LPF lpf)
{
switch(lpf) {
case LPF::M947:
ui->SourceLowpass->setCurrentIndex(0);
break;
case LPF::M1880:
ui->SourceLowpass->setCurrentIndex(1);
break;
case LPF::M3500:
ui->SourceLowpass->setCurrentIndex(2);
break;
case LPF::None:
ui->SourceLowpass->setCurrentIndex(3);
break;
}
}
ManualControlDialogVD0::LPF ManualControlDialogVD0::getHighSourceLPF()
{
LPF lpfs[4] = {LPF::M947, LPF::M1880, LPF::M3500, LPF::None};
return lpfs[ui->SourceLowpass->currentIndex()];
}
void ManualControlDialogVD0::setLowSourceEnable(bool enable)
{
ui->SourceLowEnable->setChecked(enable);
}
bool ManualControlDialogVD0::getLowSourceEnable()
{
return ui->SourceLowEnable->isChecked();
}
bool ManualControlDialogVD0::setLowSourcePower(int mA)
{
switch(mA) {
case 2:
ui->SourceLowPower->setCurrentIndex(0);
break;
case 4:
ui->SourceLowPower->setCurrentIndex(1);
break;
case 6:
ui->SourceLowPower->setCurrentIndex(2);
break;
case 8:
ui->SourceLowPower->setCurrentIndex(3);
break;
default:
// invalid power setting
return false;
}
return true;
}
int ManualControlDialogVD0::getLowSourcePower()
{
int powers[4] = {2,4,6,8};
return powers[ui->SourceLowPower->currentIndex()];
}
void ManualControlDialogVD0::setLowSourceFrequency(double f)
{
ui->SourceLowFrequency->setValue(f);
}
double ManualControlDialogVD0::getLowSourceFrequency()
{
return ui->SourceLowFrequency->value();
}
void ManualControlDialogVD0::setHighband(bool high)
{
if(high) {
ui->SwitchHighband->setChecked(true);
} else {
ui->SwitchLowband->setChecked(true);
}
}
bool ManualControlDialogVD0::getHighband()
{
return ui->SwitchHighband->isChecked();
}
void ManualControlDialogVD0::setAttenuator(double att)
{
ui->Attenuator->setValue(att);
}
double ManualControlDialogVD0::getAttenuator()
{
return ui->Attenuator->value();
}
bool ManualControlDialogVD0::setPortSwitch(int port)
{
switch(port) {
case 1:
ui->Port1Switch->setChecked(true);
break;
case 2:
ui->Port2Switch->setChecked(true);
break;
default:
// invalid port
return false;
}
return true;
}
int ManualControlDialogVD0::getPortSwitch()
{
if(ui->Port1Switch->isChecked()) {
return 1;
} else {
return 2;
}
}
void ManualControlDialogVD0::setLOHighChipEnable(bool enable)
{
ui->LOHighCE->setChecked(enable);
}
bool ManualControlDialogVD0::getLOHighChipEnable()
{
return ui->LOHighCE->isChecked();
}
bool ManualControlDialogVD0::getLOHighLocked()
{
return ui->LOLocked->isChecked();
}
void ManualControlDialogVD0::setLOHighFrequency(double f)
{
ui->LOHighFreqType->setCurrentIndex(1);
ui->LOHighFrequency->setValue(f);
}
double ManualControlDialogVD0::getLOHighFrequency()
{
return ui->LOHighFrequency->value();
}
void ManualControlDialogVD0::setIFHighFrequency(double f)
{
ui->LOHighFreqType->setCurrentIndex(0);
ui->IFHigh->setValue(f);
}
double ManualControlDialogVD0::getIFHighFrequency()
{
return ui->IFHigh->value();
}
void ManualControlDialogVD0::setLOLowEnable(bool enable)
{
ui->LOLowEnable->setChecked(enable);
}
bool ManualControlDialogVD0::getLOLowEnable()
{
return ui->LOLowEnable->isChecked();
}
void ManualControlDialogVD0::setLOLowFrequency(double f)
{
ui->LOLowFreqType->setCurrentIndex(1);
ui->LOLowFrequency->setValue(f);
}
double ManualControlDialogVD0::getLOLowFrequency()
{
return ui->LOLowFrequency->value();
}
void ManualControlDialogVD0::setIFLowFrequency(double f)
{
ui->LOLowFreqType->setCurrentIndex(0);
ui->IFLow->setValue(f);
}
double ManualControlDialogVD0::getIFLowFrequency()
{
return ui->IFLow->value();
}
void ManualControlDialogVD0::setPort1Enable(bool enable)
{
ui->Port1Enable->setChecked(enable);
}
bool ManualControlDialogVD0::getPort1Enable()
{
return ui->Port1Enable->isChecked();
}
void ManualControlDialogVD0::setPort2Enable(bool enable)
{
ui->Port2Enable->setChecked(enable);
}
bool ManualControlDialogVD0::getPort2Enable()
{
return ui->Port2Enable->isChecked();
}
void ManualControlDialogVD0::setRefEnable(bool enable)
{
ui->RefEnable->setChecked(enable);
}
bool ManualControlDialogVD0::getRefEnable()
{
return ui->RefEnable->isChecked();
}
void ManualControlDialogVD0::setNumSamples(int samples)
{
ui->Samples->setValue(samples);
}
int ManualControlDialogVD0::getNumSamples()
{
return ui->Samples->value();
}
void ManualControlDialogVD0::setWindow(ManualControlDialogVD0::Window w)
{
ui->cbWindow->setCurrentIndex((int) w);
}
ManualControlDialogVD0::Window ManualControlDialogVD0::getWindow()
{
return (Window) ui->cbWindow->currentIndex();
}
int ManualControlDialogVD0::getPort1MinADC()
{
return ui->port1min->text().toInt();
}
int ManualControlDialogVD0::getPort1MaxADC()
{
return ui->port1max->text().toInt();
}
double ManualControlDialogVD0::getPort1Magnitude()
{
return ui->port1mag->text().toDouble();
}
double ManualControlDialogVD0::getPort1Phase()
{
return ui->port1phase->text().toDouble();
}
std::complex<double> ManualControlDialogVD0::getPort1Referenced()
{
return port1referenced;
}
int ManualControlDialogVD0::getPort2MinADC()
{
return ui->port2min->text().toInt();
}
int ManualControlDialogVD0::getPort2MaxADC()
{
return ui->port2max->text().toInt();
}
double ManualControlDialogVD0::getPort2Magnitude()
{
return ui->port2mag->text().toDouble();
}
double ManualControlDialogVD0::getPort2Phase()
{
return ui->port2phase->text().toDouble();
}
std::complex<double> ManualControlDialogVD0::getPort2Referenced()
{
return port2referenced;
}
int ManualControlDialogVD0::getRefMinADC()
{
return ui->refmin->text().toInt();
}
int ManualControlDialogVD0::getRefMaxADC()
{
return ui->refmax->text().toInt();
}
double ManualControlDialogVD0::getRefMagnitude()
{
return ui->refmag->text().toDouble();
}
double ManualControlDialogVD0::getRefPhase()
{
return ui->refphase->text().toDouble();
}
void ManualControlDialogVD0::NewStatus(Protocol::ManualStatus status)
{
// ADC values
ui->port1min->setText(QString::number(status.VD0.port1min));
ui->port1max->setText(QString::number(status.VD0.port1max));
auto port1 = complex<double>(status.VD0.port1real, status.VD0.port1imag);
ui->port1mag->setText(QString::number(abs(port1)));
ui->port1phase->setText(QString::number(arg(port1)*180/M_PI));
ui->port2min->setText(QString::number(status.VD0.port2min));
ui->port2max->setText(QString::number(status.VD0.port2max));
auto port2 = complex<double>(status.VD0.port2real, status.VD0.port2imag);
ui->port2mag->setText(QString::number(abs(port2)));
ui->port2phase->setText(QString::number(arg(port2)*180/M_PI));
ui->refmin->setText(QString::number(status.VD0.refmin));
ui->refmax->setText(QString::number(status.VD0.refmax));
auto ref = complex<double>(status.VD0.refreal, status.VD0.refimag);
ui->refmag->setText(QString::number(abs(ref)));
ui->refphase->setText(QString::number(arg(ref)*180/M_PI));
port1referenced = port1 / ref;
port2referenced = port2 / ref;
auto port1db = Util::SparamTodB(port1referenced);
auto port2db = Util::SparamTodB(port2referenced);
ui->port1referenced->setText(QString::number(port1db, 'f', 1) + "db@" + QString::number(arg(port1referenced)*180/M_PI, 'f', 0) + "°");
ui->port2referenced->setText(QString::number(port2db, 'f', 1) + "db@" + QString::number(arg(port2referenced)*180/M_PI, 'f', 0) + "°");
// PLL state
ui->SourceLocked->setChecked(status.VD0.source_locked);
ui->LOLocked->setChecked(status.VD0.LO_locked);
}
void ManualControlDialogVD0::UpdateDevice()
{
Protocol::PacketInfo p;
p.type = Protocol::PacketType::ManualControl;
auto &m = p.manual.VD0;
// Source highband
m.SourceHighCE = ui->SourceCE->isChecked();
m.SourceHighPower = ui->SourceHighPower->value();
m.SourceHighFrequency = ui->SourceHighFrequency->value();
m.SourceHighLowpass = ui->SourceLowpass->currentIndex();
// Source lowband
m.SourceLowEN = ui->SourceLowEnable->isChecked();
m.SourceLowPower = ui->SourceLowPower->currentIndex();
m.SourceLowFrequency = ui->SourceLowFrequency->value();
// Source signal path
m.SourceHighband = ui->SwitchHighband->isChecked();
m.PortSwitch = ui->Port2Switch->isChecked();
m.attenuator = -ui->Attenuator->value() / 0.25;
// LO High
m.LOHighCE = ui->LOHighCE->isChecked();
m.LOHighFrequency = ui->LOHighFrequency->value();
// LOLow
m.LOLowEN = ui->LOLowEnable->isChecked();
m.LOLowFrequency = ui->LOLowFrequency->value();
m.LOLowPower = ui->LOLowPower->currentIndex();
// LO signal path
m.LOHighband = ui->LOSwitchHighband->isChecked();
// Acquisition
m.Port1EN = ui->Port1Enable->isChecked();
m.Port2EN = ui->Port2Enable->isChecked();
m.RefEN = ui->RefEnable->isChecked();
m.Samples = ui->Samples->value();
m.WindowType = ui->cbWindow->currentIndex();
qDebug() << "Updating manual control state";
dev.SendPacket(p);
}

View file

@ -0,0 +1,114 @@
#ifndef MANUALCONTROLDIALOGVD0_H
#define MANUALCONTROLDIALOGVD0_H
#include "librevnadriver.h"
#include <QDialog>
#include <complex>
namespace Ui {
class ManualControlDialogVD0;
}
class ManualControlDialogVD0 : public QDialog
{
Q_OBJECT
public:
explicit ManualControlDialogVD0(LibreVNADriver &dev, QWidget *parent = nullptr);
~ManualControlDialogVD0();
void setHighSourceChipEnable(bool enable);
bool getHighSourceChipEnable();
void setHighSourceRFEnable(bool enable);
bool getHighSourceRFEnable();
bool getHighSourceLocked();
bool setHighSourcePower(int dBm);
int getHighSourcePower();
void setHighSourceFrequency(double f);
double getHighSourceFrequency();
enum class LPF {
M947,
M1880,
M3500,
None,
};
void setHighSourceLPF(LPF lpf);
LPF getHighSourceLPF();
void setLowSourceEnable(bool enable);
bool getLowSourceEnable();
bool setLowSourcePower(int mA);
int getLowSourcePower();
void setLowSourceFrequency(double f);
double getLowSourceFrequency();
void setHighband(bool high);
bool getHighband();
void setAttenuator(double att);
double getAttenuator();
bool setPortSwitch(int port);
int getPortSwitch();
void setLOHighChipEnable(bool enable);
bool getLOHighChipEnable();
bool getLOHighLocked();
void setLOHighFrequency(double f);
double getLOHighFrequency();
void setIFHighFrequency(double f);
double getIFHighFrequency();
void setLOLowEnable(bool enable);
bool getLOLowEnable();
void setLOLowFrequency(double f);
double getLOLowFrequency();
void setIFLowFrequency(double f);
double getIFLowFrequency();
void setPort1Enable(bool enable);
bool getPort1Enable();
void setPort2Enable(bool enable);
bool getPort2Enable();
void setRefEnable(bool enable);
bool getRefEnable();
void setNumSamples(int samples);
int getNumSamples();
enum class Window {
None = 0,
Kaiser = 1,
Hann = 2,
FlatTop = 3
};
void setWindow(Window w);
Window getWindow();
int getPort1MinADC();
int getPort1MaxADC();
double getPort1Magnitude();
double getPort1Phase();
std::complex<double> getPort1Referenced();
int getPort2MinADC();
int getPort2MaxADC();
double getPort2Magnitude();
double getPort2Phase();
std::complex<double> getPort2Referenced();
int getRefMinADC();
int getRefMaxADC();
double getRefMagnitude();
double getRefPhase();
public slots:
void NewStatus(Protocol::ManualStatus status);
private:
void UpdateDevice();
Ui::ManualControlDialogVD0 *ui;
LibreVNADriver &dev;
std::complex<double> port1referenced;
std::complex<double> port2referenced;
std::vector<SCPICommand*> commands;
};
#endif // MANUALCONTROLDIALOGVD0_H

View file

@ -0,0 +1,812 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ManualControlDialogVD0</class>
<widget class="QDialog" name="ManualControlDialogVD0">
<property name="windowModality">
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>747</height>
</rect>
</property>
<property name="windowTitle">
<string>Manual System Control</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QVBoxLayout" name="verticalLayout_15">
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="title">
<string>Signal Generation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Highband Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QCheckBox" name="SourceCE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="SourceLocked">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Locked</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="SourceHighFrequency"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Lowpass:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="SourceLowpass">
<item>
<property name="text">
<string>947MHz</string>
</property>
</item>
<item>
<property name="text">
<string>1880MHz</string>
</property>
</item>
<item>
<property name="text">
<string>3500MHz</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="SourceHighPower">
<property name="maximum">
<number>63</number>
</property>
<property name="value">
<number>63</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Lowband Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="SourceLowEnable">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="SourceLowPower">
<item>
<property name="text">
<string>2mA</string>
</property>
</item>
<item>
<property name="text">
<string>4mA</string>
</property>
</item>
<item>
<property name="text">
<string>6mA</string>
</property>
</item>
<item>
<property name="text">
<string>8mA</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="SourceLowFrequency"/>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Source Switch</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QRadioButton" name="SwitchLowband">
<property name="text">
<string>Lowband</string>
</property>
<attribute name="buttonGroup">
<string notr="true">SourceSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="SwitchHighband">
<property name="text">
<string>Highband</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">SourceSwitchGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Attenuator</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QDoubleSpinBox" name="Attenuator">
<property name="suffix">
<string>db</string>
</property>
<property name="minimum">
<double>-31.750000000000000</double>
</property>
<property name="maximum">
<double>0.000000000000000</double>
</property>
<property name="singleStep">
<double>0.250000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>Port Switch</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QRadioButton" name="Port1Switch">
<property name="text">
<string>Port 1</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">PortSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="Port2Switch">
<property name="text">
<string>Port 2</string>
</property>
<attribute name="buttonGroup">
<string notr="true">PortSwitchGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>Signal Analysis</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>Highband LO</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="LOHighCE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="LOLocked">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Locked</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="LOHighFrequency">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>IF:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="IFHigh"/>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="LOHighFreqType">
<item>
<property name="text">
<string>IF</string>
</property>
</item>
<item>
<property name="text">
<string>Absolute</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Freq. Type:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>Lowband LO</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_9">
<item>
<widget class="QCheckBox" name="LOLowEnable">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="LOLowPower">
<item>
<property name="text">
<string>2mA</string>
</property>
</item>
<item>
<property name="text">
<string>4mA</string>
</property>
</item>
<item>
<property name="text">
<string>6mA</string>
</property>
</item>
<item>
<property name="text">
<string>8mA</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Freq. Type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="LOLowFreqType">
<item>
<property name="text">
<string>IF</string>
</property>
</item>
<item>
<property name="text">
<string>Absolute</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="SIUnitEdit" name="LOLowFrequency"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>IF:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="SIUnitEdit" name="IFLow"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_20">
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>Aquisition</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QCheckBox" name="Port1Enable">
<property name="text">
<string>Port 1 Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="Port2Enable">
<property name="text">
<string>Port 2 Enable</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="RefEnable">
<property name="text">
<string>Reference Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Samples:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="Samples">
<property name="minimum">
<number>16</number>
</property>
<property name="maximum">
<number>131072</number>
</property>
<property name="singleStep">
<number>16</number>
</property>
<property name="value">
<number>131072</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Window:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbWindow">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Kaiser</string>
</property>
</item>
<item>
<property name="text">
<string>Hann</string>
</property>
</item>
<item>
<property name="text">
<string>Flat Top</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_8">
<property name="title">
<string>LO Switch</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QRadioButton" name="LOSwitchLowband">
<property name="text">
<string>Lowband</string>
</property>
<attribute name="buttonGroup">
<string notr="true">LOSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="LOSwitchHighband">
<property name="text">
<string>Highband</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">LOSwitchGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_12">
<property name="title">
<string>Measurements</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="QGroupBox" name="groupBox_16">
<property name="title">
<string>Port 1</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<layout class="QFormLayout" name="formLayout_9">
<item row="0" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="port1min"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="port1max"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="port1mag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="port1phase"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Referenced:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="port1referenced"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="title">
<string>Port 2</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="port2min"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="port2max"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="port2mag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="port2phase"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Referenced:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="port2referenced"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_15">
<property name="title">
<string>Reference</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_18">
<item>
<layout class="QFormLayout" name="formLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="refmin"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="refmax"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="refmag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="refphase"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="PortSwitchGroup"/>
<buttongroup name="SourceSwitchGroup"/>
<buttongroup name="LOSwitchGroup"/>
</buttongroups>
</ui>

View file

@ -0,0 +1,179 @@
#include "manualcontroldialogVE0.h"
#include "ui_manualcontroldialogVE0.h"
#include "Util/util.h"
#include <QComboBox>
#include <QDebug>
#include <QButtonGroup>
#include <complex>
using namespace std;
ManualControlDialogVE0::ManualControlDialogVE0(LibreVNADriver &dev, QWidget *parent) :
QDialog(parent),
ui(new Ui::ManualControlDialogVE0),
dev(dev)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
emit dev.acquireControl();
ui->src1Freq->setUnit("Hz");
ui->src1Freq->setPrefixes(" kMG");
ui->src1Freq->setPrecision(6);
ui->src1Freq->setValueQuiet(100000000);
ui->src2Freq->setUnit("Hz");
ui->src2Freq->setPrefixes(" kMG");
ui->src2Freq->setPrecision(6);
ui->src2Freq->setValueQuiet(100000000);
ui->LO1Freq->setUnit("Hz");
ui->LO1Freq->setPrefixes(" kMG");
ui->LO1Freq->setPrecision(6);
ui->LO1Freq->setValueQuiet(100000000);
ui->LO2Freq->setUnit("Hz");
ui->LO2Freq->setPrefixes(" kMG");
ui->LO2Freq->setPrecision(6);
ui->LO2Freq->setValueQuiet(100000000);
// Readonly widgets
auto MakeReadOnly = [](QWidget* w) {
w->setAttribute(Qt::WA_TransparentForMouseEvents);
w->setFocusPolicy(Qt::NoFocus);
};
MakeReadOnly(ui->port1min);
MakeReadOnly(ui->port1max);
MakeReadOnly(ui->port1mag);
MakeReadOnly(ui->port1phase);
MakeReadOnly(ui->port1referenced);
MakeReadOnly(ui->port2min);
MakeReadOnly(ui->port2max);
MakeReadOnly(ui->port2mag);
MakeReadOnly(ui->port2phase);
MakeReadOnly(ui->port2referenced);
MakeReadOnly(ui->refmin);
MakeReadOnly(ui->refmax);
MakeReadOnly(ui->refmag);
MakeReadOnly(ui->refphase);
connect(&dev, &LibreVNADriver::receivedPacket, this, [=](const Protocol::PacketInfo &p){
if(p.type == Protocol::PacketType::ManualStatus) {
NewStatus(p.manualStatus);
}
}, Qt::QueuedConnection);
connect(ui->src1CE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->src2CE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->LO1CE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->LO2CE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->srcAmp, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->LO1CE, &QCheckBox::toggled, [=](bool) { UpdateDevice(); });
connect(ui->srcSel, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->portSel, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->LOSel, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->P1Path, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->P1Amp, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->P2Path, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->P2Amp, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->RefAmp, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->Window, qOverload<int>(&QComboBox::activated), [=](int) { UpdateDevice(); });
connect(ui->src1Freq, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
connect(ui->src2Freq, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
connect(ui->LO1Freq, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
connect(ui->LO2Freq, &SIUnitEdit::valueChanged, [=](double) { UpdateDevice(); });
connect(ui->src1Pwr, qOverload<int>(&QSpinBox::valueChanged), [=](double) { UpdateDevice(); });
connect(ui->src2Pwr, qOverload<int>(&QSpinBox::valueChanged), [=](double) { UpdateDevice(); });
connect(ui->LO1Pwr, qOverload<int>(&QSpinBox::valueChanged), [=](double) { UpdateDevice(); });
connect(ui->LO2Pwr, qOverload<int>(&QSpinBox::valueChanged), [=](double) { UpdateDevice(); });
connect(ui->Samples, qOverload<int>(&QSpinBox::valueChanged), [=](double) { UpdateDevice(); });
UpdateDevice();
}
ManualControlDialogVE0::~ManualControlDialogVE0()
{
emit dev.releaseControl();
delete ui;
}
void ManualControlDialogVE0::NewStatus(Protocol::ManualStatus status)
{
// ADC values
auto &s = status.VE0;
ui->port1min->setText(QString::number(s.port1min));
ui->port1max->setText(QString::number(s.port1max));
auto port1 = complex<double>(s.port1real, s.port1imag);
ui->port1mag->setText(QString::number(abs(port1)));
ui->port1phase->setText(QString::number(arg(port1)*180/M_PI));
ui->port2min->setText(QString::number(s.port2min));
ui->port2max->setText(QString::number(s.port2max));
auto port2 = complex<double>(s.port2real, s.port2imag);
ui->port2mag->setText(QString::number(abs(port2)));
ui->port2phase->setText(QString::number(arg(port2)*180/M_PI));
ui->refmin->setText(QString::number(s.refmin));
ui->refmax->setText(QString::number(s.refmax));
auto ref = complex<double>(s.refreal, s.refimag);
ui->refmag->setText(QString::number(abs(ref)));
ui->refphase->setText(QString::number(arg(ref)*180/M_PI));
port1referenced = port1 / ref;
port2referenced = port2 / ref;
auto port1db = Util::SparamTodB(port1referenced);
auto port2db = Util::SparamTodB(port2referenced);
ui->port1referenced->setText(QString::number(port1db, 'f', 2) + "db@" + QString::number(arg(port1referenced)*180/M_PI, 'f', 2) + "°");
ui->port2referenced->setText(QString::number(port2db, 'f', 2) + "db@" + QString::number(arg(port2referenced)*180/M_PI, 'f', 2) + "°");
}
void ManualControlDialogVE0::UpdateDevice()
{
Protocol::PacketInfo p;
p.type = Protocol::PacketType::ManualControl;
auto &m = p.manual.VE0;
// Source
m.src1Freq = ui->src1Freq->value();
m.src2Freq = ui->src2Freq->value();
m.src1Pwr = ui->src1Pwr->value();
m.src2Pwr = ui->src2Pwr->value();
m.src1CE = ui->src1CE->isChecked();
m.src2CE = ui->src2CE->isChecked();
m.srcSel = ui->srcSel->currentIndex();
m.portSel = ui->portSel->currentIndex();
m.srcAmp = ui->srcAmp->isChecked();
// LO
m.LO1Freq = ui->LO1Freq->value();
m.LO2Freq = ui->LO2Freq->value();
m.LO1Pwr = ui->LO1Pwr->value();
m.LO2Pwr = ui->LO2Pwr->value();
m.LO1CE = ui->LO1CE->isChecked();
m.LO2CE = ui->LO2CE->isChecked();
m.LOSel = ui->LOSel->currentIndex();
// Port 1
m.P1PathSel = ui->P1Path->currentIndex();
m.P1AmpOn = ui->P1Amp->currentIndex() == 1 ? 1 : 0;
m.P1AmpBypass = ui->P1Amp->currentIndex() == 2 ? 1 : 0;
// Port 2
m.P2PathSel = ui->P2Path->currentIndex();
m.P2AmpOn = ui->P2Amp->currentIndex() == 1 ? 1 : 0;
m.P2AmpBypass = ui->P2Amp->currentIndex() == 2 ? 1 : 0;
// Reference
m.RefAmpOn = ui->RefAmp->currentIndex() == 1 ? 1 : 0;
m.RefAmpBypass = ui->RefAmp->currentIndex() == 2 ? 1 : 0;
// Acquisition
m.Samples = ui->Samples->value();
m.WindowType = ui->Window->currentIndex();
qDebug() << "Updating manual control state";
dev.SendPacket(p);
}

View file

@ -0,0 +1,34 @@
#ifndef MANUALCONTROLDIALOGVE0_H
#define MANUALCONTROLDIALOGVE0_H
#include "librevnadriver.h"
#include <QDialog>
#include <complex>
namespace Ui {
class ManualControlDialogVE0;
}
class ManualControlDialogVE0 : public QDialog
{
Q_OBJECT
public:
explicit ManualControlDialogVE0(LibreVNADriver &dev, QWidget *parent = nullptr);
~ManualControlDialogVE0();
public slots:
void NewStatus(Protocol::ManualStatus status);
private:
void UpdateDevice();
Ui::ManualControlDialogVE0 *ui;
LibreVNADriver &dev;
std::complex<double> port1referenced;
std::complex<double> port2referenced;
std::vector<SCPICommand*> commands;
};
#endif // MANUALCONTROLDIALOGVE0_H

View file

@ -0,0 +1,797 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ManualControlDialogVE0</class>
<widget class="QDialog" name="ManualControlDialogVE0">
<property name="windowModality">
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>777</width>
<height>717</height>
</rect>
</property>
<property name="windowTitle">
<string>Manual System Control</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_10">
<property name="title">
<string>Signal Generation</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Source</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>PLL1</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QCheckBox" name="src1CE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="src1Freq"/>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="src1Pwr">
<property name="maximum">
<number>18</number>
</property>
<property name="value">
<number>18</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>PLL2</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QCheckBox" name="src2CE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="src2Freq"/>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="src2Pwr">
<property name="maximum">
<number>18</number>
</property>
<property name="value">
<number>18</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_10">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>PLL selection:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="srcSel">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>PLL1</string>
</property>
</item>
<item>
<property name="text">
<string>PLL2</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="srcAmp">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Port selection:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="portSel">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Port 1</string>
</property>
</item>
<item>
<property name="text">
<string>Port 2</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>LO</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>PLL1</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QCheckBox" name="LO1CE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_11">
<item row="0" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="LO1Freq"/>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="LO1Pwr">
<property name="maximum">
<number>63</number>
</property>
<property name="value">
<number>18</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
<string>PLL2</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_14">
<item>
<widget class="QCheckBox" name="LO2CE">
<property name="text">
<string>Chip Enable</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_12">
<item row="0" column="0">
<widget class="QLabel" name="label_31">
<property name="text">
<string>Power:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_32">
<property name="text">
<string>Frequency:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="SIUnitEdit" name="LO2Freq"/>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="LO2Pwr">
<property name="maximum">
<number>18</number>
</property>
<property name="value">
<number>18</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_13">
<item row="0" column="0">
<widget class="QLabel" name="label_33">
<property name="text">
<string>PLL selection:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="LOSel">
<item>
<property name="text">
<string>PLL1</string>
</property>
</item>
<item>
<property name="text">
<string>PLL2</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_11">
<property name="title">
<string>Signal Analysis</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="groupBox_7">
<property name="title">
<string>Port 1</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="P1Path">
<item>
<property name="text">
<string>Reflection</string>
</property>
</item>
<item>
<property name="text">
<string>Transmission</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="P1Amp">
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>On</string>
</property>
</item>
<item>
<property name="text">
<string>Bypass</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_8">
<property name="title">
<string>Port 2</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="P2Path">
<item>
<property name="text">
<string>Reflection</string>
</property>
</item>
<item>
<property name="text">
<string>Transmission</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="P2Amp">
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>On</string>
</property>
</item>
<item>
<property name="text">
<string>Bypass</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_8">
<item>
<widget class="QGroupBox" name="groupBox_9">
<property name="title">
<string>Reference</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Amplifier:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="RefAmp">
<item>
<property name="text">
<string>Off</string>
</property>
</item>
<item>
<property name="text">
<string>On</string>
</property>
</item>
<item>
<property name="text">
<string>Bypass</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_13">
<property name="title">
<string>Acquisition</string>
</property>
<layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Samples:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="Samples">
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>131072</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
<property name="value">
<number>131072</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Window:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="Window">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Kaiser</string>
</property>
</item>
<item>
<property name="text">
<string>Hann</string>
</property>
</item>
<item>
<property name="text">
<string>Flat Top</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_12">
<property name="title">
<string>Measurements</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_16">
<item>
<widget class="QGroupBox" name="groupBox_16">
<property name="title">
<string>Port 1</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_19">
<item>
<layout class="QFormLayout" name="formLayout_9">
<item row="0" column="0">
<widget class="QLabel" name="label_25">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="port1min"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="port1max"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_27">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="port1mag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="port1phase"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Referenced:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="port1referenced"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_14">
<property name="title">
<string>Port 2</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_17">
<item>
<layout class="QFormLayout" name="formLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="port2min"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="port2max"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="port2mag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="port2phase"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Referenced:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="port2referenced"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_15">
<property name="title">
<string>Reference</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_18">
<item>
<layout class="QFormLayout" name="formLayout_8">
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>ADC min:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="refmin"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>ADC max:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="refmax"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>Magnitude:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="refmag"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_24">
<property name="text">
<string>Phase:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="refphase"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SIUnitEdit</class>
<extends>QLineEdit</extends>
<header>CustomWidgets/siunitedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="PortSwitchGroup"/>
<buttongroup name="SourceSwitchGroup"/>
</buttongroups>
</ui>

View file

@ -5,6 +5,7 @@
#include "LibreVNA/Compound/compounddriver.h" #include "LibreVNA/Compound/compounddriver.h"
#include "SSA3000X/ssa3000xdriver.h" #include "SSA3000X/ssa3000xdriver.h"
#include "SNA5000A/sna5000adriver.h" #include "SNA5000A/sna5000adriver.h"
#include "Harogic/harogicb60.h"
DeviceDriver *DeviceDriver::activeDriver = nullptr; DeviceDriver *DeviceDriver::activeDriver = nullptr;
@ -25,6 +26,7 @@ std::vector<DeviceDriver *> DeviceDriver::getDrivers()
ret.push_back(new CompoundDriver); ret.push_back(new CompoundDriver);
ret.push_back(new SSA3000XDriver); ret.push_back(new SSA3000XDriver);
ret.push_back(new SNA5000ADriver); ret.push_back(new SNA5000ADriver);
ret.push_back(new HarogicB60);
} }
return ret; return ret;
} }

View file

@ -68,8 +68,9 @@ void DeviceLog::clear()
void DeviceLog::on_bToFile_clicked() void DeviceLog::on_bToFile_clicked()
{ {
auto filename = QFileDialog::getSaveFileName(this, "Select file for device log", "", "", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(this, "Select file for device log", Preferences::getInstance().UISettings.Paths.packetlog, "", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.packetlog = QFileInfo(filename).path();
// create file // create file
ofstream file; ofstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());

View file

@ -6,6 +6,7 @@ HEADERS += \
Calibration/LibreCAL/usbdevice.h \ Calibration/LibreCAL/usbdevice.h \
Calibration/calibration.h \ Calibration/calibration.h \
Calibration/calibrationmeasurement.h \ Calibration/calibrationmeasurement.h \
Calibration/calibrationviewdialog.h \
Calibration/calkit.h \ Calibration/calkit.h \
Calibration/calkitdialog.h \ Calibration/calkitdialog.h \
Calibration/calstandard.h \ Calibration/calstandard.h \
@ -19,11 +20,13 @@ HEADERS += \
CustomWidgets/toggleswitch.h \ CustomWidgets/toggleswitch.h \
CustomWidgets/touchstoneimport.h \ CustomWidgets/touchstoneimport.h \
CustomWidgets/tracesetselector.h \ CustomWidgets/tracesetselector.h \
Device/Harogic/harogicb60.h \
Device/LibreVNA/Compound/compounddevice.h \ Device/LibreVNA/Compound/compounddevice.h \
Device/LibreVNA/Compound/compounddeviceeditdialog.h \ Device/LibreVNA/Compound/compounddeviceeditdialog.h \
Device/LibreVNA/Compound/compounddriver.h \ Device/LibreVNA/Compound/compounddriver.h \
Device/LibreVNA/amplitudecaldialog.h \ Device/LibreVNA/amplitudecaldialog.h \
Device/LibreVNA/deviceconfigurationdialogv1.h \ Device/LibreVNA/deviceconfigurationdialogv1.h \
Device/LibreVNA/deviceconfigurationdialogvd0.h \
Device/LibreVNA/deviceconfigurationdialogvfe.h \ Device/LibreVNA/deviceconfigurationdialogvfe.h \
Device/LibreVNA/deviceconfigurationdialogvff.h \ Device/LibreVNA/deviceconfigurationdialogvff.h \
Device/LibreVNA/devicepacketlog.h \ Device/LibreVNA/devicepacketlog.h \
@ -34,6 +37,8 @@ HEADERS += \
Device/LibreVNA/librevnatcpdriver.h \ Device/LibreVNA/librevnatcpdriver.h \
Device/LibreVNA/librevnausbdriver.h \ Device/LibreVNA/librevnausbdriver.h \
Device/LibreVNA/manualcontroldialogV1.h \ Device/LibreVNA/manualcontroldialogV1.h \
Device/LibreVNA/manualcontroldialogVD0.h \
Device/LibreVNA/manualcontroldialogVE0.h \
Device/LibreVNA/manualcontroldialogvfe.h \ Device/LibreVNA/manualcontroldialogvfe.h \
Device/LibreVNA/manualcontroldialogvff.h \ Device/LibreVNA/manualcontroldialogvff.h \
Device/LibreVNA/receivercaldialog.h \ Device/LibreVNA/receivercaldialog.h \
@ -114,6 +119,7 @@ HEADERS += \
Traces/eyediagramplot.h \ Traces/eyediagramplot.h \
Traces/fftcomplex.h \ Traces/fftcomplex.h \
Traces/sparamtraceselector.h \ Traces/sparamtraceselector.h \
Traces/sparamtraceselectordialog.h \
Traces/trace.h \ Traces/trace.h \
Traces/traceaxis.h \ Traces/traceaxis.h \
Traces/tracecsvexport.h \ Traces/tracecsvexport.h \
@ -159,6 +165,7 @@ HEADERS += \
preferences.h \ preferences.h \
savable.h \ savable.h \
scpi.h \ scpi.h \
screenshot.h \
streamingserver.h \ streamingserver.h \
tcpserver.h \ tcpserver.h \
touchstone.h \ touchstone.h \
@ -171,6 +178,7 @@ SOURCES += \
Calibration/LibreCAL/usbdevice.cpp \ Calibration/LibreCAL/usbdevice.cpp \
Calibration/calibration.cpp \ Calibration/calibration.cpp \
Calibration/calibrationmeasurement.cpp \ Calibration/calibrationmeasurement.cpp \
Calibration/calibrationviewdialog.cpp \
Calibration/calkit.cpp \ Calibration/calkit.cpp \
Calibration/calkitdialog.cpp \ Calibration/calkitdialog.cpp \
Calibration/calstandard.cpp \ Calibration/calstandard.cpp \
@ -184,11 +192,13 @@ SOURCES += \
CustomWidgets/toggleswitch.cpp \ CustomWidgets/toggleswitch.cpp \
CustomWidgets/touchstoneimport.cpp \ CustomWidgets/touchstoneimport.cpp \
CustomWidgets/tracesetselector.cpp \ CustomWidgets/tracesetselector.cpp \
Device/Harogic/harogicb60.cpp \
Device/LibreVNA/Compound/compounddevice.cpp \ Device/LibreVNA/Compound/compounddevice.cpp \
Device/LibreVNA/Compound/compounddeviceeditdialog.cpp \ Device/LibreVNA/Compound/compounddeviceeditdialog.cpp \
Device/LibreVNA/Compound/compounddriver.cpp \ Device/LibreVNA/Compound/compounddriver.cpp \
Device/LibreVNA/amplitudecaldialog.cpp \ Device/LibreVNA/amplitudecaldialog.cpp \
Device/LibreVNA/deviceconfigurationdialogv1.cpp \ Device/LibreVNA/deviceconfigurationdialogv1.cpp \
Device/LibreVNA/deviceconfigurationdialogvd0.cpp \
Device/LibreVNA/deviceconfigurationdialogvfe.cpp \ Device/LibreVNA/deviceconfigurationdialogvfe.cpp \
Device/LibreVNA/deviceconfigurationdialogvff.cpp \ Device/LibreVNA/deviceconfigurationdialogvff.cpp \
Device/LibreVNA/devicepacketlog.cpp \ Device/LibreVNA/devicepacketlog.cpp \
@ -199,6 +209,8 @@ SOURCES += \
Device/LibreVNA/librevnatcpdriver.cpp \ Device/LibreVNA/librevnatcpdriver.cpp \
Device/LibreVNA/librevnausbdriver.cpp \ Device/LibreVNA/librevnausbdriver.cpp \
Device/LibreVNA/manualcontroldialogV1.cpp \ Device/LibreVNA/manualcontroldialogV1.cpp \
Device/LibreVNA/manualcontroldialogVD0.cpp \
Device/LibreVNA/manualcontroldialogVE0.cpp \
Device/LibreVNA/manualcontroldialogvfe.cpp \ Device/LibreVNA/manualcontroldialogvfe.cpp \
Device/LibreVNA/manualcontroldialogvff.cpp \ Device/LibreVNA/manualcontroldialogvff.cpp \
Device/LibreVNA/receivercaldialog.cpp \ Device/LibreVNA/receivercaldialog.cpp \
@ -267,6 +279,7 @@ SOURCES += \
Traces/eyediagramplot.cpp \ Traces/eyediagramplot.cpp \
Traces/fftcomplex.cpp \ Traces/fftcomplex.cpp \
Traces/sparamtraceselector.cpp \ Traces/sparamtraceselector.cpp \
Traces/sparamtraceselectordialog.cpp \
Traces/trace.cpp \ Traces/trace.cpp \
Traces/traceaxis.cpp \ Traces/traceaxis.cpp \
Traces/tracecsvexport.cpp \ Traces/tracecsvexport.cpp \
@ -310,6 +323,7 @@ SOURCES += \
preferences.cpp \ preferences.cpp \
savable.cpp \ savable.cpp \
scpi.cpp \ scpi.cpp \
screenshot.cpp \
streamingserver.cpp \ streamingserver.cpp \
tcpserver.cpp \ tcpserver.cpp \
touchstone.cpp \ touchstone.cpp \
@ -328,7 +342,7 @@ mac{
PKGCONFIG += libusb-1.0 PKGCONFIG += libusb-1.0
} }
QT += widgets network QT += widgets network svg
FORMS += \ FORMS += \
Calibration/CalStandardLineEditDialog.ui \ Calibration/CalStandardLineEditDialog.ui \
@ -340,6 +354,7 @@ FORMS += \
Calibration/LibreCAL/factoryUpdateDialog.ui \ Calibration/LibreCAL/factoryUpdateDialog.ui \
Calibration/LibreCAL/librecaldialog.ui \ Calibration/LibreCAL/librecaldialog.ui \
Calibration/calibrationdialogui.ui \ Calibration/calibrationdialogui.ui \
Calibration/calibrationviewdialog.ui \
Calibration/calkitdialog.ui \ Calibration/calkitdialog.ui \
Calibration/manualcalibrationdialog.ui \ Calibration/manualcalibrationdialog.ui \
CustomWidgets/csvimport.ui \ CustomWidgets/csvimport.ui \
@ -352,6 +367,7 @@ FORMS += \
Device/LibreVNA/amplitudecaldialog.ui \ Device/LibreVNA/amplitudecaldialog.ui \
Device/LibreVNA/automaticamplitudedialog.ui \ Device/LibreVNA/automaticamplitudedialog.ui \
Device/LibreVNA/deviceconfigurationdialogv1.ui \ Device/LibreVNA/deviceconfigurationdialogv1.ui \
Device/LibreVNA/deviceconfigurationdialogvd0.ui \
Device/LibreVNA/deviceconfigurationdialogvfe.ui \ Device/LibreVNA/deviceconfigurationdialogvfe.ui \
Device/LibreVNA/deviceconfigurationdialogvff.ui \ Device/LibreVNA/deviceconfigurationdialogvff.ui \
Device/LibreVNA/devicepacketlogview.ui \ Device/LibreVNA/devicepacketlogview.ui \
@ -359,6 +375,8 @@ FORMS += \
Device/LibreVNA/frequencycaldialog.ui \ Device/LibreVNA/frequencycaldialog.ui \
Device/LibreVNA/librevnadriversettingswidget.ui \ Device/LibreVNA/librevnadriversettingswidget.ui \
Device/LibreVNA/manualcontroldialogV1.ui \ Device/LibreVNA/manualcontroldialogV1.ui \
Device/LibreVNA/manualcontroldialogVD0.ui \
Device/LibreVNA/manualcontroldialogVE0.ui \
Device/LibreVNA/manualcontroldialogvfe.ui \ Device/LibreVNA/manualcontroldialogvfe.ui \
Device/LibreVNA/manualcontroldialogvff.ui \ Device/LibreVNA/manualcontroldialogvff.ui \
Device/devicelog.ui \ Device/devicelog.ui \
@ -383,6 +401,7 @@ FORMS += \
Traces/eyediagrameditdialog.ui \ Traces/eyediagrameditdialog.ui \
Traces/smithchartdialog.ui \ Traces/smithchartdialog.ui \
Traces/polarchartdialog.ui \ Traces/polarchartdialog.ui \
Traces/sparamtraceselectordialog.ui \
Traces/tracecsvexport.ui \ Traces/tracecsvexport.ui \
Traces/traceeditdialog.ui \ Traces/traceeditdialog.ui \
Traces/traceimportdialog.ui \ Traces/traceimportdialog.ui \

View file

@ -64,6 +64,9 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
normalize.measure = nullptr; normalize.measure = nullptr;
normalize.enable = nullptr; normalize.enable = nullptr;
lastFreq = 0.0;
lastTime = 0.0;
configurationTimer.setSingleShot(true); configurationTimer.setSingleShot(true);
connect(&configurationTimer, &QTimer::timeout, this, [=](){ connect(&configurationTimer, &QTimer::timeout, this, [=](){
ConfigureDevice(); ConfigureDevice();
@ -112,7 +115,7 @@ SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name)
auto eStart = new SIUnitEdit("Hz", " kMG", 6); auto eStart = new SIUnitEdit("Hz", " kMG", 6);
// calculate width required with expected string length // calculate width required with expected string length
auto width = QFontMetrics(eStart->font()).horizontalAdvance("3.00000GHz") + 15; auto width = QFontMetrics(eStart->font()).horizontalAdvance("10.00000 MHz") + 15;
eStart->setFixedWidth(width); eStart->setFixedWidth(width);
eStart->setToolTip("Start frequency"); eStart->setToolTip("Start frequency");
connect(eStart, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetStartFreq); connect(eStart, &SIUnitEdit::valueChanged, this, &SpectrumAnalyzer::SetStartFreq);
@ -396,6 +399,7 @@ nlohmann::json SpectrumAnalyzer::toJSON()
freq["stop"] = settings.freqStop; freq["stop"] = settings.freqStop;
sweep["frequency"] = freq; sweep["frequency"] = freq;
sweep["single"] = singleSweep; sweep["single"] = singleSweep;
sweep["averages"] = averages;
nlohmann::json acq; nlohmann::json acq;
acq["RBW"] = settings.RBW; acq["RBW"] = settings.RBW;
acq["window"] = WindowToString((DeviceDriver::SASettings::Window) settings.window).toStdString(); acq["window"] = WindowToString((DeviceDriver::SASettings::Window) settings.window).toStdString();
@ -502,6 +506,7 @@ void SpectrumAnalyzer::fromJSON(nlohmann::json j)
EnableNormalization(correctSize); EnableNormalization(correctSize);
} }
SetSingleSweep(sweep.value("single", singleSweep)); 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)"; qWarning() << "Got point" << m_avg.pointNum << "but last received point was" << lastPoint << "("<<(m_avg.pointNum-lastPoint-1)<<"missed points)";
} }
lastPoint = m_avg.pointNum; lastPoint = m_avg.pointNum;
lastFreq = m_avg.frequency;
lastTime = (double) m_avg.us / 1000000;
} }
void SpectrumAnalyzer::SettingsChanged() void SpectrumAnalyzer::SettingsChanged()
@ -914,7 +922,7 @@ void SpectrumAnalyzer::ConfigureDevice()
average.reset(DeviceDriver::SApoints()); average.reset(DeviceDriver::SApoints());
UpdateAverageCount(); UpdateAverageCount();
traceModel.clearLiveData(); traceModel.clearLiveData();
emit traceModel.SpanChanged(settings.freqStart, settings.freqStop); traceModel.setSpan(settings.freqStart, settings.freqStop);
} else { } else {
if(window->getDevice()) { if(window->getDevice()) {
changingSettings = true; changingSettings = true;
@ -1102,6 +1110,12 @@ void SpectrumAnalyzer::SetupSCPI()
}, [=](QStringList) -> QString { }, [=](QStringList) -> QString {
return singleSweep ? SCPI::getResultName(SCPI::Result::True): SCPI::getResultName(SCPI::Result::False); 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"); auto scpi_tg = new SCPINode("TRACKing");
SCPINode::add(scpi_tg); SCPINode::add(scpi_tg);
scpi_tg->add(new SCPICommand("ENable", [=](QStringList params) -> QString { scpi_tg->add(new SCPICommand("ENable", [=](QStringList params) -> QString {

View file

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

View file

@ -130,6 +130,7 @@ QString Marker::formatToString(Marker::Format f)
case Format::Inductance: return "Inductance"; case Format::Inductance: return "Inductance";
case Format::QualityFactor: return "Quality Factor"; case Format::QualityFactor: return "Quality Factor";
case Format::GroupDelay: return "Group Delay"; case Format::GroupDelay: return "Group Delay";
case Format::NumberOfPeaks: return "Number of peaks";
case Format::TOI: return "Third order intercept"; case Format::TOI: return "Third order intercept";
case Format::AvgTone: return "Average Tone Level"; case Format::AvgTone: return "Average Tone Level";
case Format::AvgModulationProduct: return "Average Modulation Product Level"; case Format::AvgModulationProduct: return "Average Modulation Product Level";
@ -194,8 +195,6 @@ std::vector<Marker::Format> Marker::applicableFormats()
case Type::Delta: case Type::Delta:
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::PeakTable:
case Type::NegativePeakTable:
if(Trace::isSAParameter(parentTrace->liveParameter())) { if(Trace::isSAParameter(parentTrace->liveParameter())) {
ret.push_back(Format::dBm); ret.push_back(Format::dBm);
ret.push_back(Format::dBuV); ret.push_back(Format::dBuV);
@ -218,6 +217,10 @@ std::vector<Marker::Format> Marker::applicableFormats()
} }
} }
break; break;
case Type::PeakTable:
case Type::NegativePeakTable:
ret.push_back(Format::NumberOfPeaks);
break;
default: default:
break; break;
} }
@ -228,8 +231,6 @@ std::vector<Marker::Format> Marker::applicableFormats()
case Type::Delta: case Type::Delta:
case Type::Maximum: case Type::Maximum:
case Type::Minimum: case Type::Minimum:
case Type::PeakTable:
case Type::NegativePeakTable:
if(Trace::isSAParameter(parentTrace->liveParameter())) { if(Trace::isSAParameter(parentTrace->liveParameter())) {
ret.push_back(Format::dBm); ret.push_back(Format::dBm);
ret.push_back(Format::dBuV); ret.push_back(Format::dBuV);
@ -253,6 +254,10 @@ std::vector<Marker::Format> Marker::applicableFormats()
} }
} }
break; break;
case Type::PeakTable:
case Type::NegativePeakTable:
ret.push_back(Format::NumberOfPeaks);
break;
case Type::Bandpass: case Type::Bandpass:
ret.push_back(Format::CenterBandwidth); ret.push_back(Format::CenterBandwidth);
ret.push_back(Format::InsertionLoss); ret.push_back(Format::InsertionLoss);
@ -451,7 +456,11 @@ QString Marker::readableData(Format f)
switch(type) { switch(type) {
case Type::PeakTable: case Type::PeakTable:
case Type::NegativePeakTable: case Type::NegativePeakTable:
return "Found " + QString::number(helperMarkers.size()) + " peaks"; switch(f) {
case Format::NumberOfPeaks: return "Found " + QString::number(helperMarkers.size()) + " peak" + (helperMarkers.size() == 1 ? "" : "s");
default: return "Invalid";
}
break;
case Type::Delta: { case Type::Delta: {
if(!delta) { if(!delta) {
return "Invalid delta marker"; return "Invalid delta marker";
@ -588,6 +597,7 @@ QString Marker::readableData(Format f)
case Format::maxDeltaPos: case Format::maxDeltaPos:
return "max. Δ+:"+Unit::ToString(maxDeltaPos, "dB", " ", 4); return "max. Δ+:"+Unit::ToString(maxDeltaPos, "dB", " ", 4);
break; break;
case Format::NumberOfPeaks:
case Format::Last: case Format::Last:
return "Invalid"; return "Invalid";
} }
@ -783,6 +793,13 @@ void Marker::setPosition(double pos)
emit positionChanged(position); emit positionChanged(position);
} }
void Marker::setToMiddleOfTrace()
{
if(parentTrace) {
setPosition((parentTrace->minX() + parentTrace->maxX()) / 2);
}
}
void Marker::parentTraceDeleted(Trace *t) void Marker::parentTraceDeleted(Trace *t)
{ {
if(t == parentTrace) { if(t == parentTrace) {
@ -892,10 +909,6 @@ void Marker::deltaDeleted()
void Marker::updateContextmenu() void Marker::updateContextmenu()
{ {
if(parent) {
// do nothing, using contextmenu from parent anyway
return;
}
// check if the contextmenu or one of its submenus is currently open // check if the contextmenu or one of its submenus is currently open
auto *activeWidget = QApplication::activePopupWidget(); auto *activeWidget = QApplication::activePopupWidget();
while (activeWidget) { while (activeWidget) {
@ -910,6 +923,8 @@ void Marker::updateContextmenu()
contextmenu.clear(); contextmenu.clear();
contextmenu.addSection("Marker"); contextmenu.addSection("Marker");
if(!parent) {
// type can only be changed for top level markers
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()) {
@ -924,6 +939,7 @@ void Marker::updateContextmenu()
typegroup->addAction(setTypeAction); typegroup->addAction(setTypeAction);
typemenu->addAction(setTypeAction); typemenu->addAction(setTypeAction);
} }
}
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);
@ -965,6 +981,8 @@ void Marker::updateContextmenu()
} }
} }
if(!parent) {
// grouping and deleting is only possible for top level markers
contextmenu.addSeparator(); contextmenu.addSeparator();
bool needsSeparator = false; bool needsSeparator = false;
@ -1007,6 +1025,7 @@ void Marker::updateContextmenu()
auto deleteAction = new QAction("Delete", &contextmenu); 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);
}
} }
void Marker::traceTypeChanged() void Marker::traceTypeChanged()
@ -1231,6 +1250,7 @@ void Marker::setType(Marker::Type t)
helper->suffix = h.suffix; helper->suffix = h.suffix;
helper->assignTrace(parentTrace); helper->assignTrace(parentTrace);
helper->setType(h.type); helper->setType(h.type);
helper->setVisible(visible);
helperMarkers.push_back(helper); helperMarkers.push_back(helper);
} }
if(type == Type::Flatness) { if(type == Type::Flatness) {
@ -1467,6 +1487,35 @@ void Marker::setNumber(int value)
} }
} }
QWidget *Marker::getTraceEditor(QAbstractItemDelegate *delegate)
{
auto c = new QComboBox;
for(auto t : model->getModel().getTraces()) {
c->addItem(t->name());
if(parentTrace == t) {
// select this item
c->setCurrentIndex(c->count() - 1);
}
}
connect(c, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
emit delegate->commitData(c);
});
return c;
}
void Marker::updateTraceFromEditor(QWidget *w)
{
QComboBox *c = (QComboBox*) w;
for(auto t : model->getModel().getTraces()) {
if(c->currentText() == t->name()) {
if(parentTrace != t) {
assignTrace(t);
}
}
}
update();
}
QWidget *Marker::getTypeEditor(QAbstractItemDelegate *delegate) QWidget *Marker::getTypeEditor(QAbstractItemDelegate *delegate)
{ {
auto c = new QComboBox; auto c = new QComboBox;
@ -1771,11 +1820,7 @@ void Marker::setVisible(bool visible)
} }
QMenu *Marker::getContextMenu() { QMenu *Marker::getContextMenu() {
if(parent) {
return parent->getContextMenu();
} else {
return &contextmenu; return &contextmenu;
}
} }
void Marker::update() void Marker::update()
@ -1800,20 +1845,29 @@ void Marker::update()
break; break;
case Type::PeakTable: case Type::PeakTable:
case Type::NegativePeakTable: { case Type::NegativePeakTable: {
deleteHelperMarkers();
auto peaks = parentTrace->findPeakFrequencies(100, peakThreshold, 3.0, xmin, xmax, type == Type::NegativePeakTable); auto peaks = parentTrace->findPeakFrequencies(100, peakThreshold, 3.0, xmin, xmax, type == Type::NegativePeakTable);
char suffix = 'a'; for(unsigned int i=0;i<peaks.size();i++) {
for(auto p : peaks) { if(helperMarkers.size() <= i) {
// needs to create a new helper marker
auto helper = new Marker(model, number, this); auto helper = new Marker(model, number, this);
helper->suffix = suffix; helper->suffix = QChar('a' + i);
helper->assignTrace(parentTrace); helper->assignTrace(parentTrace);
helper->setPosition(p);
helper->formatTable = formatTable;
helper->formatGraph = formatGraph;
helper->updateContextmenu(); helper->updateContextmenu();
suffix++; helper->setVisible(visible);
helperMarkers.push_back(helper); helperMarkers.push_back(helper);
} }
// update the position of the helper marker
helperMarkers[i]->setPosition(peaks[i]);
}
if(helperMarkers.size() > peaks.size()) {
// need to remove some helper markers
emit beginRemoveHelperMarkers(this);
for(unsigned int i = peaks.size(); i< helperMarkers.size();i++) {
delete helperMarkers[i];
}
helperMarkers.resize(peaks.size());
emit endRemoveHelperMarkers(this);
}
} }
break; break;
case Type::Lowpass: case Type::Lowpass:

View file

@ -35,6 +35,8 @@ public:
Inductance, Inductance,
QualityFactor, QualityFactor,
GroupDelay, GroupDelay,
// Peak table
NumberOfPeaks,
// Noise marker parameters // Noise marker parameters
Noise, Noise,
PhaseNoise, PhaseNoise,
@ -109,8 +111,10 @@ public:
Last, Last,
}; };
Type getType() const; Type getType() const;
QWidget *getTraceEditor(QAbstractItemDelegate *delegate = nullptr);
void updateTraceFromEditor(QWidget *w);
QWidget *getTypeEditor(QAbstractItemDelegate *delegate = nullptr); QWidget *getTypeEditor(QAbstractItemDelegate *delegate = nullptr);
void updateTypeFromEditor(QWidget *c); void updateTypeFromEditor(QWidget *w);
SIUnitEdit* getSettingsEditor(); SIUnitEdit* getSettingsEditor();
QWidget *getRestrictEditor(); QWidget *getRestrictEditor();
void adjustSettings(double value); void adjustSettings(double value);
@ -145,6 +149,7 @@ public:
public slots: public slots:
void setPosition(double freq); void setPosition(double freq);
void setToMiddleOfTrace();
void updateContextmenu(); void updateContextmenu();
signals: signals:
void positionChanged(double pos); void positionChanged(double pos);

View file

@ -77,8 +77,8 @@ Marker *MarkerModel::createDefaultMarker()
} }
} while (used); } while (used);
auto marker = new Marker(this, number); auto marker = new Marker(this, number);
marker->setPosition(2150000000);
marker->assignTrace(model.trace(0)); marker->assignTrace(model.trace(0));
marker->setToMiddleOfTrace();
return marker; return marker;
} }
@ -132,11 +132,11 @@ void MarkerModel::markerDataChanged(Marker *m)
// only update the other columns, do not override editor data // only update the other columns, do not override editor data
emit dataChanged(index(row, ColIndexData), index(row, ColIndexData)); emit dataChanged(index(row, ColIndexData), index(row, ColIndexData));
} else { } else {
emit dataChanged(index(row, ColIndexNumber), index(row, ColIndexData)); emit dataChanged(index(row, ColIndexSettings), index(row, ColIndexData));
// also update any potential helper markers // also update any potential helper markers
for(unsigned int i=0;i<m->getHelperMarkers().size();i++) { for(unsigned int i=0;i<m->getHelperMarkers().size();i++) {
auto modelIndex = createIndex(i, 0, m); auto modelIndex = createIndex(i, 0, m);
emit dataChanged(index(i, ColIndexNumber, modelIndex), index(i, ColIndexData, modelIndex)); emit dataChanged(index(i, ColIndexSettings, modelIndex), index(i, ColIndexData, modelIndex));
} }
} }
} }
@ -436,41 +436,17 @@ QSize MarkerTraceDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIn
QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const QWidget *MarkerTraceDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const
{ {
auto model = (MarkerModel*) index.model(); auto marker = static_cast<const MarkerModel*>(index.model())->markerFromIndex(index);
auto c = new QComboBox(parent); auto editor = marker->getTraceEditor(const_cast<MarkerTraceDelegate*>(this));
c->setMaximumHeight(rowHeight); editor->setMaximumHeight(rowHeight);
connect(c, qOverload<int>(&QComboBox::currentIndexChanged), [c](int) { editor->setParent(parent);
c->clearFocus(); return editor;
});
auto traces = model->getModel().getTraces();
for(auto t : traces) {
MarkerWidgetTraceInfo info;
info.trace = t;
c->addItem(t->name(), QVariant::fromValue(info));
}
return c;
} }
void MarkerTraceDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const void MarkerTraceDelegate::setModelData(QWidget *editor, QAbstractItemModel *, const QModelIndex &index) const
{ {
auto marker = static_cast<const MarkerModel*>(index.model())->markerFromIndex(index); auto marker = static_cast<const MarkerModel*>(index.model())->markerFromIndex(index);
auto c = (QComboBox*) editor; marker->updateTraceFromEditor(editor);
MarkerWidgetTraceInfo markerInfo;
markerInfo.trace = marker->trace();
for(int i=0;i<c->count();i++) {
auto info = qvariant_cast<MarkerWidgetTraceInfo>(c->itemData(i));
if(info == markerInfo) {
c->setCurrentIndex(i);
return;
}
}
}
void MarkerTraceDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
auto markerModel = (MarkerModel*) model;
auto c = (QComboBox*) editor;
markerModel->setData(index, c->itemData(c->currentIndex()));
} }
QSize MarkerSettingsDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const QSize MarkerSettingsDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const

View file

@ -14,7 +14,6 @@ class MarkerTraceDelegate : public QStyledItemDelegate
Q_OBJECT Q_OBJECT
QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const override; QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const override;
QWidget *createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override; QWidget *createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
void setEditorData(QWidget * editor, const QModelIndex & index) const override;
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
}; };

View file

@ -15,6 +15,7 @@ Math::Expression::Expression()
{ {
parser = new ParserX(pckCOMMON | pckUNIT | pckCOMPLEX); parser = new ParserX(pckCOMMON | pckUNIT | pckCOMPLEX);
parser->DefineVar("x", Variable(&x)); parser->DefineVar("x", Variable(&x));
dataType = DataType::Invalid;
expressionChanged(); expressionChanged();
} }
@ -37,10 +38,8 @@ void Math::Expression::edit()
{ {
auto d = new QDialog(); auto d = new QDialog();
auto ui = new Ui::ExpressionDialog; auto ui = new Ui::ExpressionDialog;
d->setAttribute(Qt::WA_DeleteOnClose);
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();
@ -93,7 +92,7 @@ void Math::Expression::inputSamplesChanged(unsigned int begin, unsigned int end)
data.resize(in.size()); data.resize(in.size());
// sanity check input values // sanity check input values
if(end > 0 && end > in.size()) { if(end > 0 && end > in.size()) {
end = in.size() - 1; end = in.size();
} }
if(end <= begin) { if(end <= begin) {
dataMutex.unlock(); dataMutex.unlock();
@ -119,6 +118,14 @@ void Math::Expression::inputSamplesChanged(unsigned int begin, unsigned int end)
emit outputSamplesChanged(begin, end); emit outputSamplesChanged(begin, end);
} }
void Math::Expression::inputTypeChanged(DataType type)
{
// call base class slot
TraceMath::inputTypeChanged(type);
// we need to evaluate the expression again to create the correct variables
expressionChanged();
}
void Math::Expression::expressionChanged() void Math::Expression::expressionChanged()
{ {
if(exp.isEmpty()) { if(exp.isEmpty()) {

View file

@ -24,6 +24,7 @@ public:
public slots: public slots:
void inputSamplesChanged(unsigned int begin, unsigned int end) override; void inputSamplesChanged(unsigned int begin, unsigned int end) override;
virtual void inputTypeChanged(DataType type) override;
private slots: private slots:
void expressionChanged(); void expressionChanged();

View file

@ -130,7 +130,7 @@ public slots:
// some values of the input data have changed, begin/end determine which sample(s) has changed // some values of the input data have changed, begin/end determine which sample(s) has changed
virtual void inputSamplesChanged(unsigned int begin, unsigned int end){Q_UNUSED(begin) Q_UNUSED(end)} virtual void inputSamplesChanged(unsigned int begin, unsigned int end){Q_UNUSED(begin) Q_UNUSED(end)}
void inputTypeChanged(DataType type); virtual void inputTypeChanged(DataType type);
signals: signals:
// emit this whenever a sample changed (alternatively, if all samples are about to change, emit outputDataChanged after they have changed) // emit this whenever a sample changed (alternatively, if all samples are about to change, emit outputDataChanged after they have changed)

View file

@ -7,6 +7,7 @@
#include "fftcomplex.h" #include "fftcomplex.h"
#include "preferences.h" #include "preferences.h"
#include "appwindow.h" #include "appwindow.h"
#include "screenshot.h"
#include <random> #include <random>
#include <thread> #include <thread>
@ -403,16 +404,7 @@ void EyeDiagramPlot::updateContextMenu()
auto image = new QAction("Save image...", contextmenu); auto image = new QAction("Save image...", contextmenu);
contextmenu->addAction(image); contextmenu->addAction(image);
connect(image, &QAction::triggered, this, [=]() { connect(image, &QAction::triggered, this, [=]() {
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); SaveScreenshot(this);
if(filename.isEmpty()) {
// aborted selection
return;
}
if(filename.endsWith(".png")) {
filename.chop(4);
}
filename += ".png";
grab().save(filename);
}); });
contextmenu->addSection("Traces"); contextmenu->addSection("Traces");

View file

@ -13,7 +13,8 @@ SparamTraceSelector::SparamTraceSelector(const TraceModel &model, std::vector<un
: model(model), : model(model),
empty_allowed(empty_allowed), empty_allowed(empty_allowed),
used_ports(used_ports), used_ports(used_ports),
editablePorts(editablePorts) editablePorts(editablePorts),
valid(false)
{ {
createGUI(); createGUI();
setInitialChoices(); setInitialChoices();
@ -140,6 +141,7 @@ void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
} else if(cb->currentIndex() == 0 && points > 0) { } else if(cb->currentIndex() == 0 && points > 0) {
if(!empty_allowed) { if(!empty_allowed) {
valid = false;
emit selectionValid(false); emit selectionValid(false);
} }
// Check if all trace selections are set for none // Check if all trace selections are set for none
@ -161,7 +163,8 @@ void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
} }
if(empty_allowed) { if(empty_allowed) {
// always valid as soon as at least one trace is selected // always valid as soon as at least one trace is selected
emit selectionValid(points > 0); valid = points > 0;
emit selectionValid(valid);
} else { } else {
// actually need to check // actually need to check
valid = true; valid = true;

View file

@ -16,7 +16,7 @@ public:
SparamTraceSelector(const TraceModel &model, std::vector<unsigned int> used_ports, bool empty_allowed = false, unsigned int editablePorts = 0); SparamTraceSelector(const TraceModel &model, std::vector<unsigned int> used_ports, bool empty_allowed = false, unsigned int editablePorts = 0);
SparamTraceSelector(const TraceModel &model, std::set<unsigned int> used_ports, bool empty_allowed = false, unsigned int editablePorts = 0); SparamTraceSelector(const TraceModel &model, std::set<unsigned int> used_ports, bool empty_allowed = false, unsigned int editablePorts = 0);
bool isValid(); bool isValid() {return valid;}
std::map<QString, Trace*> getTraces(); std::map<QString, Trace*> getTraces();
unsigned int getPoints() { return points;} unsigned int getPoints() { return points;}

View file

@ -0,0 +1,36 @@
#include "sparamtraceselectordialog.h"
#include "ui_sparamtraceselectordialog.h"
#include "sparamtraceselector.h"
#include <QPushButton>
SParamTraceSelectorDialog::SParamTraceSelectorDialog(const TraceModel &model, std::vector<unsigned int> used_ports, bool empty_allowed)
: QDialog(nullptr)
, ui(new Ui::SParamTraceSelectorDialog)
{
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
auto selector = new SparamTraceSelector(model, used_ports, empty_allowed);
ui->verticalLayout->replaceWidget(ui->placeholder, selector);
auto okButton = ui->buttonBox->button(QDialogButtonBox::StandardButton::Ok);
connect(selector, &SparamTraceSelector::selectionValid, okButton, &QPushButton::setEnabled);
okButton->setEnabled(selector->isValid());
connect(okButton, &QPushButton::clicked, [=](){
auto traces = selector->getTraces();
if(traces.size() == 0) {
// should not happen
reject();
}
emit tracesSelected(Trace::assembleDatapoints(traces));
accept();
});
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &SParamTraceSelectorDialog::reject);
}
SParamTraceSelectorDialog::~SParamTraceSelectorDialog()
{
delete ui;
}

View file

@ -0,0 +1,27 @@
#ifndef SPARAMTRACESELECTORDIALOG_H
#define SPARAMTRACESELECTORDIALOG_H
#include <QDialog>
#include "tracemodel.h"
namespace Ui {
class SParamTraceSelectorDialog;
}
class SParamTraceSelectorDialog : public QDialog
{
Q_OBJECT
public:
explicit SParamTraceSelectorDialog(const TraceModel &model, std::vector<unsigned int> used_ports, bool empty_allowed = false);
~SParamTraceSelectorDialog();
signals:
void tracesSelected(std::vector<DeviceDriver::VNAMeasurement> traceMeasurements);
private:
Ui::SParamTraceSelectorDialog *ui;
};
#endif // SPARAMTRACESELECTORDIALOG_H

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SParamTraceSelectorDialog</class>
<widget class="QDialog" name="SParamTraceSelectorDialog">
<property name="windowModality">
<enum>Qt::WindowModality::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>494</width>
<height>222</height>
</rect>
</property>
<property name="windowTitle">
<string>S-Paramter Trace Selector Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="placeholder" native="true"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -318,6 +318,10 @@ void Trace::fillFromDatapoints(std::map<QString, Trace *> traceSet, const std::v
{ {
// remove all previous points // remove all previous points
for(auto m : traceSet) { for(auto m : traceSet) {
if(!m.second) {
// no trace, skip
continue;
}
if(!deembedded) { if(!deembedded) {
m.second->clear(); m.second->clear();
} else { } else {
@ -332,6 +336,10 @@ void Trace::fillFromDatapoints(std::map<QString, Trace *> traceSet, const std::v
td.y = m.second; td.y = m.second;
QString measurement = m.first; QString measurement = m.first;
if(traceSet.count(measurement)) { if(traceSet.count(measurement)) {
if(!traceSet[measurement]) {
// no trace, skip
continue;
}
if(!deembedded) { if(!deembedded) {
traceSet[measurement]->addData(td, DataType::Frequency); traceSet[measurement]->addData(td, DataType::Frequency);
} else { } else {
@ -1065,16 +1073,24 @@ std::vector<DeviceDriver::VNAMeasurement> Trace::assembleDatapoints(std::map<QSt
vector<DeviceDriver::VNAMeasurement> ret; vector<DeviceDriver::VNAMeasurement> ret;
// Sanity check traces // Sanity check traces
unsigned int samples = traceSet.begin()->second->size(); unsigned int samples = 0;
auto impedance = traceSet.begin()->second->getReferenceImpedance(); auto impedance = 0;
vector<double> freqs; vector<double> freqs;
for(auto m : traceSet) { for(auto m : traceSet) {
const Trace *t = m.second; const Trace *t = m.second;
if(t->size() != samples) { if(!t) {
// trace not valid, skip
continue;
}
if(samples == 0) {
samples = t->size();
} else if(t->size() != samples) {
qWarning() << "Selected traces do not have the same size"; qWarning() << "Selected traces do not have the same size";
return ret; return ret;
} }
if(t->getReferenceImpedance() != impedance) { if(impedance == 0) {
impedance = t->getReferenceImpedance();
} else if(t->getReferenceImpedance() != impedance) {
qWarning() << "Selected traces do not have the same reference impedance"; qWarning() << "Selected traces do not have the same reference impedance";
return ret; return ret;
} }
@ -1098,13 +1114,22 @@ std::vector<DeviceDriver::VNAMeasurement> Trace::assembleDatapoints(std::map<QSt
} }
} }
if(samples == 0 || freqs.size() == 0) {
qWarning() << "Empty trace set";
return ret;
}
// Checks passed, assemble datapoints // Checks passed, assemble datapoints
for(unsigned int i=0;i<samples;i++) { for(unsigned int i=0;i<samples;i++) {
DeviceDriver::VNAMeasurement d; DeviceDriver::VNAMeasurement d;
for(auto m : traceSet) { for(auto m : traceSet) {
QString measurement = m.first; QString measurement = m.first;
const Trace *t = m.second; const Trace *t = m.second;
if(t) {
d.measurements[measurement] = t->sample(i).y; d.measurements[measurement] = t->sample(i).y;
} else {
d.measurements[measurement] = 0.0;
}
} }
d.pointNum = i; d.pointNum = i;
d.frequency = freqs[i]; d.frequency = freqs[i];

View file

@ -72,11 +72,12 @@ void TraceCSVExport::on_buttonBox_accepted()
return; return;
} }
auto filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", "", "CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(nullptr, "Save calibration data", Preferences::getInstance().UISettings.Paths.data, "CSV files (*.csv)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
if(!filename.endsWith(".csv")) { if(!filename.endsWith(".csv")) {
filename.append(".csv"); filename.append(".csv");
} }

View file

@ -9,6 +9,8 @@ using namespace std;
TraceModel::TraceModel(QObject *parent) TraceModel::TraceModel(QObject *parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
{ {
spanFmin = 0.0;
spanFmax = 6000000000.0;
traces.clear(); traces.clear();
source = DataSource::Unknown; source = DataSource::Unknown;
lastSweepPosition = 0.0; lastSweepPosition = 0.0;
@ -368,6 +370,13 @@ double TraceModel::getSweepPosition() const
} }
} }
void TraceModel::setSpan(double fmin, double fmax)
{
spanFmin = fmin;
spanFmax = fmax;
emit SpanChanged(fmin, fmax);
}
MarkerModel *TraceModel::getMarkerModel() const MarkerModel *TraceModel::getMarkerModel() const
{ {
return markerModel; return markerModel;

View file

@ -62,6 +62,10 @@ public:
double getSweepPosition() const; double getSweepPosition() const;
void setSpan(double fmin, double fmax);
double getSpanStart() {return spanFmin;}
double getSpanStop() {return spanFmax;}
signals: signals:
void SpanChanged(double fmin, double fmax); void SpanChanged(double fmin, double fmax);
void traceAdded(Trace *t); void traceAdded(Trace *t);
@ -80,6 +84,8 @@ private:
QDateTime lastReceivedData; QDateTime lastReceivedData;
std::vector<Trace*> traces; std::vector<Trace*> traces;
MarkerModel *markerModel; MarkerModel *markerModel;
double spanFmin, spanFmax;
}; };
#endif // TRACEMODEL_H #endif // TRACEMODEL_H

View file

@ -42,8 +42,7 @@ TracePlot::TracePlot(TraceModel &model, QWidget *parent)
lastUpdate = QTime::currentTime(); lastUpdate = QTime::currentTime();
replotTimer.setSingleShot(true); replotTimer.setSingleShot(true);
connect(&replotTimer, &QTimer::timeout, this, qOverload<>(&TracePlot::update)); connect(&replotTimer, &QTimer::timeout, this, qOverload<>(&TracePlot::update));
sweep_fmin = std::numeric_limits<double>::lowest(); TracePlot::updateSpan(model.getSpanStart(), model.getSpanStop());
sweep_fmax = std::numeric_limits<double>::max();
xSweep = std::numeric_limits<double>::quiet_NaN(); xSweep = std::numeric_limits<double>::quiet_NaN();
// get notified when the span changes // get notified when the span changes
connect(&model, &TraceModel::SpanChanged, this, qOverload<double, double>(&TracePlot::updateSpan)); connect(&model, &TraceModel::SpanChanged, this, qOverload<double, double>(&TracePlot::updateSpan));
@ -551,6 +550,10 @@ Marker *TracePlot::markerAtPosition(QPoint p, bool onlyMovable)
} }
auto markers = t.first->getMarkers(); auto markers = t.first->getMarkers();
for(Marker* m : markers) { for(Marker* m : markers) {
if(!m->isVisible()) {
// can not interact with invisible markers, pretend that there is nothing here
continue;
}
if(!m->isMovable() && onlyMovable) { if(!m->isMovable() && onlyMovable) {
continue; continue;
} }
@ -559,19 +562,16 @@ Marker *TracePlot::markerAtPosition(QPoint p, bool onlyMovable)
// invalid, skip // invalid, skip
continue; continue;
} }
if(!positionWithinGraphArea(markerPoint) && !Preferences::getInstance().Marker.clipToYAxis) {
// this marker is currently not visible on the graph, do not accept it as a valid choice
continue;
}
auto diff = markerPoint - p; auto diff = markerPoint - p;
unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y(); unsigned int distance = diff.x() * diff.x() + diff.y() * diff.y();
if(distance < closestDistance) { if(distance < closestDistance) {
closestDistance = distance; closestDistance = distance;
if(m->getParent()) {
closestMarker = m->getParent();
if(closestMarker->getType() == Marker::Type::Flatness) {
closestMarker = m; closestMarker = m;
} }
} else {
closestMarker = m;
}
}
} }
} }
if(closestDistance <= 400) { if(closestDistance <= 400) {

View file

@ -3,6 +3,7 @@
#include "Marker/marker.h" #include "Marker/marker.h"
#include "Util/util.h" #include "Util/util.h"
#include "preferences.h" #include "preferences.h"
#include "screenshot.h"
#include <QFileDialog> #include <QFileDialog>
@ -129,7 +130,7 @@ bool TracePolar::positionWithinGraphArea(const QPoint &p)
QPoint TracePolar::dataToPixel(std::complex<double> d) QPoint TracePolar::dataToPixel(std::complex<double> d)
{ {
return transform.map(QPoint(d.real() * polarCoordMax * (1.0 / edgeReflection), -d.imag() * polarCoordMax * (1.0 / edgeReflection))); return transform.map(QPoint(round(d.real() * polarCoordMax * (1.0 / edgeReflection)), round(-d.imag() * polarCoordMax * (1.0 / edgeReflection))));
} }
QPoint TracePolar:: dataToPixel(Trace::Data d) QPoint TracePolar:: dataToPixel(Trace::Data d)
@ -164,13 +165,22 @@ std::complex<double> TracePolar::pixelToData(QPoint p)
QPoint TracePolar::markerToPixel(Marker *m) QPoint TracePolar::markerToPixel(Marker *m)
{ {
QPoint ret = QPoint(); QPoint ret = QPoint();
// if(!m->isTimeDomain()) {
if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) { if(m->getPosition() >= sweep_fmin && m->getPosition() <= sweep_fmax) {
auto d = m->getData(); auto d = m->getData();
d = dataAddOffset(d); d = dataAddOffset(d);
if(abs(d) > edgeReflection && limitToEdge) {
// marker position is outside of the graph
auto &pref = Preferences::getInstance();
if(pref.Marker.clipToYAxis) {
// we still want to show the marker but at the edge of the graph
d /= (abs(d) / edgeReflection);
} else {
// we do not show the marker
return ret;
}
}
ret = dataToPixel(d); ret = dataToPixel(d);
} }
// }
return ret; return ret;
} }
@ -187,6 +197,10 @@ double TracePolar::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
continue; continue;
} }
data = dataAddOffset(data); data = dataAddOffset(data);
if(limitToEdge && abs(data.y) > edgeReflection) {
// this trace point is outside of the visible range, clip to circle
data.y /= (abs(data.y) / edgeReflection);
}
auto plotPoint = dataToPixel(data); auto plotPoint = dataToPixel(data);
if (plotPoint.isNull()) { if (plotPoint.isNull()) {
// destination point outside of currently displayed range // destination point outside of currently displayed range
@ -253,16 +267,7 @@ void TracePolar::updateContextMenu()
auto image = new QAction("Save image...", contextmenu); auto image = new QAction("Save image...", contextmenu);
contextmenu->addAction(image); contextmenu->addAction(image);
connect(image, &QAction::triggered, [=]() { connect(image, &QAction::triggered, [=]() {
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); SaveScreenshot(this);
if(filename.isEmpty()) {
// aborted selection
return;
}
if(filename.endsWith(".png")) {
filename.chop(4);
}
filename += ".png";
grab().save(filename);
}); });
auto createMarker = contextmenu->addAction("Add marker here"); auto createMarker = contextmenu->addAction("Add marker here");

View file

@ -367,14 +367,16 @@ void TraceSmithChart::draw(QPainter &p) {
// marker not in trace range // marker not in trace range
continue; continue;
} }
auto coords = m->getData(); auto point = markerToPixel(m);
coords = dataAddOffset(coords); if(point.isNull()) {
// marker point is invalid
if (limitToEdge && abs(coords) > edgeReflection) {
// outside of visible area
continue; continue;
} }
auto point = dataToPixel(coords);
// if (limitToEdge && abs(pixelToData(point)) > edgeReflection) {
// // outside of visible area
// continue;
// }
auto symbol = m->getSymbol(); auto symbol = m->getSymbol();
p.drawPixmap(point.x() - symbol.width()/2, point.y() - symbol.height(), symbol); p.drawPixmap(point.x() - symbol.width()/2, point.y() - symbol.height(), symbol);
} }

View file

@ -18,22 +18,42 @@ TraceTouchstoneExport::TraceTouchstoneExport(TraceModel &model, QWidget *parent)
ui->selector->setPartialSelectionAllowed(true); ui->selector->setPartialSelectionAllowed(true);
connect(ui->selector, qOverload<>(&TraceSetSelector::selectionChanged), this, &TraceTouchstoneExport::selectionChanged); connect(ui->selector, qOverload<>(&TraceSetSelector::selectionChanged), this, &TraceTouchstoneExport::selectionChanged);
connect(ui->sbPorts, &QSpinBox::valueChanged, this, &TraceTouchstoneExport::setPortNum); connect(ui->sbPorts, &QSpinBox::valueChanged, this, &TraceTouchstoneExport::setPortNum);
// figure out how many ports the user most likely needs // restore the last used settings
unsigned int p; auto& pref = Preferences::getInstance();
for(p=4;p>=1;p--) { auto ports = pref.UISettings.TouchstoneExport.ports;
// do we have a trace name which could indicate such a number of ports? setPortNum(ports);
for(unsigned int i=1;i<=p;i++) { ui->cFormat->setCurrentIndex(pref.UISettings.TouchstoneExport.formatIndex);
auto n1 = "S"+QString::number(p)+QString::number(i); ui->cUnit->setCurrentIndex(pref.UISettings.TouchstoneExport.unitIndex);
auto n2 = "S"+QString::number(i)+QString::number(p);
// attempt to set the traces that were exported last
QStringList traces = pref.UISettings.TouchstoneExport.exportedTraceNames.split(",");
if(traces.size() == ports * ports) {
// got the correct number of traces
for(unsigned int i=0;i<traces.size();i++) {
for(auto t : model.getTraces()) { for(auto t : model.getTraces()) {
if(t->name().contains(n1) || t->name().contains(n2)) { if(t->name() == traces[i]) {
goto traceFound; setTrace(i / ports + 1, i % ports + 1, t);
break;
} }
} }
} }
} }
traceFound:
setPortNum(p); // unsigned int p;
// for(p=4;p>=1;p--) {
// // do we have a trace name which could indicate such a number of ports?
// for(unsigned int i=1;i<=p;i++) {
// auto n1 = "S"+QString::number(p)+QString::number(i);
// auto n2 = "S"+QString::number(i)+QString::number(p);
// for(auto t : model.getTraces()) {
// if(t->name().contains(n1) || t->name().contains(n2)) {
// goto traceFound;
// }
// }
// }
// }
// traceFound:
// setPortNum(p);
} }
TraceTouchstoneExport::~TraceTouchstoneExport() TraceTouchstoneExport::~TraceTouchstoneExport()
@ -41,7 +61,7 @@ TraceTouchstoneExport::~TraceTouchstoneExport()
delete ui; delete ui;
} }
bool TraceTouchstoneExport::setTrace(int portFrom, int portTo, Trace *t) bool TraceTouchstoneExport::setTrace(int portTo, int portFrom, Trace *t)
{ {
return ui->selector->setTrace(portTo, portFrom, t); return ui->selector->setTrace(portTo, portFrom, t);
} }
@ -66,7 +86,7 @@ bool TraceTouchstoneExport::setPortNum(unsigned int ports)
for(auto t : traces) { for(auto t : traces) {
if(t->name().contains(name)) { if(t->name().contains(name)) {
// this could be the correct trace // this could be the correct trace
setTrace(j, i, t); setTrace(i, j, t);
break; break;
} }
} }
@ -77,17 +97,18 @@ bool TraceTouchstoneExport::setPortNum(unsigned int ports)
void TraceTouchstoneExport::on_buttonBox_accepted() void TraceTouchstoneExport::on_buttonBox_accepted()
{ {
auto ports = ui->sbPorts->value(); unsigned int ports = ui->sbPorts->value();
QString extension = ".s"+QString::number(ports)+"p"; QString extension = ".s"+QString::number(ports)+"p";
auto filename = QFileDialog::getSaveFileName(this, "Select file for exporting traces", "", "Touchstone files (*"+extension+")", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(this, "Select file for exporting traces", Preferences::getInstance().UISettings.Paths.data, "Touchstone files (*"+extension+")", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
auto t = Touchstone(ports); auto t = Touchstone(ports);
t.setReferenceImpedance(ui->selector->getReferenceImpedance()); t.setReferenceImpedance(ui->selector->getReferenceImpedance());
// add trace points to touchstone // add trace points to touchstone
for(unsigned int s=0;s<ui->selector->getPoints();s++) { for(unsigned int s=0;s<ui->selector->getPoints();s++) {
Touchstone::Datapoint tData; Touchstone::Datapoint tData;
for(int i=1;i<=ports;i++) { for(unsigned int i=1;i<=ports;i++) {
for(int j=1;j<=ports;j++) { for(unsigned int j=1;j<=ports;j++) {
auto t = ui->selector->getTrace(i, j); auto t = ui->selector->getTrace(i, j);
if(!t) { if(!t) {
// missing trace, set to 0 // missing trace, set to 0
@ -117,6 +138,25 @@ void TraceTouchstoneExport::on_buttonBox_accepted()
} }
t.toFile(filename, unit, format); t.toFile(filename, unit, format);
// update preferences for next call
auto& pref = Preferences::getInstance();
pref.UISettings.TouchstoneExport.ports = ui->sbPorts->value();
pref.UISettings.TouchstoneExport.formatIndex = ui->cFormat->currentIndex();
pref.UISettings.TouchstoneExport.unitIndex = ui->cUnit->currentIndex();
QString traceNames = "";
for(unsigned int i=0;i<ports*ports;i++) {
auto t = ui->selector->getTrace(i / ports + 1, i % ports + 1);
if(t) {
traceNames += t->name();
}
if(i != (ports*ports-1)) {
// add separator for all but the last trace name
traceNames += ",";
}
}
pref.UISettings.TouchstoneExport.exportedTraceNames = traceNames;
delete this; delete this;
} }
} }

View file

@ -18,7 +18,7 @@ class TraceTouchstoneExport : public QDialog
public: public:
explicit TraceTouchstoneExport(TraceModel &model, QWidget *parent = nullptr); explicit TraceTouchstoneExport(TraceModel &model, QWidget *parent = nullptr);
~TraceTouchstoneExport(); ~TraceTouchstoneExport();
bool setTrace(int portFrom, int portTo, Trace *t); bool setTrace(int portTo, int portFrom, Trace *t);
bool setPortNum(unsigned int ports); bool setPortNum(unsigned int ports);
private slots: private slots:

View file

@ -6,6 +6,7 @@
#include "waterfallaxisdialog.h" #include "waterfallaxisdialog.h"
#include "appwindow.h" #include "appwindow.h"
#include "tracexyplot.h" #include "tracexyplot.h"
#include "screenshot.h"
#include <QFileDialog> #include <QFileDialog>
#include <QPainter> #include <QPainter>
@ -213,16 +214,7 @@ void TraceWaterfall::updateContextMenu()
auto image = new QAction("Save image...", contextmenu); auto image = new QAction("Save image...", contextmenu);
contextmenu->addAction(image); contextmenu->addAction(image);
connect(image, &QAction::triggered, [=]() { connect(image, &QAction::triggered, [=]() {
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); SaveScreenshot(this);
if(filename.isEmpty()) {
// aborted selection
return;
}
if(filename.endsWith(".png")) {
filename.chop(4);
}
filename += ".png";
grab().save(filename);
}); });
contextmenu->addSection("Traces"); contextmenu->addSection("Traces");

View file

@ -222,8 +222,9 @@ void TraceWidget::importDialog()
} }
supported.chop(1); supported.chop(1);
supported += ")"; supported += ")";
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", supported, nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", Preferences::getInstance().UISettings.Paths.data, supported, nullptr, Preferences::QFileDialogOptions());
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
importFile(filename); importFile(filename);
} }
} }

View file

@ -9,6 +9,7 @@
#include "preferences.h" #include "preferences.h"
#include "appwindow.h" #include "appwindow.h"
#include "ui_XYPlotConstantLineEditDialog.h" #include "ui_XYPlotConstantLineEditDialog.h"
#include "screenshot.h"
#include <QGridLayout> #include <QGridLayout>
#include <cmath> #include <cmath>
@ -29,8 +30,7 @@ TraceXYPlot::TraceXYPlot(TraceModel &model, QWidget *parent)
// Setup default axis // Setup default axis
setYAxis(0, YAxis::Type::Magnitude, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Magnitude), YAxis::getDefaultLimitMax(YAxis::Type::Magnitude), 14, true); setYAxis(0, YAxis::Type::Magnitude, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Magnitude), YAxis::getDefaultLimitMax(YAxis::Type::Magnitude), 14, true);
setYAxis(1, YAxis::Type::Phase, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Phase), YAxis::getDefaultLimitMax(YAxis::Type::Phase), 12, true); setYAxis(1, YAxis::Type::Phase, false, false, YAxis::getDefaultLimitMin(YAxis::Type::Phase), YAxis::getDefaultLimitMax(YAxis::Type::Phase), 12, true);
// enable autoscaling and set for full span (no information about actual span available yet)
updateSpan(0, 6000000000);
setXAxis(XAxis::Type::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 10, true); setXAxis(XAxis::Type::Frequency, XAxisMode::UseSpan, false, 0, 6000000000, 10, true);
initializeTraceInfo(); initializeTraceInfo();
} }
@ -350,16 +350,7 @@ void TraceXYPlot::updateContextMenu()
auto image = new QAction("Save image...", contextmenu); auto image = new QAction("Save image...", contextmenu);
contextmenu->addAction(image); contextmenu->addAction(image);
connect(image, &QAction::triggered, [=]() { connect(image, &QAction::triggered, [=]() {
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); SaveScreenshot(this);
if(filename.isEmpty()) {
// aborted selection
return;
}
if(filename.endsWith(".png")) {
filename.chop(4);
}
filename += ".png";
grab().save(filename);
}); });
auto createMarker = contextmenu->addAction("Add marker here"); auto createMarker = contextmenu->addAction("Add marker here");
@ -461,8 +452,8 @@ void TraceXYPlot::draw(QPainter &p)
plotAreaWidth -= yAxisDisabledSpace; plotAreaWidth -= yAxisDisabledSpace;
} }
auto plotRect = QRect(plotAreaLeft, plotAreaTop, plotAreaWidth + 1, plotAreaBottom-plotAreaTop); auto plotRect = QRect(plotAreaLeft, plotAreaTop, plotAreaWidth + 1, plotAreaBottom-plotAreaTop + 1);
p.drawRect(plotRect); p.drawRect(QRect(plotAreaLeft, plotAreaTop, plotAreaWidth, plotAreaBottom-plotAreaTop));
// draw axis types // draw axis types
auto font = p.font(); auto font = p.font();
@ -499,7 +490,7 @@ void TraceXYPlot::draw(QPainter &p)
int lastTickLabelEnd = std::numeric_limits<int>::max(); int lastTickLabelEnd = std::numeric_limits<int>::max();
for(unsigned int j = 0; j < yAxis[i].getTicks().size(); j++) { for(unsigned int j = 0; j < yAxis[i].getTicks().size(); j++) {
auto yCoord = yAxis[i].transform(yAxis[i].getTicks()[j], w.height() - xAxisSpace, plotAreaTop); auto yCoord = yAxis[i].transform(yAxis[i].getTicks()[j], plotAreaBottom, plotAreaTop);
p.setPen(QPen(pref.Graphs.Color.axis, 1)); p.setPen(QPen(pref.Graphs.Color.axis, 1));
// draw tickmark on axis // draw tickmark on axis
auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth; auto tickStart = i == 0 ? plotAreaLeft : plotAreaLeft + plotAreaWidth;
@ -639,15 +630,6 @@ void TraceXYPlot::draw(QPainter &p)
p.drawLine(p1, p2); p.drawLine(p1, p2);
} }
if(pref.Marker.clipToYAxis) {
// clip Y coordinate of markers to visible area (always show markers, even when out of range)
if(point.y() < plotRect.top()) {
point.ry() = plotRect.top();
} else if(point.y() > plotRect.bottom()) {
point.ry() = plotRect.bottom();
}
}
if(!plotRect.contains(point)) { if(!plotRect.contains(point)) {
// out of screen // out of screen
continue; continue;
@ -1125,8 +1107,8 @@ QPointF TraceXYPlot::traceToCoordinate(Trace *t, unsigned int sample, YAxis &yax
QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis) QPoint TraceXYPlot::plotValueToPixel(QPointF plotValue, int Yaxis)
{ {
QPoint p; QPoint p;
p.setX(xAxis.transform(plotValue.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth)); p.setX(round(xAxis.transform(plotValue.x(), plotAreaLeft, plotAreaLeft + plotAreaWidth)));
p.setY(yAxis[Yaxis].transform(plotValue.y(), plotAreaBottom, plotAreaTop)); p.setY(round(yAxis[Yaxis].transform(plotValue.y(), plotAreaBottom, plotAreaTop)));
return p; return p;
} }
@ -1165,6 +1147,14 @@ QPoint TraceXYPlot::markerToPixel(Marker *m)
} else { } else {
markerPoint = traceToCoordinate(t, t->index(xPosition), yAxis[0]); markerPoint = traceToCoordinate(t, t->index(xPosition), yAxis[0]);
} }
auto &pref = Preferences::getInstance();
if(pref.Marker.clipToYAxis) {
if(markerPoint.y() > yAxis[0].getRangeMax()) {
markerPoint.setY(yAxis[0].getRangeMax());
} else if(markerPoint.y() < yAxis[0].getRangeMin()) {
markerPoint.setY(yAxis[0].getRangeMin());
}
}
return plotValueToPixel(markerPoint, 0); return plotValueToPixel(markerPoint, 0);
} }
@ -1184,6 +1174,8 @@ double TraceXYPlot::nearestTracePoint(Trace *t, QPoint pixel, double *distance)
continue; continue;
} }
auto plotPoint = plotValueToPixel(point, 0); auto plotPoint = plotValueToPixel(point, 0);
// constrain to the visible Y axis range
Util::constrain(plotPoint.ry(), plotAreaTop, plotAreaBottom);
QPointF diff = plotPoint - pixel; QPointF diff = plotPoint - pixel;
auto distance = diff.x() * diff.x() + diff.y() * diff.y(); auto distance = diff.x() * diff.x() + diff.y() * diff.y();
if(distance < closestDistance) { if(distance < closestDistance) {

View file

@ -116,7 +116,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
setupYAxisConnetions(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs); setupYAxisConnetions(ui->Y1type, ui->Y1linear, ui->Y1log, ui->Y1auto, ui->Y1min, ui->Y1max, ui->Y1Divs, ui->Y1autoDivs);
setupYAxisConnetions(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs); setupYAxisConnetions(ui->Y2type, ui->Y2linear, ui->Y2log, ui->Y2auto, ui->Y2min, ui->Y2max, ui->Y2Divs, ui->Y2autoDivs);
auto updateXenableState = [](QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs) { auto updateXenableState = [](QRadioButton *linear, QRadioButton *log, QCheckBox *CBauto, SIUnitEdit *min, SIUnitEdit *max, QSpinBox *divs, QCheckBox *autoDivs, QComboBox *autoMode) {
log->setEnabled(true); log->setEnabled(true);
linear->setEnabled(true); linear->setEnabled(true);
CBauto->setEnabled(true); CBauto->setEnabled(true);
@ -126,6 +126,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
max->setEnabled(false); max->setEnabled(false);
divs->setEnabled(false); divs->setEnabled(false);
autoDivs->setEnabled(false); autoDivs->setEnabled(false);
autoMode->setEnabled(true);
} else { } else {
min->setEnabled(true); min->setEnabled(true);
max->setEnabled(true); max->setEnabled(true);
@ -136,14 +137,15 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
autoDivs->setEnabled(true); autoDivs->setEnabled(true);
divs->setEnabled(!autoDivs->isChecked()); divs->setEnabled(!autoDivs->isChecked());
} }
autoMode->setEnabled(false);
} }
}; };
connect(ui->Xauto, &QCheckBox::toggled, [this, updateXenableState](bool) { connect(ui->Xauto, &QCheckBox::toggled, [this, updateXenableState](bool) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs, ui->Xautomode);
}); });
connect(ui->XautoDivs, &QCheckBox::toggled, [this, updateXenableState](bool) { connect(ui->XautoDivs, &QCheckBox::toggled, [this, updateXenableState](bool) {
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs, ui->Xautomode);
}); });
ui->XType->setCurrentIndex((int) plot->xAxis.getType()); ui->XType->setCurrentIndex((int) plot->xAxis.getType());
@ -159,7 +161,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
XAxisTypeChanged((int) plot->xAxis.getType()); XAxisTypeChanged((int) plot->xAxis.getType());
connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &XYplotAxisDialog::XAxisTypeChanged); connect(ui->XType, qOverload<int>(&QComboBox::currentIndexChanged), this, &XYplotAxisDialog::XAxisTypeChanged);
connect(ui->Xlog, &QCheckBox::toggled, [=](bool){ connect(ui->Xlog, &QCheckBox::toggled, [=](bool){
updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs); updateXenableState(ui->Xlinear, ui->Xlog, ui->Xauto, ui->Xmin, ui->Xmax, ui->XDivs, ui->XautoDivs, ui->Xautomode);
}); });
// Fill initial values // Fill initial values
@ -242,11 +244,12 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
removeLine(index); removeLine(index);
}); });
connect(ui->exportLines, &QPushButton::clicked, this, [=](){ connect(ui->exportLines, &QPushButton::clicked, this, [=](){
QString filename = QFileDialog::getSaveFileName(nullptr, "Save limit lines", "", "Limit files (*.limits)", nullptr, Preferences::QFileDialogOptions()); QString filename = QFileDialog::getSaveFileName(nullptr, "Save limit lines", Preferences::getInstance().UISettings.Paths.limitLines, "Limit files (*.limits)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.limitLines = QFileInfo(filename).path();
if(!filename.endsWith(".limits")) { if(!filename.endsWith(".limits")) {
filename.append(".limits"); filename.append(".limits");
} }
@ -265,7 +268,7 @@ XYplotAxisDialog::XYplotAxisDialog(TraceXYPlot *plot) :
}); });
connect(ui->importLines, &QPushButton::clicked, [=](){ connect(ui->importLines, &QPushButton::clicked, [=](){
QString filename = QFileDialog::getOpenFileName(nullptr, "Load limit lines", "", "Limit files (*.limits)", nullptr, Preferences::QFileDialogOptions()); QString filename = QFileDialog::getOpenFileName(nullptr, "Load limit lines", Preferences::getInstance().UISettings.Paths.limitLines, "Limit files (*.limits)", nullptr, Preferences::QFileDialogOptions());
ifstream file; ifstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());
if(!file.is_open()) { if(!file.is_open()) {

View file

@ -127,6 +127,31 @@ Deembedding::Deembedding(TraceModel &tm)
addOption(option); addOption(option);
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr)); }, 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 { add(new SCPICommand("CLEAR", [=](QStringList params) -> QString {
Q_UNUSED(params); Q_UNUSED(params);
clear(); clear();
@ -209,12 +234,12 @@ void Deembedding::addOption(DeembeddingOption *option)
emit optionAdded(); 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; return;
} }
std::swap(options[index], options[index+1]); std::swap(options[index1], options[index2]);
updateSCPINames(); updateSCPINames();
} }

View file

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

View file

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

View file

@ -56,6 +56,17 @@ MatchingNetwork::MatchingNetwork()
addComponent(index, c); addComponent(index, c);
return SCPI::getResultName(SCPI::Result::Empty); return SCPI::getResultName(SCPI::Result::Empty);
}, nullptr)); }, 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 { add(new SCPICommand("TYPE", nullptr, [=](QStringList params) -> QString {
unsigned long long index = 0; unsigned long long index = 0;
if(!SCPI::paramToULongLong(params, 0, index)) { if(!SCPI::paramToULongLong(params, 0, index)) {
@ -267,6 +278,7 @@ void MatchingNetwork::fromJSON(nlohmann::json j)
} }
addNetwork = j.value("addNetwork", true); addNetwork = j.value("addNetwork", true);
matching.clear(); matching.clear();
updateSCPINames();
} }
void MatchingNetwork::clearNetwork() void MatchingNetwork::clearNetwork()
@ -643,7 +655,7 @@ MatchingComponent::MatchingComponent(Type type)
// failed to load file // failed to load file
return SCPI::getResultName(SCPI::Result::Error); return SCPI::getResultName(SCPI::Result::Error);
} }
}, nullptr)); }, nullptr, false));
break; break;
default: default:
break; break;
@ -767,8 +779,9 @@ void MatchingComponent::mouseDoubleClickEvent(QMouseEvent *e)
Q_UNUSED(e); Q_UNUSED(e);
if(type == Type::DefinedThrough || type == Type::DefinedShunt) { if(type == Type::DefinedThrough || type == Type::DefinedShunt) {
// select new touchstone file // select new touchstone file
auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", "", "Touchstone files (*.s2p)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Open measurement file", Preferences::getInstance().UISettings.Paths.data, "Touchstone files (*.s2p)", nullptr, Preferences::QFileDialogOptions());
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
Preferences::getInstance().UISettings.Paths.data = QFileInfo(filename).path();
try { try {
*touchstone = Touchstone::fromFile(filename.toStdString()); *touchstone = Touchstone::fromFile(filename.toStdString());
} catch(const std::exception& e) { } catch(const std::exception& e) {
@ -786,6 +799,7 @@ void MatchingComponent::updateTouchstoneLabel()
return; return;
} }
QFont font = touchstoneLabel->font(); QFont font = touchstoneLabel->font();
touchstoneLabel->setStyleSheet("color: black");
font.setPointSize(10); font.setPointSize(10);
touchstoneLabel->setFont(font); touchstoneLabel->setFont(font);
if(touchstone->points() == 0) { if(touchstone->points() == 0) {

View file

@ -1,4 +1,4 @@
#include "vna.h" #include "vna.h"
#include "unit.h" #include "unit.h"
#include "CustomWidgets/toggleswitch.h" #include "CustomWidgets/toggleswitch.h"
@ -18,6 +18,7 @@
#include "CustomWidgets/informationbox.h" #include "CustomWidgets/informationbox.h"
#include "Deembedding/manualdeembeddingdialog.h" #include "Deembedding/manualdeembeddingdialog.h"
#include "Calibration/manualcalibrationdialog.h" #include "Calibration/manualcalibrationdialog.h"
#include "Calibration/calibrationviewdialog.h"
#include "Calibration/LibreCAL/librecaldialog.h" #include "Calibration/LibreCAL/librecaldialog.h"
#include "Util/util.h" #include "Util/util.h"
#include "Tools/parameters.h" #include "Tools/parameters.h"
@ -68,6 +69,10 @@ VNA::VNA(AppWindow *window, QString name)
calWaitFirst = false; calWaitFirst = false;
calDialog = nullptr; calDialog = nullptr;
lastFreq = 0.0;
lastPower = 0.0;
lastTime = 0.0;
changingSettings = false; changingSettings = false;
settings.sweepType = SweepType::Frequency; settings.sweepType = SweepType::Frequency;
settings.zerospan = false; settings.zerospan = false;
@ -109,7 +114,7 @@ VNA::VNA(AppWindow *window, QString name)
auto calData = calMenu->addAction("Calibration Measurements"); auto calData = calMenu->addAction("Calibration Measurements");
connect(calData, &QAction::triggered, [=](){ connect(calData, &QAction::triggered, [=](){
cal.edit(); cal.edit(&traceModel);
}); });
auto calEditKit = calMenu->addAction("Edit Calibration Kit"); auto calEditKit = calMenu->addAction("Edit Calibration Kit");
@ -129,6 +134,16 @@ VNA::VNA(AppWindow *window, QString name)
calMenu->addSeparator(); calMenu->addSeparator();
auto calViewTerms = calMenu->addAction("View error term model");
connect(calViewTerms, &QAction::triggered, [=](){
auto dialog = new CalibrationViewDialog(&cal, DeviceDriver::getInfo(window->getDevice()).Limits.VNA.ports);
if(AppWindow::showGUI()) {
dialog->show();
}
});
calMenu->addSeparator();
auto calImportTerms = calMenu->addAction("Import error terms as traces"); auto calImportTerms = calMenu->addAction("Import error terms as traces");
calImportTerms->setEnabled(false); calImportTerms->setEnabled(false);
connect(calImportTerms, &QAction::triggered, [=](){ connect(calImportTerms, &QAction::triggered, [=](){
@ -314,7 +329,7 @@ VNA::VNA(AppWindow *window, QString name)
auto eStart = new SIUnitEdit("Hz", " kMG", 6); auto eStart = new SIUnitEdit("Hz", " kMG", 6);
// calculate width required with expected string length // calculate width required with expected string length
auto width = QFontMetrics(eStart->font()).horizontalAdvance("3.00000GHz") + 15; auto width = QFontMetrics(eStart->font()).horizontalAdvance("10.00000 MHz") + 15;
eStart->setFixedWidth(width); eStart->setFixedWidth(width);
eStart->setToolTip("Start frequency"); eStart->setToolTip("Start frequency");
connect(eStart, &SIUnitEdit::valueChanged, this, &VNA::SetStartFreq); connect(eStart, &SIUnitEdit::valueChanged, this, &VNA::SetStartFreq);
@ -869,6 +884,7 @@ nlohmann::json VNA::toJSON()
sweep["power"] = power; sweep["power"] = power;
sweep["points"] = settings.npoints; sweep["points"] = settings.npoints;
sweep["IFBW"] = settings.bandwidth; sweep["IFBW"] = settings.bandwidth;
sweep["averages"] = averages;
j["sweep"] = sweep; j["sweep"] = sweep;
j["traces"] = traceModel.toJSON(); j["traces"] = traceModel.toJSON();
@ -906,6 +922,7 @@ void VNA::fromJSON(nlohmann::json j)
// restore sweep settings, keep current value as default in case of missing entry // restore sweep settings, keep current value as default in case of missing entry
SetPoints(sweep.value("points", settings.npoints)); SetPoints(sweep.value("points", settings.npoints));
SetIFBandwidth(sweep.value("IFBW", settings.bandwidth)); SetIFBandwidth(sweep.value("IFBW", settings.bandwidth));
SetAveraging(sweep.value("averages", averages));
if(sweep.contains("frequency")) { if(sweep.contains("frequency")) {
auto freq = sweep["frequency"]; auto freq = sweep["frequency"];
SetStartFreq(freq.value("start", settings.Freq.start)); SetStartFreq(freq.value("start", settings.Freq.start));
@ -1004,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); window->addStreamingData(m_avg, AppWindow::VNADataType::Raw, settings.zerospan);
if(average.settled()) { if(average.settled()) {
@ -1235,6 +1256,7 @@ void VNA::SetLogSweep(bool log)
{ {
if(settings.Freq.logSweep != log) { if(settings.Freq.logSweep != log) {
settings.Freq.logSweep = log; settings.Freq.logSweep = log;
ConstrainAndUpdateFrequencies();
emit logSweepChanged(log); emit logSweepChanged(log);
SettingsChanged(); SettingsChanged();
} }
@ -1364,7 +1386,7 @@ void VNA::ApplyCalibration(Calibration::CalType type)
// Not all required traces available // Not all required traces available
InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. The calibration can be enabled after the missing measurements have been acquired."); InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. The calibration can be enabled after the missing measurements have been acquired.");
DisableCalibration(); DisableCalibration();
cal.edit(); cal.edit(&traceModel);
} else { } else {
// Not all required traces available // Not all required traces available
InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. Please switch to frequency sweep to take these measurements."); InformationBox::ShowMessageBlocking("Missing calibration measurements", "Not all calibration measurements for this type of calibration have been taken. Please switch to frequency sweep to take these measurements.");
@ -1581,6 +1603,15 @@ void VNA::SetupSCPI()
}, [=](QStringList) -> QString { }, [=](QStringList) -> QString {
return singleSweep ? SCPI::getResultName(SCPI::Result::True) : SCPI::getResultName(SCPI::Result::False); 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"); auto scpi_stim = new SCPINode("STIMulus");
SCPINode::add(scpi_stim); SCPINode::add(scpi_stim);
scpi_stim->add(new SCPICommand("LVL", [=](QStringList params) -> QString { scpi_stim->add(new SCPICommand("LVL", [=](QStringList params) -> QString {
@ -1616,6 +1647,12 @@ void VNA::SetupSCPI()
void VNA::ConstrainAndUpdateFrequencies() 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) { if(settings.Freq.stop > DeviceDriver::getInfo(window->getDevice()).Limits.VNA.maxFreq) {
settings.Freq.stop = DeviceDriver::getInfo(window->getDevice()).Limits.VNA.maxFreq; settings.Freq.stop = DeviceDriver::getInfo(window->getDevice()).Limits.VNA.maxFreq;
} }
@ -1950,7 +1987,7 @@ void VNA::ConfigureDevice(bool resetTraces, std::function<void(bool)> cb)
double start = settings.sweepType == SweepType::Frequency ? settings.Freq.start : settings.Power.start; double start = settings.sweepType == SweepType::Frequency ? settings.Freq.start : settings.Power.start;
double stop = settings.sweepType == SweepType::Frequency ? settings.Freq.stop : settings.Power.stop; double stop = settings.sweepType == SweepType::Frequency ? settings.Freq.stop : settings.Power.stop;
int npoints = settings.npoints; int npoints = settings.npoints;
emit traceModel.SpanChanged(start, stop); traceModel.setSpan(start, stop);
if (settings.segments > 1) { if (settings.segments > 1) {
// more than one segment, adjust start/stop // more than one segment, adjust start/stop
npoints = ceil((double) settings.npoints / settings.segments); npoints = ceil((double) settings.npoints / settings.segments);

View file

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

View file

@ -229,19 +229,21 @@ void AppWindow::SetupMenu()
connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice); connect(ui->actionDisconnect, &QAction::triggered, this, &AppWindow::DisconnectDevice);
connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close); connect(ui->actionQuit, &QAction::triggered, this, &AppWindow::close);
connect(ui->actionSave_setup, &QAction::triggered, [=](){ connect(ui->actionSave_setup, &QAction::triggered, [=](){
auto filename = QFileDialog::getSaveFileName(nullptr, "Save setup data", "", "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(nullptr, "Save setup data", Preferences::getInstance().UISettings.Paths.setup, "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.setup = QFileInfo(filename).path();
SaveSetup(filename); SaveSetup(filename);
}); });
connect(ui->actionLoad_setup, &QAction::triggered, [=](){ connect(ui->actionLoad_setup, &QAction::triggered, [=](){
auto filename = QFileDialog::getOpenFileName(nullptr, "Load setup data", "", "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(nullptr, "Load setup data", Preferences::getInstance().UISettings.Paths.setup, "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions());
if(filename.isEmpty()) { if(filename.isEmpty()) {
// aborted selection // aborted selection
return; return;
} }
Preferences::getInstance().UISettings.Paths.setup = QFileInfo(filename).path();
LoadSetup(filename); LoadSetup(filename);
}); });
connect(ui->actionSave_image, &QAction::triggered, [=](){ connect(ui->actionSave_image, &QAction::triggered, [=](){

View file

@ -6,6 +6,7 @@
#include "CustomWidgets/informationbox.h" #include "CustomWidgets/informationbox.h"
#include "ui_main.h" #include "ui_main.h"
#include "screenshot.h"
#include <QPushButton> #include <QPushButton>
#include <QSettings> #include <QSettings>
@ -141,16 +142,7 @@ Mode::Type Mode::TypeFromName(QString s)
void Mode::saveSreenshot() void Mode::saveSreenshot()
{ {
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, Preferences::QFileDialogOptions()); SaveScreenshot(central);
if(filename.isEmpty()) {
// aborted selection
return;
}
if(filename.endsWith(".png")) {
filename.chop(4);
}
filename += ".png";
central->grab().save(filename);
} }
void Mode::finalize(QWidget *centralWidget) void Mode::finalize(QWidget *centralWidget)

View file

@ -68,7 +68,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
ui->StartupStack->setCurrentWidget(ui->StartupPageSetupFile); ui->StartupStack->setCurrentWidget(ui->StartupPageSetupFile);
}); });
connect(ui->StartupBrowse, &QPushButton::clicked, [=](){ connect(ui->StartupBrowse, &QPushButton::clicked, [=](){
ui->StartupSetupFile->setText(QFileDialog::getOpenFileName(nullptr, "Select startup setup file", "", "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions())); ui->StartupSetupFile->setText(QFileDialog::getOpenFileName(nullptr, "Select startup setup file", Preferences::getInstance().UISettings.Paths.setup, "Setup files (*.setup)", nullptr, Preferences::QFileDialogOptions()));
}); });
ui->StartupSweepStart->setUnit("Hz"); ui->StartupSweepStart->setUnit("Hz");
ui->StartupSweepStart->setPrefixes(" kMG"); ui->StartupSweepStart->setPrefixes(" kMG");
@ -162,7 +162,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
if(!w) { if(!w) {
continue; continue;
} }
w->setObjectName(driver->getDriverName()); w->setObjectName(driver->getDriverName().replace(" ", ""));
ui->pageWidget->addWidget(w); ui->pageWidget->addWidget(w);
auto driverItem = new QTreeWidgetItem(); auto driverItem = new QTreeWidgetItem();
driverItem->setText(0, driver->getDriverName()); driverItem->setText(0, driver->getDriverName());
@ -203,8 +203,9 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
emit p->updated(); emit p->updated();
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Save), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getSaveFileName(this, "Save preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getSaveFileName(this, "Save preferences", Preferences::getInstance().UISettings.Paths.pref, "LibreVNA preferences files (*.vnapref)", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.pref = QFileInfo(filename).path();
if(!filename.toLower().endsWith(".vnapref")) { if(!filename.toLower().endsWith(".vnapref")) {
filename.append(".vnapref"); filename.append(".vnapref");
} }
@ -216,8 +217,9 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) :
} }
}); });
connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){ connect(ui->buttonBox->button(QDialogButtonBox::Open), &QPushButton::clicked, [=](){
auto filename = QFileDialog::getOpenFileName(this, "Load preferences", "", "LibreVNA preferences files (*.vnapref)", nullptr, Preferences::QFileDialogOptions()); auto filename = QFileDialog::getOpenFileName(this, "Load preferences", Preferences::getInstance().UISettings.Paths.pref, "LibreVNA preferences files (*.vnapref)", nullptr, Preferences::QFileDialogOptions());
if(filename.length() > 0) { if(filename.length() > 0) {
Preferences::getInstance().UISettings.Paths.pref = QFileInfo(filename).path();
ifstream file; ifstream file;
file.open(filename.toStdString()); file.open(filename.toStdString());
nlohmann::json j; nlohmann::json j;
@ -274,6 +276,7 @@ void PreferencesDialog::setInitialGUIState()
ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteAllPorts); ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteAllPorts);
ui->AcquisitionAllowSegmentedSweep->setChecked(p->Acquisition.allowSegmentedSweep); ui->AcquisitionAllowSegmentedSweep->setChecked(p->Acquisition.allowSegmentedSweep);
ui->AcquisitionAllowCalStartWithUnstableLibreCALTemperature->setChecked(p->Acquisition.allowUseOfUnstableLibreCALTemp);
ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0); ui->AcquisitionAveragingMode->setCurrentIndex(p->Acquisition.useMedianAveraging ? 1 : 0);
ui->AcquisitionFullSpanBehavior->setCurrentIndex(p->Acquisition.fullSpanManual ? 1 : 0); ui->AcquisitionFullSpanBehavior->setCurrentIndex(p->Acquisition.fullSpanManual ? 1 : 0);
ui->AcquisitionFullSpanStart->setValue(p->Acquisition.fullSpanStart); ui->AcquisitionFullSpanStart->setValue(p->Acquisition.fullSpanStart);
@ -396,6 +399,7 @@ void PreferencesDialog::updateFromGUI()
p->Acquisition.alwaysExciteAllPorts = ui->AcquisitionAlwaysExciteBoth->isChecked(); p->Acquisition.alwaysExciteAllPorts = ui->AcquisitionAlwaysExciteBoth->isChecked();
p->Acquisition.allowSegmentedSweep = ui->AcquisitionAllowSegmentedSweep->isChecked(); p->Acquisition.allowSegmentedSweep = ui->AcquisitionAllowSegmentedSweep->isChecked();
p->Acquisition.allowUseOfUnstableLibreCALTemp = ui->AcquisitionAllowCalStartWithUnstableLibreCALTemperature->isChecked();
p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1; p->Acquisition.useMedianAveraging = ui->AcquisitionAveragingMode->currentIndex() == 1;
p->Acquisition.fullSpanManual = ui->AcquisitionFullSpanBehavior->currentIndex() == 1; p->Acquisition.fullSpanManual = ui->AcquisitionFullSpanBehavior->currentIndex() == 1;
p->Acquisition.fullSpanStart = ui->AcquisitionFullSpanStart->value(); p->Acquisition.fullSpanStart = ui->AcquisitionFullSpanStart->value();

View file

@ -103,8 +103,11 @@ public:
} SA; } SA;
} Startup; } Startup;
struct { struct {
// VNA settings
bool alwaysExciteAllPorts; bool alwaysExciteAllPorts;
bool allowSegmentedSweep; bool allowSegmentedSweep;
bool allowUseOfUnstableLibreCALTemp;
bool useMedianAveraging; bool useMedianAveraging;
// Full span settings // Full span settings
@ -207,6 +210,27 @@ public:
bool saveTraceData; bool saveTraceData;
bool useNativeDialogs; bool useNativeDialogs;
} Debug; } Debug;
struct {
struct {
unsigned int ports;
unsigned int formatIndex;
unsigned int unitIndex;
QString exportedTraceNames;
} TouchstoneExport;
struct {
QString setup;
QString cal;
QString calkit;
QString data;
QString image;
QString vnacaldata;
QString packetlog;
QString limitLines;
QString pref;
QString firmware;
} Paths;
qsizetype saveImageFilterIndex;
} UISettings;
bool TCPoverride; // in case of manual port specification via command line bool TCPoverride; // in case of manual port specification via command line
@ -257,6 +281,7 @@ private:
{&Startup.SA.averaging, "Startup.SA.averaging", 1}, {&Startup.SA.averaging, "Startup.SA.averaging", 1},
{&Acquisition.alwaysExciteAllPorts, "Acquisition.alwaysExciteBothPorts", true}, {&Acquisition.alwaysExciteAllPorts, "Acquisition.alwaysExciteBothPorts", true},
{&Acquisition.allowSegmentedSweep, "Acquisition.allowSegmentedSweep", true}, {&Acquisition.allowSegmentedSweep, "Acquisition.allowSegmentedSweep", true},
{&Acquisition.allowUseOfUnstableLibreCALTemp, "Acquisition.allowUseOfUnstableLibreCALTemp", true},
{&Acquisition.useMedianAveraging, "Acquisition.useMedianAveraging", false}, {&Acquisition.useMedianAveraging, "Acquisition.useMedianAveraging", false},
{&Acquisition.fullSpanManual, "Acquisition.fullSpanManual", false}, {&Acquisition.fullSpanManual, "Acquisition.fullSpanManual", false},
{&Acquisition.fullSpanStart, "Acquisition.fullSpanStart", 0.0}, {&Acquisition.fullSpanStart, "Acquisition.fullSpanStart", 0.0},
@ -374,6 +399,22 @@ private:
{&Debug.USBlogSizeLimit, "Debug.USBlogSizeLimit", 10000000.0}, {&Debug.USBlogSizeLimit, "Debug.USBlogSizeLimit", 10000000.0},
{&Debug.saveTraceData, "Debug.saveTraceData", false}, {&Debug.saveTraceData, "Debug.saveTraceData", false},
{&Debug.useNativeDialogs, "Debug.useNativeDialogs", true}, {&Debug.useNativeDialogs, "Debug.useNativeDialogs", true},
{&UISettings.TouchstoneExport.ports, "UISettings.TouchstoneExport.ports", 2},
{&UISettings.TouchstoneExport.formatIndex, "UISettings.TouchstoneExport.formatIndex", 2},
{&UISettings.TouchstoneExport.unitIndex, "UISettings.TouchstoneExport.unitIndex", 3},
{&UISettings.TouchstoneExport.exportedTraceNames, "UISettings.TouchstoneExport.exportedTraceNames", ""},
{&UISettings.Paths.setup, "UISettings.Paths.setup", ""},
{&UISettings.Paths.cal, "UISettings.Paths.cal", ""},
{&UISettings.Paths.calkit, "UISettings.Paths.calkit", ""},
{&UISettings.Paths.data, "UISettings.Paths.data", ""},
{&UISettings.Paths.image, "UISettings.Paths.image", ""},
{&UISettings.Paths.vnacaldata, "UISettings.Paths.vnacaldata", ""},
{&UISettings.Paths.packetlog, "UISettings.Paths.packetlog", ""},
{&UISettings.Paths.limitLines, "UISettings.Paths.limitLines", ""},
{&UISettings.Paths.pref, "UISettings.Paths.pref", ""},
{&UISettings.Paths.firmware, "UISettings.Paths.firmware", ""},
{&UISettings.saveImageFilterIndex, "UISettings.saveImageFilterIndex", 0},
}}; }};
}; };

View file

@ -98,7 +98,7 @@
</size> </size>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="Startup"> <widget class="QWidget" name="Startup">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
@ -112,8 +112,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>522</width> <width>683</width>
<height>945</height> <height>902</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_13"> <layout class="QVBoxLayout" name="verticalLayout_13">
@ -713,8 +713,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>564</width> <width>697</width>
<height>477</height> <height>564</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_21"> <layout class="QVBoxLayout" name="verticalLayout_21">
@ -741,6 +741,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QGroupBox" name="groupBox_17">
<property name="title">
<string>Electronic Calibration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_25">
<item>
<widget class="QCheckBox" name="AcquisitionAllowCalStartWithUnstableLibreCALTemperature">
<property name="text">
<string>Allow calibration to start before the LibreCAL temperature has stabilized</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -955,7 +971,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>683</width> <width>683</width>
<height>1217</height> <height>1182</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_22"> <layout class="QVBoxLayout" name="verticalLayout_22">
@ -1520,8 +1536,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>602</width> <width>486</width>
<height>628</height> <height>608</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_7"> <layout class="QVBoxLayout" name="verticalLayout_7">
@ -1880,8 +1896,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>168</width> <width>144</width>
<height>127</height> <height>124</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_12"> <layout class="QHBoxLayout" name="horizontalLayout_12">
@ -2163,8 +2179,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>258</width> <width>697</width>
<height>241</height> <height>564</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout_19"> <layout class="QHBoxLayout" name="horizontalLayout_19">

View file

@ -297,7 +297,7 @@ bool SCPINode::addDoubleParameter(QString name, double &param, bool gettable, bo
return SCPI::getResultName(SCPI::Result::Error); return SCPI::getResultName(SCPI::Result::Error);
} }
} : (std::function<QString(QStringList)>) nullptr; } : (std::function<QString(QStringList)>) nullptr;
auto query = gettable ? [=](QStringList params) -> QString { auto query = gettable ? [&param](QStringList params) -> QString {
Q_UNUSED(params) Q_UNUSED(params)
return QString::number(param); return QString::number(param);
} : (std::function<QString(QStringList)>) nullptr; } : (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)); 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) bool SCPINode::changeName(QString newname)
{ {
if(newname == name) { if(newname == name) {
@ -548,16 +568,43 @@ QString SCPINode::parse(QString cmd, SCPINode* &lastNode)
return SCPI::getResultName(SCPI::Result::Error); return SCPI::getResultName(SCPI::Result::Error);
} else { } else {
// no more levels, search for command // no more levels, search for command
auto params = cmd.split(" "); QStringList params;
auto cmd = params.front(); 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(); params.pop_front();
bool isQuery = false; bool isQuery = false;
if (cmd[cmd.size()-1]=='?') { if (cmdName[cmdName.size()-1]=='?') {
isQuery = true; isQuery = true;
cmd.chop(1); cmdName.chop(1);
} }
for(auto c : commands) { 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 // save current node in case of non-root for the next command
lastNode = this; lastNode = this;
if(c->convertToUppercase()) { 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 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 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 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); bool changeName(QString newname);
QString leafName() {return name.split(":").back();} QString leafName() {return name.split(":").back();}

View file

@ -0,0 +1,65 @@
#include "screenshot.h"
#include "preferences.h"
#include <QCoreApplication>
#include <QPainter>
#include <QSvgGenerator>
void SaveScreenshot(QWidget *widget)
{
Q_ASSERT(widget != nullptr);
const QStringList extensions = QStringList() << "png" << "svg";
QStringList filters;
for (const QString& ext: extensions) {
filters << QString("%1 image files (*.%2)").arg(ext.toUpper(), ext);
}
auto& settings = Preferences::getInstance().UISettings;
const QString filterString = filters.join(";;");
qsizetype filterIndex = qBound(0, settings.saveImageFilterIndex, filters.size() - 1);
QString selectedFilter = filters[filterIndex];
auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", settings.Paths.image, filterString, &selectedFilter, Preferences::QFileDialogOptions());
if(filename.isEmpty()) {
// aborted selection
return;
}
filterIndex = filters.indexOf(selectedFilter);
const QString& extension = extensions[filterIndex];
if(!filename.endsWith(extension)) {
filename += '.' + extension;
}
settings.Paths.image = QFileInfo(filename).path();
settings.saveImageFilterIndex = filterIndex;
switch (filterIndex)
{
case 0: // PNG
widget->grab().save(filename);
break;
case 1: // SVG
{
QSvgGenerator generator;
generator.setFileName(filename);
generator.setViewBox(QRect(QPoint(), widget->size()));
generator.setTitle(QCoreApplication::applicationName());
generator.setDescription(QString("Created by %1 %2").arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion()));
QPainter painter;
painter.begin(&generator);
widget->render(&painter);
painter.end();
break;
}
default:
Q_ASSERT(false);
break;
}
}

View file

@ -0,0 +1,8 @@
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
class QWidget;
void SaveScreenshot(QWidget *widget);
#endif // SCREENSHOT_H

View file

@ -68,6 +68,10 @@ QString Unit::ToString(double value, QString unit, QString prefixes, int precisi
} }
ss << value; ss << value;
sValue.append(QString::fromStdString(ss.str())); sValue.append(QString::fromStdString(ss.str()));
if(!unit.isEmpty() && !unit.startsWith(' ')) {
// add space between number and unit
sValue.append(' ');
}
sValue.append(prefixes[prefixIndex]); sValue.append(prefixes[prefixIndex]);
} }
sValue.append(unit); sValue.append(unit);

View file

@ -1,4 +1,4 @@
QT += testlib widgets network QT += testlib widgets network svg
CONFIG += qt console warn_on depend_includepath testcase CONFIG += qt console warn_on depend_includepath testcase
CONFIG -= app_bundle CONFIG -= app_bundle
@ -12,6 +12,7 @@ SOURCES += \
../LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp \ ../LibreVNA-GUI/Calibration/LibreCAL/usbdevice.cpp \
../LibreVNA-GUI/Calibration/calibration.cpp \ ../LibreVNA-GUI/Calibration/calibration.cpp \
../LibreVNA-GUI/Calibration/calibrationmeasurement.cpp \ ../LibreVNA-GUI/Calibration/calibrationmeasurement.cpp \
../LibreVNA-GUI/Calibration/calibrationviewdialog.cpp \
../LibreVNA-GUI/Calibration/calkit.cpp \ ../LibreVNA-GUI/Calibration/calkit.cpp \
../LibreVNA-GUI/Calibration/calkitdialog.cpp \ ../LibreVNA-GUI/Calibration/calkitdialog.cpp \
../LibreVNA-GUI/Calibration/calstandard.cpp \ ../LibreVNA-GUI/Calibration/calstandard.cpp \
@ -25,8 +26,10 @@ SOURCES += \
../LibreVNA-GUI/CustomWidgets/toggleswitch.cpp \ ../LibreVNA-GUI/CustomWidgets/toggleswitch.cpp \
../LibreVNA-GUI/CustomWidgets/touchstoneimport.cpp \ ../LibreVNA-GUI/CustomWidgets/touchstoneimport.cpp \
../LibreVNA-GUI/CustomWidgets/tracesetselector.cpp \ ../LibreVNA-GUI/CustomWidgets/tracesetselector.cpp \
../LibreVNA-GUI/Device/Harogic/harogicb60.cpp \
../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.cpp \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.cpp \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.cpp \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvd0.cpp \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.cpp \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.cpp \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.cpp \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.cpp \
../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.cpp \
@ -35,6 +38,8 @@ SOURCES += \
../LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp \ ../LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.cpp \
../LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp \ ../LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.cpp \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.cpp \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.cpp \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVD0.cpp \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVE0.cpp \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.cpp \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.cpp \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.cpp \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.cpp \
../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.cpp \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.cpp \
@ -108,6 +113,7 @@ SOURCES += \
../LibreVNA-GUI/Traces/eyediagramplot.cpp \ ../LibreVNA-GUI/Traces/eyediagramplot.cpp \
../LibreVNA-GUI/Traces/fftcomplex.cpp \ ../LibreVNA-GUI/Traces/fftcomplex.cpp \
../LibreVNA-GUI/Traces/sparamtraceselector.cpp \ ../LibreVNA-GUI/Traces/sparamtraceselector.cpp \
../LibreVNA-GUI/Traces/sparamtraceselectordialog.cpp \
../LibreVNA-GUI/Traces/trace.cpp \ ../LibreVNA-GUI/Traces/trace.cpp \
../LibreVNA-GUI/Traces/traceaxis.cpp \ ../LibreVNA-GUI/Traces/traceaxis.cpp \
../LibreVNA-GUI/Traces/tracecsvexport.cpp \ ../LibreVNA-GUI/Traces/tracecsvexport.cpp \
@ -150,10 +156,12 @@ SOURCES += \
../LibreVNA-GUI/preferences.cpp \ ../LibreVNA-GUI/preferences.cpp \
../LibreVNA-GUI/savable.cpp \ ../LibreVNA-GUI/savable.cpp \
../LibreVNA-GUI/scpi.cpp \ ../LibreVNA-GUI/scpi.cpp \
../LibreVNA-GUI/screenshot.cpp \
../LibreVNA-GUI/tcpserver.cpp \ ../LibreVNA-GUI/tcpserver.cpp \
../LibreVNA-GUI/streamingserver.cpp \ ../LibreVNA-GUI/streamingserver.cpp \
../LibreVNA-GUI/touchstone.cpp \ ../LibreVNA-GUI/touchstone.cpp \
../LibreVNA-GUI/unit.cpp \ ../LibreVNA-GUI/unit.cpp \
calibrationtests.cpp \
ffttests.cpp \ ffttests.cpp \
impedancerenormalizationtests.cpp \ impedancerenormalizationtests.cpp \
main.cpp \ main.cpp \
@ -198,6 +206,7 @@ HEADERS += \
../LibreVNA-GUI/Calibration/LibreCAL/usbdevice.h \ ../LibreVNA-GUI/Calibration/LibreCAL/usbdevice.h \
../LibreVNA-GUI/Calibration/calibration.h \ ../LibreVNA-GUI/Calibration/calibration.h \
../LibreVNA-GUI/Calibration/calibrationmeasurement.h \ ../LibreVNA-GUI/Calibration/calibrationmeasurement.h \
../LibreVNA-GUI/Calibration/calibrationviewdialog.h \
../LibreVNA-GUI/Calibration/calkit.h \ ../LibreVNA-GUI/Calibration/calkit.h \
../LibreVNA-GUI/Calibration/calkitdialog.h \ ../LibreVNA-GUI/Calibration/calkitdialog.h \
../LibreVNA-GUI/Calibration/calstandard.h \ ../LibreVNA-GUI/Calibration/calstandard.h \
@ -211,8 +220,10 @@ HEADERS += \
../LibreVNA-GUI/CustomWidgets/toggleswitch.h \ ../LibreVNA-GUI/CustomWidgets/toggleswitch.h \
../LibreVNA-GUI/CustomWidgets/touchstoneimport.h \ ../LibreVNA-GUI/CustomWidgets/touchstoneimport.h \
../LibreVNA-GUI/CustomWidgets/tracesetselector.h \ ../LibreVNA-GUI/CustomWidgets/tracesetselector.h \
../LibreVNA-GUI/Device/Harogic/harogicb60.h \
../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.h \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.h \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.h \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvd0.h \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.h \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.h \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.h \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.h \
../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.h \ ../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.h \
@ -221,6 +232,8 @@ HEADERS += \
../LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.h \ ../LibreVNA-GUI/Device/LibreVNA/librevnatcpdriver.h \
../LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.h \ ../LibreVNA-GUI/Device/LibreVNA/librevnausbdriver.h \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.h \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.h \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVD0.h \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVE0.h \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.h \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.h \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.h \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.h \
../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.h \ ../LibreVNA-GUI/Device/LibreVNA/receivercaldialog.h \
@ -305,6 +318,7 @@ HEADERS += \
../LibreVNA-GUI/Traces/eyediagramplot.h \ ../LibreVNA-GUI/Traces/eyediagramplot.h \
../LibreVNA-GUI/Traces/fftcomplex.h \ ../LibreVNA-GUI/Traces/fftcomplex.h \
../LibreVNA-GUI/Traces/sparamtraceselector.h \ ../LibreVNA-GUI/Traces/sparamtraceselector.h \
../LibreVNA-GUI/Traces/sparamtraceselectordialog.h \
../LibreVNA-GUI/Traces/trace.h \ ../LibreVNA-GUI/Traces/trace.h \
../LibreVNA-GUI/Traces/traceaxis.h \ ../LibreVNA-GUI/Traces/traceaxis.h \
../LibreVNA-GUI/Traces/tracecsvexport.h \ ../LibreVNA-GUI/Traces/tracecsvexport.h \
@ -348,10 +362,12 @@ HEADERS += \
../LibreVNA-GUI/preferences.h \ ../LibreVNA-GUI/preferences.h \
../LibreVNA-GUI/savable.h \ ../LibreVNA-GUI/savable.h \
../LibreVNA-GUI/scpi.h \ ../LibreVNA-GUI/scpi.h \
../LibreVNA-GUI/screenshot.h \
../LibreVNA-GUI/tcpserver.h \ ../LibreVNA-GUI/tcpserver.h \
../LibreVNA-GUI/streamingserver.h \ ../LibreVNA-GUI/streamingserver.h \
../LibreVNA-GUI/touchstone.h \ ../LibreVNA-GUI/touchstone.h \
../LibreVNA-GUI/unit.h \ ../LibreVNA-GUI/unit.h \
calibrationtests.h \
ffttests.h \ ffttests.h \
impedancerenormalizationtests.h \ impedancerenormalizationtests.h \
parametertests.h \ parametertests.h \
@ -374,6 +390,7 @@ FORMS += \
../LibreVNA-GUI/Calibration/LibreCAL/factoryUpdateDialog.ui \ ../LibreVNA-GUI/Calibration/LibreCAL/factoryUpdateDialog.ui \
../LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.ui \ ../LibreVNA-GUI/Calibration/LibreCAL/librecaldialog.ui \
../LibreVNA-GUI/Calibration/calibrationdialogui.ui \ ../LibreVNA-GUI/Calibration/calibrationdialogui.ui \
../LibreVNA-GUI/Calibration/calibrationviewdialog.ui \
../LibreVNA-GUI/Calibration/calkitdialog.ui \ ../LibreVNA-GUI/Calibration/calkitdialog.ui \
../LibreVNA-GUI/Calibration/manualcalibrationdialog.ui \ ../LibreVNA-GUI/Calibration/manualcalibrationdialog.ui \
../LibreVNA-GUI/CustomWidgets/csvimport.ui \ ../LibreVNA-GUI/CustomWidgets/csvimport.ui \
@ -385,6 +402,7 @@ FORMS += \
../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/amplitudecaldialog.ui \
../LibreVNA-GUI/Device/LibreVNA/automaticamplitudedialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/automaticamplitudedialog.ui \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.ui \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogv1.ui \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvd0.ui \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.ui \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvfe.ui \
../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.ui \ ../LibreVNA-GUI/Device/LibreVNA/deviceconfigurationdialogvff.ui \
../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/firmwareupdatedialog.ui \
@ -392,6 +410,8 @@ FORMS += \
../LibreVNA-GUI/Device/LibreVNA/librevnadriversettingswidget.ui \ ../LibreVNA-GUI/Device/LibreVNA/librevnadriversettingswidget.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.ui \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogV1.ui \
../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.ui \ ../LibreVNA-GUI/Device/LibreVNA/Compound/compounddeviceeditdialog.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVD0.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogVE0.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.ui \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvfe.ui \
../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.ui \ ../LibreVNA-GUI/Device/LibreVNA/manualcontroldialogvff.ui \
../LibreVNA-GUI/Device/devicelog.ui \ ../LibreVNA-GUI/Device/devicelog.ui \
@ -417,6 +437,7 @@ FORMS += \
../LibreVNA-GUI/Traces/eyediagrameditdialog.ui \ ../LibreVNA-GUI/Traces/eyediagrameditdialog.ui \
../LibreVNA-GUI/Traces/polarchartdialog.ui \ ../LibreVNA-GUI/Traces/polarchartdialog.ui \
../LibreVNA-GUI/Traces/smithchartdialog.ui \ ../LibreVNA-GUI/Traces/smithchartdialog.ui \
../LibreVNA-GUI/Traces/sparamtraceselectordialog.ui \
../LibreVNA-GUI/Traces/tracecsvexport.ui \ ../LibreVNA-GUI/Traces/tracecsvexport.ui \
../LibreVNA-GUI/Traces/traceeditdialog.ui \ ../LibreVNA-GUI/Traces/traceeditdialog.ui \
../LibreVNA-GUI/Traces/traceimportdialog.ui \ ../LibreVNA-GUI/Traces/traceimportdialog.ui \

View file

@ -0,0 +1,136 @@
#include "calibrationtests.h"
#include "calibration.h"
CalibrationTests::CalibrationTests() {}
void CalibrationTests::LinearDetection()
{
// create some measurements
std::vector<CalibrationMeasurement::Base*> m;
double startFreq = 100000;
double stopFreq = 6000000000;
int points = 1001;
Calibration cal;
cal.getKit().setIdealDefault();
auto open = new CalibrationMeasurement::Open(&cal);
open->setPort(1);
m.push_back(open);
auto _short = new CalibrationMeasurement::Short(&cal);
_short->setPort(1);
m.push_back(_short);
auto load = new CalibrationMeasurement::Load(&cal);
load->setPort(1);
m.push_back(load);
for(int i=0;i<points;i++) {
double f = startFreq + (stopFreq - startFreq) * i / (points-1);
DeviceDriver::VNAMeasurement meas;
meas.frequency = f;
meas.measurements["S11"] = 0.0;
m[0]->addPoint(meas);
m[1]->addPoint(meas);
m[2]->addPoint(meas);
}
// verify correct detection
double detectedStart;
double detectedStop;
int detectedPoints;
bool detectedLog;
Calibration::hasFrequencyOverlap(m, &detectedStart, &detectedStop, &detectedPoints, &detectedLog);
QVERIFY(qFuzzyCompare(detectedStart, startFreq));
QVERIFY(qFuzzyCompare(detectedStop, stopFreq));
QVERIFY(detectedPoints == points);
QVERIFY(detectedLog == false);
}
void CalibrationTests::LogDetection()
{
// create some measurements
std::vector<CalibrationMeasurement::Base*> m;
double startFreq = 100000;
double stopFreq = 6000000000;
int points = 1001;
Calibration cal;
cal.getKit().setIdealDefault();
auto open = new CalibrationMeasurement::Open(&cal);
open->setPort(1);
m.push_back(open);
auto _short = new CalibrationMeasurement::Short(&cal);
_short->setPort(1);
m.push_back(_short);
auto load = new CalibrationMeasurement::Load(&cal);
load->setPort(1);
m.push_back(load);
for(int i=0;i<points;i++) {
double f = startFreq * pow(10.0, i * log10(stopFreq / startFreq) / (points - 1));
DeviceDriver::VNAMeasurement meas;
meas.frequency = f;
meas.measurements["S11"] = 0.0;
m[0]->addPoint(meas);
m[1]->addPoint(meas);
m[2]->addPoint(meas);
}
// verify correct detection
double detectedStart;
double detectedStop;
int detectedPoints;
bool detectedLog;
Calibration::hasFrequencyOverlap(m, &detectedStart, &detectedStop, &detectedPoints, &detectedLog);
QVERIFY(qFuzzyCompare(detectedStart, startFreq));
QVERIFY(qFuzzyCompare(detectedStop, stopFreq));
QVERIFY(detectedPoints == points);
QVERIFY(detectedLog == true);
}
void CalibrationTests::MixedDetection()
{
// create some measurements
std::vector<CalibrationMeasurement::Base*> m;
double startFreq = 100000;
double stopFreq = 6000000000;
int points = 1001;
Calibration cal;
cal.getKit().setIdealDefault();
auto open = new CalibrationMeasurement::Open(&cal);
open->setPort(1);
m.push_back(open);
auto _short = new CalibrationMeasurement::Short(&cal);
_short->setPort(1);
m.push_back(_short);
auto load = new CalibrationMeasurement::Load(&cal);
load->setPort(1);
m.push_back(load);
for(int i=0;i<points;i++) {
// one linear measurement, two log measurement
double flin = startFreq + (stopFreq - startFreq) * i / (points-1);
double flog = startFreq * pow(10.0, i * log10(stopFreq / startFreq) / (points - 1));
DeviceDriver::VNAMeasurement measlin;
measlin.frequency = flin;
measlin.measurements["S11"] = 0.0;
DeviceDriver::VNAMeasurement measlog;
measlog.frequency = flog;
measlog.measurements["S11"] = 0.0;
m[0]->addPoint(measlin);
m[1]->addPoint(measlog);
m[2]->addPoint(measlog);
}
// verify correct detection
double detectedStart;
double detectedStop;
int detectedPoints;
bool detectedLog;
Calibration::hasFrequencyOverlap(m, &detectedStart, &detectedStop, &detectedPoints, &detectedLog);
QVERIFY(qFuzzyCompare(detectedStart, startFreq));
QVERIFY(qFuzzyCompare(detectedStop, stopFreq));
QVERIFY(detectedPoints == points);
QVERIFY(detectedLog == true);
}

Some files were not shown because too many files have changed in this diff Show more