mirror of
https://github.com/JS8Call-improved/JS8Call-improved
synced 2026-08-13 17:47:36 -04:00
mainwindow.cpp: move ensureMessageDock() member function to its own file
MessagePanel.cpp: fix column width/sort ordering bugs in populateMessages()
UI_Constructor.cpp: build custom title bar once in ensureMessageDock()
after messageDock_ exists, instead of inside the toggled handler,
ensures dock is restored visible on startup
ensureMessageDock.cpp: new member function file that handles the Message Inbox
and turns it into a Qt::Window instead of a Qt::Tool when
it is in floating mode
CMakeLists.txt: add ensureMessageDock.cpp to compile object code for the member function
1081 lines
38 KiB
C++
1081 lines
38 KiB
C++
// -*- Mode: C++ -*-
|
|
#ifndef MAINWINDOW_H
|
|
#define MAINWINDOW_H
|
|
|
|
#include "JS8_Audio/AudioDevice.h"
|
|
#include "JS8_Audio/NotificationAudio.h"
|
|
#include "JS8_Audio/SoundInput.h"
|
|
#include "JS8_Audio/SoundOutput.h"
|
|
#include "JS8_Include/EventFilter.h"
|
|
#include "JS8_Include/commons.h"
|
|
#include "JS8_Include/qpriorityqueue.h"
|
|
#include "JS8_JSC/JSC_checker.h"
|
|
#include "JS8_Logbook/LogBook.h"
|
|
#include "JS8_Main/APRSISClient.h"
|
|
#include "JS8_Main/AprsInboundRelay.h"
|
|
#include "JS8_Main/Bands.h"
|
|
#include "JS8_Main/DirectedMessageHighlighter.h"
|
|
#include "JS8_Main/DriftingDateTime.h"
|
|
#include "JS8_Main/FrequencyList.h"
|
|
#include "JS8_Main/Geodesic.h"
|
|
#include "JS8_Main/HBBlockingDB.h"
|
|
#include "JS8_Main/HelpTextWindow.h"
|
|
#include "JS8_Main/Inbox.h"
|
|
#include "JS8_Main/JS8MessageBox.h"
|
|
#include "JS8_Main/MessageClient.h"
|
|
#include "JS8_Main/MessageServer.h"
|
|
#include "JS8_Main/Modes.h"
|
|
#include "JS8_Main/MultiSettings.h"
|
|
#include "JS8_Main/PillRenderer.h"
|
|
#include "JS8_Main/ProcessThread.h"
|
|
#include "JS8_Main/Radio.h"
|
|
#include "JS8_Main/SelfDestructMessageBox.h"
|
|
#include "JS8_Main/SignalMeter.h"
|
|
#include "JS8_Main/StationList.h"
|
|
#include "JS8_Main/TxLoop.h"
|
|
#include "JS8_Main/Varicode.h"
|
|
#include "JS8_Main/qt_helpers.h"
|
|
#include "JS8_Main/revision_utils.h"
|
|
#include "JS8_Mode/DecodedText.h"
|
|
#include "JS8_Mode/Decoder.h"
|
|
#include "JS8_Mode/Detector.h"
|
|
#include "JS8_Mode/JS8.h"
|
|
#include "JS8_Mode/JS8Submode.h"
|
|
#include "JS8_Mode/Modulator.h"
|
|
#include "JS8_Network/NetworkAccessManager.h"
|
|
#include "JS8_Network/PSKReporter.h"
|
|
#include "JS8_Network/SpotClient.h"
|
|
#include "JS8_Network/TCPClient.h"
|
|
#include "JS8_Transceiver/Transceiver.h"
|
|
#include "JS8_Transceiver/TransceiverFactory.h"
|
|
#include "JS8_UDP/WSJTXMessageClient.h"
|
|
#include "JS8_UDP/WSJTXMessageMapper.h"
|
|
#include "JS8_UI/About.h"
|
|
#include "JS8_UI/Configuration.h"
|
|
#include "JS8_UI/WideGraph.h"
|
|
#include "JS8_UI/MessagePanel.h"
|
|
#include "LogQSO.h"
|
|
#include "MessageReplyDialog.h"
|
|
#include "styles.h"
|
|
#include "ui_mainwindow.h"
|
|
|
|
#include <QAbstractSocket>
|
|
#include <QAction>
|
|
#include <QActionGroup>
|
|
#include <QApplication>
|
|
#include <QAudioDevice>
|
|
#include <QByteArrayView>
|
|
#include <QCursor>
|
|
#include <QDateTime>
|
|
#include <QDesktopServices>
|
|
#include <QDir>
|
|
#include <QDockWidget>
|
|
#include <QElapsedTimer>
|
|
#include <QFileDialog>
|
|
#include <QHash>
|
|
#include <QHostAddress>
|
|
#include <QHostInfo>
|
|
#include <QInputDialog>
|
|
#include <QLabel>
|
|
#include <QLineEdit>
|
|
#include <QList>
|
|
#include <QLoggingCategory>
|
|
#include <QMainWindow>
|
|
#include <QMdiSubWindow>
|
|
#include <QMutex>
|
|
#include <QMutexLocker>
|
|
#include <QNetworkAccessManager>
|
|
#include <QNetworkReply>
|
|
#include <QNetworkRequest>
|
|
#include <QPair>
|
|
#include <QPixmap>
|
|
#include <QPointer>
|
|
#include <QProgressBar>
|
|
#include <QProgressDialog>
|
|
#include <QRegularExpression>
|
|
#include <QRegularExpressionValidator>
|
|
#include <QScopedPointer>
|
|
#include <QScrollBar>
|
|
#include <QSet>
|
|
#include <QSoundEffect>
|
|
#include <QStandardPaths>
|
|
#include <QStringBuilder>
|
|
#include <QStyleFactory>
|
|
#include <QtCore/QtGlobal>
|
|
#include <QTableWidget>
|
|
#include <QTextEdit>
|
|
#include <QThread>
|
|
#include <QTime>
|
|
#include <QTimeZone>
|
|
#include <QTimer>
|
|
#include <QToolButton>
|
|
#include <QToolTip>
|
|
#include <QUdpSocket>
|
|
#include <QUrl>
|
|
#include <QVariant>
|
|
#include <QVector>
|
|
#include <QVersionNumber>
|
|
#include <QtConcurrent/QtConcurrentRun>
|
|
#include <QtGui>
|
|
#include <boost/crc.hpp>
|
|
#include <fftw3.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <complex>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6,11, 1)
|
|
#error "Qt >= 6.11.1 is required to build JS8Call. Please upgrade your Qt toolchain."
|
|
#endif
|
|
|
|
Q_DECLARE_LOGGING_CATEGORY(mainwindow_js8)
|
|
|
|
extern int volatile itone[JS8_NUM_SYMBOLS]; // Audio tones for all Tx symbols
|
|
|
|
//--------------------------------------------------------------- mainwindow
|
|
// How often to poll the UI, in MS.
|
|
// Some things may depend on this being a divisor of 1000.
|
|
constexpr quint32 UI_POLL_INTERVAL_MS = 100;
|
|
|
|
namespace {
|
|
namespace Default {
|
|
constexpr Radio::Frequency DIAL_FREQUENCY = 14078000;
|
|
constexpr auto FREQUENCY = 1500;
|
|
constexpr auto SUBMODE = Varicode::JS8CallNormal;
|
|
} // namespace Default
|
|
|
|
namespace State {
|
|
constexpr auto RX = 1;
|
|
constexpr auto TX = 2;
|
|
} // namespace State
|
|
} // namespace
|
|
|
|
namespace Ui {
|
|
class UI_Constructor;
|
|
}
|
|
|
|
class QSettings;
|
|
class QLineEdit;
|
|
class QFont;
|
|
class QHostInfo;
|
|
class WideGraph;
|
|
class LogQSO;
|
|
class Transceiver;
|
|
class MessageClient;
|
|
class QTime;
|
|
class HelpTextWindow;
|
|
class SoundOutput;
|
|
class Modulator;
|
|
class SoundInput;
|
|
class Detector;
|
|
class AprsInboundRelay;
|
|
class MultiSettings;
|
|
class DecodedText;
|
|
class JSCChecker;
|
|
class MessagePanel;
|
|
|
|
using namespace std;
|
|
typedef std::function<void()> Callback;
|
|
|
|
class WSJTXMessageMapper; // Forward declaration
|
|
|
|
class UI_Constructor : public QMainWindow {
|
|
Q_OBJECT;
|
|
friend class WSJTXMessageMapper; // Allow WSJTXMessageMapper to access
|
|
// private members
|
|
|
|
struct CallDetail;
|
|
struct CommandDetail;
|
|
|
|
public:
|
|
using Frequency = Radio::Frequency;
|
|
using FrequencyDelta = Radio::FrequencyDelta;
|
|
using Mode = Modes::Mode;
|
|
|
|
explicit UI_Constructor(QString const &program_info,
|
|
QDir const &temp_directory, bool multiple,
|
|
MultiSettings *settings, QWidget *parent = nullptr);
|
|
~UI_Constructor();
|
|
|
|
private:
|
|
struct SortByReverse {
|
|
QString by;
|
|
bool reverse;
|
|
};
|
|
|
|
/**
|
|
* Sometimes, buttons are triggered by code and this refers
|
|
* to a momentary situation only. Sometimes, the trigger
|
|
* pertains to the long term processing, that is, it should
|
|
* influence (shut off, mostly) the automatic transmit loops
|
|
* for HB and CQ.
|
|
*
|
|
* In an ugly klugde, we try to distinguish these cases
|
|
* via these three *IsLongterm variables.
|
|
*/
|
|
bool m_stopTxButtonIsLongterm;
|
|
bool m_hbButtonIsLongterm;
|
|
bool m_cqButtonIsLongterm;
|
|
|
|
public slots:
|
|
void showSoundInError(const QString &errorMsg);
|
|
void showSoundOutError(const QString &errorMsg);
|
|
void showStatusMessage(const QString &statusMsg);
|
|
void dataSink(qint64 frames); // JS8_Mainwindow/dataSink.cpp
|
|
/**
|
|
* The name `guiUpdate` suggests updating of the views from the models
|
|
* (in MVC terms, but we don't do MVC in this project), animations and
|
|
* stuff. While it indeed does that, this also contains controller code.
|
|
*/
|
|
void guiUpdate();
|
|
void setXIT(int n);
|
|
void qsy(int hzDelta);
|
|
void onDriftChanged(qint64 new_drift_ms);
|
|
bool tryRestoreFreqOffset();
|
|
void changeFreq(int);
|
|
|
|
bool hasExistingMessageBufferToMe(int *pOffset);
|
|
bool hasExistingMessageBuffer(int submode, int offset, bool drift,
|
|
int *pPrevOffset);
|
|
bool hasClosedExistingMessageBuffer(int offset);
|
|
void logCallActivity(CallDetail d, bool spot = true);
|
|
void logHeardGraph(QString from, QString to);
|
|
QString lookupCallInCompoundCache(QString const &call);
|
|
void cacheActivity(QString key);
|
|
void restoreActivity(QString key);
|
|
void clearActivity();
|
|
void clearBandActivity();
|
|
void clearRXActivity();
|
|
void clearCallActivity();
|
|
void createGroupCallsignTableRows(QTableWidget *table,
|
|
const QString &selectedCall,
|
|
bool &showIconColumn);
|
|
void displayTextForFreq(QString text, int freq, QDateTime date, bool isTx,
|
|
bool isNewLine, bool isLast);
|
|
void writeNoticeTextToUI(QDateTime date, QString text);
|
|
int writeMessageTextToUI(QDateTime date, QString text, int freq, bool isTx,
|
|
int block = -1);
|
|
bool isMessageQueuedForTransmit();
|
|
bool isInDecodeDelayThreshold(int seconds);
|
|
void prependMessageText(QString text);
|
|
void addMessageText(QString text, bool clear = false,
|
|
bool selectFirstPlaceholder = false);
|
|
void confirmThenEnqueueMessage(int timeout, int priority, QString message,
|
|
int offset, Callback c); // JS8_Mainwindow/confirmThenEnqueueMessage.cpp
|
|
void enqueueMessage(int priority, QString message, int offset, Callback c);
|
|
void resetMessage();
|
|
void resetMessageUI();
|
|
void restoreMessage();
|
|
void initializeDummyData(); // JS8_Mainwindow/initializeDummyData.cpp
|
|
void initializeGroupMessage(); // JS8_Mainwindow/initializeGroupMessage.cpp
|
|
bool ensureCallsignSet(bool alert = true);
|
|
bool ensureKeyNotStuck(QString const &text);
|
|
bool ensureNotIdle();
|
|
bool ensureCanTransmit();
|
|
bool ensureCreateMessageReady(const QString &text);
|
|
QString createMessage(QString const &text, bool *pDisableTypeahead);
|
|
QString appendMessage(QString const &text, bool isData,
|
|
bool *pDisableTypeahead);
|
|
QString createMessageTransmitQueue(QString const &text, bool reset,
|
|
bool isData, bool *pDisableTypeahead);
|
|
void resetMessageTransmitQueue();
|
|
QPair<QString, int> popMessageFrame();
|
|
void tryNotify(const QString &key);
|
|
void processDecodeEvent(JS8::Event::Variant const &); // JS8_Mainwindow/processDecodeEvent.cpp
|
|
|
|
void updateCQButtonDisplay();
|
|
void updateHBButtonDisplay();
|
|
|
|
protected:
|
|
void keyPressEvent(QKeyEvent *) override;
|
|
void closeEvent(QCloseEvent *) override;
|
|
void childEvent(QChildEvent *) override;
|
|
bool eventFilter(QObject *, QEvent *) override;
|
|
|
|
private slots:
|
|
void initialize_fonts();
|
|
void on_menuModeJS8_aboutToShow();
|
|
void on_menuControl_aboutToShow();
|
|
void on_actionCheck_for_Updates_triggered();
|
|
void on_actionEnable_Monitor_RX_toggled(bool checked);
|
|
void on_actionEnable_Transmitter_TX_toggled(bool checked);
|
|
void on_actionEnable_Reporting_SPOT_toggled(bool checked);
|
|
void on_actionEnable_Tuning_Tone_TUNE_toggled(bool checked);
|
|
void on_menuWindow_aboutToShow();
|
|
void on_actionFocus_Message_Receive_Area_triggered();
|
|
void on_actionFocus_Message_Reply_Area_triggered();
|
|
void on_actionFocus_Band_Activity_Table_triggered();
|
|
void on_actionFocus_Call_Activity_Table_triggered();
|
|
void on_actionClear_All_Activity_triggered();
|
|
void on_actionClear_Band_Activity_triggered();
|
|
void on_actionClear_RX_Activity_triggered();
|
|
void on_actionClear_Call_Activity_triggered();
|
|
void on_actionSetOffset_triggered();
|
|
void on_actionShow_Fullscreen_triggered(bool checked);
|
|
void on_actionShow_Statusbar_triggered(bool checked);
|
|
void on_actionShow_Frequency_Clock_triggered(bool checked);
|
|
void on_actionShow_Band_Activity_triggered(bool checked);
|
|
void on_actionShow_Band_Heartbeats_and_ACKs_triggered(bool checked);
|
|
void on_actionShow_Call_Activity_triggered(bool checked);
|
|
void on_actionShow_Waterfall_triggered(bool checked);
|
|
void on_actionShow_Waterfall_Controls_triggered(bool checked);
|
|
void on_actionShow_Waterfall_Time_Drift_Controls_triggered(bool checked);
|
|
void on_actionReset_Window_Sizes_triggered();
|
|
void on_actionSettings_triggered();
|
|
void openSettings(int tab = 0);
|
|
void prepareApi();
|
|
void prepareSpotting();
|
|
void on_spotButton_clicked(bool checked);
|
|
void on_monitorButton_clicked(bool);
|
|
void on_actionAbout_triggered();
|
|
void resetPushButtonToggleText(QPushButton *btn);
|
|
void on_stopTxButton_clicked();
|
|
void on_dialFreqUpButton_clicked();
|
|
void on_dialFreqDownButton_clicked();
|
|
void on_actionAdd_Log_Entry_triggered();
|
|
void on_actionOpen_log_directory_triggered();
|
|
void on_actionCopyright_Notice_triggered();
|
|
void on_actionUser_Guide_triggered();
|
|
bool decode(qint32 k);
|
|
bool isDecodeReady(int submode, qint32 k, qint32 k0,
|
|
qint32 *pCurrentDecodeStart, qint32 *pNextDecodeStart,
|
|
qint32 *pStart, qint32 *pSz, qint32 *pCycle);
|
|
bool decodeEnqueueReady(qint32 k, qint32 k0);
|
|
bool decodeEnqueueReadyExperiment(qint32 k, qint32 k0);
|
|
bool decodeProcessQueue(qint32 *pSubmode);
|
|
void decodeStart();
|
|
void decodeBusy(bool b);
|
|
void decodeDone();
|
|
void on_startTxButton_toggled(bool checked);
|
|
void toggleTx(bool start);
|
|
void on_logQSOButton_clicked();
|
|
void on_actionModeJS8HB_toggled(bool checked);
|
|
void on_actionModeJS8Normal_triggered();
|
|
void on_actionModeJS8Fast_triggered();
|
|
void on_actionModeJS8Turbo_triggered();
|
|
void on_actionModeJS8Slow_triggered();
|
|
void on_actionModeJS8Ultra_triggered();
|
|
void on_actionHeartbeatAcknowledgements_toggled(bool checked);
|
|
void on_actionModeMultiDecoder_toggled(bool checked);
|
|
void on_actionModeAutoreply_toggled(bool checked);
|
|
bool canCurrentModeSendHeartbeat() const;
|
|
void prepareMonitorControls();
|
|
void prepareHeartbeatMode(bool enabled);
|
|
void f11f12(int n);
|
|
void on_actionErase_ALL_TXT_triggered();
|
|
void on_actionErase_js8call_log_adi_triggered();
|
|
void startTx();
|
|
void stopTx();
|
|
void stopTx2();
|
|
void buildFrequencyMenu(QMenu *menu);
|
|
void buildHeartbeatMenu(QMenu *menu);
|
|
void buildCQMenu(QMenu *menu);
|
|
void buildRepeatMenu(QMenu *menu, QPushButton *button, bool isLowInterval,
|
|
int *interval);
|
|
void sendHB();
|
|
void sendHeartbeatAck(QString to, int snr, QString extra);
|
|
void on_hbMacroButton_toggled(bool checked);
|
|
void on_hbMacroButton_clicked();
|
|
void sendCQ(bool repeat = false);
|
|
void on_cqMacroButton_toggled(bool checked);
|
|
void on_cqMacroButton_clicked();
|
|
void on_replyMacroButton_clicked();
|
|
void on_snrMacroButton_clicked();
|
|
void on_infoMacroButton_clicked();
|
|
void on_statusMacroButton_clicked();
|
|
void setShowColumn(QString tableKey, QString columnKey, bool value);
|
|
bool showColumn(QString tableKey, QString columnKey, bool default_ = true);
|
|
void buildShowColumnsMenu(QMenu *menu, QString tableKey);
|
|
void setSortBy(QString key, QString value);
|
|
QString getSortBy(QString const &key, QString const &defaultValue) const;
|
|
SortByReverse getSortByReverse(QString const &key,
|
|
QString const &defaultValue) const;
|
|
void buildSortByMenu(QMenu *menu, QString key, QString defaultValue,
|
|
QList<QPair<QString, QString>> values);
|
|
void buildBandActivitySortByMenu(QMenu *menu);
|
|
void buildCallActivitySortByMenu(QMenu *menu);
|
|
void buildQueryMenu(QMenu *, QString callsign); // JS8_Mainwindow/buildQueryMenu.cpp
|
|
QMap<QString, QString> buildMacroValues();
|
|
void buildColumnLabelMap();
|
|
void buildSuggestionsMenu(QMenu *menu, QTextEdit *edit,
|
|
const QPoint &point);
|
|
void buildSavedMessagesMenu(QMenu *menu);
|
|
void buildRelayMenu(QMenu *menu);
|
|
QAction *buildRelayAction(QString call);
|
|
void buildEditMenu(QMenu *, QTextEdit *);
|
|
void on_queryButton_pressed();
|
|
void on_macrosMacroButton_pressed();
|
|
void on_deselectButton_pressed();
|
|
void on_tableWidgetRXAll_cellClicked(int row, int col);
|
|
void on_tableWidgetRXAll_cellDoubleClicked(int row, int col);
|
|
QString generateCallDetail(QString selectedCall);
|
|
void on_tableWidgetCalls_cellClicked(int row, int col);
|
|
void on_tableWidgetCalls_cellDoubleClicked(int row, int col);
|
|
QList<QPair<QString, int>> buildMessageFrames(QString const &text,
|
|
bool isData,
|
|
bool *pDisableTypeahead);
|
|
bool prepareNextMessageFrame();
|
|
bool isFreqOffsetFree(int f, int bw);
|
|
int findFreeFreqOffset(int fmin, int fmax, int bw);
|
|
void setDrift(int n);
|
|
void on_tuneButton_clicked(bool);
|
|
void acceptQSO(QDateTime const &, QString const &call, QString const &grid,
|
|
Frequency dial_freq, QString const &mode,
|
|
QString const &submode, QString const &rpt_sent,
|
|
QString const &rpt_received, QString const &comments,
|
|
QString const &name, QDateTime const &QSO_date_on,
|
|
QString const &operator_call, QString const &my_call,
|
|
QString const &my_grid, QByteArray const &ADIF,
|
|
QVariantMap const &additionalFields);
|
|
void on_readFreq_clicked();
|
|
void on_outAttenuation_valueChanged(int);
|
|
void rigOpen();
|
|
void handle_transceiver_update(Transceiver::TransceiverState const &);
|
|
void handle_transceiver_failure(QString const &reason);
|
|
void band_changed();
|
|
void monitor(bool);
|
|
void end_tuning();
|
|
void stop_tuning();
|
|
void stopTuneATU();
|
|
void auto_tx_mode(bool);
|
|
void on_monitorButton_toggled(bool checked);
|
|
void on_monitorTxButton_toggled(bool checked);
|
|
void on_tuneButton_toggled(bool checked);
|
|
void on_spotButton_toggled(bool checked);
|
|
|
|
void emitPTT(bool on);
|
|
void emitTones();
|
|
void udpNetworkMessage(Message const &message);
|
|
void tcpNetworkMessage(Message const &message);
|
|
void networkMessage(Message const &message); // JS8_Mainwindow/networkMessage.cpp
|
|
bool canSendNetworkMessage();
|
|
void sendNetworkMessage(QString const &type, QString const &message);
|
|
void sendNetworkMessage(QString const &type, QString const &message,
|
|
const QVariantMap ¶ms);
|
|
void pskReporterError(QString const &);
|
|
void TxAgain();
|
|
void checkVersion(bool alertOnUpToDate); // JS8_Mainwindow/checkVersion.cpp
|
|
void checkStartupWarnings();
|
|
void clearCallsignSelected();
|
|
void refreshTextDisplay();
|
|
|
|
void manualBandHop(const StationList::Station station);
|
|
|
|
private:
|
|
Q_SIGNAL void apiSetMaxConnections(int n);
|
|
Q_SIGNAL void apiSetServer(QString host, quint16 port);
|
|
Q_SIGNAL void apiStartServer();
|
|
Q_SIGNAL void apiStopServer();
|
|
|
|
Q_SIGNAL void aprsClientEnqueueSpot(QString by_call, QString from_call,
|
|
QString grid, QString comment);
|
|
Q_SIGNAL void aprsClientEnqueueThirdParty(QString by_call,
|
|
QString from_call, QString text);
|
|
/**
|
|
* @brief Send a standard APRS message ACK frame.
|
|
* @param from_call Source callsign for the ACK.
|
|
* @param to_call Destination callsign being acknowledged.
|
|
* @param messageId APRS message identifier to acknowledge.
|
|
*/
|
|
Q_SIGNAL void aprsClientEnqueueAck(QString from_call, QString to_call,
|
|
QString messageId);
|
|
Q_SIGNAL void aprsClientSetSkipPercent(float skipPercent);
|
|
Q_SIGNAL void aprsClientSetIncomingRelayEnabled(bool enabled);
|
|
Q_SIGNAL void aprsClientSetServer(QString host, quint16 port);
|
|
Q_SIGNAL void aprsClientSetPaused(bool paused);
|
|
Q_SIGNAL void aprsClientSetLocalStation(QString mycall, QString passcode);
|
|
Q_SIGNAL void aprsClientSendReports();
|
|
|
|
Q_SIGNAL void pskReporterSendReport(bool);
|
|
Q_SIGNAL void pskReporterSetLocalStation(QString, QString, QString);
|
|
Q_SIGNAL void pskReporterAddRemoteStation(QString, QString,
|
|
Radio::Frequency, QString, int,
|
|
QDateTime);
|
|
|
|
Q_SIGNAL void spotClientSetLocalStation(QString, QString, QString);
|
|
Q_SIGNAL void spotClientEnqueueCmd(QString, QString, QString, QString,
|
|
QString, QString, QString, int, int, int,
|
|
int);
|
|
Q_SIGNAL void spotClientEnqueueSpot(QString, QString, int, int, int, int);
|
|
|
|
Q_SIGNAL void decodedLineReady(QByteArray t);
|
|
Q_SIGNAL void playNotification(const QString &name);
|
|
Q_SIGNAL void initializeNotificationAudioOutputStream(const QAudioDevice &,
|
|
unsigned) const;
|
|
Q_SIGNAL void initializeAudioOutputStream(QAudioDevice, unsigned channels,
|
|
unsigned msBuffered) const;
|
|
Q_SIGNAL void stopAudioOutputStream() const;
|
|
Q_SIGNAL void startAudioInputStream(QAudioDevice const &,
|
|
int framesPerBuffer, AudioDevice *sink,
|
|
AudioDevice::Channel) const;
|
|
Q_SIGNAL void suspendAudioInputStream() const;
|
|
Q_SIGNAL void resumeAudioInputStream() const;
|
|
Q_SIGNAL void startDetector(AudioDevice::Channel) const;
|
|
Q_SIGNAL void FFTSize(unsigned) const;
|
|
Q_SIGNAL void detectorClose() const;
|
|
Q_SIGNAL void finished() const;
|
|
Q_SIGNAL void transmitFrequency(double) const;
|
|
Q_SIGNAL void endTransmitMessage(bool quick = false) const;
|
|
Q_SIGNAL void tune(bool = true) const;
|
|
Q_SIGNAL void sendMessage(double frequency, int submode, double txDelay,
|
|
SoundOutput *, AudioDevice::Channel) const;
|
|
Q_SIGNAL void outAttenuationChanged(qreal) const;
|
|
Q_SIGNAL void toggleShorthand() const;
|
|
Q_SIGNAL void submodeChanged(Varicode::SubmodeType) const;
|
|
|
|
Q_SIGNAL void messageAdded(int) const;
|
|
|
|
private:
|
|
QByteArray wisdomFileName() const;
|
|
|
|
void writeAllTxt(QStringView message);
|
|
void writeMsgTxt(QStringView message, int snr, int offset);
|
|
|
|
void currentTextChanged();
|
|
void tableSelectionChanged(QItemSelection const &, QItemSelection const &);
|
|
void setupJS8();
|
|
void applyPillSettings();
|
|
|
|
int freq() const { return m_freq; }
|
|
|
|
/** Sabotage transmission if frequency would result in WSPR band. */
|
|
void refuseToSendIn30mWSPRBand();
|
|
|
|
void prepareSending(qint64 nowMS);
|
|
|
|
/** Update the clock shown. */
|
|
void updateClockUI(const QDateTime &);
|
|
|
|
void setFreq(int);
|
|
void transmit();
|
|
|
|
bool presentlyWantHBReplies();
|
|
|
|
QString m_nextFreeTextMsg;
|
|
|
|
NetworkAccessManager m_network_manager;
|
|
bool m_valid;
|
|
[[maybe_unused]] bool m_multiple; // Used only in Windows builds
|
|
MultiSettings *m_multi_settings;
|
|
QPushButton *m_configurations_button;
|
|
QSettings *m_settings;
|
|
bool m_settings_read;
|
|
QScopedPointer<Ui::UI_Constructor> ui;
|
|
|
|
// other windows
|
|
Configuration m_config;
|
|
JS8MessageBox m_rigErrorMessageBox;
|
|
|
|
QDockWidget* messageDock_ = nullptr;
|
|
MessagePanel* messagePanel_ = nullptr;
|
|
QDialog* messageFloatDialog_ = nullptr;
|
|
QLineEdit* m_inboxFilterEdit_ = nullptr;
|
|
|
|
enum class MessageHost { Dock, Dialog };
|
|
MessageHost messageHost_ = MessageHost::Dock;
|
|
|
|
QScopedPointer<WideGraph> m_wideGraph;
|
|
QScopedPointer<LogQSO> m_logDlg;
|
|
QScopedPointer<HelpTextWindow> m_shortcuts;
|
|
QScopedPointer<HelpTextWindow> m_prefixes;
|
|
QScopedPointer<HelpTextWindow> m_mouseCmnds;
|
|
|
|
Transceiver::TransceiverState m_rigState;
|
|
Frequency m_lastDialFreq;
|
|
QString m_lastBand;
|
|
|
|
Detector *m_detector;
|
|
unsigned m_FFTSize;
|
|
SoundInput *m_soundInput;
|
|
Modulator *m_modulator;
|
|
SoundOutput *m_soundOutput;
|
|
NotificationAudio *m_notification;
|
|
|
|
// Configuration might one day offer to send a txDelayChanged signal.
|
|
// As long as it doesn't, we poll and compare with the previous value.
|
|
double m_TxDelay; // in seconds.
|
|
|
|
TxLoop *m_cq_loop;
|
|
TxLoop *m_hb_loop;
|
|
|
|
QThread m_networkThread;
|
|
QThread m_audioThread;
|
|
QThread m_notificationAudioThread;
|
|
JS8::Decoder m_decoder;
|
|
|
|
qint64 m_secBandChanged;
|
|
|
|
Frequency m_freqNominal;
|
|
Frequency m_freqTxNominal;
|
|
|
|
int m_freq;
|
|
|
|
qint32 m_XIT;
|
|
qint32 m_sec0;
|
|
qint32 m_RxLog;
|
|
qint32 m_nutc0;
|
|
// The period of the current submode, in seconds. (15 for normal, 10 for
|
|
// fast, ...)
|
|
qint32 m_TRperiod;
|
|
qint32 m_inGain;
|
|
qint32 m_idleMinutes;
|
|
qint32 m_nSubMode;
|
|
FrequencyList_v3::const_iterator m_frequency_list_fcal_iter;
|
|
qint32 m_i3bit;
|
|
|
|
bool m_btxok; // True if OK to transmit
|
|
bool m_decoderBusy;
|
|
QString m_decoderBusyBand;
|
|
QMap<qint32, qint32>
|
|
m_lastDecodeStartMap; // submode, decode k start position
|
|
QMap<qint32, qint32>
|
|
m_lastDecodeCycleMap; // JS8 40 & JS8 60 submodes, last enqueued cycle start
|
|
Radio::Frequency m_decoderBusyFreq;
|
|
QDateTime m_decoderBusyStartTime;
|
|
bool m_auto;
|
|
bool m_restart;
|
|
bool m_bDecoded;
|
|
int m_currentMessageType;
|
|
QString m_currentMessage;
|
|
int m_currentMessageBits;
|
|
int m_lastMessageType;
|
|
QString m_lastMessageSent;
|
|
bool m_tuneup;
|
|
bool m_isTimeToSend;
|
|
|
|
int m_ihsym;
|
|
float m_px;
|
|
float m_pxmax;
|
|
float m_df3;
|
|
quint32 m_iptt = 0;
|
|
quint32 m_iptt0;
|
|
bool m_btxok0;
|
|
double m_onAirFreq0;
|
|
bool m_first_error;
|
|
|
|
char m_msg[100][80];
|
|
|
|
// labels and widgets in status and header bar
|
|
QLabel tx_status_label;
|
|
QLabel config_label;
|
|
QLabel mode_label;
|
|
QLabel frequency_label;
|
|
QLabel auto_reply_label;
|
|
QLabel last_tx_label;
|
|
QLabel auto_tx_label;
|
|
QProgressBar progressBar;
|
|
QLabel wpm_label;
|
|
Styles::OffsetSliderWidget *freqOffsetWidget = nullptr;
|
|
int m_sliderFreqBeforeHB = 0;
|
|
|
|
// QPointer<QProcess> proc_js8;
|
|
|
|
QTimer m_guiTimer;
|
|
// Timer to switch off PTT after end of transmission.
|
|
QTimer pttReleaseTimer;
|
|
QTimer logQSOTimer;
|
|
QTimer tuneButtonTimer;
|
|
QTimer tuneATU_Timer;
|
|
QTimer TxAgainTimer;
|
|
QTimer minuteTimer;
|
|
QString m_baseCall;
|
|
QString m_hisCall;
|
|
QString m_hisGrid;
|
|
QString m_appDir;
|
|
QString m_palette;
|
|
QString m_rptSent;
|
|
QString m_rptRcvd;
|
|
QString m_msgSent0;
|
|
QString m_opCall;
|
|
|
|
struct CallDetail {
|
|
QString call;
|
|
QString through;
|
|
QString grid;
|
|
int dial;
|
|
int offset;
|
|
QDateTime cqTimestamp;
|
|
QDateTime ackTimestamp;
|
|
QDateTime utcTimestamp;
|
|
int snr;
|
|
int bits;
|
|
float tdrift;
|
|
int submode;
|
|
};
|
|
|
|
struct CommandDetail {
|
|
bool isCompound;
|
|
bool isBuffered;
|
|
QString from;
|
|
QString to;
|
|
QString cmd;
|
|
int dial;
|
|
int offset;
|
|
QDateTime utcTimestamp;
|
|
int snr;
|
|
int bits;
|
|
QString grid;
|
|
QString text;
|
|
QString extra;
|
|
float tdrift;
|
|
int submode;
|
|
QString relayPath;
|
|
};
|
|
|
|
struct ActivityDetail {
|
|
bool isLowConfidence;
|
|
bool isCompound;
|
|
bool isDirected;
|
|
bool isBuffered;
|
|
int bits;
|
|
int dial;
|
|
int offset;
|
|
QString text;
|
|
QDateTime utcTimestamp;
|
|
int snr;
|
|
bool shouldDisplay;
|
|
float tdrift;
|
|
int submode;
|
|
};
|
|
|
|
struct MessageBuffer {
|
|
CommandDetail cmd;
|
|
QQueue<CallDetail> compound;
|
|
QList<ActivityDetail> msgs;
|
|
};
|
|
|
|
QString m_prevSelectedCallsign;
|
|
int m_bandActivityWidth;
|
|
int m_callActivityWidth;
|
|
int m_textActivityWidth;
|
|
int m_waterfallHeight;
|
|
bool m_bandActivityWasVisible;
|
|
bool m_rxDirty;
|
|
bool m_rxDisplayDirty;
|
|
int m_txFrameCountEstimate;
|
|
int m_txFrameCount;
|
|
int m_txFrameCountSent;
|
|
QTimer m_txTextDirtyDebounce;
|
|
bool m_txTextDirty;
|
|
QString m_txTextDirtyLastText;
|
|
QString m_txTextDirtyLastSelectedCall;
|
|
QString m_lastTxMessage;
|
|
QString m_totalTxMessage;
|
|
|
|
// moved from mainwindow.cpp, is used in multiple functions
|
|
QString since(QDateTime const &time) {
|
|
auto const delta = time.secsTo(DriftingDateTime::currentDateTimeUtc());
|
|
|
|
if (delta >= 60 * 60 * 24)
|
|
return QString("%1d").arg(delta / (60 * 60 * 24));
|
|
else if (delta >= 60 * 60)
|
|
return QString("%1h").arg(delta / (60 * 60));
|
|
else if (delta >= 60)
|
|
return QString("%1m").arg(delta / (60));
|
|
else if (delta >= 15)
|
|
return QString("%1s").arg(delta - (delta % 15));
|
|
else
|
|
return QString("now");
|
|
}
|
|
|
|
QDateTime m_lastTxStartTime;
|
|
QDateTime m_lastTxStopTime;
|
|
qint32 m_driftMsMMA;
|
|
qint32 m_driftMsMMA_N;
|
|
|
|
// moved from mainwindow.cpp, is used in multiple functions
|
|
auto replaceMacros(QString const &text,
|
|
QMap<QString, QString> const &values, bool const prune) {
|
|
QString output = text;
|
|
|
|
for (auto const [key, value] : values.asKeyValueRange()) {
|
|
output = output.replace(key, value.toUpper());
|
|
}
|
|
|
|
return prune ? output.replace(QRegularExpression("[<](?:[^>]+)[>]"), "")
|
|
: output;
|
|
}
|
|
|
|
QList<int> generateOffsets(int minOffset, int maxOffset) {
|
|
QList<int> offsets;
|
|
|
|
// TODO: these offsets aren't ordered correctly...
|
|
|
|
for (int i = minOffset; i <= maxOffset; i++) {
|
|
offsets.append(i);
|
|
}
|
|
return offsets;
|
|
}
|
|
|
|
enum Priority {
|
|
PriorityLow = 10,
|
|
PriorityNormal = 100,
|
|
PriorityHigh = 1000
|
|
};
|
|
|
|
struct PrioritizedMessage {
|
|
QDateTime date;
|
|
int priority;
|
|
QString message;
|
|
int offset;
|
|
Callback callback;
|
|
|
|
friend bool operator<(PrioritizedMessage const &a,
|
|
PrioritizedMessage const &b) {
|
|
if (a.priority < b.priority) {
|
|
return true;
|
|
}
|
|
return a.date < b.date;
|
|
}
|
|
};
|
|
|
|
struct CachedDirectedType {
|
|
bool isAllcall;
|
|
QDateTime date;
|
|
};
|
|
|
|
struct DecodeParams {
|
|
int submode;
|
|
int start;
|
|
int sz;
|
|
};
|
|
|
|
struct FrameCacheKey {
|
|
int submode;
|
|
QString frame;
|
|
|
|
FrameCacheKey(int submode, QString frame)
|
|
: submode(submode), frame(std::move(frame)) {}
|
|
|
|
bool operator==(FrameCacheKey const &) const noexcept = default;
|
|
|
|
struct Hash {
|
|
std::size_t operator()(FrameCacheKey const &key) const noexcept {
|
|
std::size_t const h1 = std::hash<int>{}(key.submode);
|
|
std::size_t const h2 = qHash(key.frame);
|
|
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
|
}
|
|
};
|
|
};
|
|
|
|
using FrameCache =
|
|
std::unordered_map<FrameCacheKey, QDateTime, FrameCacheKey::Hash>;
|
|
using BandActivity = QMap<int, QList<ActivityDetail>>;
|
|
|
|
QQueue<DecodeParams> m_decoderQueue;
|
|
FrameCache m_messageDupeCache; // submode, frame -> date seen
|
|
QVariantMap m_showColumnsCache; // table column:key -> show boolean
|
|
QVariantMap m_sortCache; // table key -> sort by
|
|
QPriorityQueue<PrioritizedMessage> m_txMessageQueue; // messages to be sent
|
|
QQueue<QPair<QString, int>> m_txFrameQueue; // frames to be sent
|
|
QQueue<ActivityDetail> m_rxActivityQueue; // all rx activity queue
|
|
QQueue<CommandDetail>
|
|
m_rxCommandQueue; // command queue for processing commands
|
|
QQueue<CallDetail>
|
|
m_rxCallQueue; // call detail queue for spots to pskreporter
|
|
QMap<QString, QString>
|
|
m_compoundCallCache; // base callsign -> compound callsign
|
|
QCache<QString, QDateTime> m_txAllcallCommandCache; // callsign -> last tx
|
|
QCache<int, QDateTime> m_rxRecentCache; // freq -> last rx
|
|
QCache<int, CachedDirectedType>
|
|
m_rxDirectedCache; // freq -> last directed rx
|
|
QCache<QString, int> m_rxCallCache; // call -> last freq seen
|
|
QMap<int, int> m_rxFrameBlockNumbers; // freq -> block
|
|
BandActivity m_bandActivity; // freq -> [(text, last timestamp), ...]
|
|
QMap<int, MessageBuffer> m_messageBuffer; // freq -> (cmd, [frames, ...])
|
|
int m_lastClosedMessageBufferOffset;
|
|
QMap<QString, CallDetail>
|
|
m_callActivity; // call -> (last freq, last timestamp)
|
|
|
|
QMap<int, QString> m_origRxHeaderLabelMap; // colIndex, label
|
|
QMap<int, QString> m_origCallActivityHeaderLabelMap; // colIndex, label
|
|
QMap<QString, QString> m_columnLabelMap; // full, minimal
|
|
|
|
QMap<QString, QSet<QString>>
|
|
m_heardGraphOutgoing; // callsign -> [stations who've this callsign has
|
|
// heard]
|
|
QMap<QString, QSet<QString>>
|
|
m_heardGraphIncoming; // callsign -> [stations who've heard this
|
|
// callsign]
|
|
|
|
QMap<QString, int> m_rxInboxCountCache; // call -> count
|
|
|
|
QMap<QString, QMap<QString, CallDetail>>
|
|
m_callActivityBandCache; // band -> call activity
|
|
QMap<QString, QMap<int, QList<ActivityDetail>>>
|
|
m_bandActivityBandCache; // band -> band activity
|
|
QMap<QString, QString> m_rxTextBandCache; // band -> rx text
|
|
QMap<QString, QMap<QString, QSet<QString>>>
|
|
m_heardGraphOutgoingBandCache; // band -> heard in
|
|
QMap<QString, QMap<QString, QSet<QString>>>
|
|
m_heardGraphIncomingBandCache; // band -> heard out
|
|
|
|
// Pending autoreply confirmations (network API and local UI notification)
|
|
struct PendingConfirmation {
|
|
int id;
|
|
int priority;
|
|
QString message;
|
|
int offset;
|
|
Callback callback;
|
|
QTimer *timer;
|
|
SelfDestructMessageBox *box;
|
|
};
|
|
int m_nextConfirmId = 0;
|
|
QMap<int, PendingConfirmation> m_pendingConfirmations;
|
|
|
|
QMap<QString, QDateTime>
|
|
m_callSelectedTime; // call -> timestamp when callsign was last selected
|
|
/**
|
|
* Cache of recently seen APRS relay keys used to suppress duplicates.
|
|
* Key format: "TO|TEXT|SENDER" (uppercased), value is last seen UTC.
|
|
*/
|
|
QHash<QString, QDateTime> m_aprsRelayDedupCache;
|
|
QSet<QString> m_callSeenHeartbeat; // call
|
|
bool m_bandHopped;
|
|
Frequency m_bandHoppedFreq;
|
|
|
|
/** Repeat period of HBs, in seconds. */
|
|
int m_hbInterval;
|
|
/** Repeat period of CQ calls, in seconds. */
|
|
int m_cqInterval;
|
|
|
|
/** Whether to resume HBs at the next opportunity. */
|
|
bool m_hbPaused;
|
|
|
|
QDateTime m_dateTimeQSOOn;
|
|
QDateTime m_dateTimeLastTX;
|
|
|
|
QSet<int> m_rxBlockedOffsets; //callsign blocking
|
|
|
|
LogBook m_logBook;
|
|
unsigned m_msAudioOutputBuffered;
|
|
unsigned m_framesAudioInputBuffered;
|
|
QThread::Priority m_audioThreadPriority;
|
|
QThread::Priority m_notificationAudioThreadPriority;
|
|
QThread::Priority m_decoderThreadPriority;
|
|
QThread::Priority m_networkThreadPriority;
|
|
bool m_splitMode;
|
|
bool m_monitoring;
|
|
bool m_generateAudioWhenPttConfirmedByTX;
|
|
bool m_transmitting;
|
|
bool m_tune;
|
|
bool m_tx_watchdog; // true when watchdog triggered
|
|
bool m_block_pwr_tooltip;
|
|
bool m_PwrBandSetOK;
|
|
Frequency m_lastMonitoredFrequency;
|
|
MessageClient *m_messageClient;
|
|
MessageServer *m_messageServer;
|
|
WSJTXMessageClient *m_wsjtxMessageClient;
|
|
WSJTXMessageMapper *m_wsjtxMessageMapper;
|
|
TCPClient *m_n3fjpClient;
|
|
PSKReporter *m_pskReporter;
|
|
SpotClient *m_spotClient;
|
|
APRSISClient *m_aprsClient;
|
|
AprsInboundRelay *m_aprsInboundRelay;
|
|
QVariantHash m_pwrBandTxMemory; // Remembers power level by band
|
|
QVariantHash
|
|
m_pwrBandTuneMemory; // Remembers power level by band for tuning
|
|
QByteArray m_geometryNoControls;
|
|
|
|
//---------------------------------------------------- private functions
|
|
void readSettings();
|
|
void set_application_font(QFont const &);
|
|
void writeSettings();
|
|
void createStatusBar();
|
|
void statusChanged();
|
|
void rigFailure(QString const &reason);
|
|
void spotSetLocal();
|
|
void pskSetLocal();
|
|
void aprsSetLocal();
|
|
void spotReport(int submode, int dial, int offset, int snr,
|
|
QString const &callsign, QString const &grid);
|
|
void spotCmd(CommandDetail const &cmd);
|
|
void spotAprsCmd(CommandDetail const &cmd);
|
|
void pskLogReport(QString const &mode, int dial, int offset, int snr,
|
|
QString const &callsign, QString const &grid,
|
|
QDateTime const &utcTimestamp);
|
|
void spotAprsGrid(int dial, int offset, int snr, QString callsign,
|
|
QString grid);
|
|
Radio::Frequency dialFrequency();
|
|
void setSubmode(int submode);
|
|
void updateCurrentBand();
|
|
void displayDialFrequency();
|
|
void transmitDisplay(bool);
|
|
void postDecode(bool is_new, QString const &message);
|
|
void displayTransmit();
|
|
void updateModeButtonText();
|
|
void updateButtonDisplay();
|
|
void updateTextDisplay();
|
|
void updateTextWordCheckerDisplay();
|
|
void updateTextStatsDisplay(QString text, int count);
|
|
void updateTxButtonDisplay();
|
|
bool isMyCallIncluded(QString const &text);
|
|
bool isAllCallIncluded(QString const &text);
|
|
bool isGroupCallIncluded(const QString &text);
|
|
QString callsignSelected(bool useInputText = false);
|
|
void callsignSelectedChanged(QString old, QString current);
|
|
bool isRecentOffset(int submode, int offset);
|
|
void markOffsetRecent(int offset);
|
|
bool isDirectedOffset(int offset, bool *pIsAllCall);
|
|
void markOffsetDirected(int offset, bool isAllCall);
|
|
void clearOffsetDirected(int offset);
|
|
void processActivity(bool force = false);
|
|
void resetTimeDeltaAverage();
|
|
void processRxActivity(); // JS8_Mainwindow/processRxActivity.cpp
|
|
void processIdleActivity();
|
|
void processCompoundActivity();
|
|
void processBufferedActivity(); // JS8_Mainwindow/processBufferedActivity.cpp
|
|
void processCommandActivity(); // JS8_Mainwindow/processCommandActivity.cpp
|
|
void processHeartbeatRateLimit(const QString &callsign); // JS8_Mainwindow/processHeartbeatRateLimit.cpp
|
|
QString inboxPath();
|
|
QString hbBlockingPath() const;
|
|
void refreshInboxCounts();
|
|
bool hasMessageHistory(QString call);
|
|
int addCommandToMyInbox(CommandDetail d);
|
|
int addCommandToStorage(QString type, CommandDetail d);
|
|
int getNextMessageIdForCallsign(QString callsign);
|
|
int getLookaheadMessageIdForCallsign(QString callsign, int afterMsgId);
|
|
int getNextGroupMessageIdForCallsign(QString group_name, QString callsign);
|
|
int getLookaheadGroupMessageIdForCallsign(QString group_name,
|
|
QString callsign, int afterMsgId);
|
|
int countUnreadForCallsign(const QString &callsign);
|
|
int countGroupUnreadForCallsign(const QString &group_name,
|
|
const QString &callsign);
|
|
bool markGroupMsgDeliveredForCallsign(int msgId, QString callsign);
|
|
bool markMsgDelivered(int mid, Message msg);
|
|
QStringList parseRelayPathCallsigns(QString from, QString text);
|
|
void processSpots();
|
|
void processTxQueue();
|
|
void displayActivity(bool force = false);
|
|
void displayBandActivity(); // JS8_Mainwindow/displayBandActivity.cpp
|
|
void displayCallActivity(); // JS8_Mainwindow/displayCallActivity.cpp
|
|
void enable_DXCC_entity(bool on);
|
|
void setRig(Frequency = 0); // zero frequency means no change
|
|
QDateTime nextTransmitCycle();
|
|
void statusUpdate();
|
|
void on_the_minute();
|
|
void tryBandHop();
|
|
void add_child_to_event_filter(QObject *);
|
|
void remove_child_from_event_filter(QObject *);
|
|
void setup_status_bar();
|
|
QString columnLabel(QString defaultLabel);
|
|
void ensureMessageDock();
|
|
|
|
void resetIdleTimer();
|
|
void incrementIdleTimer();
|
|
void tx_watchdog(bool triggered);
|
|
void write_frequency_entry(QString const &file_name);
|
|
void write_transmit_entry(QString const &file_name);
|
|
};
|
|
|
|
#endif // MAINWINDOW_H
|
|
|