WIP: preparation for zero span mode
This commit is contained in:
parent
e8560e1a67
commit
6393ae7fc3
Binary file not shown.
@ -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}{<start power>, in dBm}
|
||||
\query{Queries the currently selected start power}{VNA:POWer:START?}{None}{start power in dBm}
|
||||
|
@ -174,6 +174,39 @@ std::vector<Marker::Format> 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::Type> 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;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ public:
|
||||
Frequency,
|
||||
Time,
|
||||
Power,
|
||||
TimeZeroSpan,
|
||||
Invalid,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -324,6 +324,7 @@ std::set<YAxis::Type> 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 "";
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ public:
|
||||
Time,
|
||||
Distance,
|
||||
Power,
|
||||
TimeZeroSpan,
|
||||
Last,
|
||||
};
|
||||
XAxis();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -196,9 +196,6 @@ QPoint TraceSmithChart::dataToPixel(std::complex<double> d)
|
||||
|
||||
QPoint TraceSmithChart::dataToPixel(Trace::Data d)
|
||||
{
|
||||
if(d.x < sweep_fmin || d.x > sweep_fmax) {
|
||||
return QPoint();
|
||||
}
|
||||
return dataToPixel(d.y);}
|
||||
|
||||
std::complex<double> 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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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::function<void (Device::Transmis
|
||||
s.logSweep = settings.Freq.logSweep;
|
||||
} else if(settings.sweepType == SweepType::Power) {
|
||||
s.fixedPowerSetting = 0;
|
||||
s.f_start = start;
|
||||
s.f_stop = stop;
|
||||
s.f_start = settings.Power.frequency;
|
||||
s.f_stop = settings.Power.frequency;
|
||||
s.points = npoints;
|
||||
s.if_bandwidth = settings.bandwidth;
|
||||
s.cdbm_excitation_start = settings.Power.start * 100;
|
||||
s.cdbm_excitation_stop = settings.Power.stop * 100;
|
||||
s.cdbm_excitation_start = start * 100;
|
||||
s.cdbm_excitation_stop = stop * 100;
|
||||
s.logSweep = false;
|
||||
}
|
||||
if(window->getDevice() && 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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -14,11 +14,13 @@ public:
|
||||
std::complex<double>(d.real_S21, d.imag_S21),
|
||||
std::complex<double>(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;
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
namespace Delay {
|
||||
|
||||
void Init();
|
||||
|
||||
uint64_t get_us();
|
||||
|
||||
void ms(uint32_t t);
|
||||
void us(uint32_t t);
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Exti.hpp"
|
||||
#include "VNA.hpp"
|
||||
#include "Manual.hpp"
|
||||
#include "delay.hpp"
|
||||
#include "SpectrumAnalyzer.hpp"
|
||||
#include <cstring>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ void SetMode(Mode mode);
|
||||
void SetIdle();
|
||||
void Work();
|
||||
bool TimedOut();
|
||||
uint64_t getLastISRTimestamp();
|
||||
|
||||
void SetOutputUnlevel(bool unlev);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user