diff --git a/Software/PC_Application/Traces/trace.cpp b/Software/PC_Application/Traces/trace.cpp index ab20e28..1ff29c1 100644 --- a/Software/PC_Application/Traces/trace.cpp +++ b/Software/PC_Application/Traces/trace.cpp @@ -3,6 +3,7 @@ #include "fftcomplex.h" #include "Util/util.h" #include "Marker/marker.h" +#include "traceaxis.h" #include #include @@ -167,54 +168,48 @@ void Trace::fillFromTouchstone(Touchstone &t, unsigned int parameter) QString Trace::fillFromCSV(CSV &csv, unsigned int parameter) { // find correct column - unsigned int traceNum = 0; - vector real; - vector imag; + int traceNum = -1; unsigned int i=1; - QString traceName; + QString lastTraceName = ""; bool hasImagValues; + std::map columnMapping; for(;i parameter) { + // got all columns for the trace we are interested in + break; + } + lastTraceName = traceName; } if(traceNum == parameter) { - // this is the desired trace - break; - } else { - traceNum++; - } - if(hasImagValues) { - // next column already used by this trace, skip - i++; + // this is the trace we are looking for, get axistype and add to mapping + + // handle legacy column naming, translate to new naming + if(yaxistype == "real") { + yaxistype = YAxis::TypeToName(YAxis::Type::Real); + } else if(yaxistype == "imag") { + yaxistype = YAxis::TypeToName(YAxis::Type::Imaginary); + } + + columnMapping[YAxis::TypeFromName(yaxistype)] = i; } } - if(i >= csv.columns()) { + if(traceNum < parameter) { throw runtime_error("Not enough traces in CSV file"); } - real = csv.getColumn(i); - if(hasImagValues) { - imag = csv.getColumn(i + 1); - } else { - imag.resize(real.size()); - fill(imag.begin(), imag.end(), 0.0); + if(columnMapping.size() == 0) { + throw runtime_error("No data for trace in CSV file"); } + clear(); fileParameter = parameter; filename = csv.getFilename(); @@ -227,16 +222,20 @@ QString Trace::fillFromCSV(CSV &csv, unsigned int parameter) domain = DataType::Frequency; } for(unsigned int i=0;i data; + for(auto map : columnMapping) { + data[map.first] = csv.getColumn(map.second)[i]; + } Data d; d.x = xColumn[i]; - d.y = complex(real[i], imag[i]); + d.y = YAxis::reconstructValueFromYAxisType(data); addData(d, domain); } reflection = false; createdFromFile = true; emit typeChanged(this); emit outputSamplesChanged(0, data.size()); - return traceName; + return lastTraceName; } void Trace::fillFromDatapoints(Trace &S11, Trace &S12, Trace &S21, Trace &S22, const std::vector &data) diff --git a/Software/PC_Application/Traces/traceaxis.cpp b/Software/PC_Application/Traces/traceaxis.cpp index 0313474..4ceee20 100644 --- a/Software/PC_Application/Traces/traceaxis.cpp +++ b/Software/PC_Application/Traces/traceaxis.cpp @@ -317,6 +317,95 @@ YAxis::Type YAxis::getType() const return type; } +std::set YAxis::getSupported(XAxis::Type type, TraceModel::DataSource source) +{ + std::set ret = {YAxis::Type::Disabled}; + if(source == TraceModel::DataSource::VNA) { + switch(type) { + case XAxis::Type::Frequency: + case XAxis::Type::Power: + ret.insert(YAxis::Type::Magnitude); + ret.insert(YAxis::Type::MagnitudeLinear); + ret.insert(YAxis::Type::Phase); + ret.insert(YAxis::Type::UnwrappedPhase); + ret.insert(YAxis::Type::VSWR); + ret.insert(YAxis::Type::Real); + ret.insert(YAxis::Type::Imaginary); + ret.insert(YAxis::Type::SeriesR); + ret.insert(YAxis::Type::Reactance); + ret.insert(YAxis::Type::Capacitance); + ret.insert(YAxis::Type::Inductance); + ret.insert(YAxis::Type::QualityFactor); + ret.insert(YAxis::Type::GroupDelay); + break; + case XAxis::Type::Time: + case XAxis::Type::Distance: + ret.insert(YAxis::Type::ImpulseReal); + ret.insert(YAxis::Type::ImpulseMag); + ret.insert(YAxis::Type::Step); + ret.insert(YAxis::Type::Impedance); + break; + default: + break; + } + } else if(source == TraceModel::DataSource::SA) { + switch(type) { + case XAxis::Type::Frequency: + ret.insert(YAxis::Type::Magnitude); + ret.insert(YAxis::Type::MagnitudedBuV); + break; + default: + break; + } + } + return ret; +} + +std::complex YAxis::reconstructValueFromYAxisType(std::map yaxistypes) +{ + std::complex ret = std::numeric_limits>::quiet_NaN(); + if(yaxistypes.count(Type::Real)) { + ret.real(yaxistypes[Type::Real]); + if(yaxistypes.count(Type::Imaginary)) { + ret.imag(yaxistypes[Type::Imaginary]); + } else { + ret.imag(0.0); + } + } else if(yaxistypes.count(Type::Magnitude) || yaxistypes.count(Type::MagnitudedBuV) || yaxistypes.count(Type::MagnitudeLinear)) { + double maglin, phase; + if(yaxistypes.count(Type::MagnitudeLinear)) { + maglin = yaxistypes[Type::MagnitudeLinear]; + } else if(yaxistypes.count(Type::Magnitude)) { + maglin = Util::dBToMagnitude(yaxistypes[Type::Magnitude]); + } else { + auto dBm = Util::dBuVTodBm(yaxistypes[Type::MagnitudedBuV]); + maglin = Util::dBToMagnitude(dBm); + } + if(yaxistypes.count(Type::Phase)) { + phase = yaxistypes[Type::Phase]; + } else { + phase = 0.0; + } + ret = polar(maglin, phase / 180.0 * M_PI); + } + return ret; +} + +bool XAxis::isSupported(XAxis::Type type, TraceModel::DataSource source) +{ + if(source == TraceModel::DataSource::VNA) { + // all X axis types are supported + return true; + } else if(source == TraceModel::DataSource::SA) { + if (type == XAxis::Type::Frequency) { + return true; + } else { + return false; + } + } + return false; +} + void Axis::updateTicks() { if(log) { diff --git a/Software/PC_Application/Traces/traceaxis.h b/Software/PC_Application/Traces/traceaxis.h index 960bd9c..b832a79 100644 --- a/Software/PC_Application/Traces/traceaxis.h +++ b/Software/PC_Application/Traces/traceaxis.h @@ -29,6 +29,32 @@ protected: std::vector ticks; }; +class XAxis : public Axis { +public: + enum class Type { + Frequency, + Time, + Distance, + Power, + Last, + }; + XAxis(); + double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override; + void set(Type type, bool log, bool autorange, double min, double max, double div); + static QString TypeToName(Type type); + static Type TypeFromName(QString name); + static QString Unit(Type type); + QString TypeToName(); + QString Unit(); + + Type getType() const; + + static bool isSupported(XAxis::Type type, TraceModel::DataSource source); + +private: + Type type; +}; + class YAxis : public Axis { public: enum class Type { @@ -58,6 +84,7 @@ public: }; YAxis(); double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override; + void set(Type type, bool log, bool autorange, double min, double max, double div); static QString TypeToName(Type type); static Type TypeFromName(QString name); @@ -69,29 +96,8 @@ public: Type getType() const; -private: - Type type; -}; - -class XAxis : public Axis { -public: - enum class Type { - Frequency, - Time, - Distance, - Power, - Last, - }; - XAxis(); - double sampleToCoordinate(Trace::Data data, Trace *t = nullptr, unsigned int sample = 0) override; - void set(Type type, bool log, bool autorange, double min, double max, double div); - static QString TypeToName(Type type); - static Type TypeFromName(QString name); - static QString Unit(Type type); - QString TypeToName(); - QString Unit(); - - Type getType() const; + static std::set getSupported(XAxis::Type type, TraceModel::DataSource source); + static std::complex reconstructValueFromYAxisType(std::map yaxistypes); private: Type type; diff --git a/Software/PC_Application/Traces/tracecsvexport.cpp b/Software/PC_Application/Traces/tracecsvexport.cpp index 55eeb99..a0d9080 100644 --- a/Software/PC_Application/Traces/tracecsvexport.cpp +++ b/Software/PC_Application/Traces/tracecsvexport.cpp @@ -3,6 +3,8 @@ #include "ui_tracecsvexport.h" #include "csv.h" +#include "traceaxis.h" + #include #include #include @@ -19,6 +21,35 @@ TraceCSVExport::TraceCSVExport(TraceModel &traceModel, QWidget *parent) : ui->listView->setModel(&model); ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); connect(&model, &TraceCSVModel::selectionChanged, ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::setEnabled); + connect(&model, &TraceCSVModel::selectionChanged, [&](){ + auto traces = model.tracesToExport(); + if(traces.size() == 0) { + ui->listColumns->clear(); + } else if(ui->listColumns->count() == 0) { + // first trace has bee selected, fill column selection + auto t = traces.front(); + auto domain = t->outputType(); + auto Xaxis = XAxis::Type::Last; + switch(domain) { + case Trace::DataType::Frequency: Xaxis = XAxis::Type::Frequency; break; + case Trace::DataType::Power: Xaxis = XAxis::Type::Power; break; + case Trace::DataType::Time: Xaxis = XAxis::Type::Time; break; + } + if(Xaxis == XAxis::Type::Last) { + // invalid axis selection + return; + } + for(auto ytype : YAxis::getSupported(Xaxis, traceModel.getSource())) { + if(ytype != YAxis::Type::Disabled) { + auto item = new QListWidgetItem(YAxis::TypeToName(ytype), ui->listColumns); + item->setCheckState(Qt::Unchecked); + } + } + // first fill of selection, nothing selected yet, disable OK button + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } + }); + connect(ui->listColumns, &QListWidget::itemChanged, this, &TraceCSVExport::columnSelectionChanged); } TraceCSVExport::~TraceCSVExport() @@ -26,6 +57,12 @@ TraceCSVExport::~TraceCSVExport() delete ui; } +void TraceCSVExport::columnSelectionChanged() +{ + auto types = getSelectedYAxisTypes(); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(types.size() > 0); +} + void TraceCSVExport::on_buttonBox_accepted() { auto traces = model.tracesToExport(); @@ -52,29 +89,35 @@ void TraceCSVExport::on_buttonBox_accepted() } csv.addColumn(Xname, X); // add the trace data - for(auto t : traces) { - vector real; - vector imag; - auto samples = t->numSamples(); - for(unsigned int i=0;isample(i).y.real()); - imag.push_back(t->sample(i).y.imag()); - } - // check if this is a real or complex trace - bool allZeros = std::all_of(imag.begin(), imag.end(), [](double i) { return i==0.0; }); - if(allZeros) { - // only real values, one column is enough - csv.addColumn(t->name(), real); - } else { - // complex values, need two columns - csv.addColumn(t->name()+"_real", real); - csv.addColumn(t->name()+"_imag", imag); + for(auto trace : traces) { + for(auto ytype : getSelectedYAxisTypes()) { + auto axis = YAxis(); + axis.set(ytype, false, false, 0, 1, 1); + auto samples = trace->numSamples(); + vector values; + for(unsigned int i=0;isample(i), trace, i)); + } + csv.addColumn(trace->name()+"_"+axis.TypeToName(), values); } } csv.toFile(filename); } +std::vector TraceCSVExport::getSelectedYAxisTypes() +{ + std::vector ret; + for(unsigned int i=0;ilistColumns->count();i++) { + auto item = ui->listColumns->item(i); + if(item->checkState() == Qt::Checked) { + auto type = YAxis::TypeFromName(item->text()); + ret.push_back(type); + } + } + return ret; +} + TraceCSVModel::TraceCSVModel(std::vector traces, QObject *parent) : QAbstractListModel(parent) { diff --git a/Software/PC_Application/Traces/tracecsvexport.h b/Software/PC_Application/Traces/tracecsvexport.h index 9085089..68fd5e0 100644 --- a/Software/PC_Application/Traces/tracecsvexport.h +++ b/Software/PC_Application/Traces/tracecsvexport.h @@ -2,6 +2,7 @@ #define TRACECSVEXPORT_H #include "tracemodel.h" +#include "traceaxis.h" #include @@ -44,9 +45,11 @@ public: ~TraceCSVExport(); private slots: + void columnSelectionChanged(); void on_buttonBox_accepted(); private: + std::vector getSelectedYAxisTypes(); Ui::TraceCSVExport *ui; TraceCSVModel model; }; diff --git a/Software/PC_Application/Traces/tracecsvexport.ui b/Software/PC_Application/Traces/tracecsvexport.ui index b793847..a9ef84d 100644 --- a/Software/PC_Application/Traces/tracecsvexport.ui +++ b/Software/PC_Application/Traces/tracecsvexport.ui @@ -9,8 +9,8 @@ 0 0 - 286 - 322 + 469 + 330 @@ -19,13 +19,38 @@ true - + - - - QAbstractItemView::SelectRows - - + + + + + Traces + + + + + + QAbstractItemView::SelectRows + + + + + + + + + + Columns + + + + + + + + + diff --git a/Software/PC_Application/Traces/xyplotaxisdialog.cpp b/Software/PC_Application/Traces/xyplotaxisdialog.cpp index db46f1a..a543164 100644 --- a/Software/PC_Application/Traces/xyplotaxisdialog.cpp +++ b/Software/PC_Application/Traces/xyplotaxisdialog.cpp @@ -1,6 +1,7 @@ #include "xyplotaxisdialog.h" #include "ui_xyplotaxisdialog.h" +#include "traceaxis.h" #include @@ -223,61 +224,10 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex) std::set XYplotAxisDialog::supportedYAxis(XAxis::Type type) { - set ret = {YAxis::Type::Disabled}; - auto source = plot->getModel().getSource(); - if(source == TraceModel::DataSource::VNA) { - switch(type) { - case XAxis::Type::Frequency: - case XAxis::Type::Power: - ret.insert(YAxis::Type::Magnitude); - ret.insert(YAxis::Type::MagnitudeLinear); - ret.insert(YAxis::Type::Phase); - ret.insert(YAxis::Type::UnwrappedPhase); - ret.insert(YAxis::Type::VSWR); - ret.insert(YAxis::Type::Real); - ret.insert(YAxis::Type::Imaginary); - ret.insert(YAxis::Type::SeriesR); - ret.insert(YAxis::Type::Reactance); - ret.insert(YAxis::Type::Capacitance); - ret.insert(YAxis::Type::Inductance); - ret.insert(YAxis::Type::QualityFactor); - ret.insert(YAxis::Type::GroupDelay); - break; - case XAxis::Type::Time: - case XAxis::Type::Distance: - ret.insert(YAxis::Type::ImpulseReal); - ret.insert(YAxis::Type::ImpulseMag); - ret.insert(YAxis::Type::Step); - ret.insert(YAxis::Type::Impedance); - break; - default: - break; - } - } else if(source == TraceModel::DataSource::SA) { - switch(type) { - case XAxis::Type::Frequency: - ret.insert(YAxis::Type::Magnitude); - ret.insert(YAxis::Type::MagnitudedBuV); - break; - default: - break; - } - } - return ret; + return YAxis::getSupported(type, plot->getModel().getSource()); } bool XYplotAxisDialog::isSupported(XAxis::Type type) { - auto source = plot->getModel().getSource(); - if(source == TraceModel::DataSource::VNA) { - // all X axis types are supported - return true; - } else if(source == TraceModel::DataSource::SA) { - if (type == XAxis::Type::Frequency) { - return true; - } else { - return false; - } - } - return false; + return XAxis::isSupported(type, plot->getModel().getSource()); } diff --git a/Software/PC_Application/Util/util.cpp b/Software/PC_Application/Util/util.cpp index f42c0b6..aea99c1 100644 --- a/Software/PC_Application/Util/util.cpp +++ b/Software/PC_Application/Util/util.cpp @@ -72,3 +72,10 @@ double Util::dBmTodBuV(double dBm) double dBdiff = 10*log10(uVpower*1000); return dBm - dBdiff; } + +double Util::dBuVTodBm(double dBuV) +{ + double uVpower = 0.000001*0.000001/50.0; + double dBdiff = 10*log10(uVpower*1000); + return dBuV + dBdiff; +} diff --git a/Software/PC_Application/Util/util.h b/Software/PC_Application/Util/util.h index d04a3d8..ff5ad30 100644 --- a/Software/PC_Application/Util/util.h +++ b/Software/PC_Application/Util/util.h @@ -31,7 +31,11 @@ namespace Util { static inline double SparamTodB(std::complex d) { return SparamTodB(abs(d)); } + static inline double dBToMagnitude(double dB) { + return pow(10, dB / 20); + } double dBmTodBuV(double dBm); + double dBuVTodBm(double dBuV); static inline double SparamToDegree(std::complex d) { return (arg(d) * 180.0 / M_PI); }