diff --git a/Documentation/UserManual/ProgrammingGuide.pdf b/Documentation/UserManual/ProgrammingGuide.pdf index ff937c4..69b4737 100644 Binary files a/Documentation/UserManual/ProgrammingGuide.pdf and b/Documentation/UserManual/ProgrammingGuide.pdf differ diff --git a/Documentation/UserManual/ProgrammingGuide.tex b/Documentation/UserManual/ProgrammingGuide.tex index c412908..bf2bf35 100644 --- a/Documentation/UserManual/ProgrammingGuide.tex +++ b/Documentation/UserManual/ProgrammingGuide.tex @@ -341,6 +341,9 @@ These commands change or query VNA settings. Although most of them are available \subsubsection{VNA:FREQuency:FULL} \event{Sets the device to the maximum span possible}{VNA:FREQuency:FULL}{None} +\subsubsection{VNA:FREQuency:ZERO} +\event{Sets the device to zero span mode}{VNA:FREQuency:ZERO}{None} + \subsubsection{VNA:POWer:START} \event{Sets the start power of the power sweep}{VNA:POWer:START}{, in dBm} \query{Queries the currently selected start power}{VNA:POWer:START?}{None}{start power in dBm} diff --git a/Software/PC_Application/Traces/Marker/marker.cpp b/Software/PC_Application/Traces/Marker/marker.cpp index 93d134c..4ea29b0 100644 --- a/Software/PC_Application/Traces/Marker/marker.cpp +++ b/Software/PC_Application/Traces/Marker/marker.cpp @@ -174,6 +174,39 @@ std::vector Marker::applicableFormats() break; } break; + case Trace::DataType::TimeZeroSpan: + switch(type) { + case Type::Manual: + case Type::Delta: + case Type::Maximum: + case Type::Minimum: + case Type::PeakTable: + if(Trace::isSAParamater(parentTrace->liveParameter())) { + ret.push_back(Format::dBm); + ret.push_back(Format::dBuV); + } else { + ret.push_back(Format::dB); + ret.push_back(Format::dBAngle); + ret.push_back(Format::RealImag); + } + if(parentTrace) { + if(parentTrace->isReflection()) { + ret.push_back(Format::Impedance); + ret.push_back(Format::VSWR); + ret.push_back(Format::SeriesR); + ret.push_back(Format::Capacitance); + ret.push_back(Format::Inductance); + ret.push_back(Format::QualityFactor); + } + if(!isnan(parentTrace->getNoise(parentTrace->minX()))) { + ret.push_back(Format::Noise); + } + } + break; + default: + break; + } + break; case Trace::DataType::Frequency: switch(type) { case Type::Manual: @@ -313,6 +346,7 @@ QString Marker::readableData(Format f) break; case Trace::DataType::Frequency: case Trace::DataType::Power: + case Trace::DataType::TimeZeroSpan: switch(type) { case Type::PeakTable: return "Found " + QString::number(helperMarkers.size()) + " peaks"; @@ -461,6 +495,7 @@ QString Marker::readablePosition() } switch(getDomain()) { case Trace::DataType::Time: + case Trace::DataType::TimeZeroSpan: ret += Unit::ToString(pos, "s", "pnum ", 6); break; case Trace::DataType::Frequency: @@ -487,6 +522,17 @@ QString Marker::readableSettings() return "Unhandled case"; } break; + case Trace::DataType::TimeZeroSpan: + switch(type) { + case Type::Manual: + case Type::Maximum: + case Type::Minimum: + case Type::Delta: + return Unit::ToString(position, "s", "fpnum ", 4); + default: + return "Unhandled case"; + } + break; case Trace::DataType::Frequency: switch(type) { case Type::Manual: @@ -539,6 +585,17 @@ QString Marker::tooltipSettings() break; } break; + case Trace::DataType::TimeZeroSpan: + switch(type) { + case Type::Manual: + case Type::Maximum: + case Type::Minimum: + case Type::Delta: + return "Time"; + default: + break; + } + break; case Trace::DataType::Frequency: switch(type) { case Type::Manual: @@ -829,6 +886,12 @@ std::set Marker::getSupportedTypes() } } break; + case Trace::DataType::TimeZeroSpan: + supported.insert(Type::Manual); + supported.insert(Type::Maximum); + supported.insert(Type::Minimum); + supported.insert(Type::Delta); + break; case Trace::DataType::Power: supported.insert(Type::Manual); supported.insert(Type::Maximum); @@ -1285,6 +1348,19 @@ SIUnitEdit *Marker::getSettingsEditor() return nullptr; } break; + case Trace::DataType::TimeZeroSpan: + switch(type) { + case Type::Manual: + case Type::Maximum: + case Type::Minimum: + case Type::Delta: + ret = new SIUnitEdit("", "fpnum k", 6); + ret->setValue(position); + break; + default: + return nullptr; + } + break; case Trace::DataType::Frequency: switch(type) { case Type::Manual: @@ -1347,6 +1423,19 @@ void Marker::adjustSettings(double value) setPosition(value); } } + break; + default: + break; + } + break; + case Trace::DataType::TimeZeroSpan: + switch(type) { + case Type::Manual: + case Type::Maximum: + case Type::Minimum: + case Type::Delta: + setPosition(value); + break; default: break; } diff --git a/Software/PC_Application/Traces/Math/tracemath.h b/Software/PC_Application/Traces/Math/tracemath.h index cce935f..e5de8f6 100644 --- a/Software/PC_Application/Traces/Math/tracemath.h +++ b/Software/PC_Application/Traces/Math/tracemath.h @@ -61,6 +61,7 @@ public: Frequency, Time, Power, + TimeZeroSpan, Invalid, }; diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index 1ff29c1..a038e1d 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -63,44 +63,54 @@ void Trace::clear() { emit outputSamplesChanged(0, 0); } -void Trace::addData(const Trace::Data& d, DataType domain, double reference_impedance) { +void Trace::addData(const Trace::Data& d, DataType domain, double reference_impedance, int index) { if(this->domain != domain) { clear(); this->domain = domain; emit typeChanged(this); } - // add or replace data in vector while keeping it sorted with increasing frequency - auto lower = lower_bound(data.begin(), data.end(), d, [](const Data &lhs, const Data &rhs) -> bool { - return lhs.x < rhs.x; - }); - // calculate index now because inserting a sample into data might lead to reallocation -> arithmetic on lower not valid anymore - auto index = lower - data.begin(); - if(lower == data.end()) { - // highest frequency yet, add to vector - data.push_back(d); - } else if(lower->x == d.x) { - switch(_liveType) { - case LivedataType::Overwrite: - // replace this data element - *lower = d; - break; - case LivedataType::MaxHold: - // replace this data element - if(abs(d.y) > abs(lower->y)) { - *lower = d; - } - break; - case LivedataType::MinHold: - // replace this data element - if(abs(d.y) < abs(lower->y)) { - *lower = d; - } - break; - default: break; + if(index >= 0) { + // index position specified + if(data.size() <= index) { + data.resize(index + 1); } + data[index] = d; } else { - // insert at this position - data.insert(lower, d); + // no index given, determine position by X-coordinate + + // add or replace data in vector while keeping it sorted with increasing frequency + auto lower = lower_bound(data.begin(), data.end(), d, [](const Data &lhs, const Data &rhs) -> bool { + return lhs.x < rhs.x; + }); + // calculate index now because inserting a sample into data might lead to reallocation -> arithmetic on lower not valid anymore + index = lower - data.begin(); + if(lower == data.end()) { + // highest frequency yet, add to vector + data.push_back(d); + } else if(lower->x == d.x) { + switch(_liveType) { + case LivedataType::Overwrite: + // replace this data element + *lower = d; + break; + case LivedataType::MaxHold: + // replace this data element + if(abs(d.y) > abs(lower->y)) { + *lower = d; + } + break; + case LivedataType::MinHold: + // replace this data element + if(abs(d.y) < abs(lower->y)) { + *lower = d; + } + break; + default: break; + } + } else { + // insert at this position + data.insert(lower, d); + } } if(this->reference_impedance != reference_impedance) { this->reference_impedance = reference_impedance; diff --git a/Software/PC_Application/Traces/trace.h b/Software/PC_Application/Traces/trace.h index 2576847..46ff999 100644 --- a/Software/PC_Application/Traces/trace.h +++ b/Software/PC_Application/Traces/trace.h @@ -45,7 +45,7 @@ public: }; void clear(); - void addData(const Data& d, DataType domain, double reference_impedance = 50.0); + void addData(const Data& d, DataType domain, double reference_impedance = 50.0, int index = -1); void addData(const Data& d, const Protocol::SpectrumAnalyzerSettings& s); void setName(QString name); void setVelocityFactor(double v); diff --git a/Software/PC_Application/Traces/traceaxis.cpp b/Software/PC_Application/Traces/traceaxis.cpp index 4ceee20..716b20f 100644 --- a/Software/PC_Application/Traces/traceaxis.cpp +++ b/Software/PC_Application/Traces/traceaxis.cpp @@ -324,6 +324,7 @@ std::set YAxis::getSupported(XAxis::Type type, TraceModel::DataSour switch(type) { case XAxis::Type::Frequency: case XAxis::Type::Power: + case XAxis::Type::TimeZeroSpan: ret.insert(YAxis::Type::Magnitude); ret.insert(YAxis::Type::MagnitudeLinear); ret.insert(YAxis::Type::Phase); @@ -489,6 +490,7 @@ QString XAxis::TypeToName(Type type) case Type::Time: return "Time"; case Type::Distance: return "Distance"; case Type::Power: return "Power"; + case Type::TimeZeroSpan: return "Time (Zero Span)"; default: return "Unknown"; } } @@ -511,6 +513,7 @@ QString XAxis::Unit(Type type) case Type::Time: return "s"; case Type::Distance: return "m"; case Type::Power: return "dBm"; + case Type::TimeZeroSpan: return "s"; default: return ""; } } diff --git a/Software/PC_Application/Traces/traceaxis.h b/Software/PC_Application/Traces/traceaxis.h index b832a79..1b2abe4 100644 --- a/Software/PC_Application/Traces/traceaxis.h +++ b/Software/PC_Application/Traces/traceaxis.h @@ -36,6 +36,7 @@ public: Time, Distance, Power, + TimeZeroSpan, Last, }; XAxis(); diff --git a/Software/PC_Application/Traces/tracemodel.cpp b/Software/PC_Application/Traces/tracemodel.cpp index 29bfe89..7f343cd 100644 --- a/Software/PC_Application/Traces/tracemodel.cpp +++ b/Software/PC_Application/Traces/tracemodel.cpp @@ -212,6 +212,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype) source = DataSource::VNA; for(auto t : traces) { if (t->isLive() && !t->isPaused()) { + int index = -1; Trace::Data td; switch(datatype) { case TraceMath::DataType::Frequency: @@ -220,6 +221,10 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype) case TraceMath::DataType::Power: td.x = (double) d.cdbm / 100.0; break; + case TraceMath::DataType::TimeZeroSpan: + td.x = d.time; + index = d.pointNum; + break; default: // invalid type, can not add return; @@ -233,7 +238,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype) // not a VNA trace, skip continue; } - t->addData(td, datatype, d.reference_impedance); + t->addData(td, datatype, d.reference_impedance, index); } } } diff --git a/Software/PC_Application/Traces/tracesmithchart.cpp b/Software/PC_Application/Traces/tracesmithchart.cpp index 4071bfa..2c03c22 100644 --- a/Software/PC_Application/Traces/tracesmithchart.cpp +++ b/Software/PC_Application/Traces/tracesmithchart.cpp @@ -196,9 +196,6 @@ QPoint TraceSmithChart::dataToPixel(std::complex d) QPoint TraceSmithChart::dataToPixel(Trace::Data d) { - if(d.x < sweep_fmin || d.x > sweep_fmax) { - return QPoint(); - } return dataToPixel(d.y);} std::complex TraceSmithChart::pixelToData(QPoint p) @@ -450,6 +447,7 @@ bool TraceSmithChart::dropSupported(Trace *t) switch(t->outputType()) { case Trace::DataType::Frequency: case Trace::DataType::Power: + case Trace::DataType::TimeZeroSpan: return true; default: return false; diff --git a/Software/PC_Application/Traces/tracexyplot.cpp b/Software/PC_Application/Traces/tracexyplot.cpp index ffb97ef..b28f77c 100644 --- a/Software/PC_Application/Traces/tracexyplot.cpp +++ b/Software/PC_Application/Traces/tracexyplot.cpp @@ -244,6 +244,11 @@ bool TraceXYPlot::configureForTrace(Trace *t) setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0); setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0); break; + case Trace::DataType::TimeZeroSpan: + setXAxis(XAxis::Type::TimeZeroSpan, XAxisMode::FitTraces, false, 0, 1, 0.1); + setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0); + setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0); + break; case Trace::DataType::Invalid: // unable to add return false; @@ -875,6 +880,8 @@ bool TraceXYPlot::domainMatch(Trace *t) return t->outputType() == Trace::DataType::Time; case XAxis::Type::Power: return t->outputType() == Trace::DataType::Power; + case XAxis::Type::TimeZeroSpan: + return t->outputType() == Trace::DataType::TimeZeroSpan; case XAxis::Type::Last: return false; } diff --git a/Software/PC_Application/VNA/vna.cpp b/Software/PC_Application/VNA/vna.cpp index e0ded1b..1ac96c9 100644 --- a/Software/PC_Application/VNA/vna.cpp +++ b/Software/PC_Application/VNA/vna.cpp @@ -66,6 +66,7 @@ VNA::VNA(AppWindow *window, QString name) calEdited = false; changingSettings = false; settings.sweepType = SweepType::Frequency; + settings.zerospan = false; traceModel.setSource(TraceModel::DataSource::VNA); @@ -304,6 +305,11 @@ VNA::VNA(AppWindow *window, QString name) connect(bFull, &QPushButton::clicked, this, &VNA::SetFullSpan); frequencySweepActions.push_back(tb_sweep->addWidget(bFull)); + auto bZero = new QPushButton(QIcon::fromTheme("zoom-fit-best", QIcon(":/icons/zoom-fit.png")), ""); + bZero->setToolTip("Zero span"); + connect(bZero, &QPushButton::clicked, this, &VNA::SetZeroSpan); + frequencySweepActions.push_back(tb_sweep->addWidget(bZero)); + auto bZoomIn = new QPushButton(QIcon::fromTheme("zoom-in", QIcon(":/icons/zoom-in.png")), ""); bZoomIn->setToolTip("Zoom in"); connect(bZoomIn, &QPushButton::clicked, this, &VNA::SpanZoomIn); @@ -837,7 +843,9 @@ void VNA::NewDatapoint(Protocol::Datapoint d) auto vd = VNAData(d); - vd = average.process(vd); + if(!settings.zerospan) { + vd = average.process(vd); + } if(calMeasuring) { if(average.currentSweep() == averages) { // this is the last averaging sweep, use values for calibration @@ -862,14 +870,26 @@ void VNA::NewDatapoint(Protocol::Datapoint d) } TraceMath::DataType type; - switch(settings.sweepType) { - case SweepType::Last: - case SweepType::Frequency: - type = TraceMath::DataType::Frequency; - break; - case SweepType::Power: - type = TraceMath::DataType::Power; - break; + if(settings.zerospan) { + type = TraceMath::DataType::TimeZeroSpan; + + // keep track of first point time + if(vd.pointNum == 0) { + settings.firstPointTime = vd.time; + vd.time = 0; + } else { + vd.time -= settings.firstPointTime; + } + } else { + switch(settings.sweepType) { + case SweepType::Last: + case SweepType::Frequency: + type = TraceMath::DataType::Frequency; + break; + case SweepType::Power: + type = TraceMath::DataType::Power; + break; + } } traceModel.addVNAData(vd, type); @@ -949,12 +969,12 @@ void VNA::SettingsChanged(bool resetTraces, std::functiongetDevice() && Mode::getActiveMode() == this) { @@ -1060,6 +1080,13 @@ void VNA::SetFullSpan() ConstrainAndUpdateFrequencies(); } +void VNA::SetZeroSpan() +{ + auto center = (settings.Freq.start + settings.Freq.stop) / 2; + SetStartFreq(center); + SetStopFreq(center); +} + void VNA::SpanZoomIn() { auto center = (settings.Freq.start + settings.Freq.stop) / 2; @@ -1108,14 +1135,14 @@ void VNA::SetStartPower(double level) { settings.Power.start = level; emit startPowerChanged(level); - SettingsChanged(); + ConstrainAndUpdateFrequencies(); } void VNA::SetStopPower(double level) { settings.Power.stop = level; emit stopPowerChanged(level); - SettingsChanged(); + ConstrainAndUpdateFrequencies(); } void VNA::SetPowerSweepFrequency(double freq) @@ -1327,6 +1354,11 @@ void VNA::SetupSCPI() SetFullSpan(); return ""; }, nullptr)); + scpi_freq->add(new SCPICommand("ZERO", [=](QStringList params) -> QString { + Q_UNUSED(params) + SetZeroSpan(); + return ""; + }, nullptr)); auto scpi_power = new SCPINode("POWer"); SCPINode::add(scpi_power); scpi_power->add(new SCPICommand("START", [=](QStringList params) -> QString { @@ -1527,6 +1559,8 @@ void VNA::ConstrainAndUpdateFrequencies() if(settings.Freq.start < Device::Info(window->getDevice()).limits_minFreq) { settings.Freq.start = Device::Info(window->getDevice()).limits_minFreq; } + settings.zerospan = (settings.sweepType == SweepType::Frequency && settings.Freq.start == settings.Freq.stop) + || (settings.sweepType == SweepType::Power && settings.Power.start == settings.Power.stop); emit startFreqChanged(settings.Freq.start); emit stopFreqChanged(settings.Freq.stop); emit spanChanged(settings.Freq.stop - settings.Freq.start); diff --git a/Software/PC_Application/VNA/vna.h b/Software/PC_Application/VNA/vna.h index 2f63cc3..a07f8b5 100644 --- a/Software/PC_Application/VNA/vna.h +++ b/Software/PC_Application/VNA/vna.h @@ -69,6 +69,8 @@ public: // if the number of points is higher than supported by the hardware, the sweep has to be segmented into multiple parts int segments; int activeSegment; + bool zerospan; + double firstPointTime; // timestamp of the first point in the sweep, only use when zerospan is used }; public slots: @@ -84,6 +86,7 @@ private slots: void SetCenterFreq(double freq); void SetSpan(double span); void SetFullSpan(); + void SetZeroSpan(); void SpanZoomIn(); void SpanZoomOut(); diff --git a/Software/PC_Application/VNA/vnadata.h b/Software/PC_Application/VNA/vnadata.h index 3031e8b..ad4aea9 100644 --- a/Software/PC_Application/VNA/vnadata.h +++ b/Software/PC_Application/VNA/vnadata.h @@ -14,11 +14,13 @@ public: std::complex(d.real_S21, d.imag_S21), std::complex(d.real_S22, d.imag_S22)); frequency = d.frequency; + time = (double) d.us / 1000000.0; cdbm = d.cdbm; pointNum = d.pointNum; reference_impedance = 50.0; } double frequency; + double time; int cdbm; Sparam S; unsigned int pointNum; diff --git a/Software/VNA_embedded/Application/App.cpp b/Software/VNA_embedded/Application/App.cpp index 72437a3..25e4322 100644 --- a/Software/VNA_embedded/Application/App.cpp +++ b/Software/VNA_embedded/Application/App.cpp @@ -48,6 +48,7 @@ static void USBPacketReceived(const Protocol::PacketInfo &p) { inline void App_Init() { STM::Init(); + Delay::Init(); HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); handle = xTaskGetCurrentTaskHandle(); usb_init(communication_usb_input); diff --git a/Software/VNA_embedded/Application/Communication/Protocol.hpp b/Software/VNA_embedded/Application/Communication/Protocol.hpp index 1a7dc56..4aa34ba 100644 --- a/Software/VNA_embedded/Application/Communication/Protocol.hpp +++ b/Software/VNA_embedded/Application/Communication/Protocol.hpp @@ -13,9 +13,18 @@ using Datapoint = struct _datapoint { float real_S21, imag_S21; float real_S12, imag_S12; float real_S22, imag_S22; - uint64_t frequency; - int16_t cdbm; - uint16_t pointNum; + union { + struct { + // for non-zero span + uint64_t frequency; + int16_t cdbm; + }; + struct { + // for zero span + uint64_t us; // time in us since first datapoint + }; + }; + uint16_t pointNum; }; using SweepSettings = struct _sweepSettings { diff --git a/Software/VNA_embedded/Application/Drivers/delay.cpp b/Software/VNA_embedded/Application/Drivers/delay.cpp index f04b35f..29ad975 100644 --- a/Software/VNA_embedded/Application/Drivers/delay.cpp +++ b/Software/VNA_embedded/Application/Drivers/delay.cpp @@ -2,15 +2,51 @@ #include "stm.hpp" +static volatile uint64_t t_us = 0; + +void Delay::Init() { + TIM1->CNT = 0; + + // enable update interrupt + TIM1->DIER |= TIM_DIER_UIE; + HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); + + TIM1->CR1 |= TIM_CR1_CEN; +} + +uint64_t Delay::get_us() { + HAL_NVIC_DisableIRQ(TIM1_UP_TIM16_IRQn); + uint32_t buf = TIM1->CNT; + uint16_t timer_value = buf & 0xFFFF; + uint64_t ret; + if(buf & 0x80000000) { + // UIF bit set, timer overflow not handled yet + ret = t_us + UINT16_MAX + timer_value; + } else { + ret = t_us + timer_value; + } + HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); + return ret; +} + void Delay::ms(uint32_t t) { while(t--) { us(1000); } } void Delay::us(uint32_t t) { - TIM1->CNT = 0; - TIM1->CR1 |= TIM_CR1_CEN; - while (TIM1->CNT < t) - ; - TIM1->CR1 &= ~TIM_CR1_CEN; + uint64_t start = get_us(); + while(start + t > get_us()); +} + +extern "C" { + +void TIM1_UP_TIM16_IRQHandler() { + // clear bit + TIM1->SR &= ~TIM_SR_UIF; + // update count + t_us += UINT16_MAX; +} + } diff --git a/Software/VNA_embedded/Application/Drivers/delay.hpp b/Software/VNA_embedded/Application/Drivers/delay.hpp index 6911b8f..27e8492 100644 --- a/Software/VNA_embedded/Application/Drivers/delay.hpp +++ b/Software/VNA_embedded/Application/Drivers/delay.hpp @@ -4,6 +4,10 @@ namespace Delay { +void Init(); + +uint64_t get_us(); + void ms(uint32_t t); void us(uint32_t t); diff --git a/Software/VNA_embedded/Application/Hardware.cpp b/Software/VNA_embedded/Application/Hardware.cpp index ab3bd90..36110d6 100644 --- a/Software/VNA_embedded/Application/Hardware.cpp +++ b/Software/VNA_embedded/Application/Hardware.cpp @@ -6,6 +6,7 @@ #include "Exti.hpp" #include "VNA.hpp" #include "Manual.hpp" +#include "delay.hpp" #include "SpectrumAnalyzer.hpp" #include @@ -19,7 +20,7 @@ HW::Mode activeMode; static bool unlevel = false; static Protocol::ReferenceSettings ref; -static uint32_t lastISR; +static uint64_t lastISR; static uint32_t IF1 = HW::DefaultIF1; static uint32_t IF2 = HW::DefaultIF2; @@ -60,8 +61,8 @@ static void ReadComplete(const FPGA::SamplingResult &result) { } static void FPGA_Interrupt(void*) { + lastISR = Delay::get_us(); FPGA::InitiateSampleRead(ReadComplete); - lastISR = HAL_GetTick(); } void HW::Work() { @@ -209,7 +210,7 @@ void HW::SetMode(Mode mode) { if(mode != Mode::Idle) { // do a full initialization when switching directly between modes HW::Init(); - lastISR = HAL_GetTick(); + lastISR = Delay::get_us(); } SetIdle(); activeMode = mode; @@ -287,8 +288,8 @@ HW::AmplitudeSettings HW::GetAmplitudeSettings(int16_t cdbm, uint64_t freq, bool } bool HW::TimedOut() { - constexpr uint32_t timeout = 1000; - if(activeMode != Mode::Idle && HAL_GetTick() - lastISR > timeout) { + constexpr uint64_t timeout = 1000000; + if(activeMode != Mode::Idle && Delay::get_us() - lastISR > timeout) { return true; } else { return false; @@ -415,6 +416,10 @@ uint8_t HW::getADCPrescaler() { return ADCprescaler; } +uint64_t HW::getLastISRTimestamp() { + return lastISR; +} + uint16_t HW::getDFTPhaseInc() { return DFTphaseInc; } diff --git a/Software/VNA_embedded/Application/Hardware.hpp b/Software/VNA_embedded/Application/Hardware.hpp index c0ce9ff..e81a26f 100644 --- a/Software/VNA_embedded/Application/Hardware.hpp +++ b/Software/VNA_embedded/Application/Hardware.hpp @@ -97,6 +97,7 @@ void SetMode(Mode mode); void SetIdle(); void Work(); bool TimedOut(); +uint64_t getLastISRTimestamp(); void SetOutputUnlevel(bool unlev); diff --git a/Software/VNA_embedded/Application/VNA.cpp b/Software/VNA_embedded/Application/VNA.cpp index 52cf778..08e0a97 100644 --- a/Software/VNA_embedded/Application/VNA.cpp +++ b/Software/VNA_embedded/Application/VNA.cpp @@ -30,6 +30,10 @@ static Si5351C::DriveStrength fixedPowerLowband; static bool adcShifted; static uint32_t actualBandwidth; +static uint64_t firstPointTime; +static bool firstPoint; +static bool zerospan; + static constexpr uint8_t sourceHarmonic = 5; static constexpr uint8_t LOHarmonic = 3; @@ -140,6 +144,8 @@ bool VNA::Setup(Protocol::SweepSettings s) { IFTableIndexCnt = 0; + zerospan = (s.f_start == s.f_stop) && (s.cdbm_excitation_start == s.cdbm_excitation_stop); + bool last_lowband = false; // invalidate first entry of IFTable, preventing switing of 2.LO in halted callback @@ -302,6 +308,7 @@ bool VNA::Setup(Protocol::SweepSettings s) { FPGA::EnableInterrupt(FPGA::Interrupt::NewData); FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted); // Start the sweep + firstPoint = true; FPGA::StartSweep(); return true; } @@ -329,8 +336,20 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) { auto port1 = port1_raw / ref; auto port2 = port2_raw / ref; 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(zerospan) { + uint64_t timestamp = HW::getLastISRTimestamp(); + if(firstPoint) { + data.us = 0; + firstPointTime = timestamp; + firstPoint = false; + } else { + data.us = timestamp - firstPointTime; + } + } else { + // non-zero span, set frequency/power + data.frequency = getPointFrequency(pointCnt); + data.cdbm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1); + } if(stageCnt == 0 && settings.excitePort1) { // stimulus is present at port 1 data.real_S11 = port1.real(); diff --git a/Software/VNA_embedded/VNA_embedded.ioc b/Software/VNA_embedded/VNA_embedded.ioc index b2ba0ff..586e12a 100644 --- a/Software/VNA_embedded/VNA_embedded.ioc +++ b/Software/VNA_embedded/VNA_embedded.ioc @@ -322,7 +322,7 @@ ProjectManager.StackSize=0x400 ProjectManager.TargetToolchain=STM32CubeIDE ProjectManager.ToolChainLocation= ProjectManager.UnderRoot=true -ProjectManager.functionlistsort=1-MX_GPIO_Init-GPIO-false-HAL-true,2-MX_DMA_Init-DMA-false-HAL-true,3-SystemClock_Config-RCC-false-HAL-false,4-MX_I2C2_Init-I2C2-false-HAL-true,5-MX_SPI1_Init-SPI1-false-HAL-true,6-MX_SPI2_Init-SPI2-false-HAL-true,7-MX_UCPD1_Init-UCPD1-false-LL-true,8-MX_USART3_UART_Init-USART3-false-HAL-true,9-MX_USB_PCD_Init-USB-false-HAL-true,10-MX_USBPD_Init-USBPD-false-HAL-false,11-MX_TIM1_Init-TIM1-false-HAL-true,12-MX_TIM2_Init-TIM2-false-HAL-true,13-MX_ADC1_Init-ADC1-false-HAL-true +ProjectManager.functionlistsort=1-MX_GPIO_Init-GPIO-false-HAL-true,2-MX_DMA_Init-DMA-false-HAL-true,3-SystemClock_Config-RCC-false-HAL-false,4-MX_I2C2_Init-I2C2-false-HAL-true,5-MX_SPI1_Init-SPI1-false-HAL-true,6-MX_SPI2_Init-SPI2-false-HAL-true,7-MX_UCPD1_Init-UCPD1-false-LL-true,8-MX_USART3_UART_Init-USART3-false-HAL-true,9-MX_USB_PCD_Init-USB-false-HAL-true,10-MX_TIM1_Init-TIM1-false-HAL-true,11-MX_TIM2_Init-TIM2-false-HAL-true,12-MX_ADC1_Init-ADC1-false-HAL-true RCC.ADC12Freq_Value=160000000 RCC.AHBFreq_Value=160000000 RCC.APB1Freq_Value=160000000 @@ -387,8 +387,7 @@ SPI2.Direction=SPI_DIRECTION_2LINES SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler,DataSize SPI2.Mode=SPI_MODE_MASTER SPI2.VirtualType=VM_MASTER -TIM1.IPParameters=Prescaler,PeriodNoDither -TIM1.PeriodNoDither=65535 +TIM1.IPParameters=Prescaler TIM1.Prescaler=159 TIM2.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1 TIM2.IPParameters=Channel-PWM Generation1 CH1,Prescaler,PeriodNoDither,OCMode_PWM-PWM Generation1 CH1