File indexing completed on 2025-01-26 03:34:21

0001 /*
0002     File                 : XYHilbertTransformCurve.cpp
0003     Project              : LabPlot
0004     Description          : A xy-curve defined by a Hilbert transform
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2021 Stefan Gerlach <stefan.gerlach@uni.kn>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 /*!
0011   \class XYHilbertTransformCurve
0012   \brief A xy-curve defined by a Hilbert transform
0013 
0014   \ingroup worksheet
0015 */
0016 
0017 #include "XYHilbertTransformCurve.h"
0018 #include "XYHilbertTransformCurvePrivate.h"
0019 #include "backend/core/AbstractColumn.h"
0020 #include "backend/core/column/Column.h"
0021 #include "backend/gsl/errors.h"
0022 #include "backend/lib/XmlStreamReader.h"
0023 #include "backend/lib/commandtemplates.h"
0024 #include "backend/lib/macros.h"
0025 
0026 #include <KLocalizedString>
0027 #include <QDebug> // qWarning()
0028 #include <QElapsedTimer>
0029 #include <QIcon>
0030 #include <QThreadPool>
0031 
0032 XYHilbertTransformCurve::XYHilbertTransformCurve(const QString& name)
0033     : XYAnalysisCurve(name, new XYHilbertTransformCurvePrivate(this), AspectType::XYHilbertTransformCurve) {
0034 }
0035 
0036 XYHilbertTransformCurve::XYHilbertTransformCurve(const QString& name, XYHilbertTransformCurvePrivate* dd)
0037     : XYAnalysisCurve(name, dd, AspectType::XYHilbertTransformCurve) {
0038 }
0039 
0040 // no need to delete the d-pointer here - it inherits from QGraphicsItem
0041 // and is deleted during the cleanup in QGraphicsScene
0042 XYHilbertTransformCurve::~XYHilbertTransformCurve() = default;
0043 
0044 void XYHilbertTransformCurve::recalculate() {
0045     Q_D(XYHilbertTransformCurve);
0046     d->recalculate();
0047 }
0048 
0049 const XYAnalysisCurve::Result& XYHilbertTransformCurve::result() const {
0050     Q_D(const XYHilbertTransformCurve);
0051     return d->transformResult;
0052 }
0053 
0054 /*!
0055     Returns an icon to be used in the project explorer.
0056 */
0057 QIcon XYHilbertTransformCurve::icon() const {
0058     return QIcon::fromTheme(QStringLiteral("labplot-xy-fourier-transform-curve"));
0059 }
0060 
0061 // ##############################################################################
0062 // ##########################  getter methods  ##################################
0063 // ##############################################################################
0064 BASIC_SHARED_D_READER_IMPL(XYHilbertTransformCurve, XYHilbertTransformCurve::TransformData, transformData, transformData)
0065 
0066 // ##############################################################################
0067 // #################  setter methods and undo commands ##########################
0068 // ##############################################################################
0069 STD_SETTER_CMD_IMPL_F_S(XYHilbertTransformCurve, SetTransformData, XYHilbertTransformCurve::TransformData, transformData, recalculate)
0070 void XYHilbertTransformCurve::setTransformData(const XYHilbertTransformCurve::TransformData& transformData) {
0071     Q_D(XYHilbertTransformCurve);
0072     exec(new XYHilbertTransformCurveSetTransformDataCmd(d, transformData, ki18n("%1: set transform options and perform the Hilbert transform")));
0073 }
0074 
0075 // ##############################################################################
0076 // ######################### Private implementation #############################
0077 // ##############################################################################
0078 XYHilbertTransformCurvePrivate::XYHilbertTransformCurvePrivate(XYHilbertTransformCurve* owner)
0079     : XYAnalysisCurvePrivate(owner)
0080     , q(owner) {
0081 }
0082 
0083 // no need to delete xColumn and yColumn, they are deleted
0084 // when the parent aspect is removed
0085 XYHilbertTransformCurvePrivate::~XYHilbertTransformCurvePrivate() = default;
0086 
0087 void XYHilbertTransformCurvePrivate::resetResults() {
0088     transformResult = XYHilbertTransformCurve::TransformResult();
0089 }
0090 
0091 bool XYHilbertTransformCurvePrivate::recalculateSpecific(const AbstractColumn* tmpXDataColumn, const AbstractColumn* tmpYDataColumn) {
0092     DEBUG(Q_FUNC_INFO)
0093     if (!tmpXDataColumn || !tmpYDataColumn)
0094         return false;
0095 
0096     QElapsedTimer timer;
0097     timer.start();
0098 
0099     // copy all valid data point for the transform to temporary vectors
0100     QVector<double> xdataVector;
0101     QVector<double> ydataVector;
0102     double xmin, xmax;
0103     if (transformData.autoRange) {
0104         xmin = tmpXDataColumn->minimum();
0105         xmax = tmpXDataColumn->maximum();
0106     } else {
0107         xmin = transformData.xRange.first();
0108         xmax = transformData.xRange.last();
0109     }
0110 
0111     int rowCount = std::min(tmpXDataColumn->rowCount(), tmpYDataColumn->rowCount());
0112     DEBUG(Q_FUNC_INFO << ", row count = " << rowCount)
0113     DEBUG(Q_FUNC_INFO << ", xmin/xmax = " << xmin << '/' << xmax)
0114     for (int row = 0; row < rowCount; ++row) {
0115         // only copy those data where _all_ values (for x and y, if given) are valid
0116         if (std::isnan(tmpXDataColumn->valueAt(row)) || std::isnan(tmpYDataColumn->valueAt(row)) || tmpXDataColumn->isMasked(row)
0117             || tmpYDataColumn->isMasked(row))
0118             continue;
0119 
0120         // only when inside given range
0121         if (tmpXDataColumn->valueAt(row) >= xmin && tmpXDataColumn->valueAt(row) <= xmax) {
0122             xdataVector.append(tmpXDataColumn->valueAt(row));
0123             ydataVector.append(tmpYDataColumn->valueAt(row));
0124         }
0125     }
0126 
0127     // number of data points to transform
0128     unsigned int n = (unsigned int)ydataVector.size();
0129     if (n == 0) {
0130         transformResult.available = true;
0131         transformResult.valid = false;
0132         transformResult.status = i18n("No data points available.");
0133         DEBUG(Q_FUNC_INFO << "no data (n = 0)!")
0134         return true;
0135     }
0136 
0137     double* xdata = xdataVector.data();
0138     double* ydata = ydataVector.data();
0139 
0140     // transform settings
0141     const nsl_hilbert_result_type type = transformData.type;
0142 
0143     DEBUG("n = " << n);
0144     DEBUG("type:" << nsl_hilbert_result_type_name[type]);
0145 #ifndef NDEBUG
0146 //  QDebug out = qDebug();
0147 //  for (unsigned int i = 0; i < n; i++)
0148 //      out<<ydata[i];
0149 #endif
0150 
0151     ///////////////////////////////////////////////////////////
0152     // transform with window
0153     //  TODO: type
0154     gsl_set_error_handler_off();
0155     int status = nsl_hilbert_transform(ydata, 1, n, type);
0156 
0157     unsigned int N = n;
0158 #ifndef NDEBUG
0159 //  out = qDebug();
0160 //  for (unsigned int i = 0; i < N; i++)
0161 //      out << ydata[i] << '(' << xdata[i] << ')';
0162 #endif
0163 
0164     xVector->resize((int)N);
0165     yVector->resize((int)N);
0166     memcpy(xVector->data(), xdata, N * sizeof(double));
0167     memcpy(yVector->data(), ydata, N * sizeof(double));
0168     ///////////////////////////////////////////////////////////
0169 
0170     // write the result
0171     transformResult.available = true;
0172     transformResult.valid = (status == GSL_SUCCESS);
0173     transformResult.status = gslErrorToString(status);
0174     transformResult.elapsedTime = timer.elapsed();
0175     return true;
0176 }
0177 
0178 // ##############################################################################
0179 // ##################  Serialization/Deserialization  ###########################
0180 // ##############################################################################
0181 //! Save as XML
0182 void XYHilbertTransformCurve::save(QXmlStreamWriter* writer) const {
0183     Q_D(const XYHilbertTransformCurve);
0184 
0185     writer->writeStartElement(QStringLiteral("xyHilbertTransformCurve"));
0186 
0187     // write the base class
0188     XYAnalysisCurve::save(writer);
0189 
0190     // write xy-fourier_transform-curve specific information
0191     // transform data
0192     writer->writeStartElement(QStringLiteral("transformData"));
0193     writer->writeAttribute(QStringLiteral("autoRange"), QString::number(d->transformData.autoRange));
0194     writer->writeAttribute(QStringLiteral("xRangeMin"), QString::number(d->transformData.xRange.first()));
0195     writer->writeAttribute(QStringLiteral("xRangeMax"), QString::number(d->transformData.xRange.last()));
0196     writer->writeAttribute(QStringLiteral("type"), QString::number(d->transformData.type));
0197     writer->writeEndElement(); // transformData
0198 
0199     // transform results (generated columns)
0200     writer->writeStartElement(QStringLiteral("transformResult"));
0201     writer->writeAttribute(QStringLiteral("available"), QString::number(d->transformResult.available));
0202     writer->writeAttribute(QStringLiteral("valid"), QString::number(d->transformResult.valid));
0203     writer->writeAttribute(QStringLiteral("status"), d->transformResult.status);
0204     writer->writeAttribute(QStringLiteral("time"), QString::number(d->transformResult.elapsedTime));
0205 
0206     // save calculated columns if available
0207     if (saveCalculations() && d->xColumn && d->yColumn) {
0208         d->xColumn->save(writer);
0209         d->yColumn->save(writer);
0210     }
0211     writer->writeEndElement(); //"transformResult"
0212     writer->writeEndElement(); //"xyHilbertTransformCurve"
0213 }
0214 
0215 //! Load from XML
0216 bool XYHilbertTransformCurve::load(XmlStreamReader* reader, bool preview) {
0217     Q_D(XYHilbertTransformCurve);
0218 
0219     QXmlStreamAttributes attribs;
0220     QString str;
0221 
0222     while (!reader->atEnd()) {
0223         reader->readNext();
0224         if (reader->isEndElement() && reader->name() == QLatin1String("xyHilbertTransformCurve"))
0225             break;
0226 
0227         if (!reader->isStartElement())
0228             continue;
0229 
0230         if (reader->name() == QLatin1String("xyAnalysisCurve")) {
0231             if (!XYAnalysisCurve::load(reader, preview))
0232                 return false;
0233         } else if (!preview && reader->name() == QLatin1String("transformData")) {
0234             attribs = reader->attributes();
0235             READ_INT_VALUE("autoRange", transformData.autoRange, bool);
0236             READ_DOUBLE_VALUE("xRangeMin", transformData.xRange.first());
0237             READ_DOUBLE_VALUE("xRangeMax", transformData.xRange.last());
0238             READ_INT_VALUE("type", transformData.type, nsl_hilbert_result_type);
0239         } else if (!preview && reader->name() == QLatin1String("transformResult")) {
0240             attribs = reader->attributes();
0241             READ_INT_VALUE("available", transformResult.available, int);
0242             READ_INT_VALUE("valid", transformResult.valid, int);
0243             READ_STRING_VALUE("status", transformResult.status);
0244             READ_INT_VALUE("time", transformResult.elapsedTime, int);
0245         } else if (reader->name() == QLatin1String("column")) {
0246             Column* column = new Column(QString(), AbstractColumn::ColumnMode::Double);
0247             if (!column->load(reader, preview)) {
0248                 delete column;
0249                 return false;
0250             }
0251 
0252             if (column->name() == QLatin1String("x"))
0253                 d->xColumn = column;
0254             else if (column->name() == QLatin1String("y"))
0255                 d->yColumn = column;
0256         } else { // unknown element
0257             reader->raiseUnknownElementWarning();
0258             if (!reader->skipToEndElement())
0259                 return false;
0260         }
0261     }
0262 
0263     if (preview)
0264         return true;
0265 
0266     // wait for data to be read before using the pointers
0267     QThreadPool::globalInstance()->waitForDone();
0268 
0269     if (d->xColumn && d->yColumn) {
0270         d->xColumn->setHidden(true);
0271         addChild(d->xColumn);
0272 
0273         d->yColumn->setHidden(true);
0274         addChild(d->yColumn);
0275 
0276         d->xVector = static_cast<QVector<double>*>(d->xColumn->data());
0277         d->yVector = static_cast<QVector<double>*>(d->yColumn->data());
0278 
0279         static_cast<XYCurvePrivate*>(d_ptr)->xColumn = d->xColumn;
0280         static_cast<XYCurvePrivate*>(d_ptr)->yColumn = d->yColumn;
0281 
0282         recalcLogicalPoints();
0283     }
0284 
0285     return true;
0286 }