WIP: preparation for zero span mode

This commit is contained in:
Jan Käberich 2022-06-20 01:02:09 +02:00
parent e8560e1a67
commit 6393ae7fc3
22 changed files with 299 additions and 69 deletions

View File

@ -341,6 +341,9 @@ These commands change or query VNA settings. Although most of them are available
\subsubsection{VNA:FREQuency:FULL} \subsubsection{VNA:FREQuency:FULL}
\event{Sets the device to the maximum span possible}{VNA:FREQuency:FULL}{None} \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} \subsubsection{VNA:POWer:START}
\event{Sets the start power of the power sweep}{VNA:POWer:START}{<start power>, in dBm} \event{Sets the start power of the power sweep}{VNA:POWer:START}{<start power>, in dBm}
\query{Queries the currently selected start power}{VNA:POWer:START?}{None}{start power in dBm} \query{Queries the currently selected start power}{VNA:POWer:START?}{None}{start power in dBm}

View File

@ -174,6 +174,39 @@ std::vector<Marker::Format> Marker::applicableFormats()
break; break;
} }
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: case Trace::DataType::Frequency:
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
@ -313,6 +346,7 @@ QString Marker::readableData(Format f)
break; break;
case Trace::DataType::Frequency: case Trace::DataType::Frequency:
case Trace::DataType::Power: case Trace::DataType::Power:
case Trace::DataType::TimeZeroSpan:
switch(type) { switch(type) {
case Type::PeakTable: case Type::PeakTable:
return "Found " + QString::number(helperMarkers.size()) + " peaks"; return "Found " + QString::number(helperMarkers.size()) + " peaks";
@ -461,6 +495,7 @@ QString Marker::readablePosition()
} }
switch(getDomain()) { switch(getDomain()) {
case Trace::DataType::Time: case Trace::DataType::Time:
case Trace::DataType::TimeZeroSpan:
ret += Unit::ToString(pos, "s", "pnum ", 6); ret += Unit::ToString(pos, "s", "pnum ", 6);
break; break;
case Trace::DataType::Frequency: case Trace::DataType::Frequency:
@ -487,6 +522,17 @@ QString Marker::readableSettings()
return "Unhandled case"; return "Unhandled case";
} }
break; 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: case Trace::DataType::Frequency:
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
@ -539,6 +585,17 @@ QString Marker::tooltipSettings()
break; break;
} }
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: case Trace::DataType::Frequency:
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
@ -829,6 +886,12 @@ std::set<Marker::Type> Marker::getSupportedTypes()
} }
} }
break; 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: case Trace::DataType::Power:
supported.insert(Type::Manual); supported.insert(Type::Manual);
supported.insert(Type::Maximum); supported.insert(Type::Maximum);
@ -1285,6 +1348,19 @@ SIUnitEdit *Marker::getSettingsEditor()
return nullptr; return nullptr;
} }
break; 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: case Trace::DataType::Frequency:
switch(type) { switch(type) {
case Type::Manual: case Type::Manual:
@ -1347,6 +1423,19 @@ void Marker::adjustSettings(double value)
setPosition(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: default:
break; break;
} }

View File

@ -61,6 +61,7 @@ public:
Frequency, Frequency,
Time, Time,
Power, Power,
TimeZeroSpan,
Invalid, Invalid,
}; };

View File

