kopia lustrzana https://github.com/f4exb/sdrangel
				
				
				
			
		
			
				
	
	
		
			6412 wiersze
		
	
	
		
			240 KiB
		
	
	
	
		
			C++
		
	
	
			
		
		
	
	
			6412 wiersze
		
	
	
		
			240 KiB
		
	
	
	
		
			C++
		
	
	
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
// Copyright (C) 2016 Edouard Griffiths, F4EXB                                   //
 | 
						|
// Copyright (C) 2021-2024 Jon Beniston, M7RCE                                   //
 | 
						|
//                                                                               //
 | 
						|
// This program is free software; you can redistribute it and/or modify          //
 | 
						|
// it under the terms of the GNU General Public License as published by          //
 | 
						|
// the Free Software Foundation as version 3 of the License, or                  //
 | 
						|
// (at your option) any later version.                                           //
 | 
						|
//                                                                               //
 | 
						|
// This program is distributed in the hope that it will be useful,               //
 | 
						|
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
						|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
						|
// GNU General Public License V3 for more details.                               //
 | 
						|
//                                                                               //
 | 
						|
// You should have received a copy of the GNU General Public License             //
 | 
						|
// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
						|
///////////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
#include <limits>
 | 
						|
#include <numeric>
 | 
						|
#include <algorithm>
 | 
						|
#include <functional>
 | 
						|
#include <ctype.h>
 | 
						|
#include <QDockWidget>
 | 
						|
#include <QMainWindow>
 | 
						|
#include <QDebug>
 | 
						|
#include <QMessageBox>
 | 
						|
#include <QAction>
 | 
						|
#include <QRegExp>
 | 
						|
#include <QClipboard>
 | 
						|
#include <QFileDialog>
 | 
						|
#include <QImage>
 | 
						|
#include <QTimer>
 | 
						|
 | 
						|
#include "radioastronomygui.h"
 | 
						|
 | 
						|
#include "device/deviceuiset.h"
 | 
						|
#include "dsp/dspengine.h"
 | 
						|
#include "dsp/dspcommands.h"
 | 
						|
#include "ui_radioastronomygui.h"
 | 
						|
#include "plugin/pluginapi.h"
 | 
						|
#include "util/db.h"
 | 
						|
#include "util/astronomy.h"
 | 
						|
#include "util/interpolation.h"
 | 
						|
#include "util/png.h"
 | 
						|
#include "util/units.h"
 | 
						|
#include "gui/basicchannelsettingsdialog.h"
 | 
						|
#include "dsp/dspengine.h"
 | 
						|
#include "gui/timedelegate.h"
 | 
						|
#include "gui/decimaldelegate.h"
 | 
						|
#include "gui/dialogpositioner.h"
 | 
						|
#include "channel/channelwebapiutils.h"
 | 
						|
#include "maincore.h"
 | 
						|
#include "feature/feature.h"
 | 
						|
#include "webapi/webapiutils.h"
 | 
						|
 | 
						|
#include "radioastronomy.h"
 | 
						|
#include "radioastronomysensordialog.h"
 | 
						|
#include "radioastronomycalibrationdialog.h"
 | 
						|
 | 
						|
#include "SWGStarTrackerTarget.h"
 | 
						|
#include "SWGStarTrackerDisplaySettings.h"
 | 
						|
#include "SWGStarTrackerDisplayLoSSettings.h"
 | 
						|
 | 
						|
// Time value is in milliseconds - Displays hh:mm:ss or d hh:mm:ss
 | 
						|
