Merge branch 'master' of github.com:jankae/LibreVNA

This commit is contained in:
Jan Käberich 2022-04-05 14:10:34 +02:00
commit 4243e64f7f
45 changed files with 710 additions and 400 deletions

View File

@ -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}

View File

@ -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;

View File

@ -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;

View File

@ -224,7 +224,7 @@
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
</transform>
<transform xil_pn:end_ts="1625850461" xil_pn:in_ck="-4366097307991745463" xil_pn:name="TRAN_regenerateCores" xil_pn:prop_ck="-2723611991789822717" xil_pn:start_ts="1625850460">
<transform xil_pn:end_ts="1648844622" xil_pn:in_ck="-4366097307991745463" xil_pn:name="TRAN_regenerateCores" xil_pn:prop_ck="-2723611991789822717" xil_pn:start_ts="1648844621">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
<outfile xil_pn:name="ipcore_dir/DSP_SLICE.ngc"/>
@ -253,7 +253,7 @@
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
</transform>
<transform xil_pn:end_ts="1625861989" xil_pn:in_ck="2241500006820465658" xil_pn:name="TRANEXT_xstsynthesize_spartan6" xil_pn:prop_ck="3256065936432453276" xil_pn:start_ts="1625861965">
<transform xil_pn:end_ts="1648846416" xil_pn:in_ck="2241500006820465658" xil_pn:name="TRANEXT_xstsynthesize_spartan6" xil_pn:prop_ck="3256065936432453276" xil_pn:start_ts="1648846407">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="WarningsGenerated"/>
<status xil_pn:value="ReadyToRun"/>
@ -275,7 +275,7 @@
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
</transform>
<transform xil_pn:end_ts="1625861996" xil_pn:in_ck="5411862124762956458" xil_pn:name="TRANEXT_ngdbuild_FPGA" xil_pn:prop_ck="4604875190571501774" xil_pn:start_ts="1625861989">
<transform xil_pn:end_ts="1648846420" xil_pn:in_ck="5411862124762956458" xil_pn:name="TRANEXT_ngdbuild_FPGA" xil_pn:prop_ck="4604875190571501774" xil_pn:start_ts="1648846416">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
<outfile xil_pn:name="_ngo"/>
@ -284,7 +284,7 @@
<outfile xil_pn:name="top.ngd"/>
<outfile xil_pn:name="top_ngdbuild.xrpt"/>
</transform>
<transform xil_pn:end_ts="1625862085" xil_pn:in_ck="8512332261572065657" xil_pn:name="TRANEXT_map_spartan6" xil_pn:prop_ck="-4668962392366239264" xil_pn:start_ts="1625861996">
<transform xil_pn:end_ts="1648846449" xil_pn:in_ck="8512332261572065657" xil_pn:name="TRANEXT_map_spartan6" xil_pn:prop_ck="-4668962392366239264" xil_pn:start_ts="1648846420">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="WarningsGenerated"/>
<status xil_pn:value="ReadyToRun"/>
@ -298,7 +298,7 @@
<outfile xil_pn:name="top_summary.xml"/>
<outfile xil_pn:name="top_usage.xml"/>
</transform>
<transform xil_pn:end_ts="1625862116" xil_pn:in_ck="1117507038335044978" xil_pn:name="TRANEXT_par_spartan6" xil_pn:prop_ck="-1085068593928086116" xil_pn:start_ts="1625862085">
<transform xil_pn:end_ts="1648846462" xil_pn:in_ck="1117507038335044978" xil_pn:name="TRANEXT_par_spartan6" xil_pn:prop_ck="-1085068593928086116" xil_pn:start_ts="1648846449">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
<outfile xil_pn:name="_xmsgs/par.xmsgs"/>
@ -312,9 +312,8 @@
<outfile xil_pn:name="top_pad.txt"/>
<outfile xil_pn:name="top_par.xrpt"/>
</transform>
<transform xil_pn:end_ts="1625862132" xil_pn:in_ck="154288912438" xil_pn:name="TRANEXT_bitFile_spartan6" xil_pn:prop_ck="3274353840855015246" xil_pn:start_ts="1625862116">
<transform xil_pn:end_ts="1648846469" xil_pn:in_ck="154288912438" xil_pn:name="TRANEXT_bitFile_spartan6" xil_pn:prop_ck="3274353840855015246" xil_pn:start_ts="1648846462">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="WarningsGenerated"/>
<status xil_pn:value="ReadyToRun"/>
<outfile xil_pn:name="_xmsgs/bitgen.xmsgs"/>
<outfile xil_pn:name="top.bgn"/>
@ -366,7 +365,7 @@
<status xil_pn:value="InputChanged"/>
<status xil_pn:value="InputRemoved"/>
</transform>
<transform xil_pn:end_ts="1625862116" xil_pn:in_ck="8512326635937592693" xil_pn:name="TRAN_postRouteTrce" xil_pn:prop_ck="445577401284416184" xil_pn:start_ts="1625862109">
<transform xil_pn:end_ts="1648846462" xil_pn:in_ck="8512326635937592693" xil_pn:name="TRAN_postRouteTrce" xil_pn:prop_ck="445577401284416184" xil_pn:start_ts="1648846459">
<status xil_pn:value="SuccessfullyRun"/>
<status xil_pn:value="ReadyToRun"/>
<outfile xil_pn:name="_xmsgs/trce.xmsgs"/>

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="top.vhd" xil_pn:type="FILE_VHDL">

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="DSP_SLICE.ngc" xil_pn:type="FILE_NGC">

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="PLL.ucf" xil_pn:type="FILE_UCF">

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="SinCos.ngc" xil_pn:type="FILE_NGC">

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="SweepConfigMem.ngc" xil_pn:type="FILE_NGC">