@ -63,18 +63,27 @@ void Trace::clear() {
emit outputSamplesChanged(0, 0); 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) { if(this->domain != domain) {
clear(); clear();
this->domain = domain; this->domain = domain;
emit typeChanged(this); emit typeChanged(this);
} }
if(index >= 0) {
// index position specified
if(data.size() <= index) {
data.resize(index + 1);
}
data[index] = d;
} else {
// no index given, determine position by X-coordinate
// add or replace data in vector while keeping it sorted with increasing frequency // 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 { auto lower = lower_bound(data.begin(), data.end(), d, [](const Data &lhs, const Data &rhs) -> bool {
return lhs.x < rhs.x; return lhs.x < rhs.x;
}); });
// calculate index now because inserting a sample into data might lead to reallocation -> arithmetic on lower not valid anymore // calculate index now because inserting a sample into data might lead to reallocation -> arithmetic on lower not valid anymore
auto index = lower - data.begin(); index = lower - data.begin();
if(lower == data.end()) { if(lower == data.end()) {
// highest frequency yet, add to vector // highest frequency yet, add to vector
data.push_back(d); data.push_back(d);
@ -102,6 +111,7 @@ void Trace::addData(const Trace::Data& d, DataType domain, double reference_impe
// insert at this position // insert at this position
data.insert(lower, d); data.insert(lower, d);
} }
}
if(this->reference_impedance != reference_impedance) { if(this->reference_impedance != reference_impedance) {
this->reference_impedance = reference_impedance; this->reference_impedance = reference_impedance;
emit typeChanged(this); emit typeChanged(this);

View File

@ -45,7 +45,7 @@ public:
}; };
void clear(); 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 addData(const Data& d, const Protocol::SpectrumAnalyzerSettings& s);
void setName(QString name); void setName(QString name);
void setVelocityFactor(double v); void setVelocityFactor(double v);

View File

@ -324,6 +324,7 @@ std::set<YAxis::Type> YAxis::getSupported(XAxis::Type type, TraceModel::DataSour
switch(type) { switch(type) {
case XAxis::Type::Frequency: case XAxis::Type::Frequency:
case XAxis::Type::Power: case XAxis::Type::Power:
case XAxis::Type::TimeZeroSpan:
ret.insert(YAxis::Type::Magnitude); ret.insert(YAxis::Type::Magnitude);
ret.insert(YAxis::Type::MagnitudeLinear); ret.insert(YAxis::Type::MagnitudeLinear);
ret.insert(YAxis::Type::Phase); ret.insert(YAxis::Type::Phase);
@ -489,6 +490,7 @@ QString XAxis::TypeToName(Type type)
case Type::Time: return "Time"; case Type::Time: return "Time";
case Type::Distance: return "Distance"; case Type::Distance: return "Distance";
case Type::Power: return "Power"; case Type::Power: return "Power";
case Type::TimeZeroSpan: return "Time (Zero Span)";
default: return "Unknown"; default: return "Unknown";
} }
} }
@ -511,6 +513,7 @@ QString XAxis::Unit(Type type)
case Type::Time: return "s"; case Type::Time: return "s";
case Type::Distance: return "m"; case Type::Distance: return "m";
case Type::Power: return "dBm"; case Type::Power: return "dBm";
case Type::TimeZeroSpan: return "s";
default: return ""; default: return "";
} }
} }

View File

@ -36,6 +36,7 @@ public:
Time, Time,
Distance, Distance,
Power, Power,
TimeZeroSpan,
Last, Last,
}; };
XAxis(); XAxis();

View File

@ -212,6 +212,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype)
source = DataSource::VNA; source = DataSource::VNA;
for(auto t : traces) { for(auto t : traces) {
if (t->isLive() && !t->isPaused()) { if (t->isLive() && !t->isPaused()) {
int index = -1;
Trace::Data td; Trace::Data td;
switch(datatype) { switch(datatype) {
case TraceMath::DataType::Frequency: case TraceMath::DataType::Frequency:
@ -220,6 +221,10 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype)
case TraceMath::DataType::Power: case TraceMath::DataType::Power:
td.x = (double) d.cdbm / 100.0; td.x = (double) d.cdbm / 100.0;
break; break;
case TraceMath::DataType::TimeZeroSpan:
td.x = d.time;
index = d.pointNum;
break;
default: default:
// invalid type, can not add // invalid type, can not add
return; return;
@ -233,7 +238,7 @@ void TraceModel::addVNAData(const VNAData& d, TraceMath::DataType datatype)
// not a VNA trace, skip // not a VNA trace, skip
continue; continue;
} }
t->addData(td, datatype, d.reference_impedance); t->addData(td, datatype, d.reference_impedance, index);
} }
} }
} }

View File

