Added SCPI command for touchstone file format
This commit is contained in:
parent
68dc9842e9
commit
3176037c5d
Binary file not shown.
@ -422,6 +422,30 @@ Depending on the sweep and possible confiigured math operations, x may be either
|
|||||||
-0.0458452,-0.028729
|
-0.0458452,-0.028729
|
||||||
\end{example}
|
\end{example}
|
||||||
|
|
||||||
|
\subsubsection{VNA:TRACe:TOUCHSTONE}
|
||||||
|
\query{Returns the content of multiple trace according to the touchstone format}{VNA:TRACe:TOUCHSTONE?}{<trace1>,<trace2>,<trace3>,...}{Touchstone file content in ASCII}
|
||||||
|
Some additional constraints apply:
|
||||||
|
\begin{itemize}
|
||||||
|
\item The number of specified traces must be a square number. The number of ports in the touchstone file is inferred from that.
|
||||||
|
\item Only frequency domain traces are allowed.
|
||||||
|
\item All traces must have the same number of points and the same start/stop frequency.
|
||||||
|
\item The order in which the traces are specified matters and depending on its index and each trace must be a reflection or transmission measurement:
|
||||||
|
\begin{itemize}
|
||||||
|
\item Assuming that $n$ is the number of ports of the desired touchstone file, the $n*n$ number of traces must be specified in this order:
|
||||||
|
$$ S_{11}...S_{1n},S_{21}...S_{2n},...,S_{n1}...S_{nn} $$
|
||||||
|
\item For every trace $S_{ij}$, the trace must contain a reflection measurement if $i=j$ and a transmission measurement if $i\neq j$.
|
||||||
|
\end{itemize}
|
||||||
|
\item Traces can be specified either by name or by index.
|
||||||
|
\item A deviation from any of these points (invalid number of traces, non-existing trace, wrong order, ...) will result in an error being returned.
|
||||||
|
\end{itemize}
|
||||||
|
\begin{example}
|
||||||
|
:VNA:TRACE:TOUCHSTONE? S11 S12 S21 S22
|
||||||
|
# GHZ S RI R 50
|
||||||
|
1.000000000000 1.000497817993 0.010679213330 0.000013886895 -0.000054684886 -0.000023392624 -0.000021111371 0.401717424393 0.702864229679
|
||||||
|
1.002000000000 1.000323534012 0.010577851906 -0.000011075452 -0.000013504875 0.000000477609 -0.000007789199 0.413144201040 0.696514129639
|
||||||
|
...
|
||||||
|
\end{example}
|
||||||
|
|
||||||
\subsubsection{VNA:TRACe:MAXFrequency}
|
\subsubsection{VNA:TRACe:MAXFrequency}
|
||||||
\query{Returns the highest frequency contained in the trace}{VNA:TRACe:MAXFrequency?}{<trace>, either by name or by index}{maximum frequency in Hz}
|
\query{Returns the highest frequency contained in the trace}{VNA:TRACe:MAXFrequency?}{<trace>, either by name or by index}{maximum frequency in Hz}
|
||||||
|
|
||||||
|
@ -141,13 +141,10 @@ void TraceWidget::on_view_clicked(const QModelIndex &index)
|
|||||||
|
|
||||||
void TraceWidget::SetupSCPI()
|
void TraceWidget::SetupSCPI()
|
||||||
{
|
{
|
||||||
auto findTrace = [=](QStringList params) -> Trace* {
|
auto findTraceFromName = [=](QString name) -> Trace* {
|
||||||
if(params.size() < 1) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// check if trace is specified by number
|
// check if trace is specified by number
|
||||||
bool ok;
|
bool ok;
|
||||||
auto n = params[0].toUInt(&ok);
|
auto n = name.toUInt(&ok);
|
||||||
if(ok) {
|
if(ok) {
|
||||||
// check if enough traces exist
|
// check if enough traces exist
|
||||||
if(n < model.getTraces().size()) {
|
if(n < model.getTraces().size()) {
|
||||||
@ -159,7 +156,7 @@ void TraceWidget::SetupSCPI()
|
|||||||
} else {
|
} else {
|
||||||
// trace specified by name
|
// trace specified by name
|
||||||
for(auto t : model.getTraces()) {
|
for(auto t : model.getTraces()) {
|
||||||
if(t->name().compare(params[0], Qt::CaseInsensitive) == 0) {
|
if(t->name().compare(name, Qt::CaseInsensitive) == 0) {
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +164,12 @@ void TraceWidget::SetupSCPI()
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
auto findTrace = [=](QStringList params) -> Trace* {
|
||||||
|
if(params.size() < 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return findTraceFromName(params[0]);
|
||||||
|
};
|
||||||
|
|
||||||
auto createStringFromData = [](Trace *t, const Trace::Data &d) -> QString {
|
auto createStringFromData = [](Trace *t, const Trace::Data &d) -> QString {
|
||||||
if(Trace::isSAParamater(t->liveParameter())) {
|
if(Trace::isSAParamater(t->liveParameter())) {
|
||||||
@ -220,6 +223,63 @@ void TraceWidget::SetupSCPI()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
add(new SCPICommand("TOUCHSTONE", nullptr, [=](QStringList params) -> QString {
|
||||||
|
if(params.size() < 1) {
|
||||||
|
// no traces given
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
// check number of paramaters, must be a square number
|
||||||
|
int numTraces = params.size();
|
||||||
|
int ports = round(sqrt(numTraces));
|
||||||
|
if(ports * ports != numTraces) {
|
||||||
|
// invalid number of traces
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
Trace* traces[numTraces];
|
||||||
|
for(int i=0;i<numTraces;i++) {
|
||||||
|
traces[i] = findTraceFromName(params[i]);
|
||||||
|
if(!traces[i]) {
|
||||||
|
// couldn't find that trace
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if trace selection is valid
|
||||||
|
auto npoints = traces[0]->size();
|
||||||
|
auto f_start = traces[0]->minX();
|
||||||
|
auto f_stop = traces[0]->maxX();
|
||||||
|
for(int i=0;i<ports;i++) {
|
||||||
|
for(int j=0;j<ports;j++) {
|
||||||
|
bool need_reflection = i==j;
|
||||||
|
auto t = traces[j+i*ports];
|
||||||
|
if(t->getDataType() != Trace::DataType::Frequency) {
|
||||||
|
// invalid domain
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
if(t->isReflection() != need_reflection) {
|
||||||
|
// invalid measurement at this position
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
if((t->size() != npoints) || (t->minX() != f_start) || (t->maxX() != f_stop)) {
|
||||||
|
// frequency points are not identical
|
||||||
|
return "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all traces checked, they are valid.
|
||||||
|
// Constructing touchstone
|
||||||
|
Touchstone t = Touchstone(ports);
|
||||||
|
for(unsigned int i=0;i<npoints;i++) {
|
||||||
|
Touchstone::Datapoint d;
|
||||||
|
d.frequency = traces[0]->getSample(i).x;
|
||||||
|
for(auto trace : traces) {
|
||||||
|
d.S.push_back(trace->getSample(i).y);
|
||||||
|
}
|
||||||
|
t.AddDatapoint(d);
|
||||||
|
}
|
||||||
|
// touchstone assembled, save to dummyfile
|
||||||
|
auto s = t.toString(Touchstone::Scale::GHz, Touchstone::Format::RealImaginary);
|
||||||
|
return QString::fromStdString(s.str());
|
||||||
|
}));
|
||||||
add(new SCPICommand("MAXFrequency", nullptr, [=](QStringList params) -> QString {
|
add(new SCPICommand("MAXFrequency", nullptr, [=](QStringList params) -> QString {
|
||||||
auto t = findTrace(params);
|
auto t = findTrace(params);
|
||||||
if(!t) {
|
if(!t) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "Util/util.h"
|
#include "Util/util.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
@ -45,25 +46,34 @@ void Touchstone::toFile(string filename, Scale unit, Format format)
|
|||||||
// create file
|
// create file
|
||||||
ofstream file;
|
ofstream file;
|
||||||
file.open(filename);
|
file.open(filename);
|
||||||
file << std::fixed << std::setprecision(12);
|
|
||||||
|
|
||||||
|
file << toString(unit, format).rdbuf();
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
this->filename = QString::fromStdString(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
stringstream Touchstone::toString(Touchstone::Scale unit, Touchstone::Format format)
|
||||||
|
{
|
||||||
|
stringstream s;
|
||||||
|
s << std::fixed << std::setprecision(12);
|
||||||
// write option line
|
// write option line
|
||||||
file << "# ";
|
s << "# ";
|
||||||
switch(unit) {
|
switch(unit) {
|
||||||
case Scale::Hz: file << "HZ "; break;
|
case Scale::Hz: s << "HZ "; break;
|
||||||
case Scale::kHz: file << "KHZ "; break;
|
case Scale::kHz: s << "KHZ "; break;
|
||||||
case Scale::MHz: file << "MHZ "; break;
|
case Scale::MHz: s << "MHZ "; break;
|
||||||
case Scale::GHz: file << "GHZ "; break;
|
case Scale::GHz: s << "GHZ "; break;
|
||||||
}
|
}
|
||||||
// only S parameters supported so far
|
// only S parameters supported so far
|
||||||
file << "S ";
|
s << "S ";
|
||||||
switch(format) {
|
switch(format) {
|
||||||
case Format::DBAngle: file << "DB "; break;
|
case Format::DBAngle: s << "DB "; break;
|
||||||
case Format::RealImaginary: file << "RI "; break;
|
case Format::RealImaginary: s << "RI "; break;
|
||||||
case Format::MagnitudeAngle: file << "MA "; break;
|
case Format::MagnitudeAngle: s << "MA "; break;
|
||||||
}
|
}
|
||||||
// reference impedance is always 50 ohm
|
// reference impedance is always 50 ohm
|
||||||
file << "R 50\n";
|
s << "R 50\n";
|
||||||
|
|
||||||
auto printParameter = [format](ostream &out, complex<double> &c) {
|
auto printParameter = [format](ostream &out, complex<double> &c) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
@ -81,45 +91,44 @@ void Touchstone::toFile(string filename, Scale unit, Format format)
|
|||||||
|
|
||||||
for(auto p : m_datapoints) {
|
for(auto p : m_datapoints) {
|
||||||
switch(unit) {
|
switch(unit) {
|
||||||
case Scale::Hz: file << p.frequency; break;
|
case Scale::Hz: s << p.frequency; break;
|
||||||
case Scale::kHz: file << p.frequency / 1e3; break;
|
case Scale::kHz: s << p.frequency / 1e3; break;
|
||||||
case Scale::MHz: file << p.frequency / 1e6; break;
|
case Scale::MHz: s << p.frequency / 1e6; break;
|
||||||
case Scale::GHz: file << p.frequency / 1e9; break;
|
case Scale::GHz: s << p.frequency / 1e9; break;
|
||||||
}
|
}
|
||||||
file << " ";
|
s << " ";
|
||||||
// special cases for 1 and 2 port
|
// special cases for 1 and 2 port
|
||||||
if (m_ports == 1) {
|
if (m_ports == 1) {
|
||||||
printParameter(file, p.S[0]);
|
printParameter(s, p.S[0]);
|
||||||
file << "\n";
|
s << "\n";
|
||||||
} else if (m_ports == 2){
|
} else if (m_ports == 2){
|
||||||
printParameter(file, p.S[0]);
|
printParameter(s, p.S[0]);
|
||||||
// touchstone expects S11 S21 S12 S22 order, swap S12 and S21
|
// touchstone expects S11 S21 S12 S22 order, swap S12 and S21
|
||||||
file << " ";
|
s << " ";
|
||||||
printParameter(file, p.S[2]);
|
printParameter(s, p.S[2]);
|
||||||
file << " ";
|
s << " ";
|
||||||
printParameter(file, p.S[1]);
|
printParameter(s, p.S[1]);
|
||||||
file << " ";
|
s << " ";
|
||||||
printParameter(file, p.S[3]);
|
printParameter(s, p.S[3]);
|
||||||
file << "\n";
|
s << "\n";
|
||||||
} else {
|
} else {
|
||||||
// print parameters in matrix form
|
// print parameters in matrix form
|
||||||
for(unsigned int i=0;i<m_ports;i++) {
|
for(unsigned int i=0;i<m_ports;i++) {
|
||||||
for(unsigned int j=0;j<m_ports;j++) {
|
for(unsigned int j=0;j<m_ports;j++) {
|
||||||
printParameter(file, p.S[i*m_ports + j]);
|
printParameter(s, p.S[i*m_ports + j]);
|
||||||
if (j%4 == 3) {
|
if (j%4 == 3) {
|
||||||
file << "\n";
|
s << "\n";
|
||||||
} else {
|
} else {
|
||||||
file << " ";
|
s << " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(m_ports%4 != 0) {
|
if(m_ports%4 != 0) {
|
||||||
file << "\n";
|
s << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.close();
|
return s;
|
||||||
this->filename = QString::fromStdString(filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Touchstone Touchstone::fromFile(string filename)
|
Touchstone Touchstone::fromFile(string filename)
|
||||||
|
@ -31,6 +31,7 @@ public:
|
|||||||
Touchstone(unsigned int m_ports);
|
Touchstone(unsigned int m_ports);
|
||||||
void AddDatapoint(Datapoint p);
|
void AddDatapoint(Datapoint p);
|
||||||
void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
|
void toFile(std::string filename, Scale unit = Scale::GHz, Format format = Format::RealImaginary);
|
||||||
|
std::stringstream toString(Scale unit = Scale::GHz, Format format = Format::RealImaginary);
|
||||||
static Touchstone fromFile(std::string filename);
|
static Touchstone fromFile(std::string filename);
|
||||||
double minFreq();
|
double minFreq();
|
||||||
double maxFreq();
|
double maxFreq();
|
||||||
|
Loading…
Reference in New Issue
Block a user