301 lines
7.5 KiB
C++
301 lines
7.5 KiB
C++
#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();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|