mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-04-10 00:43:41 +00:00
Option to apply calibration and de-embedding while/after importing
This commit is contained in:
parent
f0669ab4c0
commit
7a4113cd6b
24 changed files with 1760 additions and 209 deletions
172
Software/PC_Application/Traces/sparamtraceselector.cpp
Normal file
172
Software/PC_Application/Traces/sparamtraceselector.cpp
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
#include "sparamtraceselector.h"
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
|
||||
using namespace std;
|
||||
|
||||
SparamTraceSelector::SparamTraceSelector(const TraceModel &model, unsigned int num_ports, bool empty_allowed, std::set<unsigned int> skip)
|
||||
: model(model),
|
||||
num_ports(num_ports),
|
||||
empty_allowed(empty_allowed)
|
||||
{
|
||||
// Create comboboxes
|
||||
auto layout = new QFormLayout;
|
||||
setLayout(layout);
|
||||
for(unsigned int i=0;i<num_ports;i++) {
|
||||
for(unsigned int j=0;j<num_ports;j++) {
|
||||
auto label = new QLabel("S"+QString::number(i+1)+QString::number(j+1)+":");
|
||||
auto box = new QComboBox();
|
||||
connect(box, qOverload<int>(&QComboBox::currentIndexChanged), [=](int) {
|
||||
traceSelectionChanged(box);
|
||||
});
|
||||
boxes.push_back(box);
|
||||
layout->addRow(label, box);
|
||||
if(skip.count(i*num_ports + j)) {
|
||||
label->setVisible(false);
|
||||
box->setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInitialChoices();
|
||||
}
|
||||
|
||||
std::vector<Trace*> SparamTraceSelector::getTraces()
|
||||
{
|
||||
vector<Trace*> ret;
|
||||
for(auto b : boxes) {
|
||||
if(b->currentIndex() == 0) {
|
||||
ret.push_back(nullptr);
|
||||
} else {
|
||||
auto trace = qvariant_cast<Trace*>(b->itemData(b->currentIndex()));
|
||||
ret.push_back(trace);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SparamTraceSelector::setInitialChoices()
|
||||
{
|
||||
for(unsigned int i=0;i<num_ports*num_ports;i++) {
|
||||
boxes[i]->blockSignals(true);
|
||||
boxes[i]->clear();
|
||||
boxes[i]->addItem("None");
|
||||
for(auto t : model.getTraces()) {
|
||||
if(t->outputType() != Trace::DataType::Frequency) {
|
||||
// only frequency domain traces allowed
|
||||
continue;
|
||||
}
|
||||
if(t->size() == 0) {
|
||||
// can't select empty traces
|
||||
continue;
|
||||
}
|
||||
bool reflectionRequired = i%(num_ports+1) == 0 ? true : false;
|
||||
if(reflectionRequired != t->isReflection()) {
|
||||
// invalid S parameter
|
||||
continue;
|
||||
}
|
||||
boxes[i]->addItem(t->name(), QVariant::fromValue<Trace*>(t));
|
||||
}
|
||||
boxes[i]->blockSignals(false);
|
||||
}
|
||||
points = 0;
|
||||
valid = empty_allowed;
|
||||
}
|
||||
|
||||
void SparamTraceSelector::traceSelectionChanged(QComboBox *cb)
|
||||
{
|
||||
// update available traces in combo boxes
|
||||
if(cb->currentIndex() != 0 && points == 0) {
|
||||
// the first trace has been selected, extract frequency info
|
||||
Trace *t = qvariant_cast<Trace*>(cb->itemData(cb->currentIndex()));
|
||||
points = t->size();
|
||||
if(points > 0) {
|
||||
minFreq = t->minX();
|
||||
maxFreq = t->maxX();
|
||||
}
|
||||
// remove all trace options with incompatible frequencies
|
||||
for(auto c : boxes) {
|
||||
if(!c->isVisible()) {
|
||||
// skip invisible selections
|
||||
continue;
|
||||
}
|
||||
for(int i=1;i<c->count();i++) {
|
||||
Trace *t = qvariant_cast<Trace*>(c->itemData(i));
|
||||
if(t->size() != points || (points > 0 && (t->minX() != minFreq || t->maxX() != maxFreq))) {
|
||||
// this trace is not available anymore
|
||||
c->removeItem(i);
|
||||
// decrement to check the next index in the next loop iteration
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
// try to find matching traces based on their names (convenient autoselection of matching traces)
|
||||
auto text = cb->currentText();
|
||||
text.chop(2);
|
||||
if(text.endsWith("S")) {
|
||||
// tracename ended in Sxx, probably other traces with matching prefix available
|
||||
for(unsigned int i=0;i<num_ports*num_ports;i++) {
|
||||
auto b = boxes[i];
|
||||
if(b == cb) {
|
||||
// skip this box
|
||||
continue;
|
||||
}
|
||||
for(int j=0;j<b->count();j++) {
|
||||
auto candidate = b->itemText(j);
|
||||
// check if correct parameter
|
||||
QString expectedSparam = QString::number(i/num_ports+1)+QString::number(i%num_ports+1);
|
||||
if(!candidate.endsWith(expectedSparam)) {
|
||||
// wrong S parameter, skip
|
||||
continue;
|
||||
}
|
||||
candidate.chop(2);
|
||||
if(candidate != text) {
|
||||
// trace name does not match
|
||||
continue;
|
||||
}
|
||||
// possible match, select for this box
|
||||
b->setCurrentIndex(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if(cb->currentIndex() == 0 && points > 0) {
|
||||
emit selectionValid(false);
|
||||
// Check if all trace selections are set for none
|
||||
for(auto c : boxes) {
|
||||
if(!c->isVisible()) {
|
||||
// skip invisible selections
|
||||
continue;
|
||||
}
|
||||
if(c->currentIndex() != 0) {
|
||||
// some trace is still selected, abort
|
||||
return;
|
||||
}
|
||||
}
|
||||
// all traces set for none
|
||||
points = 0;
|
||||
minFreq = 0;
|
||||
maxFreq = 0;
|
||||
setInitialChoices();
|
||||
}
|
||||
if(empty_allowed) {
|
||||
// always valid
|
||||
emit selectionValid(true);
|
||||
} else {
|
||||
// actually need to check
|
||||
valid = true;
|
||||
for(auto c : boxes) {
|
||||
if(!c->isVisible()) {
|
||||
// skip invisible selections
|
||||
continue;
|
||||
}
|
||||
if (c->currentIndex() == 0) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit selectionValid(valid);
|
||||
}
|
||||
}
|
||||
37
Software/PC_Application/Traces/sparamtraceselector.h
Normal file
37
Software/PC_Application/Traces/sparamtraceselector.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef SPARAMTRACESELECTOR_H
|
||||
#define SPARAMTRACESELECTOR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "tracemodel.h"
|
||||
#include <QComboBox>
|
||||
|
||||
class SparamTraceSelector : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SparamTraceSelector(const TraceModel &model, unsigned int num_ports, bool empty_allowed = false, std::set<unsigned int> skip = {});
|
||||
|
||||
bool isValid();
|
||||
|
||||
std::vector<Trace*> getTraces();
|
||||
unsigned int getPoints() { return points;};
|
||||
|
||||
signals:
|
||||
void selectionValid(bool valid);
|
||||
|
||||
private:
|
||||
void setInitialChoices();
|
||||
void traceSelectionChanged(QComboBox *cb);
|
||||
|
||||
const TraceModel &model;
|
||||
std::vector<QComboBox*> boxes;
|
||||
unsigned int num_ports;
|
||||
bool empty_allowed;
|
||||
|
||||
unsigned int points;
|
||||
double minFreq, maxFreq;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
#endif // SPARAMTRACESELECTOR_H
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
#include <math.h>
|
||||
#include "fftcomplex.h"
|
||||
#include <QDebug>
|
||||
#include <QScrollBar>
|
||||
#include <QSettings>
|
||||
#include <functional>
|
||||
|
||||
using namespace std;
|
||||
|
|
@ -215,6 +217,26 @@ QString Trace::fillFromCSV(CSV &csv, unsigned int parameter)
|
|||
return traceName;
|
||||
}
|
||||
|
||||
void Trace::fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<Protocol::Datapoint> &data)
|
||||
{
|
||||
S11.clear();
|
||||
S12.clear();
|
||||
S21.clear();
|
||||
S22.clear();
|
||||
for(auto d : data) {
|
||||
Trace::Data td;
|
||||
td.x = d.frequency;
|
||||
td.y = complex<double>(d.real_S11, d.imag_S11);
|
||||
S11.addData(td);
|
||||
td.y = complex<double>(d.real_S12, d.imag_S12);
|
||||
S12.addData(td);
|
||||
td.y = complex<double>(d.real_S21, d.imag_S21);
|
||||
S21.addData(td);
|
||||
td.y = complex<double>(d.real_S22, d.imag_S22);
|
||||
S22.addData(td);
|
||||
}
|
||||
}
|
||||
|
||||
void Trace::fromLivedata(Trace::LivedataType type, LiveParameter param)
|
||||
{
|
||||
timeDomain = false;
|
||||
|
|
@ -435,6 +457,61 @@ std::vector<Trace *> Trace::createFromCSV(CSV &csv)
|
|||
return traces;
|
||||
}
|
||||
|
||||
std::vector<Protocol::Datapoint> Trace::assembleDatapoints(const Trace &S11, const Trace &S12, const Trace &S21, const Trace &S22)
|
||||
{
|
||||
vector<Protocol::Datapoint> ret;
|
||||
|
||||
// Sanity check traces
|
||||
unsigned int samples = S11.size();
|
||||
vector<const Trace*> traces;
|
||||
traces.push_back(&S11);
|
||||
traces.push_back(&S12);
|
||||
traces.push_back(&S21);
|
||||
traces.push_back(&S22);
|
||||
vector<double> freqs;
|
||||
for(const auto t : traces) {
|
||||
if(t->size() != samples) {
|
||||
qWarning() << "Selected traces do not have the same size";
|
||||
return ret;
|
||||
}
|
||||
if(t->outputType() != Trace::DataType::Frequency) {
|
||||
qWarning() << "Selected trace not in frequency domain";
|
||||
return ret;
|
||||
}
|
||||
if(freqs.empty()) {
|
||||
// Create frequency vector
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
freqs.push_back(t->sample(i).x);
|
||||
}
|
||||
} else {
|
||||
// Compare with frequency vector
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
if(t->sample(i).x != freqs[i]) {
|
||||
qWarning() << "Selected traces do not have identical frequency points";
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks passed, assemble datapoints
|
||||
for(unsigned int i=0;i<samples;i++) {
|
||||
Protocol::Datapoint d;
|
||||
d.real_S11 = real(S11.sample(i).y);
|
||||
d.imag_S11 = imag(S11.sample(i).y);
|
||||
d.real_S12 = real(S12.sample(i).y);
|
||||
d.imag_S12 = imag(S12.sample(i).y);
|
||||
d.real_S21 = real(S21.sample(i).y);
|
||||
d.imag_S21 = imag(S21.sample(i).y);
|
||||
d.real_S22 = real(S22.sample(i).y);
|
||||
d.imag_S22 = imag(S22.sample(i).y);
|
||||
d.pointNum = i;
|
||||
d.frequency = freqs[i];
|
||||
ret.push_back(d);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Trace::updateLastMath(vector<MathInfo>::reverse_iterator start)
|
||||
{
|
||||
TraceMath *newLast = nullptr;
|
||||
|
|
@ -626,7 +703,7 @@ void Trace::enableMathOperation(unsigned int index, bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int Trace::size()
|
||||
unsigned int Trace::size() const
|
||||
{
|
||||
return lastMath->numSamples();
|
||||
}
|
||||
|
|
@ -724,7 +801,7 @@ std::vector<double> Trace::findPeakFrequencies(unsigned int maxPeaks, double min
|
|||
return frequencies;
|
||||
}
|
||||
|
||||
Trace::Data Trace::sample(unsigned int index, SampleType type)
|
||||
Trace::Data Trace::sample(unsigned int index, SampleType type) const
|
||||
{
|
||||
auto data = lastMath->getSample(index);
|
||||
if(type == SampleType::TimeStep) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ public:
|
|||
void setVelocityFactor(double v);
|
||||
void fillFromTouchstone(Touchstone &t, unsigned int parameter);
|
||||
QString fillFromCSV(CSV &csv, unsigned int parameter); // returns the suggested trace name (not yet set in member data)
|
||||
static void fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector<Protocol::Datapoint> &data);
|
||||
void fromLivedata(LivedataType type, LiveParameter param);
|
||||
QString name() { return _name; };
|
||||
QColor color() { return _color; };
|
||||
|
|
@ -60,8 +61,8 @@ public:
|
|||
bool isReflection();
|
||||
LiveParameter liveParameter() { return _liveParam; }
|
||||
LivedataType liveType() { return _liveType; }
|
||||
TraceMath::DataType outputType() { return lastMath->getDataType(); };
|
||||
unsigned int size();
|
||||
TraceMath::DataType outputType() const { return lastMath->getDataType(); };
|
||||
unsigned int size() const;
|
||||
double minX();
|
||||
double maxX();
|
||||
double findExtremumFreq(bool max);
|
||||
|
|
@ -77,7 +78,7 @@ public:
|
|||
TimeStep,
|
||||
};
|
||||
|
||||
Data sample(unsigned int index, SampleType type = SampleType::Frequency);
|
||||
Data sample(unsigned int index, SampleType type = SampleType::Frequency) const;
|
||||
QString getFilename() const;
|
||||
unsigned int getFileParameter() const;
|
||||
/* Returns the noise in dbm/Hz for spectrum analyzer measurements. May return NaN if calculation not possible */
|
||||
|
|
@ -125,6 +126,10 @@ public:
|
|||
static std::vector<Trace*> createFromTouchstone(Touchstone &t);
|
||||
static std::vector<Trace*> createFromCSV(CSV &csv);
|
||||
|
||||
// Assembles datapoints as received from the VNA from four S parameter traces. Requires that all traces are in the frequency domain,
|
||||
// have the same number of samples and their samples must be at the same frequencies across all traces
|
||||
static std::vector<Protocol::Datapoint> assembleDatapoints(const Trace &S11, const Trace &S12, const Trace &S21, const Trace &S22);
|
||||
|
||||
public slots:
|
||||
void setVisible(bool visible);
|
||||
void setColor(QColor color);
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ QVariant TraceModel::data(const QModelIndex &index, int role) const
|
|||
return QVariant();
|
||||
}
|
||||
|
||||
std::vector<Trace *> TraceModel::getTraces()
|
||||
std::vector<Trace *> TraceModel::getTraces() const
|
||||
{
|
||||
return traces;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public:
|
|||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
std::vector<Trace*> getTraces();
|
||||
std::vector<Trace*> getTraces() const;
|
||||
|
||||
bool PortExcitationRequired(int port);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue