File indexing completed on 2024-11-10 09:31:54

0001 /*
0002     File                 : FourierTest.cpp
0003     Project              : LabPlot
0004     Description          : Tests for fourier filtering
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2022 Martin Marmsoler <martin.marmsoler@gmail.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "FourierTest.h"
0012 #include "backend/core/column/Column.h"
0013 #include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h"
0014 // #include "backend/worksheet/plots/cartesian/XYFourierFilterCurvePrivate.h"
0015 
0016 // ##############################################################################
0017 
0018 const QString dataPath = QStringLiteral("data/"); // relative path
0019 
0020 #define READ_DATA(filename)                                                                                                                                    \
0021     auto filepath = QFINDTESTDATA(dataPath + filename);                                                                                                        \
0022     QVector<QVector<double>> data;                                                                                                                             \
0023     do {                                                                                                                                                       \
0024         QFile f(filepath);                                                                                                                                     \
0025         QCOMPARE(f.open(QIODevice::ReadOnly), true);                                                                                                           \
0026                                                                                                                                                                \
0027         while (!f.atEnd()) {                                                                                                                                   \
0028             const QString line = QLatin1String(f.readLine().simplified());                                                                                     \
0029             const auto entries = line.split(QLatin1Char(','));                                                                                                 \
0030             QVector<double> values;                                                                                                                            \
0031             for (const auto& entry : entries) {                                                                                                                \
0032                 bool ok;                                                                                                                                       \
0033                 double v = entry.toDouble(&ok);                                                                                                                \
0034                 QVERIFY(ok);                                                                                                                                   \
0035                 values.append(v);                                                                                                                              \
0036             }                                                                                                                                                  \
0037             data.append(values);                                                                                                                               \
0038         }                                                                                                                                                      \
0039         QVERIFY(data.count() > 0);                                                                                                                             \
0040         /* Check that all rows have the same number of values */                                                                                               \
0041         const auto length = data.at(0).length();                                                                                                               \
0042         for (const auto& v : data)                                                                                                                             \
0043             QCOMPARE(v.length(), length);                                                                                                                      \
0044     } while (false);
0045 
0046 /*!
0047  * Writing the results to a file to validate them
0048  */
0049 #define WRITE_RESULT_DATA(xData, yData, filteredDataRef, filteredData)                                                                                         \
0050     do {                                                                                                                                                       \
0051         QFile f(filepath + QStringLiteral("_testOutput.csv"));                                                                                                 \
0052         QCOMPARE(f.open(QIODevice::WriteOnly), true);                                                                                                          \
0053                                                                                                                                                                \
0054         f.write("x,y,filtered_reference, filtered_calculated\n"); /* header */                                                                                 \
0055         for (int i = 0; i < xData.length(); i++) {                                                                                                             \
0056             f.write(QStringLiteral("%1,%2,%3,%4\n").arg(xData.at(i)).arg(yData.at(i)).arg(filteredDataRef.at(i)).arg(filteredData.at(i)).toLatin1());          \
0057         }                                                                                                                                                      \
0058     } while (false);
0059 
0060 void FourierTest::lowPassButterWorth() {
0061     const QString filename = QStringLiteral("butterworth.csv");
0062     READ_DATA(filename);
0063 
0064     // data
0065     const auto xData = data.at(0);
0066     const auto yData = data.at(1);
0067     const auto filteredDataRef = data.at(2);
0068 
0069     // data source columns
0070     Column xDataColumn(QStringLiteral("x"), AbstractColumn::ColumnMode::Double);
0071     xDataColumn.replaceValues(0, xData);
0072 
0073     Column yDataColumn(QStringLiteral("y"), AbstractColumn::ColumnMode::Double);
0074     yDataColumn.replaceValues(0, yData);
0075 
0076     XYFourierFilterCurve curve(QStringLiteral("fourierFiltering"));
0077     curve.setXDataColumn(&xDataColumn);
0078     curve.setYDataColumn(&yDataColumn);
0079 
0080     // prepare the filtering
0081     auto filterData = curve.filterData();
0082     filterData.type = nsl_filter_type_low_pass;
0083     filterData.form = nsl_filter_form_butterworth;
0084     filterData.autoRange = true; // use complete data range
0085     filterData.cutoff = 100; // [Hz]
0086     // filterData.cutoff2; // Not relevant for lowpass filter
0087     filterData.order = 1;
0088     filterData.unit = nsl_filter_cutoff_unit_frequency;
0089     // filterData.unit2; // Not relevant for lowpass filter
0090     // filterData.xRange; // Not relevant because autoRange is true
0091     curve.setFilterData(filterData);
0092 
0093     // perform the differentiation
0094     curve.recalculate();
0095     const XYFourierFilterCurve::FilterResult& results = curve.filterResult();
0096 
0097     QVERIFY(results.available);
0098     QVERIFY(results.valid);
0099 
0100     // This does not work yet, because the implementation uses two side filtering,
0101     // considering knowing of future values (values after the current processed index)
0102     // Octave is using an IIR filter
0103     //  const auto xVec = *(curve.d_func()->xVector);
0104     //  const auto yVec = *(curve.d_func()->yVector);
0105     //  WRITE_RESULT_DATA(xData, yData, filteredDataRef, yVec);
0106 
0107     //  COMPARE_DOUBLE_VECTORS(xVec, xData);
0108     //  COMPARE_DOUBLE_VECTORS(yVec, filteredDataRef);
0109 }
0110 
0111 QTEST_MAIN(FourierTest)