@ -196,9 +196,6 @@ QPoint TraceSmithChart::dataToPixel(std::complex<double> d)
QPoint TraceSmithChart::dataToPixel(Trace::Data d) QPoint TraceSmithChart::dataToPixel(Trace::Data d)
{ {
if(d.x < sweep_fmin || d.x > sweep_fmax) {
return QPoint();
}
return dataToPixel(d.y);} return dataToPixel(d.y);}
std::complex<double> TraceSmithChart::pixelToData(QPoint p) std::complex<double> TraceSmithChart::pixelToData(QPoint p)
@ -450,6 +447,7 @@ bool TraceSmithChart::dropSupported(Trace *t)
switch(t->outputType()) { switch(t->outputType()) {
case Trace::DataType::Frequency: case Trace::DataType::Frequency:
case Trace::DataType::Power: case Trace::DataType::Power:
case Trace::DataType::TimeZeroSpan:
return true; return true;
default: default:
return false; return false;

View File

@ -244,6 +244,11 @@ bool TraceXYPlot::configureForTrace(Trace *t)
setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0); setYAxis(0, YAxis::Type::Magnitude, false, true, 0, 1, 1.0);
setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0); setYAxis(1, YAxis::Type::Phase, false, true, 0, 1, 1.0);
break; 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: case Trace::DataType::Invalid:
// unable to add // unable to add
return false; return false;
@ -875,6 +880,8 @@ bool TraceXYPlot::domainMatch(Trace *t)
return t->outputType() == Trace::DataType::Time; return t->outputType() == Trace::DataType::Time;
case XAxis::Type::Power: case XAxis::Type::Power:
return t->outputType() == Trace::DataType::Power; return t->outputType() == Trace::DataType::Power;
case XAxis::Type::TimeZeroSpan:
return t->outputType() == Trace::DataType::TimeZeroSpan;
case XAxis::Type::Last: case XAxis::Type::Last:
return false; return false;
} }

View File

