mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-19 06:41:47 -05:00
c966f1cb5a
Add sizeToContents in ChannelGUI and FeatureGUI, called when widget is rolled, so we can remove resizing code from all of the individual channels and features. In RollupContents, use minimumSizeHint for calculated size, so that minimumWidth can come from .ui file. In DeviceGUI::sizeToContents(), call adjustSize(), so Device GUIs start out at minimum needed size (which should restore appearance prior to last patch). In stackSubWindows, use available space for channels if no spectrum/features present. In stackSubWindows, fix spectrum from being sized too big, resulting in scroll bars appearing. Reset user-defined channel width in stackSubWindows, when channels are removed. Don't stack maximized windows. There's one hack in Channel/FeatureGUI::maximizeWindow(). It seems that when maximimzing a window, QOpenGLWidgets aren't always paint properly immediately afterwards, so the code forces an additional update. I can't see why the first call to paintGL doesn't work.
2046 lines
85 KiB
C++
2046 lines
85 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
|
// Copyright (C) 2020 Edouard Griffiths, F4EXB //
|
|
// //
|
|
// 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 <cmath>
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <QMessageBox>
|
|
#include <QLineEdit>
|
|
#include <QPixmap>
|
|
|
|
#include <QtCharts/QChartView>
|
|
#include <QtCharts/QLineSeries>
|
|
#include <QtCharts/QDateTimeAxis>
|
|
#include <QtCharts/QValueAxis>
|
|
|
|
#include "SWGMapItem.h"
|
|
|
|
#include "feature/featureuiset.h"
|
|
#include "feature/featurewebapiutils.h"
|
|
#include "gui/basicfeaturesettingsdialog.h"
|
|
#include "mainwindow.h"
|
|
#include "maincore.h"
|
|
#include "device/deviceuiset.h"
|
|
|
|
#include "ui_aprsgui.h"
|
|
#include "aprs.h"
|
|
#include "aprsgui.h"
|
|
#include "aprssettingsdialog.h"
|
|
|
|
#define PACKETS_COL_DATE 0
|
|
#define PACKETS_COL_TIME 1
|
|
#define PACKETS_COL_FROM 2
|
|
#define PACKETS_COL_TO 3
|
|
#define PACKETS_COL_VIA 4
|
|
#define PACKETS_COL_DATA 5
|
|
|
|
#define WEATHER_COL_DATE 0
|
|
#define WEATHER_COL_TIME 1
|
|
#define WEATHER_COL_WIND_DIRECTION 2
|
|
#define WEATHER_COL_WIND_SPEED 3
|
|
#define WEATHER_COL_GUSTS 4
|
|
#define WEATHER_COL_TEMPERATURE 5
|
|
#define WEATHER_COL_HUMIDITY 6
|
|
#define WEATHER_COL_PRESSURE 7
|
|
#define WEATHER_COL_RAIN_LAST_HOUR 8
|
|
#define WEATHER_COL_RAIN_LAST_24_HOURS 9
|
|
#define WEATHER_COL_RAIN_SINCE_MIDNIGHT 10
|
|
#define WEATHER_COL_LUMINOSITY 11
|
|
#define WEATHER_COL_SNOWFALL 12
|
|
#define WEATHER_COL_RADIATION_LEVEL 13
|
|
#define WEATHER_COL_FLOOD_LEVEL 14
|
|
|
|
#define STATUS_COL_DATE 0
|
|
#define STATUS_COL_TIME 1
|
|
#define STATUS_COL_STATUS 2
|
|
#define STATUS_COL_SYMBOL 3
|
|
#define STATUS_COL_MAIDENHEAD 4
|
|
#define STATUS_COL_BEAM_HEADING 5
|
|
#define STATUS_COL_BEAM_POWER 6
|
|
|
|
#define MESSAGE_COL_DATE 0
|
|
#define MESSAGE_COL_TIME 1
|
|
#define MESSAGE_COL_ADDRESSEE 2
|
|
#define MESSAGE_COL_MESSAGE 3
|
|
#define MESSAGE_COL_NO 4
|
|
|
|
#define TELEMETRY_COL_DATE 0
|
|
#define TELEMETRY_COL_TIME 1
|
|
#define TELEMETRY_COL_SEQ_NO 2
|
|
#define TELEMETRY_COL_A1 3
|
|
#define TELEMETRY_COL_A2 4
|
|
#define TELEMETRY_COL_A3 5
|
|
#define TELEMETRY_COL_A4 6
|
|
#define TELEMETRY_COL_A5 7
|
|
#define TELEMETRY_COL_B1 8
|
|
#define TELEMETRY_COL_B2 9
|
|
#define TELEMETRY_COL_B3 10
|
|
#define TELEMETRY_COL_B4 11
|
|
#define TELEMETRY_COL_B5 12
|
|
#define TELEMETRY_COL_B6 13
|
|
#define TELEMETRY_COL_B7 14
|
|
#define TELEMETRY_COL_B8 15
|
|
#define TELEMETRY_COL_COMMENT 16
|
|
|
|
#define MOTION_COL_DATE 0
|
|
#define MOTION_COL_TIME 1
|
|
#define MOTION_COL_LATITUDE 2
|
|
#define MOTION_COL_LONGITUDE 3
|
|
#define MOTION_COL_ALTITUDE 4
|
|
#define MOTION_COL_COURSE 5
|
|
#define MOTION_COL_SPEED 6
|
|
|
|
APRSGUI* APRSGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
|
|
{
|
|
APRSGUI* gui = new APRSGUI(pluginAPI, featureUISet, feature);
|
|
return gui;
|
|
}
|
|
|
|
void APRSGUI::destroy()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
void APRSGUI::resetToDefaults()
|
|
{
|
|
m_settings.resetToDefaults();
|
|
displaySettings();
|
|
applySettings(true);
|
|
}
|
|
|
|
QByteArray APRSGUI::serialize() const
|
|
{
|
|
return m_settings.serialize();
|
|
}
|
|
|
|
bool APRSGUI::deserialize(const QByteArray& data)
|
|
{
|
|
if (m_settings.deserialize(data))
|
|
{
|
|
m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
|
|
displaySettings();
|
|
applySettings(true);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
resetToDefaults();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool APRSGUI::handleMessage(const Message& message)
|
|
{
|
|
if (APRS::MsgConfigureAPRS::match(message))
|
|
{
|
|
qDebug("APRSGUI::handleMessage: APRS::MsgConfigureAPRS");
|
|
const APRS::MsgConfigureAPRS& cfg = (APRS::MsgConfigureAPRS&) message;
|
|
m_settings = cfg.getSettings();
|
|
qDebug() << m_settings.m_igateCallsign;
|
|
blockApplySettings(true);
|
|
displaySettings();
|
|
blockApplySettings(false);
|
|
|
|
return true;
|
|
}
|
|
else if (APRS::MsgReportAvailableChannels::match(message))
|
|
{
|
|
APRS::MsgReportAvailableChannels& report = (APRS::MsgReportAvailableChannels&) message;
|
|
m_availableChannels = report.getChannels();
|
|
updateChannelList();
|
|
|
|
return true;
|
|
}
|
|
else if (MainCore::MsgPacket::match(message))
|
|
{
|
|
MainCore::MsgPacket& report = (MainCore::MsgPacket&) message;
|
|
AX25Packet ax25;
|
|
APRSPacket *aprs = new APRSPacket();
|
|
if (ax25.decode(report.getPacket()))
|
|
{
|
|
if (aprs->decode(ax25))
|
|
{
|
|
aprs->m_dateTime = report.getDateTime();
|
|
|
|
APRSStation *station;
|
|
bool addToStationSel = false;
|
|
|
|
// Is packet for an existing object or station?
|
|
if (!aprs->m_objectName.isEmpty() && m_stations.contains(aprs->m_objectName))
|
|
{
|
|
// Add packet to existing object
|
|
station = m_stations.value(aprs->m_objectName);
|
|
station->addPacket(aprs);
|
|
}
|
|
else if (!aprs->m_objectName.isEmpty())
|
|
{
|
|
// Add new object
|
|
station = new APRSStation(aprs->m_objectName);
|
|
station->m_isObject = true;
|
|
station->m_reportingStation = aprs->m_from;
|
|
station->addPacket(aprs);
|
|
m_stations.insert(aprs->m_objectName, station);
|
|
addToStationSel = true;
|
|
}
|
|
else if (m_stations.contains(aprs->m_from))
|
|
{
|
|
// Add packet to existing station
|
|
station = m_stations.value(aprs->m_from);
|
|
station->addPacket(aprs);
|
|
}
|
|
else
|
|
{
|
|
// Add new station
|
|
station = new APRSStation(aprs->m_from);
|
|
station->addPacket(aprs);
|
|
m_stations.insert(aprs->m_from, station);
|
|
addToStationSel = true;
|
|
}
|
|
|
|
// Update station
|
|
if (aprs->m_hasSymbol)
|
|
station->m_symbolImage = aprs->m_symbolImage;
|
|
if (aprs->m_hasTimestamp)
|
|
station->m_latestPacket = aprs->dateTime();
|
|
if (aprs->m_hasStatus)
|
|
station->m_latestStatus = aprs->m_status;
|
|
if (!aprs->m_comment.isEmpty())
|
|
station->m_latestComment = aprs->m_comment;
|
|
if (aprs->m_hasPosition)
|
|
station->m_latestPosition = aprs->position();
|
|
if (aprs->m_hasAltitude)
|
|
station->m_latestAltitude = QString("%1").arg(aprs->m_altitudeFt);
|
|
if (aprs->m_hasCourseAndSpeed)
|
|
{
|
|
station->m_latestCourse = QString("%1").arg(aprs->m_course);
|
|
station->m_latestSpeed = QString("%1").arg(aprs->m_speed);
|
|
}
|
|
if (aprs->m_hasStationDetails)
|
|
{
|
|
station->m_powerWatts = QString("%1").arg(aprs->m_powerWatts);
|
|
station->m_antennaHeightFt = QString("%1").arg(aprs->m_antennaHeightFt);
|
|
station->m_antennaGainDB = QString("%1").arg(aprs->m_antennaGainDB);
|
|
station->m_antennaDirectivity = aprs->m_antennaDirectivity;
|
|
}
|
|
if (aprs->m_hasRadioRange)
|
|
station->m_radioRange = QString("%1").arg(aprs->m_radioRangeMiles);
|
|
if (aprs->m_hasWeather)
|
|
station->m_hasWeather = true;
|
|
if (aprs->m_hasTelemetry)
|
|
station->m_hasTelemetry = true;
|
|
if (aprs->m_hasCourseAndSpeed)
|
|
station->m_hasCourseAndSpeed = true;
|
|
|
|
// Update messages, which aren't station specific
|
|
if (aprs->m_hasMessage)
|
|
{
|
|
int row = ui->messagesTable->rowCount();
|
|
ui->messagesTable->setRowCount(row + 1);
|
|
QTableWidgetItem *messageDateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *messageTimeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *messageAddresseeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *messageMessageItem = new QTableWidgetItem();
|
|
QTableWidgetItem *messageNoItem = new QTableWidgetItem();
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_DATE, messageDateItem);
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_TIME, messageTimeItem);
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_ADDRESSEE, messageAddresseeItem);
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_MESSAGE, messageMessageItem);
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_NO, messageNoItem);
|
|
messageDateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
messageTimeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
messageAddresseeItem->setText(aprs->m_addressee);
|
|
messageMessageItem->setText(aprs->m_message);
|
|
messageNoItem->setText(aprs->m_messageNo);
|
|
|
|
// Process telemetry messages
|
|
if ((aprs->m_telemetryNames.size() > 0) || (aprs->m_telemetryLabels.size() > 0) || aprs->m_hasTelemetryCoefficients || aprs->m_hasTelemetryBitSense)
|
|
{
|
|
APRSStation *telemetryStation;
|
|
if (m_stations.contains(aprs->m_addressee))
|
|
telemetryStation = m_stations.value(aprs->m_addressee);
|
|
else
|
|
{
|
|
telemetryStation = new APRSStation(aprs->m_addressee);
|
|
m_stations.insert(aprs->m_addressee, telemetryStation);
|
|
}
|
|
if (aprs->m_telemetryNames.size() > 0)
|
|
{
|
|
telemetryStation->m_telemetryNames = aprs->m_telemetryNames;
|
|
for (int i = 0; i < aprs->m_telemetryNames.size(); i++)
|
|
ui->telemetryPlotSelect->setItemText(i, aprs->m_telemetryNames[i]);
|
|
}
|
|
else
|
|
telemetryStation->m_telemetryLabels = aprs->m_telemetryLabels;
|
|
if (aprs->m_hasTelemetryCoefficients > 0)
|
|
{
|
|
for (int j = 0; j < 5; j++)
|
|
{
|
|
telemetryStation->m_telemetryCoefficientsA[j] = aprs->m_telemetryCoefficientsA[j];
|
|
telemetryStation->m_telemetryCoefficientsB[j] = aprs->m_telemetryCoefficientsB[j];
|
|
telemetryStation->m_telemetryCoefficientsC[j] = aprs->m_telemetryCoefficientsC[j];
|
|
}
|
|
telemetryStation->m_hasTelemetryCoefficients = aprs->m_hasTelemetryCoefficients;
|
|
}
|
|
if (aprs->m_hasTelemetryBitSense)
|
|
{
|
|
for (int j = 0; j < 8; j++)
|
|
telemetryStation->m_telemetryBitSense[j] = aprs->m_telemetryBitSense[j];
|
|
telemetryStation->m_hasTelemetryBitSense = true;
|
|
telemetryStation->m_telemetryProjectName = aprs->m_telemetryProjectName;
|
|
}
|
|
if (ui->stationSelect->currentText() == aprs->m_addressee)
|
|
{
|
|
for (int i = 0; i < station->m_telemetryNames.size(); i++)
|
|
{
|
|
QString header;
|
|
if (station->m_telemetryLabels.size() <= i)
|
|
header = station->m_telemetryNames[i];
|
|
else
|
|
header = QString("%1 (%2)").arg(station->m_telemetryNames[i]).arg(station->m_telemetryLabels[i]);
|
|
ui->telemetryTable->horizontalHeaderItem(TELEMETRY_COL_A1+i)->setText(header);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (addToStationSel)
|
|
{
|
|
if (!filterStation(station))
|
|
ui->stationSelect->addItem(station->m_station);
|
|
}
|
|
|
|
// Refresh GUI if currently selected station
|
|
if (ui->stationSelect->currentText() == aprs->m_from)
|
|
{
|
|
updateSummary(station);
|
|
addPacketToGUI(station, aprs);
|
|
if (aprs->m_hasWeather)
|
|
plotWeather();
|
|
if (aprs->m_hasTelemetry)
|
|
plotTelemetry();
|
|
if (aprs->m_hasPosition || aprs->m_hasAltitude || aprs->m_hasCourseAndSpeed)
|
|
plotMotion();
|
|
}
|
|
|
|
// Forward to map
|
|
if (aprs->m_hasPosition && (aprs->m_from != ""))
|
|
{
|
|
QList<ObjectPipe*> mapPipes;
|
|
MainCore::instance()->getMessagePipes().getMessagePipes(m_aprs, "mapitems", mapPipes);
|
|
|
|
for (const auto& pipe : mapPipes)
|
|
{
|
|
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
|
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
|
|
|
if (!aprs->m_objectName.isEmpty()) {
|
|
swgMapItem->setName(new QString(aprs->m_objectName));
|
|
} else {
|
|
swgMapItem->setName(new QString(aprs->m_from));
|
|
}
|
|
|
|
swgMapItem->setLatitude(aprs->m_latitude);
|
|
swgMapItem->setLongitude(aprs->m_longitude);
|
|
swgMapItem->setAltitude(aprs->m_hasAltitude ? Units::feetToMetres(aprs->m_altitudeFt) : 0);
|
|
swgMapItem->setAltitudeReference(1); // CLAMP_TO_GROUND
|
|
|
|
if (aprs->m_objectKilled)
|
|
{
|
|
swgMapItem->setImage(new QString(""));
|
|
swgMapItem->setText(new QString(""));
|
|
}
|
|
else
|
|
{
|
|
swgMapItem->setImage(new QString(QString("qrc:///%1").arg(aprs->m_symbolImage)));
|
|
swgMapItem->setText(new QString(
|
|
aprs->toText(
|
|
true,
|
|
false,
|
|
'\n',
|
|
m_settings.m_altitudeUnits == APRSSettings::METRES,
|
|
(int)m_settings.m_speedUnits,
|
|
m_settings.m_temperatureUnits == APRSSettings::CELSIUS,
|
|
m_settings.m_rainfallUnits == APRSSettings::MILLIMETRE
|
|
)
|
|
));
|
|
}
|
|
|
|
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_aprs, swgMapItem);
|
|
messageQueue->push(msg);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "APRSGUI::handleMessage: Failed to decode as APRS";
|
|
qDebug() << ax25.m_from << " " << ax25.m_to << " " << ax25.m_via << " " << ax25.m_type << " " << ax25.m_pid << " "<< ax25.m_dataASCII;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
qDebug() << "APRSGUI::handleMessage: Failed to decode as AX.25";
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void APRSGUI::handleInputMessages()
|
|
{
|
|
Message* message;
|
|
|
|
while ((message = getInputMessageQueue()->pop()))
|
|
{
|
|
if (handleMessage(*message)) {
|
|
delete message;
|
|
}
|
|
}
|
|
}
|
|
|
|
void APRSGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|
{
|
|
(void) widget;
|
|
(void) rollDown;
|
|
|
|
getRollupContents()->saveState(m_rollupState);
|
|
applySettings();
|
|
}
|
|
|
|
APRSGUI::APRSGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
|
|
FeatureGUI(parent),
|
|
ui(new Ui::APRSGUI),
|
|
m_pluginAPI(pluginAPI),
|
|
m_featureUISet(featureUISet),
|
|
m_doApplySettings(true),
|
|
m_lastFeatureState(0)
|
|
{
|
|
m_feature = feature;
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
m_helpURL = "plugins/feature/aprs/readme.md";
|
|
RollupContents *rollupContents = getRollupContents();
|
|
ui->setupUi(rollupContents);
|
|
rollupContents->arrangeRollups();
|
|
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
|
|
|
m_aprs = reinterpret_cast<APRS*>(feature);
|
|
m_aprs->setMessageQueueToGUI(&m_inputMessageQueue);
|
|
|
|
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
|
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
|
|
|
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
|
m_statusTimer.start(1000);
|
|
|
|
// Resize the table using dummy data
|
|
resizeTable();
|
|
// Allow user to reorder columns
|
|
ui->weatherTable->horizontalHeader()->setSectionsMovable(true);
|
|
ui->packetsTable->horizontalHeader()->setSectionsMovable(true);
|
|
ui->statusTable->horizontalHeader()->setSectionsMovable(true);
|
|
ui->messagesTable->horizontalHeader()->setSectionsMovable(true);
|
|
ui->telemetryTable->horizontalHeader()->setSectionsMovable(true);
|
|
ui->motionTable->horizontalHeader()->setSectionsMovable(true);
|
|
// Allow user to sort table by clicking on headers
|
|
ui->weatherTable->setSortingEnabled(true);
|
|
ui->packetsTable->setSortingEnabled(true);
|
|
ui->statusTable->setSortingEnabled(true);
|
|
ui->messagesTable->setSortingEnabled(true);
|
|
ui->telemetryTable->setSortingEnabled(true);
|
|
ui->motionTable->setSortingEnabled(true);
|
|
// Add context menu to allow hiding/showing of columns
|
|
packetsTableMenu = new QMenu(ui->packetsTable);
|
|
for (int i = 0; i < ui->packetsTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->packetsTable->horizontalHeaderItem(i)->text();
|
|
packetsTableMenu->addAction(packetsTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->packetsTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->packetsTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(packetsTable_columnSelectMenu(QPoint)));
|
|
|
|
weatherTableMenu = new QMenu(ui->weatherTable);
|
|
for (int i = 0; i < ui->weatherTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->weatherTable->horizontalHeaderItem(i)->text();
|
|
weatherTableMenu->addAction(weatherTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->weatherTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->weatherTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(weatherTable_columnSelectMenu(QPoint)));
|
|
|
|
statusTableMenu = new QMenu(ui->statusTable);
|
|
for (int i = 0; i < ui->statusTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->statusTable->horizontalHeaderItem(i)->text();
|
|
statusTableMenu->addAction(statusTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->statusTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->statusTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(statusTable_columnSelectMenu(QPoint)));
|
|
|
|
messagesTableMenu = new QMenu(ui->messagesTable);
|
|
for (int i = 0; i < ui->messagesTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->messagesTable->horizontalHeaderItem(i)->text();
|
|
messagesTableMenu->addAction(messagesTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->messagesTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->messagesTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(messagesTable_columnSelectMenu(QPoint)));
|
|
|
|
telemetryTableMenu = new QMenu(ui->telemetryTable);
|
|
for (int i = 0; i < ui->telemetryTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->telemetryTable->horizontalHeaderItem(i)->text();
|
|
telemetryTableMenu->addAction(telemetryTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->telemetryTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->telemetryTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(telemetryTable_columnSelectMenu(QPoint)));
|
|
|
|
motionTableMenu = new QMenu(ui->motionTable);
|
|
for (int i = 0; i < ui->motionTable->horizontalHeader()->count(); i++)
|
|
{
|
|
QString text = ui->motionTable->horizontalHeaderItem(i)->text();
|
|
motionTableMenu->addAction(motionTable_createCheckableItem(text, i, true));
|
|
}
|
|
ui->motionTable->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->motionTable->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(motionTable_columnSelectMenu(QPoint)));
|
|
// Get signals when columns change
|
|
connect(ui->packetsTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(packetsTable_sectionMoved(int, int, int)));
|
|
connect(ui->packetsTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(packetsTable_sectionResized(int, int, int)));
|
|
connect(ui->weatherTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(weatherTable_sectionMoved(int, int, int)));
|
|
connect(ui->weatherTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(weatherTable_sectionResized(int, int, int)));
|
|
connect(ui->statusTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(statusTable_sectionMoved(int, int, int)));
|
|
connect(ui->statusTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(statusTable_sectionResized(int, int, int)));
|
|
connect(ui->messagesTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(messagesTable_sectionMoved(int, int, int)));
|
|
connect(ui->messagesTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(messagesTable_sectionResized(int, int, int)));
|
|
connect(ui->telemetryTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(telemetryTable_sectionMoved(int, int, int)));
|
|
connect(ui->telemetryTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(telemetryTable_sectionResized(int, int, int)));
|
|
connect(ui->motionTable->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(motionTable_sectionMoved(int, int, int)));
|
|
connect(ui->motionTable->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(motionTable_sectionResized(int, int, int)));
|
|
|
|
m_weatherChart.setTheme(QChart::ChartThemeDark);
|
|
m_weatherChart.legend()->hide();
|
|
ui->weatherChart->setChart(&m_weatherChart);
|
|
ui->weatherChart->setRenderHint(QPainter::Antialiasing);
|
|
m_weatherChart.addAxis(&m_weatherChartXAxis, Qt::AlignBottom);
|
|
m_weatherChart.addAxis(&m_weatherChartYAxis, Qt::AlignLeft);
|
|
m_weatherChart.layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_weatherChart.setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
m_telemetryChart.setTheme(QChart::ChartThemeDark);
|
|
m_telemetryChart.legend()->hide();
|
|
ui->telemetryChart->setChart(&m_telemetryChart);
|
|
ui->telemetryChart->setRenderHint(QPainter::Antialiasing);
|
|
m_telemetryChart.addAxis(&m_telemetryChartXAxis, Qt::AlignBottom);
|
|
m_telemetryChart.addAxis(&m_telemetryChartYAxis, Qt::AlignLeft);
|
|
m_telemetryChart.layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_telemetryChart.setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
m_motionChart.setTheme(QChart::ChartThemeDark);
|
|
m_motionChart.legend()->hide();
|
|
ui->motionChart->setChart(&m_motionChart);
|
|
ui->motionChart->setRenderHint(QPainter::Antialiasing);
|
|
m_motionChart.addAxis(&m_motionChartXAxis, Qt::AlignBottom);
|
|
m_motionChart.addAxis(&m_motionChartYAxis, Qt::AlignLeft);
|
|
m_motionChart.layout()->setContentsMargins(0, 0, 0, 0);
|
|
m_motionChart.setMargins(QMargins(1, 1, 1, 1));
|
|
|
|
m_settings.setRollupState(&m_rollupState);
|
|
|
|
m_aprs->getInputMessageQueue()->push(APRS::MsgQueryAvailableChannels::create());
|
|
|
|
displaySettings();
|
|
applySettings(true);
|
|
makeUIConnections();
|
|
}
|
|
|
|
APRSGUI::~APRSGUI()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void APRSGUI::setWorkspaceIndex(int index)
|
|
{
|
|
m_settings.m_workspaceIndex = index;
|
|
m_feature->setWorkspaceIndex(index);
|
|
}
|
|
|
|
void APRSGUI::blockApplySettings(bool block)
|
|
{
|
|
m_doApplySettings = !block;
|
|
}
|
|
|
|
bool APRSGUI::filterStation(APRSStation *station)
|
|
{
|
|
switch (m_settings.m_stationFilter)
|
|
{
|
|
case APRSSettings::ALL:
|
|
return false;
|
|
case APRSSettings::STATIONS:
|
|
return station->m_isObject;
|
|
case APRSSettings::OBJECTS:
|
|
return !station->m_isObject;
|
|
case APRSSettings::WEATHER:
|
|
return !station->m_hasWeather;
|
|
case APRSSettings::TELEMETRY:
|
|
return !station->m_hasTelemetry;
|
|
case APRSSettings::COURSE_AND_SPEED:
|
|
return !station->m_hasCourseAndSpeed;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void APRSGUI::filterStations()
|
|
{
|
|
ui->stationSelect->clear();
|
|
QHashIterator<QString,APRSStation *> i(m_stations);
|
|
while (i.hasNext())
|
|
{
|
|
i.next();
|
|
APRSStation *station = i.value();
|
|
if (!filterStation(station))
|
|
{
|
|
ui->stationSelect->addItem(station->m_station);
|
|
}
|
|
}
|
|
}
|
|
|
|
void APRSGUI::displayTableSettings(QTableWidget *table, QMenu *menu, int *columnSizes, int *columnIndexes, int columns)
|
|
{
|
|
QHeaderView *header = table->horizontalHeader();
|
|
for (int i = 0; i < columns; i++)
|
|
{
|
|
bool hidden = columnSizes[i] == 0;
|
|
header->setSectionHidden(i, hidden);
|
|
menu->actions().at(i)->setChecked(!hidden);
|
|
if (columnSizes[i] > 0)
|
|
table->setColumnWidth(i, columnSizes[i]);
|
|
header->moveSection(header->visualIndex(i), columnIndexes[i]);
|
|
}
|
|
}
|
|
|
|
void APRSGUI::displaySettings()
|
|
{
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
setWindowTitle(m_settings.m_title);
|
|
setTitle(m_settings.m_title);
|
|
blockApplySettings(true);
|
|
ui->igate->setChecked(m_settings.m_igateEnabled);
|
|
ui->stationFilter->setCurrentIndex((int)m_settings.m_stationFilter);
|
|
ui->filterAddressee->setText(m_settings.m_filterAddressee);
|
|
setUnits();
|
|
|
|
// Order and size columns
|
|
displayTableSettings(ui->packetsTable, packetsTableMenu, m_settings.m_packetsTableColumnSizes, m_settings.m_packetsTableColumnIndexes, APRS_PACKETS_TABLE_COLUMNS);
|
|
displayTableSettings(ui->weatherTable, weatherTableMenu, m_settings.m_weatherTableColumnSizes, m_settings.m_weatherTableColumnIndexes, APRS_WEATHER_TABLE_COLUMNS);
|
|
displayTableSettings(ui->statusTable, statusTableMenu, m_settings.m_statusTableColumnSizes, m_settings.m_statusTableColumnIndexes, APRS_STATUS_TABLE_COLUMNS);
|
|
displayTableSettings(ui->messagesTable, messagesTableMenu, m_settings.m_messagesTableColumnSizes, m_settings.m_messagesTableColumnIndexes, APRS_MESSAGES_TABLE_COLUMNS);
|
|
displayTableSettings(ui->telemetryTable, telemetryTableMenu, m_settings.m_telemetryTableColumnSizes, m_settings.m_telemetryTableColumnIndexes, APRS_TELEMETRY_TABLE_COLUMNS);
|
|
displayTableSettings(ui->motionTable, motionTableMenu, m_settings.m_motionTableColumnSizes, m_settings.m_motionTableColumnIndexes, APRS_MOTION_TABLE_COLUMNS);
|
|
|
|
getRollupContents()->restoreState(m_rollupState);
|
|
|
|
blockApplySettings(false);
|
|
}
|
|
|
|
void APRSGUI::updateChannelList()
|
|
{
|
|
ui->sourcePipes->blockSignals(true);
|
|
ui->sourcePipes->clear();
|
|
|
|
for (const auto& channel : m_availableChannels) {
|
|
ui->sourcePipes->addItem(tr("R%1:%2 %3").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex).arg(channel.m_type));
|
|
}
|
|
|
|
ui->sourcePipes->blockSignals(false);
|
|
}
|
|
|
|
void APRSGUI::resizeEvent(QResizeEvent* event)
|
|
{
|
|
// Replot graphs to ensure Axis are visible
|
|
plotWeather();
|
|
plotTelemetry();
|
|
plotMotion();
|
|
FeatureGUI::resizeEvent(event);
|
|
}
|
|
|
|
void APRSGUI::onMenuDialogCalled(const QPoint &p)
|
|
{
|
|
if (m_contextMenuType == ContextMenuChannelSettings)
|
|
{
|
|
BasicFeatureSettingsDialog dialog(this);
|
|
dialog.setTitle(m_settings.m_title);
|
|
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
|
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
|
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
|
dialog.setReverseAPIFeatureSetIndex(m_settings.m_reverseAPIFeatureSetIndex);
|
|
dialog.setReverseAPIFeatureIndex(m_settings.m_reverseAPIFeatureIndex);
|
|
dialog.setDefaultTitle(m_displayedName);
|
|
|
|
dialog.move(p);
|
|
dialog.exec();
|
|
|
|
m_settings.m_title = dialog.getTitle();
|
|
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
|
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
|
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
|
m_settings.m_reverseAPIFeatureSetIndex = dialog.getReverseAPIFeatureSetIndex();
|
|
m_settings.m_reverseAPIFeatureIndex = dialog.getReverseAPIFeatureIndex();
|
|
|
|
setTitle(m_settings.m_title);
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
|
|
applySettings();
|
|
}
|
|
|
|
resetContextMenuType();
|
|
}
|
|
|
|
void APRSGUI::updateSummary(APRSStation *station)
|
|
{
|
|
ui->station->setText(station->m_station);
|
|
ui->reportingStation->setText(station->m_reportingStation);
|
|
ui->symbol->setPixmap(QPixmap(QString(":%1").arg(station->m_symbolImage)));
|
|
ui->status->setText(station->m_latestStatus);
|
|
ui->comment->setText(station->m_latestComment);
|
|
ui->position->setText(station->m_latestPosition);
|
|
ui->altitude->setText(convertAltitude(station->m_latestAltitude));
|
|
ui->course->setText(station->m_latestCourse);
|
|
ui->speed->setText(convertSpeed(station->m_latestSpeed));
|
|
ui->txPower->setText(station->m_powerWatts);
|
|
ui->antennaHeight->setText(convertAltitude(station->m_antennaHeightFt));
|
|
ui->antennaGain->setText(station->m_antennaGainDB);
|
|
ui->antennaDirectivity->setText(station->m_antennaDirectivity);
|
|
ui->radioRange->setText(station->m_radioRange);
|
|
if (!station->m_packets.isEmpty())
|
|
ui->lastPacket->setText(station->m_packets.last()->m_dateTime.toString());
|
|
else
|
|
ui->lastPacket->setText("");
|
|
}
|
|
|
|
QString APRSGUI::formatDate(APRSPacket *aprs)
|
|
{
|
|
if (aprs->m_hasTimestamp)
|
|
return aprs->date();
|
|
else
|
|
return aprs->m_dateTime.date().toString("yyyy/MM/dd");
|
|
}
|
|
|
|
QString APRSGUI::formatTime(APRSPacket *aprs)
|
|
{
|
|
// Add suffix T to indicate timestamp used
|
|
if (aprs->m_hasTimestamp)
|
|
return QString("%1 T").arg(aprs->time());
|
|
else
|
|
return aprs->m_dateTime.time().toString("hh:mm:ss");
|
|
}
|
|
|
|
double APRSGUI::applyCoefficients(int idx, int value, APRSStation *station)
|
|
{
|
|
if (station->m_hasTelemetryCoefficients > idx)
|
|
return station->m_telemetryCoefficientsA[idx] * value * value + station->m_telemetryCoefficientsB[idx] * value + station->m_telemetryCoefficientsC[idx];
|
|
else
|
|
return (double)idx;
|
|
}
|
|
|
|
void APRSGUI::addPacketToGUI(APRSStation *station, APRSPacket *aprs)
|
|
{
|
|
int row;
|
|
// Weather table
|
|
if (aprs->m_hasWeather)
|
|
{
|
|
row = ui->weatherTable->rowCount();
|
|
ui->weatherTable->setRowCount(row + 1);
|
|
QTableWidgetItem *weatherDateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *weatherTimeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *windDirectionItem = new QTableWidgetItem();
|
|
QTableWidgetItem *windSpeedItem = new QTableWidgetItem();
|
|
QTableWidgetItem *gustsItem = new QTableWidgetItem();
|
|
QTableWidgetItem *temperatureItem = new QTableWidgetItem();
|
|
QTableWidgetItem *humidityItem = new QTableWidgetItem();
|
|
QTableWidgetItem *pressureItem = new QTableWidgetItem();
|
|
QTableWidgetItem *rainLastHourItem = new QTableWidgetItem();
|
|
QTableWidgetItem *rainLast24HoursItem = new QTableWidgetItem();
|
|
QTableWidgetItem *rainSinceMidnightItem = new QTableWidgetItem();
|
|
QTableWidgetItem *luminosityItem = new QTableWidgetItem();
|
|
QTableWidgetItem *snowfallItem = new QTableWidgetItem();
|
|
QTableWidgetItem *radiationLevelItem = new QTableWidgetItem();
|
|
QTableWidgetItem *floodLevelItem = new QTableWidgetItem();
|
|
ui->weatherTable->setItem(row, WEATHER_COL_DATE, weatherDateItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_TIME, weatherTimeItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_WIND_DIRECTION, windDirectionItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_WIND_SPEED, windSpeedItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_GUSTS, gustsItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_TEMPERATURE, temperatureItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_HUMIDITY, humidityItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_PRESSURE, pressureItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_LAST_HOUR, rainLastHourItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_LAST_24_HOURS, rainLast24HoursItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_SINCE_MIDNIGHT, rainSinceMidnightItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_LUMINOSITY, luminosityItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_SNOWFALL, snowfallItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RADIATION_LEVEL, radiationLevelItem);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_FLOOD_LEVEL, floodLevelItem);
|
|
weatherDateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
weatherTimeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
if (aprs->m_hasWindDirection)
|
|
windDirectionItem->setData(Qt::DisplayRole, aprs->m_windDirection);
|
|
if (aprs->m_hasWindSpeed)
|
|
windSpeedItem->setData(Qt::DisplayRole, aprs->m_windSpeed);
|
|
if (aprs->m_hasGust)
|
|
gustsItem->setData(Qt::DisplayRole, aprs->m_gust);
|
|
if (aprs->m_hasTemp)
|
|
temperatureItem->setData(Qt::DisplayRole, convertTemperature(aprs->m_temp));
|
|
if (aprs->m_hasHumidity)
|
|
humidityItem->setData(Qt::DisplayRole, aprs->m_humidity);
|
|
if (aprs->m_hasBarometricPressure)
|
|
pressureItem->setData(Qt::DisplayRole, aprs->m_barometricPressure/10.0f);
|
|
if (aprs->m_hasRainLastHr)
|
|
rainLastHourItem->setData(Qt::DisplayRole, convertRainfall(aprs->m_rainLastHr));
|
|
if (aprs->m_hasRainLast24Hrs)
|
|
rainLast24HoursItem->setData(Qt::DisplayRole, convertRainfall(aprs->m_rainLast24Hrs));
|
|
if (aprs->m_hasRainSinceMidnight)
|
|
rainSinceMidnightItem->setData(Qt::DisplayRole, convertRainfall(aprs->m_rainSinceMidnight));
|
|
if (aprs->m_hasLuminsoity)
|
|
luminosityItem->setData(Qt::DisplayRole, aprs->m_luminosity);
|
|
if (aprs->m_hasSnowfallLast24Hrs)
|
|
snowfallItem->setData(Qt::DisplayRole, aprs->m_snowfallLast24Hrs);
|
|
if (aprs->m_hasRadiationLevel)
|
|
radiationLevelItem->setData(Qt::DisplayRole, aprs->m_hasRadiationLevel);
|
|
if (aprs->m_hasFloodLevel)
|
|
floodLevelItem->setData(Qt::DisplayRole, aprs->m_floodLevel);
|
|
}
|
|
|
|
// Status table
|
|
if (aprs->m_hasStatus)
|
|
{
|
|
row = ui->statusTable->rowCount();
|
|
ui->statusTable->setRowCount(row + 1);
|
|
QTableWidgetItem *statusDateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusTimeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusSymbolItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusMaidenheadItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusBeamHeadingItem = new QTableWidgetItem();
|
|
QTableWidgetItem *statusBeamPowerItem = new QTableWidgetItem();
|
|
ui->statusTable->setItem(row, STATUS_COL_DATE, statusDateItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_TIME, statusTimeItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_STATUS, statusItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_SYMBOL, statusSymbolItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_MAIDENHEAD, statusMaidenheadItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_BEAM_HEADING, statusBeamHeadingItem);
|
|
ui->statusTable->setItem(row, STATUS_COL_BEAM_POWER, statusBeamPowerItem);
|
|
statusDateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
statusTimeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
statusItem->setText(aprs->m_status);
|
|
if (aprs->m_hasSymbol)
|
|
{
|
|
statusSymbolItem->setSizeHint(QSize(24, 24));
|
|
statusSymbolItem->setIcon(QIcon(QString(":%1").arg(station->m_symbolImage)));
|
|
}
|
|
statusMaidenheadItem->setText(aprs->m_maidenhead);
|
|
if (aprs->m_hasBeam)
|
|
{
|
|
statusBeamHeadingItem->setData(Qt::DisplayRole, aprs->m_beamHeading);
|
|
statusBeamPowerItem->setData(Qt::DisplayRole, aprs->m_beamPower);
|
|
}
|
|
}
|
|
|
|
// Telemetry table
|
|
if (aprs->m_hasTelemetry)
|
|
{
|
|
row = ui->telemetryTable->rowCount();
|
|
ui->telemetryTable->setRowCount(row + 1);
|
|
QTableWidgetItem *telemetryDateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryTimeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetrySeqNoItem = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryA1Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryA2Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryA3Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryA4Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryA5Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB1Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB2Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB3Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB4Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB5Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB6Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB7Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryB8Item = new QTableWidgetItem();
|
|
QTableWidgetItem *telemetryCommentItem = new QTableWidgetItem();
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_DATE, telemetryDateItem);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_TIME, telemetryTimeItem);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_SEQ_NO, telemetrySeqNoItem);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A1, telemetryA1Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A2, telemetryA2Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A3, telemetryA3Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A4, telemetryA4Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A5, telemetryA5Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B1, telemetryB1Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B2, telemetryB2Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B3, telemetryB3Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B4, telemetryB4Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B5, telemetryB5Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B6, telemetryB6Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B7, telemetryB7Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B8, telemetryB8Item);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_COMMENT, telemetryCommentItem);
|
|
telemetryDateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
telemetryTimeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
if (aprs->m_hasSeqNo)
|
|
telemetrySeqNoItem->setData(Qt::DisplayRole, aprs->m_seqNo);
|
|
if (aprs->m_a1HasValue)
|
|
telemetryA1Item->setData(Qt::DisplayRole, applyCoefficients(0, aprs->m_a1, station));
|
|
if (aprs->m_a2HasValue)
|
|
telemetryA2Item->setData(Qt::DisplayRole, applyCoefficients(1, aprs->m_a2, station));
|
|
if (aprs->m_a3HasValue)
|
|
telemetryA3Item->setData(Qt::DisplayRole, applyCoefficients(2, aprs->m_a3, station));
|
|
if (aprs->m_a4HasValue)
|
|
telemetryA4Item->setData(Qt::DisplayRole, applyCoefficients(3, aprs->m_a4, station));
|
|
if (aprs->m_a5HasValue)
|
|
telemetryA5Item->setData(Qt::DisplayRole, applyCoefficients(4, aprs->m_a5, station));
|
|
if (aprs->m_bHasValue)
|
|
{
|
|
telemetryB1Item->setData(Qt::DisplayRole, aprs->m_b[0] ? 1 : 0);
|
|
telemetryB2Item->setData(Qt::DisplayRole, aprs->m_b[1] ? 1 : 0);
|
|
telemetryB3Item->setData(Qt::DisplayRole, aprs->m_b[2] ? 1 : 0);
|
|
telemetryB4Item->setData(Qt::DisplayRole, aprs->m_b[3] ? 1 : 0);
|
|
telemetryB5Item->setData(Qt::DisplayRole, aprs->m_b[4] ? 1 : 0);
|
|
telemetryB6Item->setData(Qt::DisplayRole, aprs->m_b[5] ? 1 : 0);
|
|
telemetryB7Item->setData(Qt::DisplayRole, aprs->m_b[6] ? 1 : 0);
|
|
telemetryB8Item->setData(Qt::DisplayRole, aprs->m_b[7] ? 1 : 0);
|
|
}
|
|
telemetryCommentItem->setText(aprs->m_telemetryComment);
|
|
|
|
for (int i = 0; i < station->m_telemetryNames.size(); i++)
|
|
{
|
|
QString header;
|
|
if (station->m_telemetryLabels.size() <= i)
|
|
header = station->m_telemetryNames[i];
|
|
else
|
|
header = QString("%1 (%2)").arg(station->m_telemetryNames[i]).arg(station->m_telemetryLabels[i]);
|
|
ui->telemetryTable->horizontalHeaderItem(TELEMETRY_COL_A1+i)->setText(header);
|
|
}
|
|
}
|
|
|
|
// Motion table
|
|
if (aprs->m_hasPosition || aprs->m_hasAltitude || aprs->m_hasCourseAndSpeed)
|
|
{
|
|
row = ui->motionTable->rowCount();
|
|
ui->motionTable->setRowCount(row + 1);
|
|
QTableWidgetItem *motionDateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *motionTimeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *latitudeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *longitudeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *altitudeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *courseItem = new QTableWidgetItem();
|
|
QTableWidgetItem *speedItem = new QTableWidgetItem();
|
|
ui->motionTable->setItem(row, MOTION_COL_DATE, motionDateItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_TIME, motionTimeItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_LATITUDE, latitudeItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_LONGITUDE, longitudeItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_ALTITUDE, altitudeItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_COURSE, courseItem);
|
|
ui->motionTable->setItem(row, MOTION_COL_SPEED, speedItem);
|
|
motionDateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
motionTimeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
if (aprs->m_hasPosition)
|
|
{
|
|
latitudeItem->setData(Qt::DisplayRole, aprs->m_latitude);
|
|
longitudeItem->setData(Qt::DisplayRole, aprs->m_longitude);
|
|
}
|
|
if (aprs->m_hasAltitude)
|
|
altitudeItem->setData(Qt::DisplayRole, convertAltitude(aprs->m_altitudeFt));
|
|
if (aprs->m_hasCourseAndSpeed)
|
|
{
|
|
courseItem->setData(Qt::DisplayRole, aprs->m_course);
|
|
speedItem->setData(Qt::DisplayRole, convertSpeed(aprs->m_speed));
|
|
}
|
|
}
|
|
|
|
// Packets table
|
|
row = ui->packetsTable->rowCount();
|
|
ui->packetsTable->setRowCount(row + 1);
|
|
QTableWidgetItem *dateItem = new QTableWidgetItem();
|
|
QTableWidgetItem *timeItem = new QTableWidgetItem();
|
|
QTableWidgetItem *fromItem = new QTableWidgetItem();
|
|
QTableWidgetItem *toItem = new QTableWidgetItem();
|
|
QTableWidgetItem *viaItem = new QTableWidgetItem();
|
|
QTableWidgetItem *dataItem = new QTableWidgetItem();
|
|
ui->packetsTable->setItem(row, PACKETS_COL_DATE, dateItem);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_TIME, timeItem);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_FROM, fromItem);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_TO, toItem);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_VIA, viaItem);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_DATA, dataItem);
|
|
dateItem->setData(Qt::DisplayRole, formatDate(aprs));
|
|
timeItem->setData(Qt::DisplayRole, formatTime(aprs));
|
|
fromItem->setText(aprs->m_from);
|
|
toItem->setText(aprs->m_to);
|
|
viaItem->setText(aprs->m_via);
|
|
dataItem->setText(aprs->m_data);
|
|
}
|
|
|
|
void APRSGUI::on_stationFilter_currentIndexChanged(int index)
|
|
{
|
|
m_settings.m_stationFilter = static_cast<APRSSettings::StationFilter>(index);
|
|
applySettings();
|
|
filterStations();
|
|
}
|
|
|
|
void APRSGUI::on_stationSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
QString stationCallsign = ui->stationSelect->currentText();
|
|
|
|
APRSStation *station = m_stations.value(stationCallsign);
|
|
if (station == nullptr)
|
|
{
|
|
qDebug() << "APRSGUI::on_stationSelect_currentIndexChanged - station==nullptr";
|
|
return;
|
|
}
|
|
|
|
// Clear tables
|
|
ui->weatherTable->setRowCount(0);
|
|
ui->packetsTable->setRowCount(0);
|
|
ui->statusTable->setRowCount(0);
|
|
ui->telemetryTable->setRowCount(0);
|
|
ui->motionTable->setRowCount(0);
|
|
|
|
// Set telemetry plot select combo text
|
|
const char *telemetryNames[] = {"A1", "A2", "A3", "A4", "A5", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"};
|
|
int telemetryNamesSize = station->m_telemetryNames.size();
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
if (i < telemetryNamesSize)
|
|
ui->telemetryPlotSelect->setItemText(i, station->m_telemetryNames[i]);
|
|
else
|
|
ui->telemetryPlotSelect->setItemText(i, telemetryNames[i]);
|
|
}
|
|
|
|
// Update summary table
|
|
updateSummary(station);
|
|
|
|
// Display/hide fields depending if station or object
|
|
ui->stationObjectLabel->setText(station->m_isObject ? "Object" : "Station");
|
|
ui->station->setToolTip(station->m_isObject ? "Object name" : "Station callsign and substation ID");
|
|
ui->reportingStation->setVisible(station->m_isObject);
|
|
ui->reportingStationLabel->setVisible(station->m_isObject);
|
|
ui->status->setVisible(!station->m_isObject);
|
|
ui->statusLabel->setVisible(!station->m_isObject);
|
|
if (station->m_isObject)
|
|
{
|
|
if ( ui->stationsTabWidget->count() == 6)
|
|
{
|
|
ui->stationsTabWidget->removeTab(3);
|
|
ui->stationsTabWidget->removeTab(3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ui->stationsTabWidget->count() != 6)
|
|
{
|
|
ui->stationsTabWidget->insertTab(3, ui->telemetryTab, "Telemetry");
|
|
ui->stationsTabWidget->insertTab(4, ui->statusTab, "Status");
|
|
}
|
|
}
|
|
|
|
// Add packets to tables
|
|
ui->packetsTable->setSortingEnabled(false);
|
|
ui->weatherTable->setSortingEnabled(false);
|
|
ui->statusTable->setSortingEnabled(false);
|
|
ui->telemetryTable->setSortingEnabled(false);
|
|
ui->motionTable->setSortingEnabled(false);
|
|
QListIterator<APRSPacket *> i(station->m_packets);
|
|
while (i.hasNext())
|
|
{
|
|
APRSPacket *aprs = i.next();
|
|
addPacketToGUI(station, aprs);
|
|
}
|
|
ui->packetsTable->setSortingEnabled(true);
|
|
ui->weatherTable->setSortingEnabled(true);
|
|
ui->statusTable->setSortingEnabled(true);
|
|
ui->telemetryTable->setSortingEnabled(true);
|
|
ui->motionTable->setSortingEnabled(true);
|
|
plotWeather();
|
|
plotTelemetry();
|
|
plotMotion();
|
|
}
|
|
|
|
void APRSGUI::on_filterAddressee_editingFinished()
|
|
{
|
|
m_settings.m_filterAddressee = ui->filterAddressee->text();
|
|
filterMessages();
|
|
applySettings();
|
|
}
|
|
|
|
void APRSGUI::filterMessageRow(int row)
|
|
{
|
|
bool hidden = false;
|
|
if (m_settings.m_filterAddressee != "")
|
|
{
|
|
QRegExp re(m_settings.m_filterAddressee);
|
|
QTableWidgetItem *addressee = ui->messagesTable->item(row, MESSAGE_COL_ADDRESSEE);
|
|
if (!re.exactMatch(addressee->text()))
|
|
hidden = true;
|
|
}
|
|
ui->messagesTable->setRowHidden(row, hidden);
|
|
}
|
|
|
|
void APRSGUI::filterMessages()
|
|
{
|
|
for (int i = 0; i < ui->messagesTable->rowCount(); i++)
|
|
filterMessageRow(i);
|
|
}
|
|
|
|
void APRSGUI::on_deleteMessages_clicked()
|
|
{
|
|
QList<QTableWidgetItem *> list = ui->messagesTable->selectedItems();
|
|
QList<int> rows;
|
|
if (list.isEmpty())
|
|
{
|
|
// Delete all messages
|
|
if (QMessageBox::Yes == QMessageBox::question(this, "Delete all messages", "Delete all messages?", QMessageBox::Yes|QMessageBox::No))
|
|
ui->messagesTable->setRowCount(0);
|
|
}
|
|
else
|
|
{
|
|
// Delete selected messages (in reverse order)
|
|
for (int i = 0; i < list.size(); i++)
|
|
{
|
|
int row = list[i]->row();
|
|
if (!rows.contains(row))
|
|
rows.append(row);
|
|
}
|
|
std::sort(rows.begin(), rows.end(), std::greater<int>());
|
|
QListIterator<int> itr(rows);
|
|
while (itr.hasNext())
|
|
ui->messagesTable->removeRow(itr.next());
|
|
}
|
|
}
|
|
|
|
static void addToSeries(QLineSeries *series, const QDateTime& dt, double value, double& min, double &max)
|
|
{
|
|
series->append(dt.toMSecsSinceEpoch(), value);
|
|
if (value < min)
|
|
min = value;
|
|
if (value > max)
|
|
max = value;
|
|
}
|
|
|
|
void APRSGUI::plotWeather()
|
|
{
|
|
QString stationCallsign = ui->stationSelect->currentText();
|
|
if (stationCallsign.isEmpty())
|
|
return;
|
|
APRSStation *station = m_stations.value(stationCallsign);
|
|
if (station == nullptr)
|
|
return;
|
|
|
|
QLineSeries *series = new QLineSeries();
|
|
double minValue = INFINITY;
|
|
double maxValue = -INFINITY;
|
|
|
|
int timeSelectIdx = ui->weatherTimeSelect->currentIndex();
|
|
int plotSelectIdx = ui->weatherPlotSelect->currentIndex();
|
|
QDateTime limit = calcTimeLimit(timeSelectIdx);
|
|
|
|
QListIterator<APRSPacket *> i(station->m_packets);
|
|
while (i.hasNext())
|
|
{
|
|
APRSPacket *aprs = i.next();
|
|
if (aprs->m_hasWeather)
|
|
{
|
|
QDateTime dt;
|
|
if (aprs->m_hasTimestamp)
|
|
dt = aprs->m_timestamp;
|
|
else
|
|
dt = aprs->m_dateTime;
|
|
|
|
if (dt >= limit)
|
|
{
|
|
if (plotSelectIdx == 0 && aprs->m_hasWindDirection)
|
|
addToSeries(series, dt, aprs->m_windDirection, minValue, maxValue);
|
|
else if (plotSelectIdx == 1 && aprs->m_hasWindSpeed)
|
|
addToSeries(series, dt, aprs->m_windSpeed, minValue, maxValue);
|
|
else if (plotSelectIdx == 2 && aprs->m_hasGust)
|
|
addToSeries(series, dt, aprs->m_gust, minValue, maxValue);
|
|
else if (plotSelectIdx == 3 && aprs->m_hasTemp)
|
|
addToSeries(series, dt, convertTemperature(aprs->m_temp), minValue, maxValue);
|
|
else if (plotSelectIdx == 4 && aprs->m_hasHumidity)
|
|
addToSeries(series, dt, aprs->m_humidity, minValue, maxValue);
|
|
else if (plotSelectIdx == 5 && aprs->m_hasBarometricPressure)
|
|
addToSeries(series, dt, aprs->m_barometricPressure/10.0, minValue, maxValue);
|
|
else if (plotSelectIdx == 6 && aprs->m_hasRainLastHr)
|
|
addToSeries(series, dt, convertRainfall(aprs->m_rainLastHr), minValue, maxValue);
|
|
else if (plotSelectIdx == 7 && aprs->m_hasRainLast24Hrs)
|
|
addToSeries(series, dt, convertRainfall(aprs->m_rainLast24Hrs), minValue, maxValue);
|
|
else if (plotSelectIdx == 8 && aprs->m_hasRainSinceMidnight)
|
|
addToSeries(series, dt, convertRainfall(aprs->m_rainSinceMidnight), minValue, maxValue);
|
|
else if (plotSelectIdx == 9 && aprs->m_hasLuminsoity)
|
|
addToSeries(series, dt, aprs->m_luminosity, minValue, maxValue);
|
|
else if (plotSelectIdx == 10 && aprs->m_hasSnowfallLast24Hrs)
|
|
addToSeries(series, dt, aprs->m_snowfallLast24Hrs, minValue, maxValue);
|
|
else if (plotSelectIdx == 11 && aprs->m_hasRadiationLevel)
|
|
addToSeries(series, dt, aprs->m_radiationLevel, minValue, maxValue);
|
|
else if (plotSelectIdx == 12 && aprs->m_hasFloodLevel)
|
|
addToSeries(series, dt, aprs->m_floodLevel, minValue, maxValue);
|
|
}
|
|
}
|
|
}
|
|
m_weatherChart.removeAllSeries();
|
|
m_weatherChart.removeAxis(&m_weatherChartXAxis);
|
|
m_weatherChart.removeAxis(&m_weatherChartYAxis);
|
|
|
|
m_weatherChart.addSeries(series);
|
|
|
|
calcTimeAxis(timeSelectIdx, &m_weatherChartXAxis, series, ui->weatherChart->width());
|
|
|
|
m_weatherChart.addAxis(&m_weatherChartXAxis, Qt::AlignBottom);
|
|
|
|
series->attachAxis(&m_weatherChartXAxis);
|
|
|
|
m_weatherChartYAxis.setTitleText(ui->weatherPlotSelect->currentText());
|
|
calcYAxis(minValue, maxValue, &m_weatherChartYAxis);
|
|
m_weatherChart.addAxis(&m_weatherChartYAxis, Qt::AlignLeft);
|
|
series->attachAxis(&m_weatherChartYAxis);
|
|
}
|
|
|
|
void APRSGUI::on_weatherTimeSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotWeather();
|
|
}
|
|
|
|
void APRSGUI::on_weatherPlotSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotWeather();
|
|
}
|
|
|
|
void APRSGUI::calcTimeAxis(int timeSelectIdx, QDateTimeAxis *axis, QLineSeries *series, int width)
|
|
{
|
|
QDateTime startX = QDateTime::currentDateTime();
|
|
QDateTime finishX = QDateTime::currentDateTime();
|
|
finishX.setTime(QTime(0,0,0));
|
|
finishX = finishX.addDays(1);
|
|
int ticksScale = width < 650 ? 1 : 2; // FIXME: Should probably measure the width of some actual text
|
|
switch (timeSelectIdx)
|
|
{
|
|
case 0: // Today
|
|
startX.setTime(QTime(0,0,0));
|
|
axis->setTickCount(6*ticksScale+1);
|
|
axis->setFormat("hh:mm");
|
|
axis->setTitleText(QString("Time (%1)").arg(startX.date().toString()));
|
|
break;
|
|
case 1: // Last hour
|
|
startX.setTime(startX.time().addSecs(-60*60));
|
|
finishX = QDateTime::currentDateTime();
|
|
ticksScale = width < 750 ? 1 : 2;
|
|
axis->setTickCount(8*ticksScale+1);
|
|
axis->setFormat("hh:mm");
|
|
axis->setTitleText(QString("Time (%1)").arg(startX.date().toString()));
|
|
break;
|
|
case 2: // Last 24h
|
|
startX.setDate(startX.date().addDays(-1));
|
|
finishX = QDateTime::currentDateTime();
|
|
axis->setTickCount(6*ticksScale+1);
|
|
axis->setFormat("hh:mm");
|
|
axis->setTitleText(QString("Time (%1)").arg(startX.date().toString()));
|
|
break;
|
|
case 3: // Last 7 days
|
|
startX.setTime(QTime(0,0,0));
|
|
startX.setDate(finishX.date().addDays(-7));
|
|
axis->setTickCount(4*ticksScale);
|
|
axis->setFormat("MMM d");
|
|
axis->setTitleText("Date");
|
|
break;
|
|
case 4: // Last 30 days
|
|
startX.setTime(QTime(0,0,0));
|
|
startX.setDate(finishX.date().addDays(-30));
|
|
axis->setTickCount(5*ticksScale+1);
|
|
axis->setFormat("MMM d");
|
|
axis->setTitleText("Date");
|
|
break;
|
|
case 5: // All
|
|
startX = QDateTime::fromMSecsSinceEpoch(series->at(0).x());
|
|
finishX = QDateTime::fromMSecsSinceEpoch(series->at(series->count()-1).x());
|
|
// FIXME: Problem when startX == finishX - axis not drawn
|
|
if (startX.msecsTo(finishX) > 1000*60*60*24)
|
|
{
|
|
axis->setFormat("MMM d");
|
|
axis->setTitleText("Date");
|
|
}
|
|
else if (startX.msecsTo(finishX) > 1000*60*60)
|
|
{
|
|
axis->setFormat("hh:mm");
|
|
axis->setTitleText(QString("Time (hours on %1)").arg(startX.date().toString()));
|
|
}
|
|
else
|
|
{
|
|
axis->setFormat("mm:ss");
|
|
axis->setTitleText(QString("Time (minutes on %1)").arg(startX.date().toString()));
|
|
}
|
|
axis->setTickCount(5*ticksScale);
|
|
break;
|
|
}
|
|
axis->setRange(startX, finishX);
|
|
}
|
|
|
|
void APRSGUI::calcYAxis(double minValue, double maxValue, QValueAxis *axis, bool binary, int precision)
|
|
{
|
|
double range = std::abs(maxValue - minValue);
|
|
double ticks = binary ? 2 : 5;
|
|
|
|
axis->setTickCount(ticks);
|
|
|
|
if (binary)
|
|
{
|
|
minValue = 0.0;
|
|
maxValue = 1.0;
|
|
}
|
|
else if (range == 0.0)
|
|
{
|
|
// Nothing is plotted if min and max are the same, so adjust range to non-zero
|
|
if (precision == 1)
|
|
{
|
|
if ((minValue >= (ticks-1)/2) || (minValue < 0.0))
|
|
{
|
|
maxValue += (ticks-1)/2;
|
|
minValue -= (ticks-1)/2;
|
|
}
|
|
else
|
|
maxValue = maxValue + (ticks - 1 - minValue);
|
|
}
|
|
else if (maxValue == 0.0)
|
|
maxValue = ticks-1;
|
|
else
|
|
{
|
|
minValue -= minValue * (1.0/std::pow(10.0,precision));
|
|
maxValue += maxValue * (1.0/std::pow(10.0,precision));
|
|
}
|
|
range = std::abs(maxValue - minValue);
|
|
}
|
|
axis->setRange(minValue, maxValue);
|
|
if (((range < (ticks-1)) || (precision > 1)) && !binary)
|
|
axis->setLabelFormat(QString("%.%1f").arg(precision));
|
|
else
|
|
axis->setLabelFormat("%d");
|
|
}
|
|
|
|
QDateTime APRSGUI::calcTimeLimit(int timeSelectIdx)
|
|
{
|
|
QDateTime limit = QDateTime::currentDateTime();
|
|
switch (timeSelectIdx)
|
|
{
|
|
case 0: // Today
|
|
limit.setTime(QTime(0,0,0));
|
|
break;
|
|
case 1: // Last hour
|
|
limit.setTime(limit.time().addSecs(-60*60));
|
|
break;
|
|
case 2: // Last 24h
|
|
limit.setDate(limit.date().addDays(-1));
|
|
break;
|
|
case 3: // Last 7 days
|
|
limit.setTime(QTime(0,0,0));
|
|
limit.setDate(limit.date().addDays(-7));
|
|
break;
|
|
case 4: // Last 30 days
|
|
limit.setTime(QTime(0,0,0));
|
|
limit.setDate(limit.date().addDays(-30));
|
|
break;
|
|
case 5: // All
|
|
limit = QDateTime(QDate(1970, 1, 1), QTime());
|
|
break;
|
|
}
|
|
return limit;
|
|
}
|
|
|
|
void APRSGUI::plotMotion()
|
|
{
|
|
QString stationCallsign = ui->stationSelect->currentText();
|
|
if (stationCallsign.isEmpty())
|
|
return;
|
|
APRSStation *station = m_stations.value(stationCallsign);
|
|
if (station == nullptr)
|
|
return;
|
|
|
|
QLineSeries *series = new QLineSeries();
|
|
double minValue = INFINITY;
|
|
double maxValue = -INFINITY;
|
|
|
|
int timeSelectIdx = ui->motionTimeSelect->currentIndex();
|
|
int plotSelectIdx = ui->motionPlotSelect->currentIndex();
|
|
QDateTime limit = calcTimeLimit(timeSelectIdx);
|
|
|
|
QListIterator<APRSPacket *> i(station->m_packets);
|
|
while (i.hasNext())
|
|
{
|
|
APRSPacket *aprs = i.next();
|
|
|
|
if (aprs->m_hasPosition || aprs->m_hasAltitude || aprs->m_hasCourseAndSpeed)
|
|
{
|
|
QDateTime dt;
|
|
if (aprs->m_hasTimestamp)
|
|
dt = aprs->m_timestamp;
|
|
else
|
|
dt = aprs->m_dateTime;
|
|
|
|
if (dt >= limit)
|
|
{
|
|
if (plotSelectIdx == 0 && aprs->m_hasPosition)
|
|
addToSeries(series, dt, aprs->m_latitude, minValue, maxValue);
|
|
else if (plotSelectIdx == 1 && aprs->m_hasPosition)
|
|
addToSeries(series, dt, aprs->m_longitude, minValue, maxValue);
|
|
else if (plotSelectIdx == 2 && aprs->m_hasAltitude)
|
|
addToSeries(series, dt, convertAltitude(aprs->m_altitudeFt), minValue, maxValue);
|
|
else if (plotSelectIdx == 3 && aprs->m_hasCourseAndSpeed)
|
|
addToSeries(series, dt, aprs->m_course, minValue, maxValue);
|
|
else if (plotSelectIdx == 4 && aprs->m_hasCourseAndSpeed)
|
|
addToSeries(series, dt, convertSpeed(aprs->m_speed), minValue, maxValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_motionChart.removeAllSeries();
|
|
m_motionChart.removeAxis(&m_motionChartXAxis);
|
|
m_motionChart.removeAxis(&m_motionChartYAxis);
|
|
|
|
m_motionChart.addSeries(series);
|
|
calcTimeAxis(timeSelectIdx, &m_motionChartXAxis, series, ui->motionChart->width());
|
|
m_motionChart.addAxis(&m_motionChartXAxis, Qt::AlignBottom);
|
|
series->attachAxis(&m_motionChartXAxis);
|
|
|
|
m_motionChartYAxis.setTitleText(ui->motionPlotSelect->currentText());
|
|
calcYAxis(minValue, maxValue, &m_motionChartYAxis, false, plotSelectIdx <= 1 ? 5 : 1);
|
|
m_motionChart.addAxis(&m_motionChartYAxis, Qt::AlignLeft);
|
|
series->attachAxis(&m_motionChartYAxis);
|
|
}
|
|
|
|
void APRSGUI::on_motionTimeSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotMotion();
|
|
}
|
|
|
|
void APRSGUI::on_motionPlotSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotMotion();
|
|
}
|
|
|
|
void APRSGUI::plotTelemetry()
|
|
{
|
|
QString stationCallsign = ui->stationSelect->currentText();
|
|
if (stationCallsign.isEmpty())
|
|
return;
|
|
APRSStation *station = m_stations.value(stationCallsign);
|
|
if (station == nullptr)
|
|
return;
|
|
|
|
QLineSeries *series = new QLineSeries();
|
|
double minValue = INFINITY;
|
|
double maxValue = -INFINITY;
|
|
|
|
int timeSelectIdx = ui->telemetryTimeSelect->currentIndex();
|
|
int plotSelectIdx = ui->telemetryPlotSelect->currentIndex();
|
|
QDateTime limit = calcTimeLimit(timeSelectIdx);
|
|
|
|
QListIterator<APRSPacket *> i(station->m_packets);
|
|
while (i.hasNext())
|
|
{
|
|
APRSPacket *aprs = i.next();
|
|
if (aprs->m_hasTelemetry)
|
|
{
|
|
if (aprs->m_dateTime >= limit)
|
|
{
|
|
if (plotSelectIdx == 0 && aprs->m_a1HasValue)
|
|
addToSeries(series, aprs->m_dateTime, applyCoefficients(0, aprs->m_a1, station), minValue, maxValue);
|
|
else if (plotSelectIdx == 1 && aprs->m_a2HasValue)
|
|
addToSeries(series, aprs->m_dateTime, applyCoefficients(1, aprs->m_a2, station), minValue, maxValue);
|
|
else if (plotSelectIdx == 2 && aprs->m_a3HasValue)
|
|
addToSeries(series, aprs->m_dateTime, applyCoefficients(2, aprs->m_a3, station), minValue, maxValue);
|
|
else if (plotSelectIdx == 3 && aprs->m_a4HasValue)
|
|
addToSeries(series, aprs->m_dateTime, applyCoefficients(3, aprs->m_a4, station), minValue, maxValue);
|
|
else if (plotSelectIdx == 4 && aprs->m_a5HasValue)
|
|
addToSeries(series, aprs->m_dateTime, applyCoefficients(4, aprs->m_a5, station), minValue, maxValue);
|
|
else if (plotSelectIdx == 5 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[0], minValue, maxValue);
|
|
else if (plotSelectIdx == 6 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[1], minValue, maxValue);
|
|
else if (plotSelectIdx == 7 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[2], minValue, maxValue);
|
|
else if (plotSelectIdx == 8 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[3], minValue, maxValue);
|
|
else if (plotSelectIdx == 9 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[4], minValue, maxValue);
|
|
else if (plotSelectIdx == 10 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[5], minValue, maxValue);
|
|
else if (plotSelectIdx == 11 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[6], minValue, maxValue);
|
|
else if (plotSelectIdx == 12 && aprs->m_bHasValue)
|
|
addToSeries(series, aprs->m_dateTime, aprs->m_b[7], minValue, maxValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_telemetryChart.removeAllSeries();
|
|
m_telemetryChart.removeAxis(&m_telemetryChartXAxis);
|
|
m_telemetryChart.removeAxis(&m_telemetryChartYAxis);
|
|
|
|
m_telemetryChart.addSeries(series);
|
|
calcTimeAxis(timeSelectIdx, &m_telemetryChartXAxis, series, ui->telemetryChart->width());
|
|
m_telemetryChart.addAxis(&m_telemetryChartXAxis, Qt::AlignBottom);
|
|
|
|
series->attachAxis(&m_telemetryChartXAxis);
|
|
m_telemetryChartYAxis.setTitleText(ui->telemetryPlotSelect->currentText());
|
|
calcYAxis(minValue, maxValue, &m_telemetryChartYAxis, plotSelectIdx >= 5);
|
|
m_telemetryChart.addAxis(&m_telemetryChartYAxis, Qt::AlignLeft);
|
|
series->attachAxis(&m_telemetryChartYAxis);
|
|
}
|
|
|
|
void APRSGUI::on_telemetryTimeSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotTelemetry();
|
|
}
|
|
|
|
void APRSGUI::on_telemetryPlotSelect_currentIndexChanged(int index)
|
|
{
|
|
(void) index;
|
|
plotTelemetry();
|
|
}
|
|
|
|
void APRSGUI::updateStatus()
|
|
{
|
|
int state = m_aprs->getState();
|
|
|
|
if (m_lastFeatureState != state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case Feature::StNotStarted:
|
|
ui->igate->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
|
break;
|
|
case Feature::StIdle:
|
|
ui->igate->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
|
break;
|
|
case Feature::StRunning:
|
|
ui->igate->setStyleSheet("QToolButton { background-color : green; }");
|
|
break;
|
|
case Feature::StError:
|
|
ui->igate->setStyleSheet("QToolButton { background-color : red; }");
|
|
QMessageBox::information(this, tr("Message"), m_aprs->getErrorMessage());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_lastFeatureState = state;
|
|
}
|
|
}
|
|
|
|
void APRSGUI::applySettings(bool force)
|
|
{
|
|
if (m_doApplySettings)
|
|
{
|
|
APRS::MsgConfigureAPRS* message = APRS::MsgConfigureAPRS::create(m_settings, force);
|
|
m_aprs->getInputMessageQueue()->push(message);
|
|
}
|
|
}
|
|
|
|
void APRSGUI::resizeTable()
|
|
{
|
|
int row;
|
|
|
|
// Fill tables with a row of dummy data that will size the columns nicely
|
|
row = ui->packetsTable->rowCount();
|
|
ui->packetsTable->setRowCount(row + 1);
|
|
ui->packetsTable->setItem(row, PACKETS_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->packetsTable->setItem(row, PACKETS_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->packetsTable->setItem(row, PACKETS_COL_FROM, new QTableWidgetItem("123456-15-"));
|
|
ui->packetsTable->setItem(row, PACKETS_COL_TO, new QTableWidgetItem("123456-15-"));
|
|
ui->packetsTable->setItem(row, PACKETS_COL_VIA, new QTableWidgetItem("123456-15-"));
|
|
ui->packetsTable->setItem(row, PACKETS_COL_DATA, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZABCEDGHIJKLMNOPQRSTUVWXYZABCEDGHIJKLMNOPQRSTUVWXYZ"));
|
|
ui->packetsTable->resizeColumnsToContents();
|
|
ui->packetsTable->removeRow(row);
|
|
|
|
row = ui->weatherTable->rowCount();
|
|
ui->weatherTable->setRowCount(row + 1);
|
|
ui->weatherTable->setItem(row, WEATHER_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_WIND_DIRECTION, new QTableWidgetItem("Dir (o)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_WIND_SPEED, new QTableWidgetItem("Speed (mph)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_GUSTS, new QTableWidgetItem("Gusts (mph)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_TEMPERATURE, new QTableWidgetItem("Temp (F)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_HUMIDITY, new QTableWidgetItem("Humidity (%)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_PRESSURE, new QTableWidgetItem("Pressure (mbar)-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_LAST_HOUR, new QTableWidgetItem("Rain 1h-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_LAST_24_HOURS, new QTableWidgetItem("Rain 24h-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RAIN_SINCE_MIDNIGHT, new QTableWidgetItem("Rain-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_LUMINOSITY, new QTableWidgetItem("Luminosity-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_SNOWFALL, new QTableWidgetItem("Snowfall-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_RADIATION_LEVEL, new QTableWidgetItem("Radiation-"));
|
|
ui->weatherTable->setItem(row, WEATHER_COL_FLOOD_LEVEL, new QTableWidgetItem("Flood level-"));
|
|
ui->weatherTable->resizeColumnsToContents();
|
|
ui->weatherTable->removeRow(row);
|
|
|
|
row = ui->statusTable->rowCount();
|
|
ui->statusTable->setRowCount(row + 1);
|
|
ui->statusTable->setIconSize(QSize(24, 24));
|
|
ui->statusTable->setItem(row, STATUS_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->statusTable->setItem(row, STATUS_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->statusTable->setItem(row, STATUS_COL_STATUS, new QTableWidgetItem("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
|
ui->statusTable->setItem(row, STATUS_COL_SYMBOL, new QTableWidgetItem("WWW"));
|
|
ui->statusTable->setItem(row, STATUS_COL_MAIDENHEAD, new QTableWidgetItem("WW00WW"));
|
|
ui->statusTable->setItem(row, STATUS_COL_BEAM_HEADING, new QTableWidgetItem("359"));
|
|
ui->statusTable->setItem(row, STATUS_COL_BEAM_POWER, new QTableWidgetItem("8000"));
|
|
ui->statusTable->resizeColumnsToContents();
|
|
ui->statusTable->removeRow(row);
|
|
|
|
row = ui->messagesTable->rowCount();
|
|
ui->messagesTable->setRowCount(row + 1);
|
|
ui->messagesTable->setIconSize(QSize(24, 24));
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_ADDRESSEE, new QTableWidgetItem("WWWWWWWWW"));
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_MESSAGE, new QTableWidgetItem("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
|
ui->messagesTable->setItem(row, MESSAGE_COL_NO, new QTableWidgetItem("Message No"));
|
|
ui->messagesTable->resizeColumnsToContents();
|
|
ui->messagesTable->removeRow(row);
|
|
|
|
row = ui->motionTable->rowCount();
|
|
ui->motionTable->setRowCount(row + 1);
|
|
ui->motionTable->setItem(row, MOTION_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->motionTable->setItem(row, MOTION_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->motionTable->setItem(row, MOTION_COL_LATITUDE, new QTableWidgetItem("Latitude"));
|
|
ui->motionTable->setItem(row, MOTION_COL_LONGITUDE, new QTableWidgetItem("Longitude"));
|
|
ui->motionTable->setItem(row, MOTION_COL_ALTITUDE, new QTableWidgetItem("Message No"));
|
|
ui->motionTable->setItem(row, MOTION_COL_COURSE, new QTableWidgetItem("Course"));
|
|
ui->motionTable->setItem(row, MOTION_COL_SPEED, new QTableWidgetItem("Speed"));
|
|
ui->motionTable->resizeColumnsToContents();
|
|
ui->motionTable->removeRow(row);
|
|
|
|
row = ui->telemetryTable->rowCount();
|
|
ui->telemetryTable->setRowCount(row + 1);
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_DATE, new QTableWidgetItem("31/12/2020"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_TIME, new QTableWidgetItem("23:59:39 T"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_SEQ_NO, new QTableWidgetItem("Seq No"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A1, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A2, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A3, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A4, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_A5, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B1, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B2, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B3, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B4, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B5, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B6, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B7, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->setItem(row, TELEMETRY_COL_B8, new QTableWidgetItem("ABCEDF"));
|
|
ui->telemetryTable->resizeColumnsToContents();
|
|
ui->telemetryTable->removeRow(row);
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::packetsTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_packetsTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::packetsTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_packetsTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::packetsTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
packetsTableMenu->popup(ui->packetsTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::packetsTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->packetsTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::weatherTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_weatherTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::weatherTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_weatherTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::weatherTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
weatherTableMenu->popup(ui->weatherTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::weatherTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->weatherTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::statusTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_statusTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::statusTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_statusTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::statusTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
statusTableMenu->popup(ui->statusTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::statusTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->statusTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::messagesTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_messagesTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::messagesTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_messagesTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::messagesTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
messagesTableMenu->popup(ui->messagesTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::messagesTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->messagesTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::telemetryTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_telemetryTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::telemetryTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_telemetryTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::telemetryTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
telemetryTableMenu->popup(ui->telemetryTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::telemetryTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->telemetryTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Columns in table reordered
|
|
void APRSGUI::motionTable_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
{
|
|
(void) oldVisualIndex;
|
|
|
|
m_settings.m_motionTableColumnIndexes[logicalIndex] = newVisualIndex;
|
|
}
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
void APRSGUI::motionTable_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
{
|
|
(void) oldSize;
|
|
|
|
m_settings.m_motionTableColumnSizes[logicalIndex] = newSize;
|
|
}
|
|
|
|
// Right click in table header - show column select menu
|
|
void APRSGUI::motionTable_columnSelectMenu(QPoint pos)
|
|
{
|
|
motionTableMenu->popup(ui->motionTable->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
// Hide/show column when menu selected
|
|
void APRSGUI::motionTable_columnSelectMenuChecked(bool checked)
|
|
{
|
|
(void) checked;
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
if (action != nullptr)
|
|
{
|
|
int idx = action->data().toInt(nullptr);
|
|
ui->motionTable->setColumnHidden(idx, !action->isChecked());
|
|
}
|
|
}
|
|
|
|
// Create column select menu items
|
|
|
|
QAction *APRSGUI::packetsTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(packetsTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
QAction *APRSGUI::weatherTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(weatherTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
QAction *APRSGUI::statusTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(statusTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
QAction *APRSGUI::messagesTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(messagesTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
QAction *APRSGUI::telemetryTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(telemetryTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
QAction *APRSGUI::motionTable_createCheckableItem(QString &text, int idx, bool checked)
|
|
{
|
|
QAction *action = new QAction(text, this);
|
|
action->setCheckable(true);
|
|
action->setChecked(checked);
|
|
action->setData(QVariant(idx));
|
|
connect(action, SIGNAL(triggered()), this, SLOT(motionTable_columnSelectMenuChecked()));
|
|
return action;
|
|
}
|
|
|
|
void APRSGUI::setUnits()
|
|
{
|
|
ui->altitudeUnitsLabel->setText(APRSSettings::m_altitudeUnitNames[m_settings.m_altitudeUnits]);
|
|
ui->antennaHeightUnitsLabel->setText(APRSSettings::m_altitudeUnitNames[m_settings.m_altitudeUnits]);
|
|
ui->speedUnitsLabel->setText(APRSSettings::m_speedUnitNames[m_settings.m_speedUnits]);
|
|
|
|
ui->weatherTable->horizontalHeaderItem(WEATHER_COL_TEMPERATURE)->setText(QString("Temp (%1)").arg(APRSSettings::m_temperatureUnitNames[m_settings.m_temperatureUnits]));
|
|
|
|
// Display data using new units
|
|
int idx = ui->stationSelect->currentIndex();
|
|
if (idx >= 0)
|
|
on_stationSelect_currentIndexChanged(idx);
|
|
}
|
|
|
|
QString APRSGUI::convertAltitude(const QString& altitude)
|
|
{
|
|
if ((m_settings.m_altitudeUnits == APRSSettings::FEET) || altitude.isEmpty())
|
|
return altitude;
|
|
else
|
|
return QString::number((int)std::round(Units::feetToMetres(altitude.toFloat())));
|
|
}
|
|
|
|
float APRSGUI::convertAltitude(float altitude)
|
|
{
|
|
if (m_settings.m_altitudeUnits == APRSSettings::FEET)
|
|
return altitude;
|
|
else
|
|
return std::round(Units::feetToMetres(altitude));
|
|
}
|
|
|
|
QString APRSGUI::convertSpeed(const QString& speed)
|
|
{
|
|
if ((m_settings.m_speedUnits == APRSSettings::KNOTS) || speed.isEmpty())
|
|
return speed;
|
|
else if (m_settings.m_speedUnits == APRSSettings::MPH)
|
|
return QString::number(Units::knotsToIntegerMPH(speed.toFloat()));
|
|
else
|
|
return QString::number(Units::knotsToIntegerKPH(speed.toFloat()));
|
|
}
|
|
|
|
int APRSGUI::convertSpeed(int speed)
|
|
{
|
|
if (m_settings.m_speedUnits == APRSSettings::KNOTS)
|
|
return speed;
|
|
else if (m_settings.m_speedUnits == APRSSettings::MPH)
|
|
return Units::knotsToIntegerMPH(speed);
|
|
else
|
|
return Units::knotsToIntegerKPH(speed);
|
|
}
|
|
|
|
int APRSGUI::convertTemperature(int temperature)
|
|
{
|
|
if (m_settings.m_temperatureUnits == APRSSettings::FAHRENHEIT)
|
|
return temperature;
|
|
else
|
|
return (int)std::round(Units::fahrenheitToCelsius(temperature));
|
|
}
|
|
|
|
int APRSGUI::convertRainfall(int rainfall)
|
|
{
|
|
if (m_settings.m_rainfallUnits == APRSSettings::HUNDREDTHS_OF_AN_INCH)
|
|
return rainfall;
|
|
else
|
|
return (int)std::round(Units::inchesToMilimetres(rainfall/100.0f));
|
|
}
|
|
|
|
// Show settings dialog
|
|
void APRSGUI::on_displaySettings_clicked()
|
|
{
|
|
APRSSettingsDialog dialog(&m_settings);
|
|
if (dialog.exec() == QDialog::Accepted)
|
|
{
|
|
setUnits();
|
|
applySettings();
|
|
}
|
|
}
|
|
|
|
void APRSGUI::on_igate_toggled(bool checked)
|
|
{
|
|
m_settings.m_igateEnabled = checked;
|
|
applySettings();
|
|
}
|
|
|
|
// Find the selected station on the Map
|
|
void APRSGUI::on_viewOnMap_clicked()
|
|
{
|
|
QString stationName = ui->stationSelect->currentText();
|
|
if (!stationName.isEmpty())
|
|
{
|
|
APRSStation *station = m_stations.value(stationName);
|
|
if (station != nullptr)
|
|
{
|
|
FeatureWebAPIUtils::mapFind(station->m_station);
|
|
}
|
|
}
|
|
}
|
|
|
|
void APRSGUI::makeUIConnections()
|
|
{
|
|
QObject::connect(ui->stationFilter, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_stationFilter_currentIndexChanged);
|
|
QObject::connect(ui->stationSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_stationSelect_currentIndexChanged);
|
|
QObject::connect(ui->filterAddressee, &QLineEdit::editingFinished, this, &APRSGUI::on_filterAddressee_editingFinished);
|
|
QObject::connect(ui->deleteMessages, &QPushButton::clicked, this, &APRSGUI::on_deleteMessages_clicked);
|
|
QObject::connect(ui->weatherTimeSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_weatherTimeSelect_currentIndexChanged);
|
|
QObject::connect(ui->weatherPlotSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_weatherPlotSelect_currentIndexChanged);
|
|
QObject::connect(ui->telemetryTimeSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_telemetryTimeSelect_currentIndexChanged);
|
|
QObject::connect(ui->telemetryPlotSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_telemetryPlotSelect_currentIndexChanged);
|
|
QObject::connect(ui->motionTimeSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_motionTimeSelect_currentIndexChanged);
|
|
QObject::connect(ui->motionPlotSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &APRSGUI::on_motionPlotSelect_currentIndexChanged);
|
|
QObject::connect(ui->displaySettings, &QPushButton::clicked, this, &APRSGUI::on_displaySettings_clicked);
|
|
QObject::connect(ui->igate, &ButtonSwitch::toggled, this, &APRSGUI::on_igate_toggled);
|
|
QObject::connect(ui->viewOnMap, &QPushButton::clicked, this, &APRSGUI::on_viewOnMap_clicked);
|
|
}
|