LibreVNA/Software/PC_Application/Traces/Math/windowfunction.cpp

146 lines
4.3 KiB
C++
Raw Normal View History

#include "windowfunction.h"
#define _USE_MATH_DEFINES
#include <math.h>
#include <QComboBox>
#include <QLabel>
#include <QFormLayout>
#include "CustomWidgets/siunitedit.h"
QString WindowFunction::typeToName(WindowFunction::Type type)
{
switch(type) {
case Type::Rectangular: return "Rectangular"; break;
// case Type::Kaiser: return "Kaiser"; break;
case Type::Hamming: return "Hamming"; break;
case Type::Hann: return "Hann"; break;
case Type::Blackman: return "Blackman"; break;
case Type::Gaussian: return "Gaussian"; break;
// case Type::Chebyshev: return "Chebyshev"; break;
default: return "Invalid"; break;
}
}
WindowFunction::WindowFunction(WindowFunction::Type type)
{
this->type = type;
// set default parameters
kaiser_alpha = 3.0;
gaussian_sigma = 0.4;
chebyshev_alpha = 5;
}
void WindowFunction::apply(std::vector<std::complex<double> > &data)
{
unsigned int N = data.size();
for(unsigned int n = 0;n<N;n++) {
data[n] *= getFactor(n, N);
}
}
QWidget *WindowFunction::createEditor()
{
auto top = new QWidget();
auto layout = new QFormLayout();
top->setLayout(layout);
auto cbType = new QComboBox();
for(unsigned int i=0;i<(unsigned int) Type::Last;i++) {
cbType->addItem(typeToName((Type) i));
}
layout->addRow(new QLabel("Type:"), cbType);
QObject::connect(cbType, qOverload<int>(&QComboBox::currentIndexChanged), [=](int newIndex){
if(layout->rowCount() > 1) {
layout->removeRow(1);
}
type = (Type) newIndex;
QLabel *paramLabel = nullptr;
SIUnitEdit *paramEdit = nullptr;
// add GUI elements for window types that have a parameter
switch(type) {
case Type::Gaussian:
paramLabel = new QLabel("Parameter σ:");
paramEdit = new SIUnitEdit("", " ", 3);
paramEdit->setValue(gaussian_sigma);
QObject::connect(paramEdit, &SIUnitEdit::valueChanged, [=](double newval) {
gaussian_sigma = newval;
});
break;
// case Type::Chebyshev:
// paramLabel = new QLabel("Parameter α:");
// paramEdit = new SIUnitEdit("", " ", 3);
// paramEdit->setValue(chebyshev_alpha);
// QObject::connect(paramEdit, &SIUnitEdit::valueChanged, [=](double newval) {
// chebyshev_alpha = newval;
// });
// break;
// case Type::Kaiser:
// // TODO
// break;
default:
break;
}
if(paramLabel != nullptr && paramEdit != nullptr) {
layout->addRow(paramLabel, paramEdit);
QObject::connect(paramEdit, &SIUnitEdit::valueChanged, this, &WindowFunction::changed);
}
emit changed();
});
cbType->setCurrentIndex((int) type);
return top;
}
WindowFunction::Type WindowFunction::getType() const
{
return type;
}
QString WindowFunction::getDescription()
{
QString ret = typeToName(type);
if(type == Type::Gaussian) {
ret += ", σ=" + QString::number(gaussian_sigma);
// } else if(type == Type::Chebyshev) {
// ret += ", α=" + QString::number(chebyshev_alpha);
}
return ret;
}
double WindowFunction::getFactor(unsigned int n, unsigned int N)
{
// all formulas from https://en.wikipedia.org/wiki/Window_function
switch(type) {
case Type::Rectangular:
// nothing to do
return 1.0;
// case Type::Kaiser:
// // TODO
// break;
case Type::Hamming:
return 25.0/46.0 - (21.0/46.0) * cos(2*M_PI*n / N);
case Type::Hann:
return pow(sin(M_PI*n / N), 2.0);
case Type::Blackman:
return 0.42 - 0.5 * cos(2*M_PI*n / N) + 0.08 * cos(4*M_PI*n / N);
case Type::Gaussian:
return exp(-0.5 * pow((n - (double) N/2) / (gaussian_sigma * N / 2), 2));
// case Type::Chebyshev: {
// double beta = cosh(1.0 / N * acosh(pow(10, chebyshev_alpha)));
// double T_N_arg = beta * cos(M_PI*n/(N+1));
// double T_N;
// if(T_N_arg >= 1.0) {
// T_N = cosh(N * acosh(T_N_arg));
// } else if(T_N_arg <= -1.0) {
// T_N = pow(-1.0, N) * cosh(N * acosh(T_N_arg));
// } else {
// T_N = cos(N * acos(T_N_arg));
// }
// return T_N / pow(10.0, chebyshev_alpha);
// }
default:
return 1.0;
}
}