@ -66,6 +66,7 @@ VNA::VNA(AppWindow *window, QString name)
calEdited = false; calEdited = false;
changingSettings = false; changingSettings = false;
settings.sweepType = SweepType::Frequency; settings.sweepType = SweepType::Frequency;
settings.zerospan = false;
traceModel.setSource(TraceModel::DataSource::VNA); traceModel.setSource(TraceModel::DataSource::VNA);
@ -304,6 +305,11 @@ VNA::VNA(AppWindow *window, QString name)
connect(bFull, &QPushButton::clicked, this, &VNA::SetFullSpan); connect(bFull, &QPushButton::clicked, this, &VNA::SetFullSpan);
frequencySweepActions.push_back(tb_sweep->addWidget(bFull)); 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")), ""); auto bZoomIn = new QPushButton(QIcon::fromTheme("zoom-in", QIcon(":/icons/zoom-in.png")), "");
bZoomIn->setToolTip("Zoom in"); bZoomIn->setToolTip("Zoom in");
connect(bZoomIn, &QPushButton::clicked, this, &VNA::SpanZoomIn); connect(bZoomIn, &QPushButton::clicked, this, &VNA::SpanZoomIn);
@ -837,7 +843,9 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
auto vd = VNAData(d); auto vd = VNAData(d);
if(!settings.zerospan) {
vd = average.process(vd); vd = average.process(vd);
}
if(calMeasuring) { if(calMeasuring) {
if(average.currentSweep() == averages) { if(average.currentSweep() == averages) {
// this is the last averaging sweep, use values for calibration // this is the last averaging sweep, use values for calibration
@ -862,6 +870,17 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
} }
TraceMath::DataType type; TraceMath::DataType type;
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) { switch(settings.sweepType) {
case SweepType::Last: case SweepType::Last:
case SweepType::Frequency: case SweepType::Frequency:
@ -871,6 +890,7 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
type = TraceMath::DataType::Power; type = TraceMath::DataType::Power;
break; break;
} }
}
traceModel.addVNAData(vd, type); traceModel.addVNAData(vd, type);
emit dataChanged(); emit dataChanged();
@ -949,12 +969,12 @@ void VNA::SettingsChanged(bool resetTraces, std::function<void (Device::Transmis
s.logSweep = settings.Freq.logSweep; s.logSweep = settings.Freq.logSweep;
} else if(settings.sweepType == SweepType::Power) { } else if(settings.sweepType == SweepType::Power) {
s.fixedPowerSetting = 0; s.fixedPowerSetting = 0;
s.f_start = start; s.f_start = settings.Power.frequency;
s.f_stop = stop; s.f_stop = settings.Power.frequency;
s.points = npoints; s.points = npoints;
s.if_bandwidth = settings.bandwidth; s.if_bandwidth = settings.bandwidth;
s.cdbm_excitation_start = settings.Power.start * 100; s.cdbm_excitation_start = start * 100;
s.cdbm_excitation_stop = settings.Power.stop * 100; s.cdbm_excitation_stop = stop * 100;
s.logSweep = false; s.logSweep = false;
} }
if(window->getDevice() && Mode::getActiveMode() == this) { if(window->getDevice() && Mode::getActiveMode() == this) {
@ -1060,6 +1080,13 @@ void VNA::SetFullSpan()
ConstrainAndUpdateFrequencies(); ConstrainAndUpdateFrequencies();
} }
void VNA::SetZeroSpan()
{
auto center = (settings.Freq.start + settings.Freq.stop) / 2;
SetStartFreq(center);
SetStopFreq(center);
}
void VNA::SpanZoomIn() void VNA::SpanZoomIn()
{ {
auto center = (settings.Freq.start + settings.Freq.stop) / 2; auto center = (settings.Freq.start + settings.Freq.stop) / 2;
@ -1108,14 +1135,14 @@ void VNA::SetStartPower(double level)
{ {
settings.Power.start = level; settings.Power.start = level;
emit startPowerChanged(level); emit startPowerChanged(level);
SettingsChanged(); ConstrainAndUpdateFrequencies();
} }
void VNA::SetStopPower(double level) void VNA::SetStopPower(double level)
{ {
settings.Power.stop = level; settings.Power.stop = level;
emit stopPowerChanged(level); emit stopPowerChanged(level);
SettingsChanged(); ConstrainAndUpdateFrequencies();
} }
void VNA::SetPowerSweepFrequency(double freq) void VNA::SetPowerSweepFrequency(double freq)
@ -1327,6 +1354,11 @@ void VNA::SetupSCPI()
SetFullSpan(); SetFullSpan();
return ""; return "";
}, nullptr)); }, nullptr));
scpi_freq->add(new SCPICommand("ZERO", [=](QStringList params) -> QString {
Q_UNUSED(params)
SetZeroSpan();
return "";
}, nullptr));
auto scpi_power = new SCPINode("POWer"); auto scpi_power = new SCPINode("POWer");
SCPINode::add(scpi_power); SCPINode::add(scpi_power);
scpi_power->add(new SCPICommand("START", [=](QStringList params) -> QString { 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) { if(settings.Freq.start < Device::Info(window->getDevice()).limits_minFreq) {
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 startFreqChanged(settings.Freq.start);
emit stopFreqChanged(settings.Freq.stop); emit stopFreqChanged(settings.Freq.stop);
emit spanChanged(settings.Freq.stop - settings.Freq.start); emit spanChanged(settings.Freq.stop - settings.Freq.start);

View File

@ -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 // if the number of points is higher than supported by the hardware, the sweep has to be segmented into multiple parts
int segments; int segments;
int activeSegment; int activeSegment;
bool zerospan;
double firstPointTime; // timestamp of the first point in the sweep, only use when zerospan is used
}; };
public slots: public slots:
@ -84,6 +86,7 @@ private slots:
void SetCenterFreq(double freq); void SetCenterFreq(double freq);
void SetSpan(double span); void SetSpan(double span);
void SetFullSpan(); void SetFullSpan();
void SetZeroSpan();
void SpanZoomIn(); void SpanZoomIn();
void SpanZoomOut(); void SpanZoomOut();

View File

@ -14,11 +14,13 @@ public:
std::complex<double>(d.real_S21, d.imag_S21), std::complex<double>(d.real_S21, d.imag_S21),
std::complex<double>(d.real_S22, d.imag_S22)); std::complex<double>(d.real_S22, d.imag_S22));
frequency = d.frequency; frequency = d.frequency;
time = (double) d.us / 1000000.0;
cdbm = d.cdbm; cdbm = d.cdbm;
pointNum = d.pointNum; pointNum = d.pointNum;
reference_impedance = 50.0; reference_impedance = 50.0;
} }
double frequency; double frequency;
double time;
int cdbm; int cdbm;
Sparam S; Sparam S;
unsigned int pointNum; unsigned int pointNum;

