#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::createMath(TraceMath::Type type) { std::vector 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::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::quiet_NaN(); } double ret = std::numeric_limits::quiet_NaN(); if(data.size() == 0 || x < data.front().x || x > data.back().x) { ret = std::numeric_limits::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()) { 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>::quiet_NaN(); ret.x = std::numeric_limits::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;iinput) { root = root->input; } return static_cast(root); } TraceMath::Status TraceMath::getStatus() const { return status; } TraceMath::DataType TraceMath::getDataType() const { return dataType; }