mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
Upgrade to QWT 6.0.1, but still uses a locally patched copy since support for 8 axes has not been included, despite it being a relatively simple patch. Fixes #634. Fixes #567.
187 lines
4.9 KiB
C++
187 lines
4.9 KiB
C++
#include <qwt_math.h>
|
|
#include <qwt_scale_engine.h>
|
|
#include <qwt_symbol.h>
|
|
#include <qwt_plot_grid.h>
|
|
#include <qwt_plot_marker.h>
|
|
#include <qwt_plot_curve.h>
|
|
#include <qwt_legend.h>
|
|
#include <qwt_text.h>
|
|
#include <qmath.h>
|
|
#include "complexnumber.h"
|
|
#include "plot.h"
|
|
|
|
#if QT_VERSION < 0x040601
|
|
#define qExp(x) ::exp(x)
|
|
#define qAtan2(y, x) ::atan2(y, x)
|
|
#endif
|
|
|
|
static void logSpace(double *array, int size, double xmin, double xmax)
|
|
{
|
|
if ((xmin <= 0.0) || (xmax <= 0.0) || (size <= 0))
|
|
return;
|
|
|
|
const int imax = size -1;
|
|
|
|
array[0] = xmin;
|
|
array[imax] = xmax;
|
|
|
|
const double lxmin = log(xmin);
|
|
const double lxmax = log(xmax);
|
|
const double lstep = (lxmax - lxmin) / double(imax);
|
|
|
|
for (int i = 1; i < imax; i++)
|
|
array[i] = qExp(lxmin + double(i) * lstep);
|
|
}
|
|
|
|
Plot::Plot(QWidget *parent):
|
|
QwtPlot(parent)
|
|
{
|
|
setAutoReplot(false);
|
|
|
|
setTitle("Frequency Response of a Second-Order System");
|
|
|
|
setCanvasBackground(QColor(Qt::darkBlue));
|
|
|
|
// legend
|
|
QwtLegend *legend = new QwtLegend;
|
|
insertLegend(legend, QwtPlot::BottomLegend);
|
|
|
|
// grid
|
|
QwtPlotGrid *grid = new QwtPlotGrid;
|
|
grid->enableXMin(true);
|
|
grid->setMajPen(QPen(Qt::white, 0, Qt::DotLine));
|
|
grid->setMinPen(QPen(Qt::gray, 0 , Qt::DotLine));
|
|
grid->attach(this);
|
|
|
|
// axes
|
|
enableAxis(QwtPlot::yRight);
|
|
setAxisTitle(QwtPlot::xBottom, "Normalized Frequency");
|
|
setAxisTitle(QwtPlot::yLeft, "Amplitude [dB]");
|
|
setAxisTitle(QwtPlot::yRight, "Phase [deg]");
|
|
|
|
setAxisMaxMajor(QwtPlot::xBottom, 6);
|
|
setAxisMaxMinor(QwtPlot::xBottom, 10);
|
|
setAxisScaleEngine(QwtPlot::xBottom, new QwtLog10ScaleEngine);
|
|
|
|
// curves
|
|
d_curve1 = new QwtPlotCurve("Amplitude");
|
|
d_curve1->setRenderHint(QwtPlotItem::RenderAntialiased);
|
|
d_curve1->setPen(QPen(Qt::yellow));
|
|
d_curve1->setLegendAttribute(QwtPlotCurve::LegendShowLine);
|
|
d_curve1->setYAxis(QwtPlot::yLeft);
|
|
d_curve1->attach(this);
|
|
|
|
d_curve2 = new QwtPlotCurve("Phase");
|
|
d_curve2->setRenderHint(QwtPlotItem::RenderAntialiased);
|
|
d_curve2->setPen(QPen(Qt::cyan));
|
|
d_curve2->setLegendAttribute(QwtPlotCurve::LegendShowLine);
|
|
d_curve2->setYAxis(QwtPlot::yRight);
|
|
d_curve2->attach(this);
|
|
|
|
// marker
|
|
d_marker1 = new QwtPlotMarker();
|
|
d_marker1->setValue(0.0, 0.0);
|
|
d_marker1->setLineStyle(QwtPlotMarker::VLine);
|
|
d_marker1->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
|
|
d_marker1->setLinePen(QPen(Qt::green, 0, Qt::DashDotLine));
|
|
d_marker1->attach(this);
|
|
|
|
d_marker2 = new QwtPlotMarker();
|
|
d_marker2->setLineStyle(QwtPlotMarker::HLine);
|
|
d_marker2->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
|
|
d_marker2->setLinePen(QPen(QColor(200,150,0), 0, Qt::DashDotLine));
|
|
d_marker2->setSymbol( new QwtSymbol(QwtSymbol::Diamond,
|
|
QColor(Qt::yellow), QColor(Qt::green), QSize(7,7)));
|
|
d_marker2->attach(this);
|
|
|
|
setDamp(0.0);
|
|
|
|
setAutoReplot(true);
|
|
}
|
|
|
|
void Plot::showData(const double *frequency, const double *amplitude,
|
|
const double *phase, int count)
|
|
{
|
|
d_curve1->setSamples(frequency, amplitude, count);
|
|
d_curve2->setSamples(frequency, phase, count);
|
|
}
|
|
|
|
void Plot::showPeak(double freq, double amplitude)
|
|
{
|
|
QString label;
|
|
label.sprintf("Peak: %.3g dB", amplitude);
|
|
|
|
QwtText text(label);
|
|
text.setFont(QFont("Helvetica", 10, QFont::Bold));
|
|
text.setColor(QColor(200,150,0));
|
|
|
|
d_marker2->setValue(freq, amplitude);
|
|
d_marker2->setLabel(text);
|
|
}
|
|
|
|
void Plot::show3dB(double freq)
|
|
{
|
|
QString label;
|
|
label.sprintf("-3 dB at f = %.3g", freq);
|
|
|
|
QwtText text(label);
|
|
text.setFont(QFont("Helvetica", 10, QFont::Bold));
|
|
text.setColor(Qt::green);
|
|
|
|
d_marker1->setValue(freq, 0.0);
|
|
d_marker1->setLabel(text);
|
|
}
|
|
|
|
//
|
|
// re-calculate frequency response
|
|
//
|
|
void Plot::setDamp(double damping)
|
|
{
|
|
const bool doReplot = autoReplot();
|
|
setAutoReplot(false);
|
|
|
|
const int ArraySize = 200;
|
|
|
|
double frequency[ArraySize];
|
|
double amplitude[ArraySize];
|
|
double phase[ArraySize];
|
|
|
|
// build frequency vector with logarithmic division
|
|
logSpace(frequency, ArraySize, 0.01, 100);
|
|
|
|
int i3 = 1;
|
|
double fmax = 1;
|
|
double amax = -1000.0;
|
|
|
|
for (int i = 0; i < ArraySize; i++)
|
|
{
|
|
double f = frequency[i];
|
|
const ComplexNumber g =
|
|
ComplexNumber(1.0) / ComplexNumber(1.0 - f * f, 2.0 * damping * f);
|
|
|
|
amplitude[i] = 20.0 * log10(qSqrt( g.real() * g.real() + g.imag() * g.imag()));
|
|
phase[i] = qAtan2(g.imag(), g.real()) * (180.0 / M_PI);
|
|
|
|
if ((i3 <= 1) && (amplitude[i] < -3.0))
|
|
i3 = i;
|
|
if (amplitude[i] > amax)
|
|
{
|
|
amax = amplitude[i];
|
|
fmax = frequency[i];
|
|
}
|
|
|
|
}
|
|
|
|
double f3 = frequency[i3] -
|
|
(frequency[i3] - frequency[i3 - 1])
|
|
/ (amplitude[i3] - amplitude[i3 -1]) * (amplitude[i3] + 3);
|
|
|
|
showPeak(fmax, amax);
|
|
show3dB(f3);
|
|
showData(frequency, amplitude, phase, ArraySize);
|
|
|
|
setAutoReplot(doReplot);
|
|
|
|
replot();
|
|
}
|