LibreVNA/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.cpp

301 lines
7.5 KiB
C++
Raw Normal View History

2022-10-01 23:10:44 +08:00
#include "tracemath.h"
#include "medianfilter.h"
#include "tdr.h"
#include "dft.h"
#include "expression.h"
#include "timegate.h"
#include "Traces/trace.h"
#include "ui_timedomaingatingexplanationwidget.h"
TraceMath::TraceMath()
{
input = nullptr;
dataType = DataType::Invalid;
error("Invalid input");
}
std::vector<TraceMath *> TraceMath::createMath(TraceMath::Type type)
{
std::vector<TraceMath*> ret;
switch(type) {
case Type::MedianFilter:
ret.push_back(new Math::MedianFilter());
break;
case Type::TDR:
ret.push_back(new Math::TDR());
break;
case Type::DFT:
ret.push_back(new Math::DFT());
break;
case Type::Expression:
ret.push_back(new Math::Expression());
break;
case Type::TimeGate:
ret.push_back(new Math::TimeGate());
break;
case Type::TimeDomainGating:
ret.push_back(new Math::TDR());
ret.push_back(new Math::TimeGate());
ret.push_back(new Math::DFT());
break;
default:
break;
}
return ret;
}
TraceMath::TypeInfo TraceMath::getInfo(TraceMath::Type type)
{
TypeInfo ret = {};
switch(type) {
case Type::MedianFilter:
ret.name = "Median filter";
ret.explanationWidget = Math::MedianFilter::createExplanationWidget();
break;
case Type::TDR:
ret.name = "TDR";
ret.explanationWidget = Math::TDR::createExplanationWidget();
break;
case Type::DFT:
ret.name = "DFT";
ret.explanationWidget = Math::DFT::createExplanationWidget();
break;
case Type::Expression:
ret.name = "Custom Expression";
ret.explanationWidget = Math::Expression::createExplanationWidget();
break;
case Type::TimeGate:
ret.name = "Time Gate";
ret.explanationWidget = Math::TimeGate::createExplanationWidget();
break;
case Type::TimeDomainGating: {
ret.name = "Time Domain Gating";
ret.explanationWidget = new QWidget();
auto ui = new Ui::TimeDomainGatingExplanationWidget;
ui->setupUi(ret.explanationWidget);
connect(ret.explanationWidget, &QWidget::destroyed, [=](){
delete ui;
});
}
break;
default:
break;
}
return ret;
}
TraceMath::Data TraceMath::getSample(unsigned int index)
{
if(index < data.size()) {
return data[index];
} else {
TraceMath::Data d;
d.x = 0;
d.y = 0;
return d;
}
}
double TraceMath::getStepResponse(unsigned int index)
{
if(stepResponse.size() > index) {
return stepResponse[index];
} else {
return std::numeric_limits<double>::quiet_NaN();
}
}
2022-10-20 21:58:57 +08:00
double TraceMath::getInterpolatedStepResponse(double x)
{
if(stepResponse.size() != data.size()) {
// make sure all the step response data is available
return std::numeric_limits<double>::quiet_NaN();
}
double ret = std::numeric_limits<double>::quiet_NaN();
if(data.size() == 0 || x < data.front().x || x > data.back().x) {
ret = std::numeric_limits<double>::quiet_NaN();
} else {
auto it = lower_bound(data.begin(), data.end(), x, [](const Data &lhs, const double x) -> bool {
return lhs.x < x;
});
if(it->x == x || it == data.begin()) {
2022-10-20 21:58:57 +08:00
ret = stepResponse[it - data.begin()];
} else {
// no exact match, needs to interpolate
unsigned int highIndex = it - data.begin();
unsigned int lowIndex = highIndex - 1;
auto high = *it;
it--;
auto low = *it;
double alpha = (x - low.x) / (high.x - low.x);
ret = stepResponse[lowIndex] * (1 - alpha) + stepResponse[highIndex] * alpha;
}
}
return ret;
}
2022-10-01 23:10:44 +08:00
TraceMath::Data TraceMath::getInterpolatedSample(double x)
{
Data ret;
if(data.size() == 0 || x < data.front().x || x > data.back().x) {
ret.y = std::numeric_limits<std::complex<double>>::quiet_NaN();
ret.x = std::numeric_limits<double>::quiet_NaN();
} else {
auto it = lower_bound(data.begin(), data.end(), x, [](const Data &lhs, const double x) -> bool {
return lhs.x < x;
});
if(it->x == x) {
ret = *it;
} else {
// no exact match, needs to interpolate
auto high = *it;
it--;
auto low = *it;
double alpha = (x - low.x) / (high.x - low.x);
ret.y = low.y * (1 - alpha) + high.y * alpha;
ret.x = x;
}
}
return ret;
}
unsigned int TraceMath::numSamples()
{
return data.size();
}
QString TraceMath::dataTypeToString(TraceMath::DataType type)
{
switch(type) {
case DataType::Frequency:
return "Frequency";
case DataType::Power:
return "Power";
case DataType::Time:
return "Time";
case DataType::TimeZeroSpan:
return "Time (Zero Span)";
default:
return "Invalid";
}
}
TraceMath::DataType TraceMath::dataTypeFromString(QString s)
{
for(unsigned int i=0;i<(int) DataType::Invalid;i++) {
if(s.compare(dataTypeToString((DataType) i), Qt::CaseInsensitive) == 0) {
return (DataType) i;
}
}
// not found
return DataType::Invalid;
}
void TraceMath::removeInput()
{
if(input) {
// disconnect everything from the input
disconnect(input, nullptr, this, nullptr);
input = nullptr;
data.clear();
dataType = DataType::Invalid;
emit outputTypeChanged(dataType);
}
}
void TraceMath::assignInput(TraceMath *input)
{
Q_ASSERT(input != nullptr);
if(input != this->input) {
removeInput();
this->input = input;
connect(input, &TraceMath::outputTypeChanged, this, &TraceMath::inputTypeChanged);
inputTypeChanged(input->dataType);
}
}
void TraceMath::inputTypeChanged(TraceMath::DataType type)
{
auto newType = outputType(type);
dataType = newType;
data.clear();
if(dataType == DataType::Invalid) {
error("Invalid input data");
disconnect(input, &TraceMath::outputSamplesChanged, this, &TraceMath::inputSamplesChanged);
updateStepResponse(false);
} else {
connect(input, &TraceMath::outputSamplesChanged, this, &TraceMath::inputSamplesChanged, Qt::UniqueConnection);
inputSamplesChanged(0, input->data.size());
}
emit outputTypeChanged(dataType);
}
void TraceMath::warning(QString warn)
{
statusString = warn;
status = Status::Warning;
emit statusChanged();
}
void TraceMath::error(QString err)
{
statusString = err;
status = Status::Error;
emit statusChanged();
}
void TraceMath::success()
{
if(status != Status::Ok) {
status = Status::Ok;
emit statusChanged();
}
}
void TraceMath::updateStepResponse(bool valid)
{
if(valid) {
stepResponse.resize(data.size());
double accumulate = 0.0;
for(unsigned int i=0;i<data.size();i++) {
accumulate += data[i].y.real();
stepResponse[i] = accumulate;
}
} else {
stepResponse.clear();
}
}
TraceMath *TraceMath::getInput() const
{
return input;
}
QString TraceMath::getStatusDescription() const
{
return statusString;
}
Trace *TraceMath::root()
{
auto root = this;
while(root->input) {
root = root->input;
}
return static_cast<Trace*>(root);
}
TraceMath::Status TraceMath::getStatus() const
{
return status;
}
TraceMath::DataType TraceMath::getDataType() const
{
return dataType;
}