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}
|
\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}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ public:
|
|||||||
Frequency,
|
Frequency,
|
||||||
Time,
|
Time,
|
||||||
Power,
|
Power,
|
||||||
|
TimeZeroSpan,
|
||||||
Invalid,
|
Invalid,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -63,44 +63,54 @@ 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);
|
||||||
}
|
}
|
||||||
// add or replace data in vector while keeping it sorted with increasing frequency
|
if(index >= 0) {
|
||||||
auto lower = lower_bound(data.begin(), data.end(), d, [](const Data &lhs, const Data &rhs) -> bool {
|
// index position specified
|
||||||
return lhs.x < rhs.x;
|
if(data.size() <= index) {
|
||||||
});
|
data.resize(index + 1);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
data[index] = d;
|
||||||
} else {
|
} else {
|
||||||
// insert at this position
|
// no index given, determine position by X-coordinate
|
||||||
data.insert(lower, d);
|
|
||||||
|
// 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) {
|
if(this->reference_impedance != reference_impedance) {
|
||||||
this->reference_impedance = reference_impedance;
|
this->reference_impedance = reference_impedance;
|
||||||
|
@ -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);
|
||||||
|
@ -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 "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public:
|
|||||||
Time,
|
Time,
|
||||||
Distance,
|
Distance,
|
||||||
Power,
|
Power,
|
||||||
|
TimeZeroSpan,
|
||||||
Last,
|
Last,
|
||||||
};
|
};
|
||||||
XAxis();
|
XAxis();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
vd = average.process(vd);
|
if(!settings.zerospan) {
|
||||||
|
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,14 +870,26 @@ void VNA::NewDatapoint(Protocol::Datapoint d)
|
|||||||
}
|
}
|
||||||
|
|
||||||
TraceMath::DataType type;
|
TraceMath::DataType type;
|
||||||
switch(settings.sweepType) {
|
if(settings.zerospan) {
|
||||||
case SweepType::Last:
|
type = TraceMath::DataType::TimeZeroSpan;
|
||||||
case SweepType::Frequency:
|
|
||||||
type = TraceMath::DataType::Frequency;
|
// keep track of first point time
|
||||||
break;
|
if(vd.pointNum == 0) {
|
||||||
case SweepType::Power:
|
settings.firstPointTime = vd.time;
|
||||||
type = TraceMath::DataType::Power;
|
vd.time = 0;
|
||||||
break;
|
} 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);
|
traceModel.addVNAData(vd, type);
|
||||||
@ -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);
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -13,9 +13,18 @@ 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;
|
||||||
uint64_t frequency;
|
union {
|
||||||
int16_t cdbm;
|
struct {
|
||||||
uint16_t pointNum;
|
// 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 {
|
using SweepSettings = struct _sweepSettings {
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
data.frequency = getPointFrequency(pointCnt);
|
if(zerospan) {
|
||||||
data.cdbm = settings.cdbm_excitation_start + (settings.cdbm_excitation_stop - settings.cdbm_excitation_start) * pointCnt / (settings.points - 1);
|
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) {
|
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();
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user