View File

@ -48,6 +48,7 @@ static void USBPacketReceived(const Protocol::PacketInfo &p) {
inline void App_Init() { inline void App_Init() {
STM::Init(); STM::Init();
Delay::Init();
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
handle = xTaskGetCurrentTaskHandle(); handle = xTaskGetCurrentTaskHandle();
usb_init(communication_usb_input); usb_init(communication_usb_input);

View File

@ -13,8 +13,17 @@ using Datapoint = struct _datapoint {
float real_S21, imag_S21; float real_S21, imag_S21;
float real_S12, imag_S12; float real_S12, imag_S12;
float real_S22, imag_S22; float real_S22, imag_S22;
union {
struct {
// for non-zero span
uint64_t frequency; uint64_t frequency;
int16_t cdbm; int16_t cdbm;
};
struct {
// for zero span
uint64_t us; // time in us since first datapoint
};
};
uint16_t pointNum; uint16_t pointNum;
}; };

View File

@ -2,15 +2,51 @@
#include "stm.hpp" #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) { void Delay::ms(uint32_t t) {
while(t--) { while(t--) {
us(1000); us(1000);
} }
} }
void Delay::us(uint32_t t) { void Delay::us(uint32_t t) {
TIM1->CNT = 0; uint64_t start = get_us();
TIM1->CR1 |= TIM_CR1_CEN; while(start + t > get_us());
while (TIM1->CNT < t) }
;
TIM1->CR1 &= ~TIM_CR1_CEN; extern "C" {
void TIM1_UP_TIM16_IRQHandler() {
// clear bit
TIM1->SR &= ~TIM_SR_UIF;
// update count
t_us += UINT16_MAX;
}
} }

View File

