CSV export options extended
This commit is contained in:
parent
000ad0098d
commit
2cf4c5a311
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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">
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user