diff --git a/Documentation/DeveloperInfo/FPGA_protocol.pdf b/Documentation/DeveloperInfo/FPGA_protocol.pdf index d457216..c30225d 100644 Binary files a/Documentation/DeveloperInfo/FPGA_protocol.pdf and b/Documentation/DeveloperInfo/FPGA_protocol.pdf differ diff --git a/Documentation/DeveloperInfo/FPGA_protocol.tex b/Documentation/DeveloperInfo/FPGA_protocol.tex index 2f3563f..459343e 100644 --- a/Documentation/DeveloperInfo/FPGA_protocol.tex +++ b/Documentation/DeveloperInfo/FPGA_protocol.tex @@ -343,9 +343,10 @@ The register contains the number of points per sweep negative one, e.g. set to 1 \rwbits{9}{2}{Window[1:0]} \rwbits{11}{1}{SCEN} \rwbits{12}{1}{LCEN} -\rwbits{13}{1}{EXP2} -\rwbits{14}{1}{EXP1} -\rwbits{15}{1}{PSEN} +\robits{13}{3}{reserved} +%\rwbits{13}{1}{EXP2} +%\rwbits{14}{1}{EXP1} +%\rwbits{15}{1}{PSEN} \end{tikzpicture} \end{center} \begin{itemize} @@ -371,8 +372,8 @@ Setting & Window type\\ \end{center} \item \textbf{SCEN:}{Source chip enable} \item \textbf{LCEN:}{LO chip enable} -\item \textbf{EXP1:}{Excite Port1 during sweep} -\item \textbf{EXP2:}{Excite Port2 during sweep} +%\item \textbf{EXP1:}{Excite Port1 during sweep} +%\item \textbf{EXP2:}{Excite Port2 during sweep} \item \textbf{PSEN:}{Port switch enable} \end{itemize} @@ -407,6 +408,27 @@ $$ PhaseInc = \frac{4096 * f_{IF2}}{SR_{ADC}} $$ For the the default IF frequency of $f_{IF2} = \SI{250}{\kilo\hertz}$ this evaluates to 10*Presc (see ADC prescaler register). \end{itemize} +\subsection{Sweep setup: 0x06} +Each point in the sweep is done in stages. Each stage consists of (optionally) routing the source signal to one of the ports and sampling of all ADCs. A "new data" interrupt is triggered after each stage. +\label{reg:sweepsetup} +\begin{center} +\begin{tikzpicture} +\bitrect{16}{16-\bit} +\rwbits{0}{3}{Stages} +\rwbits{3}{1}{IH} +\robits{4}{6}{reserved} +\rwbits{10}{3}{Port 1 stage} +\rwbits{13}{3}{Port 2 stage} +\end{tikzpicture} +\end{center} +\begin{itemize} +\item \textbf{Stages} Number of stages per point - 1. Normally the number of stages is equal to the number of ports but it can also be less (e.g. if only S11 is measured). +\item \textbf{IH:} Individual halt: Sets the behavior of the "halt sweep" bit (see section~\ref{sweepconfig}). If 1, the sampling is halted before each stage. If 0, the sampling is only halted before the point and all stages are executed without additional halts inbetween. +\item \textbf{Port 1 stage} Number of stage during which the source signal is routed to port 1. Must not have the same value as Port 2 stage. +\item \textbf{Port 2 stage} Number of stage during which the source signal is routed to port 2. Must not have the same value as Port 1 stage. + +\end{itemize} + \subsection{MAX2871 Default Values Registers: 0x08-0x0F} See datasheet of MAX2871 for bit descriptions. Bits for the fields N, FRAC, M, VCO and DIV\_A are "don't care" as they will be overwritten by the SweepConfig setting. \begin{center} @@ -574,12 +596,11 @@ Setting & Selected Power\\ \section{Sampling Result} \label{result} -Each point in the sweep generates two sampling results. The first one contains the measurement when the source was routed to Port 1, the second sampling result was taken when the source was routed to Port 2. +Each point in the sweep generates a sampling results for each stage (see section~\ref{reg:sweepsetup}). \begin{center} \begin{tikzpicture} \bitrect{16}{304-\bit} -\rwbits{0}{1}{SRC} -\robits{1}{2}{reserved} +\rwbits{0}{3}{STAGE[2:0]} \rwbits{3}{13}{POINT\_NUMBER[12:0]} \end{tikzpicture} \begin{tikzpicture} diff --git a/FPGA/VNA/SPIConfig.vhd b/FPGA/VNA/SPIConfig.vhd index 62307c6..8503c4b 100644 --- a/FPGA/VNA/SPIConfig.vhd +++ b/FPGA/VNA/SPIConfig.vhd @@ -50,8 +50,10 @@ entity SPICommands is SWEEP_WRITE : out STD_LOGIC_VECTOR (0 downto 0); SWEEP_POINTS : out STD_LOGIC_VECTOR (12 downto 0); NSAMPLES : out STD_LOGIC_VECTOR (12 downto 0); - EXCITE_PORT1 : out STD_LOGIC; - EXCITE_PORT2 : out STD_LOGIC; + STAGES : out STD_LOGIC_VECTOR (2 downto 0); + INDIVIDUAL_HALT : out STD_LOGIC; + PORT1_STAGE : out STD_LOGIC_VECTOR (2 downto 0); + PORT2_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT1_EN : out STD_LOGIC; PORT2_EN : out STD_LOGIC; REF_EN : out STD_LOGIC; @@ -154,8 +156,6 @@ begin SOURCE_CE_EN <= '0'; LO_CE_EN <= '0'; PORTSWITCH_EN <= '0'; - EXCITE_PORT1 <= '0'; - EXCITE_PORT2 <= '0'; LEDS <= (others => '1'); WINDOW_SETTING <= "00"; unread_sampling_data <= '0'; @@ -243,10 +243,12 @@ begin WINDOW_SETTING <= spi_buf_out(6 downto 5); SOURCE_CE_EN <= spi_buf_out(4); LO_CE_EN <= spi_buf_out(3); - EXCITE_PORT1 <= spi_buf_out(1); - EXCITE_PORT2 <= spi_buf_out(2); when 4 => ADC_PRESCALER <= spi_buf_out(7 downto 0); when 5 => ADC_PHASEINC <= spi_buf_out(11 downto 0); + when 6 => STAGES <= spi_buf_out(15 downto 13); + INDIVIDUAL_HALT <= spi_buf_out(12); + PORT1_STAGE <= spi_buf_out(5 downto 3); + PORT2_STAGE <= spi_buf_out(2 downto 0); when 8 => MAX2871_DEF_0(15 downto 0) <= spi_buf_out; when 9 => MAX2871_DEF_0(31 downto 16) <= spi_buf_out; when 10 => MAX2871_DEF_1(15 downto 0) <= spi_buf_out; diff --git a/FPGA/VNA/Sweep.vhd b/FPGA/VNA/Sweep.vhd index 223b911..f2ff7d0 100644 --- a/FPGA/VNA/Sweep.vhd +++ b/FPGA/VNA/Sweep.vhd @@ -40,7 +40,6 @@ entity Sweep is SAMPLING_BUSY : in STD_LOGIC; SAMPLING_DONE : in STD_LOGIC; START_SAMPLING : out STD_LOGIC; - PORT_SELECT : out STD_LOGIC; BAND_SELECT : out STD_LOGIC; -- fixed part of source/LO registers MAX2871_DEF_4 : in STD_LOGIC_VECTOR (31 downto 0); @@ -67,8 +66,13 @@ entity Sweep is --SETTLING_TIME : in STD_LOGIC_VECTOR (15 downto 0); - EXCITE_PORT1 : in STD_LOGIC; - EXCITE_PORT2 : in STD_LOGIC; + STAGES : in STD_LOGIC_VECTOR (2 downto 0); + INDIVIDUAL_HALT : in STD_LOGIC; + PORT1_STAGE : in STD_LOGIC_VECTOR (2 downto 0); + PORT2_STAGE : in STD_LOGIC_VECTOR (2 downto 0); + + PORT1_ACTIVE : out STD_LOGIC; + PORT2_ACTIVE : out STD_LOGIC; -- Debug signals DEBUG_STATUS : out STD_LOGIC_VECTOR (10 downto 0); @@ -78,10 +82,11 @@ end Sweep; architecture Behavioral of Sweep is signal point_cnt : unsigned(12 downto 0); - type Point_states is (TriggerSetup, SettingUp, SettlingPort1, ExcitingPort1, SettlingPort2, ExcitingPort2, NextPoint, Done); + type Point_states is (TriggerSetup, SettingUp, Settling, Exciting, NextPoint, Done); signal state : Point_states; signal settling_cnt : unsigned(15 downto 0); signal settling_time : unsigned(15 downto 0); + signal stage_cnt : unsigned (2 downto 0); signal config_reg : std_logic_vector(95 downto 0); begin @@ -121,10 +126,8 @@ begin DEBUG_STATUS(10 downto 8) <= "000" when state = TriggerSetup else "001" when state = SettingUp else - "010" when state = SettlingPort1 else - "011" when state = ExcitingPort1 else - "100" when state = SettlingPort2 else - "101" when state = ExcitingPort2 else + "010" when state = Settling else + "011" when state = Exciting else "110" when state = Done else "111"; DEBUG_STATUS(7) <= PLL_RELOAD_DONE; @@ -139,15 +142,19 @@ begin if rising_edge(CLK) then if RESET = '1' then point_cnt <= (others => '0'); + stage_cnt <= (others => '0'); state <= TriggerSetup; START_SAMPLING <= '0'; RELOAD_PLL_REGS <= '0'; SWEEP_HALTED <= '0'; RESULT_INDEX <= (others => '1'); + PORT1_ACTIVE <= '0'; + PORT2_ACTIVE <= '0'; else case state is when TriggerSetup => RELOAD_PLL_REGS <= '1'; + stage_cnt <= (others => '0'); if PLL_RELOAD_DONE = '0' then state <= SettingUp; end if; @@ -166,60 +173,52 @@ begin -- check if halted sweep is resumed if config_reg(95) = '0' or SWEEP_RESUME = '1' then SWEEP_HALTED <= '0'; - if EXCITE_PORT1 = '1' then - state <= SettlingPort1; - else - state <= SettlingPort2; - end if; + state <= Settling; end if; end if; - when SettlingPort1 => - PORT_SELECT <= '1'; + when Settling => + if std_logic_vector(stage_cnt) = PORT1_STAGE then + PORT1_ACTIVE <= '1'; + else + PORT1_ACTIVE <= '0'; + end if; + if std_logic_vector(stage_cnt) = PORT2_STAGE then + PORT2_ACTIVE <= '1'; + else + PORT2_ACTIVE <= '0'; + end if; -- wait for settling time to elapse if settling_cnt > 0 then settling_cnt <= settling_cnt - 1; else START_SAMPLING <= '1'; if SAMPLING_BUSY = '1' then - state <= ExcitingPort1; + state <= Exciting; end if; end if; - when ExcitingPort1 => + when Exciting => -- wait for sampling to finish START_SAMPLING <= '0'; if SAMPLING_BUSY = '0' then - RESULT_INDEX <= "000" & std_logic_vector(point_cnt); - if EXCITE_PORT2 = '1' then - state <= SettlingPort2; + RESULT_INDEX <= std_logic_vector(stage_cnt) & std_logic_vector(point_cnt); + if stage_cnt < unsigned(STAGES) then + stage_cnt <= stage_cnt + 1; + if INDIVIDUAL_HALT = '1' then + -- wait for HALT SWEEP bit again if set + state <= SettingUp; + else + -- no need to halt again, can go directly to settling + state <= Settling; + end if; else state <= NextPoint; end if; settling_cnt <= settling_time; end if; - when SettlingPort2 => - PORT_SELECT <= '0'; - -- wait for settling time to elapse - if settling_cnt > 0 then - settling_cnt <= settling_cnt - 1; - else - START_SAMPLING <= '1'; - if SAMPLING_BUSY = '1' then - state <= ExcitingPort2; - end if; - end if; - when ExcitingPort2 => - -- wait for sampling to finish - START_SAMPLING <= '0'; - RESULT_INDEX <= "100" & std_logic_vector(point_cnt); - if SAMPLING_BUSY = '0' then - state <= NextPoint; - end if; when NextPoint => if point_cnt < unsigned(NPOINTS) then point_cnt <= point_cnt + 1; state <= TriggerSetup; - -- initial port depends on whether port 1 is exited - PORT_SELECT <= EXCITE_PORT1; else point_cnt <= (others => '0'); state <= Done; diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise index 238cc3f..67a2b99 100644 --- a/FPGA/VNA/VNA.gise +++ b/FPGA/VNA/VNA.gise @@ -224,7 +224,7 @@ - + @@ -253,7 +253,7 @@ - + @@ -275,7 +275,7 @@ - + @@ -284,7 +284,7 @@ - + @@ -298,7 +298,7 @@ - + @@ -312,9 +312,8 @@ - + - @@ -366,7 +365,7 @@ - + diff --git a/FPGA/VNA/VNA.xise b/FPGA/VNA/VNA.xise index 7f1807b..23fe1cf 100644 --- a/FPGA/VNA/VNA.xise +++ b/FPGA/VNA/VNA.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/ipcore_dir/DSP_SLICE.xise b/FPGA/VNA/ipcore_dir/DSP_SLICE.xise index b4222ea..92644f3 100644 --- a/FPGA/VNA/ipcore_dir/DSP_SLICE.xise +++ b/FPGA/VNA/ipcore_dir/DSP_SLICE.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/ipcore_dir/PLL.xise b/FPGA/VNA/ipcore_dir/PLL.xise index ceaa4a6..b0b1f74 100644 --- a/FPGA/VNA/ipcore_dir/PLL.xise +++ b/FPGA/VNA/ipcore_dir/PLL.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/ipcore_dir/SinCos.xise b/FPGA/VNA/ipcore_dir/SinCos.xise index 423665e..48631e5 100644 --- a/FPGA/VNA/ipcore_dir/SinCos.xise +++ b/FPGA/VNA/ipcore_dir/SinCos.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/ipcore_dir/SweepConfigMem.xise b/FPGA/VNA/ipcore_dir/SweepConfigMem.xise index e1d6091..eb7b0f0 100644 --- a/FPGA/VNA/ipcore_dir/SweepConfigMem.xise +++ b/FPGA/VNA/ipcore_dir/SweepConfigMem.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/ipcore_dir/result_bram.xise b/FPGA/VNA/ipcore_dir/result_bram.xise index dc81dae..caffa95 100644 --- a/FPGA/VNA/ipcore_dir/result_bram.xise +++ b/FPGA/VNA/ipcore_dir/result_bram.xise @@ -12,7 +12,7 @@ - + diff --git a/FPGA/VNA/top.bin b/FPGA/VNA/top.bin index 313f393..1ed3339 100644 Binary files a/FPGA/VNA/top.bin and b/FPGA/VNA/top.bin differ diff --git a/FPGA/VNA/top.vhd b/FPGA/VNA/top.vhd index ebd5e22..3879038 100644 --- a/FPGA/VNA/top.vhd +++ b/FPGA/VNA/top.vhd @@ -124,7 +124,6 @@ architecture Behavioral of top is PLL_LOCKED : IN std_logic; CONFIG_ADDRESS : OUT std_logic_vector(12 downto 0); START_SAMPLING : OUT std_logic; - PORT_SELECT : OUT std_logic; BAND_SELECT : out STD_LOGIC; SOURCE_REG_4 : OUT std_logic_vector(31 downto 0); SOURCE_REG_3 : OUT std_logic_vector(31 downto 0); @@ -139,8 +138,13 @@ architecture Behavioral of top is SWEEP_RESUME : in STD_LOGIC; ATTENUATOR : OUT std_logic_vector(6 downto 0); SOURCE_FILTER : OUT std_logic_vector(1 downto 0); - EXCITE_PORT1 : in STD_LOGIC; - EXCITE_PORT2 : in STD_LOGIC; + STAGES : in STD_LOGIC_VECTOR (2 downto 0); + INDIVIDUAL_HALT : in STD_LOGIC; + PORT1_STAGE : in STD_LOGIC_VECTOR (2 downto 0); + PORT2_STAGE : in STD_LOGIC_VECTOR (2 downto 0); + + PORT1_ACTIVE : out STD_LOGIC; + PORT2_ACTIVE : out STD_LOGIC; RESULT_INDEX : out STD_LOGIC_VECTOR (15 downto 0); DEBUG_STATUS : out STD_LOGIC_VECTOR (10 downto 0) ); @@ -243,8 +247,10 @@ architecture Behavioral of top is SWEEP_WRITE : OUT std_logic_vector(0 to 0); SWEEP_POINTS : OUT std_logic_vector(12 downto 0); NSAMPLES : OUT std_logic_vector(12 downto 0); - EXCITE_PORT1 : out STD_LOGIC; - EXCITE_PORT2 : out STD_LOGIC; + STAGES : out STD_LOGIC_VECTOR (2 downto 0); + INDIVIDUAL_HALT : out STD_LOGIC; + PORT1_STAGE : out STD_LOGIC_VECTOR (2 downto 0); + PORT2_STAGE : out STD_LOGIC_VECTOR (2 downto 0); PORT1_EN : out STD_LOGIC; PORT2_EN : out STD_LOGIC; REF_EN : out STD_LOGIC; @@ -360,10 +366,14 @@ architecture Behavioral of top is -- Sweep signals signal sweep_points : std_logic_vector(12 downto 0); + signal sweep_stages : STD_LOGIC_VECTOR (2 downto 0); + signal sweep_individual_halt : STD_LOGIC; + signal sweep_port1_stage : STD_LOGIC_VECTOR (2 downto 0); + signal sweep_port2_stage : STD_LOGIC_VECTOR (2 downto 0); signal sweep_config_data : std_logic_vector(95 downto 0); signal sweep_config_address : std_logic_vector(12 downto 0); signal source_filter : std_logic_vector(1 downto 0); - signal sweep_port_select : std_logic; + signal sweep_band : std_logic; signal sweep_config_write_address : std_logic_vector(12 downto 0); signal sweep_config_write_data : std_logic_vector(95 downto 0); @@ -376,8 +386,6 @@ architecture Behavioral of top is signal sweep_excite_port1 : std_logic; signal sweep_excite_port2 : std_logic; - signal sweep_band : std_logic; - -- Configuration signals signal settling_time : std_logic_vector(15 downto 0); signal def_reg_4 : std_logic_vector(31 downto 0); @@ -428,10 +436,10 @@ begin LEDS(2) <= SOURCE_LD; LEDS(3) <= LO1_LD; -- Sweep and active port - PORT_SELECT2 <= not sweep_port_select and portswitch_en; - PORT2_SELECT <= not sweep_port_select and portswitch_en; - PORT_SELECT1 <= sweep_port_select and portswitch_en; - PORT1_SELECT <= sweep_port_select and portswitch_en; + PORT_SELECT2 <= sweep_excite_port2 and portswitch_en; + PORT2_SELECT <= sweep_excite_port2 and portswitch_en; + PORT_SELECT1 <= sweep_excite_port1 and portswitch_en; + PORT1_SELECT <= sweep_excite_port1 and portswitch_en; BAND_SELECT_HIGH <= not sweep_band; BAND_SELECT_LOW <= sweep_band; PORT1_MIX2_EN <= port1mix_en; @@ -440,8 +448,8 @@ begin PORT2_MIX1_EN <= not port2mix_en; REF_MIX2_EN <= refmix_en; REF_MIX1_EN <= not refmix_en; - LEDS(4) <= not (not sweep_reset and not sweep_port_select and portswitch_en); - LEDS(5) <= not (not sweep_reset and sweep_port_select and portswitch_en); + LEDS(4) <= not (not sweep_reset and sweep_excite_port2 and portswitch_en); + LEDS(5) <= not (not sweep_reset and sweep_excite_port1 and portswitch_en); -- Uncommitted LEDs LEDS(7 downto 6) <= user_leds(1 downto 0); --LEDS(7) <= '0'; @@ -649,7 +657,6 @@ begin SAMPLING_BUSY => sampling_busy, SAMPLING_DONE => sampling_done, START_SAMPLING => sampling_start, - PORT_SELECT => sweep_port_select, BAND_SELECT => sweep_band, MAX2871_DEF_4 => def_reg_4, MAX2871_DEF_3 => def_reg_3, @@ -670,8 +677,13 @@ begin SWEEP_RESUME => sweep_resume, ATTENUATOR => ATTENUATION, SOURCE_FILTER => source_filter, - EXCITE_PORT1 => sweep_excite_port1, - EXCITE_PORT2 => sweep_excite_port2, + STAGES => sweep_stages, + INDIVIDUAL_HALT => sweep_individual_halt, + PORT1_STAGE => sweep_port1_stage, + PORT2_STAGE => sweep_port2_stage, + + PORT1_ACTIVE => sweep_excite_port1, + PORT2_ACTIVE => sweep_excite_port2, DEBUG_STATUS => debug, RESULT_INDEX => sampling_result(303 downto 288) ); @@ -740,8 +752,10 @@ begin RESET_MINMAX => adc_reset_minmax, SWEEP_HALTED => sweep_halted, SWEEP_RESUME => sweep_resume, - EXCITE_PORT1 => sweep_excite_port1, - EXCITE_PORT2 => sweep_excite_port2, + STAGES => sweep_stages, + INDIVIDUAL_HALT => sweep_individual_halt, + PORT1_STAGE => sweep_port1_stage, + PORT2_STAGE => sweep_port2_stage, DFT_BIN1_PHASEINC => dft_bin1_phaseinc, DFT_DIFFBIN_PHASEINC => dft_diffbin_phaseinc, DFT_RESULT_READY => dft_ready, diff --git a/Software/PC_Application/Calibration/amplitudecaldialog.cpp b/Software/PC_Application/Calibration/amplitudecaldialog.cpp index adb0b0c..b75e7df 100644 --- a/Software/PC_Application/Calibration/amplitudecaldialog.cpp +++ b/Software/PC_Application/Calibration/amplitudecaldialog.cpp @@ -253,7 +253,7 @@ void AmplitudeCalDialog::RemoveAllPoints() bool AmplitudeCalDialog::AddPoint(AmplitudeCalDialog::CorrectionPoint &p) { - if (points.size() >= Device::Info().limits_maxAmplitudePoints) { + if (points.size() >= Device::Info(dev).limits_maxAmplitudePoints) { // already at limit return false; } @@ -299,8 +299,8 @@ void AmplitudeCalDialog::AddPointDialog() ui->stopFreq->setUnit("Hz"); ui->stopFreq->setPrefixes(" kMG"); ui->frequency->setValue(1000000000.0); - ui->startFreq->setValue(Device::Info().limits_minFreq); - ui->stopFreq->setValue(Device::Info().limits_maxFreq); + ui->startFreq->setValue(Device::Info(dev).limits_minFreq); + ui->stopFreq->setValue(Device::Info(dev).limits_maxFreq); connect(ui->singlePoint, &QRadioButton::toggled, [=](bool single) { ui->stopFreq->setEnabled(!single); ui->startFreq->setEnabled(!single); diff --git a/Software/PC_Application/Device/device.cpp b/Software/PC_Application/Device/device.cpp index f97ff45..a07718d 100644 --- a/Software/PC_Application/Device/device.cpp +++ b/Software/PC_Application/Device/device.cpp @@ -120,16 +120,6 @@ static constexpr Protocol::DeviceInfo defaultInfo = { .FW_minor = 0, .FW_patch = 0, .HW_Revision = '0', - .extRefAvailable = 0, - .extRefInUse = 0, - .FPGA_configured = 0, - .source_locked = 0, - .LO1_locked = 0, - .ADC_overload = 0, - .unlevel = 0, - .temp_source = 0, - .temp_LO1 = 0, - .temp_MCU = 0, .limits_minFreq = 0, .limits_maxFreq = 6000000000, .limits_minIFBW = 10, @@ -143,14 +133,25 @@ static constexpr Protocol::DeviceInfo defaultInfo = { .limits_maxFreqHarmonic = 18000000000, }; -Protocol::DeviceInfo Device::lastInfo = defaultInfo; +static constexpr Protocol::DeviceStatusV1 defaultStatusV1 = { + .extRefAvailable = 0, + .extRefInUse = 0, + .FPGA_configured = 0, + .source_locked = 0, + .LO1_locked = 0, + .ADC_overload = 0, + .unlevel = 0, + .temp_source = 0, + .temp_LO1 = 0, + .temp_MCU = 0, +}; Device::Device(QString serial) { - lastInfo = defaultInfo; + info = defaultInfo; m_handle = nullptr; - lastInfoValid = false; + infoValid = false; libusb_init(&m_context); #if LIBUSB_API_VERSION >= 0x01000106 libusb_set_option(m_context, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO); @@ -261,10 +262,10 @@ bool Device::Configure(Protocol::SpectrumAnalyzerSettings settings) return SendPacket(p); } -bool Device::SetManual(Protocol::ManualControl manual) +bool Device::SetManual(Protocol::ManualControlV1 manual) { Protocol::PacketInfo p; - p.type = Protocol::PacketType::ManualControl; + p.type = Protocol::PacketType::ManualControlV1; p.manual = manual; return SendPacket(p); } @@ -393,25 +394,48 @@ void Device::SearchDevices(std::function const Protocol::DeviceInfo &Device::Info() { - return lastInfo; + return info; +} + +const Protocol::DeviceInfo &Device::Info(Device *dev) +{ + if(dev) { + return dev->Info(); + } else { + return defaultInfo; + } +} + +Protocol::DeviceStatusV1 &Device::StatusV1() +{ + return status.v1; +} + +const Protocol::DeviceStatusV1 &Device::StatusV1(Device *dev) +{ + if(dev) { + return dev->StatusV1(); + } else { + return defaultStatusV1; + } } QString Device::getLastDeviceInfoString() { QString ret; - if(!lastInfoValid) { + if(!infoValid) { ret.append("No device information available yet"); } else { ret.append("HW Rev."); - ret.append(lastInfo.HW_Revision); - ret.append(" FW "+QString::number(lastInfo.FW_major)+"."+QString::number(lastInfo.FW_minor)+"."+QString::number(lastInfo.FW_patch)); - ret.append(" Temps: "+QString::number(lastInfo.temp_source)+"°C/"+QString::number(lastInfo.temp_LO1)+"°C/"+QString::number(lastInfo.temp_MCU)+"°C"); + ret.append(info.HW_Revision); + ret.append(" FW "+QString::number(info.FW_major)+"."+QString::number(info.FW_minor)+"."+QString::number(info.FW_patch)); + ret.append(" Temps: "+QString::number(status.v1.temp_source)+"°C/"+QString::number(status.v1.temp_LO1)+"°C/"+QString::number(status.v1.temp_MCU)+"°C"); ret.append(" Reference:"); - if(lastInfo.extRefInUse) { + if(status.v1.extRefInUse) { ret.append("External"); } else { ret.append("Internal"); - if(lastInfo.extRefAvailable) { + if(status.v1.extRefAvailable) { ret.append(" (External available)"); } } @@ -431,8 +455,8 @@ void Device::ReceivedData() case Protocol::PacketType::Datapoint: emit DatapointReceived(packet.datapoint); break; - case Protocol::PacketType::Status: - emit ManualStatusReceived(packet.status); + case Protocol::PacketType::ManualStatusV1: + emit ManualStatusReceived(packet.manualStatusV1); break; case Protocol::PacketType::SpectrumAnalyzerResult: emit SpectrumResultReceived(packet.spectrumResult); @@ -443,15 +467,19 @@ void Device::ReceivedData() break; case Protocol::PacketType::DeviceInfo: if(packet.info.ProtocolVersion != Protocol::Version) { - if(!lastInfoValid) { + if(!infoValid) { emit NeedsFirmwareUpdate(packet.info.ProtocolVersion, Protocol::Version); } } else { - lastInfo = packet.info; + info = packet.info; } - lastInfoValid = true; + infoValid = true; emit DeviceInfoUpdated(); break; + case Protocol::PacketType::DeviceStatusV1: + status.v1 = packet.statusV1; + emit DeviceStatusUpdated(); + break; case Protocol::PacketType::Ack: emit AckReceived(); emit receivedAnswer(TransmissionResult::Ack); diff --git a/Software/PC_Application/Device/device.h b/Software/PC_Application/Device/device.h index ef144bd..38b81fb 100644 --- a/Software/PC_Application/Device/device.h +++ b/Software/PC_Application/Device/device.h @@ -13,7 +13,7 @@ #include Q_DECLARE_METATYPE(Protocol::Datapoint); -Q_DECLARE_METATYPE(Protocol::ManualStatus); +Q_DECLARE_METATYPE(Protocol::ManualStatusV1); Q_DECLARE_METATYPE(Protocol::SpectrumAnalyzerResult); Q_DECLARE_METATYPE(Protocol::AmplitudeCorrectionPoint); @@ -61,23 +61,27 @@ public: bool SendPacket(const Protocol::PacketInfo& packet, std::function cb = nullptr, unsigned int timeout = 500); bool Configure(Protocol::SweepSettings settings, std::function cb = nullptr); bool Configure(Protocol::SpectrumAnalyzerSettings settings); - bool SetManual(Protocol::ManualControl manual); + bool SetManual(Protocol::ManualControlV1 manual); bool SetIdle(); bool SendFirmwareChunk(Protocol::FirmwarePacket &fw); bool SendCommandWithoutPayload(Protocol::PacketType type); QString serial() const; - static const Protocol::DeviceInfo& Info(); + const Protocol::DeviceInfo& Info(); + static const Protocol::DeviceInfo& Info(Device *dev); + Protocol::DeviceStatusV1& StatusV1(); + static const Protocol::DeviceStatusV1& StatusV1(Device *dev); QString getLastDeviceInfoString(); // Returns serial numbers of all connected devices static std::set GetDevices(); signals: void DatapointReceived(Protocol::Datapoint); - void ManualStatusReceived(Protocol::ManualStatus); + void ManualStatusReceived(Protocol::ManualStatusV1); void SpectrumResultReceived(Protocol::SpectrumAnalyzerResult); void AmplitudeCorrectionPointReceived(Protocol::AmplitudeCorrectionPoint); void FrequencyCorrectionReceived(float ppm); void DeviceInfoUpdated(); + void DeviceStatusUpdated(); void ConnectionLost(); void AckReceived(); void NackReceived(); @@ -122,8 +126,11 @@ private: QString m_serial; bool m_connected; std::thread *m_receiveThread; - static Protocol::DeviceInfo lastInfo; - bool lastInfoValid; + Protocol::DeviceInfo info; + bool infoValid; + union { + Protocol::DeviceStatusV1 v1; + } status; }; #endif // DEVICE_H diff --git a/Software/PC_Application/Device/manualcontroldialog.cpp b/Software/PC_Application/Device/manualcontroldialog.cpp index b776887..02d8e35 100644 --- a/Software/PC_Application/Device/manualcontroldialog.cpp +++ b/Software/PC_Application/Device/manualcontroldialog.cpp @@ -580,7 +580,7 @@ double ManualControlDialog::getRefPhase() return ui->refphase->text().toDouble(); } -void ManualControlDialog::NewStatus(Protocol::ManualStatus status) +void ManualControlDialog::NewStatus(Protocol::ManualStatusV1 status) { // ADC values ui->port1min->setText(QString::number(status.port1min)); @@ -616,7 +616,7 @@ void ManualControlDialog::NewStatus(Protocol::ManualStatus status) void ManualControlDialog::UpdateDevice() { - Protocol::ManualControl m; + Protocol::ManualControlV1 m; // Source highband m.SourceHighCE = ui->SourceCE->isChecked(); m.SourceHighRFEN = ui->SourceRFEN->isChecked(); diff --git a/Software/PC_Application/Device/manualcontroldialog.h b/Software/PC_Application/Device/manualcontroldialog.h index ab0f048..553d549 100644 --- a/Software/PC_Application/Device/manualcontroldialog.h +++ b/Software/PC_Application/Device/manualcontroldialog.h @@ -103,7 +103,7 @@ public: double getRefPhase(); public slots: - void NewStatus(Protocol::ManualStatus status); + void NewStatus(Protocol::ManualStatusV1 status); private: void UpdateDevice(); diff --git a/Software/PC_Application/Generator/generator.cpp b/Software/PC_Application/Generator/generator.cpp index f7cc411..0d31495 100644 --- a/Software/PC_Application/Generator/generator.cpp +++ b/Software/PC_Application/Generator/generator.cpp @@ -2,11 +2,10 @@ #include -Generator::Generator(AppWindow *window) - : Mode(window, "Signal Generator") - , SCPINode("GENerator") +Generator::Generator(AppWindow *window, QString name) + : Mode(window, name, "GENerator") { - central = new SignalgeneratorWidget(window); + central = new SignalgeneratorWidget(window->getDevice(), window); auto pref = Preferences::getInstance(); diff --git a/Software/PC_Application/Generator/generator.h b/Software/PC_Application/Generator/generator.h index 0897bf4..41f4bb5 100644 --- a/Software/PC_Application/Generator/generator.h +++ b/Software/PC_Application/Generator/generator.h @@ -5,13 +5,15 @@ #include "signalgenwidget.h" #include "scpi.h" -class Generator : public Mode, public SCPINode +class Generator : public Mode { public: - Generator(AppWindow *window); + Generator(AppWindow *window, QString name = "Signal Generator"); void deactivate() override; void initializeDevice() override; + virtual Type getType() override { return Type::SG;} + // Nothing to do for now virtual nlohmann::json toJSON() override; virtual void fromJSON(nlohmann::json j) override; diff --git a/Software/PC_Application/Generator/signalgenwidget.cpp b/Software/PC_Application/Generator/signalgenwidget.cpp index 187debb..3347804 100644 --- a/Software/PC_Application/Generator/signalgenwidget.cpp +++ b/Software/PC_Application/Generator/signalgenwidget.cpp @@ -2,9 +2,10 @@ #include "ui_signalgenwidget.h" -SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) : +SignalgeneratorWidget::SignalgeneratorWidget(Device *&dev, QWidget *parent) : QWidget(parent), - ui(new Ui::SignalgeneratorWidget) + ui(new Ui::SignalgeneratorWidget), + dev(dev) { ui->setupUi(this); ui->frequency->setUnit("Hz"); @@ -31,16 +32,16 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) : ui->steps->setPrecision(0); connect(ui->frequency, &SIUnitEdit::valueChanged, [=](double newval) { - if(newval < Device::Info().limits_minFreq) { - newval = Device::Info().limits_minFreq; - } else if (newval > Device::Info().limits_maxFreq) { - newval = Device::Info().limits_maxFreq; + if(newval < Device::Info(dev).limits_minFreq) { + newval = Device::Info(dev).limits_minFreq; + } else if (newval > Device::Info(dev).limits_maxFreq) { + newval = Device::Info(dev).limits_maxFreq; } ui->frequency->setValueQuiet(newval); if (newval < ui->span->value()/2) ui->span->setValueQuiet(newval/2); - if (newval + ui->span->value()/2 > Device::Info().limits_maxFreq) - ui->span->setValueQuiet((Device::Info().limits_maxFreq - newval)*2); + if (newval + ui->span->value()/2 > Device::Info(dev).limits_maxFreq) + ui->span->setValueQuiet((Device::Info(dev).limits_maxFreq - newval)*2); newval = ui->frequency->value() - ui->span->value()/2; ui->current->setValueQuiet(newval); emit SettingsChanged(); @@ -49,8 +50,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) : connect(ui->span, &SIUnitEdit::valueChanged, [=](double newval) { if(newval < 0 ) { newval = 0; - } else if (newval > Device::Info().limits_maxFreq - Device::Info().limits_minFreq) { - newval = Device::Info().limits_maxFreq - Device::Info().limits_minFreq; + } else if (newval > Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq) { + newval = Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq; } ui->span->setValueQuiet(newval); @@ -59,8 +60,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) : ui->frequency->setValueQuiet(ui->span->value()/2); } newF = ui->frequency->value() + ui->span->value()/2; - if (newF > Device::Info().limits_maxFreq) { - ui->frequency->setValueQuiet(Device::Info().limits_maxFreq - ui->span->value()/2); + if (newF > Device::Info(dev).limits_maxFreq) { + ui->frequency->setValueQuiet(Device::Info(dev).limits_maxFreq - ui->span->value()/2); } newval = ui->frequency->value() - ui->span->value()/2; @@ -71,8 +72,8 @@ SignalgeneratorWidget::SignalgeneratorWidget(QWidget *parent) : connect(ui->current, &SIUnitEdit::valueChanged, [=](double newval) { if(newval < 0 ) { newval = 0; - } else if (newval > Device::Info().limits_maxFreq - Device::Info().limits_minFreq) { - newval = Device::Info().limits_maxFreq - Device::Info().limits_minFreq; + } else if (newval > Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq) { + newval = Device::Info(dev).limits_maxFreq - Device::Info(dev).limits_minFreq; } ui->current->setValueQuiet(newval); emit SettingsChanged(); diff --git a/Software/PC_Application/Generator/signalgenwidget.h b/Software/PC_Application/Generator/signalgenwidget.h index e64a054..6573140 100644 --- a/Software/PC_Application/Generator/signalgenwidget.h +++ b/Software/PC_Application/Generator/signalgenwidget.h @@ -15,7 +15,7 @@ class SignalgeneratorWidget : public QWidget, public Savable Q_OBJECT public: - explicit SignalgeneratorWidget(QWidget *parent = nullptr); + explicit SignalgeneratorWidget(Device*&dev, QWidget *parent = nullptr); ~SignalgeneratorWidget(); Protocol::GeneratorSettings getDeviceStatus(); @@ -36,6 +36,7 @@ protected: private: Ui::SignalgeneratorWidget *ui; int m_timerId; + Device*&dev; }; #endif // SIGNALGENERATOR_H diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp index a649dcc..74d5f25 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.cpp @@ -45,9 +45,8 @@ #include #include -SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window) - : Mode(window, "Spectrum Analyzer"), - SCPINode("SA"), +SpectrumAnalyzer::SpectrumAnalyzer(AppWindow *window, QString name) + : Mode(window, name, "SA"), central(new TileWidget(traceModel, window)) { averages = 1; @@ -422,6 +421,10 @@ using namespace std; void SpectrumAnalyzer::NewDatapoint(Protocol::SpectrumAnalyzerResult d) { + if(Mode::getActiveMode() != this) { + return; + } + if(d.pointNum >= settings.pointNum) { qWarning() << "Ignoring point with too large point number (" << d.pointNum << ")"; return; @@ -555,14 +558,14 @@ void SpectrumAnalyzer::SetStopFreq(double freq) void SpectrumAnalyzer::SetCenterFreq(double freq) { auto old_span = settings.f_stop - settings.f_start; - if (freq - old_span / 2 <= Device::Info().limits_minFreq) { + if (freq - old_span / 2 <= Device::Info(window->getDevice()).limits_minFreq) { // would shift start frequency below minimum settings.f_start = 0; settings.f_stop = 2 * freq; - } else if(freq + old_span / 2 >= Device::Info().limits_maxFreq) { + } else if(freq + old_span / 2 >= Device::Info(window->getDevice()).limits_maxFreq) { // would shift stop frequency above maximum - settings.f_start = 2 * freq - Device::Info().limits_maxFreq; - settings.f_stop = Device::Info().limits_maxFreq; + settings.f_start = 2 * freq - Device::Info(window->getDevice()).limits_maxFreq; + settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq; } else { settings.f_start = freq - old_span / 2; settings.f_stop = freq + old_span / 2; @@ -573,14 +576,14 @@ void SpectrumAnalyzer::SetCenterFreq(double freq) void SpectrumAnalyzer::SetSpan(double span) { auto old_center = (settings.f_start + settings.f_stop) / 2; - if(old_center < Device::Info().limits_minFreq + span / 2) { + if(old_center < Device::Info(window->getDevice()).limits_minFreq + span / 2) { // would shift start frequency below minimum - settings.f_start = Device::Info().limits_minFreq; - settings.f_stop = Device::Info().limits_minFreq + span; - } else if(old_center > Device::Info().limits_maxFreq - span / 2) { + settings.f_start = Device::Info(window->getDevice()).limits_minFreq; + settings.f_stop = Device::Info(window->getDevice()).limits_minFreq + span; + } else if(old_center > Device::Info(window->getDevice()).limits_maxFreq - span / 2) { // would shift stop frequency above maximum - settings.f_start = Device::Info().limits_maxFreq - span; - settings.f_stop = Device::Info().limits_maxFreq; + settings.f_start = Device::Info(window->getDevice()).limits_maxFreq - span; + settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq; } else { settings.f_start = old_center - span / 2; settings.f_stop = settings.f_start + span; @@ -590,8 +593,8 @@ void SpectrumAnalyzer::SetSpan(double span) void SpectrumAnalyzer::SetFullSpan() { - settings.f_start = Device::Info().limits_minFreq; - settings.f_stop = Device::Info().limits_maxFreq; + settings.f_start = Device::Info(window->getDevice()).limits_minFreq; + settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq; ConstrainAndUpdateFrequencies(); } @@ -619,10 +622,10 @@ void SpectrumAnalyzer::SpanZoomOut() void SpectrumAnalyzer::SetRBW(double bandwidth) { - if(bandwidth > Device::Info().limits_maxRBW) { - bandwidth = Device::Info().limits_maxRBW; - } else if(bandwidth < Device::Info().limits_minRBW) { - bandwidth = Device::Info().limits_minRBW; + if(bandwidth > Device::Info(window->getDevice()).limits_maxRBW) { + bandwidth = Device::Info(window->getDevice()).limits_maxRBW; + } else if(bandwidth < Device::Info(window->getDevice()).limits_minRBW) { + bandwidth = Device::Info(window->getDevice()).limits_minRBW; } settings.RBW = bandwidth; emit RBWChanged(settings.RBW); @@ -690,10 +693,10 @@ void SpectrumAnalyzer::SetTGPort(int port) void SpectrumAnalyzer::SetTGLevel(double level) { - if(level > Device::Info().limits_cdbm_max / 100.0) { - level = Device::Info().limits_cdbm_max / 100.0; - } else if(level < Device::Info().limits_cdbm_min / 100.0) { - level = Device::Info().limits_cdbm_min / 100.0; + if(level > Device::Info(window->getDevice()).limits_cdbm_max / 100.0) { + level = Device::Info(window->getDevice()).limits_cdbm_max / 100.0; + } else if(level < Device::Info(window->getDevice()).limits_cdbm_min / 100.0) { + level = Device::Info(window->getDevice()).limits_cdbm_min / 100.0; } emit TGLevelChanged(level); settings.trackingPower = level * 100; @@ -1015,24 +1018,24 @@ void SpectrumAnalyzer::UpdateAverageCount() void SpectrumAnalyzer::ConstrainAndUpdateFrequencies() { - if(settings.f_stop > Device::Info().limits_maxFreq) { - settings.f_stop = Device::Info().limits_maxFreq; + if(settings.f_stop > Device::Info(window->getDevice()).limits_maxFreq) { + settings.f_stop = Device::Info(window->getDevice()).limits_maxFreq; } if(settings.f_start > settings.f_stop) { settings.f_start = settings.f_stop; } - if(settings.f_start < Device::Info().limits_minFreq) { - settings.f_start = Device::Info().limits_minFreq; + if(settings.f_start < Device::Info(window->getDevice()).limits_minFreq) { + settings.f_start = Device::Info(window->getDevice()).limits_minFreq; } bool trackingOffset_limited = false; - if(settings.f_stop + settings.trackingGeneratorOffset > Device::Info().limits_maxFreq) { + if(settings.f_stop + settings.trackingGeneratorOffset > Device::Info(window->getDevice()).limits_maxFreq) { trackingOffset_limited = true; - settings.trackingGeneratorOffset = Device::Info().limits_maxFreq - settings.f_stop; + settings.trackingGeneratorOffset = Device::Info(window->getDevice()).limits_maxFreq - settings.f_stop; } - if((long) settings.f_start + settings.trackingGeneratorOffset < (long) Device::Info().limits_minFreq) { + if((long) settings.f_start + settings.trackingGeneratorOffset < (long) Device::Info(window->getDevice()).limits_minFreq) { trackingOffset_limited = true; - settings.trackingGeneratorOffset = Device::Info().limits_minFreq - settings.f_start; + settings.trackingGeneratorOffset = Device::Info(window->getDevice()).limits_minFreq - settings.f_start; } if(trackingOffset_limited) { InformationBox::ShowMessage("Warning", "The selected tracking generator offset is not reachable for all frequencies with the current span. " diff --git a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h index 46f033a..95a590f 100644 --- a/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h +++ b/Software/PC_Application/SpectrumAnalyzer/spectrumanalyzer.h @@ -12,15 +12,17 @@ #include #include -class SpectrumAnalyzer : public Mode, public SCPINode +class SpectrumAnalyzer : public Mode { Q_OBJECT public: - SpectrumAnalyzer(AppWindow *window); + SpectrumAnalyzer(AppWindow *window, QString name = "Spectrum Analyzer"); void deactivate() override; void initializeDevice() override; + virtual Type getType() override { return Type::SA;} + // Only save/load user changeable stuff, no need to save the widgets/mode name etc. virtual nlohmann::json toJSON() override; virtual void fromJSON(nlohmann::json j) override; diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 955ad53..ce8d311 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -51,9 +51,8 @@ #include #include -VNA::VNA(AppWindow *window) - : Mode(window, "Vector Network Analyzer"), - SCPINode("VNA"), +VNA::VNA(AppWindow *window, QString name) + : Mode(window, name, "VNA"), deembedding(traceModel), deembedding_active(false), central(new TileWidget(traceModel)) @@ -790,6 +789,11 @@ using namespace std; void VNA::NewDatapoint(Protocol::Datapoint d) { + if(Mode::getActiveMode() != this) { + // ignore + return; + } + if(changingSettings) { // already setting new sweep settings, ignore incoming points from old settings return; @@ -997,14 +1001,14 @@ void VNA::SetStopFreq(double freq) void VNA::SetCenterFreq(double freq) { auto old_span = settings.Freq.stop - settings.Freq.start; - if (freq - old_span / 2 <= Device::Info().limits_minFreq) { + if (freq - old_span / 2 <= Device::Info(window->getDevice()).limits_minFreq) { // would shift start frequency below minimum settings.Freq.start = 0; settings.Freq.stop = 2 * freq; - } else if(freq + old_span / 2 >= Device::Info().limits_maxFreq) { + } else if(freq + old_span / 2 >= Device::Info(window->getDevice()).limits_maxFreq) { // would shift stop frequency above maximum - settings.Freq.start = 2 * freq - Device::Info().limits_maxFreq; - settings.Freq.stop = Device::Info().limits_maxFreq; + settings.Freq.start = 2 * freq - Device::Info(window->getDevice()).limits_maxFreq; + settings.Freq.stop = Device::Info(window->getDevice()).limits_maxFreq; } else { settings.Freq.start = freq - old_span / 2; settings.Freq.stop = freq + old_span / 2; @@ -1014,12 +1018,12 @@ void VNA::SetCenterFreq(double freq) void VNA::SetSpan(double span) { - auto maxFreq = Preferences::getInstance().Acquisition.harmonicMixing ? Device::Info().limits_maxFreqHarmonic : Device::Info().limits_maxFreq; + auto maxFreq = Preferences::getInstance().Acquisition.harmonicMixing ? Device::Info(window->getDevice()).limits_maxFreqHarmonic : Device::Info(window->getDevice()).limits_maxFreq; auto old_center = (settings.Freq.start + settings.Freq.stop) / 2; - if(old_center < Device::Info().limits_minFreq + span / 2) { + if(old_center < Device::Info(window->getDevice()).limits_minFreq + span / 2) { // would shift start frequency below minimum - settings.Freq.start = Device::Info().limits_minFreq; - settings.Freq.stop = Device::Info().limits_minFreq + span; + settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq; + settings.Freq.stop = Device::Info(window->getDevice()).limits_minFreq + span; } else if(old_center > maxFreq - span / 2) { // would shift stop frequency above maximum settings.Freq.start = maxFreq - span; @@ -1033,8 +1037,8 @@ void VNA::SetSpan(double span) void VNA::SetFullSpan() { - settings.Freq.start = Device::Info().limits_minFreq; - settings.Freq.stop = Device::Info().limits_maxFreq; + settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq; + settings.Freq.stop = Device::Info(window->getDevice()).limits_maxFreq; ConstrainAndUpdateFrequencies(); } @@ -1072,10 +1076,10 @@ void VNA::SetLogSweep(bool log) void VNA::SetSourceLevel(double level) { - if(level > Device::Info().limits_cdbm_max / 100.0) { - level = Device::Info().limits_cdbm_max / 100.0; - } else if(level < Device::Info().limits_cdbm_min / 100.0) { - level = Device::Info().limits_cdbm_min / 100.0; + if(level > Device::Info(window->getDevice()).limits_cdbm_max / 100.0) { + level = Device::Info(window->getDevice()).limits_cdbm_max / 100.0; + } else if(level < Device::Info(window->getDevice()).limits_cdbm_min / 100.0) { + level = Device::Info(window->getDevice()).limits_cdbm_min / 100.0; } emit sourceLevelChanged(level); settings.Freq.excitation_power = level; @@ -1105,15 +1109,15 @@ void VNA::SetPowerSweepFrequency(double freq) void VNA::SetPoints(unsigned int points) { - unsigned int maxPoints = Preferences::getInstance().Acquisition.allowSegmentedSweep ? UINT16_MAX : Device::Info().limits_maxPoints; + unsigned int maxPoints = Preferences::getInstance().Acquisition.allowSegmentedSweep ? UINT16_MAX : Device::Info(window->getDevice()).limits_maxPoints; if(points > maxPoints) { points = maxPoints; } else if (points < 2) { points = 2; } - if (points > Device::Info().limits_maxPoints) { + if (points > Device::Info(window->getDevice()).limits_maxPoints) { // needs segmented sweep - settings.segments = ceil((double) points / Device::Info().limits_maxPoints); + settings.segments = ceil((double) points / Device::Info(window->getDevice()).limits_maxPoints); settings.activeSegment = 0; } else { // can fit all points into one segment @@ -1127,10 +1131,10 @@ void VNA::SetPoints(unsigned int points) void VNA::SetIFBandwidth(double bandwidth) { - if(bandwidth > Device::Info().limits_maxIFBW) { - bandwidth = Device::Info().limits_maxIFBW; - } else if(bandwidth < Device::Info().limits_minIFBW) { - bandwidth = Device::Info().limits_minIFBW; + if(bandwidth > Device::Info(window->getDevice()).limits_maxIFBW) { + bandwidth = Device::Info(window->getDevice()).limits_maxIFBW; + } else if(bandwidth < Device::Info(window->getDevice()).limits_minIFBW) { + bandwidth = Device::Info(window->getDevice()).limits_minIFBW; } settings.bandwidth = bandwidth; emit IFBandwidthChanged(settings.bandwidth); @@ -1478,9 +1482,9 @@ void VNA::ConstrainAndUpdateFrequencies() auto pref = Preferences::getInstance(); double maxFreq; if(pref.Acquisition.harmonicMixing) { - maxFreq = Device::Info().limits_maxFreqHarmonic; + maxFreq = Device::Info(window->getDevice()).limits_maxFreqHarmonic; } else { - maxFreq = Device::Info().limits_maxFreq; + maxFreq = Device::Info(window->getDevice()).limits_maxFreq; } if(settings.Freq.stop > maxFreq) { settings.Freq.stop = maxFreq; @@ -1488,8 +1492,8 @@ void VNA::ConstrainAndUpdateFrequencies() if(settings.Freq.start > settings.Freq.stop) { settings.Freq.start = settings.Freq.stop; } - if(settings.Freq.start < Device::Info().limits_minFreq) { - settings.Freq.start = Device::Info().limits_minFreq; + if(settings.Freq.start < Device::Info(window->getDevice()).limits_minFreq) { + settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq; } emit startFreqChanged(settings.Freq.start); emit stopFreqChanged(settings.Freq.stop); diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index 9371b45..fb960b0 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -13,17 +13,19 @@ #include #include -class VNA : public Mode, public SCPINode +class VNA : public Mode { Q_OBJECT public: - VNA(AppWindow *window); + VNA(AppWindow *window, QString name = "Vector Network Analyzer"); void deactivate() override; void initializeDevice() override; void deviceDisconnected() override; void shutdown() override; + virtual Type getType() override { return Type::VNA;} + // Only save/load user changeable stuff, no need to save the widgets/mode name etc. virtual nlohmann::json toJSON() override; virtual void fromJSON(nlohmann::json j) override; diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index 6da7c3c..ad6adf5 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -134,9 +134,9 @@ AppWindow::AppWindow(QWidget *parent) // Create GUI modes central = new QStackedWidget; setCentralWidget(central); - vna = new VNA(this); - generator = new Generator(this); - spectrumAnalyzer = new SpectrumAnalyzer(this); + auto vna = new VNA(this); + auto generator = new Generator(this); + auto spectrumAnalyzer = new SpectrumAnalyzer(this); // UI connections connect(ui->actionUpdate_Device_List, &QAction::triggered, this, &AppWindow::UpdateDeviceList); @@ -235,7 +235,7 @@ AppWindow::AppWindow(QWidget *parent) vna->activate(); qRegisterMetaType("Datapoint"); - qRegisterMetaType("Manual"); + qRegisterMetaType("ManualV1"); qRegisterMetaType("SpectrumAnalyzerResult"); qRegisterMetaType("AmplitudeCorrection"); @@ -278,9 +278,9 @@ void AppWindow::closeEvent(QCloseEvent *event) if(pref.Startup.UseSetupFile && pref.Startup.AutosaveSetupFile) { SaveSetup(pref.Startup.SetupFile); } - vna->shutdown(); - generator->shutdown(); - spectrumAnalyzer->shutdown(); + for(auto m : Mode::getModes()) { + m->shutdown(); + } delete device; QSettings settings; settings.setValue("geometry", saveGeometry()); @@ -309,7 +309,7 @@ bool AppWindow::ConnectToDevice(QString serial) UpdateStatusBar(AppWindow::DeviceStatusBar::Connected); connect(device, &Device::LogLineReceived, &deviceLog, &DeviceLog::addLine); connect(device, &Device::ConnectionLost, this, &AppWindow::DeviceConnectionLost); - connect(device, &Device::DeviceInfoUpdated, this, &AppWindow::DeviceInfoUpdated); + connect(device, &Device::DeviceStatusUpdated, this, &AppWindow::DeviceStatusUpdated); connect(device, &Device::NeedsFirmwareUpdate, this, &AppWindow::DeviceNeedsUpdate); ui->actionDisconnect->setEnabled(true); ui->actionManual_Control->setEnabled(true); @@ -465,7 +465,7 @@ void AppWindow::SetupSCPI() } return ""; }, [=](QStringList) -> QString { - switch(Device::Info().extRefInUse) { + switch(Device::StatusV1(getDevice()).extRefInUse) { case 0: return "INT"; case 1: return "EXT"; default: return "ERROR"; @@ -475,88 +475,89 @@ void AppWindow::SetupSCPI() if (params.size() != 1) { return "ERROR"; } + Mode *mode = nullptr; if (params[0] == "VNA") { - vna->activate(); + mode = Mode::findFirstOfType(Mode::Type::VNA); } else if(params[0] == "GEN") { - generator->activate(); + mode = Mode::findFirstOfType(Mode::Type::SG); } else if(params[0] == "SA") { - spectrumAnalyzer->activate(); + mode = Mode::findFirstOfType(Mode::Type::SA); } else { return "INVALID MDOE"; } - return ""; - }, [=](QStringList) -> QString { - auto active = Mode::getActiveMode(); - if(active == vna) { - return "VNA"; - } else if(active == generator) { - return "GEN"; - } else if(active == spectrumAnalyzer) { - return "SA"; + if(mode) { + mode->activate(); + return ""; } else { return "ERROR"; } + }, [=](QStringList) -> QString { + auto active = Mode::getActiveMode(); + if(active) { + switch(active->getType()) { + case Mode::Type::VNA: return "VNA"; + case Mode::Type::SG: return "SG"; + case Mode::Type::SA: return "SA"; + } + } + return "ERROR"; })); auto scpi_status = new SCPINode("STAtus"); scpi_dev->add(scpi_status); scpi_status->add(new SCPICommand("UNLOcked", nullptr, [=](QStringList){ - bool locked = Device::Info().source_locked && Device::Info().LO1_locked; + bool locked = Device::StatusV1(getDevice()).source_locked && Device::StatusV1(getDevice()).LO1_locked; return locked ? "FALSE" : "TRUE"; })); scpi_status->add(new SCPICommand("ADCOVERload", nullptr, [=](QStringList){ - return Device::Info().ADC_overload ? "TRUE" : "FALSE"; + return Device::StatusV1(getDevice()).ADC_overload ? "TRUE" : "FALSE"; })); scpi_status->add(new SCPICommand("UNLEVel", nullptr, [=](QStringList){ - return Device::Info().unlevel ? "TRUE" : "FALSE"; + return Device::StatusV1(getDevice()).unlevel ? "TRUE" : "FALSE"; })); auto scpi_info = new SCPINode("INFo"); scpi_dev->add(scpi_info); scpi_info->add(new SCPICommand("FWREVision", nullptr, [=](QStringList){ - return QString::number(Device::Info().FW_major)+"."+QString::number(Device::Info().FW_minor)+"."+QString::number(Device::Info().FW_patch); + return QString::number(Device::Info(getDevice()).FW_major)+"."+QString::number(Device::Info(getDevice()).FW_minor)+"."+QString::number(Device::Info(getDevice()).FW_patch); })); scpi_info->add(new SCPICommand("HWREVision", nullptr, [=](QStringList){ - return QString(Device::Info().HW_Revision); + return QString(Device::Info(getDevice()).HW_Revision); })); scpi_info->add(new SCPICommand("TEMPeratures", nullptr, [=](QStringList){ - return QString::number(Device::Info().temp_source)+"/"+QString::number(Device::Info().temp_LO1)+"/"+QString::number(Device::Info().temp_MCU); + return QString::number(Device::StatusV1(getDevice()).temp_source)+"/"+QString::number(Device::StatusV1(getDevice()).temp_LO1)+"/"+QString::number(Device::StatusV1(getDevice()).temp_MCU); })); auto scpi_limits = new SCPINode("LIMits"); scpi_info->add(scpi_limits); scpi_limits->add(new SCPICommand("MINFrequency", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_minFreq); + return QString::number(Device::Info(getDevice()).limits_minFreq); })); scpi_limits->add(new SCPICommand("MAXFrequency", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_maxFreq); + return QString::number(Device::Info(getDevice()).limits_maxFreq); })); scpi_limits->add(new SCPICommand("MINIFBW", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_minIFBW); + return QString::number(Device::Info(getDevice()).limits_minIFBW); })); scpi_limits->add(new SCPICommand("MAXIFBW", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_maxIFBW); + return QString::number(Device::Info(getDevice()).limits_maxIFBW); })); scpi_limits->add(new SCPICommand("MAXPoints", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_maxPoints); + return QString::number(Device::Info(getDevice()).limits_maxPoints); })); scpi_limits->add(new SCPICommand("MINPOWer", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_cdbm_min / 100.0); + return QString::number(Device::Info(getDevice()).limits_cdbm_min / 100.0); })); scpi_limits->add(new SCPICommand("MAXPOWer", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_cdbm_max / 100.0); + return QString::number(Device::Info(getDevice()).limits_cdbm_max / 100.0); })); scpi_limits->add(new SCPICommand("MINRBW", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_minRBW); + return QString::number(Device::Info(getDevice()).limits_minRBW); })); scpi_limits->add(new SCPICommand("MAXRBW", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_maxRBW); + return QString::number(Device::Info(getDevice()).limits_maxRBW); })); scpi_limits->add(new SCPICommand("MAXHARMonicfrequency", nullptr, [=](QStringList){ - return QString::number(Device::Info().limits_maxFreqHarmonic); + return QString::number(Device::Info(getDevice()).limits_maxFreqHarmonic); })); - scpi.add(vna); - scpi.add(generator); - scpi.add(spectrumAnalyzer); - auto scpi_manual = new SCPINode("MANual"); scpi_manual->add(new SCPICommand("STArt",[=](QStringList) -> QString { StartManualControl(); @@ -798,6 +799,11 @@ void AppWindow::StopTCPServer() server = nullptr; } +SCPI* AppWindow::getSCPI() +{ + return &scpi; +} + int AppWindow::UpdateDeviceList() { deviceActionGroup->setExclusive(true); @@ -918,7 +924,7 @@ void AppWindow::DeviceNeedsUpdate(int reported, int expected) } } -void AppWindow::DeviceInfoUpdated() +void AppWindow::DeviceStatusUpdated() { UpdateStatusBar(DeviceStatusBar::Updated); } @@ -963,10 +969,18 @@ void AppWindow::SaveSetup(QString filename) nlohmann::json AppWindow::SaveSetup() { nlohmann::json j; - j["activeMode"] = Mode::getActiveMode()->getName().toStdString(); - j["VNA"] = vna->toJSON(); - j["Generator"] = generator->toJSON(); - j["SpectrumAnalyzer"] = spectrumAnalyzer->toJSON(); + nlohmann::json jm; + for(auto m : Mode::getModes()) { + nlohmann::json jmode; + jmode["type"] = Mode::TypeToName(m->getType()).toStdString(); + jmode["name"] = m->getName().toStdString(); + jmode["settings"] = m->toJSON(); + jm.push_back(jmode); + } + j["Modes"] = jm; + if(Mode::getActiveMode()) { + j["activeMode"] = Mode::getActiveMode()->getName().toStdString(); + } nlohmann::json ref; ref["Mode"] = toolbars.reference.type->currentText().toStdString(); ref["Output"] = toolbars.reference.outFreq->currentText().toStdString(); @@ -1003,20 +1017,36 @@ void AppWindow::LoadSetup(nlohmann::json j) toolbars.reference.type->setCurrentText(QString::fromStdString(j["Reference"].value("Mode", "Int"))); toolbars.reference.outFreq->setCurrentText(QString::fromStdString(j["Reference"].value("Output", "Off"))); } + while(Mode::getModes().size() > 0) { + delete Mode::getModes()[0]; + } + + // old style VNA/Generator/Spectrum Analyzer settings if(j.contains("VNA")) { + auto vna = new VNA(this); vna->fromJSON(j["VNA"]); } if(j.contains("Generator")) { + auto generator = new Generator(this); generator->fromJSON(j["Generator"]); } if(j.contains("SpectrumAnalyzer")) { + auto spectrumAnalyzer = new SpectrumAnalyzer(this); spectrumAnalyzer->fromJSON(j["SpectrumAnalyzer"]); } + if(j.contains("Modes")) { + for(auto jm : j["Modes"]) { + auto type = Mode::TypeFromName(QString::fromStdString(jm.value("type", "Invalid"))); + if(type != Mode::Type::Last && jm.contains("settings")) { + auto m = Mode::createNew(this, QString::fromStdString(jm.value("name", "")), type); + m->fromJSON(jm["settings"]); + } + } + } // activate the correct mode QString modeName = QString::fromStdString(j.value("activeMode", "")); - std::vector modes = {vna, generator, spectrumAnalyzer}; - for(auto m : modes) { + for(auto m : Mode::getModes()) { if(m->getName() == modeName) { m->activate(); break; @@ -1024,7 +1054,7 @@ void AppWindow::LoadSetup(nlohmann::json j) } } -Device *AppWindow::getDevice() const +Device *&AppWindow::getDevice() { return device; } @@ -1104,14 +1134,12 @@ void AppWindow::UpdateStatusBar(DeviceStatusBar status) break; case DeviceStatusBar::Updated: lDeviceInfo.setText(device->getLastDeviceInfoString()); - lADCOverload.setVisible(device->Info().ADC_overload); - lUnlevel.setVisible(device->Info().unlevel); - lUnlock.setVisible(!device->Info().LO1_locked || !device->Info().source_locked); + lADCOverload.setVisible(device->StatusV1().ADC_overload); + lUnlevel.setVisible(device->StatusV1().unlevel); + lUnlock.setVisible(!device->StatusV1().LO1_locked || !device->StatusV1().source_locked); break; default: // invalid status break; } - - } diff --git a/Software/PC_Application/appwindow.h b/Software/PC_Application/appwindow.h index 5dbd92a..daec304 100644 --- a/Software/PC_Application/appwindow.h +++ b/Software/PC_Application/appwindow.h @@ -41,13 +41,15 @@ public: Ui::MainWindow *getUi() const; QStackedWidget *getCentral() const; - Device *getDevice() const; + Device*&getDevice(); const QString& getAppVersion() const; const QString& getAppGitHash() const; static bool showGUI(); + SCPI* getSCPI(); + protected: void closeEvent(QCloseEvent *event) override; private slots: @@ -59,7 +61,7 @@ private slots: void UpdateAcquisitionFrequencies(); void StartFirmwareUpdateDialog(); void DeviceNeedsUpdate(int reported, int expected); - void DeviceInfoUpdated(); + void DeviceStatusUpdated(); void SourceCalibrationDialog(); void ReceiverCalibrationDialog(); void FrequencyCalibrationDialog(); @@ -100,11 +102,6 @@ private: ManualControlDialog *manual; - // Modes - VNA *vna; - Generator *generator; - SpectrumAnalyzer *spectrumAnalyzer; - // Status bar widgets QLabel lConnectionStatus; QLabel lDeviceInfo; diff --git a/Software/PC_Application/mode.cpp b/Software/PC_Application/mode.cpp index 446503b..3f311c7 100644 --- a/Software/PC_Application/mode.cpp +++ b/Software/PC_Application/mode.cpp @@ -1,44 +1,115 @@ #include "mode.h" +#include "Generator/generator.h" +#include "VNA/vna.h" +#include "SpectrumAnalyzer/spectrumanalyzer.h" +#include "CustomWidgets/informationbox.h" + #include "ui_main.h" #include #include #include #include +#include +std::vector Mode::modes; Mode* Mode::activeMode = nullptr; +QTabBar* Mode::tabbar = nullptr; QWidget* Mode::cornerWidget = nullptr; -QButtonGroup* Mode::modeButtonGroup = nullptr; +//QButtonGroup* Mode::modeButtonGroup = nullptr; -Mode::Mode(AppWindow *window, QString name) +Mode::Mode(AppWindow *window, QString name, QString SCPIname) : QObject(window), + SCPINode(SCPIname), window(window), name(name), central(nullptr) -{ +{ + if(!nameAllowed(name)) { + throw std::runtime_error("Unable to create mode, name already taken"); + } // Create mode switch button - auto modeSwitch = new QPushButton(name); - modeSwitch->setCheckable(true); - modeSwitch->setMaximumHeight(window->menuBar()->height()); if(!cornerWidget) { // this is the first created mode, initialize corner widget and set this mode as active - modeSwitch->setChecked(true); - cornerWidget = new QWidget; + cornerWidget = new QWidget(); cornerWidget->setLayout(new QHBoxLayout); cornerWidget->layout()->setSpacing(0); cornerWidget->layout()->setMargin(0); cornerWidget->layout()->setContentsMargins(0,0,0,0); - window->menuBar()->setCornerWidget(cornerWidget); - modeButtonGroup = new QButtonGroup; -// window->menuBar()->setMaximumHeight(window->menuBar()->height()); - } - cornerWidget->layout()->addWidget(modeSwitch); - modeButtonGroup->addButton(modeSwitch); + cornerWidget->setMaximumHeight(window->menuBar()->height()); - connect(modeSwitch, &QPushButton::clicked, [=](){ - activate(); - }); + tabbar = new QTabBar; + tabbar->setTabsClosable(true); + tabbar->setStyleSheet("QTabBar::tab { height: "+QString::number(window->menuBar()->height())+"px;}"); + cornerWidget->layout()->addWidget(tabbar); + + auto bAdd = new QPushButton(); + QIcon icon; + QString iconThemeName = QString::fromUtf8("list-add"); + if (QIcon::hasThemeIcon(iconThemeName)) { + icon = QIcon::fromTheme(iconThemeName); + } else { + icon.addFile(QString::fromUtf8(":/icons/add.png"), QSize(), QIcon::Normal, QIcon::Off); + } + bAdd->setIcon(icon); + + auto mAdd = new QMenu(); + for(unsigned int i=0;i<(int) Type::Last;i++) { + auto type = (Type) i; + auto action = new QAction(TypeToName(type)); + mAdd->addAction(action); + connect(action, &QAction::triggered, [=](){ + bool ok; + QString text = QInputDialog::getText(window, "Create new "+TypeToName(type)+" tab", + "Name:", QLineEdit::Normal, + TypeToName(type), &ok); + if(ok) { + if(!nameAllowed(text)) { + InformationBox::ShowError("Name collision", "Unable to create tab, no duplicate names allowed"); + } else { + auto mode = Mode::createNew(window, text, type); + mode->activate(); + } + } + }); + } + bAdd->setMenu(mAdd); + bAdd->setMaximumHeight(window->menuBar()->height()); + bAdd->setMaximumWidth(40); + cornerWidget->layout()->addWidget(bAdd); + + window->menuBar()->setCornerWidget(cornerWidget); + + connect(tabbar, &QTabBar::currentChanged, [=](int index){ + modes[index]->activate(); + }); + connect(tabbar, &QTabBar::tabCloseRequested, [=](int index){ + delete modes[index]; + }); + } + modes.push_back(this); + tabbar->blockSignals(true); + tabbar->insertTab(tabbar->count(), name); + tabbar->blockSignals(false); + window->getSCPI()->add(this); +} + +Mode::~Mode() +{ + window->getSCPI()->remove(this); + if(activeMode == this) { + deactivate(); + } + auto index = findTabIndex(); + tabbar->blockSignals(true); + tabbar->removeTab(index); + tabbar->blockSignals(false); + modes.erase(modes.begin() + index); + if(modes.size() > 0) { + modes[tabbar->currentIndex()]->activate(); + } + window->getCentral()->removeWidget(central); } void Mode::activate() @@ -89,14 +160,10 @@ void Mode::activate() } activeMode = this; - // force activation of correct pushbutton in case the mode switch was done via script/setup load. - // This will trigger a second activation of this mode in the signal of the button, but since it is + // force activation of correct tab in case the mode switch was done via script/setup load. + // This will trigger a second activation of this mode in the signal of the tab bar, but since it is // already the active mode, this function will just return -> no recursion - for(auto b : modeButtonGroup->buttons()) { - if(b->text() == name) { - b->click(); - } - } + tabbar->setCurrentIndex(findTabIndex()); if(window->getDevice()) { initializeDevice(); @@ -131,6 +198,9 @@ void Mode::deactivate() } qDebug() << "Deactivated mode" << name; + if(window->getDevice()) { + window->getDevice()->SetIdle(); + } activeMode = nullptr; } @@ -139,6 +209,26 @@ Mode *Mode::getActiveMode() return activeMode; } +QString Mode::TypeToName(Mode::Type t) +{ + switch(t) { + case Type::VNA: return "Vector Network Analyzer"; + case Type::SG: return "Signal Generator"; + case Type::SA: return "Spectrum Analyzer"; + default: return "Invalid"; + } +} + +Mode::Type Mode::TypeFromName(QString s) +{ + for(unsigned int i=0;i<(int)Type::Last;i++) { + if(s == TypeToName((Type) i)) { + return (Type) i; + } + } + return Type::Last; +} + void Mode::saveSreenshot() { auto filename = QFileDialog::getSaveFileName(nullptr, "Save plot image", "", "PNG image files (*.png)", nullptr, QFileDialog::DontUseNativeDialog); @@ -153,6 +243,27 @@ void Mode::saveSreenshot() central->grab().save(filename); } +Mode *Mode::createNew(AppWindow *window, QString name, Mode::Type t) +{ + switch(t) { + case Type::VNA: return new VNA(window, name); + case Type::SG: return new Generator(window, name); + case Type::SA: return new SpectrumAnalyzer(window, name); + default: return nullptr; + } +} + +bool Mode::nameAllowed(QString name) +{ + for(auto m : modes) { + if(m->getName() == name) { + // name already taken, no duplicates allowed + return false; + } + } + return true; +} + void Mode::finalize(QWidget *centralWidget) { central = centralWidget; @@ -176,6 +287,27 @@ void Mode::finalize(QWidget *centralWidget) } } +int Mode::findTabIndex() +{ + auto it = std::find(modes.begin(), modes.end(), this); + return it - modes.begin(); +} + +std::vector Mode::getModes() +{ + return modes; +} + +Mode *Mode::findFirstOfType(Mode::Type t) +{ + for(auto m : modes) { + if(m->getType() == t) { + return m; + } + } + return nullptr; +} + void Mode::setStatusbarMessage(QString msg) { statusbarMsg = msg; @@ -188,3 +320,14 @@ QString Mode::getName() const { return name; } + +void Mode::setName(const QString &value) +{ + if(!nameAllowed(value)) { + // unable to use this name + return; + } + name = value; + tabbar->setTabText(findTabIndex(), name); +} + diff --git a/Software/PC_Application/mode.h b/Software/PC_Application/mode.h index b5f91f2..9d2f065 100644 --- a/Software/PC_Application/mode.h +++ b/Software/PC_Application/mode.h @@ -3,30 +3,50 @@ #include "appwindow.h" #include "savable.h" +#include "scpi.h" #include #include #include #include +#include #include #include -class Mode : public QObject, public Savable +class Mode : public QObject, public Savable, public SCPINode { Q_OBJECT public: - Mode(AppWindow *window, QString name); + enum class Type { + VNA, + SG, + SA, + Last, + }; + + Mode(AppWindow *window, QString name, QString SCPIname); + ~Mode(); virtual void activate(); // derived classes must call Mode::activate before doing anything virtual void deactivate(); // derived classes must call Mode::deactivate before returning virtual void shutdown(){}; // called when the application is about to exit QString getName() const; + void setName(const QString &value); static Mode *getActiveMode(); + static QString TypeToName(Type t); + static Type TypeFromName(QString s); + virtual Type getType() = 0; virtual void initializeDevice() = 0; virtual void deviceDisconnected(){}; virtual void saveSreenshot(); + + static Mode *createNew(AppWindow *window, QString name, Type t); + static bool nameAllowed(QString name); + static std::vector getModes(); + static Mode* findFirstOfType(Type t); + signals: void statusbarMessage(QString msg); protected: @@ -39,10 +59,13 @@ protected: std::set docks; private: + int findTabIndex(); + static std::vector modes; static Mode *activeMode; + static QTabBar *tabbar; static QWidget *cornerWidget; - static QButtonGroup *modeButtonGroup; - const QString name; +// static QButtonGroup *modeButtonGroup; + QString name; QString statusbarMsg; QWidget *central; }; diff --git a/Software/PC_Application/scpi.cpp b/Software/PC_Application/scpi.cpp index 01b31d1..5e5e730 100644 --- a/Software/PC_Application/scpi.cpp +++ b/Software/PC_Application/scpi.cpp @@ -106,6 +106,17 @@ bool SCPINode::add(SCPINode *node) return true; } +bool SCPINode::remove(SCPINode *node) +{ + auto it = std::find(subnodes.begin(), subnodes.end(), node); + if(it != subnodes.end()) { + subnodes.erase(it); + return true; + } else { + return false; + } +} + bool SCPINode::add(SCPICommand *cmd) { if(nameCollision(cmd->name())) { diff --git a/Software/PC_Application/scpi.h b/Software/PC_Application/scpi.h index 57614f2..97ab8b3 100644 --- a/Software/PC_Application/scpi.h +++ b/Software/PC_Application/scpi.h @@ -31,6 +31,7 @@ public: name(name){} bool add(SCPINode *node); + bool remove(SCPINode *node); bool add(SCPICommand *cmd); private: diff --git a/Software/VNA_embedded/.settings/language.settings.xml b/Software/VNA_embedded/.settings/language.settings.xml index 0c8cdbb..87b5f55 100644 --- a/Software/VNA_embedded/.settings/language.settings.xml +++ b/Software/VNA_embedded/.settings/language.settings.xml @@ -6,7 +6,7 @@ - + @@ -18,7 +18,7 @@ - + diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp index ee82562..72437a3 100644 --- a/Software/VNA_embedded/Application/App.cpp +++ b/Software/VNA_embedded/Application/App.cpp @@ -117,7 +117,7 @@ inline void App_Process() { sweepActive = VNA::Setup(recv_packet.settings); Communication::SendWithoutPayload(Protocol::PacketType::Ack); break; - case Protocol::PacketType::ManualControl: + case Protocol::PacketType::ManualControlV1: sweepActive = false; last_measure_packet = recv_packet; Manual::Setup(recv_packet.manual); @@ -145,12 +145,21 @@ inline void App_Process() { SA::Setup(recv_packet.spectrumSettings); Communication::SendWithoutPayload(Protocol::PacketType::Ack); break; - case Protocol::PacketType::RequestDeviceInfo: + case Protocol::PacketType::RequestDeviceInfo: { Communication::SendWithoutPayload(Protocol::PacketType::Ack); Protocol::PacketInfo p; p.type = Protocol::PacketType::DeviceInfo; - HW::fillDeviceInfo(&p.info); + p.info = HW::Info; Communication::Send(p); + } + break; + case Protocol::PacketType::RequestDeviceStatus: { + Communication::SendWithoutPayload(Protocol::PacketType::Ack); + Protocol::PacketInfo p; + p.type = Protocol::PacketType::DeviceStatusV1; + HW::getDeviceStatus(&p.statusV1); + Communication::Send(p); + } break; case Protocol::PacketType::SetIdle: HW::SetMode(HW::Mode::Idle); diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp index 2384d39..99b0654 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.cpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp @@ -91,8 +91,9 @@ uint16_t Protocol::EncodePacket(const PacketInfo &packet, uint8_t *dest, uint16_ case PacketType::SweepSettings: payload_size = sizeof(packet.settings); break; case PacketType::Reference: payload_size = sizeof(packet.reference); break; case PacketType::DeviceInfo: payload_size = sizeof(packet.info); break; - case PacketType::Status: payload_size = sizeof(packet.status); break; - case PacketType::ManualControl: payload_size = sizeof(packet.manual); break; + case PacketType::DeviceStatusV1: payload_size = sizeof(packet.statusV1); break; + case PacketType::ManualStatusV1: payload_size = sizeof(packet.manualStatusV1); break; + case PacketType::ManualControlV1: payload_size = sizeof(packet.manual); break; case PacketType::FirmwarePacket: payload_size = sizeof(packet.firmware); break; case PacketType::Generator: payload_size = sizeof(packet.generator); break; case PacketType::SpectrumAnalyzerSettings: payload_size = sizeof(packet.spectrumSettings); break; diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index 009ab1d..1a7dc56 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -4,7 +4,7 @@ namespace Protocol { -static constexpr uint16_t Version = 9; +static constexpr uint16_t Version = 10; #pragma pack(push, 1) @@ -50,17 +50,8 @@ using DeviceInfo = struct _deviceInfo { uint8_t FW_major; uint8_t FW_minor; uint8_t FW_patch; + uint8_t hardware_version; char HW_Revision; - uint8_t extRefAvailable:1; - uint8_t extRefInUse:1; - uint8_t FPGA_configured:1; - uint8_t source_locked:1; - uint8_t LO1_locked:1; - uint8_t ADC_overload:1; - uint8_t unlevel:1; - uint8_t temp_source; - uint8_t temp_LO1; - uint8_t temp_MCU; uint64_t limits_minFreq; uint64_t limits_maxFreq; uint32_t limits_minIFBW; @@ -74,7 +65,21 @@ using DeviceInfo = struct _deviceInfo { uint64_t limits_maxFreqHarmonic; }; -using ManualStatus = struct _manualstatus { +using DeviceStatusV1 = struct _deviceStatusV1 { + uint8_t extRefAvailable:1; + uint8_t extRefInUse:1; + uint8_t FPGA_configured:1; + uint8_t source_locked:1; + uint8_t LO1_locked:1; + uint8_t ADC_overload:1; + uint8_t unlevel:1; + uint8_t temp_source; + uint8_t temp_LO1; + uint8_t temp_MCU; +}; + + +using ManualStatusV1 = struct _manualstatusV1 { int16_t port1min, port1max; int16_t port2min, port2max; int16_t refmin, refmax; @@ -87,7 +92,7 @@ using ManualStatus = struct _manualstatus { uint8_t LO_locked :1; }; -using ManualControl = struct _manualControl { +using ManualControlV1 = struct _manualControlV1 { // Highband Source uint8_t SourceHighCE :1; uint8_t SourceHighRFEN :1; @@ -170,8 +175,8 @@ enum class PacketType : uint8_t { None = 0, Datapoint = 1, SweepSettings = 2, - Status = 3, - ManualControl = 4, + ManualStatusV1 = 3, + ManualControlV1 = 4, DeviceInfo = 5, FirmwarePacket = 6, Ack = 7, @@ -192,6 +197,8 @@ enum class PacketType : uint8_t { FrequencyCorrection = 22, RequestAcquisitionFrequencySettings = 23, AcquisitionFrequencySettings = 24, + DeviceStatusV1 = 25, + RequestDeviceStatus = 26, }; using PacketInfo = struct _packetinfo { @@ -201,10 +208,11 @@ using PacketInfo = struct _packetinfo { SweepSettings settings; ReferenceSettings reference; GeneratorSettings generator; + DeviceStatusV1 statusV1; DeviceInfo info; - ManualControl manual; + ManualControlV1 manual; FirmwarePacket firmware; - ManualStatus status; + ManualStatusV1 manualStatusV1; SpectrumAnalyzerSettings spectrumSettings; SpectrumAnalyzerResult spectrumResult; AmplitudeCorrectionPoint amplitudePoint; diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp index bbafe92..7f47c4e 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.cpp @@ -130,6 +130,17 @@ void FPGA::SetSamplesPerPoint(uint32_t nsamples) { WriteRegister(Reg::SamplesPerPoint, nsamples); } +void FPGA::SetupSweep(uint8_t stages, uint8_t port1_stage, uint8_t port2_stage, bool individual_halt) { + uint16_t value = 0x0000; + value |= (uint16_t) (stages & 0x07) << 13; + if(individual_halt) { + value |= 0x1000; + } + value |= (port1_stage & 0x07) << 3; + value |= (port2_stage & 0x07) << 0; + WriteRegister(Reg::SweepSetup, value); +} + void FPGA::Enable(Periphery p, bool enable) { if (enable) { SysCtrlReg |= (uint16_t) p; @@ -282,7 +293,7 @@ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) { result.RefI = assembleSampleResultValue(&raw[8]); result.RefQ = assembleSampleResultValue(&raw[2]); result.pointNum = (uint16_t)(raw[38]&0x1F) << 8 | raw[39]; - result.activePort = raw[38] & 0x80 ? 1 : 0; + result.stageNum = (raw[38] & 0xE0) >> 5; High(CS); busy_reading = false; if ((status & 0x0004) && callback) { diff --git a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp index 6540fd6..98cd586 100644 --- a/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp +++ b/Software/VNA_embedded/Application/Drivers/FPGA/FPGA.hpp @@ -17,6 +17,7 @@ enum class Reg { SystemControl = 0x03, ADCPrescaler = 0x04, PhaseIncrement = 0x05, + SweepSetup = 0x06, MAX2871Def0LSB = 0x08, MAX2871Def0MSB = 0x09, MAX2871Def1LSB = 0x0A, @@ -33,8 +34,8 @@ using SamplingResult = struct _samplingresult { int64_t P1I, P1Q; int64_t P2I, P2Q; int64_t RefI, RefQ; - uint16_t pointNum :15; - uint16_t activePort :1; + uint16_t pointNum :13; + uint16_t stageNum :3; }; using DFTResult = struct _dftresult { @@ -59,8 +60,7 @@ enum class Periphery { DebugLED = 0x0080, SourceChip = 0x0010, LO1Chip = 0x0008, - ExcitePort2 = 0x0004, - ExcitePort1 = 0x0002, + PortSwitch = 0x0001, }; @@ -113,6 +113,7 @@ bool Init(HaltedCallback cb = nullptr); void WriteRegister(FPGA::Reg reg, uint16_t value); void SetNumberOfPoints(uint16_t npoints); void SetSamplesPerPoint(uint32_t nsamples); +void SetupSweep(uint8_t stages, uint8_t port1_stage, uint8_t port2_stage, bool individual_halt = false); void Enable(Periphery p, bool enable = true); void Disable(Periphery p); bool IsEnabled(Periphery p); diff --git a/Software/VNA_embedded/Application/Generator.cpp b/Software/VNA_embedded/Application/Generator.cpp index ee8a2cb..acbb85c 100644 --- a/Software/VNA_embedded/Application/Generator.cpp +++ b/Software/VNA_embedded/Application/Generator.cpp @@ -11,7 +11,7 @@ void Generator::Setup(Protocol::GeneratorSettings g) { HW::SetIdle(); return; } - Protocol::ManualControl m; + Protocol::ManualControlV1 m; // LOs not required m.LO1CE = 0; m.LO1Frequency = 1000000000; diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index 8afb45c..ab3bd90 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -299,9 +299,7 @@ void HW::SetOutputUnlevel(bool unlev) { unlevel = unlev; } -void HW::fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy) { - // copy constant default values - memcpy(info, &HW::Info, sizeof(HW::Info)); +void HW::getDeviceStatus(Protocol::DeviceStatusV1 *status, bool updateEvenWhenBusy) { if(activeMode == Mode::Idle || updateEvenWhenBusy) { // updating values from FPGA allowed @@ -318,21 +316,21 @@ void HW::fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy) { if(limits.P1min < -ADC_LIMIT || limits.P1max > ADC_LIMIT || limits.P2min < -ADC_LIMIT || limits.P2max > ADC_LIMIT || limits.Rmin < -ADC_LIMIT || limits.Rmax > ADC_LIMIT) { - info->ADC_overload = true; + status->ADC_overload = true; } else { - info->ADC_overload = false; + status->ADC_overload = false; } - auto status = FPGA::GetStatus(); - info->LO1_locked = (status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1; - info->source_locked = (status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1; - info->extRefAvailable = Ref::available(); - info->extRefInUse = extRefInUse; - info->unlevel = unlevel; - info->temp_LO1 = tempLO; - info->temp_source = tempSource; + auto FPGA_status = FPGA::GetStatus(); + status->LO1_locked = (FPGA_status & (int) FPGA::Interrupt::LO1Unlock) ? 0 : 1; + status->source_locked = (FPGA_status & (int) FPGA::Interrupt::SourceUnlock) ? 0 : 1; + status->extRefAvailable = Ref::available(); + status->extRefInUse = extRefInUse; + status->unlevel = unlevel; + status->temp_LO1 = tempLO; + status->temp_source = tempSource; FPGA::ResetADCLimits(); } - info->temp_MCU = STM::getTemperature(); + status->temp_MCU = STM::getTemperature(); } bool HW::Ref::available() { diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index 656569f..c0ce9ff 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -70,17 +70,8 @@ static constexpr Protocol::DeviceInfo Info = { .FW_major = FW_MAJOR, .FW_minor = FW_MINOR, .FW_patch = FW_PATCH, + .hardware_version = 1, .HW_Revision = HW_REVISION, - .extRefAvailable = 0, - .extRefInUse = 0, - .FPGA_configured = 0, - .source_locked = 0, - .LO1_locked = 0, - .ADC_overload = 0, - .unlevel = 0, - .temp_source = 0, - .temp_LO1 = 0, - .temp_MCU = 0, .limits_minFreq = 0, .limits_maxFreq = 6000000000, .limits_minIFBW = DefaultADCSamplerate / MaxSamples, @@ -120,7 +111,7 @@ using AmplitudeSettings = struct _amplitudeSettings { AmplitudeSettings GetAmplitudeSettings(int16_t cdbm, uint64_t freq = 0, bool applyCorrections = false, bool port2 = false); bool GetTemps(uint8_t *source, uint8_t *lo); -void fillDeviceInfo(Protocol::DeviceInfo *info, bool updateEvenWhenBusy = false); +void getDeviceStatus(Protocol::DeviceStatusV1 *status, bool updateEvenWhenBusy = false); namespace Ref { bool available(); bool usingExternal(); diff --git a/Software/VNA_embedded/Application/Manual.cpp b/Software/VNA_embedded/Application/Manual.cpp index 9229457..54bf7f8 100644 --- a/Software/VNA_embedded/Application/Manual.cpp +++ b/Software/VNA_embedded/Application/Manual.cpp @@ -6,11 +6,11 @@ static bool active = false; static uint32_t samples; -static Protocol::ManualStatus status; +static Protocol::ManualStatusV1 status; using namespace HWHAL; -void Manual::Setup(Protocol::ManualControl m) { +void Manual::Setup(Protocol::ManualControlV1 m) { HW::SetMode(HW::Mode::Manual); samples = m.Samples; FPGA::AbortSweep(); @@ -69,8 +69,7 @@ void Manual::Setup(Protocol::ManualControl m) { FPGA::Enable(FPGA::Periphery::Port1Mixer, m.Port1EN); FPGA::Enable(FPGA::Periphery::Port2Mixer, m.Port2EN); FPGA::Enable(FPGA::Periphery::RefMixer, m.RefEN); - FPGA::Enable(FPGA::Periphery::ExcitePort1, m.PortSwitch == 0); - FPGA::Enable(FPGA::Periphery::ExcitePort2, m.PortSwitch == 1); + FPGA::SetupSweep(0, m.PortSwitch == 1, m.PortSwitch == 0); FPGA::Enable(FPGA::Periphery::PortSwitch); // Enable new data and sweep halt interrupt @@ -100,38 +99,38 @@ void Manual::Work() { return; } Protocol::PacketInfo p; - p.type = Protocol::PacketType::Status; - p.status = status; + p.type = Protocol::PacketType::ManualStatusV1; + p.manualStatusV1 = status; uint16_t isr_flags = FPGA::GetStatus(); if (!(isr_flags & 0x0002)) { - p.status.source_locked = 1; + p.manualStatusV1.source_locked = 1; } else { - p.status.source_locked = 0; + p.manualStatusV1.source_locked = 0; } if (!(isr_flags & 0x0001)) { - p.status.LO_locked = 1; + p.manualStatusV1.LO_locked = 1; } else { - p.status.LO_locked = 0; + p.manualStatusV1.LO_locked = 0; } auto limits = FPGA::GetADCLimits(); FPGA::ResetADCLimits(); - p.status.port1min = limits.P1min; - p.status.port1max = limits.P1max; - p.status.port2min = limits.P2min; - p.status.port2max = limits.P2max; - p.status.refmin = limits.Rmin; - p.status.refmax = limits.Rmax; - HW::GetTemps(&p.status.temp_source, &p.status.temp_LO); + p.manualStatusV1.port1min = limits.P1min; + p.manualStatusV1.port1max = limits.P1max; + p.manualStatusV1.port2min = limits.P2min; + p.manualStatusV1.port2max = limits.P2max; + p.manualStatusV1.refmin = limits.Rmin; + p.manualStatusV1.refmax = limits.Rmax; + HW::GetTemps(&p.manualStatusV1.temp_source, &p.manualStatusV1.temp_LO); Communication::Send(p); HW::Ref::update(); Protocol::PacketInfo packet; - packet.type = Protocol::PacketType::DeviceInfo; + packet.type = Protocol::PacketType::DeviceStatusV1; // Enable PLL chips for temperature reading bool srcEn = FPGA::IsEnabled(FPGA::Periphery::SourceChip); bool LOEn = FPGA::IsEnabled(FPGA::Periphery::LO1Chip); FPGA::Enable(FPGA::Periphery::SourceChip); FPGA::Enable(FPGA::Periphery::LO1Chip); - HW::fillDeviceInfo(&packet.info, true); + HW::getDeviceStatus(&packet.statusV1, true); // restore PLL state FPGA::Enable(FPGA::Periphery::SourceChip, srcEn); FPGA::Enable(FPGA::Periphery::LO1Chip, LOEn); diff --git a/Software/VNA_embedded/Application/Manual.hpp b/Software/VNA_embedded/Application/Manual.hpp index 35e6517..e13ed81 100644 --- a/Software/VNA_embedded/Application/Manual.hpp +++ b/Software/VNA_embedded/Application/Manual.hpp @@ -5,7 +5,7 @@ namespace Manual { -void Setup(Protocol::ManualControl m); +void Setup(Protocol::ManualControlV1 m); bool MeasurementDone(const FPGA::SamplingResult &result); void Work(); void Stop(); diff --git a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp index e9c60b6..f616251 100644 --- a/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp +++ b/Software/VNA_embedded/Application/SpectrumAnalyzer.cpp @@ -209,8 +209,7 @@ void SA::Setup(Protocol::SpectrumAnalyzerSettings settings) { FPGA::SetWindow((FPGA::Window) s.WindowType); FPGA::Enable(FPGA::Periphery::LO1Chip); FPGA::Enable(FPGA::Periphery::LO1RF); - FPGA::Enable(FPGA::Periphery::ExcitePort1, s.trackingGeneratorPort == 0); - FPGA::Enable(FPGA::Periphery::ExcitePort2, s.trackingGeneratorPort == 1); + FPGA::SetupSweep(0, s.trackingGeneratorPort == 1, s.trackingGeneratorPort == 0); FPGA::Enable(FPGA::Periphery::PortSwitch, s.trackingGenerator); FPGA::Enable(FPGA::Periphery::Amplifier, s.trackingGenerator); FPGA::Enable(FPGA::Periphery::Port1Mixer); @@ -386,8 +385,8 @@ void SA::Work() { // send device info every nth point FPGA::Enable(FPGA::Periphery::SourceChip); // needs to enable the chip to get a valid temperature reading Protocol::PacketInfo packet; - packet.type = Protocol::PacketType::DeviceInfo; - HW::fillDeviceInfo(&packet.info, true); + packet.type = Protocol::PacketType::DeviceStatusV1; + HW::getDeviceStatus(&packet.statusV1, true); FPGA::Disable(FPGA::Periphery::SourceChip); Communication::Send(packet); } diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 3e9b9de..4e3e4e5 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -21,8 +21,9 @@ static Protocol::SweepSettings settings; static uint16_t pointCnt; +static uint8_t stageCnt; +static uint8_t stages; static double logMultiplier, logFrequency; -static bool excitingPort1; static Protocol::Datapoint data; static bool active = false; static Si5351C::DriveStrength fixedPowerLowband; @@ -278,12 +279,22 @@ bool VNA::Setup(Protocol::SweepSettings s) { FPGA::Enable(FPGA::Periphery::SourceRF); FPGA::Enable(FPGA::Periphery::LO1Chip); FPGA::Enable(FPGA::Periphery::LO1RF); - FPGA::Enable(FPGA::Periphery::ExcitePort1, s.excitePort1); - FPGA::Enable(FPGA::Periphery::ExcitePort2, s.excitePort2); + if(s.excitePort1 && s.excitePort2) { + // two stages, port 1 first, followed by port 2 + FPGA::SetupSweep(1, 0, 1); + stages = 2; + } else if(s.excitePort1) { + // one stage, port 1 only + FPGA::SetupSweep(0, 0, 1); + stages = 1; + } else { + // one stage, port 2 only + FPGA::SetupSweep(0, 1, 0); + stages = 1; + } FPGA::Enable(FPGA::Periphery::PortSwitch); pointCnt = 0; - // starting port depends on whether port 1 is active in sweep - excitingPort1 = s.excitePort1; + stageCnt = 0; IFTableIndexCnt = 0; adcShifted = false; active = true; @@ -306,8 +317,8 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { if(!active) { return false; } - if(result.pointNum != pointCnt || !result.activePort != excitingPort1) { - LOG_WARN("Indicated point does not match (%u != %u, %d != %d)", result.pointNum, pointCnt, result.activePort, !excitingPort1); + if(result.pointNum != pointCnt || result.stageNum != stageCnt) { + LOG_WARN("Indicated point does not match (%u != %u, %d != %d)", result.pointNum, pointCnt, result.stageNum, stageCnt); FPGA::AbortSweep(); return false; } @@ -320,29 +331,24 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { data.pointNum = pointCnt; data.frequency = getPointFrequency(pointCnt); data.cdbm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1); - if(excitingPort1) { + if(stageCnt == 0 && settings.excitePort1) { + // stimulus is present at port 1 data.real_S11 = port1.real(); data.imag_S11 = port1.imag(); data.real_S21 = port2.real(); data.imag_S21 = port2.imag(); } else { + // stimulus is present at port 2 data.real_S12 = port1.real(); data.imag_S12 = port1.imag(); data.real_S22 = port2.real(); data.imag_S22 = port2.imag(); } - // figure out whether this sweep point is complete and which port gets excited next - bool pointComplete = false; - if(settings.excitePort1 == 1 && settings.excitePort2 == 1) { - // point is complete when port 2 was active - pointComplete = !excitingPort1; - // next measurement will be from other port - excitingPort1 = !excitingPort1; - } else { - // only one port active, point is complete after every measurement - pointComplete = true; - } - if(pointComplete) { + // figure out whether this sweep point is complete + stageCnt++; + if(stageCnt == stages) { + // point is complete + stageCnt = 0; STM::DispatchToInterrupt(PassOnData); pointCnt++; if (pointCnt >= settings.points) { @@ -361,8 +367,8 @@ void VNA::Work() { HW::Ref::update(); // Compile info packet Protocol::PacketInfo packet; - packet.type = Protocol::PacketType::DeviceInfo; - HW::fillDeviceInfo(&packet.info, true); + packet.type = Protocol::PacketType::DeviceStatusV1; + HW::getDeviceStatus(&packet.statusV1, true); Communication::Send(packet); // do not reset unlevel flag here, as it is calculated only once at the setup of the sweep // Start next sweep