@ -4,6 +4,10 @@
namespace Delay { namespace Delay {
void Init();
uint64_t get_us();
void ms(uint32_t t); void ms(uint32_t t);
void us(uint32_t t); void us(uint32_t t);

View File

@ -6,6 +6,7 @@
#include "Exti.hpp" #include "Exti.hpp"
#include "VNA.hpp" #include "VNA.hpp"
#include "Manual.hpp" #include "Manual.hpp"
#include "delay.hpp"
#include "SpectrumAnalyzer.hpp" #include "SpectrumAnalyzer.hpp"
#include <cstring> #include <cstring>
@ -19,7 +20,7 @@ HW::Mode activeMode;
static bool unlevel = false; static bool unlevel = false;
static Protocol::ReferenceSettings ref; static Protocol::ReferenceSettings ref;
static uint32_t lastISR; static uint64_t lastISR;
static uint32_t IF1 = HW::DefaultIF1; static uint32_t IF1 = HW::DefaultIF1;
static uint32_t IF2 = HW::DefaultIF2; static uint32_t IF2 = HW::DefaultIF2;
@ -60,8 +61,8 @@ static void ReadComplete(const FPGA::SamplingResult &result) {
} }
static void FPGA_Interrupt(void*) { static void FPGA_Interrupt(void*) {
lastISR = Delay::get_us();
FPGA::InitiateSampleRead(ReadComplete); FPGA::InitiateSampleRead(ReadComplete);
lastISR = HAL_GetTick();
} }
void HW::Work() { void HW::Work() {
@ -209,7 +210,7 @@ void HW::SetMode(Mode mode) {
if(mode != Mode::Idle) { if(mode != Mode::Idle) {
// do a full initialization when switching directly between modes // do a full initialization when switching directly between modes
HW::Init(); HW::Init();
lastISR = HAL_GetTick(); lastISR = Delay::get_us();
} }
SetIdle(); SetIdle();
activeMode = mode; activeMode = mode;
@ -287,8 +288,8 @@ HW::AmplitudeSettings HW::GetAmplitudeSettings(int16_t cdbm, uint64_t freq, bool
} }
bool HW::TimedOut() { bool HW::TimedOut() {
constexpr uint32_t timeout = 1000; constexpr uint64_t timeout = 1000000;
if(activeMode != Mode::Idle && HAL_GetTick() - lastISR > timeout) { if(activeMode != Mode::Idle && Delay::get_us() - lastISR > timeout) {
return true; return true;
} else { } else {
return false; return false;
@ -415,6 +416,10 @@ uint8_t HW::getADCPrescaler() {
return ADCprescaler; return ADCprescaler;
} }
uint64_t HW::getLastISRTimestamp() {
return lastISR;
}
uint16_t HW::getDFTPhaseInc() { uint16_t HW::getDFTPhaseInc() {
return DFTphaseInc; return DFTphaseInc;
} }

View File

@ -97,6 +97,7 @@ void SetMode(Mode mode);
void SetIdle(); void SetIdle();
void Work(); void Work();
bool TimedOut(); bool TimedOut();
uint64_t getLastISRTimestamp();
void SetOutputUnlevel(bool unlev); void SetOutputUnlevel(bool unlev);

View File

@ -30,6 +30,10 @@ static Si5351C::DriveStrength fixedPowerLowband;
static bool adcShifted; static bool adcShifted;
static uint32_t actualBandwidth; static uint32_t actualBandwidth;
static uint64_t firstPointTime;
static bool firstPoint;
static bool zerospan;
static constexpr uint8_t sourceHarmonic = 5; static constexpr uint8_t sourceHarmonic = 5;
static constexpr uint8_t LOHarmonic = 3; static constexpr uint8_t LOHarmonic = 3;
@ -140,6 +144,8 @@ bool VNA::Setup(Protocol::SweepSettings s) {
IFTableIndexCnt = 0; IFTableIndexCnt = 0;
zerospan = (s.f_start == s.f_stop) && (s.cdbm_excitation_start == s.cdbm_excitation_stop);
bool last_lowband = false; bool last_lowband = false;
// invalidate first entry of IFTable, preventing switing of 2.LO in halted callback // 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::NewData);
FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted); FPGA::EnableInterrupt(FPGA::Interrupt::SweepHalted);
// Start the sweep // Start the sweep
firstPoint = true;
FPGA::StartSweep(); FPGA::StartSweep();
return true; return true;
} }
@ -329,8 +336,20 @@ bool VNA::MeasurementDone(const FPGA::SamplingResult &result) {
auto port1 = port1_raw / ref; auto port1 = port1_raw / ref;
auto port2 = port2_raw / ref; auto port2 = port2_raw / ref;
data.pointNum = pointCnt; data.pointNum = pointCnt;
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.frequency = getPointFrequency(pointCnt);
data.cdbm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1); data.cdbm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1);
}
if(stageCnt == 0 && settings.excitePort1) { if(stageCnt == 0 && settings.excitePort1) {
// stimulus is present at port 1 // stimulus is present at port 1
data.real_S11 = port1.real(); data.real_S11 = port1.real();

View File

@ -322,7 +322,7 @@ ProjectManager.StackSize=0x400
ProjectManager.TargetToolchain=STM32CubeIDE ProjectManager.TargetToolchain=STM32CubeIDE
ProjectManager.ToolChainLocation= ProjectManager.ToolChainLocation=
ProjectManager.UnderRoot=true 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.ADC12Freq_Value=160000000
RCC.AHBFreq_Value=160000000 RCC.AHBFreq_Value=160000000
RCC.APB1Freq_Value=160000000 RCC.APB1Freq_Value=160000000
@ -387,8 +387,7 @@ SPI2.Direction=SPI_DIRECTION_2LINES
SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler,DataSize SPI2.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler,DataSize
SPI2.Mode=SPI_MODE_MASTER SPI2.Mode=SPI_MODE_MASTER
SPI2.VirtualType=VM_MASTER SPI2.VirtualType=VM_MASTER
TIM1.IPParameters=Prescaler,PeriodNoDither TIM1.IPParameters=Prescaler
TIM1.PeriodNoDither=65535
TIM1.Prescaler=159 TIM1.Prescaler=159
TIM2.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1 TIM2.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
TIM2.IPParameters=Channel-PWM Generation1 CH1,Prescaler,PeriodNoDither,OCMode_PWM-PWM Generation1 CH1 TIM2.IPParameters=Channel-PWM Generation1 CH1,Prescaler,PeriodNoDither,OCMode_PWM-PWM Generation1 CH1