diff --git a/FPGA/VNA/Sweep.vhd b/FPGA/VNA/Sweep.vhd index 72e0d32..5ea40f1 100644 --- a/FPGA/VNA/Sweep.vhd +++ b/FPGA/VNA/Sweep.vhd @@ -77,7 +77,7 @@ end Sweep; architecture Behavioral of Sweep is signal point_cnt : unsigned(12 downto 0); - type Point_states is (TriggerSetup, SettingUp, SettlingPort1, ExcitingPort1, SettlingPort2, ExcitingPort2, Done); + type Point_states is (TriggerSetup, SettingUp, SettlingPort1, ExcitingPort1, SettlingPort2, ExcitingPort2, NextPoint, Done); signal state : Point_states; signal settling_cnt : unsigned(15 downto 0); signal settling_time : unsigned(15 downto 0); @@ -187,7 +187,7 @@ begin if EXCITE_PORT2 = '1' then state <= SettlingPort2; else - state <= Done; + state <= NextPoint; end if; settling_cnt <= unsigned(SETTLING_TIME); end if; @@ -206,15 +206,18 @@ begin -- wait for sampling to finish START_SAMPLING <= '0'; if SAMPLING_BUSY = '0' then - if point_cnt < unsigned(NPOINTS) then - point_cnt <= point_cnt + 1; - state <= TriggerSetup; - PORT_SELECT <= '1'; - else - point_cnt <= (others => '0'); - state <= Done; - end if; + 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; + end if; when others => end case; end if; diff --git a/FPGA/VNA/VNA.gise b/FPGA/VNA/VNA.gise index 7f4fdf7..021d95c 100644 --- a/FPGA/VNA/VNA.gise +++ b/FPGA/VNA/VNA.gise @@ -223,7 +223,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -254,12 +254,10 @@ - + - - @@ -270,7 +268,7 @@ - + @@ -284,7 +282,7 @@ - + @@ -330,7 +328,7 @@ - + diff --git a/Software/PC_Application/Application b/Software/PC_Application/Application index 4dab255..4ed48be 100755 Binary files a/Software/PC_Application/Application and b/Software/PC_Application/Application differ diff --git a/Software/PC_Application/Calibration/calibration.cpp b/Software/PC_Application/Calibration/calibration.cpp index 2621355..a7bc99c 100644 --- a/Software/PC_Application/Calibration/calibration.cpp +++ b/Software/PC_Application/Calibration/calibration.cpp @@ -279,7 +279,12 @@ Calibration::InterpolationType Calibration::getInterpolation(Protocol::SweepSett return InterpolationType::Extrapolate; } // Either exact or interpolation, check individual frequencies - uint32_t f_step = (settings.f_stop - settings.f_start) / (settings.points - 1); + uint32_t f_step; + if(settings.points > 1) { + f_step = (settings.f_stop - settings.f_start) / (settings.points - 1); + } else { + f_step = settings.f_stop - settings.f_start; + } for(uint64_t f = settings.f_start; f <= settings.f_stop; f += f_step) { if(find_if(points.begin(), points.end(), [&f](const Point& p){ return abs(f - p.frequency) < 100; @@ -549,8 +554,8 @@ istream& operator >>(istream &in, Calibration &c) } else { throw runtime_error("Incomplete calibration data, the requested \"" + line + "\"-Calibration could not be performed."); } + break; } - break; } } return in; diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp index 5015a7e..af3cb92 100644 --- a/Software/PC_Application/Traces/tracemodel.cpp +++ b/Software/PC_Application/Traces/tracemodel.cpp @@ -51,6 +51,7 @@ void TraceModel::togglePause(unsigned int index) traces[index]->pause(); } emit dataChanged(createIndex(index, 1), createIndex(index, 1)); + emit requiredExcitation(PortExcitationRequired(1), PortExcitationRequired(2)); } } @@ -109,6 +110,23 @@ std::vector TraceModel::getTraces() return traces; } +bool TraceModel::PortExcitationRequired(int port) +{ + for(auto t : traces) { + if(t->isLive() && !t->isPaused()) { + // this trace needs measurements from VNA, check if port has to be excited for its measurement + auto param = t->liveParameter(); + if(port == 1 && (param == Trace::LiveParameter::S11 || param == Trace::LiveParameter::S21)) { + return true; + } else if(port == 2 && (param == Trace::LiveParameter::S22 || param == Trace::LiveParameter::S12)) { + return true; + } + } + } + // checked all traces, none requires this port to be excited + return false; +} + void TraceModel::clearVNAData() { for(auto t : traces) { diff --git a/Software/PC_Application/Traces/tracemodel.h b/Software/PC_Application/Traces/tracemodel.h index 0ae29b6..06c50c7 100644 --- a/Software/PC_Application/Traces/tracemodel.h +++ b/Software/PC_Application/Traces/tracemodel.h @@ -23,9 +23,12 @@ public: QVariant data(const QModelIndex &index, int role) const override; std::vector getTraces(); + + bool PortExcitationRequired(int port); signals: void traceAdded(Trace *t); void traceRemoved(Trace *t); + void requiredExcitation(bool excitePort1, bool excitePort2); public slots: void clearVNAData(); diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index 2091fd2..b7086bd 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -43,13 +43,9 @@ VNA::VNA(AppWindow *window) : Mode(window, "Vector Network Analyzer"), + pref(window->getPreferenceRef()), central(new TileWidget(traceModel)) { - QCoreApplication::setOrganizationName("VNA"); - QCoreApplication::setApplicationName("Application"); - - pref.load(); - averages = 1; calValid = false; calMeasuring = false; @@ -79,6 +75,8 @@ VNA::VNA(AppWindow *window) auto tracebode2 = new TraceBodePlot(traceModel); tracebode2->enableTrace(tS21, true); + connect(&traceModel, &TraceModel::requiredExcitation, this, &VNA::ExcitationRequired); + central->splitVertically(); central->Child1()->splitHorizontally(); central->Child2()->splitHorizontally(); @@ -422,6 +420,13 @@ VNA::VNA(AppWindow *window) qRegisterMetaType("Datapoint"); // Set initial sweep settings + if(pref.Acquisition.alwaysExciteBothPorts) { + settings.excitePort1 = 1; + settings.excitePort2 = 1; + } else { + settings.excitePort1 = traceModel.PortExcitationRequired(1); + settings.excitePort2 = traceModel.PortExcitationRequired(2); + } if(pref.Startup.RememberSweepSettings) { LoadSweepSettings(); } else { @@ -654,6 +659,22 @@ void VNA::SetAveraging(unsigned int averages) SettingsChanged(); } +void VNA::ExcitationRequired(bool port1, bool port2) +{ + qDebug() << pref.Acquisition.alwaysExciteBothPorts; + if(pref.Acquisition.alwaysExciteBothPorts) { + port1 = true; + port2 = true; + } + // check if settings actually changed + if(settings.excitePort1 != port1 + || settings.excitePort2 != port2) { + settings.excitePort1 = port1; + settings.excitePort2 = port2; + SettingsChanged(); + } +} + void VNA::DisableCalibration(bool force) { if(calValid || force) { diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index f4d8f08..66eaa6f 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -32,6 +32,7 @@ private slots: void SetPoints(unsigned int points); void SetIFBandwidth(double bandwidth); void SetAveraging(unsigned int averages); + void ExcitationRequired(bool port1, bool port2); // Calibration void DisableCalibration(bool force = false); void ApplyCalibration(Calibration::Type type); @@ -47,9 +48,8 @@ private: void LoadSweepSettings(); void StoreSweepSettings(); - Preferences pref; + Preferences &pref; - QActionGroup *deviceActionGroup; Protocol::SweepSettings settings; unsigned int averages; TraceModel traceModel; diff --git a/Software/PC_Application/appwindow.cpp b/Software/PC_Application/appwindow.cpp index f3c2a06..f6f3491 100644 --- a/Software/PC_Application/appwindow.cpp +++ b/Software/PC_Application/appwindow.cpp @@ -105,7 +105,9 @@ AppWindow::AppWindow(QWidget *parent) } }); connect(ui->actionPreferences, &QAction::triggered, [=](){ + qDebug() << pref.Acquisition.alwaysExciteBothPorts; pref.edit(); + qDebug() << pref.Acquisition.alwaysExciteBothPorts; }); setWindowTitle("VNA"); @@ -248,6 +250,11 @@ void AppWindow::CreateToolbars() addToolBar(tb_reference); } +Preferences &AppWindow::getPreferenceRef() +{ + return pref; +} + int AppWindow::UpdateDeviceList() { deviceActionGroup->setExclusive(true); diff --git a/Software/PC_Application/appwindow.h b/Software/PC_Application/appwindow.h index 7af49b9..ba8c60e 100644 --- a/Software/PC_Application/appwindow.h +++ b/Software/PC_Application/appwindow.h @@ -34,6 +34,8 @@ public: QStackedWidget *getCentral() const; Device *getDevice() const; + Preferences &getPreferenceRef(); + protected: void closeEvent(QCloseEvent *event) override; private slots: diff --git a/Software/PC_Application/preferences.cpp b/Software/PC_Application/preferences.cpp index ef43cc5..0f42d27 100644 --- a/Software/PC_Application/preferences.cpp +++ b/Software/PC_Application/preferences.cpp @@ -66,6 +66,7 @@ PreferencesDialog::PreferencesDialog(Preferences *pref, QWidget *parent) : p->Startup.DefaultSweep.bandwidth = ui->StartupSweepBandwidth->value(); p->Startup.DefaultSweep.points = ui->StartupSweepPoints->value(); p->Startup.DefaultSweep.excitation = ui->StartupSweepLevel->value(); + p->Acquisition.alwaysExciteBothPorts = ui->AcquisitionAlwaysExciteBoth->isChecked(); accept(); }); @@ -90,6 +91,8 @@ void PreferencesDialog::setInitialGUIState() ui->StartupSweepBandwidth->setValueQuiet(p->Startup.DefaultSweep.bandwidth); ui->StartupSweepPoints->setValue(p->Startup.DefaultSweep.points); ui->StartupSweepLevel->setValue(p->Startup.DefaultSweep.excitation); + + ui->AcquisitionAlwaysExciteBoth->setChecked(p->Acquisition.alwaysExciteBothPorts); } void Preferences::load() diff --git a/Software/PC_Application/preferences.h b/Software/PC_Application/preferences.h index b3539e3..68800d4 100644 --- a/Software/PC_Application/preferences.h +++ b/Software/PC_Application/preferences.h @@ -46,13 +46,16 @@ public: double excitation; } DefaultSweep; } Startup; + struct { + bool alwaysExciteBothPorts; + } Acquisition; private: using SettingDescription = struct { QPointerVariant var; QString name; QVariant def; }; - const std::array descr = {{ + const std::array descr = {{ {&Startup.ConnectToFirstDevice, "Startup.ConnectToFirstDevice", true}, {&Startup.RememberSweepSettings, "Startup.RememberSweepSettings", false}, {&Startup.DefaultSweep.start, "Startup.DefaultSweep.start", 1000000.0}, @@ -60,6 +63,7 @@ private: {&Startup.DefaultSweep.points, "Startup.DefaultSweep.points", 501}, {&Startup.DefaultSweep.bandwidth, "Startup.DefaultSweep.bandwidth", 1000.0}, {&Startup.DefaultSweep.excitation, "Startup.DefaultSweep.excitation", -10.00}, + {&Acquisition.alwaysExciteBothPorts, "Acquisition.alwaysExciteBothPorts", false}, }}; }; diff --git a/Software/PC_Application/preferencesdialog.ui b/Software/PC_Application/preferencesdialog.ui index 1d10a4a..27adf98 100644 --- a/Software/PC_Application/preferencesdialog.ui +++ b/Software/PC_Application/preferencesdialog.ui @@ -232,7 +232,7 @@ - + <html><head/><body><p>If only S11/S21 or S22/S12 are enabled, faster sweeps are possible by only exciting one port. Checking this option forces the device to always excite both ports even when the measurements from one port will not be used.</p></body></html> diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp index 4106d91..3a27dc1 100644 --- a/Software/VNA_embedded/Application/App.cpp +++ b/Software/VNA_embedded/Application/App.cpp @@ -178,8 +178,7 @@ void App_Start() { case Protocol::PacketType::SweepSettings: LOG_INFO("New settings received"); settings = packet.settings; - VNA::ConfigureSweep(settings, VNACallback); - sweepActive = true; + sweepActive = VNA::ConfigureSweep(settings, VNACallback); lastNewPoint = HAL_GetTick(); Communication::SendWithoutPayload(Protocol::PacketType::Ack); break; diff --git a/Software/VNA_embedded/Application/Communication/Protocol.cpp b/Software/VNA_embedded/Application/Communication/Protocol.cpp index 1bbe155..574746d 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.cpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.cpp @@ -174,6 +174,8 @@ static Protocol::SweepSettings DecodeSweepSettings(uint8_t *buf) { e.get(d.points); e.get(d.if_bandwidth); e.get(d.cdbm_excitation); + d.excitePort1 = e.getBits(1); + d.excitePort2 = e.getBits(1); return d; } static int16_t EncodeSweepSettings(Protocol::SweepSettings d, uint8_t *buf, @@ -184,6 +186,8 @@ static int16_t EncodeSweepSettings(Protocol::SweepSettings d, uint8_t *buf, e.add(d.points); e.add(d.if_bandwidth); e.add(d.cdbm_excitation); + e.addBits(d.excitePort1, 1); + e.addBits(d.excitePort2, 1); return e.getSize(); } diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index a4c6dee..23d7b46 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -21,6 +21,8 @@ using SweepSettings = struct _sweepSettings { uint16_t points; uint32_t if_bandwidth; int16_t cdbm_excitation; // in 1/100 dbm + uint8_t excitePort1:1; + uint8_t excitePort2:1; }; using ReferenceSettings = struct _referenceSettings { diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 3f3d4e7..057ed60 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -82,9 +82,9 @@ static void ReadComplete(FPGA::SamplingResult result) { auto ref = std::complex(result.RefI, result.RefQ); auto port1 = port1_raw / ref; auto port2 = port2_raw / ref; + data.pointNum = pointCnt; + data.frequency = settings.f_start + (settings.f_stop - settings.f_start) * pointCnt / (settings.points - 1); if(excitingPort1) { - data.pointNum = pointCnt; - data.frequency = settings.f_start + (settings.f_stop - settings.f_start) * pointCnt / (settings.points - 1); data.real_S11 = port1.real(); data.imag_S11 = port1.imag(); data.real_S21 = port2.real(); @@ -94,6 +94,19 @@ static void ReadComplete(FPGA::SamplingResult result) { 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) { if (sweepCallback) { sweepCallback(data); } @@ -102,10 +115,8 @@ static void ReadComplete(FPGA::SamplingResult result) { // reached end of sweep, start again pointCnt = 0; IFTableIndexCnt = 0; - // FPGA::StartSweep(); } } - excitingPort1 = !excitingPort1; } else { // Manual control mode, simply pass on raw result if(statusCallback) { @@ -218,6 +229,11 @@ bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) { // was used in manual mode last, do full initialization before starting sweep VNA::Init(); } + if(s.excitePort1 == 0 && s.excitePort2 == 0) { + // both ports disabled, set to idle + SetIdle(); + return false; + } sweepCallback = cb; settings = s; // Abort possible active sweep first @@ -316,7 +332,7 @@ bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) { } LO1.SetFrequency(freq + used_IF); FPGA::WriteSweepConfig(i, lowband, Source.GetRegisters(), - LO1.GetRegisters(), attenuator, freq, FPGA::SettlingTime::us540, + LO1.GetRegisters(), attenuator, freq, FPGA::SettlingTime::us20, FPGA::Samples::SPPRegister, needs_halt); last_lowband = lowband; } @@ -332,10 +348,11 @@ bool VNA::ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb) { FPGA::Enable(FPGA::Periphery::SourceRF); FPGA::Enable(FPGA::Periphery::LO1Chip); FPGA::Enable(FPGA::Periphery::LO1RF); - FPGA::Enable(FPGA::Periphery::ExcitePort1); - FPGA::Enable(FPGA::Periphery::ExcitePort2); + FPGA::Enable(FPGA::Periphery::ExcitePort1, s.excitePort1); + FPGA::Enable(FPGA::Periphery::ExcitePort2, s.excitePort2); pointCnt = 0; - excitingPort1 = true; + // starting port depends on whether port 1 is active in sweep + excitingPort1 = s.excitePort1; IFTableIndexCnt = 0; // Start the sweep FPGA::StartSweep(); @@ -461,7 +478,10 @@ bool VNA::Ref::applySettings(Protocol::ReferenceSettings s) { LOG_INFO("External reference output set to %luHz", extOutFreq); } } - bool useExternal = s.UseExternalRef || (s.AutomaticSwitch && Ref::available()); + bool useExternal = s.UseExternalRef; + if (s.AutomaticSwitch) { + useExternal = Ref::available(); + } if(useExternal != extRefInUse) { // switch between internal and external reference extRefInUse = useExternal; diff --git a/Software/VNA_embedded/Application/VNA.hpp b/Software/VNA_embedded/Application/VNA.hpp index 17528fa..d337c1b 100644 --- a/Software/VNA_embedded/Application/VNA.hpp +++ b/Software/VNA_embedded/Application/VNA.hpp @@ -10,6 +10,7 @@ using SweepCallback = void(*)(Protocol::Datapoint); using StatusCallback = void(*)(FPGA::SamplingResult); bool Init(); +// returns whether the sweep is actually started bool ConfigureSweep(Protocol::SweepSettings s, SweepCallback cb); bool ConfigureManual(Protocol::ManualControl m, StatusCallback cb); bool ConfigureGenerator(Protocol::GeneratorSettings g);