LibreVNA/Software/PC_Application/LibreVNA-GUI/Traces/Math/tracemath.h
2022-12-22 22:15:03 +01:00

157 lines
5.9 KiB
C++

#ifndef TRACEMATH_H
#define TRACEMATH_H
#include "savable.h"
#include <QObject>
#include <vector>
#include <complex>
/*
* How to implement a new type of math operation:
* 1. Create your new math operation class by deriving from this class. Put the new class in the namespace
* "Math" to avoid name collisions.
* 2. Implement required virtual functions:
* a. outputType(DataType inputType):
* Indicates what kind of result your math operation creates, depending on the type of its input.
* If the input type is not applicable for your operation (e.g. frequency domain data as input for
* a (forward) DFT, you can return DataType::Invalid
* b. description():
* Return a short, user-readable description how the operation is set up. This will be displayed
* in the math edit dialog.
* c. edit():
* Optional. If your operation has customizable parameters, calling this function should start a
* dialog that allows the user to change these parameters.
* d. inputSamplesChanged(unsigned int begin, unsigned int end)
* This slot gets called whenever the input data has changed. Override it and implement your math
* operation in it. Parameters begin and end indicate which input samples have changed: If, for
* example, only the 2nd and third input values have changed, they are set like this: begin=1 end=3
* CAUTION: the size of the input vector may have changed, check before accessing it.
*
* Emit the signal outputSamplesChanged(unsigned int begin, unsigned int end) after your operation is
* finished. Also call either success(), warning() or error() at the end of this slot, depending on
* whether the operation succeeded:
* success(): everything went well, output data contains valid values
* warning(): something might be wrong (e.g. not enough input samples to create meaningful data, ...).
* Provide a hint by passing a short description string
* error(): something went wrong (called with wrong type of data, mathematical error, ...).
* Provide a hint by passing a short description string
* e. getType(): return the type of the operation
* f. toJSON() and fromJSON(). Save/load all internal parameters
* 3. Add a new type to the Type enum for your operation
* 4. Extend the createMath(Type type) factory function to create an instance of your operation
* 5. Add a static function "createExplanationWidget" which returns a QWidget explaining what your operation does.
* This will be displayed when the user chooses to add a new math operation.
* 6. Extend the function getInfo(Type type) to set a name and create the explanation widget for your operation
*/
class Trace;
class TraceMath : public QObject, public Savable {
Q_OBJECT
public:
TraceMath();
virtual ~TraceMath(){}
class Data {
public:
Data() : x(0), y(0){}
double x;
std::complex<double> y;
};
enum class DataType {
Frequency,
Time,
Power,
TimeZeroSpan,
Invalid,
};
enum class Status {
Ok,
Warning,
Error,
};
enum class Type {
MedianFilter,
TDR,
DFT,
Expression,
TimeGate,
TimeDomainGating,
// Add new math operations here, do not explicitly assign values and keep the Last entry at the last position
Last,
};
static std::vector<TraceMath*> createMath(Type type);
class TypeInfo {
public:
QString name;
QWidget *explanationWidget;
};
static TypeInfo getInfo(Type type);
virtual Data getSample(unsigned int index);
virtual Data getInterpolatedSample(double x);
double getStepResponse(unsigned int index);
double getInterpolatedStepResponse(double x);
virtual unsigned int numSamples();
static QString dataTypeToString(DataType type);
static DataType dataTypeFromString(QString s);
// indicate whether this function produces time or frequency domain data
virtual DataType outputType(DataType inputType) = 0;
virtual QString description() = 0;
virtual void edit(){}
void removeInput();
void assignInput(TraceMath *input);
DataType getDataType() const;
virtual std::vector<Data>& rData() { return data;}
Status getStatus() const;
QString getStatusDescription() const;
virtual Type getType() = 0;
// returns the trace this math operation is attached to
Trace* root();
TraceMath *getInput() const;
public slots:
// some values of the input data have changed, begin/end determine which sample(s) has changed
virtual void inputSamplesChanged(unsigned int begin, unsigned int end){Q_UNUSED(begin) Q_UNUSED(end)}
void inputTypeChanged(DataType type);
signals:
// emit this whenever a sample changed (alternatively, if all samples are about to change, emit outputDataChanged after they have changed)
void outputSamplesChanged(unsigned int begin, unsigned int end);
// emit when the output type changed
void outputTypeChanged(DataType type);
protected:
// call one of these functions in the derived classes after output data has been updated
void warning(QString warn);
void error(QString err);
void success();
std::vector<Data> data;
// buffer for time domain step response data. This makes it possible to access an arbitrary sample of the step response without having to
// integrate the impulse response every time. Call updateStepResponse in your derived class, if step response data is valid after updating
// data.
std::vector<double> stepResponse;
void updateStepResponse(bool valid);
TraceMath *input;
DataType dataType;
private:
Status status;
QString statusString;
signals:
void statusChanged();
};
#endif // TRACEMATH_H