Files
2025-02-02 12:33:23 -08:00

971 lines
27 KiB
C++

#include "widegraph.h"
#include <algorithm>
#include <cmath>
#include <QElapsedTimer>
#include <QMenu>
#include <QMutexLocker>
#include <QSettings>
#include <QSignalBlocker>
#include <QTimer>
#include "ui_widegraph.h"
#include "Configuration.hpp"
#include "DriftingDateTime.h"
#include "EventFilter.hpp"
#include "MessageBox.hpp"
#include "SettingsGroup.hpp"
#include "varicode.h"
#include "moc_widegraph.cpp"
namespace
{
auto const user_defined = QObject::tr ("User Defined");
// Time formats; we're likely only ever to use the second.
constexpr QStringView TIME_FORMAT_MINS = u"hh:mm";
constexpr QStringView TIME_FORMAT_SECS = u"hh:mm:ss";
constexpr auto
timeFormat(int const period)
{
return period < 60
? TIME_FORMAT_SECS
: TIME_FORMAT_MINS;
}
// Set the spinbox to the value, ensuring that signals are
// blocked during the set operation and restoring the prior
// blocked state afterward.
void
setValueBlocked(int const value,
QSpinBox * block)
{
QSignalBlocker blocker(block);
block->setValue(value);
};
// Set the dial to the value, ensuring that signals are
// blocked during the set operation and restoring the prior
// blocked state afterward.
void
setValueBlocked(int const value,
QDial * block)
{
QSignalBlocker blocker(block);
block->setValue(value);
};
// Set the checkbox to the value, ensuring that signals are
// blocked during the set operation and restoring the prior
// blocked state afterward.
void
setValueBlocked(bool const value,
QCheckBox * block)
{
QSignalBlocker blocker(block);
block->setChecked(value);
};
}
WideGraph::WideGraph(QSettings * settings,
QWidget * parent)
: QWidget {parent}
, ui {new Ui::WideGraph}
, m_settings {settings}
, m_drawTimer {new QTimer(this)}
, m_autoSyncTimer {new QTimer(this)}
, m_palettes_path {":/Palettes"}
, m_timeFormat {timeFormat(m_TRperiod)}
{
ui->setupUi(this);
setMaximumHeight (880);
ui->splitter->setChildrenCollapsible(false);
ui->splitter->setCollapsible(ui->splitter->indexOf(ui->controls_widget), false);
ui->splitter->updateGeometry();
// If the escape key is pressed while the filter center spin box has focus,
// put the default value in the filter center field.
ui->filterCenterSpinBox->installEventFilter(new EventFilter::EscapeKeyPress([this](QKeyEvent *)
{
setFilterCenter(1500);
return true;
}, this));
// If the escape key is pressed while the filter width spin box has focus,
// put the default value in the filter width field.
ui->filterWidthSpinBox->installEventFilter(new EventFilter::EscapeKeyPress([this](QKeyEvent *)
{
setFilterWidth(2000);
return true;
}, this));
// The filter center and width spin boxes are the lead controls; any time the associated
// dials change, ensure the spin boxes are updated to the value of the dials. Same thing
// in the reverse direction, but with events blocked to avoid recursion.
connect(ui->filterCenterDial, &QDial::valueChanged, ui->filterCenterSpinBox, &QSpinBox::setValue);
connect(ui->filterWidthDial, &QDial::valueChanged, ui->filterWidthSpinBox, &QSpinBox::setValue);
connect(ui->filterCenterSpinBox,
QOverload<int>::of(&QSpinBox::valueChanged),
this,
[this](int const value)
{
setValueBlocked(value, ui->filterCenterDial);
});
connect(ui->filterWidthSpinBox,
QOverload<int>::of(&QSpinBox::valueChanged),
this,
[this](int const value)
{
setValueBlocked(value, ui->filterWidthDial);
});
ui->widePlot->setCursor(Qt::CrossCursor);
ui->widePlot->setMaximumWidth(WF::MaxScreenWidth);
ui->widePlot->setMaximumHeight(800);
ui->widePlot->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->widePlot, &CPlotter::customContextMenuRequested, this, [this](const QPoint &pos){
auto menu = new QMenu(this);
auto const f = ui->widePlot->frequencyAt(pos.x());
auto offsetAction = menu->addAction(QString("Set &Offset to %1 Hz").arg(f));
connect(offsetAction, &QAction::triggered, this, [this, f](){
ui->offsetSpinBox->setValue(f);
});
menu->addSeparator();
if(m_filterEnabled){
auto disableAction = menu->addAction(QString("&Disable Filter"));
connect(disableAction, &QAction::triggered, this, [this](){
ui->filterCheckBox->setChecked(false);
});
}
auto centerAction = menu->addAction(QString("Set Filter &Center to %1 Hz").arg(f));
connect(centerAction, &QAction::triggered, this, [this, f](){
ui->filterCenterSpinBox->setValue(f);
ui->filterCheckBox->setChecked(true);
});
auto widthMenu = menu->addMenu("Set Filter &Width to...");
auto widths = QList<int>{ 25, 50, 75, 100, 250, 500, 750, 1000, 1500, 2000 };
foreach(auto width, widths){
if(width < m_filterMinWidth){ continue; }
auto widthAction = widthMenu->addAction(QString("%1 Hz").arg(width));
connect(widthAction, &QAction::triggered, this, [this, width](){
ui->filterWidthSpinBox->setValue(width);
ui->filterCheckBox->setChecked(true);
});
}
menu->popup(ui->widePlot->mapToGlobal(pos));
});
connect(ui->widePlot, &CPlotter::changeFreq, this, &WideGraph::changeFreq);
{
//Restore user's settings
SettingsGroup g {m_settings, "WideGraph"};
restoreGeometry (m_settings->value ("geometry", saveGeometry ()).toByteArray ());
ui->widePlot->setPlotZero(m_settings->value("PlotZero", 0).toInt());
ui->widePlot->setPlotGain(m_settings->value("PlotGain", 0).toInt());
ui->widePlot->setPlot2dGain(m_settings->value("Plot2dGain", 0).toInt());
ui->widePlot->setPlot2dZero(m_settings->value("Plot2dZero", 0).toInt());
ui->widePlot->setFlatten(m_settings->value("Flatten", true).toBool());
ui->widePlot->setBinsPerPixel(m_settings->value("BinsPerPixel", 2).toInt());
ui->widePlot->setPercent2D(m_settings->value("Percent2D", 0).toInt());
ui->zeroSlider->setValue(ui->widePlot->plotZero());
ui->gainSlider->setValue(ui->widePlot->plotGain());
ui->gain2dSlider->setValue(ui->widePlot->plot2dGain());
ui->zero2dSlider->setValue(ui->widePlot->plot2dZero());
ui->cbFlatten->setChecked(ui->widePlot->flatten());
ui->bppSpinBox->setValue(ui->widePlot->binsPerPixel());
ui->sbPercent2dPlot->setValue(ui->widePlot->percent2D());
m_nsmo=m_settings->value("SmoothYellow",1).toInt();
ui->smoSpinBox->setValue(m_nsmo);
m_waterfallAvg = m_settings->value("WaterfallAvg", 1).toInt();
ui->waterfallAvgSpinBox->setValue(m_waterfallAvg);
ui->widePlot->setWaterfallAvg(m_waterfallAvg);
ui->widePlot->setSpectrum(m_settings->value("WaterfallSpectrum", QVariant::fromValue(WF::Spectrum::Current)).value<WF::Spectrum>());
if(ui->widePlot->spectrum() == WF::Spectrum::Current) ui->spec2dComboBox->setCurrentIndex(0);
if(ui->widePlot->spectrum() == WF::Spectrum::Cumulative) ui->spec2dComboBox->setCurrentIndex(1);
if(ui->widePlot->spectrum() == WF::Spectrum::LinearAvg) ui->spec2dComboBox->setCurrentIndex(2);
ui->widePlot->setStartFreq(m_settings->value("StartFreq", 500).toInt());
ui->centerSpinBox->setValue(m_settings->value("CenterOffset", 1500).toInt());
ui->fStartSpinBox->setValue(ui->widePlot->startFreq());
m_waterfallPalette=m_settings->value("WaterfallPalette","Default").toString();
m_userPalette = WF::Palette {m_settings->value("UserPalette").value<WF::Palette::Colours> ()};
ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool());
ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt());
ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool());
ui->autoDriftAutoStopCheckBox->setChecked(m_settings->value ("StopAutoSyncOnDecode", true).toBool());
ui->autoDriftStopSpinBox->setValue(m_settings->value ("StopAutoSyncAfter", 1).toInt());
auto splitState = m_settings->value("SplitState").toByteArray();
if(!splitState.isEmpty()){
ui->splitter->restoreState(splitState);
}
setFilterCenter (m_settings->value("FilterCenter", 1500).toInt());
setFilterWidth (m_settings->value("FilterWidth", 2000).toInt());
setFilterOpacityPercent(m_settings->value("FilterOpacityPercent", 50).toInt());
setFilterEnabled (m_settings->value("FilterEnabled", true).toBool());
}
int index = 0;
for (QString const & file :m_palettes_path.entryList(QDir::NoDotAndDotDot |
QDir::System |
QDir::Hidden |
QDir::AllDirs |
QDir::Files,
QDir::DirsFirst))
{
QString t = file.mid(0, file.length() - 4);
ui->paletteComboBox->addItem(t);
if (t == m_waterfallPalette) ui->paletteComboBox->setCurrentIndex(index);
index++;
}
ui->paletteComboBox->addItem(user_defined);
if (user_defined == m_waterfallPalette) ui->paletteComboBox->setCurrentIndex(index);
readPalette();
connect(m_drawTimer, &QTimer::timeout, this, [this]
{
auto const fps = std::clamp(ui->fpsSpinBox->value(), 1, 100);
qint64 const loopMs = 1000 / (fps * devicePixelRatio()) * m_waterfallAvg;
QElapsedTimer timer;
// Start the elapsed timer and do the drawing, unless we're paused.
timer.start();
if (!m_paused)
{
QMutexLocker lock(&m_drawLock);
// Draw the tr cycle horizontal lines if needed.
auto const now = DriftingDateTime::currentDateTimeUtc();
auto const secondInToday = now.time().msecsSinceStartOfDay() / 1000;
int const secondInPeriod = secondInToday % m_TRperiod;
if (secondInPeriod < m_lastSecondInPeriod)
{
ui->widePlot->drawLine(now.toString(m_timeFormat).append(m_band));
}
m_lastSecondInPeriod = secondInPeriod;
// Draw the data, handing the plotter a copy, and informing them
// of our current state.
ui->widePlot->drawData(m_swide, m_state);
// Whatever our state was, the sink is now drained until new data
// arrives; data we draw until then will duplicate the operation
// we just performed.
m_state = WF::Sink::Drained;
}
// Compute the processing time and adjust loop to hit the next frame.
m_drawTimer->start(std::max(std::chrono::milliseconds(loopMs - timer.elapsed()),
std::chrono::milliseconds::zero()));
});
m_drawTimer->setTimerType(Qt::PreciseTimer);
m_drawTimer->setSingleShot(true);
m_drawTimer->start(100); //### Don't change the 100 ms! ###
}
WideGraph::~WideGraph() = default;
void
WideGraph::closeEvent (QCloseEvent * event)
{
saveSettings();
QWidget::closeEvent(event);
}
void WideGraph::saveSettings() //saveSettings
{
SettingsGroup g {m_settings, "WideGraph"};
m_settings->setValue ("geometry", saveGeometry ());
m_settings->setValue ("PlotZero", ui->widePlot->plotZero());
m_settings->setValue ("PlotGain", ui->widePlot->plotGain());
m_settings->setValue ("Plot2dGain", ui->widePlot->plot2dGain());
m_settings->setValue ("Plot2dZero", ui->widePlot->plot2dZero());
m_settings->setValue ("SmoothYellow", ui->smoSpinBox->value ());
m_settings->setValue ("Percent2D", ui->widePlot->percent2D());
m_settings->setValue ("WaterfallAvg", ui->waterfallAvgSpinBox->value ());
m_settings->setValue ("WaterfallSpectrum", QVariant::fromValue(ui->widePlot->spectrum()));
m_settings->setValue ("BinsPerPixel", ui->widePlot->binsPerPixel ());
m_settings->setValue ("StartFreq", ui->widePlot->startFreq ());
m_settings->setValue ("WaterfallPalette", m_waterfallPalette);
m_settings->setValue ("UserPalette", QVariant::fromValue (m_userPalette.colours ()));
m_settings->setValue ("Flatten", ui->widePlot->flatten());
m_settings->setValue ("HideControls", ui->controls_widget->isHidden ());
m_settings->setValue ("CenterOffset", ui->centerSpinBox->value());
m_settings->setValue ("FilterCenter", m_filterCenter);
m_settings->setValue ("FilterWidth", m_filterWidth);
m_settings->setValue ("FilterEnabled", m_filterEnabled);
m_settings->setValue ("FilterOpacityPercent", ui->filterOpacitySpinBox->value());
m_settings->setValue ("SplitState", ui->splitter->saveState());
m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value());
m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked());
m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked());
m_settings->setValue ("StopAutoSyncAfter", ui->autoDriftStopSpinBox->value());
}
bool
WideGraph::shouldDisplayDecodeAttempts() const
{
return ui->decodeAttemptCheckBox->isChecked();
}
bool
WideGraph::isAutoSyncEnabled() const
{
// enabled if we're auto drifting
// and we are not auto stopping
// or if we are auto stopping,
// we have auto sync decodes left
return ui->autoDriftButton->isChecked() && (
!ui->autoDriftAutoStopCheckBox->isChecked() ||
m_autoSyncDecodesLeft > 0
);
}
bool
WideGraph::shouldAutoSyncSubmode(int const submode) const
{
return isAutoSyncEnabled() && (
submode == Varicode::JS8CallSlow
|| submode == Varicode::JS8CallNormal
// || submode == Varicode::JS8CallFast
// || submode == Varicode::JS8CallTurbo
// || submode == Varicode::JS8CallUltra
);
}
void
WideGraph::notifyDriftedSignalsDecoded(int const signalsDecoded)
{
//qDebug() << "decoded" << signalsDecoded << "with" << m_autoSyncDecodesLeft << "left";
m_autoSyncDecodesLeft -= signalsDecoded;
if(ui->autoDriftAutoStopCheckBox->isChecked() && m_autoSyncDecodesLeft <= 0)
{
ui->autoDriftButton->setChecked(false);
}
}
void
WideGraph::on_autoDriftButton_toggled(bool const checked)
{
if (!m_autoSyncConnected)
{
connect(m_autoSyncTimer, &QTimer::timeout, this, [this]()
{
// if auto drift isn't checked, don't worry about this...
if (!ui->autoDriftButton->isChecked()) return;
// uncheck after timeout
if (m_autoSyncTimeLeft == 0)
{
ui->autoDriftButton->setChecked(false);
return;
}
// set new text and decrement timeleft
auto const text = ui->autoDriftButton->text();
auto const newText = QString("%1 (%2)")
.arg(text.left(text.indexOf("(")).trimmed())
.arg(m_autoSyncTimeLeft--);
ui->autoDriftButton->setText(newText);
});
m_autoSyncConnected = true;
}
// if in the future we want to auto sync timeout after a time period
auto const autoSyncTimeout = false;
auto text = ui->autoDriftButton->text();
if (autoSyncTimeout)
{
if (checked)
{
m_autoSyncTimeLeft = 120;
m_autoSyncTimer->setInterval(1000);
m_autoSyncTimer->start();
ui->autoDriftButton->setText(QString("%1 (%2)")
.arg(text.replace("Start", "Stop"))
.arg(m_autoSyncTimeLeft--));
}
else
{
m_autoSyncTimeLeft = 0;
m_autoSyncTimer->stop();
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start"));
}
}
else
{
if (checked)
{
m_autoSyncDecodesLeft = ui->autoDriftStopSpinBox->value();
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop"));
ui->autoDriftStopSpinBox->setEnabled(false);
}
else
{
m_autoSyncDecodesLeft = 0;
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start"));
ui->autoDriftStopSpinBox->setEnabled(true);
}
}
}
void
WideGraph::drawDecodeLine(QColor const & color,
int const ia,
int const ib)
{
ui->widePlot->drawDecodeLine(color, ia, ib);
}
void
WideGraph::drawHorizontalLine(QColor const & color,
int const x,
int const width)
{
ui->widePlot->drawHorizontalLine(color, x, width);
}
void
WideGraph::dataSink(WF::SPlot const & s,
float const df3)
{
QMutexLocker lock(&m_drawLock);
// If we need a fresh picture, just copy the entirety of the inbound
// data. Otherwise, we're somewhere in the process of averaging data,
// so add to what we've already accumulated.
if (m_waterfallNow == 0)
{
m_splot = s;
}
else
{
std::transform(s.begin(),
s.end(),
m_splot.begin(),
m_splot.begin(),
std::plus<>{});
}
// We can be confident at this point we've got summary data in the
// sink that needs to be drained to the plotter.
m_state |= WF::Sink::Summary;
// Either way, that was another round; see if we've hit the point at
// which we should prepare results to be flushed to the plotter. Note
// that m_waterfallAvg can change at any time, so we must be defensive
// here; it could have been a high value last round, and is now a low
// one.
if (++m_waterfallNow >= m_waterfallAvg)
{
// We've now hit the averaging threshold, and are ready to commit
// data to be flushed to the plotter, which is a bit of a schlep:
//
// 1. Each source value must be averaged over the number of
// accumulations that it took to get to this point.
// 2. Source values must be reduced from bins to pixels.
// 3. Values must be converted from power scaled to dB scaled.
//
// Fortunately, we can manage that in a single pass, and can work
// only on the data to be displayed, rather than all of it.
auto const bpp = ui->widePlot->binsPerPixel();
auto sit = m_splot.begin() + static_cast<int>(ui->widePlot->startFreq() / df3 + 0.5f);
auto it = m_swide.begin();
auto const end = it + std::min(m_swide.size(), static_cast<std::size_t>(5000.0f / (bpp * df3)));
auto const avg = [runs = m_waterfallNow](auto const value)
{
return value / runs;
};
for (; it != end; ++it, sit += bpp)
{
*it = 10.0f * std::log10(bpp * std::transform_reduce(sit,
sit + bpp,
0.0f,
std::plus<>{},
avg));
}
// Next round, we'll need a fresh picture, and we've now progressed
// to having current data in the sink.
m_waterfallNow = 0;
m_state |= WF::Sink::Current;
}
}
void
WideGraph::on_bppSpinBox_valueChanged(int const n)
{
ui->widePlot->setBinsPerPixel(n);
}
void
WideGraph::on_qsyPushButton_clicked()
{
emit qsy(freq() - centerFreq());
}
void
WideGraph::on_offsetSpinBox_valueChanged(int const n)
{
if (n == freq()) return;
// TODO: jsherer - here's where we'd set minimum frequency again (later?)
auto const newFreq = qMax(0, n);
setFreq(newFreq);
emit changeFreq(newFreq);
}
void
WideGraph::on_waterfallAvgSpinBox_valueChanged(int const n)
{
m_waterfallAvg = n;
ui->widePlot->setWaterfallAvg(n);
}
void
WideGraph::keyPressEvent(QKeyEvent * event)
{
switch (event->key())
{
case Qt::Key_F11:
emit f11f12(11);
break;
case Qt::Key_F12:
emit f11f12(12);
break;
default:
event->ignore();
}
}
int
WideGraph::freq() const
{
return ui->widePlot->freq();
}
int
WideGraph::centerFreq() const
{
return ui->centerSpinBox->value();
}
int
WideGraph::nStartFreq() const
{
return ui->widePlot->startFreq();
}
int
WideGraph::filterMinimum() const
{
return std::clamp(m_filterCenter - m_filterWidth / 2, 0, 5000);
}
int
WideGraph::filterMaximum() const
{
return std::clamp(m_filterCenter + m_filterWidth / 2, 0, 5000);
}
bool
WideGraph::filterEnabled() const
{
return m_filterEnabled;
}
void
WideGraph::setFilterCenter(int const value)
{
m_filterCenter = value;
setValueBlocked(value, ui->filterCenterSpinBox);
setValueBlocked(value, ui->filterCenterDial);
ui->widePlot->setFilter(m_filterCenter,
m_filterWidth);
}
void
WideGraph::setFilterWidth(int const value)
{
m_filterWidth = value;
setValueBlocked(value, ui->filterWidthSpinBox);
setValueBlocked(value, ui->filterWidthDial);
ui->widePlot->setFilter(m_filterCenter,
m_filterWidth);
}
void
WideGraph::setFilterMinimumBandwidth(int const width)
{
m_filterMinWidth = width;
ui->filterWidthSpinBox->setMinimum(width);
ui->filterWidthDial->setMinimum(width);
setFilterWidth(std::max(m_filterWidth, width));
}
void
WideGraph::setFilterEnabled(bool enabled)
{
m_filterEnabled = enabled;
ui->filterCenterSyncButton->setEnabled(enabled);
ui->filterCenterSpinBox->setEnabled(enabled);
ui->filterCenterDial->setEnabled(enabled);
ui->filterWidthSpinBox->setEnabled(enabled);
ui->filterWidthDial->setEnabled(enabled);
setValueBlocked(enabled, ui->filterCheckBox);
ui->widePlot->setFilterEnabled(enabled);
}
void
WideGraph::setFilterOpacityPercent(int const n)
{
setValueBlocked(n, ui->filterOpacitySpinBox);
ui->widePlot->setFilterOpacity(int((float(n)/100.0)*255));
}
void
WideGraph::setPeriod(int const ntrperiod)
{
m_TRperiod = ntrperiod;
m_timeFormat = timeFormat(m_TRperiod);
}
void
WideGraph::setFreq(int const n)
{
emit setXIT(n);
ui->widePlot->setFreq(n);
ui->offsetSpinBox->setValue(n);
}
void
WideGraph::setSubMode(int const n)
{
ui->widePlot->setSubMode(n);
}
void
WideGraph::on_spec2dComboBox_currentIndexChanged(int const index)
{
ui->smoSpinBox->setEnabled(false);
switch (index)
{
case 0:
ui->widePlot->setSpectrum(WF::Spectrum::Current);
break;
case 1:
ui->widePlot->setSpectrum(WF::Spectrum::Cumulative);
break;
case 2:
ui->widePlot->setSpectrum(WF::Spectrum::LinearAvg);
ui->smoSpinBox->setEnabled(true);
break;
}
}
void
WideGraph::setDialFreq(float const dialFreq)
{
ui->widePlot->setDialFreq(dialFreq);
}
void
WideGraph::setTimeControlsVisible(bool const visible)
{
setControlsVisible(visible, false);
ui->tabWidget->setCurrentWidget(ui->timingTab);
}
bool
WideGraph::timeControlsVisible() const
{
return controlsVisible() && ui->tabWidget->currentWidget() == ui->timingTab;
}
void
WideGraph::setControlsVisible(bool const visible,
bool const controlTab)
{
if (ui->controls_widget->isVisible() != visible)
{
if (visible)
{
if (m_sizes.isEmpty())
{
auto const width = ui->splitter->width();
m_sizes = {width,
width / 4};
}
ui->splitter->setSizes(m_sizes);
if (controlTab)
{
ui->tabWidget->setCurrentWidget(ui->controlTab);
}
}
else
{
m_sizes = ui->splitter->sizes();
}
ui->controls_widget->setVisible(visible);
}
}
bool
WideGraph::controlsVisible() const
{
return ui->controls_widget->isVisible();
}
void
WideGraph::setBand(QString const & band)
{
m_band = QString(4, ' ').append(band);
}
void
WideGraph::on_fStartSpinBox_valueChanged(int const n)
{
ui->widePlot->setStartFreq(n);
}
void
WideGraph::readPalette()
{
try
{
ui->widePlot->setColors(user_defined == m_waterfallPalette
? WF::Palette{m_userPalette}.interpolate()
: WF::Palette{m_palettes_path.absoluteFilePath(m_waterfallPalette + ".pal")}.interpolate());
}
catch (std::exception const & e)
{
MessageBox::warning_message(this, tr("Read Palette"), e.what());
}
}
void
WideGraph::on_paletteComboBox_activated(int const palette_index)
{
m_waterfallPalette = ui->paletteComboBox->itemText(palette_index);
readPalette();
}
void
WideGraph::on_cbFlatten_toggled(bool const flatten)
{
ui->widePlot->setFlatten(flatten);
}
void
WideGraph::on_adjust_palette_push_button_clicked(bool)
{
try
{
if (m_userPalette.design ())
{
m_waterfallPalette = user_defined;
ui->paletteComboBox->setCurrentText(m_waterfallPalette);
readPalette();
}
}
catch (std::exception const & e)
{
MessageBox::warning_message(this, tr("Read Palette"), e.what());
}
}
void
WideGraph::on_gainSlider_valueChanged(int const value)
{
ui->widePlot->setPlotGain(value);
}
void
WideGraph::on_zeroSlider_valueChanged(int const value)
{
ui->widePlot->setPlotZero(value);
}
void
WideGraph::on_gain2dSlider_valueChanged(int const value)
{
ui->widePlot->setPlot2dGain(value);
}
void
WideGraph::on_zero2dSlider_valueChanged(int const value)
{
ui->widePlot->setPlot2dZero(value);
}
void
WideGraph::on_smoSpinBox_valueChanged(int const n)
{
m_nsmo = n;
}
int
WideGraph::smoothYellow() const
{
return m_nsmo;
}
void
WideGraph::on_sbPercent2dPlot_valueChanged(int const n)
{
ui->widePlot->setPercent2D(n);
}
void
WideGraph::on_filterCenterSpinBox_valueChanged(int const value)
{
if (!ui->filterCenterSpinBox->hasFocus()) setFilterCenter(value);
}
void
WideGraph::on_filterCenterSpinBox_editingFinished()
{
setFilterCenter(ui->filterCenterSpinBox->value());
}
void
WideGraph::on_filterWidthSpinBox_valueChanged(int const value)
{
if (!ui->filterWidthSpinBox->hasFocus()) setFilterWidth(value);
}
void
WideGraph::on_filterWidthSpinBox_editingFinished()
{
setFilterWidth( ui->filterWidthSpinBox->value());
}
void
WideGraph::on_filterCenterSyncButton_clicked()
{
setFilterCenter(ui->offsetSpinBox->value());
}
void
WideGraph::on_filterCheckBox_toggled(bool const b)
{
setFilterEnabled(b);
}
void
WideGraph::on_filterOpacitySpinBox_valueChanged(int const n)
{
setFilterOpacityPercent(n);
}
void
WideGraph::on_driftSpinBox_valueChanged(int const n)
{
if (n != DriftingDateTime::drift()) setDrift(n);
}
void
WideGraph::on_driftSyncButton_clicked()
{
auto const now = QDateTime::currentDateTimeUtc();
int const pos = m_TRperiod - (now.time().second() % m_TRperiod);
int const neg = (now.time().second() % m_TRperiod) - m_TRperiod;
auto const sec = abs(neg) < pos ? neg : pos;
setDrift(sec * 1000);
}
void
WideGraph::on_driftSyncEndButton_clicked()
{
auto const now = QDateTime::currentDateTimeUtc();
int const pos = m_TRperiod - (now.time().second() % m_TRperiod);
int const neg = (now.time().second() % m_TRperiod) - m_TRperiod;
auto const sec = abs(neg) < pos ? neg + 2 : pos - 2;
setDrift(sec * 1000);
}
void
WideGraph::on_driftSyncMinuteButton_clicked()
{
auto const now = QDateTime::currentDateTimeUtc();
auto const val = now.time().second();
auto const sec = val < 30 ? -val : 60 - val;
setDrift(sec * 1000);
}
void
WideGraph::on_driftSyncResetButton_clicked()
{
setDrift(0);
}
void
WideGraph::setDrift(int const n)
{
auto const prev = drift();
DriftingDateTime::setDrift(n);
qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n;
qDebug() << qSetRealNumberPrecision(12) << "Clock time:" << QDateTime::currentDateTimeUtc();
qDebug() << qSetRealNumberPrecision(12) << "Drifted time:" << DriftingDateTime::currentDateTimeUtc();
if (ui->driftSpinBox->value() != n) ui->driftSpinBox->setValue(n);
emit drifted(prev, n);
}
int
WideGraph::drift() const
{
return DriftingDateTime::drift();
}