2026-01-01 15:12:24 -06:00
|
|
|
/**
|
|
|
|
|
* @brief Construct a WSJT-X message mapper
|
|
|
|
|
*
|
|
|
|
|
* Sets up signal connections to handle incoming WSJT-X protocol messages
|
|
|
|
|
* and map them to JS8Call actions.
|
|
|
|
|
*
|
|
|
|
|
* @param client WSJT-X message client to send messages through
|
|
|
|
|
* @param main_window Main window instance for accessing JS8Call state
|
|
|
|
|
* @param parent Parent QObject
|
|
|
|
|
*/
|
2026-01-18 12:53:55 -06:00
|
|
|
|
|
|
|
|
#include "WSJTXMessageMapper.h"
|
|
|
|
|
#include "JS8_UI/mainwindow.h"
|
|
|
|
|
|
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
|
|
Q_LOGGING_CATEGORY(wsjtx_mapper_js8, "wsjtx.mapper.js8", QtWarningMsg)
|
|
|
|
|
|
2026-01-01 15:12:24 -06:00
|
|
|
WSJTXMessageMapper::WSJTXMessageMapper(WSJTXMessageClient *client,
|
2026-01-18 12:53:55 -06:00
|
|
|
UI_Constructor *main_window,
|
|
|
|
|
QObject *parent)
|
2026-01-01 15:12:24 -06:00
|
|
|
: QObject{parent}, client_{client}, main_window_{main_window} {
|
|
|
|
|
connect(client_, &WSJTXMessageClient::reply, this,
|
|
|
|
|
&WSJTXMessageMapper::handleReply);
|
|
|
|
|
connect(client_, &WSJTXMessageClient::free_text, this,
|
|
|
|
|
&WSJTXMessageMapper::handleFreeText);
|
|
|
|
|
connect(client_, &WSJTXMessageClient::halt_tx, this,
|
|
|
|
|
&WSJTXMessageMapper::handleHaltTx);
|
|
|
|
|
connect(client_, &WSJTXMessageClient::location, this,
|
|
|
|
|
&WSJTXMessageMapper::handleLocation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Send a Status update message
|
|
|
|
|
*
|
|
|
|
|
* Maps JS8Call's status information to WSJT-X Status message format and
|
|
|
|
|
* sends it through the WSJT-X message client.
|
|
|
|
|
*
|
|
|
|
|
* @param dial_freq Dial frequency (Hz)
|
|
|
|
|
* @param offset Frequency offset (Hz)
|
|
|
|
|
* @param mode Operating mode string
|
|
|
|
|
* @param dx_call DX station callsign (selected station)
|
|
|
|
|
* @param de_call My callsign
|
|
|
|
|
* @param de_grid My grid square
|
|
|
|
|
* @param dx_grid DX station grid square
|
|
|
|
|
* @param tx_enabled Whether TX is enabled
|
|
|
|
|
* @param transmitting Whether currently transmitting
|
|
|
|
|
* @param decoding Whether currently decoding
|
|
|
|
|
* @param tx_message Current TX message text
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::sendStatusUpdate(
|
|
|
|
|
Radio::Frequency dial_freq, Radio::Frequency offset, QString const &mode,
|
|
|
|
|
QString const &dx_call, QString const &de_call, QString const &de_grid,
|
|
|
|
|
QString const &dx_grid, bool tx_enabled, bool transmitting, bool decoding,
|
|
|
|
|
QString const &tx_message) {
|
|
|
|
|
qCDebug(wsjtx_mapper_js8)
|
|
|
|
|
<< "WSJTXMessageMapper: sendStatusUpdate called"
|
|
|
|
|
<< "dial_freq:" << dial_freq << "offset:" << offset << "mode:" << mode
|
|
|
|
|
<< "dx_call:" << dx_call << "de_call:" << de_call
|
|
|
|
|
<< "de_grid:" << de_grid << "dx_grid:" << dx_grid
|
|
|
|
|
<< "tx_enabled:" << tx_enabled << "transmitting:" << transmitting
|
|
|
|
|
<< "decoding:" << decoding;
|
|
|
|
|
Radio::Frequency freq = dial_freq + offset;
|
|
|
|
|
QString submode = mode; // JS8Call submode
|
|
|
|
|
bool fast_mode = false; // Map from JS8Call speed
|
|
|
|
|
quint8 special_op_mode = 0; // NONE
|
|
|
|
|
quint32 frequency_tolerance = std::numeric_limits<quint32>::max();
|
|
|
|
|
quint32 tr_period = std::numeric_limits<quint32>::max();
|
|
|
|
|
QString configuration_name = "";
|
|
|
|
|
|
|
|
|
|
client_->status_update(
|
|
|
|
|
freq, mode, dx_call, "", mode, tx_enabled, transmitting, decoding,
|
|
|
|
|
static_cast<quint32>(offset), static_cast<quint32>(offset), de_call,
|
|
|
|
|
de_grid, dx_grid, false, submode, fast_mode, special_op_mode,
|
|
|
|
|
frequency_tolerance, tr_period, configuration_name, tx_message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Send a Decode message
|
|
|
|
|
*
|
|
|
|
|
* Maps JS8Call's decode information to WSJT-X Decode message format and
|
|
|
|
|
* sends it through the WSJT-X message client.
|
|
|
|
|
*
|
|
|
|
|
* @param is_new Whether this is a new decode
|
|
|
|
|
* @param time Decode time
|
|
|
|
|
* @param snr Signal-to-noise ratio (dB)
|
|
|
|
|
* @param delta_time Time offset from expected time (seconds)
|
|
|
|
|
* @param delta_frequency Frequency offset from nominal (Hz)
|
|
|
|
|
* @param mode Operating mode string
|
|
|
|
|
* @param message Decoded message text
|
|
|
|
|
* @param low_confidence Whether decode confidence is low
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::sendDecode(bool is_new, QTime time, qint32 snr,
|
|
|
|
|
float delta_time, quint32 delta_frequency,
|
|
|
|
|
QString const &mode, QString const &message,
|
|
|
|
|
bool low_confidence) {
|
|
|
|
|
qCDebug(wsjtx_mapper_js8)
|
|
|
|
|
<< "WSJTXMessageMapper: sendDecode called"
|
|
|
|
|
<< "is_new:" << is_new << "time:" << time.toString("hh:mm:ss")
|
|
|
|
|
<< "snr:" << snr << "delta_time:" << delta_time
|
|
|
|
|
<< "delta_frequency:" << delta_frequency << "mode:" << mode
|
|
|
|
|
<< "message:" << message << "low_confidence:" << low_confidence;
|
|
|
|
|
client_->decode(is_new, time, snr, delta_time, delta_frequency, mode,
|
|
|
|
|
message, low_confidence, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Send a QSO Logged message
|
|
|
|
|
*
|
|
|
|
|
* Maps JS8Call's QSO log information to WSJT-X QSOLogged message format and
|
|
|
|
|
* sends it through the WSJT-X message client.
|
|
|
|
|
*
|
|
|
|
|
* @param time_off QSO end time
|
|
|
|
|
* @param dx_call DX station callsign
|
|
|
|
|
* @param dx_grid DX station grid square
|
|
|
|
|
* @param dial_frequency Dial frequency (Hz)
|
|
|
|
|
* @param mode Operating mode
|
|
|
|
|
* @param report_sent Report sent to DX station
|
|
|
|
|
* @param report_received Report received from DX station
|
|
|
|
|
* @param my_call My callsign
|
|
|
|
|
* @param my_grid My grid square
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::sendQSOLogged(
|
|
|
|
|
QDateTime time_off, QString const &dx_call, QString const &dx_grid,
|
|
|
|
|
Radio::Frequency dial_frequency, QString const &mode,
|
|
|
|
|
QString const &report_sent, QString const &report_received,
|
|
|
|
|
QString const &my_call, QString const &my_grid) {
|
|
|
|
|
qCDebug(wsjtx_mapper_js8)
|
|
|
|
|
<< "WSJTXMessageMapper: sendQSOLogged called"
|
|
|
|
|
<< "time_off:" << time_off.toString(Qt::ISODate)
|
|
|
|
|
<< "dx_call:" << dx_call << "dx_grid:" << dx_grid
|
|
|
|
|
<< "dial_frequency:" << dial_frequency << "mode:" << mode
|
|
|
|
|
<< "report_sent:" << report_sent
|
|
|
|
|
<< "report_received:" << report_received << "my_call:" << my_call
|
|
|
|
|
<< "my_grid:" << my_grid;
|
|
|
|
|
QDateTime time_on = time_off; // JS8Call doesn't track time_on separately
|
|
|
|
|
QString operator_call = "";
|
|
|
|
|
QString exchange_sent = "";
|
|
|
|
|
QString exchange_rcvd = "";
|
|
|
|
|
QString propmode = "";
|
|
|
|
|
|
|
|
|
|
client_->qso_logged(time_off, dx_call, dx_grid, dial_frequency, mode,
|
|
|
|
|
report_sent, report_received, "", "", "", time_on,
|
|
|
|
|
operator_call, my_call, my_grid, exchange_sent,
|
|
|
|
|
exchange_rcvd, propmode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Handle incoming Reply message from WSJT-X protocol
|
|
|
|
|
*
|
|
|
|
|
* Maps WSJT-X Reply message to JS8Call action. This would trigger a reply
|
|
|
|
|
* in JS8Call similar to double-clicking a decode.
|
|
|
|
|
*
|
|
|
|
|
* @param time Reply time (unused)
|
|
|
|
|
* @param snr Signal-to-noise ratio (unused)
|
|
|
|
|
* @param delta_time Time offset (unused)
|
|
|
|
|
* @param delta_frequency Frequency offset (unused)
|
|
|
|
|
* @param mode Operating mode (unused)
|
|
|
|
|
* @param message_text Reply message text (unused)
|
|
|
|
|
* @param low_confidence Whether decode confidence is low (unused)
|
|
|
|
|
* @param modifiers Message modifiers (unused)
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::handleReply(QTime /*time*/, qint32 /*snr*/,
|
|
|
|
|
float /*delta_time*/,
|
|
|
|
|
quint32 /*delta_frequency*/,
|
|
|
|
|
QString const & /*mode*/,
|
|
|
|
|
QString const & /*message_text*/,
|
|
|
|
|
bool /*low_confidence*/,
|
|
|
|
|
quint8 /*modifiers*/) {
|
|
|
|
|
// Map WSJT-X Reply to JS8Call action
|
|
|
|
|
// This would trigger a reply in JS8Call similar to double-clicking a decode
|
2026-01-18 12:53:55 -06:00
|
|
|
// Implementation depends on UI_Constructor API - for now, just send as
|
|
|
|
|
// network message
|
2026-01-01 15:12:24 -06:00
|
|
|
if (main_window_) {
|
|
|
|
|
// TODO: Map to appropriate JS8Call action
|
|
|
|
|
// main_window_->sendNetworkMessage("REPLY", message_text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Handle incoming Free Text message from WSJT-X protocol
|
|
|
|
|
*
|
|
|
|
|
* Maps WSJT-X Free Text message to JS8Call TX.SET_TEXT and optionally
|
|
|
|
|
* TX.SEND_MESSAGE network messages.
|
|
|
|
|
*
|
|
|
|
|
* @param text Free text to send
|
|
|
|
|
* @param send Whether to send immediately
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::handleFreeText(QString const &text, bool send) {
|
|
|
|
|
// Map to JS8Call TX.SET_TEXT message
|
|
|
|
|
if (main_window_) {
|
|
|
|
|
main_window_->sendNetworkMessage("TX.SET_TEXT", text);
|
|
|
|
|
if (send) {
|
|
|
|
|
main_window_->sendNetworkMessage("TX.SEND_MESSAGE", text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Handle incoming Halt TX message from WSJT-X protocol
|
|
|
|
|
*
|
|
|
|
|
* Maps WSJT-X Halt TX message to JS8Call TX halt action.
|
|
|
|
|
*
|
|
|
|
|
* @param auto_only If true, only halt auto sequences
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::handleHaltTx(bool auto_only) {
|
|
|
|
|
// Stop transmission
|
|
|
|
|
if (main_window_ && !auto_only) {
|
|
|
|
|
// TODO: Stop TX immediately
|
|
|
|
|
// main_window_->sendNetworkMessage("TX.HALT", "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Handle incoming Location message from WSJT-X protocol
|
|
|
|
|
*
|
|
|
|
|
* Maps WSJT-X Location message to JS8Call STATION.SET_GRID network message.
|
|
|
|
|
*
|
|
|
|
|
* @param location Grid square or location string
|
|
|
|
|
*/
|
|
|
|
|
void WSJTXMessageMapper::handleLocation(QString const &location) {
|
|
|
|
|
// Map to JS8Call STATION.SET_GRID
|
|
|
|
|
if (main_window_) {
|
|
|
|
|
main_window_->sendNetworkMessage("STATION.SET_GRID", location);
|
|
|
|
|
}
|
|
|
|
|
}
|