class TimeDeltaDelegate : public QStyledItemDelegate {
 | 
						|
 | 
						|
public:
 | 
						|
    TimeDeltaDelegate(QObject *parent = nullptr) :
 | 
						|
        QStyledItemDelegate(parent) {}
 | 
						|
 | 
						|
    virtual QString displayText(const QVariant &value, const QLocale &locale) const override
 | 
						|
    {
 | 
						|
        (void) locale;
 | 
						|
        qint64 v = value.toLongLong(); // In milliseconds
 | 
						|
        bool neg = v < 0;
 | 
						|
        v = abs(v);
 | 
						|
        qint64 days = v / (1000*60*60*24);
 | 
						|
        v = v % (1000*60*60*24);
 | 
						|
        qint64 hours = v / (1000*60*60);
 | 
						|
        v = v % (1000*60*60);
 | 
						|
        qint64 minutes = v / (1000*60);
 | 
						|
        v = v % (1000*60);
 | 
						|
        qint64 seconds = v / (1000);
 | 
						|
        //qint64 msec = v % 1000;
 | 
						|
 | 
						|
        if (days > 0) {
 | 
						|
            return QString("%1%2 %3:%4:%5").arg(neg ? "-" : "").arg(days).arg(hours, 2, 10, QChar('0')).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
 | 
						|
        } else {
 | 
						|
            return QString("%1%2:%3:%4").arg(neg ? "-" : "").arg(hours, 2, 10, QChar('0')).arg(minutes, 2, 10, QChar('0')).arg(seconds, 2, 10, QChar('0'));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    QString m_format;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
// Delegate for table to display hours, minutes and seconds
 | 
						|
class HMSDelegate : public QStyledItemDelegate {
 | 
						|
 | 
						|
public:
 | 
						|
    HMSDelegate(QObject *parent = nullptr) :
 | 
						|
        QStyledItemDelegate(parent) {}
 | 
						|
 | 
						|
    virtual QString displayText(const QVariant &value, const QLocale &locale) const override
 | 
						|
    {
 | 
						|
        (void) locale;
 | 
						|
        return Units::decimalHoursToHoursMinutesAndSeconds(value.toDouble());
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
// Delegate for table to display degrees, minutes and seconds
 | 
						|
class DMSDelegate : public QStyledItemDelegate {
 | 
						|
 | 
						|
public:
 | 
						|
    DMSDelegate(QObject *parent = nullptr) :
 | 
						|
        QStyledItemDelegate(parent) {}
 | 
						|
 | 
						|
    virtual QString displayText(const QVariant &value, const QLocale &locale) const override
 | 
						|
    {
 | 
						|
        (void) locale;
 | 
						|
        return Units::decimalDegreesToDegreeMinutesAndSeconds(value.toDouble());
 | 
						|
    }
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
void RadioAstronomyGUI::LABData::read(QFile* file, float l, float b)
 | 
						|
{
 | 
						|
    m_l = l;
 | 
						|
    m_b = b;
 | 
						|
    m_vlsr.clear();
 | 
						|
    m_temp.clear();
 | 
						|
    QTextStream in(file);
 | 
						|
 | 
						|
    while (!in.atEnd())
 | 
						|
    {
 | 
						|
        QString line = in.readLine().trimmed();
 | 
						|
        if (!line.startsWith("%") && (line.size() > 0))  // Lines starting with % are comments
 | 
						|
        {
 | 
						|
            // 4 cols: v_lsr [km/s], T_B [K], freq. [Mhz], wavel. [cm]
 | 
						|
            line = line.simplified();
 | 
						|
            QStringList cols = line.split(" ");
 | 
						|
            if (cols.size() == 4)
 | 
						|
            {
 | 
						|
                m_vlsr.append(cols[0].toFloat());
 | 
						|
                m_temp.append(cols[1].toFloat());
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                qDebug() << "RadioAstronomyGUI::parseLAB: Unexpected number of columns";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::LABData::toSeries(QLineSeries *series)
 | 
						|
{
 | 
						|
    series->clear();
 | 
						|
    series->setName(QString("LAB l=%1 b=%2").arg(m_l).arg(m_b));
 | 
						|
    for (int i = 0; i < m_vlsr.size(); i++) {
 | 
						|
        series->append(m_vlsr[i], m_temp[i]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::init(const QString& name, bool visible)
 | 
						|
{
 | 
						|
    m_series = new QLineSeries();
 | 
						|
    m_series->setName(name);
 | 
						|
    m_series->setVisible(visible);
 | 
						|
    m_yAxis = new QValueAxis();
 | 
						|
    m_yAxis->setTitleText(name);
 | 
						|
    m_yAxis->setVisible(visible);
 | 
						|
    m_min = std::numeric_limits<double>::max();
 | 
						|
    m_max = -std::numeric_limits<double>::max();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::setName(const QString& name)
 | 
						|
{
 | 
						|
    if (m_series) {
 | 
						|
        m_series->setName(name);
 | 
						|
    }
 | 
						|
    if (m_yAxis) {
 | 
						|
       m_yAxis->setTitleText(name);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::clicked(bool checked)
 | 
						|
{
 | 
						|
    if (m_series) {
 | 
						|
        m_series->setVisible(checked);
 | 
						|
    }
 | 
						|
    if (m_yAxis) {
 | 
						|
        m_yAxis->setVisible(checked);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::append(SensorMeasurement *measurement)
 | 
						|
{
 | 
						|
    m_measurements.append(measurement);
 | 
						|
    addToSeries(measurement);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::addToSeries(SensorMeasurement *measurement)
 | 
						|
{
 | 
						|
    m_series->append(measurement->m_dateTime.toMSecsSinceEpoch(), measurement->m_value);
 | 
						|
 | 
						|
    m_max = std::max(m_max, measurement->m_value);
 | 
						|
    m_min = std::min(m_min, measurement->m_value);
 | 
						|
    if (m_min == m_max) {
 | 
						|
        // Axis isn't drawn properly if min and max are the same
 | 
						|
        m_yAxis->setRange(m_min*0.9, m_max*1.1);
 | 
						|
    } else {
 | 
						|
        m_yAxis->setRange(m_min, m_max);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::addAllToSeries()
 | 
						|
{
 | 
						|
    for (int i = 0; i < m_measurements.size(); i++) {
 | 
						|
        addToSeries(m_measurements[i]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::clear()
 | 
						|
{
 | 
						|
    m_series->clear();
 | 
						|
    qDeleteAll(m_measurements);
 | 
						|
    m_measurements.clear();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::addToChart(QChart* chart, QDateTimeAxis* xAxis)
 | 
						|
{
 | 
						|
    chart->addSeries(m_series);
 | 
						|
    m_series->attachAxis(xAxis);
 | 
						|
    m_series->attachAxis(m_yAxis);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::SensorMeasurements::setPen(const QPen& pen)
 | 
						|
{
 | 
						|
    m_series->setPen(pen);
 | 
						|
}
 | 
						|
 | 
						|
QValueAxis* RadioAstronomyGUI::SensorMeasurements::yAxis() const
 | 
						|
{
 | 
						|
    return m_yAxis;
 | 
						|
}
 | 
						|
 | 
						|
qreal RadioAstronomyGUI::SensorMeasurements::lastValue()
 | 
						|
{
 | 
						|
    if (m_measurements.size() > 0) {
 | 
						|
        return m_measurements.last()->m_value;
 | 
						|
    } else {
 | 
						|
        return 0.0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::resizePowerTable()
 | 
						|
{
 | 
						|
    // Fill table with a row of dummy data that will size the columns nicely
 | 
						|
    // Trailing spaces are for sort arrow
 | 
						|
    int row = ui->powerTable->rowCount();
 | 
						|
    ui->powerTable->setRowCount(row + 1);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_DATE, new QTableWidgetItem("15/04/2016"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TIME, new QTableWidgetItem("10:17:00"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER, new QTableWidgetItem("1.235-e5"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER_DB, new QTableWidgetItem("-100.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER_DBM, new QTableWidgetItem("-100.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSYS, new QTableWidgetItem("3000"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSYS0, new QTableWidgetItem("100"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSOURCE, new QTableWidgetItem("300"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TB, new QTableWidgetItem("100000"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSKY, new QTableWidgetItem("300"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_FLUX, new QTableWidgetItem("100000.00"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SIGMA_T, new QTableWidgetItem("0.01"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SIGMA_S, new QTableWidgetItem("1000.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_OMEGA_A, new QTableWidgetItem("0.000001"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_OMEGA_S, new QTableWidgetItem("0.000001"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_RA, new QTableWidgetItem("12h59m59.10s"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_DEC, new QTableWidgetItem("-90d59\'59.00\""));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_GAL_LAT, new QTableWidgetItem("-90.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_GAL_LON, new QTableWidgetItem("359.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_AZ, new QTableWidgetItem("359.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_EL, new QTableWidgetItem("90.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_VBCRS, new QTableWidgetItem("10.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_VLSR, new QTableWidgetItem("10.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SOLAR_FLUX, new QTableWidgetItem("60.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_AIR_TEMP, new QTableWidgetItem("20.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SENSOR_1, new QTableWidgetItem("1.0000000"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SENSOR_2, new QTableWidgetItem("1.0000000"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_UTC, new QTableWidgetItem("15/04/2016 10:17:00"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_AZ, new QTableWidgetItem("359.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_EL, new QTableWidgetItem("-90.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_AZ_OFF, new QTableWidgetItem("-10.0"));
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_EL_OFF, new QTableWidgetItem("-10.0"));
 | 
						|
    ui->powerTable->resizeColumnsToContents();
 | 
						|
    ui->powerTable->removeRow(row);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::resizePowerMarkerTable()
 | 
						|
{
 | 
						|
    // Fill table with a row of dummy data that will size the columns nicely
 | 
						|
    int row = ui->powerMarkerTable->rowCount();
 | 
						|
    ui->powerMarkerTable->setRowCount(row + 1);
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_NAME, new QTableWidgetItem("Max"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DATE, new QTableWidgetItem("15/04/2016"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_TIME, new QTableWidgetItem("10:17:00"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_VALUE, new QTableWidgetItem("1000.0"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_X, new QTableWidgetItem("1 23:59:59"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_Y, new QTableWidgetItem("1000.0"));
 | 
						|
    ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_TO, new QTableWidgetItem("Max"));
 | 
						|
    ui->powerMarkerTable->resizeColumnsToContents();
 | 
						|
    ui->powerMarkerTable->removeRow(row);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::resizeSpectrumMarkerTable()
 | 
						|
{
 | 
						|
    // Fill table with a row of dummy data that will size the columns nicely
 | 
						|
    int row = ui->spectrumMarkerTable->rowCount();
 | 
						|
    ui->spectrumMarkerTable->setRowCount(row + 1);
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_NAME, new QTableWidgetItem("Max"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_FREQ, new QTableWidgetItem("1420.405000"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_VALUE, new QTableWidgetItem("1000.0"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_X, new QTableWidgetItem("1420.405000"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_Y, new QTableWidgetItem("1000.0"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_TO, new QTableWidgetItem("M1"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_VR, new QTableWidgetItem("-100.0"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_R, new QTableWidgetItem("10.0"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_D, new QTableWidgetItem("10.0/10.0"));
 | 
						|
    QTableWidgetItem* check = new QTableWidgetItem();
 | 
						|
    check->setFlags(Qt::ItemIsUserCheckable);
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_PLOT_MAX, check);
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_R_MIN, new QTableWidgetItem("10.0"));
 | 
						|
    ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_V, new QTableWidgetItem("250.0"));
 | 
						|
    ui->spectrumMarkerTable->resizeColumnsToContents();
 | 
						|
    ui->spectrumMarkerTable->removeRow(row);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcSpectrumMarkerDelta()
 | 
						|
{
 | 
						|
    if (m_spectrumM1Valid && m_spectrumM2Valid)
 | 
						|
    {
 | 
						|
        qreal dx = m_spectrumM2X - m_spectrumM1X;
 | 
						|
        qreal dy = m_spectrumM2Y - m_spectrumM1Y;
 | 
						|
 | 
						|
        ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_DELTA_X)->setData(Qt::DisplayRole, dx);
 | 
						|
        ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_DELTA_Y)->setData(Qt::DisplayRole, dy);
 | 
						|
        ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_DELTA_TO)->setData(Qt::DisplayRole, "M1");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcPowerMarkerDelta()
 | 
						|
{
 | 
						|
    if (m_powerM1Valid && m_powerM2Valid)
 | 
						|
    {
 | 
						|
        qreal dx = m_powerM2X - m_powerM1X;
 | 
						|
        qreal dy = m_powerM2Y - m_powerM1Y;
 | 
						|
 | 
						|
        ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_DELTA_X)->setData(Qt::DisplayRole, dx);
 | 
						|
        ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_DELTA_Y)->setData(Qt::DisplayRole, dy);
 | 
						|
        ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_DELTA_TO)->setData(Qt::DisplayRole, "M1");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcPowerPeakDelta()
 | 
						|
{
 | 
						|
    qreal dx = m_powerMaxX - m_powerMinX;
 | 
						|
    qreal dy = m_powerMaxY - m_powerMinY;
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_X)->setData(Qt::DisplayRole, dx);
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_Y)->setData(Qt::DisplayRole, dy);
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_TO)->setData(Qt::DisplayRole, "Max");
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::addToPowerSeries(FFTMeasurement *fft, bool skipCalcs)
 | 
						|
{
 | 
						|
    if (   ((m_settings.m_powerYUnits == RadioAstronomySettings::PY_DBFS) || fft->m_temp)                 // Only dBFS valid if no temp was calculated
 | 
						|
        && !((m_settings.m_powerYUnits == RadioAstronomySettings::PY_DBM) && (fft->m_tSys == 0.0f))  // dBm value not valid if temp is 0
 | 
						|
       )
 | 
						|
    {
 | 
						|
        qreal power;
 | 
						|
        switch (m_settings.m_powerYData)
 | 
						|
        {
 | 
						|
        case RadioAstronomySettings::PY_POWER:
 | 
						|
            switch (m_settings.m_powerYUnits)
 | 
						|
            {
 | 
						|
            case RadioAstronomySettings::PY_DBFS:
 | 
						|
                power = fft->m_totalPowerdBFS;
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_DBM:
 | 
						|
                power = fft->m_totalPowerdBm;
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_WATTS:
 | 
						|
                power = fft->m_totalPowerWatts;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_TSYS:
 | 
						|
            power = fft->m_tSys;
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_TSOURCE:
 | 
						|
            power = fft->m_tSource;
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_FLUX:
 | 
						|
            switch (m_settings.m_powerYUnits)
 | 
						|
            {
 | 
						|
            case RadioAstronomySettings::PY_SFU:
 | 
						|
                power = Units::wattsPerMetrePerHertzToSolarFluxUnits(fft->m_flux);
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_JANSKY:
 | 
						|
                power = Units::wattsPerMetrePerHertzToJansky(fft->m_flux);
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        QDateTime dateTime = fft->m_dateTime;
 | 
						|
 | 
						|
        if (m_powerSeries->count() == 0)
 | 
						|
        {
 | 
						|
            m_powerMin = power;
 | 
						|
            m_powerMax = power;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            m_powerMin = std::min(power, m_powerMin);
 | 
						|
            m_powerMax = std::max(power, m_powerMax);
 | 
						|
        }
 | 
						|
        m_powerSeries->append(dateTime.toMSecsSinceEpoch(), power);
 | 
						|
        addToPowerFilter(dateTime.toMSecsSinceEpoch(), power);
 | 
						|
        if (!skipCalcs)
 | 
						|
        {
 | 
						|
            if (m_settings.m_powerAutoscale)
 | 
						|
            {
 | 
						|
                blockApplySettings(true);
 | 
						|
                powerAutoscaleY(false);
 | 
						|
                blockApplySettings(false);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (m_settings.m_powerYUnits == RadioAstronomySettings::PY_KELVIN) {
 | 
						|
            m_powerTsys0Series->append(dateTime.toMSecsSinceEpoch(), fft->m_tSys0);
 | 
						|
        } else if (m_settings.m_powerYUnits == RadioAstronomySettings::PY_DBM) {
 | 
						|
            m_powerTsys0Series->append(dateTime.toMSecsSinceEpoch(), Astronomy::noisePowerdBm(fft->m_tSys0, fft->m_sampleRate));
 | 
						|
        } else if (m_settings.m_powerYUnits == RadioAstronomySettings::PY_WATTS) {
 | 
						|
            m_powerTsys0Series->append(dateTime.toMSecsSinceEpoch(), Astronomy::m_boltzmann * fft->m_tSys0 * fft->m_sampleRate);
 | 
						|
        }
 | 
						|
 | 
						|
        if (!m_powerPeakValid)
 | 
						|
        {
 | 
						|
            m_powerPeakValid = true;
 | 
						|
            m_powerMinY = power;
 | 
						|
            m_powerMinX = dateTime.toMSecsSinceEpoch();
 | 
						|
            m_powerMaxY = power;
 | 
						|
            m_powerMaxX = dateTime.toMSecsSinceEpoch();
 | 
						|
            m_powerPeakSeries->clear();
 | 
						|
            m_powerPeakSeries->append(m_powerMaxX, m_powerMaxY);
 | 
						|
            m_powerPeakSeries->append(m_powerMaxX, m_powerMaxY);
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_powerMaxX);
 | 
						|
            if (!skipCalcs)
 | 
						|
            {
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerMaxY);
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerMinY);
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_X)->setData(Qt::DisplayRole, 0.0);
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_Y)->setData(Qt::DisplayRole, 0.0);
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DELTA_TO)->setData(Qt::DisplayRole, "Max");
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (power > m_powerMaxY)
 | 
						|
        {
 | 
						|
            m_powerMaxY = power;
 | 
						|
            m_powerMaxX = dateTime.toMSecsSinceEpoch();
 | 
						|
            m_powerPeakSeries->replace(0, m_powerMaxX, m_powerMaxY);
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_powerMaxX);
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerMaxY);
 | 
						|
            calcPowerPeakDelta();
 | 
						|
        }
 | 
						|
        else if (power < m_powerMinY)
 | 
						|
        {
 | 
						|
            m_powerMinY = power;
 | 
						|
            m_powerMinX = dateTime.toMSecsSinceEpoch();
 | 
						|
            m_powerPeakSeries->replace(1, m_powerMinX, m_powerMinY);
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_powerMinX);
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerMinY);
 | 
						|
            calcPowerPeakDelta();
 | 
						|
        }
 | 
						|
 | 
						|
        // Update markers (E.g. if scale changes)
 | 
						|
        int c = m_powerSeries->count();
 | 
						|
        if (c >= 2)
 | 
						|
        {
 | 
						|
            QPointF p1 = m_powerSeries->at(c-2);
 | 
						|
            QPointF p2 = m_powerSeries->at(c-1);
 | 
						|
            if (m_powerM1Valid && (m_powerM1X >= p1.x()) && (m_powerM1X < p2.x()))
 | 
						|
            {
 | 
						|
                m_powerM1Y = Interpolation::interpolate(p1.x(), p1.y(), p2.x(), p2.y(), m_powerM1X);
 | 
						|
                m_powerMarkerSeries->insert(0, QPointF(dateTime.toMSecsSinceEpoch(), m_powerM1Y));
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_M1, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerM1Y);
 | 
						|
                calcPowerMarkerDelta();
 | 
						|
            }
 | 
						|
            if (m_powerM2Valid && (m_powerM2X >= p1.x()) && (m_powerM2X < p2.x()))
 | 
						|
            {
 | 
						|
                m_powerM2Y = Interpolation::interpolate(p1.x(), p1.y(), p2.x(), p2.y(), m_powerM2X);
 | 
						|
                m_powerMarkerSeries->append(dateTime.toMSecsSinceEpoch(), m_powerM2Y);
 | 
						|
                ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerM2Y);
 | 
						|
                calcPowerMarkerDelta();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!skipCalcs)
 | 
						|
        {
 | 
						|
            // Set X axis format to include date if measurements span over different days
 | 
						|
            // Seems there's a QT bug here, if we call m_powerXAxis->setFormat, the chart isn't
 | 
						|
            // redrawn properly, so we have to redraw the whole thing
 | 
						|
            QDateTime minDateTime = m_powerXAxis->min();
 | 
						|
            QDateTime maxDateTime = m_powerXAxis->max();
 | 
						|
            bool sameDay = minDateTime.date() == maxDateTime.date();
 | 
						|
            if (!sameDay && m_powerXAxisSameDay)
 | 
						|
            {
 | 
						|
                m_powerXAxisSameDay = true;
 | 
						|
                QTimer::singleShot(100, this, SLOT(plotPowerChart()));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (!skipCalcs && ui->powerShowAvg->isChecked()) {
 | 
						|
            calcAverages();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (m_powerSeries->count() <= 1)    // Don't check skipCalcs here, as that will be set for first data
 | 
						|
    {
 | 
						|
        ui->powerStartTime->setMinimumDateTime(fft->m_dateTime);
 | 
						|
        ui->powerEndTime->setMinimumDateTime(fft->m_dateTime);
 | 
						|
        if (m_settings.m_powerAutoscale)
 | 
						|
        {
 | 
						|
            ui->powerStartTime->setDateTime(fft->m_dateTime);
 | 
						|
            ui->powerEndTime->setDateTime(fft->m_dateTime);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (!skipCalcs)
 | 
						|
    {
 | 
						|
        ui->powerStartTime->setMaximumDateTime(fft->m_dateTime);
 | 
						|
        ui->powerEndTime->setMaximumDateTime(fft->m_dateTime);
 | 
						|
        if (m_settings.m_powerAutoscale) {
 | 
						|
            ui->powerEndTime->setDateTime(fft->m_dateTime);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::degreesToSteradian(double deg) const
 | 
						|
{
 | 
						|
    // https://en.wikipedia.org/wiki/Steradian - Other properties
 | 
						|
    double s = sin(Units::degreesToRadians(deg) / 4.0);
 | 
						|
    return 4.0 * M_PI * s * s;
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::hpbwToSteradians(double hpbw) const
 | 
						|
{
 | 
						|
    // https://www.cv.nrao.edu/~sransom/web/Ch3.html#E118
 | 
						|
    double theta = Units::degreesToRadians(hpbw);
 | 
						|
    return theta * theta * M_PI / (4.0 * M_LN2);
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcOmegaA() const
 | 
						|
{
 | 
						|
    return hpbwToSteradians(m_beamWidth);
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcOmegaS() const
 | 
						|
{
 | 
						|
    if (m_settings.m_sourceType == RadioAstronomySettings::UNKNOWN)
 | 
						|
    {
 | 
						|
        return 0.0;
 | 
						|
    }
 | 
						|
    else if (m_settings.m_sourceType == RadioAstronomySettings::EXTENDED)
 | 
						|
    {
 | 
						|
        return calcOmegaA();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return m_settings.m_omegaSUnits == RadioAstronomySettings::STERRADIANS ? m_settings.m_omegaS : degreesToSteradian(m_settings.m_omegaS);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::beamFillingFactor() const
 | 
						|
{
 | 
						|
    if (m_settings.m_sourceType == RadioAstronomySettings::EXTENDED)
 | 
						|
    {
 | 
						|
        return 1.0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // https://www.cv.nrao.edu/~sransom/web/Ch3.html#E55
 | 
						|
        return calcOmegaS() / calcOmegaA();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::powerMeasurementReceived(FFTMeasurement *fft, bool skipCalcs)
 | 
						|
{
 | 
						|
    ui->powerTable->setSortingEnabled(false);
 | 
						|
    int row = ui->powerTable->rowCount();
 | 
						|
    ui->powerTable->setRowCount(row + 1);
 | 
						|
 | 
						|
    QTableWidgetItem* dateItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* timeItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* powerItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* powerDBItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* powerdBmItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* tSysItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* tSys0Item = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* tSourceItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* tBItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* tSkyItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* fluxItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* sigmaTItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* sigmaSItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* omegaAItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* omegaSItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* raItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* decItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* lonItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* latItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* azItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* elItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* vBCRSItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* vLSRItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* solarFluxItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* airTempItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* sensor1Item = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* sensor2Item = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* utcItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* rotAzItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* rotElItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* rotAzOffItem = new QTableWidgetItem();
 | 
						|
    QTableWidgetItem* rotElOffItem = new QTableWidgetItem();
 | 
						|
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_DATE, dateItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TIME, timeItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER, powerItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER_DB, powerDBItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_POWER_DBM, powerdBmItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSYS, tSysItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSYS0, tSys0Item);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSOURCE, tSourceItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TB, tBItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_TSKY, tSkyItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_FLUX, fluxItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SIGMA_T, sigmaTItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SIGMA_S, sigmaSItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_OMEGA_A, omegaAItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_OMEGA_S, omegaSItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_RA, raItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_DEC, decItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_GAL_LON, lonItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_GAL_LAT, latItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_AZ, azItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_EL, elItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_VBCRS, vBCRSItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_VLSR, vLSRItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SOLAR_FLUX, solarFluxItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_AIR_TEMP, airTempItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SENSOR_1, sensor1Item);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_SENSOR_2, sensor2Item);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_UTC, utcItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_AZ, rotAzItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_EL, rotElItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_AZ_OFF, rotAzOffItem);
 | 
						|
    ui->powerTable->setItem(row, POWER_COL_ROT_EL_OFF, rotElOffItem);
 | 
						|
 | 
						|
    ui->powerTable->setSortingEnabled(true);
 | 
						|
 | 
						|
    QDateTime dateTime = fft->m_dateTime;
 | 
						|
    dateItem->setData(Qt::DisplayRole, dateTime.date());
 | 
						|
    timeItem->setData(Qt::DisplayRole, dateTime.time());
 | 
						|
    utcItem->setData(Qt::DisplayRole, dateTime.toUTC());
 | 
						|
 | 
						|
    powerItem->setData(Qt::DisplayRole, fft->m_totalPower);
 | 
						|
    powerDBItem->setData(Qt::DisplayRole, fft->m_totalPowerdBFS);
 | 
						|
    if (fft->m_tSys != 0.0f) {
 | 
						|
        powerdBmItem->setData(Qt::DisplayRole, fft->m_totalPowerdBm);
 | 
						|
    }
 | 
						|
    if (fft->m_temp) {
 | 
						|
        updatePowerColumns(row, fft);
 | 
						|
    }
 | 
						|
    if (fft->m_coordsValid)
 | 
						|
    {
 | 
						|
        raItem->setData(Qt::DisplayRole, fft->m_ra);
 | 
						|
        decItem->setData(Qt::DisplayRole, fft->m_dec);
 | 
						|
        latItem->setData(Qt::DisplayRole, fft->m_b);
 | 
						|
        lonItem->setData(Qt::DisplayRole, fft->m_l);
 | 
						|
        azItem->setData(Qt::DisplayRole, fft->m_azimuth);
 | 
						|
        elItem->setData(Qt::DisplayRole, fft->m_elevation);
 | 
						|
        vBCRSItem->setData(Qt::DisplayRole, fft->m_vBCRS);
 | 
						|
        vLSRItem->setData(Qt::DisplayRole, fft->m_vLSR);
 | 
						|
        tSkyItem->setData(Qt::DisplayRole, fft->m_skyTemp);
 | 
						|
    }
 | 
						|
    solarFluxItem->setData(Qt::DisplayRole, fft->m_solarFlux);
 | 
						|
    airTempItem->setData(Qt::DisplayRole, fft->m_airTemp);
 | 
						|
    sensor1Item->setData(Qt::DisplayRole, fft->m_sensor[0]);
 | 
						|
    sensor2Item->setData(Qt::DisplayRole, fft->m_sensor[1]);
 | 
						|
    if (fft->m_rotValid)
 | 
						|
    {
 | 
						|
        rotAzItem->setData(Qt::DisplayRole, fft->m_rotAz);
 | 
						|
        rotElItem->setData(Qt::DisplayRole, fft->m_rotEl);
 | 
						|
        rotAzOffItem->setData(Qt::DisplayRole, fft->m_rotAzOff);
 | 
						|
        rotElOffItem->setData(Qt::DisplayRole, fft->m_rotElOff);
 | 
						|
    }
 | 
						|
 | 
						|
    addToPowerSeries(fft, skipCalcs);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::powerAutoscale()
 | 
						|
{
 | 
						|
    if (m_settings.m_powerAutoscale)
 | 
						|
    {
 | 
						|
        on_powerAutoscaleX_clicked();
 | 
						|
        on_powerAutoscaleY_clicked();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Scale X and Y axis according to min and max values
 | 
						|
void RadioAstronomyGUI::on_powerAutoscale_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerAutoscale = checked;
 | 
						|
    ui->powerAutoscaleX->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    ui->powerAutoscaleY->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    ui->powerReference->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    ui->powerRange->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    ui->powerStartTime->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    ui->powerEndTime->setEnabled(!m_settings.m_powerAutoscale);
 | 
						|
    powerAutoscale();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::powerAutoscaleY(bool adjustAxis)
 | 
						|
{
 | 
						|
    double min = m_powerMin;
 | 
						|
    double max = m_powerMax;
 | 
						|
    double range = max - min;
 | 
						|
    // Round to 1 or 2 decimal places
 | 
						|
    if (range > 1.0)
 | 
						|
    {
 | 
						|
        min = std::floor(min * 10.0) / 10.0;
 | 
						|
        max = std::ceil(max * 10.0) / 10.0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        min = std::floor(min * 100.0) / 100.0;
 | 
						|
        max = std::ceil(max * 100.0) / 100.0;
 | 
						|
    }
 | 
						|
    range = max - min;
 | 
						|
    max += range * 0.2; // Add 20% space for markers
 | 
						|
    range = max - min;
 | 
						|
    range = std::max(0.1, range);     // Don't be smaller than minimum value we can set in GUI
 | 
						|
 | 
						|
    if (adjustAxis) {
 | 
						|
        m_powerYAxis->setRange(min, max);
 | 
						|
    }
 | 
						|
    ui->powerRange->setValue(range); // Call before setting reference, so number of decimals are adjusted
 | 
						|
    ui->powerReference->setValue(max);
 | 
						|
}
 | 
						|
 | 
						|
// Scale Y axis according to min and max values
 | 
						|
void RadioAstronomyGUI::on_powerAutoscaleY_clicked()
 | 
						|
{
 | 
						|
    if (m_powerYAxis) {
 | 
						|
        powerAutoscaleY(true);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Scale X axis according to min and max values in series
 | 
						|
void RadioAstronomyGUI::on_powerAutoscaleX_clicked()
 | 
						|
{
 | 
						|
    if (m_powerSeries && (m_powerSeries->count() > 0))
 | 
						|
    {
 | 
						|
        QDateTime start = QDateTime::fromMSecsSinceEpoch(m_powerSeries->at(0).x());
 | 
						|
        QDateTime end = QDateTime::fromMSecsSinceEpoch(m_powerSeries->at(m_powerSeries->count()-1).x());
 | 
						|
        ui->powerStartTime->setDateTime(start);
 | 
						|
        ui->powerEndTime->setDateTime(end);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerReference_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_powerReference = value;
 | 
						|
    if (m_powerYAxis) {
 | 
						|
        m_powerYAxis->setRange(m_settings.m_powerReference - m_settings.m_powerRange, m_settings.m_powerReference);
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerRange_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_powerRange = value;
 | 
						|
    if (m_settings.m_powerRange <= 1.0)
 | 
						|
    {
 | 
						|
        ui->powerRange->setSingleStep(0.1);
 | 
						|
        ui->powerRange->setDecimals(2);
 | 
						|
        ui->powerReference->setDecimals(2);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->powerRange->setSingleStep(1.0);
 | 
						|
        ui->powerRange->setDecimals(1);
 | 
						|
        ui->powerReference->setDecimals(1);
 | 
						|
    }
 | 
						|
    if (m_powerYAxis) {
 | 
						|
        m_powerYAxis->setRange(m_settings.m_powerReference - m_settings.m_powerRange, m_settings.m_powerReference);
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerStartTime_dateTimeChanged(QDateTime value)
 | 
						|
{
 | 
						|
    if (m_powerXAxis) {
 | 
						|
        m_powerXAxis->setMin(value);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerEndTime_dateTimeChanged(QDateTime value)
 | 
						|
{
 | 
						|
    if (m_powerXAxis) {
 | 
						|
        m_powerXAxis->setMax(value);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Columns in table reordered
 | 
						|
void RadioAstronomyGUI::powerTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
 | 
						|
{
 | 
						|
    (void) oldVisualIndex;
 | 
						|
 | 
						|
    m_settings.m_powerTableColumnIndexes[logicalIndex] = newVisualIndex;
 | 
						|
}
 | 
						|
 | 
						|
// Column in table resized (when hidden size is 0)
 | 
						|
void RadioAstronomyGUI::powerTable_sectionResized(int logicalIndex, int oldSize, int newSize)
 | 
						|
{
 | 
						|
    (void) oldSize;
 | 
						|
 | 
						|
    m_settings.m_powerTableColumnSizes[logicalIndex] = newSize;
 | 
						|
}
 | 
						|
 | 
						|
// Right click in table header - show column select menu
 | 
						|
void RadioAstronomyGUI::powerTableColumnSelectMenu(QPoint pos)
 | 
						|
{
 | 
						|
    powerTableMenu->popup(ui->powerTable->horizontalHeader()->viewport()->mapToGlobal(pos));
 | 
						|
}
 | 
						|
 | 
						|
// Hide/show column when menu selected
 | 
						|
void RadioAstronomyGUI::powerTableColumnSelectMenuChecked(bool checked)
 | 
						|
{
 | 
						|
    (void) checked;
 | 
						|
 | 
						|
    QAction* action = qobject_cast<QAction*>(sender());
 | 
						|
    if (action != nullptr)
 | 
						|
    {
 | 
						|
        int idx = action->data().toInt(nullptr);
 | 
						|
        ui->powerTable->setColumnHidden(idx, !action->isChecked());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Create column select menu item
 | 
						|
QAction *RadioAstronomyGUI::createCheckableItem(QString &text, int idx, bool checked, const char *slot)
 | 
						|
{
 | 
						|
    QAction *action = new QAction(text, this);
 | 
						|
    action->setCheckable(true);
 | 
						|
    action->setChecked(checked);
 | 
						|
    action->setData(QVariant(idx));
 | 
						|
    connect(action, SIGNAL(triggered()), this, slot);
 | 
						|
    return action;
 | 
						|
}
 | 
						|
 | 
						|
RadioAstronomyGUI* RadioAstronomyGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
 | 
						|
{
 | 
						|
    RadioAstronomyGUI* gui = new RadioAstronomyGUI(pluginAPI, deviceUISet, rxChannel);
 | 
						|
    return gui;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::destroy()
 | 
						|
{
 | 
						|
    delete this;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::resetToDefaults()
 | 
						|
{
 | 
						|
    m_settings.resetToDefaults();
 | 
						|
    displaySettings();
 | 
						|
    applySettings(true);
 | 
						|
}
 | 
						|
 | 
						|
QByteArray RadioAstronomyGUI::serialize() const
 | 
						|
{
 | 
						|
    return m_settings.serialize();
 | 
						|
}
 | 
						|
 | 
						|
bool RadioAstronomyGUI::deserialize(const QByteArray& data)
 | 
						|
{
 | 
						|
    if(m_settings.deserialize(data)) {
 | 
						|
        displaySettings();
 | 
						|
        applySettings(true);
 | 
						|
        return true;
 | 
						|
    } else {
 | 
						|
        resetToDefaults();
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateAvailableFeatures(const AvailableChannelOrFeatureList& availableFeatures, const QStringList& renameFrom, const QStringList& renameTo)
 | 
						|
{
 | 
						|
    // Update starTracker settting if it has been renamed
 | 
						|
    if (renameFrom.contains(m_settings.m_starTracker))
 | 
						|
    {
 | 
						|
        m_settings.m_starTracker = renameTo[renameFrom.indexOf(m_settings.m_starTracker)];
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
 | 
						|
    ui->starTracker->blockSignals(true);
 | 
						|
    ui->starTracker->clear();
 | 
						|
 | 
						|
    for (const auto& feature : availableFeatures) {
 | 
						|
        ui->starTracker->addItem(feature.getLongId());
 | 
						|
    }
 | 
						|
 | 
						|
    int idx = ui->starTracker->findText(m_settings.m_starTracker);
 | 
						|
    if (idx >= 0) {
 | 
						|
        ui->starTracker->setCurrentIndex(idx);
 | 
						|
    } else  {
 | 
						|
        ui->starTracker->setCurrentIndex(0);
 | 
						|
    }
 | 
						|
 | 
						|
    ui->starTracker->blockSignals(false);
 | 
						|
 | 
						|
    QString newText = ui->starTracker->currentText();
 | 
						|
    if (m_settings.m_starTracker != newText)
 | 
						|
    {
 | 
						|
       m_settings.m_starTracker = newText;
 | 
						|
       applySettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool RadioAstronomyGUI::handleMessage(const Message& message)
 | 
						|
{
 | 
						|
    if (DSPSignalNotification::match(message))
 | 
						|
    {
 | 
						|
        DSPSignalNotification& notif = (DSPSignalNotification&) message;
 | 
						|
        m_basebandSampleRate = notif.getSampleRate();
 | 
						|
        m_centerFrequency = notif.getCenterFrequency();
 | 
						|
        ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
 | 
						|
        ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2));
 | 
						|
        updateAbsoluteCenterFrequency();
 | 
						|
 | 
						|
        if (m_settings.m_tempGalLink) {
 | 
						|
            calcGalacticBackgroundTemp();
 | 
						|
        }
 | 
						|
 | 
						|
        updateTSys0();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgReportAvailableFeatures::match(message))
 | 
						|
    {
 | 
						|
        qDebug("RadioAstronomyGUI::handleMessage: MsgReportAvailableFeatures");
 | 
						|
        RadioAstronomy::MsgReportAvailableFeatures& report = (RadioAstronomy::MsgReportAvailableFeatures&) message;
 | 
						|
        updateAvailableFeatures(report.getFeatures(), report.getRenameFrom(), report.getRenameTo());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (MainCore::MsgStarTrackerTarget::match(message))
 | 
						|
    {
 | 
						|
        MainCore::MsgStarTrackerTarget& msg = (MainCore::MsgStarTrackerTarget&)message;
 | 
						|
        SWGSDRangel::SWGStarTrackerTarget *target = msg.getSWGStarTrackerTarget();
 | 
						|
        m_coordsValid = true;
 | 
						|
        m_ra = target->getRa();
 | 
						|
        m_dec = target->getDec();
 | 
						|
        m_azimuth = target->getAzimuth();
 | 
						|
        m_elevation = target->getElevation();
 | 
						|
        m_l = target->getL();
 | 
						|
        m_b = target->getB();
 | 
						|
        m_vBCRS = target->getEarthRotationVelocity() + target->getEarthOrbitVelocityBcrs();
 | 
						|
        m_vLSR = target->getSunVelocityLsr() + m_vBCRS;
 | 
						|
        m_solarFlux = target->getSolarFlux();
 | 
						|
        double airTemp = target->getAirTemperature();
 | 
						|
        m_skyTemp = target->getSkyTemperature();
 | 
						|
        m_beamWidth = target->getHpbw();
 | 
						|
 | 
						|
        if (m_settings.m_elevationLink) {
 | 
						|
            ui->elevation->setValue(m_elevation);
 | 
						|
        }
 | 
						|
        if (m_settings.m_tempAirLink) {
 | 
						|
            ui->tempAir->setValue(airTemp);
 | 
						|
        }
 | 
						|
        SensorMeasurement* sm = new SensorMeasurement(QDateTime::currentDateTime(), airTemp);
 | 
						|
        m_airTemps.append(sm);
 | 
						|
        updateTSys0();
 | 
						|
        updateOmegaA();
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgConfigureRadioAstronomy::match(message))
 | 
						|
    {
 | 
						|
        const RadioAstronomy::MsgConfigureRadioAstronomy& cfg = (RadioAstronomy::MsgConfigureRadioAstronomy&) message;
 | 
						|
        m_settings = cfg.getSettings();
 | 
						|
        blockApplySettings(true);
 | 
						|
        m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
 | 
						|
        displaySettings();
 | 
						|
        blockApplySettings(false);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgMeasurementProgress::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgMeasurementProgress& progress = (RadioAstronomy::MsgMeasurementProgress&) message;
 | 
						|
        ui->measurementProgress->setValue(progress.getPercentComplete());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgSweepStatus::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgSweepStatus& status = (RadioAstronomy::MsgSweepStatus&) message;
 | 
						|
        ui->sweepStatus->setText(status.getStatus());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgSweepComplete::match(message))
 | 
						|
    {
 | 
						|
        ui->startStop->blockSignals(true);
 | 
						|
        ui->startStop->setChecked(false);
 | 
						|
        ui->startStop->blockSignals(false);
 | 
						|
        ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgCalComplete::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgCalComplete& measurement = (RadioAstronomy::MsgCalComplete&) message;
 | 
						|
        calCompletetReceived(measurement);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgFFTMeasurement::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgFFTMeasurement& measurement = (RadioAstronomy::MsgFFTMeasurement&) message;
 | 
						|
        fftMeasurementReceived(measurement);
 | 
						|
        if (m_settings.m_runMode == RadioAstronomySettings::SINGLE)
 | 
						|
        {
 | 
						|
            ui->startStop->blockSignals(true);
 | 
						|
            ui->startStop->setChecked(false);
 | 
						|
            ui->startStop->blockSignals(false);
 | 
						|
            ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgSensorMeasurement::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgSensorMeasurement& measurement = (RadioAstronomy::MsgSensorMeasurement&) message;
 | 
						|
        sensorMeasurementReceived(measurement);
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if (RadioAstronomy::MsgReportAvailableRotators::match(message))
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgReportAvailableRotators& report = (RadioAstronomy::MsgReportAvailableRotators&) message;
 | 
						|
        updateRotatorList(report.getFeatures(), report.getRenameFrom(), report.getRenameTo());
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::handleInputMessages()
 | 
						|
{
 | 
						|
    Message* message;
 | 
						|
 | 
						|
    while ((message = getInputMessageQueue()->pop()) != 0)
 | 
						|
    {
 | 
						|
        if (handleMessage(*message))
 | 
						|
        {
 | 
						|
            delete message;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::channelMarkerChangedByCursor()
 | 
						|
{
 | 
						|
    ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
 | 
						|
    m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::channelMarkerHighlightedByCursor()
 | 
						|
{
 | 
						|
    setHighlighted(m_channelMarker.getHighlighted());
 | 
						|
}
 | 
						|
 | 
						|
// Calculate Tsys0 - i.e. receiver noise temperature when there's no source signal, just unwanted noise
 | 
						|
void RadioAstronomyGUI::updateTSys0()
 | 
						|
{
 | 
						|
    double tSys0 = calcTSys0();
 | 
						|
    ui->tSys0->setText(QString("%1").arg(round(tSys0)));
 | 
						|
    double sigmaT = calcSigmaT(tSys0);
 | 
						|
    double sigmaS = calcSigmaS(tSys0);
 | 
						|
    ui->sigmaTSys0->setText(QString("%1").arg(sigmaT, 0, 'f', 1));
 | 
						|
    ui->sigmaSSys0->setText(QString("%1").arg(sigmaS, 0, 'f', 1));
 | 
						|
}
 | 
						|
 | 
						|
// Estimate of system noise temperature due to all sources of unwanted noise, from user settings
 | 
						|
double RadioAstronomyGUI::calcTSys0() const
 | 
						|
{
 | 
						|
    return m_settings.m_tempRX + m_settings.m_tempCMB + m_settings.m_tempGal + m_settings.m_tempSP + m_settings.m_tempAtm;
 | 
						|
}
 | 
						|
 | 
						|
// Calculate measurement time
 | 
						|
double RadioAstronomyGUI::calcTau() const
 | 
						|
{
 | 
						|
    return m_settings.m_integration / (m_settings.m_sampleRate / (double)m_settings.m_fftSize);
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcTau(const FFTMeasurement* fft) const
 | 
						|
{
 | 
						|
    return fft->m_integration / (fft->m_sampleRate / (double)fft->m_fftSize);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate variation in Tsys due to random noise fluctuations, including receiver gain variations
 | 
						|
// Minimum temp we can reliably detect will be ~5x this
 | 
						|
// Uses practical total-power radiometer equation: https://www.cv.nrao.edu/~sransom/web/Ch3.html#E158
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcSigmaT(double tSys) const
 | 
						|
{
 | 
						|
    double tau = calcTau();
 | 
						|
    return tSys * sqrt(1.0/(m_settings.m_rfBandwidth * tau) + m_settings.m_gainVariation * m_settings.m_gainVariation);
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcSigmaT(const FFTMeasurement* fft) const
 | 
						|
{
 | 
						|
    double tau = calcTau(fft);
 | 
						|
    return fft->m_tSys * sqrt(1.0/(fft->m_rfBandwidth * tau) + m_settings.m_gainVariation * m_settings.m_gainVariation);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate variations in flux due to random noise fluctuations, including receiver gain variations
 | 
						|
// Minimum flux we can reliably detect will be ~5x this
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcSigmaS(double tSys) const
 | 
						|
{
 | 
						|
    double omegaA = hpbwToSteradians(m_beamWidth);
 | 
						|
    double lambda = Astronomy::m_speedOfLight / (double)m_centerFrequency;
 | 
						|
    double flux = 2.0 * Astronomy::m_boltzmann * tSys * omegaA / (lambda * lambda); // Should we use Aeff here instead?
 | 
						|
    double tau = calcTau();
 | 
						|
    double sigma = flux * sqrt(1.0/(m_settings.m_rfBandwidth * tau) + m_settings.m_gainVariation * m_settings.m_gainVariation);
 | 
						|
    return Units::wattsPerMetrePerHertzToJansky(sigma);
 | 
						|
}
 | 
						|
 | 
						|
double RadioAstronomyGUI::calcSigmaS(const FFTMeasurement* fft) const
 | 
						|
{
 | 
						|
    double omegaA = fft->m_omegaA;
 | 
						|
    double lambda = Astronomy::m_speedOfLight / (double)fft->m_centerFrequency;
 | 
						|
    double flux = 2.0 * Astronomy::m_boltzmann * fft->m_tSys * omegaA / (lambda * lambda); // Should we use Aeff here instead?
 | 
						|
    double tau = calcTau(fft);
 | 
						|
    double sigma = flux * sqrt(1.0/(fft->m_rfBandwidth * tau) + m_settings.m_gainVariation * m_settings.m_gainVariation);
 | 
						|
    return Units::wattsPerMetrePerHertzToJansky(sigma);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate and display how long a single measurement will take
 | 
						|
void RadioAstronomyGUI::updateIntegrationTime()
 | 
						|
{
 | 
						|
    double secs = calcTau();
 | 
						|
    if (secs >= 60) {
 | 
						|
         ui->integrationTime->setText(QString("%1m").arg(secs/60, 0, 'f', 1));
 | 
						|
    } else {
 | 
						|
        ui->integrationTime->setText(QString("%1s").arg(secs, 0, 'f', 1));
 | 
						|
    }
 | 
						|
    updateTSys0();
 | 
						|
}
 | 
						|
 | 
						|
// Limit bandwidth to be less than sample rate
 | 
						|
void RadioAstronomyGUI::updateBWLimits()
 | 
						|
{
 | 
						|
    qint64 sr = (qint64) m_settings.m_sampleRate;
 | 
						|
    int digits = ceil(log10(sr+1));
 | 
						|
    ui->rfBW->setValueRange(true, digits, 100, sr);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_deltaFrequency_changed(qint64 value)
 | 
						|
{
 | 
						|
    m_channelMarker.setCenterFrequency(value);
 | 
						|
    m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
 | 
						|
    updateAbsoluteCenterFrequency();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sampleRate_changed(qint64 value)
 | 
						|
{
 | 
						|
    float sr = value;
 | 
						|
    m_settings.m_sampleRate = sr;
 | 
						|
    updateBWLimits();
 | 
						|
    updateIntegrationTime();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_rfBW_changed(qint64 value)
 | 
						|
{
 | 
						|
    float bw = value;
 | 
						|
    m_channelMarker.setBandwidth(bw);
 | 
						|
    m_settings.m_rfBandwidth = bw;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_integration_changed(qint64 value)
 | 
						|
{
 | 
						|
    m_settings.m_integration = value;
 | 
						|
    updateIntegrationTime();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_recalibrate_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_recalibrate = checked;
 | 
						|
    applySettings();
 | 
						|
    if (checked) {
 | 
						|
        recalibrate();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_showCalSettings_clicked()
 | 
						|
{
 | 
						|
    RadioAstronomyCalibrationDialog dialog(&m_settings);
 | 
						|
    if (dialog.exec() == QDialog::Accepted) {
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Start hot calibration
 | 
						|
void RadioAstronomyGUI::on_startCalHot_clicked()
 | 
						|
{
 | 
						|
    if (ui->startStop->isChecked()) {
 | 
						|
        ui->startStop->click();
 | 
						|
    }
 | 
						|
    m_radioAstronomy->getInputMessageQueue()->push(RadioAstronomy::MsgStartCal::create(true));
 | 
						|
    ui->startCalHot->setStyleSheet("QToolButton { background-color : green; }");
 | 
						|
}
 | 
						|
 | 
						|
// Start cold calibration
 | 
						|
void RadioAstronomyGUI::on_startCalCold_clicked()
 | 
						|
{
 | 
						|
    if (ui->startStop->isChecked()) {
 | 
						|
        ui->startStop->click();
 | 
						|
    }
 | 
						|
    m_radioAstronomy->getInputMessageQueue()->push(RadioAstronomy::MsgStartCal::create(false));
 | 
						|
    ui->startCalCold->setStyleSheet("QToolButton { background-color : green; }");
 | 
						|
}
 | 
						|
 | 
						|
// Clear all measurements (but not calibration data)
 | 
						|
void RadioAstronomyGUI::clearData()
 | 
						|
{
 | 
						|
    ui->powerTable->setRowCount(0);
 | 
						|
    m_powerSeries->clear();
 | 
						|
    m_powerPeakSeries->clear();
 | 
						|
    m_powerMarkerSeries->clear();
 | 
						|
    m_powerTsys0Series->clear();
 | 
						|
    m_powerFilteredSeries->clear();
 | 
						|
    m_airTemps.clear();
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        m_sensors[i].clear();
 | 
						|
    }
 | 
						|
    for (int row = 0; row < POWER_MARKER_ROWS; row++)
 | 
						|
    {
 | 
						|
        for (int col = POWER_MARKER_COL_DATE; col <= POWER_MARKER_COL_DELTA_TO; col++)
 | 
						|
        {
 | 
						|
            ui->powerMarkerTable->item(row, col)->setText("");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_powerM1Valid = false;
 | 
						|
    m_powerM2Valid = false;
 | 
						|
 | 
						|
    qDeleteAll(m_fftMeasurements);
 | 
						|
    m_fftMeasurements.clear();
 | 
						|
    m_fftSeries->clear();
 | 
						|
    m_fftPeakSeries->clear();
 | 
						|
    m_fftMarkerSeries->clear();
 | 
						|
    for (int row = 0; row < SPECTRUM_MARKER_ROWS; row++)
 | 
						|
    {
 | 
						|
        for (int col = SPECTRUM_MARKER_COL_FREQ; col <= SPECTRUM_MARKER_COL_D; col++)
 | 
						|
        {
 | 
						|
            ui->spectrumMarkerTable->item(row, col)->setText("");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_spectrumM1Valid = false;
 | 
						|
    m_spectrumM2Valid = false;
 | 
						|
    clearLoSMarker("Max");
 | 
						|
    clearLoSMarker("M1");
 | 
						|
    clearLoSMarker("M2");
 | 
						|
 | 
						|
    ui->spectrumIndex->setRange(0, 0);
 | 
						|
    ui->spectrumDateTime->setDateTime(QDateTime::currentDateTime());
 | 
						|
    ui->powerMean->setText("");
 | 
						|
    ui->powerRMS->setText("");
 | 
						|
    ui->powerSD->setText("");
 | 
						|
    plotPowerVsTimeChart(); // To ensure min/max/peaks are reset
 | 
						|
 | 
						|
    create2DImage();
 | 
						|
    plotPowerChart();
 | 
						|
 | 
						|
    ui->measurementProgress->setValue(0);
 | 
						|
    ui->sweepStatus->setText("");
 | 
						|
}
 | 
						|
 | 
						|
// Clear calibration data
 | 
						|
void RadioAstronomyGUI::clearCalData()
 | 
						|
{
 | 
						|
    delete m_calHot;
 | 
						|
    delete m_calCold;
 | 
						|
    delete m_calG;
 | 
						|
    m_calHot = nullptr;
 | 
						|
    m_calCold = nullptr;
 | 
						|
    m_calG = nullptr;
 | 
						|
    m_calHotSeries->clear();
 | 
						|
    m_calColdSeries->clear();
 | 
						|
    ui->calAvgDiff->setText("");
 | 
						|
}
 | 
						|
 | 
						|
// deleteRowsComplete should be called after all rows are deleted
 | 
						|
// Returns if the row being deleted is the currently displayed FFT
 | 
						|
bool RadioAstronomyGUI::deleteRow(int row)
 | 
						|
{
 | 
						|
    ui->powerTable->removeRow(row);
 | 
						|
    delete m_fftMeasurements[row];
 | 
						|
    m_fftMeasurements.removeAt(row);
 | 
						|
    return row == ui->spectrumIndex->value();
 | 
						|
}
 | 
						|
 | 
						|
// Updates GUI after rows have been deleted
 | 
						|
void RadioAstronomyGUI::deleteRowsComplete(bool deletedCurrent, int next)
 | 
						|
{
 | 
						|
    if (m_fftMeasurements.size() == 0)
 | 
						|
    {
 | 
						|
        clearData();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (deletedCurrent) {
 | 
						|
            ui->spectrumIndex->setValue(next);
 | 
						|
        }
 | 
						|
        plotPowerChart();
 | 
						|
        powerAutoscale();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Calculate average difference in hot and cold cal data - so we can easily validate results
 | 
						|
void RadioAstronomyGUI::calcCalAvgDiff()
 | 
						|
{
 | 
						|
    if ((m_calHot && m_calCold) && (m_calHot->m_fftSize == m_calCold->m_fftSize))
 | 
						|
    {
 | 
						|
        Real sum = 0.0f;
 | 
						|
        for (int i = 0; i < m_calHot->m_fftSize; i++) {
 | 
						|
            sum += CalcDb::dbPower(m_calHot->m_fftData[i]) - CalcDb::dbPower(m_calCold->m_fftData[i]);
 | 
						|
        }
 | 
						|
        Real avg = sum / m_calHot->m_fftSize;
 | 
						|
        ui->calAvgDiff->setText(QString::number(avg, 'f', 1));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->calAvgDiff->setText("");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcCalibrationScaleFactors()
 | 
						|
{
 | 
						|
    if (m_calHot)
 | 
						|
    {
 | 
						|
        delete[] m_calG;
 | 
						|
        m_calG = new double[m_calHot->m_fftSize];
 | 
						|
        // Calculate scaling factors from FFT mag to temperature
 | 
						|
        // FIXME: This assumes cal hot is fixed reference temp - E.g. 50Ohm term
 | 
						|
        for (int i = 0; i < m_calHot->m_fftSize; i++) {
 | 
						|
            m_calG[i] = (m_settings.m_tCalHot + m_settings.m_tempRX) / m_calHot->m_fftData[i];
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calibrate()
 | 
						|
{
 | 
						|
    if (m_calHotSeries)
 | 
						|
    {
 | 
						|
        calcCalibrationScaleFactors();
 | 
						|
        calcCalTrx();
 | 
						|
        calcCalTsp();
 | 
						|
 | 
						|
        if (m_settings.m_recalibrate)
 | 
						|
        {
 | 
						|
            // Apply new calibration to existing measurements
 | 
						|
            recalibrate();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Apply calibration to all existing measurements
 | 
						|
void RadioAstronomyGUI::recalibrate()
 | 
						|
{
 | 
						|
    for (int i = 0; i < m_fftMeasurements.size(); i++)
 | 
						|
    {
 | 
						|
        FFTMeasurement* fft = m_fftMeasurements[i];
 | 
						|
        // Recalibrate
 | 
						|
        calcFFTTemperatures(fft);
 | 
						|
        calcFFTTotalTemperature(fft);
 | 
						|
        // Update table
 | 
						|
        if (fft->m_tSys != 0.0f) {
 | 
						|
            ui->powerTable->item(i, POWER_COL_POWER_DBM)->setData(Qt::DisplayRole, fft->m_totalPowerdBm);
 | 
						|
        }
 | 
						|
        if (fft->m_temp) {
 | 
						|
            updatePowerColumns(i, fft);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    // Update charts
 | 
						|
    plotFFTMeasurement();
 | 
						|
    plotPowerChart();
 | 
						|
}
 | 
						|
 | 
						|
// Calculate Trx using Y-factor method
 | 
						|
void RadioAstronomyGUI::calcCalTrx()
 | 
						|
{
 | 
						|
    if ((m_calHot && m_calCold) && (m_calHot->m_fftSize == m_calCold->m_fftSize))
 | 
						|
    {
 | 
						|
        // y=Ph/Pc
 | 
						|
        double sumH = 0.0;
 | 
						|
        double sumC = 0.0;
 | 
						|
        for (int i = 0; i < m_calHot->m_fftSize; i++)
 | 
						|
        {
 | 
						|
            sumH += m_calHot->m_fftData[i];
 | 
						|
            sumC += m_calCold->m_fftData[i];
 | 
						|
        }
 | 
						|
        double y = sumH/sumC;
 | 
						|
        // Use y to calculate Trx, which should be the same for both calibration points
 | 
						|
        double Trx = (m_settings.m_tCalHot - (m_settings.m_tCalCold * y)) / (y - 1.0);
 | 
						|
        ui->calYFactor->setText(QString::number(y, 'f', 2));
 | 
						|
        ui->calTrx->setText(QString::number(Trx, 'f', 1));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->calYFactor->setText("");
 | 
						|
        ui->calTrx->setText("");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Estimate spillover temperature (This is typically very Az/El depenedent as ground noise will vary)
 | 
						|
void RadioAstronomyGUI::calcCalTsp()
 | 
						|
{
 | 
						|
    if (!ui->calTrx->text().isEmpty() && !ui->calTsky->text().isEmpty() && !ui->calYFactor->text().isEmpty())
 | 
						|
    {
 | 
						|
        double Trx = ui->calTrx->text().toDouble();
 | 
						|
        double Tsky = ui->calTsky->text().toDouble();
 | 
						|
        double y = ui->calYFactor->text().toDouble();
 | 
						|
        double atmosphericAbsorbtion = std::exp(-m_settings.m_zenithOpacity/cos(Units::degreesToRadians(90.0f - m_settings.m_elevation)));
 | 
						|
 | 
						|
        double Tsp = (m_settings.m_tCalHot + Trx) / y - (Tsky*atmosphericAbsorbtion) - m_settings.m_tempAtm - Trx;
 | 
						|
 | 
						|
        ui->calTsp->setText(QString::number(Tsp, 'f', 1));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->calTsp->setText("");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_clearData_clicked()
 | 
						|
{
 | 
						|
    clearData();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_clearCal_clicked()
 | 
						|
{
 | 
						|
    clearCalData();
 | 
						|
}
 | 
						|
 | 
						|
// Save power data in table to a CSV file
 | 
						|
void RadioAstronomyGUI::on_savePowerData_clicked()
 | 
						|
{
 | 
						|
    // Get filename to save to
 | 
						|
    QFileDialog fileDialog(nullptr, "Select file to save data to", "", "*.csv");
 | 
						|
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
 | 
						|
    if (fileDialog.exec())
 | 
						|
    {
 | 
						|
        QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
        if (fileNames.size() > 0)
 | 
						|
        {
 | 
						|
            QFile file(fileNames[0]);
 | 
						|
            if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 | 
						|
                QMessageBox::critical(this, "Radio Astronomy", QString("Failed to open file %1").arg(fileNames[0]));
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            QTextStream out(&file);
 | 
						|
 | 
						|
            // Create a CSV file from the values in the table
 | 
						|
            for (int i = 0; i < ui->powerTable->horizontalHeader()->count(); i++)
 | 
						|
            {
 | 
						|
                QString text = ui->powerTable->horizontalHeaderItem(i)->text();
 | 
						|
                out << text << ",";
 | 
						|
            }
 | 
						|
            out << "\n";
 | 
						|
            for (int i = 0; i < ui->powerTable->rowCount(); i++)
 | 
						|
            {
 | 
						|
                for (int j = 0; j < ui->powerTable->horizontalHeader()->count(); j++)
 | 
						|
                {
 | 
						|
                    out << ui->powerTable->item(i,j)->data(Qt::DisplayRole).toString() << ",";
 | 
						|
                }
 | 
						|
                out << "\n";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Create a hash mapping from column name to array index
 | 
						|
QHash<QString,int> RadioAstronomyGUI::csvHeadersToHash(QStringList cols)
 | 
						|
{
 | 
						|
    QHash<QString,int> hash;
 | 
						|
    for (int i = 0; i < cols.size(); i++) {
 | 
						|
        hash.insert(cols[i], i);
 | 
						|
    }
 | 
						|
    return hash;
 | 
						|
}
 | 
						|
 | 
						|
// Get data from column with given name, if available
 | 
						|
QString RadioAstronomyGUI::csvData(QHash<QString,int> hash, QStringList cols, QString col)
 | 
						|
{
 | 
						|
    QString s;
 | 
						|
    if (hash.contains(col))
 | 
						|
    {
 | 
						|
        int idx = hash[col];
 | 
						|
        if (idx < cols.size()) {
 | 
						|
            s = cols[idx];
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return s;
 | 
						|
}
 | 
						|
 | 
						|
bool RadioAstronomyGUI::hasNeededFFTData(QHash<QString,int> hash)
 | 
						|
{
 | 
						|
    return hash.contains("FFT Size") && hash.contains("Data");
 | 
						|
}
 | 
						|
 | 
						|
// Write FFTMeasurement to a stream
 | 
						|
void RadioAstronomyGUI::saveFFT(QTextStream& out, const FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    out << fft->m_dateTime.toString();
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_centerFrequency;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_sampleRate;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_integration;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_rfBandwidth;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_omegaA;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_omegaS;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_totalPower;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_totalPowerdBFS;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_totalPowerdBm;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_totalPowerWatts;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_tSys;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_tSys0;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_tSource;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_flux;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_sigmaT;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_sigmaS;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_tempMin;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_baseline;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_ra;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_dec;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_azimuth;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_elevation;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_l;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_b;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_vBCRS;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_vLSR;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_solarFlux;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_airTemp;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_skyTemp;
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_sensor[0];
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_sensor[1];
 | 
						|
    out << ",";
 | 
						|
    out << fft->m_fftSize;
 | 
						|
    out << ",";
 | 
						|
    for (int j = 0; j < fft->m_fftSize; j++)
 | 
						|
    {
 | 
						|
        out << fft->m_fftData[j];
 | 
						|
        out << ",";
 | 
						|
    }
 | 
						|
    if (fft->m_snr)
 | 
						|
    {
 | 
						|
        for (int j = 0; j < fft->m_fftSize; j++)
 | 
						|
        {
 | 
						|
            out << fft->m_snr[j];
 | 
						|
            out << ",";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (fft->m_temp)
 | 
						|
    {
 | 
						|
        for (int j = 0; j < fft->m_fftSize; j++)
 | 
						|
        {
 | 
						|
            out << fft->m_temp[j];
 | 
						|
            out << ",";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    out << "\n";
 | 
						|
}
 | 
						|
 | 
						|
// Create a FFTMeasurement from data read from CSV file
 | 
						|
RadioAstronomyGUI::FFTMeasurement* RadioAstronomyGUI::loadFFT(QHash<QString,int> hash, QStringList cols)
 | 
						|
{
 | 
						|
    int fftSize = csvData(hash, cols, "FFT Size").toInt();
 | 
						|
    int fftDataIdx = hash["Data"];
 | 
						|
    if ((fftSize > 0) && (cols.size() >= fftDataIdx + fftSize))
 | 
						|
    {
 | 
						|
        FFTMeasurement* fft = new FFTMeasurement();
 | 
						|
        fft->m_dateTime = QDateTime::fromString(csvData(hash, cols, "Date Time"));
 | 
						|
        fft->m_centerFrequency = csvData(hash, cols, "Centre Freq").toLongLong();
 | 
						|
        fft->m_sampleRate = csvData(hash, cols, "Sample Rate").toInt();
 | 
						|
        fft->m_integration = csvData(hash, cols, "Integration").toInt();
 | 
						|
        fft->m_rfBandwidth = csvData(hash, cols, "Bandwidth").toInt();
 | 
						|
        fft->m_omegaA = csvData(hash, cols, "OmegaA").toFloat();
 | 
						|
        fft->m_omegaS = csvData(hash, cols, "OmegaS").toFloat();
 | 
						|
 | 
						|
        fft->m_fftSize = fftSize;
 | 
						|
        fft->m_fftData = new Real[fftSize];
 | 
						|
        fft->m_db = new Real[fftSize];
 | 
						|
        for (int i = 0; i < fftSize; i++)
 | 
						|
        {
 | 
						|
            fft->m_fftData[i] = cols[fftDataIdx+i].toFloat();
 | 
						|
            fft->m_db[i] = (Real)CalcDb::dbPower(fft->m_fftData[i]);
 | 
						|
        }
 | 
						|
        if (cols.size() >= fftDataIdx + 2*fftSize)
 | 
						|
        {
 | 
						|
            fft->m_snr = new Real[fftSize];
 | 
						|
            for (int i = 0; i < fftSize; i++) {
 | 
						|
                fft->m_snr[i] = cols[fftDataIdx+fftSize+i].toFloat();
 | 
						|
            }
 | 
						|
            if (cols.size() >= fftDataIdx + 3*fftSize)
 | 
						|
            {
 | 
						|
                fft->m_temp = new Real[fftSize];
 | 
						|
                for (int i = 0; i < fftSize; i++) {
 | 
						|
                    fft->m_temp[i] = cols[fftDataIdx+2*fftSize+i].toFloat();
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        fft->m_totalPower = csvData(hash, cols, "Power (FFT)").toFloat();
 | 
						|
        fft->m_totalPowerdBFS = csvData(hash, cols, "Power (dBFS)").toFloat();
 | 
						|
        fft->m_totalPowerdBm = csvData(hash, cols, "Power (dBm)").toFloat();
 | 
						|
        fft->m_totalPowerWatts = csvData(hash, cols, "Power (Watts)").toFloat();
 | 
						|
        fft->m_tSys = csvData(hash, cols, "Tsys").toFloat();
 | 
						|
        fft->m_tSys0 = csvData(hash, cols, "Tsys0").toFloat();
 | 
						|
        fft->m_tSource = csvData(hash, cols, "Tsource").toFloat();
 | 
						|
        fft->m_flux = csvData(hash, cols, "Sv").toFloat();
 | 
						|
        fft->m_sigmaT = csvData(hash, cols, "SigmaTsys").toFloat();
 | 
						|
        fft->m_sigmaS = csvData(hash, cols, "SigmaSsys").toFloat();
 | 
						|
        fft->m_tempMin = csvData(hash, cols, "Min Temp").toFloat();
 | 
						|
        fft->m_baseline = (RadioAstronomySettings::SpectrumBaseline)csvData(hash, cols, "Baseline").toInt();
 | 
						|
 | 
						|
 | 
						|
        fft->m_ra = csvData(hash, cols, "RA").toFloat();
 | 
						|
        fft->m_dec = csvData(hash, cols, "Dec").toFloat();
 | 
						|
        fft->m_azimuth = csvData(hash, cols, "Azimuth").toFloat();
 | 
						|
        fft->m_elevation = csvData(hash, cols, "Elevation").toFloat();
 | 
						|
        fft->m_l = csvData(hash, cols, "l").toFloat();
 | 
						|
        fft->m_b = csvData(hash, cols, "b").toFloat();
 | 
						|
        if ((fft->m_ra != 0.0) || (fft->m_dec != 0.0) || (fft->m_azimuth != 0.0) || (fft->m_elevation != 0.0) || (fft->m_l != 0.0) || (fft->m_b != 0.0)) {
 | 
						|
            fft->m_coordsValid = true;
 | 
						|
        }
 | 
						|
        fft->m_vBCRS = csvData(hash, cols, "vBCRS").toFloat();
 | 
						|
        fft->m_vLSR = csvData(hash, cols, "vLSR").toFloat();
 | 
						|
        fft->m_solarFlux = csvData(hash, cols, "Solar Flux").toFloat();
 | 
						|
        fft->m_airTemp = csvData(hash, cols, "Air Temp").toFloat();
 | 
						|
        fft->m_skyTemp = csvData(hash, cols, "Sky Temp").toFloat();
 | 
						|
        fft->m_sensor[0] = csvData(hash, cols, "Sensor 1").toFloat();
 | 
						|
        fft->m_sensor[1] = csvData(hash, cols, "Sensor 2").toFloat();
 | 
						|
 | 
						|
        if (fft->m_rfBandwidth == 0)
 | 
						|
        {
 | 
						|
            fft->m_rfBandwidth = 0.9 * fft->m_sampleRate; // Older files don't have this column and we need a value for min
 | 
						|
            calcFFTTotalPower(fft);
 | 
						|
            /*calcFFTMinTemperature(fft);
 | 
						|
            calcFFTTotalTemperature(fft);*/
 | 
						|
        }
 | 
						|
        return fft;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return nullptr;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_saveSpectrumData_clicked()
 | 
						|
{
 | 
						|
    // Get filename to save to
 | 
						|
    QFileDialog fileDialog(nullptr, "Select file to save data to", "", "*.csv");
 | 
						|
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
 | 
						|
    if (fileDialog.exec())
 | 
						|
    {
 | 
						|
        QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
        if (fileNames.size() > 0)
 | 
						|
        {
 | 
						|
            QFile file(fileNames[0]);
 | 
						|
            if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
 | 
						|
                QMessageBox::critical(this, "Radio Astronomy", QString("Failed to open file %1").arg(fileNames[0]));
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            QTextStream out(&file);
 | 
						|
 | 
						|
            if (ui->spectrumChartSelect->currentIndex() == 0)
 | 
						|
            {
 | 
						|
                // Create a CSV file for all the spectrum data
 | 
						|
                out << "Date Time,Centre Freq,Sample Rate,Integration,Bandwidth,OmegaA,OmegaS,Power (FFT),Power (dBFS),Power (dBm),Power (Watts),Tsys,Tsys0,Tsource,Sv,SigmaTsys,SigmaSsys,Min Temp,Baseline,RA,Dec,Azimuth,Elevation,l,b,vBCRS,vLSR,Solar Flux,Air Temp,Sky Temp,Sensor 1,Sensor 2,FFT Size,Data\n";
 | 
						|
                for (int i = 0; i < m_fftMeasurements.size(); i++) {
 | 
						|
                    saveFFT(out, m_fftMeasurements[i]);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Create a CSV file for calibration data
 | 
						|
                out << "Cal,Cal Temp,Date Time,Centre Freq,Sample Rate,Integration,Bandwidth,OmegaA,OmegaS,Power (FFT),Power (dBFS),Power (dBm),Power (Watts),Tsys,Tsys0,Tsource,Sv,SigmaTsys,SigmaSsys,Min Temp,Baseline,RA,Dec,Azimuth,Elevation,l,b,vBCRS,vLSR,Solar Flux,Air Temp,Sky Temp,Sensor 1,Sensor 2,FFT Size,Data\n";
 | 
						|
                if (m_calHot)
 | 
						|
                {
 | 
						|
                    out << "Hot,";
 | 
						|
                    out << m_settings.m_tCalHot;
 | 
						|
                    out << ",";
 | 
						|
                    saveFFT(out, m_calHot);
 | 
						|
                }
 | 
						|
                if (m_calCold)
 | 
						|
                {
 | 
						|
                    out << "Cold,";
 | 
						|
                    out << m_settings.m_tCalCold;
 | 
						|
                    out << ",";
 | 
						|
                    saveFFT(out, m_calCold);
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_loadSpectrumData_clicked()
 | 
						|
{
 | 
						|
    // Get filename to load from
 | 
						|
    QFileDialog fileDialog(nullptr, "Select file to load data from", "", "*.csv");
 | 
						|
    fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
 | 
						|
    if (fileDialog.exec())
 | 
						|
    {
 | 
						|
        QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
        if (fileNames.size() > 0)
 | 
						|
        {
 | 
						|
            QFile file(fileNames[0]);
 | 
						|
            if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
 | 
						|
                QMessageBox::critical(this, "Radio Astronomy", QString("Failed to open file %1").arg(fileNames[0]));
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Get column names
 | 
						|
            QTextStream in(&file);
 | 
						|
            QString header = in.readLine();
 | 
						|
            QStringList colNames = header.split(",");
 | 
						|
            QHash<QString,int> hash = csvHeadersToHash(colNames);
 | 
						|
 | 
						|
            if (ui->spectrumChartSelect->currentIndex() == 0)
 | 
						|
            {
 | 
						|
                // Load data from CSV file
 | 
						|
                if (hasNeededFFTData(hash))
 | 
						|
                {
 | 
						|
                    // Remove old data - we could support multiple series for comparison
 | 
						|
                    clearData();
 | 
						|
                    // Read in FFT data from file
 | 
						|
                    ui->spectrumIndex->blockSignals(true); // Prevent every spectrum from being displayed
 | 
						|
                    while (!in.atEnd())
 | 
						|
                    {
 | 
						|
                        QString row = in.readLine();
 | 
						|
                        QStringList cols = row.split(",");
 | 
						|
                        FFTMeasurement* fft = loadFFT(hash, cols);
 | 
						|
                        if (fft) {
 | 
						|
                            addFFT(fft, true);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    ui->spectrumIndex->blockSignals(false);
 | 
						|
                    // Add data from FFT to sensor measurements
 | 
						|
                    for (int i = 0; i < m_fftMeasurements.size(); i++)
 | 
						|
                    {
 | 
						|
                        SensorMeasurement* sm;
 | 
						|
                        sm = new SensorMeasurement(m_fftMeasurements[i]->m_dateTime, m_fftMeasurements[i]->m_airTemp);
 | 
						|
                        m_airTemps.append(sm);
 | 
						|
                        for (int j = 0; j < RADIOASTRONOMY_SENSORS; j++)
 | 
						|
                        {
 | 
						|
                            sm = new SensorMeasurement(m_fftMeasurements[i]->m_dateTime, m_fftMeasurements[i]->m_sensor[j]);
 | 
						|
                            m_sensors[j].append(sm);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    // If we're loading data from scratch, autoscale both axis
 | 
						|
                    if ((ui->spectrumCenterFreq->value() == 0.0) || m_settings.m_spectrumAutoscale)
 | 
						|
                    {
 | 
						|
                        on_spectrumAutoscaleY_clicked();
 | 
						|
                        on_spectrumAutoscaleX_clicked();
 | 
						|
                    }
 | 
						|
                    // Ensure both charts are redrawn fully, as we've disabled some updates/calcs during load
 | 
						|
                    on_spectrumIndex_valueChanged(m_fftMeasurements.size() - 1);   // Don't call setValue, as it already has this value
 | 
						|
                    plotPowerChart();
 | 
						|
                    // As signals were blocked above, power axis may not match up with GUI. Manually update
 | 
						|
                    // Just calling autoscale will not work, as the GUI values may not change
 | 
						|
                    on_powerStartTime_dateTimeChanged(ui->powerStartTime->dateTime());
 | 
						|
                    on_powerEndTime_dateTimeChanged(ui->powerEndTime->dateTime());
 | 
						|
                    on_powerRange_valueChanged(m_settings.m_powerRange);
 | 
						|
                    on_powerReference_valueChanged(m_settings.m_powerReference);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                // Load calibration data from CSV file
 | 
						|
                if (hasNeededFFTData(hash) && hash.contains("Cal"))
 | 
						|
                {
 | 
						|
                    while (!in.atEnd())
 | 
						|
                    {
 | 
						|
                        QString row = in.readLine();
 | 
						|
                        QStringList cols = row.split(",");
 | 
						|
 | 
						|
                        QString calName = csvData(hash, cols, "Cal");
 | 
						|
 | 
						|
                        FFTMeasurement** calp = nullptr;
 | 
						|
                        FFTMeasurement* cal = nullptr;
 | 
						|
                        if (calName == "Hot") {
 | 
						|
                            calp = &m_calHot;
 | 
						|
                        } else if (calName == "Cold") {
 | 
						|
                            calp = &m_calCold;
 | 
						|
                        } else {
 | 
						|
                            qDebug() << "RadioAstronomyGUI::on_loadSpectrumData_clicked: Skipping unknown calibration " << calName;
 | 
						|
                        }
 | 
						|
                        if (calp)
 | 
						|
                        {
 | 
						|
                            cal = loadFFT(hash, cols);
 | 
						|
                            if (cal)
 | 
						|
                            {
 | 
						|
                                delete *calp;
 | 
						|
                                *calp = cal;
 | 
						|
                                qDebug() << "RadioAstronomyGUI::on_loadSpectrumData_clicked: Loaded calibration " << calName;
 | 
						|
                                if (calName == "Cold") {
 | 
						|
                                    ui->calTsky->setText(QString::number(cal->m_skyTemp, 'f', 1));
 | 
						|
                                }
 | 
						|
                                QString calTempString = csvData(hash, cols, "Cal Temp");
 | 
						|
                                bool ok;
 | 
						|
                                double calTemp = calTempString.toDouble(&ok);
 | 
						|
                                if (ok)
 | 
						|
                                {
 | 
						|
                                    if (calName == "Cold")
 | 
						|
                                    {
 | 
						|
                                        ui->tCalColdSelect->setCurrentIndex(0);
 | 
						|
                                        ui->tCalCold->setValue(calTemp);
 | 
						|
                                    }
 | 
						|
                                    else
 | 
						|
                                    {
 | 
						|
                                        ui->tCalHotSelect->setCurrentIndex(0);
 | 
						|
                                        ui->tCalHot->setValue(calTemp);
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    calcCalAvgDiff();
 | 
						|
                    calibrate();
 | 
						|
                    plotCalMeasurements();
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    QMessageBox::critical(this, "Radio Astronomy", QString("Missing required columns in file %1").arg(fileNames[0]));
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerTable_cellDoubleClicked(int row, int column)
 | 
						|
{
 | 
						|
    if ((column >= POWER_COL_RA) && (column <= POWER_COL_EL))
 | 
						|
    {
 | 
						|
        // Display target in Star Tracker
 | 
						|
        QList<ObjectPipe*> starTrackerPipes;
 | 
						|
        MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
 | 
						|
 | 
						|
        for (const auto& pipe : starTrackerPipes)
 | 
						|
        {
 | 
						|
            MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
 | 
						|
            SWGSDRangel::SWGStarTrackerDisplaySettings *swgSettings = new SWGSDRangel::SWGStarTrackerDisplaySettings();
 | 
						|
            QDateTime dt(ui->powerTable->item(row, POWER_COL_DATE)->data(Qt::DisplayRole).toDate(),
 | 
						|
                            ui->powerTable->item(row, POWER_COL_TIME)->data(Qt::DisplayRole).toTime());
 | 
						|
            swgSettings->setDateTime(new QString(dt.toString(Qt::ISODateWithMs)));
 | 
						|
            swgSettings->setAzimuth(ui->powerTable->item(row, POWER_COL_AZ)->data(Qt::DisplayRole).toFloat());
 | 
						|
            swgSettings->setElevation(ui->powerTable->item(row, POWER_COL_EL)->data(Qt::DisplayRole).toFloat());
 | 
						|
            messageQueue->push(MainCore::MsgStarTrackerDisplaySettings::create(m_radioAstronomy, swgSettings));
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Display in Spectrometer
 | 
						|
        ui->spectrumIndex->setValue(row);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::onWidgetRolled(QWidget* widget, bool rollDown)
 | 
						|
{
 | 
						|
    (void) widget;
 | 
						|
    (void) rollDown;
 | 
						|
 | 
						|
    getRollupContents()->saveState(m_rollupState);
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::onMenuDialogCalled(const QPoint &p)
 | 
						|
{
 | 
						|
    if (m_contextMenuType == ContextMenuChannelSettings)
 | 
						|
    {
 | 
						|
        BasicChannelSettingsDialog dialog(&m_channelMarker, this);
 | 
						|
        dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
 | 
						|
        dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
 | 
						|
        dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
 | 
						|
        dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
 | 
						|
        dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
 | 
						|
        dialog.setDefaultTitle(m_displayedName);
 | 
						|
 | 
						|
        if (m_deviceUISet->m_deviceMIMOEngine)
 | 
						|
        {
 | 
						|
            dialog.setNumberOfStreams(m_radioAstronomy->getNumberOfDeviceStreams());
 | 
						|
            dialog.setStreamIndex(m_settings.m_streamIndex);
 | 
						|
        }
 | 
						|
 | 
						|
        dialog.move(p);
 | 
						|
        new DialogPositioner(&dialog, false);
 | 
						|
        dialog.exec();
 | 
						|
 | 
						|
        m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
 | 
						|
        m_settings.m_title = m_channelMarker.getTitle();
 | 
						|
        m_settings.m_useReverseAPI = dialog.useReverseAPI();
 | 
						|
        m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
 | 
						|
        m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
 | 
						|
        m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
 | 
						|
        m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
 | 
						|
 | 
						|
        setWindowTitle(m_settings.m_title);
 | 
						|
        setTitle(m_channelMarker.getTitle());
 | 
						|
        setTitleColor(m_settings.m_rgbColor);
 | 
						|
 | 
						|
        if (m_deviceUISet->m_deviceMIMOEngine)
 | 
						|
        {
 | 
						|
            m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
 | 
						|
            m_channelMarker.clearStreamIndexes();
 | 
						|
            m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
 | 
						|
            updateIndexLabel();
 | 
						|
        }
 | 
						|
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
 | 
						|
    resetContextMenuType();
 | 
						|
}
 | 
						|
 | 
						|
RadioAstronomyGUI::RadioAstronomyGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
 | 
						|
    ChannelGUI(parent),
 | 
						|
    ui(new Ui::RadioAstronomyGUI),
 | 
						|
    m_pluginAPI(pluginAPI),
 | 
						|
    m_deviceUISet(deviceUISet),
 | 
						|
    m_channelMarker(this),
 | 
						|
    m_doApplySettings(true),
 | 
						|
    m_basebandSampleRate(0),
 | 
						|
    m_centerFrequency(0),
 | 
						|
    m_tickCount(0),
 | 
						|
    m_powerChart(nullptr),
 | 
						|
    m_powerSeries(nullptr),
 | 
						|
    m_powerXAxis(nullptr),
 | 
						|
    m_powerYAxis(nullptr),
 | 
						|
    m_powerPeakSeries(nullptr),
 | 
						|
    m_powerMarkerSeries(nullptr),
 | 
						|
    m_powerTsys0Series(nullptr),
 | 
						|
    m_powerGaussianSeries(nullptr),
 | 
						|
    m_powerFilteredSeries(nullptr),
 | 
						|
    m_powerPeakValid(false),
 | 
						|
    m_2DChart(nullptr),
 | 
						|
    m_2DXAxis(nullptr),
 | 
						|
    m_2DYAxis(nullptr),
 | 
						|
    m_2DMapIntensity(nullptr),
 | 
						|
    m_sweepIndex(0),
 | 
						|
    m_calChart(nullptr),
 | 
						|
    m_calXAxis(nullptr),
 | 
						|
    m_calYAxis(nullptr),
 | 
						|
    m_calHotSeries(nullptr),
 | 
						|
    m_calColdSeries(nullptr),
 | 
						|
    m_calHot(nullptr),
 | 
						|
    m_calCold(nullptr),
 | 
						|
    m_calG(nullptr),
 | 
						|
    m_fftChart(nullptr),
 | 
						|
    m_fftSeries(nullptr),
 | 
						|
    m_fftHlineSeries(nullptr),
 | 
						|
    m_fftPeakSeries(nullptr),
 | 
						|
    m_fftMarkerSeries(nullptr),
 | 
						|
    m_fftGaussianSeries(nullptr),
 | 
						|
    m_fftLABSeries(nullptr),
 | 
						|
    m_fftXAxis(nullptr),
 | 
						|
    m_fftYAxis(nullptr),
 | 
						|
    m_fftDopplerAxis(nullptr),
 | 
						|
    m_powerM1Valid(false),
 | 
						|
    m_powerM2Valid(false),
 | 
						|
    m_spectrumM1Valid(false),
 | 
						|
    m_spectrumM2Valid(false),
 | 
						|
    m_coordsValid(false),
 | 
						|
    m_ra(0.0f),
 | 
						|
    m_dec(0.0f),
 | 
						|
    m_azimuth(0.0f),
 | 
						|
    m_elevation(0.0f),
 | 
						|
    m_l(0.0f),
 | 
						|
    m_b(0.0f),
 | 
						|
    m_vBCRS(0.0f),
 | 
						|
    m_vLSR(0.0f),
 | 
						|
    m_solarFlux(0.0f),
 | 
						|
    m_beamWidth(5.6f),
 | 
						|
    m_lLAB(0.0f),
 | 
						|
    m_bLAB(0.0f),
 | 
						|
    m_downloadingLAB(false),
 | 
						|
    m_window(nullptr),
 | 
						|
    m_windowSorted(nullptr),
 | 
						|
    m_windowIdx(0),
 | 
						|
    m_windowCount(0)
 | 
						|
{
 | 
						|
    qDebug("RadioAstronomyGUI::RadioAstronomyGUI");
 | 
						|
    setAttribute(Qt::WA_DeleteOnClose, true);
 | 
						|
    m_helpURL = "plugins/channelrx/radioastronomy/readme.md";
 | 
						|
    RollupContents *rollupContents = getRollupContents();
 | 
						|
	ui->setupUi(rollupContents);
 | 
						|
    setSizePolicy(rollupContents->sizePolicy());
 | 
						|
    rollupContents->arrangeRollups();
 | 
						|
	connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
 | 
						|
    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
 | 
						|
 | 
						|
    m_radioAstronomy = reinterpret_cast<RadioAstronomy*>(rxChannel);
 | 
						|
    m_radioAstronomy->setMessageQueueToGUI(getInputMessageQueue());
 | 
						|
 | 
						|
    connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
 | 
						|
 | 
						|
    m_networkManager = new QNetworkAccessManager();
 | 
						|
    QObject::connect(
 | 
						|
        m_networkManager,
 | 
						|
        &QNetworkAccessManager::finished,
 | 
						|
        this,
 | 
						|
        &RadioAstronomyGUI::networkManagerFinished
 | 
						|
    );
 | 
						|
    connect(&m_dlm, &HttpDownloadManager::downloadComplete, this, &RadioAstronomyGUI::downloadFinished);
 | 
						|
 | 
						|
    ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
 | 
						|
    ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
 | 
						|
    ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
 | 
						|
 | 
						|
    // Need to setValue before calling setValueRange, otherwise valueChanged is called
 | 
						|
    // overwriting the default settings (could also blockSignals)
 | 
						|
    // Also, set bandwidth before sampleRate
 | 
						|
    ui->rfBW->setColorMapper(ColorMapper(ColorMapper::GrayGold));
 | 
						|
    ui->rfBW->setValue(m_settings.m_rfBandwidth);
 | 
						|
    ui->rfBW->setValueRange(true, 8, 100, 99999999);
 | 
						|
    ui->sampleRate->setColorMapper(ColorMapper(ColorMapper::GrayGold));
 | 
						|
    ui->sampleRate->setValue(m_settings.m_sampleRate);
 | 
						|
    ui->sampleRate->setValueRange(true, 8, 1000, 99999999);
 | 
						|
    ui->integration->setColorMapper(ColorMapper(ColorMapper::GrayGold));
 | 
						|
    ui->integration->setValue(m_settings.m_integration);
 | 
						|
    ui->integration->setValueRange(true, 7, 1, 99999999);
 | 
						|
 | 
						|
    m_channelMarker.blockSignals(true);
 | 
						|
    m_channelMarker.setColor(Qt::yellow);
 | 
						|
    m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
 | 
						|
    m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
 | 
						|
    m_channelMarker.setTitle("Radio Astronomy");
 | 
						|
    m_channelMarker.blockSignals(false);
 | 
						|
    m_channelMarker.setVisible(true); // activate signal on the last setting only
 | 
						|
 | 
						|
    setTitleColor(m_channelMarker.getColor());
 | 
						|
    m_settings.setChannelMarker(&m_channelMarker);
 | 
						|
    m_settings.setRollupState(&m_rollupState);
 | 
						|
 | 
						|
    m_deviceUISet->addChannelMarker(&m_channelMarker);
 | 
						|
 | 
						|
    connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
 | 
						|
    connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
 | 
						|
    connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
 | 
						|
 | 
						|
    // Resize the table using dummy data
 | 
						|
    resizePowerTable();
 | 
						|
    // Allow user to reorder columns
 | 
						|
    ui->powerTable->horizontalHeader()->setSectionsMovable(true);
 | 
						|
    // Allow user to sort table by clicking on headers
 | 
						|
    ui->powerTable->setSortingEnabled(true);
 | 
						|
    // Add context menu to allow hiding/showing of columns
 | 
						|
    powerTableMenu = new QMenu(ui->powerTable);
 | 
						|
    for (int i = 0; i < ui->powerTable->horizontalHeader()->count(); i++)
 | 
						|
    {
 | 
						|
        QString text = ui->powerTable->horizontalHeaderItem(i)->text();
 | 
						|
        powerTableMenu->addAction(createCheckableItem(text, i, true, SLOT(powerTableColumnSelectMenuChecked())));
 | 
						|
    }
 | 
						|
    ui->powerTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
 | 
						|
    connect(ui->powerTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(powerTableColumnSelectMenu(QPoint)));
 | 
						|
    // Get signals when columns change
 | 
						|
    connect(ui->powerTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(powerTable_sectionMoved(int, int, int)));
 | 
						|
    connect(ui->powerTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(powerTable_sectionResized(int, int, int)));
 | 
						|
    ui->powerTable->setContextMenuPolicy(Qt::CustomContextMenu);
 | 
						|
    connect(ui->powerTable, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customContextMenuRequested(QPoint)));
 | 
						|
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TIME, new TimeDelegate());
 | 
						|
    //ui->powerTable->setItemDelegateForColumn(POWER_COL_POWER, new DecimalDelegate(6));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_POWER_DB, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_POWER_DBM, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TSYS, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TSYS0, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TSOURCE, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TB, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_TSKY, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_FLUX, new DecimalDelegate(2));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_SIGMA_T, new DecimalDelegate(2));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_SIGMA_S, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_RA, new HMSDelegate());
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_DEC, new DMSDelegate());
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_GAL_LAT, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_GAL_LON, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_AZ, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_EL, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_VBCRS, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_VLSR, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_AIR_TEMP, new DecimalDelegate(1));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_ROT_AZ, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_ROT_EL, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_ROT_AZ_OFF, new DecimalDelegate(0));
 | 
						|
    ui->powerTable->setItemDelegateForColumn(POWER_COL_ROT_EL_OFF, new DecimalDelegate(0));
 | 
						|
 | 
						|
    resizeSpectrumMarkerTable();
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_FREQ, new DecimalDelegate(6));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_VALUE, new DecimalDelegate(1));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_DELTA_X, new DecimalDelegate(6));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_DELTA_Y, new DecimalDelegate(1));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_VR, new DecimalDelegate(2));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_R, new DecimalDelegate(1));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_D, new DecimalDelegate(1));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_R_MIN, new DecimalDelegate(1));
 | 
						|
    ui->spectrumMarkerTable->setItemDelegateForColumn(SPECTRUM_MARKER_COL_V, new DecimalDelegate(1));
 | 
						|
 | 
						|
    // Create blank marker table
 | 
						|
    ui->spectrumMarkerTable->setRowCount(SPECTRUM_MARKER_ROWS); // 1 peak and two markers
 | 
						|
    for (int row = 0; row < SPECTRUM_MARKER_ROWS; row++)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_NAME, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_FREQ, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_VALUE, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_X, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_Y, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_DELTA_TO, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_VR, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_R, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_D, new QTableWidgetItem());
 | 
						|
        // It seems clearing Qt::ItemIsUserCheckable doesn't remove the checkbox, so once set, we always have it
 | 
						|
        QTableWidgetItem* item = new QTableWidgetItem();
 | 
						|
        item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 | 
						|
        item->setCheckState(Qt::Unchecked);
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_PLOT_MAX, item);
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_R_MIN, new QTableWidgetItem());
 | 
						|
        ui->spectrumMarkerTable->setItem(row, SPECTRUM_MARKER_COL_V, new QTableWidgetItem());
 | 
						|
    }
 | 
						|
    ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_PEAK, SPECTRUM_MARKER_COL_NAME)->setText("Max");
 | 
						|
    ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M1, SPECTRUM_MARKER_COL_NAME)->setText("M1");
 | 
						|
    ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_NAME)->setText("M2");
 | 
						|
    connect(ui->spectrumMarkerTable, &QTableWidget::itemChanged, this, &RadioAstronomyGUI::spectrumMarkerTableItemChanged);
 | 
						|
 | 
						|
    resizePowerMarkerTable();
 | 
						|
    ui->powerMarkerTable->setItemDelegateForColumn(POWER_MARKER_COL_TIME, new TimeDelegate());
 | 
						|
    ui->powerMarkerTable->setItemDelegateForColumn(POWER_MARKER_COL_VALUE, new DecimalDelegate(1));
 | 
						|
    ui->powerMarkerTable->setItemDelegateForColumn(POWER_MARKER_COL_DELTA_X, new TimeDeltaDelegate());
 | 
						|
    ui->powerMarkerTable->setItemDelegateForColumn(POWER_MARKER_COL_DELTA_Y, new DecimalDelegate(1));
 | 
						|
 | 
						|
    // Create blank marker table
 | 
						|
    ui->powerMarkerTable->setRowCount(POWER_MARKER_ROWS); // 1 peak and two markers
 | 
						|
    for (int row = 0; row < POWER_MARKER_ROWS; row++)
 | 
						|
    {
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_NAME, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DATE, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_TIME, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_VALUE, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_X, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_Y, new QTableWidgetItem());
 | 
						|
        ui->powerMarkerTable->setItem(row, POWER_MARKER_COL_DELTA_TO, new QTableWidgetItem());
 | 
						|
    }
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MAX, POWER_MARKER_COL_NAME)->setText("Max");
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_PEAK_MIN, POWER_MARKER_COL_NAME)->setText("Min");
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_M1, POWER_MARKER_COL_NAME)->setText("M1");
 | 
						|
    ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_NAME)->setText("M2");
 | 
						|
 | 
						|
    ui->sweepStartDateTime->setMinimumDateTime(QDateTime::currentDateTime());
 | 
						|
    ui->spectrumDateTime->setDateTime(QDateTime::currentDateTime());
 | 
						|
 | 
						|
    ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
 | 
						|
 | 
						|
    displaySettings();
 | 
						|
    makeUIConnections();
 | 
						|
    applySettings(true);
 | 
						|
    m_resizer.enableChildMouseTracking();
 | 
						|
 | 
						|
    create2DImage();
 | 
						|
 | 
						|
    plotCalSpectrum();
 | 
						|
    plotSpectrum();
 | 
						|
    plotPowerChart();
 | 
						|
 | 
						|
    m_radioAstronomy->getInputMessageQueue()->push(RadioAstronomy::MsgScanAvailableFeatures::create());
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::customContextMenuRequested(QPoint pos)
 | 
						|
{
 | 
						|
    QTableWidgetItem *item = ui->powerTable->itemAt(pos);
 | 
						|
    if (item)
 | 
						|
    {
 | 
						|
        QMenu* tableContextMenu = new QMenu(ui->powerTable);
 | 
						|
        connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
 | 
						|
 | 
						|
        // Copy cell contents to clipboard
 | 
						|
        QAction* copyAction = new QAction("Copy cell", tableContextMenu);
 | 
						|
        const QString text = item->text();
 | 
						|
        connect(copyAction, &QAction::triggered, this, [text]()->void {
 | 
						|
            QClipboard *clipboard = QGuiApplication::clipboard();
 | 
						|
            clipboard->setText(text);
 | 
						|
        });
 | 
						|
        tableContextMenu->addAction(copyAction);
 | 
						|
 | 
						|
        // Delete selected rows
 | 
						|
        QAction* delAction = new QAction("Delete rows", tableContextMenu);
 | 
						|
        connect(delAction, &QAction::triggered, this, [this]()->void {
 | 
						|
            QModelIndexList rowIndexes = ui->powerTable->selectionModel()->selectedRows();
 | 
						|
            if (rowIndexes.size() > 0)
 | 
						|
            {
 | 
						|
                // Delete in reverse row order
 | 
						|
                std::vector<int> rows;
 | 
						|
                foreach (auto rowIndex, rowIndexes) {
 | 
						|
                    rows.push_back(rowIndex.row());
 | 
						|
                }
 | 
						|
                std::sort(rows.begin(), rows.end(), std::greater<int>());
 | 
						|
                bool deletedCurrent = false;
 | 
						|
                int next;
 | 
						|
                foreach (auto row, rows) {
 | 
						|
                    next = row - 1;
 | 
						|
                    if (deleteRow(row)) {
 | 
						|
                        deletedCurrent = true;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                deleteRowsComplete(deletedCurrent, next);
 | 
						|
            }
 | 
						|
        });
 | 
						|
        tableContextMenu->addAction(delAction);
 | 
						|
 | 
						|
        // Update rows with new Tsys0 and baseline
 | 
						|
        QAction* updateTSysAction = new QAction(QString("Update Tsys0 / baseline / %1").arg(QChar(937)), tableContextMenu);
 | 
						|
        connect(updateTSysAction, &QAction::triggered, this, [this]()->void {
 | 
						|
            QModelIndexList rowIndexes = ui->powerTable->selectionModel()->selectedRows();
 | 
						|
            if (rowIndexes.size() > 0)
 | 
						|
            {
 | 
						|
                foreach (auto rowIndex, rowIndexes)
 | 
						|
                {
 | 
						|
                    int row = rowIndex.row();
 | 
						|
                    m_fftMeasurements[row]->m_tSys0 = calcTSys0();
 | 
						|
                    m_fftMeasurements[row]->m_baseline = m_settings.m_spectrumBaseline;
 | 
						|
                    m_fftMeasurements[row]->m_omegaA = calcOmegaA();
 | 
						|
                    m_fftMeasurements[row]->m_omegaS = calcOmegaS();
 | 
						|
                    calcFFTTotalTemperature(m_fftMeasurements[row]);
 | 
						|
                    updatePowerColumns(row, m_fftMeasurements[row]);
 | 
						|
                }
 | 
						|
                plotFFTMeasurement();
 | 
						|
            }
 | 
						|
        });
 | 
						|
        tableContextMenu->addAction(updateTSysAction);
 | 
						|
 | 
						|
        tableContextMenu->popup(ui->powerTable->viewport()->mapToGlobal(pos));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
RadioAstronomyGUI::~RadioAstronomyGUI()
 | 
						|
{
 | 
						|
    delete m_networkManager;
 | 
						|
    delete ui;
 | 
						|
    delete m_calHot;
 | 
						|
    delete m_calCold;
 | 
						|
    qDeleteAll(m_dataLAB);
 | 
						|
    m_dataLAB.clear();
 | 
						|
    delete[] m_2DMapIntensity;
 | 
						|
    delete[] m_window;
 | 
						|
    delete[] m_windowSorted;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::blockApplySettings(bool block)
 | 
						|
{
 | 
						|
    m_doApplySettings = !block;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::applySettings(bool force)
 | 
						|
{
 | 
						|
    if (m_doApplySettings)
 | 
						|
    {
 | 
						|
        RadioAstronomy::MsgConfigureRadioAstronomy* message = RadioAstronomy::MsgConfigureRadioAstronomy::create( m_settings, force);
 | 
						|
        m_radioAstronomy->getInputMessageQueue()->push(message);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int RadioAstronomyGUI::fftSizeToIndex(int size)
 | 
						|
{
 | 
						|
    switch (size)
 | 
						|
    {
 | 
						|
    case 16:
 | 
						|
        return 0;
 | 
						|
    case 32:
 | 
						|
        return 1;
 | 
						|
    case 64:
 | 
						|
        return 2;
 | 
						|
    case 128:
 | 
						|
        return 3;
 | 
						|
    case 256:
 | 
						|
        return 4;
 | 
						|
    case 512:
 | 
						|
        return 5;
 | 
						|
    case 1024:
 | 
						|
        return 6;
 | 
						|
    case 2048:
 | 
						|
        return 7;
 | 
						|
    case 4096:
 | 
						|
        return 8;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::displaySettings()
 | 
						|
{
 | 
						|
    m_channelMarker.blockSignals(true);
 | 
						|
    m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
 | 
						|
    m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
 | 
						|
    m_channelMarker.setTitle(m_settings.m_title);
 | 
						|
    m_channelMarker.blockSignals(false);
 | 
						|
    m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
 | 
						|
 | 
						|
    setTitleColor(m_settings.m_rgbColor);
 | 
						|
    setWindowTitle(m_channelMarker.getTitle());
 | 
						|
    setTitle(m_channelMarker.getTitle());
 | 
						|
 | 
						|
    blockApplySettings(true);
 | 
						|
    ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
 | 
						|
    float rfBW = m_settings.m_rfBandwidth; // Save value, as it may be corrupted when setting sampleRate
 | 
						|
    ui->sampleRate->setValue(m_settings.m_sampleRate);
 | 
						|
    ui->rfBW->setValue(rfBW);
 | 
						|
    updateBWLimits();
 | 
						|
 | 
						|
    ui->integration->setValue(m_settings.m_integration);
 | 
						|
    ui->fftSize->setCurrentIndex(fftSizeToIndex(m_settings.m_fftSize));
 | 
						|
    ui->fftWindow->setCurrentIndex((int)m_settings.m_fftWindow);
 | 
						|
    ui->filterFreqs->setText(m_settings.m_filterFreqs);
 | 
						|
 | 
						|
    int idx = ui->starTracker->findText(m_settings.m_starTracker);
 | 
						|
    if (idx != -1) {
 | 
						|
        ui->starTracker->setCurrentIndex(idx);
 | 
						|
    }
 | 
						|
    idx = ui->rotator->findText(m_settings.m_rotator);
 | 
						|
    if (idx != -1) {
 | 
						|
        ui->rotator->setCurrentIndex(idx);
 | 
						|
    }
 | 
						|
 | 
						|
    ui->tempRXSelect->setCurrentIndex(0);
 | 
						|
    ui->tempRX->setValue(m_settings.m_tempRX);
 | 
						|
    ui->tempRXUnitsLabel->setText("K");
 | 
						|
    ui->tempCMB->setValue(m_settings.m_tempCMB);
 | 
						|
    ui->tempGal->setValue(m_settings.m_tempGal);
 | 
						|
    ui->tempGal->setEnabled(!m_settings.m_tempGalLink);
 | 
						|
    ui->tempGalLink->setChecked(m_settings.m_tempGalLink);
 | 
						|
    ui->tempSP->setValue(m_settings.m_tempSP);
 | 
						|
    ui->tempAtm->setValue(m_settings.m_tempAtm);
 | 
						|
    ui->tempAtm->setEnabled(!m_settings.m_tempAtmLink);
 | 
						|
    ui->tempAtmLink->setChecked(m_settings.m_tempAtmLink);
 | 
						|
    ui->tempAir->setValue(m_settings.m_tempAir);
 | 
						|
    ui->tempAir->setEnabled(!m_settings.m_tempAirLink);
 | 
						|
    ui->tempAirLink->setChecked(m_settings.m_tempAirLink);
 | 
						|
    ui->zenithOpacity->setValue(m_settings.m_zenithOpacity);
 | 
						|
    ui->elevation->setValue(m_settings.m_elevation);
 | 
						|
    ui->elevation->setEnabled(!m_settings.m_elevationLink);
 | 
						|
    ui->elevationLink->setChecked(m_settings.m_elevationLink);
 | 
						|
 | 
						|
    ui->gainVariation->setValue(m_settings.m_gainVariation);
 | 
						|
    ui->sourceType->setCurrentIndex((int)m_settings.m_sourceType);
 | 
						|
    ui->omegaS->setValue(m_settings.m_omegaS);
 | 
						|
    ui->omegaSUnits->setCurrentIndex((int)m_settings.m_omegaSUnits);
 | 
						|
    ui->omegaAUnits->setCurrentIndex((int)m_settings.m_omegaAUnits);
 | 
						|
 | 
						|
    ui->recalibrate->setChecked(m_settings.m_recalibrate);
 | 
						|
    ui->tCalHot->setValue(m_settings.m_tCalHot);
 | 
						|
    ui->tCalCold->setValue(m_settings.m_tCalCold);
 | 
						|
 | 
						|
    ui->spectrumAutoscale->setChecked(m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumReference->setValue(m_settings.m_spectrumReference);
 | 
						|
    ui->spectrumRange->setValue(m_settings.m_spectrumRange);
 | 
						|
    FFTMeasurement* fft = currentFFT();
 | 
						|
    if (fft) {
 | 
						|
        ui->spectrumCenterFreq->setValue(fft->m_centerFrequency/1e6 + m_settings.m_spectrumCenterFreqOffset);
 | 
						|
    } else {
 | 
						|
        ui->spectrumCenterFreq->setValue(m_centerFrequency/1e6 + m_settings.m_spectrumCenterFreqOffset);
 | 
						|
    }
 | 
						|
    ui->spectrumSpan->setValue(m_settings.m_spectrumSpan);
 | 
						|
    ui->spectrumYUnits->setCurrentIndex((int)m_settings.m_spectrumYScale);
 | 
						|
    ui->spectrumBaseline->setCurrentIndex((int)m_settings.m_spectrumBaseline);
 | 
						|
    ui->spectrumAutoscaleX->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumAutoscaleY->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumReference->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumRange->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumCenterFreq->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumSpan->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
 | 
						|
    ui->powerAutoscale->setChecked(m_settings.m_powerAutoscale);
 | 
						|
    ui->powerReference->setValue(m_settings.m_powerReference);
 | 
						|
    ui->powerRange->setValue(m_settings.m_powerRange);
 | 
						|
    ui->powerShowPeak->setChecked(m_settings.m_powerPeaks);
 | 
						|
    if (m_powerPeakSeries)
 | 
						|
    {
 | 
						|
        m_powerPeakSeries->setVisible(m_settings.m_powerPeaks);
 | 
						|
        m_powerChart->legend()->markers(m_powerPeakSeries)[0]->setVisible(false);
 | 
						|
    }
 | 
						|
    ui->powerShowMarker->setChecked(m_settings.m_powerMarkers);
 | 
						|
    if (m_powerMarkerSeries)
 | 
						|
    {
 | 
						|
        m_powerMarkerSeries->setVisible(m_settings.m_powerMarkers);
 | 
						|
        m_powerChart->legend()->markers(m_powerMarkerSeries)[0]->setVisible(false);
 | 
						|
    }
 | 
						|
    ui->powerShowAvg->setChecked(m_settings.m_powerAvg);
 | 
						|
    ui->powerChartAvgWidgets->setVisible(m_settings.m_powerAvg);
 | 
						|
    ui->powerShowGaussian->setChecked(m_settings.m_powerShowGaussian);
 | 
						|
    ui->powerGaussianWidgets->setVisible(m_settings.m_powerShowGaussian);
 | 
						|
    if (m_powerGaussianSeries) {
 | 
						|
        m_powerGaussianSeries->setVisible(m_settings.m_powerShowGaussian);
 | 
						|
    }
 | 
						|
    ui->powerShowLegend->setChecked(m_settings.m_powerLegend);
 | 
						|
    if (m_powerChart) {
 | 
						|
        m_powerChart->legend()->setVisible(m_settings.m_powerLegend);
 | 
						|
    }
 | 
						|
    ui->powerChartSelect->setCurrentIndex((int)m_settings.m_powerYData);
 | 
						|
    ui->powerYUnits->setCurrentIndex(powerYUnitsToIndex(m_settings.m_powerYUnits));
 | 
						|
    ui->powerShowTsys0->setChecked(m_settings.m_powerShowTsys0);
 | 
						|
    ui->powerShowAirTemp->setChecked(m_settings.m_powerShowAirTemp);
 | 
						|
    m_airTemps.clicked(m_settings.m_powerShowAirTemp);
 | 
						|
    ui->powerShowSensor1->setChecked(m_settings.m_sensorVisible[0]);
 | 
						|
    m_sensors[0].setName(m_settings.m_sensorName[0]);
 | 
						|
    m_sensors[0].clicked(m_settings.m_sensorVisible[0]);
 | 
						|
    ui->powerShowSensor2->setChecked(m_settings.m_sensorVisible[1]);
 | 
						|
    m_sensors[1].setName(m_settings.m_sensorName[1]);
 | 
						|
    m_sensors[1].clicked(m_settings.m_sensorVisible[1]);
 | 
						|
    ui->powerShowFiltered->setChecked(m_settings.m_powerShowFiltered);
 | 
						|
    if (m_powerFilteredSeries) {
 | 
						|
        m_powerFilteredSeries->setVisible(m_settings.m_powerShowFiltered);
 | 
						|
    }
 | 
						|
    ui->powerFilterWidgets->setVisible(m_settings.m_powerShowFiltered);
 | 
						|
    ui->powerFilter->setCurrentIndex((int)m_settings.m_powerFilter);
 | 
						|
    ui->powerFilterN->setValue(m_settings.m_powerFilterN);
 | 
						|
    ui->powerShowMeasurement->setChecked(m_settings.m_powerShowMeasurement);
 | 
						|
    if (m_powerSeries) {
 | 
						|
        m_powerSeries->setVisible(m_settings.m_powerShowMeasurement);
 | 
						|
    }
 | 
						|
 | 
						|
    ui->power2DLinkSweep->setChecked(m_settings.m_power2DLinkSweep);
 | 
						|
    ui->power2DSweepType->setCurrentIndex((int)m_settings.m_power2DSweepType);
 | 
						|
    ui->power2DWidth->setValue(m_settings.m_power2DWidth);
 | 
						|
    ui->power2DHeight->setValue(m_settings.m_power2DHeight);
 | 
						|
    ui->power2DXMin->setValue(m_settings.m_power2DXMin);
 | 
						|
    ui->power2DXMax->setValue(m_settings.m_power2DXMax);
 | 
						|
    ui->power2DYMin->setValue(m_settings.m_power2DYMin);
 | 
						|
    ui->power2DYMax->setValue(m_settings.m_power2DYMax);
 | 
						|
    ui->powerColourAutoscale->setChecked(m_settings.m_powerColourAutoscale);
 | 
						|
    ui->powerColourScaleMin->setValue(m_settings.m_powerColourScaleMin);
 | 
						|
    ui->powerColourScaleMin->setEnabled(!m_settings.m_powerColourAutoscale);
 | 
						|
    ui->powerColourScaleMax->setValue(m_settings.m_powerColourScaleMax);
 | 
						|
    ui->powerColourScaleMax->setEnabled(!m_settings.m_powerColourAutoscale);
 | 
						|
    ui->powerColourPalette->setCurrentIndex(ui->powerColourPalette->findText(m_settings.m_powerColourPalette));
 | 
						|
 | 
						|
    ui->spectrumReverseXAxis->setChecked(m_settings.m_spectrumReverseXAxis);
 | 
						|
    ui->spectrumPeak->setChecked(m_settings.m_spectrumPeaks);
 | 
						|
    ui->spectrumMarker->setChecked(m_settings.m_spectrumMarkers);
 | 
						|
    ui->spectrumTemp->setChecked(m_settings.m_spectrumTemp);
 | 
						|
    if (m_fftGaussianSeries) {
 | 
						|
        m_fftGaussianSeries->setVisible(m_settings.m_spectrumTemp);
 | 
						|
    }
 | 
						|
    ui->spectrumShowRefLine->setChecked(m_settings.m_spectrumRefLine);
 | 
						|
    if (m_fftHlineSeries)
 | 
						|
    {
 | 
						|
        m_fftHlineSeries->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
        m_fftDopplerAxis->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
    }
 | 
						|
    ui->spectrumShowLAB->setChecked(m_settings.m_spectrumLAB);
 | 
						|
    if (m_fftLABSeries) {
 | 
						|
        m_fftLABSeries->setVisible(m_settings.m_spectrumLAB);
 | 
						|
    }
 | 
						|
    ui->spectrumShowDistance->setChecked(m_settings.m_spectrumDistance);
 | 
						|
    updateDistanceColumns();
 | 
						|
    ui->spectrumShowLegend->setChecked(m_settings.m_spectrumLegend);
 | 
						|
    if (m_fftChart) {
 | 
						|
        m_fftChart->legend()->setVisible(m_settings.m_spectrumLegend);
 | 
						|
    }
 | 
						|
    if (m_calChart) {
 | 
						|
        m_calChart->legend()->setVisible(m_settings.m_spectrumLegend);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    ui->refFrame->setCurrentIndex((int)m_settings.m_refFrame);
 | 
						|
    ui->spectrumLine->setCurrentIndex((int)m_settings.m_line);
 | 
						|
    ui->sunDistanceToGC->setValue(m_settings.m_sunDistanceToGC);
 | 
						|
    ui->sunOrbitalVelocity->setValue(m_settings.m_sunOrbitalVelocity);
 | 
						|
    displaySpectrumLineFrequency();
 | 
						|
    updateSpectrumSelect();
 | 
						|
    updatePowerSelect();
 | 
						|
 | 
						|
    // Updates visibility of widgets
 | 
						|
    updateSpectrumMarkerTableVisibility();
 | 
						|
    updatePowerMarkerTableVisibility();
 | 
						|
    updatePowerChartWidgetsVisibility();
 | 
						|
    updateSpectrumChartWidgetsVisibility();
 | 
						|
 | 
						|
    updateIntegrationTime();
 | 
						|
 | 
						|
    ui->runMode->setCurrentIndex((int)m_settings.m_runMode);
 | 
						|
    ui->sweepStartAtTime->setCurrentIndex(m_settings.m_sweepStartAtTime ? 1 : 0);
 | 
						|
    ui->sweepStartDateTime->setDateTime(m_settings.m_sweepStartDateTime);
 | 
						|
    ui->sweepStartDateTime->setVisible(m_settings.m_sweepStartAtTime);
 | 
						|
    ui->sweepType->setCurrentIndex((int)m_settings.m_sweepType);
 | 
						|
    ui->sweep1Start->setValue(m_settings.m_sweep1Start);
 | 
						|
    ui->sweep1Stop->setValue(m_settings.m_sweep1Stop);
 | 
						|
    ui->sweep1Step->setValue(m_settings.m_sweep1Step);
 | 
						|
    ui->sweep1Delay->setValue(m_settings.m_sweep1Delay);
 | 
						|
    ui->sweep2Start->setValue(m_settings.m_sweep2Start);
 | 
						|
    ui->sweep2Stop->setValue(m_settings.m_sweep2Stop);
 | 
						|
    ui->sweep2Step->setValue(m_settings.m_sweep2Step);
 | 
						|
    ui->sweep2Delay->setValue(m_settings.m_sweep2Delay);
 | 
						|
    displayRunModeSettings();
 | 
						|
 | 
						|
    updateIndexLabel();
 | 
						|
 | 
						|
    // Order and size columns
 | 
						|
    QHeaderView *header = ui->powerTable->horizontalHeader();
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_POWERTABLE_COLUMNS; i++)
 | 
						|
    {
 | 
						|
        bool hidden = m_settings.m_powerTableColumnSizes[i] == 0;
 | 
						|
        header->setSectionHidden(i, hidden);
 | 
						|
        powerTableMenu->actions().at(i)->setChecked(!hidden);
 | 
						|
        if (m_settings.m_powerTableColumnSizes[i] > 0)
 | 
						|
            ui->powerTable->setColumnWidth(i, m_settings.m_powerTableColumnSizes[i]);
 | 
						|
        header->moveSection(header->visualIndex(i), m_settings.m_powerTableColumnIndexes[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    getRollupContents()->restoreState(m_rollupState);
 | 
						|
    updateAbsoluteCenterFrequency();
 | 
						|
    blockApplySettings(false);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::leaveEvent(QEvent* event)
 | 
						|
{
 | 
						|
    m_channelMarker.setHighlighted(false);
 | 
						|
    ChannelGUI::leaveEvent(event);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::enterEvent(EnterEventType* event)
 | 
						|
{
 | 
						|
    m_channelMarker.setHighlighted(true);
 | 
						|
    ChannelGUI::enterEvent(event);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::tick()
 | 
						|
{
 | 
						|
    double magsqAvg, magsqPeak;
 | 
						|
    int nbMagsqSamples;
 | 
						|
    m_radioAstronomy->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
 | 
						|
    double powDbAvg = CalcDb::dbPower(magsqAvg);
 | 
						|
 | 
						|
    if (m_tickCount % 4 == 0) {
 | 
						|
        ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
 | 
						|
    }
 | 
						|
 | 
						|
    m_tickCount++;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateRotatorList(const AvailableChannelOrFeatureList& rotators, const QStringList& renameFrom, const QStringList& renameTo)
 | 
						|
{
 | 
						|
    // Update rotator settting if it has been renamed
 | 
						|
    if (renameFrom.contains(m_settings.m_rotator))
 | 
						|
    {
 | 
						|
        m_settings.m_rotator = renameTo[renameFrom.indexOf(m_settings.m_rotator)];
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
 | 
						|
    // Update list of rotators
 | 
						|
    ui->rotator->blockSignals(true);
 | 
						|
    ui->rotator->clear();
 | 
						|
    ui->rotator->addItem("None");
 | 
						|
 | 
						|
    for (const auto& rotator : rotators) {
 | 
						|
        ui->rotator->addItem(rotator.getLongId());
 | 
						|
    }
 | 
						|
 | 
						|
    // Rotator feature can be created after this plugin, so select it
 | 
						|
    // if the chosen rotator appears
 | 
						|
    int rotatorIndex = ui->rotator->findText(m_settings.m_rotator);
 | 
						|
 | 
						|
    if (rotatorIndex >= 0)
 | 
						|
    {
 | 
						|
        ui->rotator->setCurrentIndex(rotatorIndex);
 | 
						|
        setColumnPrecisionFromRotator();
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->rotator->setCurrentIndex(0); // return to None
 | 
						|
    }
 | 
						|
 | 
						|
    ui->rotator->blockSignals(false);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_fftSize_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_fftSize = 1 << (4+index);
 | 
						|
    applySettings();
 | 
						|
    updateIntegrationTime();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_fftWindow_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_fftWindow = (RadioAstronomySettings::FFTWindow)index;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_filterFreqs_editingFinished()
 | 
						|
{
 | 
						|
    m_settings.m_filterFreqs = ui->filterFreqs->text();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_gainVariation_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_gainVariation = value;
 | 
						|
    applySettings();
 | 
						|
    updateTSys0();
 | 
						|
}
 | 
						|
 | 
						|
// Can we allow user to enter text that is automatically looked up on SIMBAD?
 | 
						|
void RadioAstronomyGUI::on_sourceType_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_sourceType = (RadioAstronomySettings::SourceType)index;
 | 
						|
    applySettings();
 | 
						|
    if (m_settings.m_sourceType == RadioAstronomySettings::SUN)
 | 
						|
    {
 | 
						|
        // Mean diameter of Sun in degrees from Earth
 | 
						|
        ui->omegaS->setValue(0.53);
 | 
						|
        ui->omegaSUnits->setCurrentIndex(0);
 | 
						|
    }
 | 
						|
    else if (m_settings.m_sourceType == RadioAstronomySettings::CAS_A)
 | 
						|
    {
 | 
						|
        // Diameter of Cas A in degrees http://simbad.u-strasbg.fr/simbad/sim-id?Ident=Cassiopeia+A
 | 
						|
        ui->omegaS->setValue(0.08333);
 | 
						|
        ui->omegaSUnits->setCurrentIndex(0);
 | 
						|
    }
 | 
						|
    bool visible = index == 1 || index >= 3;
 | 
						|
    ui->omegaS->setVisible(visible);
 | 
						|
    ui->omegaSUnits->setVisible(visible);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_omegaS_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_omegaS = value;
 | 
						|
    if ((m_settings.m_sourceType == RadioAstronomySettings::SUN) && (value != 0.53)) {
 | 
						|
        ui->sourceType->setCurrentIndex((int)RadioAstronomySettings::COMPACT);
 | 
						|
    } else if ((m_settings.m_sourceType == RadioAstronomySettings::CAS_A) && (value != 0.08333)) {
 | 
						|
        ui->sourceType->setCurrentIndex((int)RadioAstronomySettings::COMPACT);
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateOmegaA()
 | 
						|
{
 | 
						|
    if (m_settings.m_omegaAUnits == RadioAstronomySettings::DEGREES) {
 | 
						|
        ui->omegaA->setText(QString("%1").arg(m_beamWidth, 0, 'f', 1));
 | 
						|
    } else {
 | 
						|
        ui->omegaA->setText(QString("%1").arg(hpbwToSteradians(m_beamWidth), 0, 'f', 4));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_omegaAUnits_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_omegaAUnits = (RadioAstronomySettings::AngleUnits)index;
 | 
						|
    updateOmegaA();
 | 
						|
    if (m_settings.m_omegaAUnits == RadioAstronomySettings::DEGREES) {
 | 
						|
        ui->omegaALabel->setText("HPBW");
 | 
						|
    } else {
 | 
						|
        ui->omegaALabel->setText(QString("%1<sub>A</sub>").arg(QChar(937)));
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_omegaSUnits_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_omegaSUnits = (RadioAstronomySettings::AngleUnits)index;
 | 
						|
    if (  (   (m_settings.m_sourceType == RadioAstronomySettings::SUN)
 | 
						|
           || (m_settings.m_sourceType == RadioAstronomySettings::CAS_A)
 | 
						|
          )
 | 
						|
        && (m_settings.m_omegaSUnits != RadioAstronomySettings::DEGREES)
 | 
						|
       )
 | 
						|
    {
 | 
						|
        ui->sourceType->setCurrentIndex((int)RadioAstronomySettings::COMPACT);
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_starTracker_currentTextChanged(const QString& text)
 | 
						|
{
 | 
						|
    m_settings.m_starTracker = text;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_rotator_currentTextChanged(const QString& text)
 | 
						|
{
 | 
						|
    m_settings.m_rotator = text;
 | 
						|
    applySettings();
 | 
						|
    setColumnPrecisionFromRotator();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::setColumnPrecisionFromRotator()
 | 
						|
{
 | 
						|
    // Match rotator precision
 | 
						|
    unsigned int featureSetIndex, featureIndex;
 | 
						|
 | 
						|
    if (MainCore::getFeatureIndexFromId(m_settings.m_rotator, featureSetIndex, featureIndex))
 | 
						|
    {
 | 
						|
        int precision = 0;
 | 
						|
        if (ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "precision", precision))
 | 
						|
        {
 | 
						|
            int old = ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_GAL_LAT))->getPrecision();
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_GAL_LAT))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_GAL_LON))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_AZ))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_EL))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_ROT_AZ))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_ROT_EL))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_ROT_AZ_OFF))->setPrecision(precision);
 | 
						|
            ((DecimalDelegate *)ui->powerTable->itemDelegateForColumn(POWER_COL_ROT_EL_OFF))->setPrecision(precision);
 | 
						|
            if (precision > old)
 | 
						|
            {
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_GAL_LAT);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_GAL_LON);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_AZ);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_EL);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_ROT_AZ);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_ROT_EL);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_ROT_AZ_OFF);
 | 
						|
                ui->powerTable->resizeColumnToContents(POWER_COL_ROT_EL_OFF);
 | 
						|
            }
 | 
						|
            ui->powerTable->viewport()->update();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_showSensors_clicked()
 | 
						|
{
 | 
						|
    RadioAstronomySensorDialog dialog(&m_settings);
 | 
						|
    if (dialog.exec() == QDialog::Accepted)
 | 
						|
    {
 | 
						|
        m_sensors[0].setName(m_settings.m_sensorName[0]);
 | 
						|
        m_sensors[1].setName(m_settings.m_sensorName[1]);
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::sensorMeasurementReceived(const RadioAstronomy::MsgSensorMeasurement& measurement)
 | 
						|
{
 | 
						|
    int sensor = measurement.getSensor();
 | 
						|
    double value = measurement.getValue();
 | 
						|
    QDateTime dateTime = measurement.getDateTime();
 | 
						|
    SensorMeasurement* sm = new SensorMeasurement(dateTime, value);
 | 
						|
    m_sensors[sensor].append(sm);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerChartSelect_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_powerYData = (RadioAstronomySettings::PowerYData)index;
 | 
						|
    ui->powerYUnits->clear();
 | 
						|
    switch (m_settings.m_powerYData)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::PY_POWER:
 | 
						|
        ui->powerYUnits->addItem("dBFS");
 | 
						|
        ui->powerYUnits->addItem("dBm");
 | 
						|
        ui->powerYUnits->addItem("Watts");
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_TSYS:
 | 
						|
    case RadioAstronomySettings::PY_TSOURCE:
 | 
						|
        ui->powerYUnits->addItem("K");
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_FLUX:
 | 
						|
        ui->powerYUnits->addItem("SFU");
 | 
						|
        ui->powerYUnits->addItem("Jy");
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_2D_MAP:
 | 
						|
        ui->powerYUnits->addItem("dBFS");
 | 
						|
        ui->powerYUnits->addItem("dBm");
 | 
						|
        //ui->powerYUnits->addItem("Watts"); // No watts for now, as range spin boxes can't handle scientific notation
 | 
						|
        ui->powerYUnits->addItem("K");
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    updatePowerMarkerTableVisibility();
 | 
						|
    updatePowerChartWidgetsVisibility();
 | 
						|
    plotPowerChart();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updatePowerChartWidgetsVisibility()
 | 
						|
{
 | 
						|
    bool powerChart;
 | 
						|
    if (m_settings.m_powerYData != RadioAstronomySettings::PY_2D_MAP) {
 | 
						|
        powerChart = true;
 | 
						|
    } else {
 | 
						|
        powerChart = false;
 | 
						|
    }
 | 
						|
    ui->powerShowLegend->setVisible(powerChart);
 | 
						|
    ui->powerShowSensor2->setVisible(powerChart);
 | 
						|
    ui->powerShowSensor1->setVisible(powerChart);
 | 
						|
    ui->powerShowAirTemp->setVisible(powerChart);
 | 
						|
    ui->powerShowTsys0->setVisible(powerChart);
 | 
						|
    ui->powerShowAvg->setVisible(powerChart);
 | 
						|
    ui->powerShowGaussian->setVisible(powerChart);
 | 
						|
    ui->powerShowMarker->setVisible(powerChart);
 | 
						|
    ui->powerShowPeak->setVisible(powerChart);
 | 
						|
    ui->powerScaleWidgets->setVisible(powerChart);
 | 
						|
    ui->powerGaussianWidgets->setVisible(powerChart && m_settings.m_powerShowGaussian);
 | 
						|
    ui->powerMarkerTableWidgets->setVisible(powerChart && (m_settings.m_powerPeaks || m_settings.m_powerMarkers));
 | 
						|
    ui->power2DScaleWidgets->setVisible(!powerChart);
 | 
						|
    ui->power2DColourScaleWidgets->setVisible(!powerChart);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
int RadioAstronomyGUI::powerYUnitsToIndex(RadioAstronomySettings::PowerYUnits units)
 | 
						|
{
 | 
						|
    switch (units)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::PY_DBFS:
 | 
						|
        return 0;
 | 
						|
    case RadioAstronomySettings::PY_DBM:
 | 
						|
        return 1;
 | 
						|
    case RadioAstronomySettings::PY_WATTS:
 | 
						|
        return 2;
 | 
						|
    case RadioAstronomySettings::PY_KELVIN:
 | 
						|
        return 0;
 | 
						|
    case RadioAstronomySettings::PY_SFU:
 | 
						|
        return 0;
 | 
						|
    case RadioAstronomySettings::PY_JANSKY:
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerYUnits_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    (void) index;
 | 
						|
 | 
						|
    QString text = ui->powerYUnits->currentText();
 | 
						|
    if (text == "dBFS")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_DBFS;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Power (dBFS)");
 | 
						|
        ui->powerColourScaleMin->setDecimals(2);
 | 
						|
        ui->powerColourScaleMax->setDecimals(2);
 | 
						|
    }
 | 
						|
    else if (text == "dBm")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_DBM;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Power (dBm)");
 | 
						|
        ui->powerColourScaleMin->setDecimals(2);
 | 
						|
        ui->powerColourScaleMax->setDecimals(2);
 | 
						|
    }
 | 
						|
    else if (text == "Watts")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_WATTS;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Power (W)");
 | 
						|
    }
 | 
						|
    else if (text == "K")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_KELVIN;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Temp (K)");
 | 
						|
        ui->powerColourScaleMin->setDecimals(0);
 | 
						|
        ui->powerColourScaleMax->setDecimals(0);
 | 
						|
    }
 | 
						|
    else if (text == "SFU")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_SFU;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Flux (SFU)");
 | 
						|
    }
 | 
						|
    else if (text == "Jy")
 | 
						|
    {
 | 
						|
        m_settings.m_powerYUnits = RadioAstronomySettings::PY_JANSKY;
 | 
						|
        ui->powerMarkerTable->horizontalHeaderItem(POWER_MARKER_COL_VALUE)->setText("Flux (Jy)");
 | 
						|
    }
 | 
						|
    if (text == "dBFS")
 | 
						|
    {
 | 
						|
        ui->powerColourScaleMinUnits->setText("dB");
 | 
						|
        ui->powerColourScaleMaxUnits->setText("dB");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->powerColourScaleMinUnits->setText(text);
 | 
						|
        ui->powerColourScaleMaxUnits->setText(text);
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
    plotPowerChart();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumChartSelect_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    updateSpectrumMarkerTableVisibility();  // If this follows updateSpectrumChartWidgetsVisibility, widgets are not redrawn properly.
 | 
						|
    updateSpectrumChartWidgetsVisibility();  // when switching from cal to spectrum if table is visible
 | 
						|
    if (index == 0)
 | 
						|
    {
 | 
						|
        if (m_fftChart) {
 | 
						|
            ui->spectrumChart->setChart(m_fftChart);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (m_calChart) {
 | 
						|
            ui->spectrumChart->setChart(m_calChart);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateSpectrumChartWidgetsVisibility()
 | 
						|
{
 | 
						|
    bool fft = ui->spectrumChartSelect->currentIndex() == 0;
 | 
						|
    ui->spectrumYUnits->setVisible(fft);
 | 
						|
    ui->spectrumScaleWidgets->setVisible(fft);
 | 
						|
    ui->spectrumSelectWidgets->setVisible(fft);
 | 
						|
    ui->spectrumRefLineWidgets->setVisible(fft && m_settings.m_spectrumRefLine);
 | 
						|
 | 
						|
    ui->spectrumGaussianWidgets->setVisible(fft && m_settings.m_spectrumTemp);
 | 
						|
    ui->calWidgets->setVisible(!fft);
 | 
						|
    ui->recalibrate->setVisible(!fft);
 | 
						|
    ui->startCalHot->setVisible(!fft);
 | 
						|
    ui->startCalCold->setVisible(!fft);
 | 
						|
    ui->clearCal->setVisible(!fft);
 | 
						|
    ui->showCalSettings->setVisible(!fft);
 | 
						|
    ui->spectrumShowRefLine->setVisible(fft);
 | 
						|
    ui->spectrumShowDistance->setVisible(fft);
 | 
						|
    ui->spectrumShowLAB->setVisible(fft);
 | 
						|
    ui->spectrumTemp->setVisible(fft);
 | 
						|
    ui->spectrumMarker->setVisible(fft);
 | 
						|
    ui->spectrumPeak->setVisible(fft);
 | 
						|
    ui->saveSpectrumChartImages->setVisible(fft);
 | 
						|
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
// Calulate mean, RMS and standard deviation
 | 
						|
// Currently this is for all data - but could make it only for visible data
 | 
						|
void RadioAstronomyGUI::calcAverages()
 | 
						|
{
 | 
						|
    qreal sum = 0.0;
 | 
						|
    qreal sumSq = 0.0;
 | 
						|
    QVector<QPointF> points = m_powerSeries->pointsVector();
 | 
						|
    for (int i = 0; i < points.size(); i++)
 | 
						|
    {
 | 
						|
        QPointF point = points.at(i);
 | 
						|
        qreal y = point.y();
 | 
						|
        sum += y;
 | 
						|
        sumSq += y * y;
 | 
						|
    }
 | 
						|
    qreal mean = sum / points.size();
 | 
						|
    qreal rms = std::sqrt(sumSq / points.size());
 | 
						|
    qreal sumSqDiff = 0.0;
 | 
						|
    for (int i = 0; i < points.size(); i++)
 | 
						|
    {
 | 
						|
        QPointF point = points.at(i);
 | 
						|
        qreal y = point.y();
 | 
						|
        qreal diff = y - mean;
 | 
						|
        sumSqDiff += diff * diff;
 | 
						|
    }
 | 
						|
    qreal sigma = std::sqrt(sumSqDiff / points.size());
 | 
						|
 | 
						|
    ui->powerMean->setText(QString::number(mean));
 | 
						|
    ui->powerRMS->setText(QString::number(rms));
 | 
						|
    ui->powerSD->setText(QString::number(sigma));
 | 
						|
}
 | 
						|
 | 
						|
QRgb RadioAstronomyGUI::intensityToColor(float intensity)
 | 
						|
{
 | 
						|
    QRgb c1, c2;
 | 
						|
    float scale;
 | 
						|
 | 
						|
    if (std::isnan(intensity)) {
 | 
						|
        return qRgb(0, 0, 0);
 | 
						|
    }
 | 
						|
    // Get in range 0-1
 | 
						|
    intensity = (intensity - m_settings.m_powerColourScaleMin) / (m_settings.m_powerColourScaleMax - m_settings.m_powerColourScaleMin);
 | 
						|
    intensity = std::min(intensity, 1.0f);
 | 
						|
    intensity = std::max(intensity, 0.0f);
 | 
						|
 | 
						|
    if (m_settings.m_powerColourPalette[0] == 'C')
 | 
						|
    {
 | 
						|
        // Colour heat map gradient
 | 
						|
        if (intensity <= 0.25f)
 | 
						|
        {
 | 
						|
            c1 = qRgb(0, 0, 0x80); // Navy
 | 
						|
            c2 = qRgb(0, 0, 0xff); // Blue
 | 
						|
            scale = intensity * 4.0f;
 | 
						|
        }
 | 
						|
        else if (intensity <= 0.5f)
 | 
						|
        {
 | 
						|
            c1 = qRgb(0, 0, 0xff); // Blue
 | 
						|
            c2 = qRgb(0, 0xff, 0); // Green
 | 
						|
            scale = (intensity - 0.25f) * 4.0f;
 | 
						|
        }
 | 
						|
        else if (intensity <= 0.75f)
 | 
						|
        {
 | 
						|
            c1 = qRgb(0, 0xff, 0); // Green
 | 
						|
            c2 = qRgb(0xff, 0xff, 0); // Yellow
 | 
						|
            scale = (intensity - 0.5f) * 4.0f;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            c1 = qRgb(0xff, 0xff, 0); // Yellow
 | 
						|
            c2 = qRgb(0xff, 0, 0); // Red
 | 
						|
            scale = (intensity - 0.75f) * 4.0f;
 | 
						|
        }
 | 
						|
 | 
						|
        int r, g, b;
 | 
						|
        r = (qRed(c2)-qRed(c1))*scale+qRed(c1);
 | 
						|
        g = (qGreen(c2)-qGreen(c1))*scale+qGreen(c1);
 | 
						|
        b = (qBlue(c2)-qBlue(c1))*scale+qBlue(c1);
 | 
						|
 | 
						|
        return qRgb(r, g, b);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Greyscale
 | 
						|
        int g = 255 * intensity;
 | 
						|
        return qRgb(g, g, g);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plot2DChart()
 | 
						|
{
 | 
						|
    // Only plot if visible
 | 
						|
    if (ui->powerChartSelect->currentIndex() == 4)
 | 
						|
    {
 | 
						|
        QChart *oldChart = m_2DChart;
 | 
						|
 | 
						|
        m_2DChart = new QChart();
 | 
						|
 | 
						|
        m_2DChart->layout()->setContentsMargins(0, 0, 0, 0);
 | 
						|
        m_2DChart->setMargins(QMargins(1, 1, 1, 1));
 | 
						|
        m_2DChart->setTheme(QChart::ChartThemeDark);
 | 
						|
        m_2DChart->setTitle("");
 | 
						|
 | 
						|
        m_2DXAxis = new QValueAxis();
 | 
						|
        m_2DYAxis = new QValueAxis();
 | 
						|
 | 
						|
        m_2DXAxis->setGridLineVisible(false);
 | 
						|
        m_2DYAxis->setGridLineVisible(false);
 | 
						|
        set2DAxisTitles();
 | 
						|
        m_2DXAxis->setRange(m_settings.m_power2DXMin, m_settings.m_power2DXMax);
 | 
						|
        m_2DYAxis->setRange(m_settings.m_power2DYMin, m_settings.m_power2DYMax);
 | 
						|
 | 
						|
        m_2DChart->addAxis(m_2DXAxis, Qt::AlignBottom);
 | 
						|
        m_2DChart->addAxis(m_2DYAxis, Qt::AlignLeft);
 | 
						|
 | 
						|
        m_2DMap.fill(qRgb(0, 0, 0));
 | 
						|
        for (int i = 0; i < m_fftMeasurements.size(); i++) {
 | 
						|
            update2DImage(m_fftMeasurements[i], i < m_fftMeasurements.size() - 1);
 | 
						|
        }
 | 
						|
        if (m_settings.m_powerColourAutoscale) {
 | 
						|
            powerColourAutoscale();
 | 
						|
        }
 | 
						|
 | 
						|
        connect(m_2DChart, SIGNAL(plotAreaChanged(QRectF)), this, SLOT(plotAreaChanged(QRectF)));
 | 
						|
 | 
						|
        ui->powerChart->setChart(m_2DChart);
 | 
						|
 | 
						|
        delete oldChart;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::set2DAxisTitles()
 | 
						|
{
 | 
						|
    if (m_settings.m_power2DSweepType == RadioAstronomySettings::SWP_LB)
 | 
						|
    {
 | 
						|
        m_2DXAxis->setTitleText(QString("Galactic longitude (%1)").arg(QChar(0xb0)));
 | 
						|
        m_2DYAxis->setTitleText(QString("Galactic latitude (%1)").arg(QChar(0xb0)));
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_2DXAxis->setTitleText(QString("Azimuth (%1)").arg(QChar(0xb0)));
 | 
						|
        m_2DYAxis->setTitleText(QString("Elevation (%1)").arg(QChar(0xb0)));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::update2DSettingsFromSweep()
 | 
						|
{
 | 
						|
    if (m_settings.m_runMode == RadioAstronomySettings::SWEEP)
 | 
						|
    {
 | 
						|
        ui->power2DSweepType->setCurrentIndex((int)m_settings.m_sweepType);
 | 
						|
 | 
						|
        // Calculate width and height of image - 1 pixel per sweep measurement
 | 
						|
        float sweep1Start, sweep1Stop;
 | 
						|
        sweep1Start = m_settings.m_sweep1Start;
 | 
						|
        sweep1Stop = m_settings.m_sweep1Stop;
 | 
						|
        // Handle azimuth/l sweep through 0. E.g. 340deg -> 20deg with +vs step, or 20deg -> 340deg with -ve step
 | 
						|
        if ((m_settings.m_sweep1Stop < m_settings.m_sweep1Start) && (m_settings.m_sweep1Step > 0)) {
 | 
						|
            sweep1Stop = m_settings.m_sweep1Stop + 360.0;
 | 
						|
        } else if ((m_settings.m_sweep1Stop > m_settings.m_sweep1Start) && (m_settings.m_sweep1Step < 0)) {
 | 
						|
            sweep1Start += 360.0;
 | 
						|
        }
 | 
						|
        int width = abs((sweep1Stop - sweep1Start) / m_settings.m_sweep1Step) + 1;
 | 
						|
        int height = abs((m_settings.m_sweep2Stop - m_settings.m_sweep2Start) / m_settings.m_sweep2Step) + 1;
 | 
						|
        ui->power2DWidth->setValue(width);
 | 
						|
        ui->power2DHeight->setValue(height);
 | 
						|
 | 
						|
        // Subtract/add half a step so that pixels are centred on coordinates
 | 
						|
        int start1 = m_settings.m_sweep1Start - m_settings.m_sweep1Step / 2;
 | 
						|
        int stop1 = m_settings.m_sweep1Stop + m_settings.m_sweep1Step / 2;
 | 
						|
        if (start1 < stop1)
 | 
						|
        {
 | 
						|
            ui->power2DXMin->setValue(start1);
 | 
						|
            ui->power2DXMax->setValue(stop1);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ui->power2DXMin->setValue(stop1);
 | 
						|
            ui->power2DXMax->setValue(start1);
 | 
						|
        }
 | 
						|
 | 
						|
        int start2 = m_settings.m_sweep2Start - m_settings.m_sweep2Step / 2;
 | 
						|
        int stop2 = m_settings.m_sweep2Stop + m_settings.m_sweep2Step / 2;
 | 
						|
        if (start2 < stop2)
 | 
						|
        {
 | 
						|
            ui->power2DYMin->setValue(start2);
 | 
						|
            ui->power2DYMax->setValue(stop2);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            ui->power2DYMin->setValue(stop2);
 | 
						|
            ui->power2DYMax->setValue(start2);
 | 
						|
        }
 | 
						|
 | 
						|
        m_sweepIndex = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::create2DImage()
 | 
						|
{
 | 
						|
    // Intensity array holds power/temperature values which are then colourised in to a QImage
 | 
						|
    delete m_2DMapIntensity;
 | 
						|
    int size = m_settings.m_power2DWidth * m_settings.m_power2DHeight;
 | 
						|
    if (size > 0)
 | 
						|
    {
 | 
						|
        m_2DMapIntensity = new float[size];
 | 
						|
        for (int i = 0; i < size; i++) {
 | 
						|
            m_2DMapIntensity[i] = NAN;
 | 
						|
        }
 | 
						|
        m_2DMapMin = std::numeric_limits<float>::max();
 | 
						|
        m_2DMapMax = -std::numeric_limits<float>::max();
 | 
						|
        QImage map(m_settings.m_power2DWidth, m_settings.m_power2DHeight, QImage::Format_ARGB32);
 | 
						|
        map.fill(qRgb(0, 0, 0));
 | 
						|
        m_2DMap = map;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_2DMapIntensity = nullptr;
 | 
						|
        m_2DMap = QImage();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::update2DImage(FFTMeasurement* fft, bool skipCalcs)
 | 
						|
{
 | 
						|
    if (m_2DMap.width() > 0)
 | 
						|
    {
 | 
						|
        int x, y;
 | 
						|
        if (m_settings.m_power2DSweepType == RadioAstronomySettings::SWP_OFFSET)
 | 
						|
        {
 | 
						|
            y = fft->m_sweepIndex / m_2DMap.width();
 | 
						|
            x = fft->m_sweepIndex % m_2DMap.width();
 | 
						|
 | 
						|
            if (m_settings.m_sweep2Step >= 0) {
 | 
						|
                y = m_2DMap.height() - 1 - y;
 | 
						|
            }
 | 
						|
            if (m_settings.m_sweep1Step < 0) {
 | 
						|
                x = m_2DMap.width() - 1 - x;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if (m_settings.m_power2DSweepType == RadioAstronomySettings::SWP_LB)
 | 
						|
            {
 | 
						|
                x = (int)round(fft->m_l);
 | 
						|
                y = (int)round(fft->m_b);
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                x = (int)round(fft->m_azimuth);
 | 
						|
                y = (int)round(fft->m_elevation);
 | 
						|
            }
 | 
						|
 | 
						|
            // Map coordinates to pixels
 | 
						|
            float xRange = m_settings.m_power2DXMax - m_settings.m_power2DXMin;
 | 
						|
            float yRange = m_settings.m_power2DYMax - m_settings.m_power2DYMin;
 | 
						|
 | 
						|
            x = (x - m_settings.m_power2DXMin) * m_settings.m_power2DWidth / xRange;
 | 
						|
            y = (y - m_settings.m_power2DYMin) * m_settings.m_power2DHeight / yRange;
 | 
						|
 | 
						|
            if (yRange >= 0) {
 | 
						|
                y = m_2DMap.height() - 1 - y;
 | 
						|
            }
 | 
						|
            if (xRange < 0) {
 | 
						|
                x = m_2DMap.width() - 1 - x;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if ((x >= 0) && (x < m_2DMap.width()) && (y >= 0) && (y < m_2DMap.height()))
 | 
						|
        {
 | 
						|
            float intensity;
 | 
						|
            switch (m_settings.m_powerYUnits)
 | 
						|
            {
 | 
						|
            case RadioAstronomySettings::PY_DBFS:
 | 
						|
                intensity = fft->m_totalPowerdBFS;
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_DBM:
 | 
						|
                intensity = fft->m_totalPowerdBm;
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_WATTS:
 | 
						|
                intensity = fft->m_totalPowerWatts;
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::PY_KELVIN:
 | 
						|
                intensity = fft->m_tSys;
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            float newMin = std::min(m_2DMapMin, intensity);
 | 
						|
            float newMax = std::max(m_2DMapMax, intensity);
 | 
						|
            bool rescale = false;
 | 
						|
            if ((newMin != m_2DMapMin) || (newMax != m_2DMapMax)) {
 | 
						|
                rescale = m_settings.m_powerColourAutoscale;
 | 
						|
            }
 | 
						|
            m_2DMapMin = newMin;
 | 
						|
            m_2DMapMax = newMax;
 | 
						|
            m_2DMapIntensity[y*m_2DMap.width()+x] = intensity;
 | 
						|
            m_2DMap.setPixel(x, y, intensityToColor(intensity));
 | 
						|
            if (rescale && !skipCalcs) {
 | 
						|
                powerColourAutoscale();
 | 
						|
            }
 | 
						|
            if (m_2DChart && !skipCalcs) {
 | 
						|
                plotAreaChanged(m_2DChart->plotArea());
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::recolour2DImage()
 | 
						|
{
 | 
						|
    for (int y = 0; y < m_2DMap.height(); y++)
 | 
						|
    {
 | 
						|
        for (int x = 0; x < m_2DMap.width(); x++) {
 | 
						|
            m_2DMap.setPixel(x, y, intensityToColor(m_2DMapIntensity[y*m_2DMap.width()+x]));
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (m_2DChart) {
 | 
						|
        plotAreaChanged(m_2DChart->plotArea());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotAreaChanged(const QRectF& plotArea)
 | 
						|
{
 | 
						|
    if (ui->powerChartSelect->currentIndex() == 4)
 | 
						|
    {
 | 
						|
        int width = static_cast<int>(plotArea.width());
 | 
						|
        int height = static_cast<int>(plotArea.height());
 | 
						|
        int viewW = static_cast<int>(ui->powerChart->width());
 | 
						|
        int viewH = static_cast<int>(ui->powerChart->height());
 | 
						|
 | 
						|
        // Scale the image to fit plot area
 | 
						|
        QImage image = m_2DMap.scaled(QSize(width, height), Qt::IgnoreAspectRatio);
 | 
						|
        QImage translated(viewW, viewH, QImage::Format_ARGB32);
 | 
						|
        translated.fill(Qt::black);
 | 
						|
        QPainter painter(&translated);
 | 
						|
        painter.drawImage(plotArea.topLeft(), image);
 | 
						|
 | 
						|
        m_2DChart->setPlotAreaBackgroundBrush(translated);
 | 
						|
        m_2DChart->setPlotAreaBackgroundVisible(true);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Find min and max coordinates from existing data
 | 
						|
void RadioAstronomyGUI::power2DAutoscale()
 | 
						|
{
 | 
						|
    if (m_fftMeasurements.size() > 0)
 | 
						|
    {
 | 
						|
        float minX = std::numeric_limits<float>::max();
 | 
						|
        float maxX = -std::numeric_limits<float>::max();
 | 
						|
        float minY = std::numeric_limits<float>::max();
 | 
						|
        float maxY = -std::numeric_limits<float>::max();
 | 
						|
 | 
						|
        for (int i = 0; i < m_fftMeasurements.size(); i++)
 | 
						|
        {
 | 
						|
            FFTMeasurement* fft = m_fftMeasurements[i];
 | 
						|
            float x, y;
 | 
						|
            if (m_settings.m_power2DSweepType == RadioAstronomySettings::SWP_LB)
 | 
						|
            {
 | 
						|
                x = fft->m_l;
 | 
						|
                y = fft->m_b;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                x = fft->m_azimuth;
 | 
						|
                y = fft->m_elevation;
 | 
						|
            }
 | 
						|
            minX = std::min(minX, x);
 | 
						|
            maxX = std::max(maxX, x);
 | 
						|
            minY = std::min(minY, y);
 | 
						|
            maxY = std::max(maxY, y);
 | 
						|
        }
 | 
						|
 | 
						|
        // Adjust so pixels are centered
 | 
						|
        float xAdjust = (maxX - minX) / m_2DMap.width() / 2;
 | 
						|
        float yAdjust = (maxY - minY) / m_2DMap.height() / 2;
 | 
						|
 | 
						|
        ui->power2DXMin->setValue(minX - xAdjust);
 | 
						|
        ui->power2DXMax->setValue(maxX + xAdjust);
 | 
						|
        ui->power2DYMin->setValue(minY - yAdjust);
 | 
						|
        ui->power2DYMax->setValue(maxY + xAdjust);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DAutoscale_clicked()
 | 
						|
{
 | 
						|
    power2DAutoscale();
 | 
						|
    plot2DChart();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DLinkSweep_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_power2DLinkSweep = checked;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DSweepType_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_power2DSweepType = (RadioAstronomySettings::SweepType)index;
 | 
						|
    applySettings();
 | 
						|
    plot2DChart();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DWidth_valueChanged(int value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DWidth = value;
 | 
						|
    applySettings();
 | 
						|
    create2DImage();
 | 
						|
    plot2DChart();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DHeight_valueChanged(int value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DHeight = value;
 | 
						|
    applySettings();
 | 
						|
    create2DImage();
 | 
						|
    plot2DChart();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DXMin_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DXMin = value;
 | 
						|
    applySettings();
 | 
						|
    if (m_2DXAxis)
 | 
						|
    {
 | 
						|
        m_2DXAxis->setMin(m_settings.m_power2DXMin);
 | 
						|
        plot2DChart();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DXMax_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DXMax = value;
 | 
						|
    applySettings();
 | 
						|
    if (m_2DXAxis)
 | 
						|
    {
 | 
						|
        m_2DXAxis->setMax(m_settings.m_power2DXMax);
 | 
						|
        plot2DChart();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DYMin_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DYMin = value;
 | 
						|
    applySettings();
 | 
						|
    if (m_2DYAxis)
 | 
						|
    {
 | 
						|
        m_2DYAxis->setMin(m_settings.m_power2DYMin);
 | 
						|
        plot2DChart();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_power2DYMax_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_power2DYMax = value;
 | 
						|
    applySettings();
 | 
						|
    if (m_2DYAxis)
 | 
						|
    {
 | 
						|
        m_2DYAxis->setMax(m_settings.m_power2DYMax);
 | 
						|
        plot2DChart();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::powerColourAutoscale()
 | 
						|
{
 | 
						|
    int width = m_2DMap.width();
 | 
						|
    int height = m_2DMap.height();
 | 
						|
    float newMin = std::numeric_limits<float>::max();
 | 
						|
    float newMax = -std::numeric_limits<float>::max();
 | 
						|
    for (int i = 0; i < width * height; i++)
 | 
						|
    {
 | 
						|
        if (!std::isnan(m_2DMapIntensity[i]))
 | 
						|
        {
 | 
						|
            newMin = std::min(newMin, m_2DMapIntensity[i]);
 | 
						|
            newMax = std::max(newMax, m_2DMapIntensity[i]);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if ((newMin != ui->powerColourScaleMin->value()) || (newMax != ui->powerColourScaleMax->value()))
 | 
						|
    {
 | 
						|
        ui->powerColourScaleMin->setValue(std::floor(newMin * 10.0) / 10.0);
 | 
						|
        ui->powerColourScaleMax->setValue(std::ceil(newMax * 10.0) / 10.0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerColourAutoscale_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerColourAutoscale = checked;
 | 
						|
    applySettings();
 | 
						|
    if (m_settings.m_powerColourAutoscale) {
 | 
						|
        powerColourAutoscale();
 | 
						|
    }
 | 
						|
    ui->powerColourScaleMin->setEnabled(!m_settings.m_powerColourAutoscale);
 | 
						|
    ui->powerColourScaleMax->setEnabled(!m_settings.m_powerColourAutoscale);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updatePowerColourScaleStep()
 | 
						|
{
 | 
						|
    float diff = abs(m_settings.m_powerColourScaleMax - m_settings.m_powerColourScaleMin);
 | 
						|
    double step = (diff <= 1.0f) ? 0.1 : 1.0f;
 | 
						|
    ui->powerColourScaleMin->setSingleStep(step);
 | 
						|
    ui->powerColourScaleMax->setSingleStep(step);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerColourScaleMin_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_powerColourScaleMin = value;
 | 
						|
    updatePowerColourScaleStep();
 | 
						|
    applySettings();
 | 
						|
    recolour2DImage();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerColourScaleMax_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_powerColourScaleMax = value;
 | 
						|
    updatePowerColourScaleStep();
 | 
						|
    applySettings();
 | 
						|
    recolour2DImage();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerColourPalette_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    (void) index;
 | 
						|
    m_settings.m_powerColourPalette = ui->powerColourPalette->currentText();
 | 
						|
    applySettings();
 | 
						|
    recolour2DImage();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotPowerChart()
 | 
						|
{
 | 
						|
    if (ui->powerChartSelect->currentIndex() == 4) {
 | 
						|
        plot2DChart();
 | 
						|
    } else {
 | 
						|
        plotPowerVsTimeChart();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotPowerVsTimeChart()
 | 
						|
{
 | 
						|
    QChart *oldChart = m_powerChart;
 | 
						|
 | 
						|
    m_powerChart = new QChart();
 | 
						|
 | 
						|
    m_powerChart->layout()->setContentsMargins(0, 0, 0, 0);
 | 
						|
    m_powerChart->setMargins(QMargins(1, 1, 1, 1));
 | 
						|
    m_powerChart->setTheme(QChart::ChartThemeDark);
 | 
						|
 | 
						|
    m_powerChart->legend()->setAlignment(Qt::AlignRight);
 | 
						|
    m_powerChart->legend()->setVisible(m_settings.m_powerLegend);
 | 
						|
 | 
						|
    // Create measurement data series
 | 
						|
    m_powerSeries = new QLineSeries();
 | 
						|
    m_powerSeries->setVisible(m_settings.m_powerShowMeasurement);
 | 
						|
    connect(m_powerSeries, &QXYSeries::clicked, this, &RadioAstronomyGUI::powerSeries_clicked);
 | 
						|
 | 
						|
    // Plot peak info
 | 
						|
    m_powerPeakSeries = new QScatterSeries();
 | 
						|
    m_powerPeakSeries->setName("Peak");
 | 
						|
    m_powerPeakSeries->setPointLabelsVisible(true);
 | 
						|
    m_powerPeakSeries->setPointLabelsFormat("@yPoint"); // Qt can't display dates, so omit @xPoint
 | 
						|
    m_powerPeakSeries->setMarkerSize(5);
 | 
						|
    m_powerPeakSeries->setVisible(m_settings.m_powerPeaks);
 | 
						|
 | 
						|
    // Markers
 | 
						|
    m_powerMarkerSeries = new QScatterSeries();
 | 
						|
    m_powerMarkerSeries->setName("Marker");
 | 
						|
    m_powerMarkerSeries->setPointLabelsVisible(true);
 | 
						|
    m_powerMarkerSeries->setPointLabelsFormat("@yPoint");
 | 
						|
    m_powerMarkerSeries->setMarkerSize(5);
 | 
						|
    m_powerMarkerSeries->setVisible(m_settings.m_powerMarkers);
 | 
						|
 | 
						|
    // Noise
 | 
						|
    m_powerTsys0Series = new QLineSeries();
 | 
						|
    m_powerTsys0Series->setName("Tsys0");
 | 
						|
    m_powerTsys0Series->setVisible(m_settings.m_powerShowTsys0);
 | 
						|
 | 
						|
    // Air temperature
 | 
						|
    m_airTemps.init("Air temp", m_settings.m_powerShowAirTemp);
 | 
						|
 | 
						|
    // Gaussian
 | 
						|
    m_powerGaussianSeries = new QLineSeries();
 | 
						|
    m_powerGaussianSeries->setName("Gaussian fit");
 | 
						|
    m_powerGaussianSeries->setVisible(m_settings.m_powerShowGaussian);
 | 
						|
 | 
						|
    // Filtered measurement
 | 
						|
    m_powerFilteredSeries = new QLineSeries();
 | 
						|
    m_powerFilteredSeries->setName("Filtered");
 | 
						|
    m_powerFilteredSeries->setVisible(m_settings.m_powerShowFiltered);
 | 
						|
    plotPowerFiltered();
 | 
						|
 | 
						|
    // Sensors
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        m_sensors[i].init(m_settings.m_sensorName[i], m_settings.m_sensorVisible[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    // Reset min/max and peaks
 | 
						|
    m_powerMin = std::numeric_limits<double>::max();
 | 
						|
    m_powerMax = -std::numeric_limits<double>::max();
 | 
						|
    m_powerPeakValid = false;
 | 
						|
 | 
						|
    // Create X axis
 | 
						|
    m_powerXAxis = new QDateTimeAxis();
 | 
						|
    int rows = ui->powerTable->rowCount();
 | 
						|
    QString dateTimeFormat = "hh:mm:ss";
 | 
						|
    m_powerXAxisSameDay = true;
 | 
						|
    if (rows > 1)
 | 
						|
    {
 | 
						|
        QDate start = ui->powerTable->item(0, POWER_COL_DATE)->data(Qt::DisplayRole).toDate();
 | 
						|
        QDate end = ui->powerTable->item(rows-1, POWER_COL_DATE)->data(Qt::DisplayRole).toDate();
 | 
						|
        if (start != end)
 | 
						|
        {
 | 
						|
            dateTimeFormat = QString("%1 hh:mm").arg(QLocale::system().dateFormat(QLocale::ShortFormat));
 | 
						|
            m_powerXAxisSameDay = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    m_powerXAxis->setFormat(dateTimeFormat);
 | 
						|
    m_powerXAxis->setRange(ui->powerStartTime->dateTime(), ui->powerEndTime->dateTime());
 | 
						|
    ui->powerStartTime->setDisplayFormat(dateTimeFormat);
 | 
						|
    ui->powerEndTime->setDisplayFormat(dateTimeFormat);
 | 
						|
 | 
						|
    // Create Y axis
 | 
						|
    m_powerYAxis = new QValueAxis();
 | 
						|
 | 
						|
    m_powerXAxis->setTitleText("Time");
 | 
						|
    calcPowerChartTickCount(size().width());
 | 
						|
    switch (m_settings.m_powerYData)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::PY_POWER:
 | 
						|
        m_powerSeries->setName("Measurement");
 | 
						|
        switch (m_settings.m_powerYUnits)
 | 
						|
        {
 | 
						|
        case RadioAstronomySettings::PY_DBFS:
 | 
						|
            m_powerYAxis->setTitleText("Power (dBFS)");
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_DBM:
 | 
						|
            m_powerYAxis->setTitleText("Power (dBm)");
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_WATTS:
 | 
						|
            m_powerYAxis->setTitleText("Power (Watts)");
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_TSYS:
 | 
						|
        m_powerSeries->setName("Tsys");
 | 
						|
        m_powerYAxis->setTitleText("Tsys (K)");
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_TSOURCE:
 | 
						|
        m_powerSeries->setName("Tsource");
 | 
						|
        m_powerYAxis->setTitleText("Tsource (K)");
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::PY_FLUX:
 | 
						|
        m_powerSeries->setName("Flux density");
 | 
						|
        switch (m_settings.m_powerYUnits)
 | 
						|
        {
 | 
						|
        case RadioAstronomySettings::PY_SFU:
 | 
						|
            m_powerYAxis->setTitleText("Flux density (SFU)");
 | 
						|
            break;
 | 
						|
        case RadioAstronomySettings::PY_JANSKY:
 | 
						|
            m_powerYAxis->setTitleText("Flux density (Jy)");
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
 | 
						|
    m_powerChart->addAxis(m_powerXAxis, Qt::AlignBottom);
 | 
						|
    m_powerChart->addAxis(m_powerYAxis, Qt::AlignLeft);
 | 
						|
    m_powerChart->addAxis(m_airTemps.yAxis(), Qt::AlignRight);
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        m_powerChart->addAxis(m_sensors[i].yAxis(), Qt::AlignRight);
 | 
						|
    }
 | 
						|
 | 
						|
    // Add data to series and calculate peaks
 | 
						|
    for (int i = 0; i < m_fftMeasurements.size(); i++) {
 | 
						|
        addToPowerSeries(m_fftMeasurements[i], i < (m_fftMeasurements.size() - 1));
 | 
						|
    }
 | 
						|
    m_airTemps.addAllToSeries();
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        m_sensors[i].addAllToSeries();
 | 
						|
    }
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerSeries);
 | 
						|
    m_powerSeries->attachAxis(m_powerXAxis);
 | 
						|
    m_powerSeries->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerTsys0Series);
 | 
						|
    m_powerTsys0Series->attachAxis(m_powerXAxis);
 | 
						|
    m_powerTsys0Series->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerGaussianSeries);
 | 
						|
    m_powerGaussianSeries->attachAxis(m_powerXAxis);
 | 
						|
    m_powerGaussianSeries->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    m_airTemps.addToChart(m_powerChart, m_powerXAxis);
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        m_sensors[i].addToChart(m_powerChart, m_powerXAxis);
 | 
						|
    }
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerFilteredSeries);
 | 
						|
    m_powerFilteredSeries->attachAxis(m_powerXAxis);
 | 
						|
    m_powerFilteredSeries->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerPeakSeries);
 | 
						|
    m_powerPeakSeries->attachAxis(m_powerXAxis);
 | 
						|
    m_powerPeakSeries->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    m_powerChart->addSeries(m_powerMarkerSeries);
 | 
						|
    m_powerMarkerSeries->attachAxis(m_powerXAxis);
 | 
						|
    m_powerMarkerSeries->attachAxis(m_powerYAxis);
 | 
						|
 | 
						|
    // Dark theme only has 5 colours for series, so use an extra unique colour (purple)
 | 
						|
    QPen pen(QColor(qRgb(146, 65, 146)), 2, Qt::SolidLine);
 | 
						|
    m_sensors[1].setPen(pen);
 | 
						|
 | 
						|
    // Don't have peaks and markers in legend
 | 
						|
    m_powerChart->legend()->markers(m_powerPeakSeries)[0]->setVisible(false);
 | 
						|
    m_powerChart->legend()->markers(m_powerMarkerSeries)[0]->setVisible(false);
 | 
						|
 | 
						|
    ui->powerChart->setChart(m_powerChart);
 | 
						|
 | 
						|
    delete oldChart;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calCompletetReceived(const RadioAstronomy::MsgCalComplete& measurement)
 | 
						|
{
 | 
						|
    bool hot = measurement.getHot();
 | 
						|
    int size = measurement.getSize();
 | 
						|
    Real* data = measurement.getCal();
 | 
						|
 | 
						|
    FFTMeasurement* fft = new FFTMeasurement();
 | 
						|
    if (hot)
 | 
						|
    {
 | 
						|
        delete m_calHot;
 | 
						|
        m_calHot = fft;
 | 
						|
        ui->startCalHot->setStyleSheet("QToolButton { background: none; }");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        delete m_calCold;
 | 
						|
        m_calCold = fft;
 | 
						|
        ui->startCalCold->setStyleSheet("QToolButton { background: none; }");
 | 
						|
    }
 | 
						|
    fft->m_fftData = data;
 | 
						|
    fft->m_fftSize = size;
 | 
						|
    fft->m_dateTime = measurement.getDateTime();
 | 
						|
    fft->m_centerFrequency = m_centerFrequency;
 | 
						|
    fft->m_sampleRate = m_settings.m_sampleRate;
 | 
						|
    fft->m_integration = m_settings.m_integration;
 | 
						|
    fft->m_rfBandwidth = m_settings.m_rfBandwidth;
 | 
						|
    fft->m_omegaA = calcOmegaA();
 | 
						|
    fft->m_omegaS = calcOmegaS();
 | 
						|
    fft->m_coordsValid = m_coordsValid;
 | 
						|
    fft->m_ra = m_ra;
 | 
						|
    fft->m_dec = m_dec;
 | 
						|
    fft->m_azimuth = m_azimuth;
 | 
						|
    fft->m_elevation = m_elevation;
 | 
						|
    fft->m_l = m_l;
 | 
						|
    fft->m_b = m_b;
 | 
						|
    fft->m_vBCRS = m_vBCRS;
 | 
						|
    fft->m_vLSR = m_vLSR;
 | 
						|
    fft->m_solarFlux = m_solarFlux;
 | 
						|
    fft->m_airTemp = m_airTemps.lastValue();
 | 
						|
    fft->m_skyTemp = m_skyTemp;
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        fft->m_sensor[i] = m_sensors[i].lastValue();
 | 
						|
    }
 | 
						|
    fft->m_tSys0 = calcTSys0();
 | 
						|
    fft->m_baseline = m_settings.m_spectrumBaseline;
 | 
						|
    getRotatorData(fft);
 | 
						|
 | 
						|
 | 
						|
    if (!hot) {
 | 
						|
        ui->calTsky->setText(QString::number(m_skyTemp, 'f', 1));
 | 
						|
    }
 | 
						|
 | 
						|
    calcFFTTotalPower(fft);
 | 
						|
    calcCalAvgDiff();
 | 
						|
    calibrate();
 | 
						|
    calcFFTTemperatures(fft);
 | 
						|
    calcFFTTotalTemperature(fft);
 | 
						|
    plotCalMeasurements();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotCalMeasurements()
 | 
						|
{
 | 
						|
    m_calHotSeries->clear();
 | 
						|
    m_calColdSeries->clear();
 | 
						|
 | 
						|
    if (m_calHot || m_calCold)
 | 
						|
    {
 | 
						|
        float minVal = std::numeric_limits<float>::max();
 | 
						|
        float maxVal = -std::numeric_limits<float>::max();
 | 
						|
 | 
						|
        double size;
 | 
						|
        double sampleRate;
 | 
						|
        double centerFrequency;
 | 
						|
        if (m_calCold && m_calHot)
 | 
						|
        {
 | 
						|
            size = (double)std::min(m_calCold->m_fftSize, m_calHot->m_fftSize);
 | 
						|
            sampleRate = (double)m_calCold->m_sampleRate;
 | 
						|
            centerFrequency = (double)m_calCold->m_centerFrequency;
 | 
						|
        }
 | 
						|
        else if (m_calCold)
 | 
						|
        {
 | 
						|
            size = (double)m_calCold->m_fftSize;
 | 
						|
            sampleRate = (double)m_calCold->m_sampleRate;
 | 
						|
            centerFrequency = (double)m_calCold->m_centerFrequency;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            size = (double)m_calHot->m_fftSize;
 | 
						|
            sampleRate = (double)m_calHot->m_sampleRate;
 | 
						|
            centerFrequency = (double)m_calHot->m_centerFrequency;
 | 
						|
        }
 | 
						|
 | 
						|
        double binRes = sampleRate / size;
 | 
						|
        double startFreq = centerFrequency - sampleRate / 2.0;
 | 
						|
        double freq = startFreq;
 | 
						|
        for (int i = 0; i < size; i++)
 | 
						|
        {
 | 
						|
            float value;
 | 
						|
            bool hotValid = m_calHot && (i < m_calHot->m_fftSize);
 | 
						|
            bool coldValid = m_calCold && (i < m_calCold->m_fftSize);
 | 
						|
 | 
						|
            if (hotValid)
 | 
						|
            {
 | 
						|
                value = CalcDb::dbPower(m_calHot->m_fftData[i]);
 | 
						|
                m_calHotSeries->append(freq / 1e6, value);
 | 
						|
                minVal = std::min(minVal, value);
 | 
						|
                maxVal = std::max(maxVal, value);
 | 
						|
            }
 | 
						|
            if (coldValid)
 | 
						|
            {
 | 
						|
                value = CalcDb::dbPower(m_calCold->m_fftData[i]);
 | 
						|
                m_calColdSeries->append(freq / 1e6, value);
 | 
						|
                minVal = std::min(minVal, value);
 | 
						|
                maxVal = std::max(maxVal, value);
 | 
						|
            }
 | 
						|
            freq += binRes;
 | 
						|
        }
 | 
						|
 | 
						|
        m_calYAxis->setRange(minVal, maxVal);
 | 
						|
 | 
						|
        double startFreqMHz = centerFrequency/1e6 - sampleRate/ 1e6 / 2.0;
 | 
						|
        double endFreqMHz = centerFrequency/1e6 + sampleRate/ 1e6 / 2.0;
 | 
						|
        m_calXAxis->setRange(startFreqMHz, endFreqMHz);
 | 
						|
        m_calXAxis->setReverse(m_settings.m_spectrumReverseXAxis);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotCalSpectrum()
 | 
						|
{
 | 
						|
    QChart *oldChart = m_calChart;
 | 
						|
 | 
						|
    m_calChart = new QChart();
 | 
						|
 | 
						|
    m_calChart->layout()->setContentsMargins(0, 0, 0, 0);
 | 
						|
    m_calChart->setMargins(QMargins(1, 1, 1, 1));
 | 
						|
    m_calChart->setTheme(QChart::ChartThemeDark);
 | 
						|
 | 
						|
    m_calChart->legend()->setAlignment(Qt::AlignRight);
 | 
						|
    m_calChart->legend()->setVisible(m_settings.m_spectrumLegend);
 | 
						|
 | 
						|
    m_calHotSeries = new QLineSeries();
 | 
						|
    m_calHotSeries->setName("Hot");
 | 
						|
    m_calColdSeries = new QLineSeries();
 | 
						|
    m_calColdSeries->setName("Cold");
 | 
						|
 | 
						|
    m_calXAxis = new QValueAxis();
 | 
						|
    m_calYAxis = new QValueAxis();
 | 
						|
 | 
						|
    m_calChart->addAxis(m_calXAxis, Qt::AlignBottom);
 | 
						|
    m_calChart->addAxis(m_calYAxis, Qt::AlignLeft);
 | 
						|
 | 
						|
    m_calXAxis->setTitleText("Frequency (MHz)");
 | 
						|
    calcSpectrumChartTickCount(m_calXAxis, size().width());
 | 
						|
    m_calYAxis->setTitleText("Power (dBFS)");
 | 
						|
 | 
						|
    m_calChart->addSeries(m_calHotSeries);
 | 
						|
    m_calHotSeries->attachAxis(m_calXAxis);
 | 
						|
    m_calHotSeries->attachAxis(m_calYAxis);
 | 
						|
 | 
						|
    m_calChart->addSeries(m_calColdSeries);
 | 
						|
    m_calColdSeries->attachAxis(m_calXAxis);
 | 
						|
    m_calColdSeries->attachAxis(m_calYAxis);
 | 
						|
 | 
						|
    plotCalMeasurements();
 | 
						|
 | 
						|
    ui->spectrumChart->setChart(m_calChart);
 | 
						|
 | 
						|
    delete oldChart;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumReference_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumReference = value;
 | 
						|
    spectrumUpdateYRange();
 | 
						|
    if (!m_settings.m_spectrumAutoscale) {
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumRange_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumRange = value;
 | 
						|
    if (m_settings.m_spectrumRange <= 1.0)
 | 
						|
    {
 | 
						|
        ui->spectrumRange->setSingleStep(0.1);
 | 
						|
        ui->spectrumRange->setDecimals(2);
 | 
						|
        ui->spectrumReference->setDecimals(2);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumRange->setSingleStep(1.0);
 | 
						|
        ui->spectrumRange->setDecimals(1);
 | 
						|
        ui->spectrumReference->setDecimals(1);
 | 
						|
    }
 | 
						|
    spectrumUpdateYRange();
 | 
						|
    if (!m_settings.m_spectrumAutoscale) {
 | 
						|
        applySettings();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumSpan_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumSpan = value;
 | 
						|
    spectrumUpdateXRange();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumCenterFreq_valueChanged(double value)
 | 
						|
{
 | 
						|
    double offset;
 | 
						|
    FFTMeasurement* fft = currentFFT();
 | 
						|
    if (fft) {
 | 
						|
        offset = value - fft->m_centerFrequency/1e6;
 | 
						|
    } else {
 | 
						|
        offset = value - m_centerFrequency/1e6;
 | 
						|
    }
 | 
						|
    m_settings.m_spectrumCenterFreqOffset = offset;
 | 
						|
    spectrumUpdateXRange();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::spectrumUpdateXRange(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    if (!fft) {
 | 
						|
        fft = currentFFT();
 | 
						|
    }
 | 
						|
    if (m_fftXAxis && fft)
 | 
						|
    {
 | 
						|
        double startFreqMHz = fft->m_centerFrequency/1e6 - m_settings.m_spectrumSpan / 2.0;
 | 
						|
        double endFreqMHz = fft->m_centerFrequency/1e6 + m_settings.m_spectrumSpan / 2.0;
 | 
						|
        m_fftXAxis->setRange(startFreqMHz + m_settings.m_spectrumCenterFreqOffset, endFreqMHz + m_settings.m_spectrumCenterFreqOffset);
 | 
						|
        double lineFreqMHz = ui->spectrumLineFrequency->value();
 | 
						|
        double lineFreq = lineFreqMHz * 1e6;
 | 
						|
        double startFreq = fft->m_centerFrequency + m_settings.m_spectrumSpan*1e6 / 2.0 + m_settings.m_spectrumCenterFreqOffset*1e6;
 | 
						|
        double endFreq = fft->m_centerFrequency - m_settings.m_spectrumSpan*1e6 / 2.0 + m_settings.m_spectrumCenterFreqOffset*1e6;
 | 
						|
        m_fftDopplerAxis->setRange(dopplerToVelocity(lineFreq, startFreq, fft),
 | 
						|
                                   dopplerToVelocity(lineFreq, endFreq, fft));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::spectrumUpdateYRange(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    if (!fft) {
 | 
						|
        fft = currentFFT();
 | 
						|
    }
 | 
						|
    if (m_fftYAxis && fft) {
 | 
						|
        m_fftYAxis->setRange(m_settings.m_spectrumReference - m_settings.m_spectrumRange, m_settings.m_spectrumReference);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::spectrumAutoscale()
 | 
						|
{
 | 
						|
    if (m_settings.m_spectrumAutoscale)
 | 
						|
    {
 | 
						|
        on_spectrumAutoscaleX_clicked();
 | 
						|
        on_spectrumAutoscaleY_clicked();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Scale Y axis according to min and max values
 | 
						|
void RadioAstronomyGUI::on_spectrumAutoscale_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumAutoscale = checked;
 | 
						|
    ui->spectrumAutoscaleX->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumAutoscaleY->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumReference->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumRange->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumCenterFreq->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    ui->spectrumSpan->setEnabled(!m_settings.m_spectrumAutoscale);
 | 
						|
    spectrumAutoscale();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
// Get minimum and maximum values in a series
 | 
						|
// Assumes minVal and maxVal are initialised
 | 
						|
static bool seriesMinAndMax(QLineSeries* series, qreal& minVal, qreal& maxVal)
 | 
						|
{
 | 
						|
    QVector<QPointF> points = series->pointsVector();
 | 
						|
    for (int i = 0; i < points.size(); i++)
 | 
						|
    {
 | 
						|
        qreal power = points[i].y();
 | 
						|
        minVal = std::min(minVal, power);
 | 
						|
        maxVal = std::max(maxVal, power);
 | 
						|
    }
 | 
						|
    return points.size() > 0;
 | 
						|
}
 | 
						|
 | 
						|
// Scale Y axis according to min and max values
 | 
						|
void RadioAstronomyGUI::on_spectrumAutoscaleY_clicked()
 | 
						|
{
 | 
						|
    double minVal = std::numeric_limits<double>::max();
 | 
						|
    double maxVal = -std::numeric_limits<double>::max();
 | 
						|
    bool v0 = false, v1 = false;
 | 
						|
    if (m_fftSeries) {
 | 
						|
        v0 = seriesMinAndMax(m_fftSeries, minVal, maxVal);
 | 
						|
    }
 | 
						|
    if (m_fftLABSeries && m_settings.m_spectrumLAB) {
 | 
						|
        v1 = seriesMinAndMax(m_fftLABSeries, minVal, maxVal);
 | 
						|
    }
 | 
						|
    if (v0 || v1)
 | 
						|
    {
 | 
						|
        double range = (maxVal - minVal) * 1.2; // 20% wider than signal range, for space for markers
 | 
						|
        range = std::max(0.1, range);     // Don't be smaller than minimum value we can set in GUI
 | 
						|
        ui->spectrumRange->setValue(range); // Call before setting reference, so number of decimals are adjusted
 | 
						|
        ui->spectrumReference->setValue(maxVal + range * 0.15);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Scale X axis according to min and max values
 | 
						|
void RadioAstronomyGUI::on_spectrumAutoscaleX_clicked()
 | 
						|
{
 | 
						|
    FFTMeasurement* fft = currentFFT();
 | 
						|
    if (fft)
 | 
						|
    {
 | 
						|
        ui->spectrumSpan->setValue(fft->m_sampleRate/1e6);
 | 
						|
        ui->spectrumCenterFreq->setValue(fft->m_centerFrequency/1e6);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumSpan->setValue(m_basebandSampleRate/1e6);
 | 
						|
        ui->spectrumCenterFreq->setValue(m_centerFrequency/1e6);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
RadioAstronomyGUI::FFTMeasurement* RadioAstronomyGUI::currentFFT()
 | 
						|
{
 | 
						|
    int index = ui->spectrumIndex->value();
 | 
						|
    if ((index >= 0) && (index < m_fftMeasurements.size())) {
 | 
						|
        return m_fftMeasurements[index];
 | 
						|
    } else {
 | 
						|
        return nullptr;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumYUnits_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    (void) index;
 | 
						|
 | 
						|
    QString text = ui->spectrumYUnits->currentText();
 | 
						|
    if (text == "dBFS")
 | 
						|
    {
 | 
						|
        m_settings.m_spectrumYScale = RadioAstronomySettings::SY_DBFS;
 | 
						|
        ui->spectrumMarkerTable->horizontalHeaderItem(SPECTRUM_MARKER_COL_VALUE)->setText("Power (dBFS)");
 | 
						|
    }
 | 
						|
    else if (text == "SNR")
 | 
						|
    {
 | 
						|
        m_settings.m_spectrumYScale = RadioAstronomySettings::SY_SNR;
 | 
						|
        ui->spectrumMarkerTable->horizontalHeaderItem(SPECTRUM_MARKER_COL_VALUE)->setText("SNR");
 | 
						|
    }
 | 
						|
    else if (text == "dBm")
 | 
						|
    {
 | 
						|
        m_settings.m_spectrumYScale = RadioAstronomySettings::SY_DBM;
 | 
						|
        ui->spectrumMarkerTable->horizontalHeaderItem(SPECTRUM_MARKER_COL_VALUE)->setText("Power (dBm)");
 | 
						|
    }
 | 
						|
    else if (text == "Tsys K")
 | 
						|
    {
 | 
						|
        m_settings.m_spectrumYScale = RadioAstronomySettings::SY_TSYS;
 | 
						|
        ui->spectrumMarkerTable->horizontalHeaderItem(SPECTRUM_MARKER_COL_VALUE)->setText("Tsys (K)");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_settings.m_spectrumYScale = RadioAstronomySettings::SY_TSOURCE;
 | 
						|
        ui->spectrumMarkerTable->horizontalHeaderItem(SPECTRUM_MARKER_COL_VALUE)->setText("Tsource (K)");
 | 
						|
    }
 | 
						|
    plotFFTMeasurement();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumBaseline_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumBaseline = (RadioAstronomySettings::SpectrumBaseline)index;
 | 
						|
    plotFFTMeasurement();
 | 
						|
    if ((m_settings.m_powerYData == RadioAstronomySettings::PY_TSOURCE) || (m_settings.m_powerYData == RadioAstronomySettings::PY_FLUX)) {
 | 
						|
        plotPowerChart();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
// Convert frequency shift to velocity in km/s (+ve approaching)
 | 
						|
static double lineDopplerVelocity(double centre, double f)
 | 
						|
{
 | 
						|
    return Astronomy::dopplerToVelocity(f, centre) / 1000.0f;
 | 
						|
}
 | 
						|
 | 
						|
// Convert frequency shift to velocity (+ve receeding - which seems to be the astronomical convention)
 | 
						|
double RadioAstronomyGUI::dopplerToVelocity(double centre, double f, FFTMeasurement *fft)
 | 
						|
{
 | 
						|
    double v = lineDopplerVelocity(centre, f);
 | 
						|
    // Adjust in to selected reference frame
 | 
						|
    switch (m_settings.m_refFrame)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::BCRS:
 | 
						|
        v -= fft->m_vBCRS;
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::LSR:
 | 
						|
        v -= fft->m_vLSR;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    // Make +ve receeding
 | 
						|
    return -v;
 | 
						|
}
 | 
						|
 | 
						|
// Replot current FFT
 | 
						|
void RadioAstronomyGUI::plotFFTMeasurement()
 | 
						|
{
 | 
						|
    plotFFTMeasurement(ui->spectrumIndex->value());
 | 
						|
}
 | 
						|
 | 
						|
// Plot FFT with specified index
 | 
						|
void RadioAstronomyGUI::plotFFTMeasurement(int index)
 | 
						|
{
 | 
						|
    if (index < m_fftMeasurements.size())
 | 
						|
    {
 | 
						|
        FFTMeasurement *fft = m_fftMeasurements[index];
 | 
						|
 | 
						|
        m_fftSeries->clear();
 | 
						|
        m_fftHlineSeries->clear();
 | 
						|
        m_fftGaussianSeries->clear();
 | 
						|
        m_fftLABSeries->clear();
 | 
						|
        m_fftPeakSeries->clear();
 | 
						|
 | 
						|
        double binRes = fft->m_sampleRate / (double)fft->m_fftSize;
 | 
						|
        double startFreq = fft->m_centerFrequency - fft->m_sampleRate / 2.0;
 | 
						|
 | 
						|
        // Plot reference spectral line and Doppler axis
 | 
						|
        plotRefLine(fft);
 | 
						|
 | 
						|
        // Plot Gaussian for temp estimation
 | 
						|
        plotTempGaussian(startFreq, binRes, fft->m_fftSize);
 | 
						|
 | 
						|
        // Plot LAB reference data
 | 
						|
        if (   fft->m_coordsValid && m_settings.m_spectrumLAB
 | 
						|
            && (   (m_settings.m_spectrumYScale == RadioAstronomySettings::SY_TSYS)
 | 
						|
                || (m_settings.m_spectrumYScale == RadioAstronomySettings::SY_TSOURCE)
 | 
						|
               )
 | 
						|
           )
 | 
						|
        {
 | 
						|
            plotLAB(fft->m_l, fft->m_b, m_beamWidth);
 | 
						|
        }
 | 
						|
 | 
						|
        if (   ((m_settings.m_spectrumYScale == RadioAstronomySettings::SY_SNR) && !fft->m_snr)
 | 
						|
            || ((m_settings.m_spectrumYScale == RadioAstronomySettings::SY_DBM) && !fft->m_temp)
 | 
						|
            || ((m_settings.m_spectrumYScale == RadioAstronomySettings::SY_TSYS) && !fft->m_temp)
 | 
						|
            || ((m_settings.m_spectrumYScale == RadioAstronomySettings::SY_TSOURCE) && !fft->m_temp)
 | 
						|
           )
 | 
						|
        {
 | 
						|
            m_fftChart->setTitle("No cal data: Run calibration or set units to dBFS.");
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            m_fftChart->setTitle("");
 | 
						|
 | 
						|
            if (fft->m_coordsValid)
 | 
						|
            {
 | 
						|
                m_fftChart->setTitle(QString("RA: %1 Dec: %2 l: %3%7 b: %4%7 Az: %5%7 El: %6%7")
 | 
						|
                                            .arg(Units::decimalHoursToHoursMinutesAndSeconds(fft->m_ra))
 | 
						|
                                            .arg(Units::decimalDegreesToDegreeMinutesAndSeconds(fft->m_dec))
 | 
						|
                                            .arg(QString::number(fft->m_l, 'f', 1))
 | 
						|
                                            .arg(QString::number(fft->m_b, 'f', 1))
 | 
						|
                                            .arg(QString::number(fft->m_azimuth, 'f', 0))
 | 
						|
                                            .arg(QString::number(fft->m_elevation, 'f', 0))
 | 
						|
                                            .arg(QChar(0xb0)));
 | 
						|
            }
 | 
						|
 | 
						|
            int peakIdx = 0;
 | 
						|
            double peakValue = -std::numeric_limits<double>::max();
 | 
						|
 | 
						|
            double freq = startFreq;   // Main spectrum seems to use bin midpoint - this uses lowest frequency, so we're tone at centre freq appears in centre of plot
 | 
						|
 | 
						|
            // Plot power/temp
 | 
						|
            for (int i = 0; i < fft->m_fftSize; i++)
 | 
						|
            {
 | 
						|
                qreal value;
 | 
						|
                switch (m_settings.m_spectrumYScale)
 | 
						|
                {
 | 
						|
                case RadioAstronomySettings::SY_DBFS:
 | 
						|
                    value = fft->m_db[i];
 | 
						|
                    break;
 | 
						|
                case RadioAstronomySettings::SY_SNR:
 | 
						|
                    value = fft->m_snr[i];
 | 
						|
                    break;
 | 
						|
                case RadioAstronomySettings::SY_DBM:
 | 
						|
                    value = Astronomy::noisePowerdBm(fft->m_temp[i], fft->m_sampleRate/(double)fft->m_fftSize);
 | 
						|
                    break;
 | 
						|
                case RadioAstronomySettings::SY_TSYS:
 | 
						|
                    value = fft->m_temp[i];
 | 
						|
                    break;
 | 
						|
                case RadioAstronomySettings::SY_TSOURCE:
 | 
						|
                    switch (m_settings.m_spectrumBaseline)
 | 
						|
                    {
 | 
						|
                    case RadioAstronomySettings::SBL_TSYS0:
 | 
						|
                        value = fft->m_temp[i] - fft->m_tSys0;
 | 
						|
                        break;
 | 
						|
                    case RadioAstronomySettings::SBL_TMIN:
 | 
						|
                        value = fft->m_temp[i] - fft->m_tempMin;
 | 
						|
                        break;
 | 
						|
                    case RadioAstronomySettings::SBL_CAL_COLD:
 | 
						|
                        if (m_calCold) {
 | 
						|
                            value = m_calG[i] * (fft->m_fftData[i] - m_calCold->m_fftData[i]);
 | 
						|
                        } else {
 | 
						|
                            value = 0.0;
 | 
						|
                        }
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                if (value > peakValue)
 | 
						|
                {
 | 
						|
                    peakValue = value;
 | 
						|
                    peakIdx = i;
 | 
						|
                }
 | 
						|
 | 
						|
                m_fftSeries->append(freq / 1e6, value);
 | 
						|
                freq += binRes;
 | 
						|
            }
 | 
						|
 | 
						|
            double startFreqMHz = fft->m_centerFrequency/1e6 - m_settings.m_spectrumSpan / 2.0;
 | 
						|
 | 
						|
            spectrumUpdateXRange(fft);
 | 
						|
            spectrumUpdateYRange(fft);
 | 
						|
            m_fftXAxis->setReverse(m_settings.m_spectrumReverseXAxis);
 | 
						|
 | 
						|
            switch (m_settings.m_spectrumYScale)
 | 
						|
            {
 | 
						|
            case RadioAstronomySettings::SY_DBFS:
 | 
						|
                m_fftYAxis->setTitleText("Power (dBFS)");
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::SY_SNR:
 | 
						|
                m_fftYAxis->setTitleText("SNR");
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::SY_DBM:
 | 
						|
                m_fftYAxis->setTitleText("Power (dBm)");
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::SY_TSYS:
 | 
						|
                m_fftYAxis->setTitleText("Tsys (K)");
 | 
						|
                break;
 | 
						|
            case RadioAstronomySettings::SY_TSOURCE:
 | 
						|
                m_fftYAxis->setTitleText("Tsource (K)");
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            // Plot peaks
 | 
						|
            if (m_settings.m_spectrumPeaks)
 | 
						|
            {
 | 
						|
                double peakFreqMHz = (startFreq + peakIdx * binRes) / 1e6;
 | 
						|
                double peakFreq = peakFreqMHz * 1e6;
 | 
						|
 | 
						|
                m_fftPeakSeries->append(peakFreqMHz, peakValue);
 | 
						|
 | 
						|
                ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_PEAK, SPECTRUM_MARKER_COL_FREQ)->setData(Qt::DisplayRole, peakFreqMHz);
 | 
						|
                ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_PEAK, SPECTRUM_MARKER_COL_VALUE)->setData(Qt::DisplayRole, peakValue);
 | 
						|
 | 
						|
                calcVrAndDistanceToPeak(peakFreq, fft, SPECTRUM_MARKER_ROW_PEAK);
 | 
						|
            }
 | 
						|
 | 
						|
            // Update markers to track current data
 | 
						|
            if (m_spectrumM1Valid)
 | 
						|
            {
 | 
						|
                m_fftMarkerSeries->clear();
 | 
						|
                int idx;
 | 
						|
                idx = (m_spectrumM1X - startFreqMHz) / (binRes/1e6);
 | 
						|
                if ((idx >= 0) && (idx < m_fftSeries->count()))
 | 
						|
                {
 | 
						|
                    m_spectrumM1Y = m_fftSeries->at(idx).y();
 | 
						|
                    ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M1, SPECTRUM_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_spectrumM1Y);
 | 
						|
                    m_fftMarkerSeries->append(m_spectrumM1X, m_spectrumM1Y);
 | 
						|
                    calcVrAndDistanceToPeak(m_spectrumM1X*1e6, fft, SPECTRUM_MARKER_ROW_M1);
 | 
						|
                }
 | 
						|
                if (m_spectrumM2Valid)
 | 
						|
                {
 | 
						|
                    idx = (m_spectrumM2X - startFreqMHz) / (binRes/1e6);
 | 
						|
                    if (idx < m_fftSeries->count())
 | 
						|
                    {
 | 
						|
                        m_spectrumM2Y = m_fftSeries->at(idx).y();
 | 
						|
                        ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_spectrumM2Y);
 | 
						|
                        m_fftMarkerSeries->append(m_spectrumM2X, m_spectrumM2Y);
 | 
						|
                        calcVrAndDistanceToPeak(m_spectrumM2X*1e6, fft, SPECTRUM_MARKER_ROW_M2);
 | 
						|
                        calcSpectrumMarkerDelta();
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            spectrumAutoscale();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
bool RadioAstronomyGUI::losMarkerEnabled(const QString& name)
 | 
						|
{
 | 
						|
    if (m_settings.m_spectrumDistance && m_settings.m_spectrumRefLine)
 | 
						|
    {
 | 
						|
        if (name == "Max") {
 | 
						|
            return m_settings.m_spectrumPeaks;
 | 
						|
        } else if (name == "M1") {
 | 
						|
            return m_settings.m_spectrumMarkers;
 | 
						|
        } else {
 | 
						|
            return m_settings.m_spectrumMarkers;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::showLoSMarker(const QString& name)
 | 
						|
{
 | 
						|
    if (losMarkerEnabled(name))
 | 
						|
    {
 | 
						|
        if (name == "Max") {
 | 
						|
            showLoSMarker(SPECTRUM_MARKER_ROW_PEAK);
 | 
						|
        } else if (name == "M1") {
 | 
						|
            showLoSMarker(SPECTRUM_MARKER_ROW_M1);
 | 
						|
        } else {
 | 
						|
            showLoSMarker(SPECTRUM_MARKER_ROW_M2);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::showLoSMarker(int row)
 | 
						|
{
 | 
						|
    bool ok;
 | 
						|
    float d = ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_D)->data(Qt::DisplayRole).toFloat(&ok);
 | 
						|
    if (ok)
 | 
						|
    {
 | 
						|
        FFTMeasurement *fft = currentFFT();
 | 
						|
        if (fft)
 | 
						|
        {
 | 
						|
            QString name = ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_NAME)->text();
 | 
						|
            updateLoSMarker(name, fft->m_l, fft->m_b, d);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateLoSMarker(const QString& name, float l, float b, float d)
 | 
						|
{
 | 
						|
    // Send to Star Tracker
 | 
						|
    QList<ObjectPipe*> starTrackerPipes;
 | 
						|
    MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
 | 
						|
 | 
						|
    for (const auto& pipe : starTrackerPipes)
 | 
						|
    {
 | 
						|
        MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
 | 
						|
        SWGSDRangel::SWGStarTrackerDisplayLoSSettings *swgSettings = new SWGSDRangel::SWGStarTrackerDisplayLoSSettings();
 | 
						|
        swgSettings->setName(new QString(name));
 | 
						|
        swgSettings->setL(l);
 | 
						|
        swgSettings->setB(b);
 | 
						|
        swgSettings->setD(d);
 | 
						|
        messageQueue->push(MainCore::MsgStarTrackerDisplayLoSSettings::create(m_radioAstronomy, swgSettings));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::clearLoSMarker(const QString& name)
 | 
						|
{
 | 
						|
    // Set d to 0 to clear
 | 
						|
    updateLoSMarker(name, 0.0f, 0.0f, 0.0f);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcVrAndDistanceToPeak(double freq, FFTMeasurement *fft, int row)
 | 
						|
{
 | 
						|
    double lineFreq = ui->spectrumLineFrequency->value() * 1e6;
 | 
						|
 | 
						|
    // Calculate radial velocity (along line-of-sight) from Doppler shift
 | 
						|
    double vR = dopplerToVelocity(lineFreq, freq, fft);
 | 
						|
    ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_VR)->setData(Qt::DisplayRole, vR);
 | 
						|
 | 
						|
    // Tangent point method only valid for Galactic quadrants I (0-90) and IV (270-360)
 | 
						|
    if ((fft->m_l < 90.0) || (fft->m_l > 270.0))
 | 
						|
    {
 | 
						|
        // Calculate minimum distance to Galactic centre along line of sight (tangential radius)
 | 
						|
        double rMin = m_settings.m_sunDistanceToGC * sin(Units::degreesToRadians(fft->m_l));
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_R_MIN)->setData(Qt::DisplayRole, rMin);
 | 
						|
 | 
						|
        // Calculate orbital velocity at tangent/minimum point
 | 
						|
        // This is the velocity to plot for the rotation curve
 | 
						|
        double w0 = m_settings.m_sunOrbitalVelocity / m_settings.m_sunDistanceToGC;
 | 
						|
        double vOrb = vR + rMin * w0;
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_V)->setData(Qt::DisplayRole, vOrb);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_R_MIN)->setText("");
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_V)->setText("");
 | 
						|
    }
 | 
						|
 | 
						|
    // Calculate distance of HI cloud (as indicated by a peak) to Sun and Galactic center
 | 
						|
    double r, d1, d2;
 | 
						|
    int solutions = calcDistanceToPeak(vR, fft->m_l, fft->m_b, r, d1, d2);
 | 
						|
    if (solutions == 0)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_R)->setText("");
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_D)->setText("");
 | 
						|
    }
 | 
						|
    else if (solutions == 1)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_R)->setData(Qt::DisplayRole, r);
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_D)->setData(Qt::DisplayRole, d1);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_R)->setData(Qt::DisplayRole, r);
 | 
						|
        ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_D)->setText(QString("%1/%2").arg(QString::number(d1, 'f', 1)).arg(QString::number(d2, 'f', 1)));
 | 
						|
    }
 | 
						|
 | 
						|
    // Send to Star Tracker
 | 
						|
    QString name = ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_NAME)->text();
 | 
						|
    if (losMarkerEnabled(name))
 | 
						|
    {
 | 
						|
        if ((solutions == 0) || std::isnan(d1))
 | 
						|
        {
 | 
						|
            updateLoSMarker(name, fft->m_l, fft->m_b, 0.0f);
 | 
						|
        }
 | 
						|
        else if (solutions == 1)
 | 
						|
        {
 | 
						|
            updateLoSMarker(name, fft->m_l, fft->m_b, d1);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            bool plotMax = ui->spectrumMarkerTable->item(row, SPECTRUM_MARKER_COL_PLOT_MAX)->checkState() == Qt::Checked;
 | 
						|
            if ((plotMax && (d1 > d2)) || (!plotMax && (d1 < d2))) {
 | 
						|
                updateLoSMarker(name, fft->m_l, fft->m_b, d1);
 | 
						|
            } else {
 | 
						|
                updateLoSMarker(name, fft->m_l, fft->m_b, d2);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int RadioAstronomyGUI::calcDistanceToPeak(double vr, float l, float b, double& r, double &d1, double &d2)
 | 
						|
{
 | 
						|
    // Radio Astronomy 4th edition - Burke - p343
 | 
						|
    double r0 = m_settings.m_sunDistanceToGC; // Distance of Sun to Galactic centre in kpc
 | 
						|
    double v0 = m_settings.m_sunOrbitalVelocity; // Orbital velocity of the Sun around Galactic centre in km/s
 | 
						|
    double w0 = v0/r0;   // Angular velocity of the Sun
 | 
						|
    double gl = Units::degreesToRadians(l);
 | 
						|
    double gb = Units::degreesToRadians(b);
 | 
						|
    double w = vr/(r0*sin(gl)*cos(gb))+w0;
 | 
						|
    r = v0/w; // Assume constant v, regardless of distance from GC - Dark matter magic - Not valid <1kpc from GC
 | 
						|
    if (r < 0) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_non-included_angle_given_(SSA)
 | 
						|
    double beta = gl;
 | 
						|
    double d = sin(beta)*r0/r;
 | 
						|
    if (d > 1.0) {
 | 
						|
        // No solutions
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ((r <= r0) && (beta >= M_PI/2.0)) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    double gamma = asin(d);
 | 
						|
    double alpha1 = M_PI - beta - gamma;
 | 
						|
    d1 = r * sin(alpha1)/sin(beta); // Distance from Sun to peak
 | 
						|
    if (r < r0)
 | 
						|
    {
 | 
						|
        double alpha2 = M_PI - beta - (M_PI-gamma);
 | 
						|
        d2 = r * sin(alpha2)/sin(beta); // Distance from Sun to peak
 | 
						|
        return 2;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::spectrumMarkerTableItemChanged(QTableWidgetItem *item)
 | 
						|
{
 | 
						|
    if (item->column() == SPECTRUM_MARKER_COL_PLOT_MAX) {
 | 
						|
        // Plot max checkbox clicked
 | 
						|
        calcDistances();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcDistances()
 | 
						|
{
 | 
						|
    double freq;
 | 
						|
    bool ok;
 | 
						|
    FFTMeasurement* fft = currentFFT();
 | 
						|
    if (fft)
 | 
						|
    {
 | 
						|
        freq = ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_PEAK, SPECTRUM_MARKER_COL_FREQ)->data(Qt::DisplayRole).toDouble(&ok);
 | 
						|
        if (ok) {
 | 
						|
            calcVrAndDistanceToPeak(freq*1e6, fft, SPECTRUM_MARKER_ROW_PEAK);
 | 
						|
        }
 | 
						|
        freq = ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M1, SPECTRUM_MARKER_COL_FREQ)->data(Qt::DisplayRole).toDouble(&ok);
 | 
						|
        if (ok) {
 | 
						|
            calcVrAndDistanceToPeak(freq*1e6, fft, SPECTRUM_MARKER_ROW_M1);
 | 
						|
        }
 | 
						|
        freq = ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_FREQ)->data(Qt::DisplayRole).toDouble(&ok);
 | 
						|
        if (ok) {
 | 
						|
            calcVrAndDistanceToPeak(freq*1e6, fft, SPECTRUM_MARKER_ROW_M2);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotRefLine(FFTMeasurement *fft)
 | 
						|
{
 | 
						|
    double lineFreqMHz = ui->spectrumLineFrequency->value();
 | 
						|
    double lineFreq = lineFreqMHz * 1e6;
 | 
						|
    QString refFrame[] = {"Topocentric", "Barycentric", "LSR"};
 | 
						|
    m_fftDopplerAxis->setTitleText(QString("%1 radial velocity (km/s - +ve receding)").arg(refFrame[m_settings.m_refFrame]));
 | 
						|
    m_fftHlineSeries->setName(QString("%1 line").arg(ui->spectrumLine->currentText()));
 | 
						|
    m_fftHlineSeries->append(0.0f, -200.0f); // For dB
 | 
						|
    m_fftHlineSeries->append(0.0f, 10000.0f); // For temp can be >1e6?
 | 
						|
    double startFreq = fft->m_centerFrequency + m_settings.m_spectrumSpan*1e6 / 2.0 + m_settings.m_spectrumCenterFreqOffset*1e6;
 | 
						|
    double endFreq = fft->m_centerFrequency - m_settings.m_spectrumSpan*1e6 / 2.0 + m_settings.m_spectrumCenterFreqOffset*1e6;
 | 
						|
    m_fftDopplerAxis->setRange(dopplerToVelocity(lineFreq, startFreq, fft),
 | 
						|
                               dopplerToVelocity(lineFreq, endFreq, fft));
 | 
						|
    m_fftDopplerAxis->setReverse(!m_settings.m_spectrumReverseXAxis);
 | 
						|
    m_fftDopplerAxis->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotTempGaussian(double startFreq, double freqStep, int steps)
 | 
						|
{
 | 
						|
    m_fftGaussianSeries->clear();
 | 
						|
    double f0 = ui->spectrumGaussianFreq->value() * 1e6;
 | 
						|
    double a = ui->spectrumGaussianAmp->value();
 | 
						|
    double floor = ui->spectrumGaussianFloor->value();
 | 
						|
    double fwhm = ui->spectrumGaussianFWHM->value();
 | 
						|
    double fwhm_sq = fwhm*fwhm;
 | 
						|
    double freq = startFreq;
 | 
						|
    for (int i = 0; i < steps; i++)
 | 
						|
    {
 | 
						|
        double fd = freq - f0;
 | 
						|
        double g = a * std::exp(-4.0*M_LN2*fd*fd/fwhm_sq) + floor;
 | 
						|
        m_fftGaussianSeries->append(freq/1e6, g);
 | 
						|
        freq += freqStep;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFFTPower(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    // Convert linear to dB
 | 
						|
    for (int i = 0; i < fft->m_fftSize; i++) {
 | 
						|
        fft->m_db[i] = CalcDb::dbPower(fft->m_fftData[i]);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFFTTotalPower(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    double total = 0.0;
 | 
						|
    for (int i = 0; i < fft->m_fftSize; i++) {
 | 
						|
        total += fft->m_fftData[i];
 | 
						|
    }
 | 
						|
    fft->m_totalPower = total;
 | 
						|
    fft->m_totalPowerdBFS = CalcDb::dbPower(total);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFFTTemperatures(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    if (m_calCold && !fft->m_snr) {
 | 
						|
        fft->m_snr = new Real[fft->m_fftSize];
 | 
						|
    }
 | 
						|
    if (m_calG && !fft->m_temp) {
 | 
						|
        fft->m_temp = new Real[fft->m_fftSize];
 | 
						|
    }
 | 
						|
    for (int i = 0; i < fft->m_fftSize; i++)
 | 
						|
    {
 | 
						|
        if (fft->m_snr && m_calCold)
 | 
						|
        {
 | 
						|
            // Calculate SNR (relative to cold cal)
 | 
						|
            fft->m_snr[i] = fft->m_fftData[i] / m_calCold->m_fftData[i];
 | 
						|
        }
 | 
						|
        if (m_calG)
 | 
						|
        {
 | 
						|
            // Calculate temperature using scaling from hot cal
 | 
						|
            fft->m_temp[i] = m_calG[i] * fft->m_fftData[i];
 | 
						|
            // Calculate temperature using linear interpolation from hot/cold cal
 | 
						|
            //fft->m_temp[i] = m_calG[i] * (fft->m_fftData[i] - m_calCold->m_fftData[i]) + m_settings.m_tCalCold;
 | 
						|
            //fft->m_temp[i] = std::max(fft->m_temp[i], 0.0f); // Can't have negative temperatures
 | 
						|
        }
 | 
						|
    }
 | 
						|
    calcFFTMinTemperature(fft);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFFTMinTemperature(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    fft->m_tempMin = 0;
 | 
						|
    if (fft->m_temp)
 | 
						|
    {
 | 
						|
        // Select minimum from within band. 95% of that to account for a little bit of inband rolloff
 | 
						|
        float tempMin = std::numeric_limits<float>::max();
 | 
						|
        double pc = 0.95 * fft->m_rfBandwidth / (double)fft->m_sampleRate;
 | 
						|
        int count = (int)(pc * fft->m_fftSize);
 | 
						|
        int start = (fft->m_fftSize - count) / 2;
 | 
						|
        for (int i = 0; i < count; i++)
 | 
						|
        {
 | 
						|
            int idx = i + start;
 | 
						|
            tempMin = std::min(tempMin, fft->m_temp[idx]);
 | 
						|
        }
 | 
						|
        if (tempMin != std::numeric_limits<float>::max()) {
 | 
						|
            fft->m_tempMin = tempMin;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Estimate Tsource by subtracting selected baseline
 | 
						|
double RadioAstronomyGUI::calcTSource(FFTMeasurement *fft) const
 | 
						|
{
 | 
						|
    switch (fft->m_baseline)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::SBL_TSYS0:
 | 
						|
        return fft->m_tSys - fft->m_tSys0;
 | 
						|
    case RadioAstronomySettings::SBL_TMIN:
 | 
						|
        return fft->m_tSys - fft->m_tempMin;
 | 
						|
    case RadioAstronomySettings::SBL_CAL_COLD:
 | 
						|
        if (m_calCold) {
 | 
						|
            return fft->m_tSys - m_calCold->m_tSys;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    return fft->m_tSys;
 | 
						|
}
 | 
						|
 | 
						|
// Calculate spectral flux density of source
 | 
						|
double RadioAstronomyGUI::calcFlux(double Ta, const FFTMeasurement *fft) const
 | 
						|
{
 | 
						|
    // Factor of 2 here assumes single polarization
 | 
						|
    // See equation 5.13 and 5.20 in Radio Astronomy 4th edition - Burke
 | 
						|
    double lambda = Astronomy::m_speedOfLight / (double)fft->m_centerFrequency;
 | 
						|
    return 2.0 * Astronomy::m_boltzmann * Ta * fft->m_omegaA / (lambda * lambda);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFFTTotalTemperature(FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    if (fft->m_temp)
 | 
						|
    {
 | 
						|
        double tempSum = 0.0;
 | 
						|
        for (int i = 0; i < fft->m_fftSize; i++) {
 | 
						|
            tempSum += fft->m_temp[i];
 | 
						|
        }
 | 
						|
 | 
						|
        // Convert from temperature to power in Watts and dBm
 | 
						|
        Real bw = fft->m_sampleRate/(Real)fft->m_fftSize;
 | 
						|
        fft->m_totalPowerWatts = Astronomy::m_boltzmann * tempSum * bw;
 | 
						|
        fft->m_totalPowerdBm = Astronomy::noisePowerdBm(tempSum, bw);
 | 
						|
        fft->m_tSys = tempSum/fft->m_fftSize;
 | 
						|
 | 
						|
        // Esimate source temperature
 | 
						|
        fft->m_tSource = calcTSource(fft);
 | 
						|
 | 
						|
        // Calculate error due to thermal noise and gain variation
 | 
						|
        fft->m_sigmaT = calcSigmaT(fft);
 | 
						|
        fft->m_sigmaS = calcSigmaS(fft);
 | 
						|
 | 
						|
        // Calculate spectral flux density of source
 | 
						|
        fft->m_flux = calcFlux(fft->m_tSource, fft);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updatePowerColumns(int row, FFTMeasurement* fft)
 | 
						|
{
 | 
						|
    ui->powerTable->item(row, POWER_COL_TSYS)->setData(Qt::DisplayRole, fft->m_tSys);
 | 
						|
    ui->powerTable->item(row, POWER_COL_TSYS0)->setData(Qt::DisplayRole, fft->m_tSys0);
 | 
						|
    ui->powerTable->item(row, POWER_COL_TSOURCE)->setData(Qt::DisplayRole, fft->m_tSource);
 | 
						|
    if (m_settings.m_sourceType != RadioAstronomySettings::UNKNOWN) {
 | 
						|
        ui->powerTable->item(row, POWER_COL_TB)->setData(Qt::DisplayRole, fft->m_tSource/beamFillingFactor());
 | 
						|
    } else {
 | 
						|
        ui->powerTable->item(row, POWER_COL_TB)->setText("");
 | 
						|
    }
 | 
						|
    ui->powerTable->item(row, POWER_COL_FLUX)->setData(Qt::DisplayRole, Units::wattsPerMetrePerHertzToJansky(fft->m_flux));
 | 
						|
    ui->powerTable->item(row, POWER_COL_SIGMA_T)->setData(Qt::DisplayRole, fft->m_sigmaT);
 | 
						|
    ui->powerTable->item(row, POWER_COL_SIGMA_S)->setData(Qt::DisplayRole, fft->m_sigmaS);
 | 
						|
    ui->powerTable->item(row, POWER_COL_OMEGA_A)->setData(Qt::DisplayRole, fft->m_omegaA);
 | 
						|
    ui->powerTable->item(row, POWER_COL_OMEGA_S)->setData(Qt::DisplayRole, fft->m_omegaS);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::addFFT(FFTMeasurement *fft, bool skipCalcs)
 | 
						|
{
 | 
						|
    m_fftMeasurements.append(fft);
 | 
						|
 | 
						|
    powerMeasurementReceived(fft, skipCalcs); // Call before ui->spectrumIndex->setValue, so table row is valid
 | 
						|
    update2DImage(fft, skipCalcs);
 | 
						|
 | 
						|
    ui->spectrumIndex->setRange(0, m_fftMeasurements.size() - 1);
 | 
						|
    if ((ui->spectrumIndex->value() == m_fftMeasurements.size() - 2) || (m_fftMeasurements.size() == 1)) {
 | 
						|
        ui->spectrumIndex->setValue(m_fftMeasurements.size() - 1);
 | 
						|
    }
 | 
						|
    if ( m_fftMeasurements.size() == 1)
 | 
						|
    {
 | 
						|
        // Force drawing for first measurement
 | 
						|
        on_spectrumIndex_valueChanged(0);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::getRotatorData(FFTMeasurement *fft)
 | 
						|
{
 | 
						|
    unsigned int rotatorFeatureSetIndex, rotatorFeatureIndex;
 | 
						|
 | 
						|
    if (MainCore::getFeatureIndexFromId(m_settings.m_rotator, rotatorFeatureSetIndex, rotatorFeatureIndex))
 | 
						|
    {
 | 
						|
        SWGSDRangel::SWGFeatureReport featureReport;
 | 
						|
        double value;
 | 
						|
        qDebug() << m_settings.m_rotator << rotatorFeatureSetIndex << rotatorFeatureIndex;
 | 
						|
 | 
						|
        if (ChannelWebAPIUtils::getFeatureReport(rotatorFeatureSetIndex, rotatorFeatureIndex, featureReport))
 | 
						|
        {
 | 
						|
            QJsonObject *jsonObj = featureReport.asJsonObject();
 | 
						|
            qDebug() << *jsonObj;
 | 
						|
            if (WebAPIUtils::getSubObjectDouble(*jsonObj, "currentAzimuth", value)) {
 | 
						|
                fft->m_rotAz = value;
 | 
						|
            } else {
 | 
						|
                qDebug() << "RadioAstronomyGUI::getRotatorData: getSubObjectDouble currentAzimuth failed";
 | 
						|
            }
 | 
						|
            if (WebAPIUtils::getSubObjectDouble(*jsonObj, "currentElevation", value)) {
 | 
						|
                fft->m_rotEl = value;
 | 
						|
            } else {
 | 
						|
                qDebug() << "RadioAstronomyGUI::getRotatorData: getSubObjectDouble currentElevation failed";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RadioAstronomyGUI::getRotatorData: getFeatureReport failed";
 | 
						|
        }
 | 
						|
 | 
						|
        SWGSDRangel::SWGFeatureSettings featureSettingsResponse;
 | 
						|
        Feature *feature;
 | 
						|
        if (ChannelWebAPIUtils::getFeatureSettings(rotatorFeatureSetIndex, rotatorFeatureIndex, featureSettingsResponse, feature))
 | 
						|
        {
 | 
						|
            QJsonObject *jsonObj = featureSettingsResponse.asJsonObject();
 | 
						|
            qDebug() << *jsonObj;
 | 
						|
            if (WebAPIUtils::getSubObjectDouble(*jsonObj, "azimuthOffset", value)) {
 | 
						|
                fft->m_rotAzOff = value;
 | 
						|
            } else {
 | 
						|
                qDebug() << "RadioAstronomyGUI::getRotatorData: getSubObjectDouble azimuthOffset failed";
 | 
						|
            }
 | 
						|
            if (WebAPIUtils::getSubObjectDouble(*jsonObj, "elevationOffset", value)) {
 | 
						|
                fft->m_rotElOff = value;
 | 
						|
            } else {
 | 
						|
                qDebug() << "RadioAstronomyGUI::getRotatorData: getSubObjectDouble elevationOffset ";
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RadioAstronomyGUI::getRotatorData: getFeatureSettings failed";
 | 
						|
        }
 | 
						|
 | 
						|
        fft->m_rotValid = true;
 | 
						|
    }
 | 
						|
    else
 | 
						|
        qDebug() << "Couldn't parse rotator feature " << m_settings.m_rotator;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::fftMeasurementReceived(const RadioAstronomy::MsgFFTMeasurement& measurement)
 | 
						|
{
 | 
						|
    FFTMeasurement *fft = new FFTMeasurement();
 | 
						|
    fft->m_fftData = measurement.getFFT();
 | 
						|
    fft->m_fftSize = measurement.getSize();
 | 
						|
    fft->m_dateTime = measurement.getDateTime();
 | 
						|
    fft->m_centerFrequency = m_centerFrequency;
 | 
						|
    fft->m_sampleRate = m_settings.m_sampleRate;
 | 
						|
    fft->m_integration = m_settings.m_integration;
 | 
						|
    fft->m_rfBandwidth = m_settings.m_rfBandwidth;
 | 
						|
    fft->m_omegaA = calcOmegaA();
 | 
						|
    fft->m_omegaS = calcOmegaS();
 | 
						|
    fft->m_coordsValid = m_coordsValid;
 | 
						|
    fft->m_ra = m_ra;
 | 
						|
    fft->m_dec = m_dec;
 | 
						|
    fft->m_azimuth = m_azimuth;
 | 
						|
    fft->m_elevation = m_elevation;
 | 
						|
    fft->m_l = m_l;
 | 
						|
    fft->m_b = m_b;
 | 
						|
    fft->m_vBCRS = m_vBCRS;
 | 
						|
    fft->m_vLSR = m_vLSR;
 | 
						|
    fft->m_solarFlux = m_solarFlux;
 | 
						|
    fft->m_airTemp = m_airTemps.lastValue();
 | 
						|
    fft->m_skyTemp = m_skyTemp;
 | 
						|
    for (int i = 0; i < RADIOASTRONOMY_SENSORS; i++) {
 | 
						|
        fft->m_sensor[i] = m_sensors[i].lastValue();
 | 
						|
    }
 | 
						|
    fft->m_db = new Real[fft->m_fftSize];
 | 
						|
    fft->m_sweepIndex = m_sweepIndex++;
 | 
						|
    fft->m_tSys0 = calcTSys0();
 | 
						|
    fft->m_baseline = m_settings.m_spectrumBaseline;
 | 
						|
    getRotatorData(fft);
 | 
						|
 | 
						|
    calcFFTPower(fft);
 | 
						|
    calcFFTTotalPower(fft);
 | 
						|
    calcFFTTemperatures(fft);
 | 
						|
    calcFFTTotalTemperature(fft);
 | 
						|
    addFFT(fft);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumIndex_valueChanged(int value)
 | 
						|
{
 | 
						|
    if (value < m_fftMeasurements.size())
 | 
						|
    {
 | 
						|
        plotFFTMeasurement(value);
 | 
						|
 | 
						|
        // Highlight in table
 | 
						|
        ui->powerTable->selectRow(value);
 | 
						|
        ui->powerTable->scrollTo(ui->powerTable->model()->index(value, 0));
 | 
						|
        ui->spectrumDateTime->setDateTime(m_fftMeasurements[value]->m_dateTime);
 | 
						|
 | 
						|
        // Display target in Star Tracker
 | 
						|
        QList<ObjectPipe*> starTrackerPipes;
 | 
						|
        MainCore::instance()->getMessagePipes().getMessagePipes(m_radioAstronomy, "startracker.display", starTrackerPipes);
 | 
						|
 | 
						|
        for (const auto& pipe : starTrackerPipes)
 | 
						|
        {
 | 
						|
            MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
 | 
						|
            SWGSDRangel::SWGStarTrackerDisplaySettings *swgSettings = new SWGSDRangel::SWGStarTrackerDisplaySettings();
 | 
						|
            swgSettings->setDateTime(new QString(m_fftMeasurements[value]->m_dateTime.toString(Qt::ISODateWithMs)));
 | 
						|
            swgSettings->setAzimuth(m_fftMeasurements[value]->m_azimuth);
 | 
						|
            swgSettings->setElevation(m_fftMeasurements[value]->m_elevation);
 | 
						|
            messageQueue->push(MainCore::MsgStarTrackerDisplaySettings::create(m_radioAstronomy, swgSettings));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotSpectrum()
 | 
						|
{
 | 
						|
    QChart *oldChart = m_fftChart;
 | 
						|
 | 
						|
    m_fftChart = new QChart();
 | 
						|
 | 
						|
    m_fftChart->layout()->setContentsMargins(0, 0, 0, 0);
 | 
						|
    m_fftChart->setMargins(QMargins(1, 1, 1, 1));
 | 
						|
    m_fftChart->setTheme(QChart::ChartThemeDark);
 | 
						|
 | 
						|
    m_fftChart->legend()->setAlignment(Qt::AlignRight);
 | 
						|
    m_fftChart->legend()->setVisible(m_settings.m_spectrumLegend);
 | 
						|
 | 
						|
    m_fftSeries = new QLineSeries();
 | 
						|
    m_fftSeries->setName("Measurement");
 | 
						|
    connect(m_fftSeries, &QXYSeries::clicked, this, &RadioAstronomyGUI::spectrumSeries_clicked);
 | 
						|
 | 
						|
    // Plot vertical reference spectral line
 | 
						|
    m_fftHlineSeries = new QLineSeries();
 | 
						|
    m_fftHlineSeries->setName(QString("%1 line").arg(ui->spectrumLine->currentText()));
 | 
						|
    m_fftHlineSeries->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
 | 
						|
    // Plot peak info
 | 
						|
    m_fftPeakSeries = new QScatterSeries();
 | 
						|
    m_fftPeakSeries->setPointLabelsVisible(true);
 | 
						|
    m_fftPeakSeries->setMarkerSize(5);
 | 
						|
    m_fftPeakSeries->setName("Max");
 | 
						|
 | 
						|
    // Markers
 | 
						|
    m_fftMarkerSeries = new QScatterSeries();
 | 
						|
    m_fftMarkerSeries->setPointLabelsVisible(true);
 | 
						|
    m_fftMarkerSeries->setMarkerSize(5);
 | 
						|
    m_fftMarkerSeries->setName("Markers");
 | 
						|
 | 
						|
    // Gaussian
 | 
						|
    m_fftGaussianSeries = new QLineSeries();
 | 
						|
    m_fftGaussianSeries->setName("Gaussian fit");
 | 
						|
    m_fftGaussianSeries->setVisible(m_settings.m_spectrumTemp);
 | 
						|
 | 
						|
    m_fftLABSeries  = new QLineSeries();
 | 
						|
    m_fftLABSeries->setName("LAB reference");
 | 
						|
    m_fftLABSeries->setVisible(m_settings.m_spectrumLAB);
 | 
						|
 | 
						|
    m_fftXAxis = new QValueAxis();
 | 
						|
    m_fftYAxis = new QValueAxis();
 | 
						|
    m_fftDopplerAxis = new QValueAxis();
 | 
						|
 | 
						|
    m_fftChart->addAxis(m_fftXAxis, Qt::AlignBottom);
 | 
						|
    m_fftChart->addAxis(m_fftYAxis, Qt::AlignLeft);
 | 
						|
    m_fftChart->addAxis(m_fftDopplerAxis, Qt::AlignTop);
 | 
						|
 | 
						|
    m_fftXAxis->setTitleText("Frequency (MHz)");
 | 
						|
    calcSpectrumChartTickCount(m_fftXAxis, size().width());
 | 
						|
    calcSpectrumChartTickCount(m_fftDopplerAxis, size().width());
 | 
						|
    m_fftYAxis->setTitleText("Power");
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftSeries);
 | 
						|
    m_fftSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftHlineSeries);
 | 
						|
    //m_fftHlineSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftHlineSeries->attachAxis(m_fftDopplerAxis);
 | 
						|
    m_fftHlineSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftGaussianSeries);
 | 
						|
    m_fftGaussianSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftGaussianSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftLABSeries);
 | 
						|
    //m_fftLABSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftLABSeries->attachAxis(m_fftDopplerAxis);
 | 
						|
    m_fftLABSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftPeakSeries);
 | 
						|
    m_fftPeakSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftPeakSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    m_fftChart->addSeries(m_fftMarkerSeries);
 | 
						|
    m_fftMarkerSeries->attachAxis(m_fftXAxis);
 | 
						|
    m_fftMarkerSeries->attachAxis(m_fftYAxis);
 | 
						|
 | 
						|
    // Don't have peaks and markers in legend
 | 
						|
    m_fftChart->legend()->markers(m_fftPeakSeries)[0]->setVisible(false);
 | 
						|
    m_fftChart->legend()->markers(m_fftMarkerSeries)[0]->setVisible(false);
 | 
						|
 | 
						|
    ui->spectrumChart->setChart(m_fftChart);
 | 
						|
 | 
						|
    delete oldChart;
 | 
						|
}
 | 
						|
 | 
						|
// Calculate galactic background temperature based on center frequency
 | 
						|
void RadioAstronomyGUI::calcGalacticBackgroundTemp()
 | 
						|
{
 | 
						|
    // https://arxiv.org/ftp/arxiv/papers/1912/1912.12699.pdf - page 6
 | 
						|
    // 17.1, 25.2 and 54.8 K for the 10th, 50th and 90th percentile of the all-sky distribution
 | 
						|
    // See also ITU-R P.372-7 section 6
 | 
						|
    // If this is used for cold calibration, we don't want to use the higher value
 | 
						|
    double temp = 25.2 * std::pow(m_centerFrequency/408000000.0, -2.75);
 | 
						|
    ui->tempGal->setValue(temp);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate athmospheric noise temperature based on air temperature, zenith opacity and elevation
 | 
						|
void RadioAstronomyGUI::calcAtmosphericTemp()
 | 
						|
{
 | 
						|
    float el = m_settings.m_elevation;
 | 
						|
    if (m_settings.m_elevation < 1.0f) {
 | 
						|
        el = 1.0f; // Avoid divide by 0 and limit max value to match ITU-R P.372-7 figure 5
 | 
						|
    }
 | 
						|
    double temp = Units::celsiusToKelvin(m_settings.m_tempAir) * (1.0 - std::exp(-m_settings.m_zenithOpacity/cos(Units::degreesToRadians(90.0f - el))));
 | 
						|
    ui->tempAtm->setValue(temp);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempRXSelect_currentIndexChanged(int value)
 | 
						|
{
 | 
						|
    if (value == 0)
 | 
						|
    {
 | 
						|
        // T_RX
 | 
						|
        ui->tempRX->setValue(m_settings.m_tempRX);
 | 
						|
        ui->tempRXUnitsLabel->setText("K");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // NF
 | 
						|
        ui->tempRX->setValue(Units::noiseTempToNoiseFigureTo(m_settings.m_tempRX));
 | 
						|
        ui->tempRXUnitsLabel->setText("dB");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempRX_valueChanged(double value)
 | 
						|
{
 | 
						|
    if (ui->tempRXSelect->currentIndex() == 0) {
 | 
						|
        m_settings.m_tempRX = value;
 | 
						|
    } else {
 | 
						|
        m_settings.m_tempRX = Units::noiseFigureToNoiseTemp(value);
 | 
						|
    }
 | 
						|
    updateTSys0();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempCMB_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_tempCMB = value;
 | 
						|
    updateTSys0();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempGal_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_tempGal = value;
 | 
						|
    updateTSys0();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempSP_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_tempSP = value;
 | 
						|
    updateTSys0();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempAtm_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_tempAtm = value;
 | 
						|
    updateTSys0();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempAir_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_tempAir = value;
 | 
						|
    if (m_settings.m_tempAtmLink) {
 | 
						|
        calcAtmosphericTemp();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_zenithOpacity_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_zenithOpacity = value;
 | 
						|
    if (m_settings.m_tempAtmLink) {
 | 
						|
        calcAtmosphericTemp();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_elevation_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_elevation = value;
 | 
						|
    if (m_settings.m_tempAtmLink) {
 | 
						|
        calcAtmosphericTemp();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_elevationLink_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_elevationLink = checked;
 | 
						|
    ui->elevation->setValue(m_elevation);
 | 
						|
    ui->elevation->setEnabled(!m_settings.m_elevationLink);
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempAtmLink_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_tempAtmLink = checked;
 | 
						|
    ui->tempAtm->setEnabled(!m_settings.m_tempAtmLink);
 | 
						|
    if (checked) {
 | 
						|
        calcAtmosphericTemp();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempAirLink_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_tempAirLink = checked;
 | 
						|
    ui->tempAir->setEnabled(!m_settings.m_tempAirLink);
 | 
						|
    if (checked)
 | 
						|
    {
 | 
						|
        ui->tempAir->setValue(m_airTemps.lastValue());
 | 
						|
        calcAtmosphericTemp();
 | 
						|
    }
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tempGalLink_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_tempGalLink = checked;
 | 
						|
    if (checked) {
 | 
						|
        calcGalacticBackgroundTemp();
 | 
						|
    }
 | 
						|
    ui->tempGal->setEnabled(!m_settings.m_tempGalLink);
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tCalHotSelect_currentIndexChanged(int value)
 | 
						|
{
 | 
						|
    if (value == 0)
 | 
						|
    {
 | 
						|
        // Thot
 | 
						|
        ui->tCalHot->setValue(m_settings.m_tCalHot);
 | 
						|
        ui->tCalHotUnitsLabel->setText("K");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Phot
 | 
						|
        double power = Astronomy::noisePowerdBm(m_settings.m_tCalHot, m_settings.m_sampleRate);
 | 
						|
        ui->tCalHot->setValue(power);
 | 
						|
        ui->tCalHotUnitsLabel->setText("dBm");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tCalHot_valueChanged(double value)
 | 
						|
{
 | 
						|
    double temp;
 | 
						|
    if (ui->tCalHotSelect->currentIndex() == 0) {
 | 
						|
        temp = value;
 | 
						|
    } else {
 | 
						|
        temp = Astronomy::noiseTemp(value, m_settings.m_sampleRate);
 | 
						|
    }
 | 
						|
    m_settings.m_tCalHot = (float)temp;
 | 
						|
    calibrate();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tCalColdSelect_currentIndexChanged(int value)
 | 
						|
{
 | 
						|
    if (value == 0)
 | 
						|
    {
 | 
						|
        // Tcold
 | 
						|
        ui->tCalCold->setValue(m_settings.m_tCalCold);
 | 
						|
        ui->tCalColdUnitsLabel->setText("K");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Pcold
 | 
						|
        double power = Astronomy::noisePowerdBm(m_settings.m_tCalCold, m_settings.m_sampleRate);
 | 
						|
        ui->tCalCold->setValue(power);
 | 
						|
        ui->tCalColdUnitsLabel->setText("dBm");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_tCalCold_valueChanged(double value)
 | 
						|
{
 | 
						|
    double temp;
 | 
						|
    if (ui->tCalColdSelect->currentIndex() == 0) {
 | 
						|
        temp = value;
 | 
						|
    } else {
 | 
						|
        temp = Astronomy::noiseTemp(value, m_settings.m_sampleRate);
 | 
						|
    }
 | 
						|
    m_settings.m_tCalCold = (float)temp;
 | 
						|
    calibrate();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumLine_currentIndexChanged(int value)
 | 
						|
{
 | 
						|
    m_settings.m_line = (RadioAstronomySettings::Line)value;
 | 
						|
    displaySpectrumLineFrequency();
 | 
						|
    plotFFTMeasurement();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::displaySpectrumLineFrequency()
 | 
						|
{
 | 
						|
    switch (m_settings.m_line)
 | 
						|
    {
 | 
						|
    case RadioAstronomySettings::HI:
 | 
						|
        ui->spectrumLineFrequency->setValue(Astronomy::m_hydrogenLineFrequency / 1e6);
 | 
						|
        ui->spectrumLineFrequency->setEnabled(false);
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::OH:
 | 
						|
        ui->spectrumLineFrequency->setValue(Astronomy::m_hydroxylLineFrequency / 1e6);
 | 
						|
        ui->spectrumLineFrequency->setEnabled(false);
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::DI:
 | 
						|
        ui->spectrumLineFrequency->setValue(Astronomy::m_deuteriumLineFrequency / 1e6);
 | 
						|
        ui->spectrumLineFrequency->setEnabled(false);
 | 
						|
        break;
 | 
						|
    case RadioAstronomySettings::CUSTOM_LINE:
 | 
						|
        ui->spectrumLineFrequency->setValue(m_settings.m_lineCustomFrequency / 1e6);
 | 
						|
        ui->spectrumLineFrequency->setEnabled(true);
 | 
						|
        break;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumLineFrequency_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_lineCustomFrequency = value * 1e6;
 | 
						|
    plotFFTMeasurement();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_refFrame_currentIndexChanged(int value)
 | 
						|
{
 | 
						|
    m_settings.m_refFrame = (RadioAstronomySettings::RefFrame)value;
 | 
						|
    plotFFTMeasurement();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sunDistanceToGC_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sunDistanceToGC = value;
 | 
						|
    applySettings();
 | 
						|
    calcDistances();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sunOrbitalVelocity_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sunOrbitalVelocity = value;
 | 
						|
    applySettings();
 | 
						|
    calcDistances();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_savePowerChartImage_clicked()
 | 
						|
{
 | 
						|
    QFileDialog fileDialog(nullptr, "Select file to save image to", "", "*.png *.jpg *.jpeg *.bmp *.ppm *.xbm *.xpm");
 | 
						|
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
 | 
						|
    if (fileDialog.exec())
 | 
						|
    {
 | 
						|
        QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
        if (fileNames.size() > 0)
 | 
						|
        {
 | 
						|
            QImage image(ui->powerChart->size(), QImage::Format_ARGB32);
 | 
						|
            image.fill(Qt::transparent);
 | 
						|
            QPainter painter(&image);
 | 
						|
            ui->powerChart->render(&painter);
 | 
						|
            if (!image.save(fileNames[0])) {
 | 
						|
                QMessageBox::critical(this, "Radio Astronomy", QString("Failed to save image to %1").arg(fileNames[0]));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_saveSpectrumChartImage_clicked()
 | 
						|
{
 | 
						|
    QFileDialog fileDialog(nullptr, "Select file to save image to", "", "*.png *.jpg *.jpeg *.bmp *.ppm *.xbm *.xpm");
 | 
						|
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
 | 
						|
    if (fileDialog.exec())
 | 
						|
    {
 | 
						|
        QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
        if (fileNames.size() > 0)
 | 
						|
        {
 | 
						|
            QImage image(ui->spectrumChart->size(), QImage::Format_ARGB32);
 | 
						|
            image.fill(Qt::transparent);
 | 
						|
            QPainter painter(&image);
 | 
						|
            ui->spectrumChart->render(&painter);
 | 
						|
            if (!image.save(fileNames[0])) {
 | 
						|
                QMessageBox::critical(this, "Radio Astronomy", QString("Failed to save image to %1").arg(fileNames[0]));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_saveSpectrumChartImages_clicked()
 | 
						|
{
 | 
						|
    if (m_fftMeasurements.size() > 1)
 | 
						|
    {
 | 
						|
        // Get filename of animation file
 | 
						|
        QFileDialog fileDialog(nullptr, "Select file to save animation to", "", "*.png");
 | 
						|
        fileDialog.setAcceptMode(QFileDialog::AcceptSave);
 | 
						|
        if (fileDialog.exec())
 | 
						|
        {
 | 
						|
            QStringList fileNames = fileDialog.selectedFiles();
 | 
						|
            if (fileNames.size() > 0)
 | 
						|
            {
 | 
						|
                // Create animation file
 | 
						|
                APNG apng(m_fftMeasurements.size());
 | 
						|
 | 
						|
                // Plot each FFT to a temp .png file (in memory) then append to animation file
 | 
						|
                for (int i = 0; i < m_fftMeasurements.size(); i++)
 | 
						|
                {
 | 
						|
                    plotFFTMeasurement(i);
 | 
						|
                    QApplication::processEvents(); // To get chart title to be updated
 | 
						|
                    QImage image(ui->spectrumChart->size(), QImage::Format_ARGB32);
 | 
						|
                    image.fill(Qt::transparent);
 | 
						|
                    QPainter painter(&image);
 | 
						|
                    ui->spectrumChart->render(&painter);
 | 
						|
                    apng.addImage(image);
 | 
						|
                }
 | 
						|
                if (!apng.save(fileNames[0])) {
 | 
						|
                    QMessageBox::critical(this, "Radio Astronomy", QString("Failed to write to file %1").arg(fileNames[0]));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumReverseXAxis_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumReverseXAxis = checked;
 | 
						|
    applySettings();
 | 
						|
    if (ui->spectrumChartSelect->currentIndex() == 0) {
 | 
						|
        plotFFTMeasurement();
 | 
						|
    } else {
 | 
						|
        m_calXAxis->setReverse(m_settings.m_spectrumReverseXAxis);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowPeak_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerPeaks = checked;
 | 
						|
    updatePowerMarkerTableVisibility();
 | 
						|
    applySettings();
 | 
						|
    if (m_powerPeakSeries)
 | 
						|
    {
 | 
						|
        m_powerPeakSeries->setVisible(checked);
 | 
						|
        if (checked) {
 | 
						|
            m_powerChart->legend()->markers(m_powerPeakSeries)[0]->setVisible(false);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumPeak_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumPeaks = checked;
 | 
						|
    updateSpectrumMarkerTableVisibility();
 | 
						|
    plotFFTMeasurement();
 | 
						|
    applySettings();
 | 
						|
    if (m_fftChart)
 | 
						|
    {
 | 
						|
        if (checked) {
 | 
						|
            m_fftChart->legend()->markers(m_fftPeakSeries)[0]->setVisible(false);
 | 
						|
            showLoSMarker("Max");
 | 
						|
        } else {
 | 
						|
            clearLoSMarker("Max");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowMarker_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerMarkers = checked;
 | 
						|
    updatePowerMarkerTableVisibility();
 | 
						|
    applySettings();
 | 
						|
    if (m_powerMarkerSeries)
 | 
						|
    {
 | 
						|
        m_powerMarkerSeries->setVisible(checked);
 | 
						|
        if (checked) {
 | 
						|
            m_powerChart->legend()->markers(m_powerMarkerSeries)[0]->setVisible(false);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    updatePowerSelect();
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowAvg_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerAvg = checked;
 | 
						|
    applySettings();
 | 
						|
    ui->powerChartAvgWidgets->setVisible(checked);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
    if (checked) {
 | 
						|
        calcAverages();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowLegend_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerLegend = checked;
 | 
						|
    applySettings();
 | 
						|
    if (m_powerChart)
 | 
						|
    {
 | 
						|
        if (checked) {
 | 
						|
            m_powerChart->legend()->show();
 | 
						|
        } else {
 | 
						|
            m_powerChart->legend()->hide();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowTsys0_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerShowTsys0 = checked;
 | 
						|
    applySettings();
 | 
						|
    if (m_powerTsys0Series) {
 | 
						|
        m_powerTsys0Series->setVisible(checked);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowAirTemp_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerShowAirTemp = checked;
 | 
						|
    applySettings();
 | 
						|
    m_airTemps.clicked(checked);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowSensor1_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_sensorVisible[0] = checked;
 | 
						|
    applySettings();
 | 
						|
    m_sensors[0].clicked(checked);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowSensor2_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_sensorVisible[1] = checked;
 | 
						|
    applySettings();
 | 
						|
    m_sensors[1].clicked(checked);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updatePowerMarkerTableVisibility()
 | 
						|
{
 | 
						|
    ui->powerMarkerTableWidgets->setVisible(m_settings.m_powerPeaks || m_settings.m_powerMarkers);
 | 
						|
    if (m_settings.m_powerPeaks)
 | 
						|
    {
 | 
						|
        ui->powerMarkerTable->showRow(POWER_MARKER_ROW_PEAK_MAX);
 | 
						|
        ui->powerMarkerTable->showRow(POWER_MARKER_ROW_PEAK_MIN);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->powerMarkerTable->hideRow(POWER_MARKER_ROW_PEAK_MAX);
 | 
						|
        ui->powerMarkerTable->hideRow(POWER_MARKER_ROW_PEAK_MIN);
 | 
						|
    }
 | 
						|
    if (m_settings.m_powerMarkers)
 | 
						|
    {
 | 
						|
        ui->powerMarkerTable->showRow(POWER_MARKER_ROW_M1);
 | 
						|
        ui->powerMarkerTable->showRow(POWER_MARKER_ROW_M2);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->powerMarkerTable->hideRow(POWER_MARKER_ROW_M1);
 | 
						|
        ui->powerMarkerTable->hideRow(POWER_MARKER_ROW_M2);
 | 
						|
    }
 | 
						|
    ui->powerMarkerTableWidgets->updateGeometry(); // Without this, widgets aren't resized properly
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateSpectrumMarkerTableVisibility()
 | 
						|
{
 | 
						|
    bool fft = ui->spectrumChartSelect->currentIndex() == 0;
 | 
						|
    ui->spectrumMarkerTableWidgets->setVisible(fft && (m_settings.m_spectrumPeaks || m_settings.m_spectrumMarkers));
 | 
						|
    if (m_settings.m_spectrumPeaks)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->showRow(SPECTRUM_MARKER_ROW_PEAK);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->hideRow(SPECTRUM_MARKER_ROW_PEAK);
 | 
						|
    }
 | 
						|
    if (m_settings.m_spectrumMarkers)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->showRow(SPECTRUM_MARKER_ROW_M1);
 | 
						|
        ui->spectrumMarkerTable->showRow(SPECTRUM_MARKER_ROW_M2);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->hideRow(SPECTRUM_MARKER_ROW_M1);
 | 
						|
        ui->spectrumMarkerTable->hideRow(SPECTRUM_MARKER_ROW_M2);
 | 
						|
    }
 | 
						|
    ui->spectrumMarkerTableWidgets->updateGeometry(); // Without this, widgets aren't resized properly
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumMarker_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumMarkers = checked;
 | 
						|
    applySettings();
 | 
						|
    updateSpectrumMarkerTableVisibility();
 | 
						|
    m_fftMarkerSeries->setVisible(checked);
 | 
						|
    if (checked)
 | 
						|
    {
 | 
						|
        m_fftChart->legend()->markers(m_fftMarkerSeries)[0]->setVisible(false);
 | 
						|
        showLoSMarker("M1");
 | 
						|
        showLoSMarker("M2");
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        clearLoSMarker("M1");
 | 
						|
        clearLoSMarker("M2");
 | 
						|
    }
 | 
						|
    updateSpectrumSelect();
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumTemp_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumTemp = checked;
 | 
						|
    applySettings();
 | 
						|
    ui->spectrumGaussianWidgets->setVisible(checked);
 | 
						|
    m_fftGaussianSeries->setVisible(checked);
 | 
						|
    updateSpectrumSelect();
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumShowLegend_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumLegend = checked;
 | 
						|
    applySettings();
 | 
						|
    if (m_fftChart)
 | 
						|
    {
 | 
						|
        m_fftChart->legend()->setVisible(checked);
 | 
						|
        m_calChart->legend()->setVisible(checked);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumShowRefLine_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumRefLine = checked;
 | 
						|
    applySettings();
 | 
						|
    ui->spectrumRefLineWidgets->setVisible(checked);
 | 
						|
    if (m_fftHlineSeries)
 | 
						|
    {
 | 
						|
        m_fftHlineSeries->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
        m_fftDopplerAxis->setVisible(m_settings.m_spectrumRefLine);
 | 
						|
    }
 | 
						|
    updateDistanceColumns();
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumShowLAB_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumLAB = checked;
 | 
						|
    applySettings();
 | 
						|
    m_fftLABSeries->setVisible(m_settings.m_spectrumLAB);
 | 
						|
    if (m_settings.m_spectrumLAB) {
 | 
						|
        plotLAB(); // Replot incase data needs to be downloaded
 | 
						|
    }
 | 
						|
    spectrumAutoscale();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateDistanceColumns()
 | 
						|
{
 | 
						|
    if (m_settings.m_spectrumDistance && m_settings.m_spectrumRefLine)
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->showColumn(SPECTRUM_MARKER_COL_R);
 | 
						|
        ui->spectrumMarkerTable->showColumn(SPECTRUM_MARKER_COL_D);
 | 
						|
        ui->spectrumMarkerTable->showColumn(SPECTRUM_MARKER_COL_PLOT_MAX);
 | 
						|
        ui->spectrumMarkerTable->showColumn(SPECTRUM_MARKER_COL_R_MIN);
 | 
						|
        ui->spectrumMarkerTable->showColumn(SPECTRUM_MARKER_COL_V);
 | 
						|
        showLoSMarker("Max");
 | 
						|
        showLoSMarker("M1");
 | 
						|
        showLoSMarker("M2");
 | 
						|
        ui->sunDistanceToGCLine->setVisible(true);
 | 
						|
        ui->sunDistanceToGCLabel->setVisible(true);
 | 
						|
        ui->sunDistanceToGC->setVisible(true);
 | 
						|
        ui->sunDistanceToGCUnits->setVisible(true);
 | 
						|
        ui->sunOrbitalVelocityLine->setVisible(true);
 | 
						|
        ui->sunOrbitalVelocityLabel->setVisible(true);
 | 
						|
        ui->sunOrbitalVelocity->setVisible(true);
 | 
						|
        ui->sunOrbitalVelocityUnits->setVisible(true);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        ui->spectrumMarkerTable->hideColumn(SPECTRUM_MARKER_COL_R);
 | 
						|
        ui->spectrumMarkerTable->hideColumn(SPECTRUM_MARKER_COL_D);
 | 
						|
        ui->spectrumMarkerTable->hideColumn(SPECTRUM_MARKER_COL_PLOT_MAX);
 | 
						|
        ui->spectrumMarkerTable->hideColumn(SPECTRUM_MARKER_COL_R_MIN);
 | 
						|
        ui->spectrumMarkerTable->hideColumn(SPECTRUM_MARKER_COL_V);
 | 
						|
        clearLoSMarker("Max");
 | 
						|
        clearLoSMarker("M1");
 | 
						|
        clearLoSMarker("M2");
 | 
						|
        ui->sunDistanceToGCLine->setVisible(false);
 | 
						|
        ui->sunDistanceToGCLabel->setVisible(false);
 | 
						|
        ui->sunDistanceToGC->setVisible(false);
 | 
						|
        ui->sunDistanceToGCUnits->setVisible(false);
 | 
						|
        ui->sunOrbitalVelocityLine->setVisible(false);
 | 
						|
        ui->sunOrbitalVelocityLabel->setVisible(false);
 | 
						|
        ui->sunOrbitalVelocity->setVisible(false);
 | 
						|
        ui->sunOrbitalVelocityUnits->setVisible(false);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumShowDistance_toggled(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_spectrumDistance = checked;
 | 
						|
    applySettings();
 | 
						|
    if (m_settings.m_spectrumDistance && !m_settings.m_spectrumRefLine) {
 | 
						|
        ui->spectrumShowRefLine->setChecked(true);
 | 
						|
    }
 | 
						|
    updateDistanceColumns();
 | 
						|
}
 | 
						|
 | 
						|
// point isn't necessarily a point in the series - may be interpolated
 | 
						|
void RadioAstronomyGUI::powerSeries_clicked(const QPointF &point)
 | 
						|
{
 | 
						|
    QString selection = ui->powerSelect->currentText();
 | 
						|
    if (selection.startsWith("M"))
 | 
						|
    {
 | 
						|
        if (selection == "M1")
 | 
						|
        {
 | 
						|
            // Place marker 1
 | 
						|
            m_powerM1X = point.x();
 | 
						|
            m_powerM1Y = point.y();
 | 
						|
            if (m_powerM1Valid) {
 | 
						|
                m_powerMarkerSeries->replace(0, m_powerM1X, m_powerM1Y);
 | 
						|
            } else {
 | 
						|
                m_powerMarkerSeries->insert(0, QPointF(m_powerM1X, m_powerM1Y));
 | 
						|
            }
 | 
						|
            m_powerM1Valid = true;
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_powerM1X);
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M1, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M1, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M1, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerM1Y);
 | 
						|
            calcPowerMarkerDelta();
 | 
						|
        }
 | 
						|
        else if (selection == "M2")
 | 
						|
        {
 | 
						|
            // Place marker 2
 | 
						|
            m_powerM2X = point.x();
 | 
						|
            m_powerM2Y = point.y();
 | 
						|
            if (m_powerM2Valid) {
 | 
						|
                m_powerMarkerSeries->replace(1, m_powerM2X, m_powerM2Y);
 | 
						|
            } else {
 | 
						|
                m_powerMarkerSeries->insert(1, QPointF(m_powerM2X, m_powerM2Y));
 | 
						|
            }
 | 
						|
            m_powerM2Valid = true;
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(m_powerM2X);
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_DATE)->setData(Qt::DisplayRole, dt.date());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_TIME)->setData(Qt::DisplayRole, dt.time());
 | 
						|
            ui->powerMarkerTable->item(POWER_MARKER_ROW_M2, POWER_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_powerM2Y);
 | 
						|
            calcPowerMarkerDelta();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (selection == "Gaussian")
 | 
						|
    {
 | 
						|
        // Fit a Gaussian assuming point clicked is the peak
 | 
						|
        ui->powerGaussianCenter->setDateTime(QDateTime::fromMSecsSinceEpoch(point.x()));
 | 
						|
        // Calculate noise floor - take average of lowest 10%
 | 
						|
        qreal floor = calcSeriesFloor(m_powerSeries);
 | 
						|
        ui->powerGaussianFloor->setValue(floor);
 | 
						|
        // Set amplitude to achieve selected point
 | 
						|
        ui->powerGaussianAmp->setValue(point.y() - floor);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        if (m_fftMeasurements.size() > 1)
 | 
						|
        {
 | 
						|
            // Select row closest to clicked data
 | 
						|
            QDateTime dt = QDateTime::fromMSecsSinceEpoch(point.x());
 | 
						|
            int i = 0;
 | 
						|
            while ((i < m_fftMeasurements.size()) && (dt > m_fftMeasurements[i]->m_dateTime)) {
 | 
						|
                i++;
 | 
						|
            }
 | 
						|
            if (i < m_fftMeasurements.size()) {
 | 
						|
                ui->spectrumIndex->setValue(i);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
qreal RadioAstronomyGUI::calcSeriesFloor(QXYSeries *series, int percent)
 | 
						|
{
 | 
						|
    QList<qreal> minValues;
 | 
						|
    double count = series->count() * percent / 100.0;
 | 
						|
    for (int i = 0; i < series->count(); i++)
 | 
						|
    {
 | 
						|
        qreal y = series->at(i).y();
 | 
						|
        if (minValues.size() < count)
 | 
						|
        {
 | 
						|
            minValues.append(y);
 | 
						|
            std::sort(minValues.begin(), minValues.end());
 | 
						|
        }
 | 
						|
        else if (y < minValues.last())
 | 
						|
        {
 | 
						|
            minValues.append(y);
 | 
						|
            std::sort(minValues.begin(), minValues.end());
 | 
						|
        }
 | 
						|
    }
 | 
						|
    qreal sum = std::accumulate(minValues.begin(), minValues.end(), 0.0);
 | 
						|
    return sum / minValues.size();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateSpectrumSelect()
 | 
						|
{
 | 
						|
    ui->spectrumSelect->clear();
 | 
						|
    if (m_settings.m_spectrumMarkers)
 | 
						|
    {
 | 
						|
        ui->spectrumSelect->addItem("M1");
 | 
						|
        ui->spectrumSelect->addItem("M2");
 | 
						|
    }
 | 
						|
    if (m_settings.m_spectrumTemp)
 | 
						|
    {
 | 
						|
        ui->spectrumSelect->addItem("Gaussian");
 | 
						|
    }
 | 
						|
    bool visible = ui->spectrumSelect->count() != 0;
 | 
						|
    ui->spectrumSelectLabel->setVisible(visible);
 | 
						|
    ui->spectrumSelect->setVisible(visible);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updatePowerSelect()
 | 
						|
{
 | 
						|
    ui->powerSelect->clear();
 | 
						|
    if (m_settings.m_powerMarkers || m_settings.m_powerShowGaussian) {
 | 
						|
        ui->powerSelect->addItem("Row");
 | 
						|
    }
 | 
						|
    if (m_settings.m_powerMarkers)
 | 
						|
    {
 | 
						|
        ui->powerSelect->addItem("M1");
 | 
						|
        ui->powerSelect->addItem("M2");
 | 
						|
    }
 | 
						|
    if (m_settings.m_powerShowGaussian) {
 | 
						|
        ui->powerSelect->addItem("Gaussian");
 | 
						|
    }
 | 
						|
    bool visible = ui->powerSelect->count() != 0;
 | 
						|
    ui->powerSelectLabel->setVisible(visible);
 | 
						|
    ui->powerSelect->setVisible(visible);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::spectrumSeries_clicked(const QPointF &point)
 | 
						|
{
 | 
						|
    QString selection = ui->spectrumSelect->currentText();
 | 
						|
    if (selection.startsWith("M"))
 | 
						|
    {
 | 
						|
        FFTMeasurement *fft = currentFFT();
 | 
						|
        if (selection == "M1")
 | 
						|
        {
 | 
						|
            m_spectrumM1X = point.x();
 | 
						|
            m_spectrumM1Y = point.y();
 | 
						|
            m_spectrumM1Valid = true;
 | 
						|
            ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M1, SPECTRUM_MARKER_COL_FREQ)->setData(Qt::DisplayRole, m_spectrumM1X);
 | 
						|
            ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M1, SPECTRUM_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_spectrumM1Y);
 | 
						|
            calcVrAndDistanceToPeak(m_spectrumM1X*1e6, fft, SPECTRUM_MARKER_ROW_M1);
 | 
						|
        }
 | 
						|
        else if (selection == "M2")
 | 
						|
        {
 | 
						|
            m_spectrumM2X = point.x();
 | 
						|
            m_spectrumM2Y = point.y();
 | 
						|
            m_spectrumM2Valid = true;
 | 
						|
            ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_FREQ)->setData(Qt::DisplayRole, m_spectrumM2X);
 | 
						|
            ui->spectrumMarkerTable->item(SPECTRUM_MARKER_ROW_M2, SPECTRUM_MARKER_COL_VALUE)->setData(Qt::DisplayRole, m_spectrumM2Y);
 | 
						|
            calcVrAndDistanceToPeak(m_spectrumM2X*1e6, fft, SPECTRUM_MARKER_ROW_M2);
 | 
						|
        }
 | 
						|
        calcSpectrumMarkerDelta();
 | 
						|
 | 
						|
        m_fftMarkerSeries->clear();
 | 
						|
        if (m_spectrumM1Valid) {
 | 
						|
            m_fftMarkerSeries->append(m_spectrumM1X, m_spectrumM1Y);
 | 
						|
        }
 | 
						|
        if (m_spectrumM2Valid) {
 | 
						|
            m_fftMarkerSeries->append(m_spectrumM2X, m_spectrumM2Y);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else if (selection == "Gaussian")
 | 
						|
    {
 | 
						|
        ui->spectrumGaussianFreq->setValue(point.x());
 | 
						|
        // Calculate noise floor - take average of lowest 10%
 | 
						|
        qreal floor = calcSeriesFloor(m_fftSeries);
 | 
						|
        ui->spectrumGaussianFloor->setValue(floor);
 | 
						|
        // Set amplitude to achieve selected point
 | 
						|
        ui->spectrumGaussianAmp->setValue(point.y() - floor);
 | 
						|
        plotFFTMeasurement();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumGaussianFreq_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    calcFWHM();
 | 
						|
    plotFFTMeasurement();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumGaussianAmp_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    plotFFTMeasurement();
 | 
						|
    calcColumnDensity();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumGaussianFloor_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    plotFFTMeasurement();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumGaussianFWHM_valueChanged(double fFWHM)
 | 
						|
{
 | 
						|
    double c = Astronomy::m_speedOfLight;
 | 
						|
    double k = Astronomy::m_boltzmann;
 | 
						|
    double m = Astronomy::m_hydrogenMass;
 | 
						|
    double f0 = ui->spectrumGaussianFreq->value() * 1e6;
 | 
						|
    double vTurb = ui->spectrumGaussianTurb->value() * 1e3; // RSM turbulent velocties - Convert to m/s
 | 
						|
    const double bf = 2.0*sqrt(M_LN2);  // Convert from Doppler parameter to FWHM
 | 
						|
 | 
						|
    double fr = fFWHM * c / (bf*f0);
 | 
						|
    double frs = fr*fr;
 | 
						|
 | 
						|
    double T = m * (frs-vTurb*vTurb) / (2.0*k);
 | 
						|
 | 
						|
    ui->spectrumTemperature->blockSignals(true);
 | 
						|
    ui->spectrumTemperature->setValue(T);
 | 
						|
    ui->spectrumTemperature->blockSignals(false);
 | 
						|
    plotFFTMeasurement();
 | 
						|
    calcColumnDensity();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumGaussianTurb_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    calcFWHM();
 | 
						|
    plotFFTMeasurement();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_spectrumTemperature_valueChanged(double value)
 | 
						|
{
 | 
						|
   (void) value;
 | 
						|
   calcFWHM();
 | 
						|
   plotFFTMeasurement();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcFWHM()
 | 
						|
{
 | 
						|
    double c = Astronomy::m_speedOfLight;
 | 
						|
    double k = Astronomy::m_boltzmann;
 | 
						|
    double m = Astronomy::m_hydrogenMass;
 | 
						|
    double f0 = ui->spectrumGaussianFreq->value() * 1e6;
 | 
						|
    double vTurb = ui->spectrumGaussianTurb->value() * 1e3; // RSM turbulent velocties - Convert to m/s
 | 
						|
    double T = ui->spectrumTemperature->value();
 | 
						|
    const double bf = 2.0*sqrt(M_LN2);  // Convert from Doppler parameter to FWHM
 | 
						|
 | 
						|
    double fFWHM = bf * f0/c * sqrt((2*k*T)/m + vTurb * vTurb);
 | 
						|
 | 
						|
    ui->spectrumGaussianFWHM->blockSignals(true);
 | 
						|
    ui->spectrumGaussianFWHM->setValue(fFWHM);
 | 
						|
    ui->spectrumGaussianFWHM->blockSignals(false);
 | 
						|
    calcColumnDensity();
 | 
						|
}
 | 
						|
 | 
						|
// Assumes optically thin
 | 
						|
void RadioAstronomyGUI::calcColumnDensity()
 | 
						|
{
 | 
						|
    double f0 = ui->spectrumLineFrequency->value() * 1e6;
 | 
						|
    double f = f0 + ui->spectrumGaussianFWHM->value() / 2.0;
 | 
						|
    double v = lineDopplerVelocity(f0, f) * 2.0;
 | 
						|
    double a = ui->spectrumGaussianAmp->value();
 | 
						|
    double integratedIntensity = v * a;
 | 
						|
    double columnDensity = 1.81e18 * integratedIntensity;
 | 
						|
    ui->columnDensity->setText(QString::number(columnDensity, 'g', 2));
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowGaussian_clicked(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerShowGaussian = checked;
 | 
						|
    applySettings();
 | 
						|
    ui->powerGaussianWidgets->setVisible(checked);
 | 
						|
    m_powerGaussianSeries->setVisible(checked);
 | 
						|
    updatePowerSelect();
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
    update();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotPowerGaussian()
 | 
						|
{
 | 
						|
    m_powerGaussianSeries->clear();
 | 
						|
    double dt0 = ui->powerGaussianCenter->dateTime().toMSecsSinceEpoch();
 | 
						|
    double a = ui->powerGaussianAmp->value();
 | 
						|
    double floor = ui->powerGaussianFloor->value();
 | 
						|
    double fwhm = ui->powerGaussianFWHM->value() * 1000; // Convert from s to ms
 | 
						|
    double fwhm_sq = fwhm*fwhm;
 | 
						|
    qint64 dt = m_powerXAxis->min().toMSecsSinceEpoch();
 | 
						|
    qint64 end = m_powerXAxis->max().toMSecsSinceEpoch();
 | 
						|
    int steps = 256;
 | 
						|
    qint64 step = (end - dt) / steps;
 | 
						|
    for (int i = 0; i < steps; i++)
 | 
						|
    {
 | 
						|
        double fd = dt - dt0;
 | 
						|
        double g = a * std::exp(-4.0*M_LN2*fd*fd/fwhm_sq) + floor;
 | 
						|
        m_powerGaussianSeries->append(dt, g);
 | 
						|
        dt += step;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Calculate antenna HPBW from Sun's FWHM time
 | 
						|
void RadioAstronomyGUI::calcHPBWFromFWHM()
 | 
						|
{
 | 
						|
    double fwhmSeconds = ui->powerGaussianFWHM->value();
 | 
						|
    double sunDegPerSecond = 360.0/(24.0*60.0*60.0);
 | 
						|
    double hpbwDeg = fwhmSeconds * sunDegPerSecond;
 | 
						|
    ui->powerGaussianHPBW->setValue(hpbwDeg);
 | 
						|
}
 | 
						|
 | 
						|
// Calculate Sun's FWHM time for anntena HPBW
 | 
						|
void RadioAstronomyGUI::calcFHWMFromHPBW()
 | 
						|
{
 | 
						|
    double hpwmDeg = ui->powerGaussianHPBW->value();
 | 
						|
    double sunDegPerSecond = 360.0/(24.0*60.0*60.0);
 | 
						|
    double fwhmSeconds = hpwmDeg / sunDegPerSecond;
 | 
						|
    ui->powerGaussianFWHM->setValue(fwhmSeconds);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerGaussianCenter_dateTimeChanged(QDateTime dateTime)
 | 
						|
{
 | 
						|
    (void) dateTime;
 | 
						|
    plotPowerGaussian();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerGaussianAmp_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    plotPowerGaussian();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerGaussianFloor_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    plotPowerGaussian();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerGaussianFWHM_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    plotPowerGaussian();
 | 
						|
    ui->powerGaussianHPBW->blockSignals(true);
 | 
						|
    calcHPBWFromFWHM();
 | 
						|
    ui->powerGaussianHPBW->blockSignals(false);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerGaussianHPBW_valueChanged(double value)
 | 
						|
{
 | 
						|
    (void) value;
 | 
						|
    calcFHWMFromHPBW();
 | 
						|
    ui->powerGaussianFWHM->blockSignals(true);
 | 
						|
    plotPowerGaussian();
 | 
						|
    ui->powerGaussianFWHM->blockSignals(false);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::addToPowerFilter(qreal x, qreal y)
 | 
						|
{
 | 
						|
    // Add data to circular buffer
 | 
						|
    m_window[m_windowIdx] = y;
 | 
						|
    m_windowIdx = (m_windowIdx + 1) % m_settings.m_powerFilterN;
 | 
						|
    if (m_windowCount < m_settings.m_powerFilterN) {
 | 
						|
        m_windowCount++;
 | 
						|
    }
 | 
						|
 | 
						|
    // Filter
 | 
						|
    if (m_settings.m_powerFilter == RadioAstronomySettings::FILT_MOVING_AVERAGE)
 | 
						|
    {
 | 
						|
        // Moving average
 | 
						|
        qreal sum = 0.0;
 | 
						|
        for (int i = 0; i < m_windowCount; i++) {
 | 
						|
            sum += m_window[i];
 | 
						|
        }
 | 
						|
        qreal mean = sum / m_windowCount;
 | 
						|
        y = mean;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Median
 | 
						|
        std::partial_sort_copy(m_window, m_window + m_windowCount, m_windowSorted, m_windowSorted + m_windowCount);
 | 
						|
        qreal median;
 | 
						|
        if ((m_windowCount & 1) == 1) {
 | 
						|
            median = m_windowSorted[m_windowCount / 2];
 | 
						|
        } else {
 | 
						|
            median = (m_windowSorted[m_windowCount / 2 - 1] + m_windowSorted[m_windowCount / 2]) / 2.0;
 | 
						|
        }
 | 
						|
        y = median;
 | 
						|
    }
 | 
						|
 | 
						|
    // Add to series for chart
 | 
						|
    m_powerFilteredSeries->append(x, y);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotPowerFiltered()
 | 
						|
{
 | 
						|
    delete[] m_window;
 | 
						|
    delete[] m_windowSorted;
 | 
						|
    m_window = new qreal[m_settings.m_powerFilterN];
 | 
						|
    m_windowSorted = new qreal[m_settings.m_powerFilterN];
 | 
						|
    m_windowIdx = 0;
 | 
						|
    m_windowCount = 0;
 | 
						|
 | 
						|
    m_powerFilteredSeries->clear();
 | 
						|
    QVector<QPointF> powerSeries = m_powerSeries->pointsVector();
 | 
						|
    for (int i = 0; i < powerSeries.size(); i++)
 | 
						|
    {
 | 
						|
        QPointF point = powerSeries.at(i);
 | 
						|
        addToPowerFilter(point.x(), point.y());
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowFiltered_clicked(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerShowFiltered = checked;
 | 
						|
    applySettings();
 | 
						|
    ui->powerFilterWidgets->setVisible(checked);
 | 
						|
    m_powerFilteredSeries->setVisible(checked);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
    update();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerFilter_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_powerFilter = (RadioAstronomySettings::PowerFilter)index;
 | 
						|
    applySettings();
 | 
						|
    plotPowerFiltered();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerFilterN_valueChanged(int value)
 | 
						|
{
 | 
						|
    m_settings.m_powerFilterN = value;
 | 
						|
    applySettings();
 | 
						|
    plotPowerFiltered();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_powerShowMeasurement_clicked(bool checked)
 | 
						|
{
 | 
						|
    m_settings.m_powerShowMeasurement = checked;
 | 
						|
    applySettings();
 | 
						|
    m_powerSeries->setVisible(checked);
 | 
						|
}
 | 
						|
 | 
						|
RadioAstronomyGUI::LABData* RadioAstronomyGUI::parseLAB(QFile* file, float l, float b)
 | 
						|
{
 | 
						|
    LABData *data = new LABData();
 | 
						|
    data->read(file, l, b);
 | 
						|
    m_dataLAB.append(data);
 | 
						|
    return data;
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotLAB()
 | 
						|
{
 | 
						|
    int index = ui->spectrumIndex->value();
 | 
						|
    if (index < m_fftMeasurements.size())
 | 
						|
    {
 | 
						|
        FFTMeasurement *fft = m_fftMeasurements[index];
 | 
						|
        plotLAB(fft->m_l, fft->m_b, m_beamWidth);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::plotLAB(float l, float b, float beamWidth)
 | 
						|
{
 | 
						|
    // Assume a beamwidth >1deg
 | 
						|
    l = round(l);
 | 
						|
    b = round(b);
 | 
						|
 | 
						|
    // Check if we already have the data in memory
 | 
						|
    LABData* data = nullptr;
 | 
						|
    for (int i = 0; i < m_dataLAB.size(); i++)
 | 
						|
    {
 | 
						|
        if ((m_dataLAB[i]->m_l == l) && (m_dataLAB[i]->m_b == b))
 | 
						|
        {
 | 
						|
            data = m_dataLAB[i];
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!data)
 | 
						|
    {
 | 
						|
        // Try to open previously downloaded data
 | 
						|
        QString filenameLAB = HttpDownloadManager::downloadDir() + "/" + QString("lab_l_%1_b_%2.txt").arg(l).arg(b);
 | 
						|
        QFile file(filenameLAB);
 | 
						|
        if (file.open(QIODevice::ReadOnly))
 | 
						|
        {
 | 
						|
            qDebug() << "RadioAstronomyGUI::plotLAB: Using cached file: " << filenameLAB;
 | 
						|
            data = parseLAB(&file, l, b);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // Only download one file at a time, so we don't overload the server
 | 
						|
            if (!m_downloadingLAB)
 | 
						|
            {
 | 
						|
                m_downloadingLAB = true;
 | 
						|
                m_lLAB = l;
 | 
						|
                m_bLAB = b;
 | 
						|
                m_filenameLAB = filenameLAB;
 | 
						|
 | 
						|
                // Request data be generated via web server
 | 
						|
                QNetworkRequest request(QUrl("https://www.astro.uni-bonn.de/hisurvey/euhou/LABprofile/index.php"));
 | 
						|
                request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
 | 
						|
 | 
						|
                QUrlQuery params;
 | 
						|
                params.addQueryItem("coordinates", "lb");
 | 
						|
                params.addQueryItem("ral", QString::number(l));
 | 
						|
                params.addQueryItem("decb",  QString::number(b));
 | 
						|
                params.addQueryItem("beam",  QString::number(beamWidth));
 | 
						|
                params.addQueryItem("vmin", "-100.0" );
 | 
						|
                params.addQueryItem("vmax", "100.0" );
 | 
						|
                params.addQueryItem("search", "Search data" );
 | 
						|
 | 
						|
                m_networkManager->post(request, params.query(QUrl::FullyEncoded).toUtf8());
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (data)
 | 
						|
    {
 | 
						|
        data->toSeries(m_fftLABSeries);
 | 
						|
        spectrumAutoscale();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::networkManagerFinished(QNetworkReply *reply)
 | 
						|
{
 | 
						|
    QNetworkReply::NetworkError replyError = reply->error();
 | 
						|
 | 
						|
    if (replyError)
 | 
						|
    {
 | 
						|
        qWarning() << "RadioAstronomyGUI::networkManagerFinished:"
 | 
						|
                << " error(" << (int) replyError
 | 
						|
                << "): " << replyError
 | 
						|
                << ": " << reply->errorString();
 | 
						|
        m_downloadingLAB = false;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        QString answer = reply->readAll();
 | 
						|
        QRegExp re("a href=\\\"download.php([^\"]*)\"");
 | 
						|
        if (re.indexIn(answer) != -1)
 | 
						|
        {
 | 
						|
            QString filename = re.capturedTexts()[1];
 | 
						|
            qDebug() << "RadioAstronomyGUI: Downloading LAB reference data: " << filename;
 | 
						|
            m_dlm.download(QUrl("https://www.astro.uni-bonn.de/hisurvey/euhou/LABprofile/download.php" + filename), m_filenameLAB);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            qDebug() << "RadioAstronomyGUI::networkManagerFinished - No filename found: " << answer;
 | 
						|
            m_downloadingLAB = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    reply->deleteLater();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::downloadFinished(const QString& filename, bool success)
 | 
						|
{
 | 
						|
    if (success)
 | 
						|
    {
 | 
						|
        QFile file(filename);
 | 
						|
        if (file.open(QIODevice::ReadOnly))
 | 
						|
        {
 | 
						|
            LABData *data = parseLAB(&file, m_lLAB, m_bLAB);
 | 
						|
            file.close();
 | 
						|
            // Check if the data we've downloaded is for the current FFT being displayed
 | 
						|
            int index = ui->spectrumIndex->value();
 | 
						|
            if (index < m_fftMeasurements.size())
 | 
						|
            {
 | 
						|
                FFTMeasurement *fft = m_fftMeasurements[index];
 | 
						|
                if (m_lLAB == fft->m_l && m_bLAB == fft->m_b)
 | 
						|
                {
 | 
						|
                    data->toSeries(m_fftLABSeries);
 | 
						|
                    spectrumAutoscale();
 | 
						|
                    m_downloadingLAB = false;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                    // Try ploting for current FFT (as we only allow one download at a time, so may have been skipped)
 | 
						|
                    m_downloadingLAB = false;
 | 
						|
                    plotLAB(fft->m_l, fft->m_b, m_beamWidth);
 | 
						|
                    // Don't clear m_downloadingLAB after this point
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            qDebug() << "RadioAstronomyGUI::downloadFinished: Failed to open downloaded file: " << filename;
 | 
						|
            m_downloadingLAB = false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        qDebug() << "RadioAstronomyGUI::downloadFinished: Failed to download: " << filename;
 | 
						|
        m_downloadingLAB = false;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::displayRunModeSettings()
 | 
						|
{
 | 
						|
    bool sweep = m_settings.m_runMode == RadioAstronomySettings::SWEEP;
 | 
						|
    ui->sweep1CoordLabel->setVisible(sweep);
 | 
						|
    ui->sweepType->setVisible(sweep);
 | 
						|
    ui->sweep1StartLabel->setVisible(sweep);
 | 
						|
    ui->sweep1Start->setVisible(sweep);
 | 
						|
    ui->sweep1StopLabel->setVisible(sweep);
 | 
						|
    ui->sweep1Stop->setVisible(sweep);
 | 
						|
    ui->sweep1StepLabel->setVisible(sweep);
 | 
						|
    ui->sweep1Step->setVisible(sweep);
 | 
						|
    ui->sweep1DelayLabel->setVisible(sweep);
 | 
						|
    ui->sweep1Delay->setVisible(sweep);
 | 
						|
    ui->sweep2CoordLabel->setVisible(sweep);
 | 
						|
    ui->sweep2StartLabel->setVisible(sweep);
 | 
						|
    ui->sweep2Start->setVisible(sweep);
 | 
						|
    ui->sweep2StopLabel->setVisible(sweep);
 | 
						|
    ui->sweep2Stop->setVisible(sweep);
 | 
						|
    ui->sweep2StepLabel->setVisible(sweep);
 | 
						|
    ui->sweep2Step->setVisible(sweep);
 | 
						|
    ui->sweep2DelayLabel->setVisible(sweep);
 | 
						|
    ui->sweep2Delay->setVisible(sweep);
 | 
						|
    ui->sweepStatus->setVisible(sweep);
 | 
						|
    ui->runLayout->activate();         // Needed otherwise height of rollup doesn't seem to be reduced
 | 
						|
    ui->statusLayout->activate();      // going from sweep to single/continuous
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_runMode_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_runMode = (RadioAstronomySettings::RunMode)index;
 | 
						|
    applySettings();
 | 
						|
    displayRunModeSettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweepType_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_sweepType = (RadioAstronomySettings::SweepType)index;
 | 
						|
    if ((index == 0) || (index == 2))
 | 
						|
    {
 | 
						|
        ui->sweep1CoordLabel->setText("Az");
 | 
						|
        ui->sweep2CoordLabel->setText("El");
 | 
						|
    }
 | 
						|
    else if (index == 1)
 | 
						|
    {
 | 
						|
        ui->sweep1CoordLabel->setText("l");
 | 
						|
        ui->sweep2CoordLabel->setText("b");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep1Start_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep1Start = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep1Stop_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep1Stop = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep1Step_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep1Step = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep1Delay_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep1Delay = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep2Start_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep2Start = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep2Stop_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep2Stop = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep2Step_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep2Step = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweep2Delay_valueChanged(double value)
 | 
						|
{
 | 
						|
    m_settings.m_sweep2Delay = value;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweepStartAtTime_currentIndexChanged(int index)
 | 
						|
{
 | 
						|
    m_settings.m_sweepStartAtTime = ui->sweepStartAtTime->currentIndex() == 1;
 | 
						|
    ui->sweepStartDateTime->setVisible(index == 1);
 | 
						|
    getRollupContents()->arrangeRollups();
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_sweepStartDateTime_dateTimeChanged(const QDateTime& dateTime)
 | 
						|
{
 | 
						|
    m_settings.m_sweepStartDateTime = dateTime;
 | 
						|
    applySettings();
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::on_startStop_clicked(bool checked)
 | 
						|
{
 | 
						|
    if (checked)
 | 
						|
    {
 | 
						|
        ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
 | 
						|
        applySettings();
 | 
						|
        if (m_settings.m_power2DLinkSweep)
 | 
						|
        {
 | 
						|
            update2DSettingsFromSweep();
 | 
						|
            create2DImage();
 | 
						|
        }
 | 
						|
        m_radioAstronomy->getInputMessageQueue()->push(RadioAstronomy::MsgStartSweep::create());
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        m_radioAstronomy->getInputMessageQueue()->push(RadioAstronomy::MsgStopSweep::create());
 | 
						|
        if (m_settings.m_runMode != RadioAstronomySettings::SWEEP) {
 | 
						|
            ui->startStop->setStyleSheet("QToolButton { background-color : blue; }");
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcPowerChartTickCount(int width)
 | 
						|
{
 | 
						|
    // These values should probably be dependent on the font used
 | 
						|
    if (m_powerXAxis) {
 | 
						|
        if (m_powerXAxisSameDay) {
 | 
						|
            m_powerXAxis->setTickCount(width > 700 ? 10 : 5);
 | 
						|
        } else {
 | 
						|
            m_powerXAxis->setTickCount(width > 1200 ? 10 : 5);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::calcSpectrumChartTickCount(QValueAxis *axis, int width)
 | 
						|
{
 | 
						|
    if (axis) {
 | 
						|
        axis->setTickCount(width > 700 ? 10 : 5);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::resizeEvent(QResizeEvent* size)
 | 
						|
{
 | 
						|
    int width = size->size().width();
 | 
						|
    calcPowerChartTickCount(width);
 | 
						|
    calcSpectrumChartTickCount(m_fftXAxis, width);
 | 
						|
    calcSpectrumChartTickCount(m_fftDopplerAxis, width);
 | 
						|
    calcSpectrumChartTickCount(m_calXAxis, width);
 | 
						|
    ChannelGUI::resizeEvent(size);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::makeUIConnections()
 | 
						|
{
 | 
						|
    QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_deltaFrequency_changed);
 | 
						|
    QObject::connect(ui->sampleRate, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_sampleRate_changed);
 | 
						|
    QObject::connect(ui->rfBW, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_rfBW_changed);
 | 
						|
    QObject::connect(ui->integration, &ValueDialZ::changed, this, &RadioAstronomyGUI::on_integration_changed);
 | 
						|
    QObject::connect(ui->fftSize, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_fftSize_currentIndexChanged);
 | 
						|
    QObject::connect(ui->fftWindow, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_fftWindow_currentIndexChanged);
 | 
						|
    QObject::connect(ui->filterFreqs, &QLineEdit::editingFinished, this, &RadioAstronomyGUI::on_filterFreqs_editingFinished);
 | 
						|
    QObject::connect(ui->starTracker, &QComboBox::currentTextChanged, this, &RadioAstronomyGUI::on_starTracker_currentTextChanged);
 | 
						|
    QObject::connect(ui->rotator, &QComboBox::currentTextChanged, this, &RadioAstronomyGUI::on_rotator_currentTextChanged);
 | 
						|
    QObject::connect(ui->showSensors, &QToolButton::clicked, this, &RadioAstronomyGUI::on_showSensors_clicked);
 | 
						|
    QObject::connect(ui->tempRXSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tempRXSelect_currentIndexChanged);
 | 
						|
    QObject::connect(ui->tempRX, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempRX_valueChanged);
 | 
						|
    QObject::connect(ui->tempCMB, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempCMB_valueChanged);
 | 
						|
    QObject::connect(ui->tempGal, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempGal_valueChanged);
 | 
						|
    QObject::connect(ui->tempSP, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempSP_valueChanged);
 | 
						|
    QObject::connect(ui->tempAtm, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempAtm_valueChanged);
 | 
						|
    QObject::connect(ui->tempAir, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tempAir_valueChanged);
 | 
						|
    QObject::connect(ui->zenithOpacity, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_zenithOpacity_valueChanged);
 | 
						|
    QObject::connect(ui->elevation, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_elevation_valueChanged);
 | 
						|
    QObject::connect(ui->tempAtmLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempAtmLink_toggled);
 | 
						|
    QObject::connect(ui->tempAirLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempAirLink_toggled);
 | 
						|
    QObject::connect(ui->tempGalLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_tempGalLink_toggled);
 | 
						|
    QObject::connect(ui->elevationLink, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_elevationLink_toggled);
 | 
						|
    QObject::connect(ui->gainVariation, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_gainVariation_valueChanged);
 | 
						|
    QObject::connect(ui->omegaAUnits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_omegaAUnits_currentIndexChanged);
 | 
						|
    QObject::connect(ui->sourceType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sourceType_currentIndexChanged);
 | 
						|
    QObject::connect(ui->omegaS, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_omegaS_valueChanged);
 | 
						|
    QObject::connect(ui->omegaSUnits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_omegaSUnits_currentIndexChanged);
 | 
						|
    QObject::connect(ui->spectrumChartSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumChartSelect_currentIndexChanged);
 | 
						|
    QObject::connect(ui->showCalSettings, &QToolButton::clicked, this, &RadioAstronomyGUI::on_showCalSettings_clicked);
 | 
						|
    QObject::connect(ui->startCalHot, &QToolButton::clicked, this, &RadioAstronomyGUI::on_startCalHot_clicked);
 | 
						|
    QObject::connect(ui->startCalCold, &QToolButton::clicked, this, &RadioAstronomyGUI::on_startCalCold_clicked);
 | 
						|
    QObject::connect(ui->recalibrate, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_recalibrate_toggled);
 | 
						|
    QObject::connect(ui->spectrumShowLegend, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowLegend_toggled);
 | 
						|
    QObject::connect(ui->spectrumShowRefLine, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowRefLine_toggled);
 | 
						|
    QObject::connect(ui->spectrumTemp, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumTemp_toggled);
 | 
						|
    QObject::connect(ui->spectrumMarker, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumMarker_toggled);
 | 
						|
    QObject::connect(ui->spectrumPeak, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumPeak_toggled);
 | 
						|
    QObject::connect(ui->spectrumReverseXAxis, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumReverseXAxis_toggled);
 | 
						|
    QObject::connect(ui->savePowerData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_savePowerData_clicked);
 | 
						|
    QObject::connect(ui->savePowerChartImage, &QToolButton::clicked, this, &RadioAstronomyGUI::on_savePowerChartImage_clicked);
 | 
						|
    QObject::connect(ui->saveSpectrumData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumData_clicked);
 | 
						|
    QObject::connect(ui->loadSpectrumData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_loadSpectrumData_clicked);
 | 
						|
    QObject::connect(ui->saveSpectrumChartImage, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumChartImage_clicked);
 | 
						|
    QObject::connect(ui->saveSpectrumChartImages, &QToolButton::clicked, this, &RadioAstronomyGUI::on_saveSpectrumChartImages_clicked);
 | 
						|
    QObject::connect(ui->clearData, &QToolButton::clicked, this, &RadioAstronomyGUI::on_clearData_clicked);
 | 
						|
    QObject::connect(ui->clearCal, &QToolButton::clicked, this, &RadioAstronomyGUI::on_clearCal_clicked);
 | 
						|
    QObject::connect(ui->spectrumAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_spectrumAutoscale_toggled);
 | 
						|
    QObject::connect(ui->spectrumAutoscaleX, &QToolButton::clicked, this, &RadioAstronomyGUI::on_spectrumAutoscaleX_clicked);
 | 
						|
    QObject::connect(ui->spectrumAutoscaleY, &QToolButton::clicked, this, &RadioAstronomyGUI::on_spectrumAutoscaleY_clicked);
 | 
						|
    QObject::connect(ui->spectrumReference, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumReference_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumRange, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumRange_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumCenterFreq, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumCenterFreq_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumSpan, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumSpan_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumYUnits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumYUnits_currentIndexChanged);
 | 
						|
    QObject::connect(ui->spectrumBaseline, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumBaseline_currentIndexChanged);
 | 
						|
    QObject::connect(ui->spectrumIndex, &QSlider::valueChanged, this, &RadioAstronomyGUI::on_spectrumIndex_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumLine, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_spectrumLine_currentIndexChanged);
 | 
						|
    QObject::connect(ui->spectrumLineFrequency, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumLineFrequency_valueChanged);
 | 
						|
    QObject::connect(ui->refFrame, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_refFrame_currentIndexChanged);
 | 
						|
    QObject::connect(ui->sunDistanceToGC, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sunDistanceToGC_valueChanged);
 | 
						|
    QObject::connect(ui->sunOrbitalVelocity, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sunOrbitalVelocity_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumGaussianFreq, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFreq_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumGaussianAmp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianAmp_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumGaussianFloor, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFloor_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumGaussianFWHM, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianFWHM_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumGaussianTurb, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumGaussianTurb_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumTemperature, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_spectrumTemperature_valueChanged);
 | 
						|
    QObject::connect(ui->spectrumShowLAB, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowLAB_toggled);
 | 
						|
    QObject::connect(ui->spectrumShowDistance, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_spectrumShowDistance_toggled);
 | 
						|
    QObject::connect(ui->tCalHotSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tCalHotSelect_currentIndexChanged);
 | 
						|
    QObject::connect(ui->tCalHot, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tCalHot_valueChanged);
 | 
						|
    QObject::connect(ui->tCalColdSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_tCalColdSelect_currentIndexChanged);
 | 
						|
    QObject::connect(ui->tCalCold, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_tCalCold_valueChanged);
 | 
						|
    QObject::connect(ui->powerChartSelect, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerChartSelect_currentIndexChanged);
 | 
						|
    QObject::connect(ui->powerYUnits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerYUnits_currentIndexChanged);
 | 
						|
    QObject::connect(ui->powerShowMarker, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowMarker_toggled);
 | 
						|
    QObject::connect(ui->powerShowAirTemp, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowAirTemp_toggled);
 | 
						|
    QObject::connect(ui->powerShowPeak, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowPeak_toggled);
 | 
						|
    QObject::connect(ui->powerShowAvg, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowAvg_toggled);
 | 
						|
    QObject::connect(ui->powerShowLegend, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowLegend_toggled);
 | 
						|
    QObject::connect(ui->powerAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_powerAutoscale_toggled);
 | 
						|
    QObject::connect(ui->powerAutoscaleY, &QToolButton::clicked, this, &RadioAstronomyGUI::on_powerAutoscaleY_clicked);
 | 
						|
    QObject::connect(ui->powerAutoscaleX, &QToolButton::clicked, this, &RadioAstronomyGUI::on_powerAutoscaleX_clicked);
 | 
						|
    QObject::connect(ui->powerReference, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerReference_valueChanged);
 | 
						|
    QObject::connect(ui->powerRange, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerRange_valueChanged);
 | 
						|
    QObject::connect(ui->powerStartTime, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerStartTime_dateTimeChanged);
 | 
						|
    QObject::connect(ui->powerEndTime, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerEndTime_dateTimeChanged);
 | 
						|
    QObject::connect(ui->powerShowGaussian, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_powerShowGaussian_clicked);
 | 
						|
    QObject::connect(ui->powerGaussianCenter, &WrappingDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_powerGaussianCenter_dateTimeChanged);
 | 
						|
    QObject::connect(ui->powerGaussianAmp, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianAmp_valueChanged);
 | 
						|
    QObject::connect(ui->powerGaussianFloor, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianFloor_valueChanged);
 | 
						|
    QObject::connect(ui->powerGaussianFWHM, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianFWHM_valueChanged);
 | 
						|
    QObject::connect(ui->powerGaussianHPBW, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerGaussianHPBW_valueChanged);
 | 
						|
    QObject::connect(ui->runMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_runMode_currentIndexChanged);
 | 
						|
    QObject::connect(ui->sweepType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sweepType_currentIndexChanged);
 | 
						|
    QObject::connect(ui->sweep1Start, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep1Start_valueChanged);
 | 
						|
    QObject::connect(ui->sweep1Stop, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep1Stop_valueChanged);
 | 
						|
    QObject::connect(ui->sweep1Step, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep1Step_valueChanged);
 | 
						|
    QObject::connect(ui->sweep1Delay, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep1Delay_valueChanged);
 | 
						|
    QObject::connect(ui->sweep2Start, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep2Start_valueChanged);
 | 
						|
    QObject::connect(ui->sweep2Stop, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep2Stop_valueChanged);
 | 
						|
    QObject::connect(ui->sweep2Step, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep2Step_valueChanged);
 | 
						|
    QObject::connect(ui->sweep2Delay, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_sweep2Delay_valueChanged);
 | 
						|
    QObject::connect(ui->sweepStartAtTime, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_sweepStartAtTime_currentIndexChanged);
 | 
						|
    QObject::connect(ui->sweepStartDateTime, &QDateTimeEdit::dateTimeChanged, this, &RadioAstronomyGUI::on_sweepStartDateTime_dateTimeChanged);
 | 
						|
    QObject::connect(ui->startStop, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_startStop_clicked);
 | 
						|
    QObject::connect(ui->power2DAutoscale, &QToolButton::clicked, this, &RadioAstronomyGUI::on_power2DAutoscale_clicked);
 | 
						|
    QObject::connect(ui->power2DLinkSweep, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_power2DLinkSweep_toggled);
 | 
						|
    QObject::connect(ui->power2DSweepType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_power2DSweepType_currentIndexChanged);
 | 
						|
    QObject::connect(ui->power2DWidth, QOverload<int>::of(&QSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DWidth_valueChanged);
 | 
						|
    QObject::connect(ui->power2DHeight, QOverload<int>::of(&QSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DHeight_valueChanged);
 | 
						|
    QObject::connect(ui->power2DXMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DXMin_valueChanged);
 | 
						|
    QObject::connect(ui->power2DXMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DXMax_valueChanged);
 | 
						|
    QObject::connect(ui->power2DYMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DYMin_valueChanged);
 | 
						|
    QObject::connect(ui->power2DYMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_power2DYMax_valueChanged);
 | 
						|
    QObject::connect(ui->powerShowSensor1, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowSensor1_toggled);
 | 
						|
    QObject::connect(ui->powerShowSensor2, &ButtonSwitch::toggled, this, &RadioAstronomyGUI::on_powerShowSensor1_toggled);
 | 
						|
    QObject::connect(ui->powerShowFiltered, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_powerShowFiltered_clicked);
 | 
						|
    QObject::connect(ui->powerFilter, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerFilter_currentIndexChanged);
 | 
						|
    QObject::connect(ui->powerFilterN, QOverload<int>::of(&QSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerFilterN_valueChanged);
 | 
						|
    QObject::connect(ui->powerShowMeasurement, &ButtonSwitch::clicked, this, &RadioAstronomyGUI::on_powerShowMeasurement_clicked);
 | 
						|
    QObject::connect(ui->powerColourAutoscale, &QToolButton::toggled, this, &RadioAstronomyGUI::on_powerColourAutoscale_toggled);
 | 
						|
    QObject::connect(ui->powerColourScaleMin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerColourScaleMin_valueChanged);
 | 
						|
    QObject::connect(ui->powerColourScaleMax, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &RadioAstronomyGUI::on_powerColourScaleMax_valueChanged);
 | 
						|
    QObject::connect(ui->powerColourPalette, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RadioAstronomyGUI::on_powerColourPalette_currentIndexChanged);
 | 
						|
    QObject::connect(ui->powerTable, &QTableWidget::cellDoubleClicked, this, &RadioAstronomyGUI::on_powerTable_cellDoubleClicked);
 | 
						|
}
 | 
						|
 | 
						|
void RadioAstronomyGUI::updateAbsoluteCenterFrequency()
 | 
						|
{
 | 
						|
    setStatusFrequency(m_centerFrequency + m_settings.m_inputFrequencyOffset);
 | 
						|
}
 |