Compare commits

...

14 Commits

Author SHA1 Message Date
Jordan Sherer 536f017fd4 Bump to v0.5.1 2018-09-01 13:43:16 -04:00
Jordan Sherer 5c313af3ab Removed PWR. Added APRS SSID 2018-09-01 10:34:12 -04:00
Jordan Sherer 046fe83193 Fixed compound buffered commands double printing in the rx window 2018-08-31 22:29:13 -04:00
Jordan Sherer e861308d70 Added auto-prefix messages with directed callsigns 2018-08-31 21:30:34 -04:00
Jordan Sherer 6dcf4b96d2 Fixed bug in waterfall displaying mouse hover without mouse over 2018-08-31 19:04:57 -04:00
Jordan Sherer f00f5e23c1 Updated message textbox placeholder default 2018-08-31 19:03:31 -04:00
Jordan Sherer 2e8cde4193 Added deselect to the activity menus 2018-08-31 09:59:43 -04:00
Jordan Sherer 7caa7c83b8 Updated GRID command to no longer ACK a reply. It was giving off the wrong impression 2018-08-30 20:45:51 -04:00
Jordan Sherer 08d8beed64 Fixed issue with double printing of directed and undirected activity. Fixed enter key press with no message 2018-08-30 17:19:58 -04:00
Jordan Sherer e5347eb4b6 Fixed scrolling bug for band activity and call activity lists 2018-08-30 16:13:43 -04:00
Jordan Sherer 64022c18b6 Added compound callsigns to APRS position comment 2018-08-30 15:01:43 -04:00
Jordan Sherer 42c8d1c0bb Updated APRS client to be more resilient to server failure 2018-08-30 14:31:31 -04:00
Jordan Sherer 6ab3d32e3b Updated APRS spotting connections for better reliability 2018-08-30 12:18:20 -04:00
Jordan Sherer 2b62734437 Fixed bug with SNR report menu item 2018-08-29 22:17:14 -04:00
10 changed files with 301 additions and 226 deletions
+48 -9
View File
@@ -13,7 +13,7 @@ APRSISClient::APRSISClient(QString host, quint16 port, QObject *parent):
m_port(port) m_port(port)
{ {
connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports); connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports);
m_timer.setInterval(60*1000); // every minute m_timer.setInterval(30*1000); // every 30 seconds
m_timer.start(); m_timer.start();
} }
@@ -184,18 +184,16 @@ QPair<QString, QString> APRSISClient::grid2aprs(QString grid){
}; };
} }
void APRSISClient::enqueueSpot(QString theircall, QString grid, quint64 frequency, int snr){ void APRSISClient::enqueueSpot(QString theircall, QString grid, QString comment){
if(m_localCall.isEmpty()) return; if(m_localCall.isEmpty()) return;
auto geo = APRSISClient::grid2aprs(grid); auto geo = APRSISClient::grid2aprs(grid);
auto spotFrame = QString("%1>%2,APRS,TCPIP*:=%3/%4nFT8CALL %5 %6MHz %7dB\n"); auto spotFrame = QString("%1>%2,APRS,TCPIP*:=%3/%4nFT8CALL %5\n");
spotFrame = spotFrame.arg(theircall); spotFrame = spotFrame.arg(theircall);
spotFrame = spotFrame.arg(m_localCall); spotFrame = spotFrame.arg(m_localCall);
spotFrame = spotFrame.arg(geo.first); spotFrame = spotFrame.arg(geo.first);
spotFrame = spotFrame.arg(geo.second); spotFrame = spotFrame.arg(geo.second);
spotFrame = spotFrame.arg(m_localGrid.left(4)); spotFrame = spotFrame.arg(comment.left(43));
spotFrame = spotFrame.arg(Radio::frequency_MHz_string(frequency));
spotFrame = spotFrame.arg(Varicode::formatSNR(snr));
enqueueRaw(spotFrame); enqueueRaw(spotFrame);
} }
@@ -222,8 +220,12 @@ void APRSISClient::enqueueRaw(QString aprsFrame){
} }
void APRSISClient::processQueue(bool disconnect){ void APRSISClient::processQueue(bool disconnect){
// don't process queue if we haven't set our local callsign
if(m_localCall.isEmpty()) return; if(m_localCall.isEmpty()) return;
// don't process queue if there's nothing to process
if(m_frameQueue.isEmpty()) return;
// 1. connect (and read) // 1. connect (and read)
// 2. login (and read) // 2. login (and read)
// 3. for each raw frame in queue, send // 3. for each raw frame in queue, send
@@ -237,19 +239,56 @@ void APRSISClient::processQueue(bool disconnect){
} }
} }
auto re = QRegExp("(full|unavailable|busy)");
auto line = QString(readLine());
if(line.toLower().indexOf(re) >= 0){
qDebug() << "APRSISClient Connection Busy:" << line;
return;
}
if(write(loginFrame(m_localCall).toLocal8Bit()) == -1){ if(write(loginFrame(m_localCall).toLocal8Bit()) == -1){
qDebug() << "APRSISClient Write Login Error:" << errorString(); qDebug() << "APRSISClient Write Login Error:" << errorString();
return; return;
} }
if(!waitForReadyRead(5000)){
qDebug() << "APRSISClient Login Error: Server Not Responding";
return;
}
line = QString(readAll());
if(line.toLower().indexOf(re) >= 0){
qDebug() << "APRSISClient Server Busy:" << line;
return;
}
while(!m_frameQueue.isEmpty()){ while(!m_frameQueue.isEmpty()){
if(write(m_frameQueue.head().toLocal8Bit()) == -1){ QByteArray data = m_frameQueue.head().toLocal8Bit();
if(write(data) == -1){
qDebug() << "APRSISClient Write Error:" << errorString(); qDebug() << "APRSISClient Write Error:" << errorString();
return; return;
} }
auto frame = m_frameQueue.dequeue(); if(!waitForBytesWritten(5000)){
qDebug() << "APRISISClient Write:" << frame; qDebug() << "APRSISClient Cannot Write Error: Write Timeout";
return;
}
qDebug() << "APRSISClient Write:" << data;
if(waitForReadyRead(5000)){
line = QString(readLine());
qDebug() << "APRSISClient Read:" << line;
if(line.toLower().indexOf(re) >= 0){
qDebug() << "APRSISClient Cannot Write Error:" << line;
return;
}
}
m_frameQueue.dequeue();
} }
if(disconnect){ if(disconnect){
+2 -2
View File
@@ -21,12 +21,12 @@ public:
m_localGrid = mygrid; m_localGrid = mygrid;
} }
void enqueueSpot(QString theircall, QString grid, quint64 frequency, int snr); void enqueueSpot(QString theircall, QString grid, QString comment);
void enqueueMessage(QString tocall, QString message); void enqueueMessage(QString tocall, QString message);
void enqueueThirdParty(QString theircall, QString payload); void enqueueThirdParty(QString theircall, QString payload);
void enqueueRaw(QString aprsFrame); void enqueueRaw(QString aprsFrame);
void processQueue(bool disconnect=false); void processQueue(bool disconnect=true);
public slots: public slots:
void sendReports(){ processQueue(true); } void sendReports(){ processQueue(true); }
+6 -40
View File
@@ -555,7 +555,7 @@ private:
QString my_callsign_; QString my_callsign_;
QString my_grid_; QString my_grid_;
QString my_station_; QString my_station_;
int my_dBm_; QString aprs_ssid_;
QString my_qth_; QString my_qth_;
QString reply_; QString reply_;
int callsign_aging_; int callsign_aging_;
@@ -865,8 +865,8 @@ QString Configuration::my_station() const
return station; return station;
} }
int Configuration::my_dBm() const { QString Configuration::aprs_ssid() const {
return m_->my_dBm_; return m_->aprs_ssid_;
} }
QString Configuration::my_qth() const QString Configuration::my_qth() const
@@ -1221,40 +1221,6 @@ void Configuration::impl::initialize_models ()
pal.setColor (QPalette::Base, Qt::white); pal.setColor (QPalette::Base, Qt::white);
} }
QMap<int, int> dbm2mw = {
{0 , 1},
{3 , 2},
{7 , 5},
{10 , 10},
{13 , 20},
{17 , 50},
{20 , 100},
{23 , 200},
{27 , 500},
{30 , 1000}, // 1W
{33 , 2000}, // 2W
{37 , 5000}, // 5W
{40 , 10000}, // 10W
{43 , 20000}, // 20W
{47 , 50000}, // 50W
{50 , 100000}, // 100W
{53 , 200000}, // 200W
{57 , 500000}, // 500W
{60 , 1000000}, // 1000W
};
ui_->station_power_combo_box->clear();
ui_->station_power_combo_box->addItem(QString(""), -1);
foreach(auto dbm, dbm2mw.keys()){
ui_->station_power_combo_box->addItem(QString("%1 (%2 dBm)").arg(Varicode::formatPWR(dbm)).arg(dbm), dbm);
if(dbm == my_dBm_){
ui_->station_power_combo_box->setCurrentIndex(ui_->station_power_combo_box->count()-1);
}
}
ui_->callsign_line_edit->setPalette (pal); ui_->callsign_line_edit->setPalette (pal);
ui_->grid_line_edit->setPalette (pal); ui_->grid_line_edit->setPalette (pal);
ui_->auto_switch_bands_check_box->setChecked(auto_switch_bands_); ui_->auto_switch_bands_check_box->setChecked(auto_switch_bands_);
@@ -1390,7 +1356,7 @@ void Configuration::impl::read_settings ()
my_callsign_ = settings_->value ("MyCall", QString {}).toString (); my_callsign_ = settings_->value ("MyCall", QString {}).toString ();
my_grid_ = settings_->value ("MyGrid", QString {}).toString (); my_grid_ = settings_->value ("MyGrid", QString {}).toString ();
my_station_ = settings_->value("MyStation", QString {}).toString(); my_station_ = settings_->value("MyStation", QString {}).toString();
my_dBm_ = settings_->value("MyPower", -1).toInt(); aprs_ssid_ = settings_->value("APRSSSID", "-0").toString();
callsign_aging_ = settings_->value ("CallsignAging", 0).toInt (); callsign_aging_ = settings_->value ("CallsignAging", 0).toInt ();
activity_aging_ = settings_->value ("ActivityAging", 2).toInt (); activity_aging_ = settings_->value ("ActivityAging", 2).toInt ();
my_qth_ = settings_->value("MyQTH", QString {}).toString(); my_qth_ = settings_->value("MyQTH", QString {}).toString();
@@ -1586,7 +1552,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("MyCall", my_callsign_); settings_->setValue ("MyCall", my_callsign_);
settings_->setValue ("MyGrid", my_grid_); settings_->setValue ("MyGrid", my_grid_);
settings_->setValue ("MyStation", my_station_); settings_->setValue ("MyStation", my_station_);
settings_->setValue ("MyPower", my_dBm_); settings_->setValue ("APRSSSID", aprs_ssid_);
settings_->setValue ("MyQTH", my_qth_); settings_->setValue ("MyQTH", my_qth_);
settings_->setValue ("Reply", reply_); settings_->setValue ("Reply", reply_);
settings_->setValue ("CallsignAging", callsign_aging_); settings_->setValue ("CallsignAging", callsign_aging_);
@@ -2050,7 +2016,7 @@ void Configuration::impl::accept ()
my_grid_ = ui_->grid_line_edit->text (); my_grid_ = ui_->grid_line_edit->text ();
my_station_ = ui_->station_message_line_edit->text().toUpper(); my_station_ = ui_->station_message_line_edit->text().toUpper();
reply_ = ui_->reply_message_line_edit->text().toUpper(); reply_ = ui_->reply_message_line_edit->text().toUpper();
my_dBm_ = ui_->station_power_combo_box->currentData().toInt(); aprs_ssid_ = ui_->aprs_ssid_line_edit->text().toUpper();
my_qth_ = ui_->qth_message_line_edit->text().toUpper(); my_qth_ = ui_->qth_message_line_edit->text().toUpper();
callsign_aging_ = ui_->callsign_aging_spin_box->value(); callsign_aging_ = ui_->callsign_aging_spin_box->value();
activity_aging_ = ui_->activity_aging_spin_box->value(); activity_aging_ = ui_->activity_aging_spin_box->value();
+1 -1
View File
@@ -98,7 +98,7 @@ public:
QString my_callsign () const; QString my_callsign () const;
QString my_grid () const; QString my_grid () const;
QString my_station () const; QString my_station () const;
int my_dBm() const; QString aprs_ssid() const;
int activity_aging() const; int activity_aging() const;
int callsign_aging() const; int callsign_aging() const;
QString my_qth () const; QString my_qth () const;
+68 -34
View File
@@ -175,6 +175,20 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Reply Message:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="reply_message_line_edit">
<property name="text">
<string>HW CPY?</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_14"> <widget class="QLabel" name="label_14">
<property name="toolTip"> <property name="toolTip">
@@ -209,34 +223,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Station Power:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="station_power_combo_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Approximate or average station transmit power to be sent in response to &amp;quot;%&amp;quot; directed queries. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Reply Message:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="reply_message_line_edit">
<property name="text">
<string>HW CPY?</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -2042,7 +2028,12 @@ comments field.</string>
<string>Network Services</string> <string>Network Services</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_17"> <layout class="QGridLayout" name="gridLayout_17">
<item row="0" column="0"> <item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="bottomMargin">
<number>20</number>
</property>
<item>
<widget class="QCheckBox" name="psk_reporter_check_box"> <widget class="QCheckBox" name="psk_reporter_check_box">
<property name="toolTip"> <property name="toolTip">
<string>The program can send your station details and all <string>The program can send your station details and all
@@ -2051,13 +2042,57 @@ This is used for reverse beacon analysis which is very useful
for assessing propagation and system performance.</string> for assessing propagation and system performance.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable spotting to reporting networks (PSKReporter, APRS-IS, etc)</string> <string>Enable spotting to reporting networks (PSKReporter &amp;&amp; APRS-IS)</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_16">
<property name="toolTip">
<string>The SSID to be used for local APRS spots</string>
</property>
<property name="text">
<string>APRS SSID:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="aprs_ssid_line_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>-0</string>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@@ -3100,7 +3135,6 @@ soundcard changes</string>
<tabstop>delete_macro_push_button</tabstop> <tabstop>delete_macro_push_button</tabstop>
<tabstop>macros_list_view</tabstop> <tabstop>macros_list_view</tabstop>
<tabstop>prompt_to_log_check_box</tabstop> <tabstop>prompt_to_log_check_box</tabstop>
<tabstop>psk_reporter_check_box</tabstop>
<tabstop>udp_server_line_edit</tabstop> <tabstop>udp_server_line_edit</tabstop>
<tabstop>udp_server_port_spin_box</tabstop> <tabstop>udp_server_port_spin_box</tabstop>
<tabstop>accept_udp_requests_check_box</tabstop> <tabstop>accept_udp_requests_check_box</tabstop>
@@ -3192,12 +3226,12 @@ soundcard changes</string>
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_audio_source_button_group"/> <buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/> <buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="TX_mode_button_group"/> <buttongroup name="PTT_method_button_group"/>
<buttongroup name="CAT_handshake_button_group"/> <buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/> <buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="split_mode_button_group"/>
</buttongroups> </buttongroups>
</ui> </ui>
+1 -1
View File
@@ -1,6 +1,6 @@
# Version number components # Version number components
set (WSJTX_VERSION_MAJOR 0) set (WSJTX_VERSION_MAJOR 0)
set (WSJTX_VERSION_MINOR 5) set (WSJTX_VERSION_MINOR 5)
set (WSJTX_VERSION_PATCH 0) set (WSJTX_VERSION_PATCH 1)
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
+139 -44
View File
@@ -1029,7 +1029,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if(pProcessed) *pProcessed = false; if(pProcessed) *pProcessed = false;
return; return;
} }
if(pProcessed) *pProcessed = true; if(pProcessed) *pProcessed = true;
if(ui->extFreeTextMsgEdit->toPlainText().trimmed().isEmpty()){
return;
}
toggleTx(true); toggleTx(true);
}); });
ui->extFreeTextMsgEdit->installEventFilter(enterFilter); ui->extFreeTextMsgEdit->installEventFilter(enterFilter);
@@ -1047,6 +1053,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
ui->textEditRX->addAction(clearAction1); ui->textEditRX->addAction(clearAction1);
ui->textEditRX->addAction(clearActionAll); ui->textEditRX->addAction(clearActionAll);
auto clearAction2 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->extFreeTextMsgEdit); auto clearAction2 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->extFreeTextMsgEdit);
connect(clearAction2, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->extFreeTextMsgEdit); }); connect(clearAction2, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->extFreeTextMsgEdit); });
ui->extFreeTextMsgEdit->setContextMenuPolicy(Qt::ActionsContextMenu); ui->extFreeTextMsgEdit->setContextMenuPolicy(Qt::ActionsContextMenu);
@@ -1068,6 +1076,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
menu->popup(ui->extFreeTextMsgEdit->mapToGlobal(point)); menu->popup(ui->extFreeTextMsgEdit->mapToGlobal(point));
}); });
auto clearAction3 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->tableWidgetRXAll); auto clearAction3 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->tableWidgetRXAll);
connect(clearAction3, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->tableWidgetRXAll); }); connect(clearAction3, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->tableWidgetRXAll); });
@@ -1088,6 +1098,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect(logAction, &QAction::triggered, this, &MainWindow::on_logQSOButton_clicked); connect(logAction, &QAction::triggered, this, &MainWindow::on_logQSOButton_clicked);
ui->tableWidgetRXAll->setContextMenuPolicy(Qt::CustomContextMenu); ui->tableWidgetRXAll->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->tableWidgetRXAll, &QTableWidget::customContextMenuRequested, this, [this, clearAction3, clearActionAll, removeActivity, logAction](QPoint const &point){ connect(ui->tableWidgetRXAll, &QTableWidget::customContextMenuRequested, this, [this, clearAction3, clearActionAll, removeActivity, logAction](QPoint const &point){
QMenu * menu = new QMenu(ui->tableWidgetRXAll); QMenu * menu = new QMenu(ui->tableWidgetRXAll);
@@ -1108,6 +1119,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
directedMenu->setDisabled(missingCallsign); directedMenu->setDisabled(missingCallsign);
buildQueryMenu(directedMenu, selectedCall); buildQueryMenu(directedMenu, selectedCall);
auto deselect = menu->addAction("Deselect");
deselect->setDisabled(missingCallsign);
connect(deselect, &QAction::triggered, this, [this](){
ui->tableWidgetRXAll->clearSelection();
ui->tableWidgetCalls->clearSelection();
});
menu->addSeparator(); menu->addSeparator();
removeActivity->setDisabled(selectedOffset == -1); removeActivity->setDisabled(selectedOffset == -1);
@@ -1160,6 +1178,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
directedMenu->setDisabled(missingCallsign); directedMenu->setDisabled(missingCallsign);
buildQueryMenu(directedMenu, selectedCall); buildQueryMenu(directedMenu, selectedCall);
auto deselect = menu->addAction("Deselect");
deselect->setDisabled(missingCallsign);
connect(deselect, &QAction::triggered, this, [this](){
ui->tableWidgetRXAll->clearSelection();
ui->tableWidgetCalls->clearSelection();
});
menu->addSeparator(); menu->addSeparator();
removeStation->setDisabled(missingCallsign || isAllCall); removeStation->setDisabled(missingCallsign || isAllCall);
@@ -1177,6 +1202,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
menu->popup(ui->tableWidgetCalls->mapToGlobal(point)); menu->popup(ui->tableWidgetCalls->mapToGlobal(point));
}); });
connect(ui->tableWidgetRXAll->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::on_tableWidgetRXAll_selectionChanged);
connect(ui->tableWidgetCalls->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::on_tableWidgetCalls_selectionChanged);
// Don't block beacon's first run... // Don't block beacon's first run...
m_lastTxTime = QDateTime::currentDateTimeUtc().addSecs(-300); m_lastTxTime = QDateTime::currentDateTimeUtc().addSecs(-300);
@@ -2354,8 +2382,8 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
{ {
switch (e->key()) { switch (e->key()) {
case Qt::Key_Escape: case Qt::Key_Escape:
stopTx();
on_stopTxButton_clicked(); on_stopTxButton_clicked();
stopTx();
return; return;
} }
@@ -3887,7 +3915,11 @@ void MainWindow::aprsLogReport(int offset, int snr, QString callsign, QString gr
return; return;
} }
m_aprsClient->enqueueSpot(Radio::base_callsign(callsign), grid, frequency, snr); auto comment = QString("%1MHz %2dB").arg(Radio::frequency_MHz_string(frequency)).arg(Varicode::formatSNR(snr));
if(callsign.contains("/")){
comment = QString("%1 %2").arg(callsign).arg(comment);
}
m_aprsClient->enqueueSpot(Radio::base_callsign(callsign), grid, comment);
} }
void MainWindow::killFile () void MainWindow::killFile ()
@@ -5796,6 +5828,7 @@ void MainWindow::displayTextForFreq(QString text, int freq, QDateTime date, bool
int highFreq = lowFreq + 10; int highFreq = lowFreq + 10;
int block = -1; int block = -1;
if(m_rxFrameBlockNumbers.contains(freq)){ if(m_rxFrameBlockNumbers.contains(freq)){
block = m_rxFrameBlockNumbers[freq]; block = m_rxFrameBlockNumbers[freq];
} else if(m_rxFrameBlockNumbers.contains(lowFreq)){ } else if(m_rxFrameBlockNumbers.contains(lowFreq)){
@@ -5807,7 +5840,7 @@ void MainWindow::displayTextForFreq(QString text, int freq, QDateTime date, bool
} }
if(isNewLine){ if(isNewLine){
m_rxFrameBlockNumbers.remove(freq); //m_rxFrameBlockNumbers.remove(freq);
m_rxFrameBlockNumbers.remove(lowFreq); m_rxFrameBlockNumbers.remove(lowFreq);
m_rxFrameBlockNumbers.remove(highFreq); m_rxFrameBlockNumbers.remove(highFreq);
block = -1; block = -1;
@@ -5816,7 +5849,9 @@ void MainWindow::displayTextForFreq(QString text, int freq, QDateTime date, bool
block = writeMessageTextToUI(date, text, freq, isTx, block); block = writeMessageTextToUI(date, text, freq, isTx, block);
// never cache tx or last lines // never cache tx or last lines
if(!isTx && !isLast){ if(isTx || isLast) {
// pass
} else {
m_rxFrameBlockNumbers.insert(freq, block); m_rxFrameBlockNumbers.insert(freq, block);
m_rxFrameBlockNumbers.insert(lowFreq, block); m_rxFrameBlockNumbers.insert(lowFreq, block);
m_rxFrameBlockNumbers.insert(highFreq, block); m_rxFrameBlockNumbers.insert(highFreq, block);
@@ -5872,7 +5907,6 @@ int MainWindow::writeMessageTextToUI(QDateTime date, QString text, int freq, boo
c.movePosition(QTextCursor::End); c.movePosition(QTextCursor::End);
ui->textEditRX->ensureCursorVisible(); ui->textEditRX->ensureCursorVisible();
ui->textEditRX->verticalScrollBar()->setValue(ui->textEditRX->verticalScrollBar()->maximum()); ui->textEditRX->verticalScrollBar()->setValue(ui->textEditRX->verticalScrollBar()->maximum());
@@ -5971,12 +6005,14 @@ void MainWindow::createMessage(QString const& text){
on_stopTxButton_clicked(); on_stopTxButton_clicked();
return; return;
} }
resetMessageTransmitQueue(); resetMessageTransmitQueue();
createMessageTransmitQueue(text); createMessageTransmitQueue(text);
} }
void MainWindow::createMessageTransmitQueue(QString const& text){ void MainWindow::createMessageTransmitQueue(QString const& text){
auto frames = buildFT8MessageFrames(text); auto frames = buildFT8MessageFrames(text);
m_txFrameQueue.append(frames); m_txFrameQueue.append(frames);
m_txFrameCount = frames.length(); m_txFrameCount = frames.length();
@@ -6058,8 +6094,14 @@ int MainWindow::countFT8MessageFrames(QString const& text){
} }
QStringList MainWindow::buildFT8MessageFrames(QString const& text){ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
#define ALLOW_SEND_COMPOUND 1
#define AUTO_PREPEND_DIRECTED 1
QStringList frames; QStringList frames;
// prepare selected callsign for directed message
QString selectedCall = callsignSelected();
// prepare compound // prepare compound
bool compound = Radio::is_compound_callsign(m_config.my_callsign()); bool compound = Radio::is_compound_callsign(m_config.my_callsign());
QString mygrid = m_config.my_grid().left(4); QString mygrid = m_config.my_grid().left(4);
@@ -6070,7 +6112,6 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
} }
foreach(QString line, text.split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts)){ foreach(QString line, text.split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts)){
// once we find a directed call, data encode the rest of the line. // once we find a directed call, data encode the rest of the line.
bool hasDirected = false; bool hasDirected = false;
@@ -6085,7 +6126,33 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
line = lstrip(line.mid(basecall.length() + 1)); line = lstrip(line.mid(basecall.length() + 1));
} }
#define ALLOW_SEND_COMPOUND 1 #if AUTO_PREPEND_DIRECTED
// see if we need to prepend the directed call to the line...
// if we have a selected call and the text doesn't start with that call...
// and if this isn't a raw message (starting with "<")... then...
if(!selectedCall.isEmpty() && !line.startsWith(selectedCall) && !line.startsWith("<")){
auto calls = Varicode::parseCallsigns(line);
bool lineStartsWithBaseCall = (
line.startsWith("ALLCALL") ||
line.startsWith("CQCQCQ") ||
line.startsWith("BEACON")
);
bool lineStartsWithStandardCall = !calls.isEmpty() && line.startsWith(calls.first());
if(lineStartsWithBaseCall || lineStartsWithStandardCall){
// pass
} else {
// if the message doesn't start with a base call
// and if there are no other callsigns in this message
// or if the first callsign in the message isn't at the beginning...
// then we should be auto-prefixing this line with the selected call
line = QString("%1 %2").arg(selectedCall).arg(line);
}
}
#endif
while(line.size() > 0){ while(line.size() > 0){
QString frame; QString frame;
@@ -6515,6 +6582,7 @@ void MainWindow::on_startTxButton_toggled(bool checked)
} else { } else {
resetMessage(); resetMessage();
stopTx(); stopTx();
on_stopTxButton_clicked();
} }
} }
@@ -7041,7 +7109,7 @@ void MainWindow::band_changed (Frequency f)
m_bandEdited = false; m_bandEdited = false;
psk_Reporter->sendReport(); // Upload any queued spots before changing band psk_Reporter->sendReport(); // Upload any queued spots before changing band
m_aprsClient->processQueue(); m_aprsClient->processQueue(true);
if (!m_transmitting) monitor (true); if (!m_transmitting) monitor (true);
if ("FreqCal" == m_mode) if ("FreqCal" == m_mode)
{ {
@@ -7301,6 +7369,8 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
addMessageText(QString("%1 ").arg(selectedCall), true); addMessageText(QString("%1 ").arg(selectedCall), true);
}); });
menu->addSeparator();
auto sendReplyAction = menu->addAction(QString("%1 Reply - Send reply message to selected callsign").arg(call).trimmed()); auto sendReplyAction = menu->addAction(QString("%1 Reply - Send reply message to selected callsign").arg(call).trimmed());
connect(sendReplyAction, &QAction::triggered, this, [this](){ connect(sendReplyAction, &QAction::triggered, this, [this](){
QString selectedCall = callsignSelected(); QString selectedCall = callsignSelected();
@@ -7312,7 +7382,7 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
}); });
auto sendSNRAction = menu->addAction(QString("%1 SNR - Send a signal report to the selected callsign").arg(call).trimmed()); auto sendSNRAction = menu->addAction(QString("%1 SNR - Send a signal report to the selected callsign").arg(call).trimmed());
sendSNRAction->setEnabled(m_callActivity.contains(call)); sendSNRAction->setEnabled(m_callActivity.contains(callsignSelected()));
connect(sendSNRAction, &QAction::triggered, this, [this](){ connect(sendSNRAction, &QAction::triggered, this, [this](){
QString selectedCall = callsignSelected(); QString selectedCall = callsignSelected();
@@ -7330,20 +7400,6 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
if(m_config.transmit_directed()) toggleTx(true); if(m_config.transmit_directed()) toggleTx(true);
}); });
auto sendPWRAction = menu->addAction(QString("%1 PWR - Send station power level to the selected callsign").arg(call).trimmed());
sendPWRAction->setDisabled(isAllCall || m_config.my_dBm() < 0);
connect(sendPWRAction, &QAction::triggered, this, [this](){
QString selectedCall = callsignSelected();
if(selectedCall.isEmpty()){
return;
}
addMessageText(QString("%1 PWR %2").arg(selectedCall).arg(Varicode::formatPWR(m_config.my_dBm())), true);
if(m_config.transmit_directed()) toggleTx(true);
});
menu->addSeparator(); menu->addSeparator();
auto snrQueryAction = menu->addAction(QString("%1? - What is my signal report?").arg(call)); auto snrQueryAction = menu->addAction(QString("%1? - What is my signal report?").arg(call));
@@ -7730,6 +7786,15 @@ void MainWindow::on_tableWidgetRXAll_cellDoubleClicked(int row, int col){
} }
void MainWindow::on_tableWidgetRXAll_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/){ void MainWindow::on_tableWidgetRXAll_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/){
on_extFreeTextMsgEdit_currentTextChanged(ui->extFreeTextMsgEdit->toPlainText());
auto placeholderText = QString("Type your outgoing messages here.");
auto selectedCall = callsignSelected();
if(!selectedCall.isEmpty()){
placeholderText = QString("Type your outgoing directed message to %1 here.").arg(selectedCall);
}
ui->extFreeTextMsgEdit->setPlaceholderText(placeholderText);
updateButtonDisplay(); updateButtonDisplay();
} }
@@ -8332,7 +8397,15 @@ void MainWindow::pskSetLocal ()
void MainWindow::aprsSetLocal () void MainWindow::aprsSetLocal ()
{ {
m_aprsClient->setLocalStation(Radio::base_callsign(m_config.my_callsign()), m_config.my_grid()); auto ssid = m_config.aprs_ssid();
auto call = Radio::base_callsign(m_config.my_callsign());
if(!ssid.isEmpty()){
if(!ssid.startsWith("-")){
ssid = "-" + ssid;
}
call = call + ssid;
}
m_aprsClient->setLocalStation(call, m_config.my_grid());
} }
void MainWindow::transmitDisplay (bool transmitting) void MainWindow::transmitDisplay (bool transmitting)
@@ -8925,6 +8998,7 @@ void MainWindow::processCompoundActivity() {
buffer.cmd.from = d.call; buffer.cmd.from = d.call;
buffer.cmd.grid = d.grid; buffer.cmd.grid = d.grid;
buffer.cmd.isCompound = true; buffer.cmd.isCompound = true;
buffer.cmd.utcTimestamp = qMin(buffer.cmd.utcTimestamp, d.utcTimestamp);
if ((d.bits & Varicode::FT8CallLast) == Varicode::FT8CallLast) { if ((d.bits & Varicode::FT8CallLast) == Varicode::FT8CallLast) {
buffer.cmd.bits = d.bits; buffer.cmd.bits = d.bits;
@@ -8935,6 +9009,7 @@ void MainWindow::processCompoundActivity() {
auto d = buffer.compound.dequeue(); auto d = buffer.compound.dequeue();
buffer.cmd.to = d.call; buffer.cmd.to = d.call;
buffer.cmd.isCompound = true; buffer.cmd.isCompound = true;
buffer.cmd.utcTimestamp = qMin(buffer.cmd.utcTimestamp, d.utcTimestamp);
if ((d.bits & Varicode::FT8CallLast) == Varicode::FT8CallLast) { if ((d.bits & Varicode::FT8CallLast) == Varicode::FT8CallLast) {
buffer.cmd.bits = d.bits; buffer.cmd.bits = d.bits;
@@ -8946,6 +9021,18 @@ void MainWindow::processCompoundActivity() {
continue; continue;
} }
// fixup the datetime with the "minimum" dt seen
// this will allow us to delete the activity lines
// when the compound buffered command comes in.
auto dt = buffer.cmd.utcTimestamp;
foreach(auto c, buffer.compound){
dt = qMin(dt, c.utcTimestamp);
}
foreach(auto m, buffer.msgs){
dt = qMin(dt, m.utcTimestamp);
}
buffer.cmd.utcTimestamp = dt;
qDebug() << "buffered compound command ready" << buffer.cmd.from << buffer.cmd.to << buffer.cmd.cmd; qDebug() << "buffered compound command ready" << buffer.cmd.from << buffer.cmd.to << buffer.cmd.cmd;
m_rxCommandQueue.append(buffer.cmd); m_rxCommandQueue.append(buffer.cmd);
@@ -9103,12 +9190,18 @@ void MainWindow::processCommandActivity() {
// we'd be double printing here if were on frequency, so let's be "smart" about this... // we'd be double printing here if were on frequency, so let's be "smart" about this...
bool shouldDisplay = true; bool shouldDisplay = true;
if(shouldDisplay){ if(shouldDisplay){
if(isRecentOffset(d.freq) && ui->textEditRX->find(QString("(%1)").arg(ad.freq), QTextDocument::FindBackward)){
// ... maybe we could delete the last line that had this message on this frequency...
auto c = ui->textEditRX->textCursor(); auto c = ui->textEditRX->textCursor();
c.movePosition(QTextCursor::End);
ui->textEditRX->setTextCursor(c);
if(isRecentOffset(d.freq) && ui->textEditRX->find(d.utcTimestamp.time().toString(), QTextDocument::FindBackward)){
// ... maybe we could delete the last line that had this message on this frequency...
c = ui->textEditRX->textCursor();
c.movePosition(QTextCursor::StartOfBlock); c.movePosition(QTextCursor::StartOfBlock);
c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); c.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
qDebug() << "should display directed message, erasing last rx activity line..." << c.selectedText(); qDebug() << "should display directed message, erasing last rx activity line..." << c.selectedText();
c.deletePreviousChar();
c.deletePreviousChar();
c.deleteChar(); c.deleteChar();
c.deleteChar(); c.deleteChar();
} }
@@ -9140,10 +9233,6 @@ void MainWindow::processCommandActivity() {
if (d.cmd == "?") { if (d.cmd == "?") {
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr)); reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
} }
// QUERIED PWR
else if (d.cmd == "%" && !isAllCall && m_config.my_dBm() >= 0) {
reply = QString("%1 PWR %2").arg(d.from).arg(Varicode::formatPWR(m_config.my_dBm()));
}
// QUERIED QTH // QUERIED QTH
else if (d.cmd == "@" && !isAllCall) { else if (d.cmd == "@" && !isAllCall) {
QString qth = m_config.my_qth(); QString qth = m_config.my_qth();
@@ -9258,7 +9347,7 @@ void MainWindow::processCommandActivity() {
logCallActivity(cd, true); logCallActivity(cd, true);
} }
reply = QString("%1 ACK").arg(d.from); continue;
} }
// PROCESS APRS // PROCESS APRS
else if(d.cmd == " APRS:" && m_config.spot_to_reporting_networks()){ else if(d.cmd == " APRS:" && m_config.spot_to_reporting_networks()){
@@ -9544,18 +9633,19 @@ void MainWindow::displayBandActivity() {
} }
ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount()); ui->tableWidgetRXAll->insertRow(ui->tableWidgetRXAll->rowCount());
int row = ui->tableWidgetRXAll->rowCount() - 1;
auto offsetItem = new QTableWidgetItem(QString("%1").arg(offset)); auto offsetItem = new QTableWidgetItem(QString("%1").arg(offset));
offsetItem->setData(Qt::UserRole, QVariant(offset)); offsetItem->setData(Qt::UserRole, QVariant(offset));
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, offsetItem); ui->tableWidgetRXAll->setItem(row, 0, offsetItem);
auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age)); auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age));
ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, ageItem); ui->tableWidgetRXAll->setItem(row, 1, ageItem);
auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr))); auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr)));
snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, snrItem); ui->tableWidgetRXAll->setItem(row, 2, snrItem);
// align right if eliding... // align right if eliding...
int colWidth = ui->tableWidgetRXAll->columnWidth(3); int colWidth = ui->tableWidgetRXAll->columnWidth(3);
@@ -9586,10 +9676,12 @@ void MainWindow::displayBandActivity() {
textItem->setBackground(QBrush(m_config.color_MyCall())); textItem->setBackground(QBrush(m_config.color_MyCall()));
} }
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 3, textItem); ui->tableWidgetRXAll->setItem(row, 3, textItem);
if (offset == selectedOffset) { if (offset == selectedOffset) {
ui->tableWidgetRXAll->selectRow(ui->tableWidgetRXAll->rowCount() - 1); for(int i = 0; i < ui->tableWidgetRXAll->columnCount(); i++){
ui->tableWidgetRXAll->item(row, i)->setSelected(true);
}
} }
} }
} }
@@ -9611,10 +9703,10 @@ void MainWindow::displayCallActivity() {
// Selected callsign // Selected callsign
QString selectedCall = callsignSelected(); QString selectedCall = callsignSelected();
ui->tableWidgetCalls->setUpdatesEnabled(false);
{
auto currentScrollPos = ui->tableWidgetCalls->verticalScrollBar()->value(); auto currentScrollPos = ui->tableWidgetCalls->verticalScrollBar()->value();
ui->tableWidgetCalls->setUpdatesEnabled(false);
{
// Clear the table // Clear the table
clearTableWidget(ui->tableWidgetCalls); clearTableWidget(ui->tableWidgetCalls);
@@ -9705,22 +9797,25 @@ void MainWindow::displayCallActivity() {
} }
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount()); ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
int row = ui->tableWidgetCalls->rowCount() - 1;
QString displayCall = d.through.isEmpty() ? d.call : QString("%1 | %2").arg(d.through).arg(d.call); QString displayCall = d.through.isEmpty() ? d.call : QString("%1 | %2").arg(d.through).arg(d.call);
auto displayItem = new QTableWidgetItem(displayCall); auto displayItem = new QTableWidgetItem(displayCall);
displayItem->setData(Qt::UserRole, QVariant((d.call))); displayItem->setData(Qt::UserRole, QVariant((d.call)));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, displayItem); ui->tableWidgetCalls->setItem(row, 0, displayItem);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 1, new QTableWidgetItem(QString("(%1)").arg(since(d.utcTimestamp)))); ui->tableWidgetCalls->setItem(row, 1, new QTableWidgetItem(QString("(%1)").arg(since(d.utcTimestamp))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 2, new QTableWidgetItem(QString("%1").arg(d.freq))); ui->tableWidgetCalls->setItem(row, 2, new QTableWidgetItem(QString("%1").arg(d.freq)));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 3, new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(d.snr)))); ui->tableWidgetCalls->setItem(row, 3, new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(d.snr))));
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 4, new QTableWidgetItem(QString("%1").arg(d.grid))); ui->tableWidgetCalls->setItem(row, 4, new QTableWidgetItem(QString("%1").arg(d.grid)));
auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid)); auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid));
distanceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); distanceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 5, distanceItem); ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 5, distanceItem);
if (call == selectedCall) { if (call == selectedCall) {
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1); for(int i = 0; i < ui->tableWidgetCalls->columnCount(); i++){
ui->tableWidgetCalls->item(row, i)->setSelected(true);
}
} }
} }
+1 -1
View File
@@ -1118,7 +1118,7 @@ QTextEdit[transmitting=&quot;true&quot;] {
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="placeholderText"> <property name="placeholderText">
<string>Type your outgoing messages / commands here.</string> <string>Type your outgoing messages here.</string>
</property> </property>
</widget> </widget>
</widget> </widget>
+2 -1
View File
@@ -40,7 +40,8 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor
m_Percent2DScreen0 {0}, m_Percent2DScreen0 {0},
m_rxFreq {1020}, m_rxFreq {1020},
m_txFreq {0}, m_txFreq {0},
m_startFreq {0} m_startFreq {0},
m_lastMouseX {-1}
{ {
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
+20 -80
View File
@@ -45,7 +45,7 @@ QMap<QString, int> directed_cmds = {
{"&", 2 }, // query station message {"&", 2 }, // query station message
{"$", 3 }, // query station(s) heard {"$", 3 }, // query station(s) heard
{"^", 4 }, // query grid {"^", 4 }, // query grid
{"%", 5 }, // query pwr // {"%" 5 }, // unused
{"|", 6 }, // retransmit message {"|", 6 }, // retransmit message
{"!", 7 }, // alert message {"!", 7 }, // alert message
{"#", 8 }, // all or nothing message {"#", 8 }, // all or nothing message
@@ -68,7 +68,7 @@ QMap<QString, int> directed_cmds = {
{" RR", 21 }, // roger roger {" RR", 21 }, // roger roger
{" QSL?", 22 }, // do you copy? {" QSL?", 22 }, // do you copy?
{" QSL", 23 }, // i copy {" QSL", 23 }, // i copy
{" PWR", 24 }, // power level // {"", 24 }, // unused
{" SNR", 25 }, // seen a station at the provided snr {" SNR", 25 }, // seen a station at the provided snr
{" NO", 26 }, // negative confirm {" NO", 26 }, // negative confirm
{" YES", 27 }, // confirm {" YES", 27 }, // confirm
@@ -78,7 +78,7 @@ QMap<QString, int> directed_cmds = {
{" ", 31 }, // send freetext {" ", 31 }, // send freetext
}; };
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 8, /*...*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; QSet<int> allowed_cmds = {0, 1, 2, 3, 4, /*5,*/ 6, 7, 8, /*...*/ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, /*24,*/ 25, 26, 27, 28, 29, 30, 31};
QSet<int> buffered_cmds = {6, 7, 8, 13, 14, 15}; QSet<int> buffered_cmds = {6, 7, 8, 13, 14, 15};
@@ -92,16 +92,14 @@ QMap<int, int> checksum_cmds = {
}; };
QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)"); QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|PWR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^ ]))?"); QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|ACK|73|YES|NO|SNR|QSL[?]?|RR|HEARING|HW CPY[?]|FB|QTH|QTC|GRID|APRS[:]|QSO|[?@&$%|!#^ ]))?");
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?"); QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?"); QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
QString optional_pwr_pattern = QString("(?<pwr>(?<=PWR)\\s?\\d+\\s?[KM]?W)?");
QString optional_num_pattern = QString("(?<num>(?<=SNR|HEARING)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); QString optional_num_pattern = QString("(?<num>(?<=SNR|HEARING)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
QRegularExpression directed_re("^" + QRegularExpression directed_re("^" +
callsign_pattern + callsign_pattern +
optional_cmd_pattern + optional_cmd_pattern +
optional_pwr_pattern +
optional_num_pattern); optional_num_pattern);
QRegularExpression beacon_re(R"(^\s*(?<type>CQCQCQ|BEACON)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)"); QRegularExpression beacon_re(R"(^\s*(?<type>CQCQCQ|BEACON)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)");
@@ -111,7 +109,6 @@ QRegularExpression compound_re("^\\s*[<]" +
"(?<extra>" + "(?<extra>" +
optional_grid_pattern + optional_grid_pattern +
optional_cmd_pattern + optional_cmd_pattern +
optional_pwr_pattern +
optional_num_pattern + optional_num_pattern +
")[>]"); ")[>]");
@@ -440,19 +437,6 @@ QString Varicode::formatSNR(int snr){
return QString("%1%2").arg(snr >= 0 ? "+" : "").arg(snr, snr < 0 ? 3 : 2, 10, QChar('0')); return QString("%1%2").arg(snr >= 0 ? "+" : "").arg(snr, snr < 0 ? 3 : 2, 10, QChar('0'));
} }
QString Varicode::formatPWR(int dbm){
if(dbm < 0 || dbm > 60){
return QString();
}
int mwatts = dbmTomwatts(dbm);
if(mwatts < 1000){
return QString("%1mW").arg(mwatts);
}
return QString("%1W").arg(mwatts/1000);
}
QString Varicode::checksum16(QString const &input){ QString Varicode::checksum16(QString const &input){
auto fromBytes = input.toLocal8Bit(); auto fromBytes = input.toLocal8Bit();
auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_16_KERMIT()); auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_16_KERMIT());
@@ -1061,43 +1045,18 @@ quint8 Varicode::packNum(QString const &num, bool *ok){
return inum + 30 + 1; return inum + 30 + 1;
} }
// pack pwr string into a dbm between 0 and 62
quint8 Varicode::packPwr(QString const &pwr, bool *ok){
int ipwr = 0;
if(pwr.isEmpty()){
if(ok) *ok = false;
return ipwr;
}
int factor = 1000;
if(pwr.endsWith("KW")){
factor = 1000000;
}
else if(pwr.endsWith("MW")){
factor = 1;
}
ipwr = QString(pwr).replace(QRegExp("[KM]?W", Qt::CaseInsensitive), "").toInt() * factor;
ipwr = mwattsToDbm(ipwr);
if(ok) *ok = true;
return ipwr + 1;
}
// pack a reduced fidelity command and a number into the extra bits provided between nbasegrid and nmaxgrid // pack a reduced fidelity command and a number into the extra bits provided between nbasegrid and nmaxgrid
quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){ quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){
//quint8 allowed = nmaxgrid - nbasegrid - 1; //quint8 allowed = nmaxgrid - nbasegrid - 1;
// if cmd == pwr || cmd == snr // if cmd == snr
quint8 value = 0; quint8 value = 0;
if(cmd == directed_cmds[" PWR"] || cmd == directed_cmds[" SNR"]){ if(cmd == directed_cmds[" SNR"]){
// 8 bits - 1 bit flag + 1 bit type + 6 bit number // 8 bits - 1 bit flag + 1 bit type + 6 bit number
// [1][X][6] // [1][X][6]
// X = 0 == SNR // X = 0 == SNR
// X = 1 == PWR value = (1 << 1) << 6;
value = value + (num & ((1<<6)-1));
value = ((1 << 1) + ((int)cmd == directed_cmds[" PWR"])) << 6;
value = value + num;
if(pPackedNum) *pPackedNum = true; if(pPackedNum) *pPackedNum = true;
} else { } else {
value = cmd & ((1<<7)-1); value = cmd & ((1<<7)-1);
@@ -1108,18 +1067,10 @@ quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){
} }
quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){ quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){
// if the first bit is 1, the second bit will indicate if its pwr or snr... // if the first bit is 1, this is an SNR with a number encoded in the lower 6 bits
if(value & (1<<7)){ if(value & (1<<7)){
// either pwr or snr...
bool pwr = value & (1<<6);
if(pNum) *pNum = value & ((1<<6)-1); if(pNum) *pNum = value & ((1<<6)-1);
if(pwr){
return directed_cmds[" PWR"];
} else {
return directed_cmds[" SNR"]; return directed_cmds[" SNR"];
}
} else { } else {
if(pNum) *pNum = 0; if(pNum) *pNum = 0;
return value & ((1<<7)-1); return value & ((1<<7)-1);
@@ -1238,7 +1189,6 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
QString grid = parsedText.captured("grid"); QString grid = parsedText.captured("grid");
QString cmd = parsedText.captured("cmd"); QString cmd = parsedText.captured("cmd");
QString num = parsedText.captured("num").trimmed(); QString num = parsedText.captured("num").trimmed();
QString pwr = parsedText.captured("pwr").trimmed().toUpper();
QString base; QString base;
QString fix; QString fix;
@@ -1272,9 +1222,6 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
if (!cmd.isEmpty() && directed_cmds.contains(cmd) && Varicode::isCommandAllowed(cmd)){ if (!cmd.isEmpty() && directed_cmds.contains(cmd) && Varicode::isCommandAllowed(cmd)){
bool packedNum = false; bool packedNum = false;
qint8 inum = Varicode::packNum(num, nullptr); qint8 inum = Varicode::packNum(num, nullptr);
if(cmd == " PWR"){
inum = Varicode::packPwr(pwr, nullptr);
}
extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum, &packedNum); extra = nusergrid + Varicode::packCmd(directed_cmds[cmd], inum, &packedNum);
type = FrameCompoundDirected; type = FrameCompoundDirected;
@@ -1305,9 +1252,7 @@ QStringList Varicode::unpackCompoundMessage(const QString &text, quint8 *pType){
unpacked.append(directed_cmds.key(cmd)); unpacked.append(directed_cmds.key(cmd));
if(cmd == directed_cmds[" PWR"]){ if(cmd == directed_cmds[" SNR"]){
unpacked.append(Varicode::formatPWR(num - 1));
} else if(cmd == directed_cmds[" SNR"]){
unpacked.append(Varicode::formatSNR(num - 31)); unpacked.append(Varicode::formatSNR(num - 31));
} }
} }
@@ -1412,7 +1357,6 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi
QString to = match.captured("callsign"); QString to = match.captured("callsign");
QString cmd = match.captured("cmd"); QString cmd = match.captured("cmd");
QString num = match.captured("num").trimmed(); QString num = match.captured("num").trimmed();
QString pwr = match.captured("pwr").trimmed().toUpper();
// ensure we have a directed command // ensure we have a directed command
if(cmd.isEmpty()){ if(cmd.isEmpty()){
@@ -1439,23 +1383,15 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi
} }
} }
// validate command // validate command
if(!Varicode::isCommandAllowed(cmd)){ if(!Varicode::isCommandAllowed(cmd) && !Varicode::isCommandAllowed(cmd.trimmed())){
if(n) *n = 0; if(n) *n = 0;
return frame; return frame;
} }
// packing general number... // packing general number...
quint8 inum = 0; quint8 inum = Varicode::packNum(num, nullptr);
if(cmd.trimmed() == "PWR"){
inum = Varicode::packPwr(pwr, nullptr);
if(pNum) *pNum = pwr;
} else {
inum = Varicode::packNum(num, nullptr);
if(pNum) *pNum = num; if(pNum) *pNum = num;
}
quint32 packed_from = Varicode::packCallsign(from); quint32 packed_from = Varicode::packCallsign(from);
quint32 packed_to = Varicode::packCallsign(to); quint32 packed_to = Varicode::packCallsign(to);
@@ -1465,7 +1401,13 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi
return frame; return frame;
} }
quint8 packed_cmd = directed_cmds[cmd]; quint8 packed_cmd = 0;
if(directed_cmds.contains(cmd)){
packed_cmd = directed_cmds[cmd];
}
if(directed_cmds.contains(cmd.trimmed())){
packed_cmd = directed_cmds[cmd.trimmed()];
}
quint8 packed_flag = FrameDirected; quint8 packed_flag = FrameDirected;
quint8 packed_extra = inum; quint8 packed_extra = inum;
@@ -1512,9 +1454,7 @@ QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){
if(extra != 0){ if(extra != 0){
// TODO: jsherer - should we decide which format to use on the command, or something else? // TODO: jsherer - should we decide which format to use on the command, or something else?
if(packed_cmd == directed_cmds[" PWR"]){ if(packed_cmd == directed_cmds[" SNR"]) {
unpacked.append(Varicode::formatPWR(extra-1));
} else if(packed_cmd == directed_cmds[" SNR"]) {
unpacked.append(Varicode::formatSNR((int)extra-31)); unpacked.append(Varicode::formatSNR((int)extra-31));
} else { } else {
unpacked.append(QString("%1").arg(extra-31)); unpacked.append(QString("%1").arg(extra-31));