View File

@ -12,7 +12,7 @@
<!-- Copyright (c) 1995-2013 Xilinx, Inc. All rights reserved. -->
</header>
<version xil_pn:ise_version="14.6" xil_pn:schema_version="2"/>
<version xil_pn:ise_version="14.7" xil_pn:schema_version="2"/>
<files>
<file xil_pn:name="result_bram.ngc" xil_pn:type="FILE_NGC">

Binary file not shown.

View File

@ -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,

View File

@ -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);

View File

@ -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<bool (libusb_device_handle *, QString)>
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);

View File

@ -13,7 +13,7 @@
#include <QTimer>
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<void(TransmissionResult)> cb = nullptr, unsigned int timeout = 500);
bool Configure(Protocol::SweepSettings settings, std::function<void(TransmissionResult)> 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<QString> 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

View File

@ -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();

View File

@ -103,7 +103,7 @@ public:
double getRefPhase();
public slots:
void NewStatus(Protocol::ManualStatus status);
void NewStatus(Protocol::ManualStatusV1 status);
private:
void UpdateDevice();

View File

@ -2,11 +2,10 @@
#include <QSettings>
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();

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -45,9 +45,8 @@
#include <fstream>
#include <QDateTime>
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. "

View File

@ -12,15 +12,17 @@
#include <QComboBox>
#include <QCheckBox>
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;

View File

@ -51,9 +51,8 @@
#include <QErrorMessage>
#include <QDebug>
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);

View File

@ -13,17 +13,19 @@
#include <QWidget>
#include <functional>
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;

View File

@ -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<Protocol::Datapoint>("Datapoint");
qRegisterMetaType<Protocol::ManualStatus>("Manual");
qRegisterMetaType<Protocol::ManualStatusV1>("ManualV1");
qRegisterMetaType<Protocol::SpectrumAnalyzerResult>("SpectrumAnalyzerResult");
qRegisterMetaType<Protocol::AmplitudeCorrectionPoint>("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<Mode*> 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;
}
}

View File

@ -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;

View File

@ -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 <QPushButton>
#include <QSettings>
#include <QDebug>
#include <QFileDialog>
#include <QInputDialog>
std::vector<Mode*> 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 *> 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);
}

View File

@ -3,30 +3,50 @@
#include "appwindow.h"
#include "savable.h"
#include "scpi.h"
#include <QString>
#include <QWidget>
#include <QButtonGroup>
#include <QToolBar>
#include <QTabBar>
#include <QDockWidget>
#include <set>
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<Mode *> getModes();
static Mode* findFirstOfType(Type t);
signals:
void statusbarMessage(QString msg);
protected:
@ -39,10 +59,13 @@ protected:
std::set<QDockWidget*> docks;
private:
int findTabIndex();
static std::vector<Mode*> 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;
};

View File

@ -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())) {

View File

@ -31,6 +31,7 @@ public:
name(name){}
bool add(SCPINode *node);
bool remove(SCPINode *node);
bool add(SCPICommand *cmd);
private:

View File

@ -6,7 +6,7 @@
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="-1671998965483228530" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="1949595105660943601" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>
@ -18,7 +18,7 @@
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
<provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="-1671998965483228530" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<provider class="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" console="false" env-hash="1949595105660943601" id="com.st.stm32cube.ide.mcu.toolchain.armnone.setup.CrossBuiltinSpecsDetector" keep-relative-paths="false" name="MCU ARM GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
<language-scope id="org.eclipse.cdt.core.gcc"/>
<language-scope id="org.eclipse.cdt.core.g++"/>
</provider>

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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() {

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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);
}

View File

@ -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