diff --git a/Documentation/UserManual/ProgrammingGuide.pdf b/Documentation/UserManual/ProgrammingGuide.pdf index 22da8d3..021a813 100644 Binary files a/Documentation/UserManual/ProgrammingGuide.pdf and b/Documentation/UserManual/ProgrammingGuide.pdf differ diff --git a/Documentation/UserManual/ProgrammingGuide.tex b/Documentation/UserManual/ProgrammingGuide.tex index 79c5140..4236ce5 100644 --- a/Documentation/UserManual/ProgrammingGuide.tex +++ b/Documentation/UserManual/ProgrammingGuide.tex @@ -422,6 +422,30 @@ Depending on the sweep and possible confiigured math operations, x may be either -0.0458452,-0.028729 \end{example} +\subsubsection{VNA:TRACe:TOUCHSTONE} +\query{Returns the content of multiple trace according to the touchstone format}{VNA:TRACe:TOUCHSTONE?}{,,,...}{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} \query{Returns the highest frequency contained in the trace}{VNA:TRACe:MAXFrequency?}{, either by name or by index}{maximum frequency in Hz} diff --git a/Software/PC_Application/Traces/tracewidget.cpp b/Software/PC_Application/Traces/tracewidget.cpp index baf963b..4f67fdd 100644 --- a/Software/PC_Application/Traces/tracewidget.cpp +++ b/Software/PC_Application/Traces/tracewidget.cpp @@ -141,13 +141,10 @@ void TraceWidget::on_view_clicked(const QModelIndex &index) void TraceWidget::SetupSCPI() { - auto findTrace = [=](QStringList params) -> Trace* { - if(params.size() < 1) { - return nullptr; - } + auto findTraceFromName = [=](QString name) -> Trace* { // check if trace is specified by number bool ok; - auto n = params[0].toUInt(&ok); + auto n = name.toUInt(&ok); if(ok) { // check if enough traces exist if(n < model.getTraces().size()) { @@ -159,7 +156,7 @@ void TraceWidget::SetupSCPI() } else { // trace specified by name for(auto t : model.getTraces()) { - if(t->name().compare(params[0], Qt::CaseInsensitive) == 0) { + if(t->name().compare(name, Qt::CaseInsensitive) == 0) { return t; } } @@ -167,6 +164,12 @@ void TraceWidget::SetupSCPI() 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 { 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;isize(); + auto f_start = traces[0]->minX(); + auto f_stop = traces[0]->maxX(); + for(int i=0;igetDataType() != 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;igetSample(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 { auto t = findTrace(params); if(!t) { diff --git a/Software/PC_Application/touchstone.cpp b/Software/PC_Application/touchstone.cpp index 0c38c54..8b22cac 100644 --- a/Software/PC_Application/touchstone.cpp +++ b/Software/PC_Application/touchstone.cpp @@ -7,6 +7,7 @@ #include #include #include "Util/util.h" +#include using namespace std; @@ -45,25 +46,34 @@ void Touchstone::toFile(string filename, Scale unit, Format format) // create file ofstream file; 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 - file << "# "; + s << "# "; switch(unit) { - case Scale::Hz: file << "HZ "; break; - case Scale::kHz: file << "KHZ "; break; - case Scale::MHz: file << "MHZ "; break; - case Scale::GHz: file << "GHZ "; break; + case Scale::Hz: s << "HZ "; break; + case Scale::kHz: s << "KHZ "; break; + case Scale::MHz: s << "MHZ "; break; + case Scale::GHz: s << "GHZ "; break; } // only S parameters supported so far - file << "S "; + s << "S "; switch(format) { - case Format::DBAngle: file << "DB "; break; - case Format::RealImaginary: file << "RI "; break; - case Format::MagnitudeAngle: file << "MA "; break; + case Format::DBAngle: s << "DB "; break; + case Format::RealImaginary: s << "RI "; break; + case Format::MagnitudeAngle: s << "MA "; break; } // reference impedance is always 50 ohm - file << "R 50\n"; + s << "R 50\n"; auto printParameter = [format](ostream &out, complex &c) { switch (format) { @@ -81,45 +91,44 @@ void Touchstone::toFile(string filename, Scale unit, Format format) for(auto p : m_datapoints) { switch(unit) { - case Scale::Hz: file << p.frequency; break; - case Scale::kHz: file << p.frequency / 1e3; break; - case Scale::MHz: file << p.frequency / 1e6; break; - case Scale::GHz: file << p.frequency / 1e9; break; + case Scale::Hz: s << p.frequency; break; + case Scale::kHz: s << p.frequency / 1e3; break; + case Scale::MHz: s << p.frequency / 1e6; break; + case Scale::GHz: s << p.frequency / 1e9; break; } - file << " "; + s << " "; // special cases for 1 and 2 port if (m_ports == 1) { - printParameter(file, p.S[0]); - file << "\n"; + printParameter(s, p.S[0]); + s << "\n"; } 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 - file << " "; - printParameter(file, p.S[2]); - file << " "; - printParameter(file, p.S[1]); - file << " "; - printParameter(file, p.S[3]); - file << "\n"; + s << " "; + printParameter(s, p.S[2]); + s << " "; + printParameter(s, p.S[1]); + s << " "; + printParameter(s, p.S[3]); + s << "\n"; } else { // print parameters in matrix form for(unsigned int i=0;ifilename = QString::fromStdString(filename); + return s; } Touchstone Touchstone::fromFile(string filename) diff --git a/Software/PC_Application/touchstone.h b/Software/PC_Application/touchstone.h index 9e638a1..a73b4ce 100644 --- a/Software/PC_Application/touchstone.h +++ b/Software/PC_Application/touchstone.h @@ -31,6 +31,7 @@ public: Touchstone(unsigned int m_ports); void AddDatapoint(Datapoint p); 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); double minFreq(); double maxFreq();