CSV export options extended

This commit is contained in:
Jan Käberich 2022-04-08 21:55:58 +02:00
parent 000ad0098d
commit 2cf4c5a311
9 changed files with 266 additions and 140 deletions

View File

@ -3,6 +3,7 @@
#include "fftcomplex.h"
#include "Util/util.h"
#include "Marker/marker.h"
#include "traceaxis.h"
#include <math.h>
#include <QDebug>
@ -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<double> real;
vector<double> imag;
int traceNum = -1;
unsigned int i=1;
QString traceName;
QString lastTraceName = "";
bool hasImagValues;
std::map<YAxis::Type, int> columnMapping;
for(;i<csv.columns();i++) {
traceName = QString();
hasImagValues = false;
// check column names
if(i < csv.columns() - 1) {
// not the last column, check if this and next header implies real/imag values
auto name_real = csv.getHeader(i);
auto name_imag = csv.getHeader(i + 1);
if(name_real.endsWith("_real") && name_imag.endsWith("_imag")) {
// check if headers have the same beginning
name_real.chop(5);
name_imag.chop(5);
if(name_real == name_imag) {
hasImagValues = true;
traceName = name_real;
}
}
auto header = csv.getHeader(i);
auto splitIndex = header.lastIndexOf("_");
if(splitIndex == -1) {
// no "_", not following naming format of CSV export, skip
continue;
}
if(!hasImagValues) {
traceName = csv.getHeader(i);
auto traceName = header.left(splitIndex);
auto yaxistype = header.right(header.size() - splitIndex - 1);
if(traceName != lastTraceName) {
traceNum++;
if(traceNum > 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<xColumn.size();i++) {
std::map<YAxis::Type, double> data;
for(auto map : columnMapping) {
data[map.first] = csv.getColumn(map.second)[i];
}
Data d;
d.x = xColumn[i];
d.y = complex<double>(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<VNAData> &data)

View File

@ -317,6 +317,95 @@ YAxis::Type YAxis::getType() const
return type;
}
std::set<YAxis::Type> YAxis::getSupported(XAxis::Type type, TraceModel::DataSource source)
{
std::set<YAxis::Type> 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<double> YAxis::reconstructValueFromYAxisType(std::map<YAxis::Type, double> yaxistypes)
{
std::complex<double> ret = std::numeric_limits<std::complex<double>>::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<double>(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) {

View File

@ -29,6 +29,32 @@ protected:
std::vector<double> 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<YAxis::Type> getSupported(XAxis::Type type, TraceModel::DataSource source);
static std::complex<double> reconstructValueFromYAxisType(std::map<Type, double> yaxistypes);
private:
Type type;

View File

@ -3,6 +3,8 @@
#include "ui_tracecsvexport.h"
#include "csv.h"
#include "traceaxis.h"
#include <QDialogButtonBox>
#include <QPushButton>
#include <QFileDialog>
@ -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<double> real;
vector<double> imag;
auto samples = t->numSamples();
for(unsigned int i=0;i<samples;i++) {
real.push_back(t->sample(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<double> values;
for(unsigned int i=0;i<samples;i++) {
values.push_back(axis.sampleToCoordinate(trace->sample(i), trace, i));
}
csv.addColumn(trace->name()+"_"+axis.TypeToName(), values);
}
}
csv.toFile(filename);
}
std::vector<YAxis::Type> TraceCSVExport::getSelectedYAxisTypes()
{
std::vector<YAxis::Type> ret;
for(unsigned int i=0;i<ui->listColumns->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<Trace *> traces, QObject *parent)
: QAbstractListModel(parent)
{

View File

@ -2,6 +2,7 @@
#define TRACECSVEXPORT_H
#include "tracemodel.h"
#include "traceaxis.h"
#include <QDialog>
@ -44,9 +45,11 @@ public:
~TraceCSVExport();
private slots:
void columnSelectionChanged();
void on_buttonBox_accepted();
private:
std::vector<YAxis::Type> getSelectedYAxisTypes();
Ui::TraceCSVExport *ui;
TraceCSVModel model;
};

View File

@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>286</width>
<height>322</height>
<width>469</width>
<height>330</height>
</rect>
</property>
<property name="windowTitle">
@ -19,13 +19,38 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QListView" name="listView">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Traces</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListView" name="listView">
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Columns</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QListWidget" name="listColumns"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">

View File

@ -1,6 +1,7 @@
#include "xyplotaxisdialog.h"
#include "ui_xyplotaxisdialog.h"
#include "traceaxis.h"
#include <QStandardItemModel>
@ -223,61 +224,10 @@ void XYplotAxisDialog::XAxisTypeChanged(int XAxisIndex)
std::set<YAxis::Type> XYplotAxisDialog::supportedYAxis(XAxis::Type type)
{
set<YAxis::Type> 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());
}

View File

@ -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;
}

View File

@ -31,7 +31,11 @@ namespace Util {
static inline double SparamTodB(std::complex<double> 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<double> d) {
return (arg(d) * 180.0 / M_PI);
}