SVN r8568

This commit is contained in:
Jordan Sherer
2018-03-17 12:56:24 -04:00
parent 587950f372
commit 45cc6416c1
633 changed files with 186 additions and 366401 deletions
@@ -1,522 +0,0 @@
#include "widegraph.h"
#include <algorithm>
#include <QApplication>
#include <QSettings>
#include "ui_widegraph.h"
#include "commons.h"
#include "Configuration.hpp"
#include "MessageBox.hpp"
#include "SettingsGroup.hpp"
#include "moc_widegraph.cpp"
namespace
{
auto user_defined = QObject::tr ("User Defined");
float swide[MAX_SCREENSIZE];
}
WideGraph::WideGraph(QSettings * settings, QWidget *parent) :
QDialog(parent),
ui(new Ui::WideGraph),
m_settings (settings),
m_palettes_path {":/Palettes"},
m_ntr0 {0},
m_lockTxFreq {false},
m_bHaveTransmitted {false},
m_n {0}
{
ui->setupUi(this);
setWindowTitle (QApplication::applicationName () + " - " + tr ("Wide Graph"));
setWindowFlags (Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint);
setMaximumWidth (MAX_SCREENSIZE);
setMaximumHeight (880);
ui->widePlot->setCursor(Qt::CrossCursor);
ui->widePlot->setMaximumHeight(800);
ui->widePlot->setCurrent(false);
connect(ui->widePlot, SIGNAL(freezeDecode1(int)),this,
SLOT(wideFreezeDecode(int)));
connect(ui->widePlot, SIGNAL(setFreq1(int,int)),this,
SLOT(setFreq2(int,int)));
{
//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->zeroSlider->setValue(ui->widePlot->plotZero());
ui->gainSlider->setValue(ui->widePlot->plotGain());
ui->gain2dSlider->setValue(ui->widePlot->plot2dGain());
ui->zero2dSlider->setValue(ui->widePlot->plot2dZero());
int n = m_settings->value("BinsPerPixel",2).toInt();
m_bFlatten=m_settings->value("Flatten",true).toBool();
m_bRef=m_settings->value("UseRef",false).toBool();
ui->cbFlatten->setChecked(m_bFlatten);
ui->widePlot->setFlatten(m_bFlatten,m_bRef);
ui->cbRef->setChecked(m_bRef);
ui->widePlot->setBreadth(m_settings->value("PlotWidth",1000).toInt());
ui->bppSpinBox->setValue(n);
m_nsmo=m_settings->value("SmoothYellow",1).toInt();
ui->smoSpinBox->setValue(m_nsmo);
m_Percent2DScreen=m_settings->value("Percent2D",30).toInt();
ui->sbPercent2dPlot->setValue(m_Percent2DScreen);
m_waterfallAvg = m_settings->value("WaterfallAvg",5).toInt();
ui->waterfallAvgSpinBox->setValue(m_waterfallAvg);
ui->widePlot->setWaterfallAvg(m_waterfallAvg);
ui->widePlot->setCurrent(m_settings->value("Current",false).toBool());
ui->widePlot->setCumulative(m_settings->value("Cumulative",true).toBool());
ui->widePlot->setLinearAvg(m_settings->value("LinearAvg",false).toBool());
ui->widePlot->setReference(m_settings->value("Reference",false).toBool());
if(ui->widePlot->current()) ui->spec2dComboBox->setCurrentIndex(0);
if(ui->widePlot->cumulative()) ui->spec2dComboBox->setCurrentIndex(1);
if(ui->widePlot->linearAvg()) ui->spec2dComboBox->setCurrentIndex(2);
if(ui->widePlot->Reference()) ui->spec2dComboBox->setCurrentIndex(3);
int nbpp=m_settings->value("BinsPerPixel",2).toInt();
ui->widePlot->setBinsPerPixel(nbpp);
ui->widePlot->setStartFreq(m_settings->value("StartFreq",0).toInt());
ui->fStartSpinBox->setValue(ui->widePlot->startFreq());
m_waterfallPalette=m_settings->value("WaterfallPalette","Default").toString();
m_userPalette = WFPalette {m_settings->value("UserPalette").value<WFPalette::Colours> ()};
int m_fMin = m_settings->value ("Fmin", 2500).toInt ();
ui->fSplitSpinBox->setValue (m_fMin);
setRxRange ();
ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool ());
}
saveSettings (); // update config with defaults
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 ();
}
WideGraph::~WideGraph ()
{
}
void WideGraph::closeEvent (QCloseEvent * e)
{
saveSettings ();
QDialog::closeEvent (e);
}
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 ("PlotWidth", ui->widePlot->plotWidth ());
m_settings->setValue ("BinsPerPixel", ui->bppSpinBox->value ());
m_settings->setValue ("SmoothYellow", ui->smoSpinBox->value ());
m_settings->setValue ("Percent2D",m_Percent2DScreen);
m_settings->setValue ("WaterfallAvg", ui->waterfallAvgSpinBox->value ());
m_settings->setValue ("Current", ui->widePlot->current());
m_settings->setValue ("Cumulative", ui->widePlot->cumulative());
m_settings->setValue ("LinearAvg", ui->widePlot->linearAvg());
m_settings->setValue ("Reference", ui->widePlot->Reference());
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",m_bFlatten);
m_settings->setValue("UseRef",m_bRef);
m_settings->setValue ("HideControls", ui->controls_widget->isHidden ());
}
void WideGraph::drawRed(int ia, int ib)
{
ui->widePlot->drawRed(ia,ib,swide);
}
void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2
{
static float splot[NSMAX];
int nbpp = ui->widePlot->binsPerPixel();
//Average spectra over specified number, m_waterfallAvg
if (m_n==0) {
for (int i=0; i<NSMAX; i++)
splot[i]=s[i];
} else {
for (int i=0; i<NSMAX; i++)
splot[i] += s[i];
}
m_n++;
if (m_n>=m_waterfallAvg) {
for (int i=0; i<NSMAX; i++)
splot[i] /= m_n; //Normalize the average
m_n=0;
int i=int(ui->widePlot->startFreq()/df3 + 0.5);
int jz=5000.0/(nbpp*df3);
if(jz>MAX_SCREENSIZE) jz=MAX_SCREENSIZE;
for (int j=0; j<jz; j++) {
float ss=0;
for (int k=0; k<nbpp; k++) {
if(splot[i]>ss) ss=splot[i];
i++;
}
swide[j]=nbpp*ss;
}
// Time according to this computer
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int ntr = (ms/1000) % m_TRperiod;
if((ndiskdata && ihsym <= m_waterfallAvg) || (!ndiskdata && ntr<m_ntr0)) {
float flagValue=1.0e30;
if(m_bHaveTransmitted) flagValue=2.0e30;
for (int i=0; i<2048; i++) {
swide[i] = flagValue;
}
m_bHaveTransmitted=false;
}
m_ntr0=ntr;
ui->widePlot->draw(swide,true,false);
}
}
void WideGraph::on_bppSpinBox_valueChanged(int n) //bpp
{
ui->widePlot->setBinsPerPixel(n);
}
void WideGraph::on_waterfallAvgSpinBox_valueChanged(int n) //Navg
{
m_waterfallAvg = n;
ui->widePlot->setWaterfallAvg(n);
}
void WideGraph::keyPressEvent(QKeyEvent *e) //F11, F12
{
switch(e->key())
{
int n;
case Qt::Key_F11:
n=11;
if(e->modifiers() & Qt::ControlModifier) n+=100;
emit f11f12(n);
break;
case Qt::Key_F12:
n=12;
if(e->modifiers() & Qt::ControlModifier) n+=100;
emit f11f12(n);
break;
case Qt::Key_M:
if(e->modifiers() & Qt::ControlModifier) {
ui->controls_widget->setVisible(!ui->controls_widget->isVisible());
}
break;
default:
QDialog::keyPressEvent (e);
}
}
void WideGraph::setRxFreq(int n) //setRxFreq
{
ui->widePlot->setRxFreq(n);
ui->widePlot->draw(swide,false,false);
if(m_lockTxFreq) setTxFreq(n);
}
int WideGraph::rxFreq() //rxFreq
{
return ui->widePlot->rxFreq();
}
int WideGraph::nStartFreq() //nStartFreq
{
return ui->widePlot->startFreq();
}
void WideGraph::wideFreezeDecode(int n) //wideFreezeDecode
{
emit freezeDecode2(n);
}
void WideGraph::setRxRange ()
{
ui->widePlot->setRxRange (Fmin ());
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
int WideGraph::Fmin() //Fmin
{
return "60m" == m_rxBand ? 0 : m_fMin;
}
int WideGraph::Fmax() //Fmax
{
return std::min(5000,ui->widePlot->Fmax());
}
int WideGraph::fSpan()
{
return ui->widePlot->fSpan ();
}
void WideGraph::setPeriod(int ntrperiod, int nsps) //SetPeriod
{
m_TRperiod=ntrperiod;
m_nsps=nsps;
ui->widePlot->setNsps(ntrperiod, nsps);
}
void WideGraph::setTxFreq(int n) //setTxFreq
{
emit setXIT2(n);
ui->widePlot->setTxFreq(n);
}
void WideGraph::setMode(QString mode) //setMode
{
m_mode=mode;
ui->fSplitSpinBox->setEnabled(m_mode=="JT9+JT65");
ui->widePlot->setMode(mode);
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
void WideGraph::setSubMode(int n) //setSubMode
{
m_nSubMode=n;
ui->widePlot->setSubMode(n);
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
void WideGraph::setModeTx(QString modeTx) //setModeTx
{
m_modeTx=modeTx;
ui->widePlot->setModeTx(modeTx);
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
//Current-Cumulative-Yellow
void WideGraph::on_spec2dComboBox_currentIndexChanged(const QString &arg1)
{
ui->widePlot->setCurrent(false);
ui->widePlot->setCumulative(false);
ui->widePlot->setLinearAvg(false);
ui->widePlot->setReference(false);
ui->smoSpinBox->setEnabled(false);
if(arg1=="Current") ui->widePlot->setCurrent(true);
if(arg1=="Cumulative") ui->widePlot->setCumulative(true);
if(arg1=="Linear Avg") {
ui->widePlot->setLinearAvg(true);
ui->smoSpinBox->setEnabled(true);
}
if(arg1=="Reference") {
ui->widePlot->setReference(true);
}
if(ui->widePlot->m_bScaleOK) ui->widePlot->draw(swide,false,false);
}
void WideGraph::on_fSplitSpinBox_valueChanged(int n) //fSplit
{
if (m_rxBand != "60m") m_fMin=n;
setRxRange ();
}
void WideGraph::setLockTxFreq(bool b) //LockTxFreq
{
m_lockTxFreq=b;
ui->widePlot->setLockTxFreq(b);
}
void WideGraph::setFreq2(int rxFreq, int txFreq) //setFreq2
{
emit setFreq3(rxFreq,txFreq);
}
void WideGraph::setDialFreq(double d) //setDialFreq
{
ui->widePlot->setDialFreq(d);
}
void WideGraph::setRxBand (QString const& band)
{
m_rxBand = band;
if ("60m" == m_rxBand)
{
ui->fSplitSpinBox->setEnabled (false);
ui->fSplitSpinBox->setValue (0);
}
else
{
ui->fSplitSpinBox->setValue (m_fMin);
ui->fSplitSpinBox->setEnabled (true);
}
ui->widePlot->setRxBand(band);
setRxRange ();
}
void WideGraph::on_fStartSpinBox_valueChanged(int n) //fStart
{
ui->widePlot->setStartFreq(n);
}
void WideGraph::readPalette () //readPalette
{
try
{
if (user_defined == m_waterfallPalette)
{
ui->widePlot->setColours (WFPalette {m_userPalette}.interpolate ());
}
else
{
ui->widePlot->setColours (WFPalette {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 (QString const& palette) //palette selector
{
m_waterfallPalette = palette;
readPalette();
}
void WideGraph::on_cbFlatten_toggled(bool b) //Flatten On/Off
{
m_bFlatten=b;
if(m_bRef and m_bFlatten) {
m_bRef=false;
ui->cbRef->setChecked(false);
}
ui->widePlot->setFlatten(m_bFlatten,m_bRef);
}
void WideGraph::on_cbRef_toggled(bool b)
{
m_bRef=b;
if(m_bRef and m_bFlatten) {
m_bFlatten=false;
ui->cbFlatten->setChecked(false);
}
ui->widePlot->setFlatten(m_bFlatten,m_bRef);
}
void WideGraph::on_cbControls_toggled(bool b)
{
ui->controls_widget->setVisible(b);
}
void WideGraph::on_adjust_palette_push_button_clicked (bool) //Adjust Palette
{
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 ());
}
}
bool WideGraph::flatten() //Flatten
{
return m_bFlatten;
}
bool WideGraph::useRef() //Flatten
{
return m_bRef;
}
void WideGraph::on_gainSlider_valueChanged(int value) //Gain
{
ui->widePlot->setPlotGain(value);
}
void WideGraph::on_zeroSlider_valueChanged(int value) //Zero
{
ui->widePlot->setPlotZero(value);
}
void WideGraph::on_gain2dSlider_valueChanged(int value) //Gain2
{
ui->widePlot->setPlot2dGain(value);
if(ui->widePlot->m_bScaleOK) {
ui->widePlot->draw(swide,false,false);
if(m_mode=="QRA64") ui->widePlot->draw(swide,false,true);
}
}
void WideGraph::on_zero2dSlider_valueChanged(int value) //Zero2
{
ui->widePlot->setPlot2dZero(value);
if(ui->widePlot->m_bScaleOK) {
ui->widePlot->draw(swide,false,false);
if(m_mode=="QRA64") ui->widePlot->draw(swide,false,true);
}
}
void WideGraph::setTol(int n) //setTol
{
ui->widePlot->setTol(n);
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
void WideGraph::on_smoSpinBox_valueChanged(int n)
{
m_nsmo=n;
}
int WideGraph::smoothYellow()
{
return m_nsmo;
}
void WideGraph::setWSPRtransmitted()
{
m_bHaveTransmitted=true;
}
void WideGraph::setVHF(bool bVHF)
{
ui->widePlot->setVHF(bVHF);
}
void WideGraph::on_sbPercent2dPlot_valueChanged(int n)
{
m_Percent2DScreen=n;
ui->widePlot->SetPercent2DScreen(n);
}
void WideGraph::setRedFile(QString fRed)
{
ui->widePlot->setRedFile(fRed);
}
@@ -1,546 +0,0 @@
#include "PhaseEqualizationDialog.hpp"
#include <iterator>
#include <algorithm>
#include <fstream>
#include <limits>
#include <cmath>
#include <QDir>
#include <QVector>
#include <QHBoxLayout>
#include <QDialog>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QFileDialog>
#include <QSettings>
#include "SettingsGroup.hpp"
#include "qcustomplot.h"
#include "pimpl_impl.hpp"
namespace
{
float constexpr PI = 3.1415927f;
char const * const title = "Phase Equalization";
size_t constexpr intervals = 144;
// plot data loaders - wraps a plot providing value_type and
// push_back so that a std::back_inserter output iterator can be
// used to load plot data
template<typename T, typename A>
struct plot_data_loader
{
public:
typedef T value_type;
// the adjust argument is a function that is passed the plot
// pointer, the graph index and a data point, it returns a
// possibly adjusted data point and can modify the graph including
// adding extra points or gaps (quiet_NaN)
plot_data_loader (QCustomPlot * plot, int graph_index, A adjust)
: plot_ {plot}
, index_ {graph_index}
, adjust_ (adjust)
{
}
// load point into graph
void push_back (value_type const& d)
{
plot_->graph (index_)->data ()->add (adjust_ (plot_, index_, d));
}
private:
QCustomPlot * plot_;
int index_;
A adjust_;
};
// helper function template to make a plot_data_loader instance
template<typename A>
auto make_plot_data_loader (QCustomPlot * plot, int index, A adjust)
-> plot_data_loader<QCPGraphData, decltype (adjust)>
{
return plot_data_loader<QCPGraphData, decltype (adjust)> {plot, index, adjust};
}
// identity adjust function when no adjustment is needed with the
// above instantiation helper
QCPGraphData adjust_identity (QCustomPlot *, int, QCPGraphData const& v) {return v;}
// a plot_data_loader adjustment function that wraps Y values of
// (-1..+1) plotting discontinuities as gaps in the graph data
auto wrap_pi = [] (QCustomPlot * plot, int index, QCPGraphData d)
{
double constexpr limit {1};
static unsigned wrap_count {0};
static double last_x {std::numeric_limits<double>::lowest ()};
d.value += 2 * limit * wrap_count;
if (d.value > limit)
{
// insert a gap in the graph
plot->graph (index)->data ()->add ({last_x + (d.key - last_x) / 2
, std::numeric_limits<double>::quiet_NaN ()});
while (d.value > limit)
{
--wrap_count;
d.value -= 2 * limit;
}
}
else if (d.value < -limit)
{
// insert a gap into the graph
plot->graph (index)->data ()->add ({last_x + (d.key - last_x) / 2
, std::numeric_limits<double>::quiet_NaN ()});
while (d.value < -limit)
{
++wrap_count;
d.value += 2 * limit;
}
}
last_x = d.key;
return d;
};
// generate points of type R from a function of type F for X in
// (-1..+1) with N intervals and function of type SX to scale X and
// of type SY to scale Y
//
// it is up to the user to call the generator sufficient times which
// is interval+1 times to reach +1
template<typename R, typename F, typename SX, typename SY>
struct graph_generator
{
public:
graph_generator (F f, size_t intervals, SX x_scaling, SY y_scaling)
: x_ {0}
, f_ (f)
, intervals_ {intervals}
, x_scaling_ (x_scaling)
, y_scaling_ (y_scaling)
{
}
R operator () ()
{
typename F::value_type x {x_++ * 2.f / intervals_ - 1.f};
return {x_scaling_ (x), y_scaling_ (f_ (x))};
}
private:
int x_;
F f_;
size_t intervals_;
SX x_scaling_;
SY y_scaling_;
};
// helper function template to make a graph_generator instance for
// QCPGraphData type points with intervals intervals
template<typename F, typename SX, typename SY>
auto make_graph_generator (F function, SX x_scaling, SY y_scaling)
-> graph_generator<QCPGraphData, F, decltype (x_scaling), decltype (y_scaling)>
{
return graph_generator<QCPGraphData, F, decltype (x_scaling), decltype (y_scaling)>
{function, intervals, x_scaling, y_scaling};
}
// template function object for a polynomial with coefficients
template<typename C>
class polynomial
{
public:
typedef typename C::value_type value_type;
explicit polynomial (C const& coefficients)
: c_ {coefficients}
{
}
value_type operator () (value_type const& x)
{
value_type y {};
for (typename C::size_type i = c_.size (); i > 0; --i)
{
y = c_[i - 1] + x * y;
}
return y;
}
private:
C c_;
};
// helper function template to instantiate a polynomial instance
template<typename C>
auto make_polynomial (C const& coefficients) -> polynomial<C>
{
return polynomial<C> (coefficients);
}
// template function object for a group delay with coefficients
template<typename C>
class group_delay
{
public:
typedef typename C::value_type value_type;
explicit group_delay (C const& coefficients)
: c_ {coefficients}
{
}
value_type operator () (value_type const& x)
{
value_type tau {};
for (typename C::size_type i = 2; i < c_.size (); ++i)
{
tau += i * c_[i] * std::pow (x, i - 1);
}
return -1 / (2 * PI) * tau;
}
private:
C c_;
};
// helper function template to instantiate a group_delay function
// object
template<typename C>
auto make_group_delay (C const& coefficients) -> group_delay<C>
{
return group_delay<C> (coefficients);
}
// handy identity function
template<typename T> T identity (T const& v) {return v;}
// a lambda that scales the X axis from normalized to (500..2500)Hz
auto freq_scaling = [] (float v) -> float {return 1500.f + 1000.f * v;};
// a lambda that scales the phase Y axis from radians to units of Pi
auto pi_scaling = [] (float v) -> float {return v / PI;};
}
class PhaseEqualizationDialog::impl final
: public QDialog
{
Q_OBJECT
public:
explicit impl (PhaseEqualizationDialog * self, QSettings * settings
, QDir const& data_directory, QVector<double> const& coefficients
, QWidget * parent);
~impl () {save_window_state ();}
protected:
void closeEvent (QCloseEvent * e) override
{
save_window_state ();
QDialog::closeEvent (e);
}
private:
void save_window_state ()
{
SettingsGroup g (settings_, title);
settings_->setValue ("geometry", saveGeometry ());
}
void plot_current ();
void plot_phase ();
void plot_amplitude ();
PhaseEqualizationDialog * self_;
QSettings * settings_;
QDir data_directory_;
QHBoxLayout layout_;
QVector<double> current_coefficients_;
QVector<double> new_coefficients_;
unsigned amp_poly_low_;
unsigned amp_poly_high_;
QVector<double> amp_coefficients_;
QCustomPlot plot_;
QDialogButtonBox button_box_;
};
#include "PhaseEqualizationDialog.moc"
PhaseEqualizationDialog::PhaseEqualizationDialog (QSettings * settings
, QDir const& data_directory
, QVector<double> const& coefficients
, QWidget * parent)
: m_ {this, settings, data_directory, coefficients, parent}
{
}
void PhaseEqualizationDialog::show ()
{
m_->show ();
}
PhaseEqualizationDialog::impl::impl (PhaseEqualizationDialog * self
, QSettings * settings
, QDir const& data_directory
, QVector<double> const& coefficients
, QWidget * parent)
: QDialog {parent}
, self_ {self}
, settings_ {settings}
, data_directory_ {data_directory}
, current_coefficients_ {coefficients}
, amp_poly_low_ {0}
, amp_poly_high_ {6000}
, button_box_ {QDialogButtonBox::Discard | QDialogButtonBox::Apply
| QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Close
, Qt::Vertical}
{
setWindowTitle (windowTitle () + ' ' + tr (title));
resize (500, 600);
{
SettingsGroup g {settings_, title};
restoreGeometry (settings_->value ("geometry", saveGeometry ()).toByteArray ());
}
auto legend_title = new QCPTextElement {&plot_, tr ("Phase"), QFont {"sans", 9, QFont::Bold}};
legend_title->setLayer (plot_.legend->layer ());
plot_.legend->addElement (0, 0, legend_title);
plot_.legend->setVisible (true);
plot_.xAxis->setLabel (tr ("Freq (Hz)"));
plot_.xAxis->setRange (500, 2500);
plot_.yAxis->setLabel (tr ("Phase (Π)"));
plot_.yAxis->setRange (-1, +1);
plot_.yAxis2->setLabel (tr ("Delay (ms)"));
plot_.axisRect ()->setRangeDrag (Qt::Vertical);
plot_.axisRect ()->setRangeZoom (Qt::Vertical);
plot_.yAxis2->setVisible (true);
plot_.axisRect ()->setRangeDragAxes (nullptr, plot_.yAxis2);
plot_.axisRect ()->setRangeZoomAxes (nullptr, plot_.yAxis2);
plot_.axisRect ()->insetLayout ()->setInsetAlignment (0, Qt::AlignBottom|Qt::AlignRight);
plot_.setInteractions (QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
plot_.addGraph ()->setName (tr ("Measured"));
plot_.graph ()->setPen (QPen {Qt::blue});
plot_.graph ()->setVisible (false);
plot_.graph ()->removeFromLegend ();
plot_.addGraph ()->setName (tr ("Proposed"));
plot_.graph ()->setPen (QPen {Qt::red});
plot_.graph ()->setVisible (false);
plot_.graph ()->removeFromLegend ();
plot_.addGraph ()->setName (tr ("Current"));
plot_.graph ()->setPen (QPen {Qt::green});
plot_.addGraph (plot_.xAxis, plot_.yAxis2)->setName (tr ("Group Delay"));
plot_.graph ()->setPen (QPen {Qt::darkGreen});
plot_.plotLayout ()->addElement (new QCPAxisRect {&plot_});
plot_.plotLayout ()->setRowStretchFactor (1, 0.5);
auto amp_legend = new QCPLegend;
plot_.axisRect (1)->insetLayout ()->addElement (amp_legend, Qt::AlignTop | Qt::AlignRight);
plot_.axisRect (1)->insetLayout ()->setMargins (QMargins {12, 12, 12, 12});
amp_legend->setVisible (true);
amp_legend->setLayer (QLatin1String {"legend"});
legend_title = new QCPTextElement {&plot_, tr ("Amplitude"), QFont {"sans", 9, QFont::Bold}};
legend_title->setLayer (amp_legend->layer ());
amp_legend->addElement (0, 0, legend_title);
plot_.axisRect (1)->axis (QCPAxis::atBottom)->setLabel (tr ("Freq (Hz)"));
plot_.axisRect (1)->axis (QCPAxis::atBottom)->setRange (0, 6000);
plot_.axisRect (1)->axis (QCPAxis::atLeft)->setLabel (tr ("Relative Power (dB)"));
plot_.axisRect (1)->axis (QCPAxis::atLeft)->setRangeLower (0);
plot_.axisRect (1)->setRangeDragAxes (nullptr, nullptr);
plot_.axisRect (1)->setRangeZoomAxes (nullptr, nullptr);
plot_.addGraph (plot_.axisRect (1)->axis (QCPAxis::atBottom)
, plot_.axisRect (1)->axis (QCPAxis::atLeft))->setName (tr ("Reference"));
plot_.graph ()->setPen (QPen {Qt::blue});
plot_.graph ()->removeFromLegend ();
plot_.graph ()->addToLegend (amp_legend);
layout_.addWidget (&plot_);
auto load_phase_button = button_box_.addButton (tr ("Phase ..."), QDialogButtonBox::ActionRole);
auto refresh_button = button_box_.addButton (tr ("Refresh"), QDialogButtonBox::ActionRole);
layout_.addWidget (&button_box_);
setLayout (&layout_);
connect (&button_box_, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect (&button_box_, &QDialogButtonBox::clicked, [=] (QAbstractButton * button) {
if (button == load_phase_button)
{
plot_phase ();
}
else if (button == refresh_button)
{
plot_current ();
}
else if (button == button_box_.button (QDialogButtonBox::Apply))
{
if (plot_.graph (0)->dataCount ()) // something loaded
{
current_coefficients_ = new_coefficients_;
Q_EMIT self_->phase_equalization_changed (current_coefficients_);
plot_current ();
}
}
else if (button == button_box_.button (QDialogButtonBox::RestoreDefaults))
{
current_coefficients_ = QVector<double> {0., 0., 0., 0., 0.};
Q_EMIT self_->phase_equalization_changed (current_coefficients_);
plot_current ();
}
else if (button == button_box_.button (QDialogButtonBox::Discard))
{
new_coefficients_ = QVector<double> {0., 0., 0., 0., 0.};
plot_.graph (0)->data ()->clear ();
plot_.graph (0)->setVisible (false);
plot_.graph (0)->removeFromLegend ();
plot_.graph (1)->data ()->clear ();
plot_.graph (1)->setVisible (false);
plot_.graph (1)->removeFromLegend ();
plot_.replot ();
}
});
plot_current ();
}
struct PowerSpectrumPoint
{
operator QCPGraphData () const
{
return QCPGraphData {freq_, power_};
}
float freq_;
float power_;
};
// read an amplitude point line from a stream (refspec.dat)
std::istream& operator >> (std::istream& is, PowerSpectrumPoint& r)
{
float y1, y3, y4; // discard these
is >> r.freq_ >> y1 >> r.power_ >> y3 >> y4;
return is;
}
void PhaseEqualizationDialog::impl::plot_current ()
{
auto phase_graph = make_plot_data_loader (&plot_, 2, wrap_pi);
plot_.graph (2)->data ()->clear ();
std::generate_n (std::back_inserter (phase_graph), intervals + 1
, make_graph_generator (make_polynomial (current_coefficients_), freq_scaling, pi_scaling));
auto group_delay_graph = make_plot_data_loader (&plot_, 3, adjust_identity);
plot_.graph (3)->data ()->clear ();
std::generate_n (std::back_inserter (group_delay_graph), intervals + 1
, make_graph_generator (make_group_delay (current_coefficients_), freq_scaling, identity<double>));
plot_.graph (3)->rescaleValueAxis ();
QFileInfo refspec_file_info {data_directory_.absoluteFilePath ("refspec.dat")};
std::ifstream refspec_file (refspec_file_info.absoluteFilePath ().toLatin1 ().constData (), std::ifstream::in);
unsigned n;
if (refspec_file >> amp_poly_low_ >> amp_poly_high_ >> n)
{
std::istream_iterator<double> isi {refspec_file};
amp_coefficients_.clear ();
std::copy_n (isi, n, std::back_inserter (amp_coefficients_));
}
else
{
// may be old format refspec.dat with no header so rewind
refspec_file.clear ();
refspec_file.seekg (0);
}
auto reference_spectrum_graph = make_plot_data_loader (&plot_, 4, adjust_identity);
plot_.graph (4)->data ()->clear ();
std::copy (std::istream_iterator<PowerSpectrumPoint> {refspec_file},
std::istream_iterator<PowerSpectrumPoint> {},
std::back_inserter (reference_spectrum_graph));
plot_.graph (4)->rescaleValueAxis (true);
plot_.replot ();
}
struct PhasePoint
{
operator QCPGraphData () const
{
return QCPGraphData {freq_, phase_};
}
double freq_;
double phase_;
};
// read a phase point line from a stream (pcoeff file)
std::istream& operator >> (std::istream& is, PhasePoint& c)
{
double pp, sigmay; // discard these
if (is >> c.freq_ >> pp >> c.phase_ >> sigmay)
{
c.freq_ = 1500. + 1000. * c.freq_; // scale frequency to Hz
c.phase_ /= PI; // scale to units of Pi
}
return is;
}
void PhaseEqualizationDialog::impl::plot_phase ()
{
auto const& phase_file_name = QFileDialog::getOpenFileName (this
, "Select Phase Response Coefficients"
, data_directory_.absolutePath ()
, "Phase Coefficient Files (*.pcoeff)");
if (!phase_file_name.size ()) return;
std::ifstream phase_file (phase_file_name.toLatin1 ().constData (), std::ifstream::in);
int n;
float chi;
float rmsdiff;
unsigned freq_low;
unsigned freq_high;
unsigned terms;
// read header information
if (phase_file >> n >> chi >> rmsdiff >> freq_low >> freq_high >> terms)
{
std::istream_iterator<double> isi {phase_file};
new_coefficients_.clear ();
std::copy_n (isi, terms, std::back_inserter (new_coefficients_));
if (phase_file)
{
plot_.graph (0)->data ()->clear ();
plot_.graph (1)->data ()->clear ();
// read the phase data and plot as graph 0
auto graph = make_plot_data_loader (&plot_, 0, adjust_identity);
std::copy_n (std::istream_iterator<PhasePoint> {phase_file},
intervals + 1, std::back_inserter (graph));
if (phase_file)
{
plot_.graph(0)->setLineStyle(QCPGraph::lsNone);
plot_.graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 4));
plot_.graph (0)->setVisible (true);
plot_.graph (0)->addToLegend ();
// generate the proposed polynomial plot as graph 1
auto graph = make_plot_data_loader (&plot_, 1, wrap_pi);
std::generate_n (std::back_inserter (graph), intervals + 1
, make_graph_generator (make_polynomial (new_coefficients_)
, freq_scaling, pi_scaling));
plot_.graph (1)->setVisible (true);
plot_.graph (1)->addToLegend ();
}
plot_.replot ();
}
}
}
#include "moc_PhaseEqualizationDialog.cpp"
@@ -1,181 +0,0 @@
// -*- Mode: C++ -*-
///////////////////////////////////////////////////////////////////////////
// Some code in this file and accompanying files is based on work by
// Moe Wheatley, AE4Y, released under the "Simplified BSD License".
// For more details see the accompanying file LICENSE_WHEATLEY.TXT
///////////////////////////////////////////////////////////////////////////
#ifndef PLOTTER_H
#define PLOTTER_H
#ifdef QT5
#include <QtWidgets>
#else
#include <QtGui>
#endif
#include <QFrame>
#include <QImage>
#include <QVector>
#include <cstring>
#define VERT_DIVS 7 //specify grid screen divisions
#define HORZ_DIVS 20
extern bool g_single_decode;
class CPlotter : public QFrame
{
Q_OBJECT;
public:
explicit CPlotter(QWidget *parent = 0);
~CPlotter();
QSize minimumSizeHint() const;
QSize sizeHint() const;
bool m_bScaleOK;
void draw(float swide[], bool bScroll, bool bRed); //Update the waterfall
void SetRunningState(bool running);
void setPlotZero(int plotZero);
int plotZero();
void setPlotGain(int plotGain);
int plotGain();
int plot2dGain();
void setPlot2dGain(int n);
int plot2dZero();
void setPlot2dZero(int plot2dZero);
void setStartFreq(int f);
int startFreq();
int plotWidth();
void UpdateOverlay();
void setDataFromDisk(bool b);
void setRxRange(int fMin);
void setBinsPerPixel(int n);
int binsPerPixel();
void setWaterfallAvg(int n);
void setRxFreq(int n);
void DrawOverlay();
int rxFreq();
void setFsample(int n);
void setNsps(int ntrperiod, int nsps);
void setTxFreq(int n);
void setMode(QString mode);
void setSubMode(int n);
void setModeTx(QString modeTx);
void SetPercent2DScreen(int percent);
int Fmax();
void setDialFreq(double d);
void setCurrent(bool b) {m_bCurrent = b;}
bool current() const {return m_bCurrent;}
void setCumulative(bool b) {m_bCumulative = b;}
bool cumulative() const {return m_bCumulative;}
void setLinearAvg(bool b) {m_bLinearAvg = b;}
bool linearAvg() const {return m_bLinearAvg;}
void setBreadth(qint32 w) {m_w = w;}
qint32 breadth() const {return m_w;}
float fSpan() const {return m_fSpan;}
void setColours(QVector<QColor> const& cl);
void setFlatten(bool b1, bool b2);
void setTol(int n);
void setRxBand(QString band);
void setReference(bool b) {m_bReference = b;}
bool Reference() const {return m_bReference;}
void drawRed(int ia, int ib, float swide[]);
void setVHF(bool bVHF);
void setRedFile(QString fRed);
signals:
void freezeDecode1(int n);
void setFreq1(int rxFreq, int txFreq);
protected:
//re-implemented widget event handlers
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent* event);
private:
void MakeFrequencyStrs();
int XfromFreq(float f);
float FreqfromX(int x);
bool m_bCurrent;
bool m_bCumulative;
bool m_bLinearAvg;
bool m_bReference;
bool m_bReference0;
bool m_bVHF;
float m_fSpan;
qint32 m_plotZero;
qint32 m_plotGain;
qint32 m_plot2dGain;
qint32 m_plot2dZero;
qint32 m_binsPerPixel;
qint32 m_waterfallAvg;
qint32 m_w;
qint32 m_Flatten;
qint32 m_nSubMode;
qint32 m_ia;
qint32 m_ib;
QPixmap m_WaterfallPixmap;
QPixmap m_2DPixmap;
QPixmap m_ScalePixmap;
QPixmap m_OverlayPixmap;
QSize m_Size;
QString m_Str;
QString m_HDivText[483];
QString m_mode;
QString m_modeTx;
QString m_rxBand;
QString m_redFile;
bool m_Running;
bool m_paintEventBusy;
bool m_dataFromDisk;
double m_fftBinWidth;
double m_dialFreq;
double m_xOffset;
float m_sum[2048];
qint32 m_dBStepSize;
qint32 m_FreqUnits;
qint32 m_hdivs;
qint32 m_line;
qint32 m_fSample;
qint32 m_xClick;
qint32 m_freqPerDiv;
qint32 m_nsps;
qint32 m_Percent2DScreen;
qint32 m_Percent2DScreen0;
qint32 m_h;
qint32 m_h1;
qint32 m_h2;
qint32 m_TRperiod;
qint32 m_rxFreq;
qint32 m_txFreq;
qint32 m_fMin;
qint32 m_fMax;
qint32 m_startFreq;
qint32 m_tol;
char m_sutc[6];
private slots:
void mousePressEvent(QMouseEvent *event);
void mouseDoubleClickEvent(QMouseEvent *event);
};
extern QVector<QColor> g_ColorTbl;
extern "C" {
void flat4_(float swide[], int* iz, int* nflatten);
}
#endif // PLOTTER_H
@@ -1,278 +0,0 @@
#include "astro.h"
#include <stdio.h>
#include <QApplication>
#include <QFile>
#include <QTextStream>
#include <QSettings>
#include <QDateTime>
#include <QDir>
#include <QDebug>
#include "commons.h"
#include "MessageBox.hpp"
#include "Configuration.hpp"
#include "SettingsGroup.hpp"
#include "qt_helpers.hpp"
#include "ui_astro.h"
#include "moc_astro.cpp"
extern "C" {
void astrosub_(int* nyear, int* month, int* nday, double* uth, double* freqMoon,
const char* mygrid, const char* hisgrid, double* azsun,
double* elsun, double* azmoon, double* elmoon, double* azmoondx,
double* elmoondx, int* ntsky, int* ndop, int* ndop00,
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
double* xnr, double* techo, double* width1, double* width2,
bool* bTx, const char* AzElFileName, const char* jpleph,
int len1, int len2, int len3, int len4);
}
Astro::Astro(QSettings * settings, Configuration const * configuration, QWidget * parent)
: QDialog {parent, Qt::WindowTitleHint}
, settings_ {settings}
, configuration_ {configuration}
, ui_ {new Ui::Astro}
, m_DopplerMethod {0}
{
ui_->setupUi (this);
setWindowTitle (QApplication::applicationName () + " - " + tr ("Astronomical Data"));
setStyleSheet ("QWidget {background: white;}");
connect (ui_->cbDopplerTracking, &QAbstractButton::toggled, ui_->doppler_widget, &QWidget::setVisible);
read_settings ();
ui_->text_label->clear ();
}
Astro::~Astro ()
{
ui_->cbDopplerTracking->setChecked (false);
Q_EMIT tracking_update ();
if (isVisible ()) write_settings ();
}
void Astro::closeEvent (QCloseEvent * e)
{
write_settings ();
QWidget::closeEvent (e);
}
void Astro::read_settings ()
{
SettingsGroup g (settings_, "Astro");
ui_->doppler_widget->setVisible (ui_->cbDopplerTracking->isChecked ());
m_DopplerMethod=settings_->value("DopplerMethod",0).toInt();
switch (m_DopplerMethod)
{
case 0: ui_->rbNoDoppler->setChecked (true); break;
case 1: ui_->rbFullTrack->setChecked (true); break;
case 2: ui_->rbConstFreqOnMoon->setChecked (true); break;
case 3: ui_->rbRxOnly->setChecked (true); break;
}
move (settings_->value ("window/pos", pos ()).toPoint ());
}
void Astro::write_settings ()
{
SettingsGroup g (settings_, "Astro");
//settings_->setValue ("DopplerTracking", ui_->cbDopplerTracking->isChecked ());
settings_->setValue ("DopplerMethod",m_DopplerMethod);
settings_->setValue ("window/pos", pos ());
}
auto Astro::astroUpdate(QDateTime const& t, QString const& mygrid, QString const& hisgrid, Frequency freq,
bool dx_is_self, bool bTx, bool no_tx_QSY, int TR_period) -> Correction
{
Frequency freq_moon {freq};
double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx;
double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2;
int ntsky;
QString date {t.date().toString("yyyy MMM dd").trimmed ()};
QString utc {t.time().toString().trimmed ()};
int nyear {t.date().year()};
int month {t.date().month()};
int nday {t.date().day()};
int nhr {t.time().hour()};
int nmin {t.time().minute()};
double sec {t.time().second() + 0.001*t.time().msec()};
double uth {nhr + nmin/60.0 + sec/3600.0};
if(freq_moon < 1) freq_moon = 144000000;
int nfreq {static_cast<int> (freq_moon / 1000000u)};
double freq8 {static_cast<double> (freq_moon)};
auto const& AzElFileName = QDir::toNativeSeparators (configuration_->azel_directory ().absoluteFilePath ("azel.dat"));
auto const& jpleph = configuration_->data_dir ().absoluteFilePath ("JPLEPH");
int ndop;
int ndop00;
QString mygrid_padded {(mygrid + " ").left (6)};
QString hisgrid_padded {(hisgrid + " ").left (6)};
astrosub_(&nyear, &month, &nday, &uth, &freq8, mygrid_padded.toLatin1().constData(),
hisgrid_padded.toLatin1().constData(), &azsun, &elsun, &azmoon, &elmoon,
&azmoondx, &elmoondx, &ntsky, &ndop, &ndop00, &ramoon, &decmoon,
&dgrd, &poloffset, &xnr, &techo, &width1, &width2, &bTx,
AzElFileName.toLatin1().constData(), jpleph.toLatin1().constData(), 6, 6,
AzElFileName.length(), jpleph.length());
if(hisgrid_padded==" ") {
azmoondx=0.0;
elmoondx=0.0;
ndop=0;
width2=0.0;
}
QString message;
{
QTextStream out {&message};
out << " " << date << "\n"
"UTC: " << utc << "\n"
<< fixed
<< qSetFieldWidth (6)
<< qSetRealNumberPrecision (1)
<< "Az: " << azmoon << "\n"
"El: " << elmoon << "\n"
"SelfDop:" << ndop00 << "\n"
"Width: " << int(width1) << "\n"
<< qSetRealNumberPrecision (2)
<< "Delay: " << techo << "\n"
<< qSetRealNumberPrecision (1)
<< "DxAz: " << azmoondx << "\n"
"DxEl: " << elmoondx << "\n"
"DxDop: " << ndop << "\n"
"DxWid: " << int(width2) << "\n"
"Dec: " << decmoon << "\n"
"SunAz: " << azsun << "\n"
"SunEl: " << elsun << "\n"
"Freq: " << nfreq << "\n";
if(nfreq>=50) { //Suppress data not relevant below VHF
out << "Tsky: " << ntsky << "\n"
"Dpol: " << poloffset << "\n"
"MNR: " << xnr << "\n"
"Dgrd: " << dgrd;
}
}
ui_->text_label->setText(message);
Correction correction;
if (ui_->cbDopplerTracking->isChecked ()) {
switch (m_DopplerMethod)
{
case 1: // All Doppler correction done here; DX station stays at nominal dial frequency.
case 3: // Both stations do full correction on Rx and none on Tx
correction.rx = dx_is_self ? ndop00 : ndop;
break;
case 2:
// Doppler correction to constant frequency on Moon
correction.rx = ndop00 / 2;
break;
}
if (3 != m_DopplerMethod) correction.tx = -correction.rx;
if(dx_is_self && m_DopplerMethod == 1) correction.rx = 0;
if (no_tx_QSY && 3 != m_DopplerMethod && 0 != m_DopplerMethod)
{
// calculate a single correction for transmit half way through
// the period as a compromise for rigs that can't CAT QSY
// while transmitting
//
// use a base time of (secs-since-epoch + 2) so as to be sure
// we do the next period if we calculate just before it starts
auto sec_since_epoch = t.toMSecsSinceEpoch () / 1000 + 2;
auto target_sec = sec_since_epoch - sec_since_epoch % TR_period + TR_period / 2;
auto target_date_time = QDateTime::fromMSecsSinceEpoch (target_sec * 1000);
QString date {target_date_time.date().toString("yyyy MMM dd").trimmed ()};
QString utc {target_date_time.time().toString().trimmed ()};
int nyear {target_date_time.date().year()};
int month {target_date_time.date().month()};
int nday {target_date_time.date().day()};
int nhr {target_date_time.time().hour()};
int nmin {target_date_time.time().minute()};
double sec {target_date_time.time().second() + 0.001*target_date_time.time().msec()};
double uth {nhr + nmin/60.0 + sec/3600.0};
astrosub_(&nyear, &month, &nday, &uth, &freq8, mygrid_padded.toLatin1().constData(),
hisgrid_padded.toLatin1().constData(), &azsun, &elsun, &azmoon, &elmoon,
&azmoondx, &elmoondx, &ntsky, &ndop, &ndop00, &ramoon, &decmoon,
&dgrd, &poloffset, &xnr, &techo, &width1, &width2, &bTx,
"", jpleph.toLatin1().constData(), 6, 6,
0, jpleph.length());
FrequencyDelta offset {0};
switch (m_DopplerMethod)
{
case 1:
// All Doppler correction done here; DX station stays at nominal dial frequency.
offset = dx_is_self ? ndop00 : ndop;
break;
case 2:
// Doppler correction to constant frequency on Moon
offset = ndop00 / 2;
break;
}
correction.tx = -offset;
}
}
return correction;
}
void Astro::check_split ()
{
if (doppler_tracking () && !configuration_->split_mode ())
{
MessageBox::warning_message (this, tr ("Doppler Tracking Error"),
tr ("Split operating is required for Doppler tracking"),
tr ("Go to \"Menu->File->Settings->Radio\" to enable split operation"));
ui_->rbNoDoppler->click ();
}
}
void Astro::on_rbFullTrack_clicked()
{
m_DopplerMethod = 1;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbRxOnly_clicked()
{
m_DopplerMethod = 3;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbConstFreqOnMoon_clicked()
{
m_DopplerMethod = 2;
check_split ();
Q_EMIT tracking_update ();
}
void Astro::on_rbNoDoppler_clicked()
{
m_DopplerMethod = 0;
Q_EMIT tracking_update ();
}
bool Astro::doppler_tracking () const
{
return ui_->cbDopplerTracking->isChecked () && m_DopplerMethod;
}
void Astro::on_cbDopplerTracking_toggled(bool)
{
check_split ();
Q_EMIT tracking_update ();
}
void Astro::nominal_frequency (Frequency rx, Frequency tx)
{
ui_->sked_frequency_label->setText (Radio::pretty_frequency_MHz_string (rx));
ui_->sked_tx_frequency_label->setText (Radio::pretty_frequency_MHz_string (tx));
}
void Astro::hideEvent (QHideEvent * e)
{
Q_EMIT tracking_update ();
QWidget::hideEvent (e);
}