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;
+78 -44
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,21 +2028,70 @@ 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">
<widget class="QCheckBox" name="psk_reporter_check_box"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="toolTip"> <property name="bottomMargin">
<string>The program can send your station details and all <number>20</number>
</property>
<item>
<widget class="QCheckBox" name="psk_reporter_check_box">
<property name="toolTip">
<string>The program can send your station details and all
decoded signals as spots to the http://pskreporter.info web site. decoded signals as spots to the http://pskreporter.info web site.
This is used for reverse beacon analysis which is very useful 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>
<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> </item>
</layout> </layout>
</widget> </widget>
@@ -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
+140 -45
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,8 +5828,9 @@ 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)){
block = m_rxFrameBlockNumbers[lowFreq]; block = m_rxFrameBlockNumbers[lowFreq];
freq = lowFreq; freq = 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)){ 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... // ... maybe we could delete the last line that had this message on this frequency...
auto c = ui->textEditRX->textCursor(); 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();
auto currentScrollPos = ui->tableWidgetCalls->verticalScrollBar()->value();
ui->tableWidgetCalls->setUpdatesEnabled(false); ui->tableWidgetCalls->setUpdatesEnabled(false);
{ {
auto currentScrollPos = ui->tableWidgetCalls->verticalScrollBar()->value();
// 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);
+22 -82
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);
return directed_cmds[" SNR"];
if(pwr){
return directed_cmds[" PWR"];
} else {
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(pNum) *pNum = num;
if(cmd.trimmed() == "PWR"){
inum = Varicode::packPwr(pwr, nullptr);
if(pNum) *pNum = pwr;
} else {
inum = Varicode::packNum(num, nullptr);
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));