Compare commits

..

53 Commits

Author SHA1 Message Date
Jordan Sherer c0e8a791e6 Hotfix. Broken heartbeat ack when a directed callsign is selected 2018-10-31 18:17:34 -04:00
Jordan Sherer 74523985bb Time drift button labeling 2018-10-31 11:58:11 -04:00
Jordan Sherer 8d28ea345a 0.8 is not backwards compatible 2018-10-31 11:56:12 -04:00
Jordan Sherer 33446297fa Update heartbeat to allow on demand only. Add AUTO requirement for ACKs 2018-10-31 10:51:17 -04:00
Jordan Sherer 6f648d5a60 Added compatibility parsing for commands 2018-10-30 21:00:04 -04:00
Jordan Sherer f1f618bbcd Added worked before status 2018-10-30 20:50:31 -04:00
Jordan Sherer ac7da998a2 15 seconds is not now 2018-10-30 17:04:30 -04:00
Jordan Sherer b3cd705fb6 OCD 2018-10-30 17:00:14 -04:00
Jordan Sherer 604e366d4a Rig name in title 2018-10-29 22:41:54 -04:00
Jordan Sherer 8d73805dce Average time drift labels 2018-10-29 22:34:26 -04:00
Jordan Sherer 5c491dc10d Update threshold and tones. Cleanup bits 2018-10-29 17:56:31 -04:00
Jordan Sherer d611259a25 Fixed typo in varicode 2018-10-29 13:39:15 -04:00
Jordan Sherer 00f2d46167 Reduce late start threshold to 2 seconds 2018-10-29 09:24:52 -04:00
Jordan Sherer e4cba50144 Better labeling 2018-10-29 09:24:38 -04:00
Jordan Sherer 440311a75b Restructured data frame packing so we can send more over the wire in fewer frames 2018-10-29 03:26:10 -04:00
Jordan Sherer 38fc98702b Average time delta computation 2018-10-29 02:02:58 -04:00
Jordan Sherer 6201de8c12 Tweak decoder for better decodes under poor conditions 2018-10-29 01:09:17 -04:00
Jordan Sherer 4321bd5e75 Time drift columns and show column labels 2018-10-28 17:54:10 -04:00
Jordan Sherer 9f7fd2e7e2 Replaced character queries with textual queries to make it easier to read 2018-10-28 12:37:47 -04:00
Jordan Sherer 65a2411c46 Better interface for idle timer values 2018-10-28 12:06:37 -04:00
Jordan Sherer d076eedeb8 Fixed idle timer for idle watchdog 2018-10-28 11:24:00 -04:00
Jordan Sherer 7da30200f5 Heartbeat commands instead 2018-10-28 10:06:03 -04:00
Jordan Sherer cac5f798a3 Rename beacon to heartbean with pings and ping acks 2018-10-28 09:52:07 -04:00
Jordan Sherer 41fb7481ed Bump eol 2018-10-28 09:50:16 -04:00
Jordan Sherer 09effda8e4 Added default macro for CQ message 2018-10-27 15:25:23 -04:00
Jordan Sherer eecf7f65c7 Macro for CQ, Reply, QTH, QTC messages 2018-10-27 15:19:49 -04:00
Jordan Sherer 0618b1cc09 Do not allow text overrides for selected calls during parsing 2018-10-27 15:14:54 -04:00
Jordan Sherer 00685b9117 Warning message for stupid messages 2018-10-27 15:01:27 -04:00
Jordan Sherer a5a6c95a44 Added SK short message 2018-10-27 14:54:48 -04:00
Jordan Sherer 8612f2cd32 Hashed messages have a 16-bit checksum instead of a 32-bit checksum 2018-10-27 14:52:11 -04:00
Jordan Sherer 5fa60659f3 Idle minutes should always be incremented 2018-10-27 14:51:11 -04:00
Jordan Sherer 08629019d4 Added macro like functionality for saved messages 2018-10-27 14:41:57 -04:00
Jordan Sherer 7293d1e796 Remove table configuration from context menu 2018-10-27 14:41:40 -04:00
Jordan Sherer aff001ce96 Bump to v0.8.0 2018-10-27 12:42:16 -04:00
Jordan Sherer 31d80a9b1d Updated configuration tooltip and validation 2018-10-27 12:41:30 -04:00
Jordan Sherer 9010fa07c0 Replace ALLCALL with @ALLCALL 2018-10-27 10:13:34 -04:00
Jordan Sherer 121bb0fdb5 Replace GROUPCALL with actual group calls 2018-10-27 10:09:40 -04:00
Jordan Sherer 47a7c06854 Added configuration for groups 2018-10-27 09:54:13 -04:00
Jordan Sherer c4089c0af9 Don't allow compound calls greater than 9 chars 2018-10-27 09:53:57 -04:00
Jordan Sherer 5c28d154ff Added autoprepend back in 2018-10-26 22:16:48 -04:00
Jordan Sherer 6dfd037736 Compound callsigns and group callsigns parse correctly 2018-10-26 22:10:08 -04:00
Jordan Sherer 3767b347d9 Working compound extended callsigns with group identifiers 2018-10-26 22:10:07 -04:00
Jordan Sherer ff12fd2240 Added validation message for callsigns 2018-10-26 22:10:07 -04:00
Jordan Sherer 6a0fea1d0a Experiment trying to extend compound callsigns 2018-10-26 22:10:07 -04:00
Jordan Sherer f11e02c966 Do not delete band activity or call activity rows that are selected 2018-10-26 22:09:38 -04:00
Jordan Sherer 0763ecfe37 Expose transmit delay in configuration 2018-10-23 12:35:09 -04:00
Jordan Sherer 4c3ed5c719 Removed temp txt file 2018-10-23 12:34:29 -04:00
Jordan Sherer 0064176736 Reduce late start to 25% 2018-10-23 02:26:19 -04:00
Jordan Sherer 67386a09c7 Simple buffered directed commands should log immediately 2018-10-23 02:18:03 -04:00
Jordan Sherer 3c81108cc6 Moved relay responses into the main window 2018-10-23 02:06:24 -04:00
Jordan Sherer 71ed0c1f10 Added menu for decode depth. Removed some WSPR. 2018-10-21 15:37:12 -04:00
Jordan Sherer 20a44171e4 Ripped out decoded.txt and houndcallers.txt junk 2018-10-20 09:04:03 -04:00
Jordan Sherer 644633721f Disable stations heard command: 2018-10-19 09:53:51 -04:00
27 changed files with 2275 additions and 1877 deletions
+65 -13
View File
@@ -454,6 +454,7 @@ private:
Q_SLOT void on_delete_macro_push_button_clicked (bool = false);
Q_SLOT void on_PTT_method_button_group_buttonClicked (int);
Q_SLOT void on_station_message_line_edit_textChanged(QString const&);
Q_SLOT void on_groups_line_edit_textChanged(QString const&);
Q_SLOT void on_qth_message_line_edit_textChanged(QString const&);
Q_SLOT void on_cq_message_line_edit_textChanged(QString const&);
Q_SLOT void on_reply_message_line_edit_textChanged(QString const&);
@@ -584,6 +585,7 @@ private:
QString my_callsign_;
QString my_grid_;
QString my_station_;
QStringList my_groups_;
QString my_qth_;
QString cq_;
QString reply_;
@@ -626,7 +628,7 @@ private:
bool spot_to_reporting_networks_;
bool transmit_directed_;
bool autoreply_off_at_startup_;
bool beacon_anywhere_;
bool heartbeat_anywhere_;
bool relay_disabled_;
bool monitor_off_at_startup_;
bool monitor_last_used_;
@@ -640,7 +642,7 @@ private:
bool miles_;
bool quick_call_;
bool disable_TX_on_73_;
int beacon_;
int heartbeat_;
int watchdog_;
bool TX_messages_;
bool enable_VHF_features_;
@@ -757,7 +759,7 @@ void Configuration::set_spot_to_reporting_networks (bool spot)
bool Configuration::transmit_directed() const { return m_->transmit_directed_; }
bool Configuration::autoreply_off_at_startup () const {return m_->autoreply_off_at_startup_;}
bool Configuration::beacon_anywhere() const { return m_->beacon_anywhere_;}
bool Configuration::heartbeat_anywhere() const { return m_->heartbeat_anywhere_;}
bool Configuration::relay_off() const { return m_->relay_disabled_; }
bool Configuration::monitor_off_at_startup () const {return m_->monitor_off_at_startup_;}
bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;}
@@ -771,7 +773,7 @@ bool Configuration::clear_DX () const {return m_->clear_DX_;}
bool Configuration::miles () const {return m_->miles_;}
bool Configuration::quick_call () const {return m_->quick_call_;}
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
int Configuration::beacon () const {return m_->beacon_;}
int Configuration::heartbeat () const {return m_->heartbeat_;}
int Configuration::watchdog () const {return m_->watchdog_;}
bool Configuration::TX_messages () const {return m_->TX_messages_;}
bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;}
@@ -934,6 +936,10 @@ QString Configuration::my_station() const
return station.trimmed();
}
QSet<QString> Configuration::my_groups() const {
return QSet<QString>::fromList(m_->my_groups_);
}
QString Configuration::my_qth() const
{
return m_->my_qth_.trimmed();
@@ -1300,6 +1306,7 @@ void Configuration::impl::initialize_models ()
ui_->callsign_aging_spin_box->setValue(callsign_aging_);
ui_->activity_aging_spin_box->setValue(activity_aging_);
ui_->station_message_line_edit->setText (my_station_.toUpper());
ui_->groups_line_edit->setText(my_groups_.join(", "));
ui_->qth_message_line_edit->setText (my_qth_.toUpper());
ui_->cq_message_line_edit->setText(cq_.toUpper());
ui_->reply_message_line_edit->setText (reply_.toUpper());
@@ -1331,7 +1338,7 @@ void Configuration::impl::initialize_models ()
ui_->psk_reporter_check_box->setChecked (spot_to_reporting_networks_);
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
ui_->beacon_anywhere_check_box->setChecked(beacon_anywhere_);
ui_->heartbeat_anywhere_check_box->setChecked(heartbeat_anywhere_);
ui_->relay_disabled_check_box->setChecked(relay_disabled_);
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
@@ -1343,7 +1350,7 @@ void Configuration::impl::initialize_models ()
ui_->miles_check_box->setChecked (miles_);
ui_->quick_call_check_box->setChecked (quick_call_);
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
ui_->beacon_spin_box->setValue (beacon_);
ui_->heartbeat_spin_box->setValue (heartbeat_);
ui_->tx_watchdog_spin_box->setValue (watchdog_);
ui_->enable_VHF_features_check_box->setChecked(enable_VHF_features_);
ui_->decode_at_52s_check_box->setChecked(decode_at_52s_);
@@ -1437,10 +1444,11 @@ void Configuration::impl::read_settings ()
my_callsign_ = settings_->value ("MyCall", QString {}).toString ();
my_grid_ = settings_->value ("MyGrid", QString {}).toString ();
my_station_ = settings_->value("MyStation", QString {}).toString();
my_groups_ = settings_->value("MyGroups", QStringList{}).toStringList();
callsign_aging_ = settings_->value ("CallsignAging", 0).toInt ();
activity_aging_ = settings_->value ("ActivityAging", 2).toInt ();
my_qth_ = settings_->value("MyQTH", QString {}).toString();
cq_ = settings_->value("CQMessage", QString {"CQCQCQ"}).toString();
cq_ = settings_->value("CQMessage", QString {"CQCQCQ <MYGRID4>"}).toString();
reply_ = settings_->value("Reply", QString {"HW CPY?"}).toString();
next_color_cq_ = color_cq_ = settings_->value("colorCQ","#66ff66").toString();
next_color_mycall_ = color_mycall_ = settings_->value("colorMyCall","#ff6666").toString();
@@ -1588,7 +1596,7 @@ void Configuration::impl::read_settings ()
transmit_directed_ = settings_->value ("TransmitDirected", true).toBool();
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
beacon_anywhere_ = settings_->value("BeaconAnywhere", false).toBool();
heartbeat_anywhere_ = settings_->value("BeaconAnywhere", false).toBool();
relay_disabled_ = settings_->value ("RelayOFF", false).toBool ();
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
@@ -1649,7 +1657,7 @@ void Configuration::impl::read_settings ()
miles_ = settings_->value ("Miles", false).toBool ();
quick_call_ = settings_->value ("QuickCall", false).toBool ();
disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
beacon_ = settings_->value ("TxBeacon", 30).toInt ();
heartbeat_ = settings_->value ("TxBeacon", 30).toInt ();
watchdog_ = settings_->value ("TxIdleWatchdog", 60).toInt ();
if(watchdog_){
watchdog_ = qMax(5, watchdog_);
@@ -1692,6 +1700,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("MyCall", my_callsign_);
settings_->setValue ("MyGrid", my_grid_);
settings_->setValue ("MyStation", my_station_);
settings_->setValue ("MyGroups", my_groups_);
settings_->setValue ("MyQTH", my_qth_);
settings_->setValue ("CQMessage", cq_);
settings_->setValue ("Reply", reply_);
@@ -1753,7 +1762,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
settings_->setValue ("TransmitDirected", transmit_directed_);
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
settings_->setValue ("BeaconAnywhere", beacon_anywhere_);
settings_->setValue ("BeaconAnywhere", heartbeat_anywhere_);
settings_->setValue ("RelayOFF", relay_disabled_);
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
@@ -1782,7 +1791,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("Miles", miles_);
settings_->setValue ("QuickCall", quick_call_);
settings_->setValue ("73TxDisable", disable_TX_on_73_);
settings_->setValue ("TxBeacon", beacon_);
settings_->setValue ("TxBeacon", heartbeat_);
settings_->setValue ("TxIdleWatchdog", watchdog_);
settings_->setValue ("Tx2QSO", TX_messages_);
settings_->setValue ("CATForceDTR", rig_params_.force_dtr);
@@ -1948,8 +1957,39 @@ void Configuration::impl::set_rig_invariants ()
|| TransceiverFactory::basic_transceiver_name_ != rig);
}
QStringList splitGroups(QString groupsString, bool filter){
QStringList groups;
if(groupsString.isEmpty()){
return groups;
}
foreach(QString group, groupsString.split(",")){
auto g = group.trimmed();
if(filter && !g.startsWith("@")){
continue;
}
groups.append(group.trimmed());
}
return groups;
}
bool Configuration::impl::validate ()
{
auto callsign = ui_->callsign_line_edit->text().trimmed();
if(!Varicode::isValidCallsign(callsign, nullptr) || callsign.startsWith("@")){
MessageBox::critical_message (this, tr ("The callsign format you provided is not supported"));
return false;
}
foreach(auto group, splitGroups(ui_->groups_line_edit->text(), false)){
if(!Varicode::isCompoundCallsign(group)){
MessageBox::critical_message (this, QString("%1 is not a valid group").arg(group));
return false;
}
}
if (ui_->sound_input_combo_box->currentIndex () < 0
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioInput).empty ())
{
@@ -2201,6 +2241,9 @@ void Configuration::impl::accept ()
my_callsign_ = ui_->callsign_line_edit->text ();
my_grid_ = ui_->grid_line_edit->text ();
my_station_ = ui_->station_message_line_edit->text().toUpper();
my_groups_ = splitGroups(ui_->groups_line_edit->text().toUpper().trimmed(), true);
cq_ = ui_->cq_message_line_edit->text().toUpper();
reply_ = ui_->reply_message_line_edit->text().toUpper();
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
@@ -2217,7 +2260,7 @@ void Configuration::impl::accept ()
tx_qsy_allowed_ = ui_->tx_qsy_check_box->isChecked ();
transmit_directed_ = ui_->transmit_directed_check_box->isChecked();
autoreply_off_at_startup_ = ui_->autoreply_off_check_box->isChecked ();
beacon_anywhere_ = ui_->beacon_anywhere_check_box->isChecked();
heartbeat_anywhere_ = ui_->heartbeat_anywhere_check_box->isChecked();
relay_disabled_ = ui_->relay_disabled_check_box->isChecked();
monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
@@ -2229,7 +2272,7 @@ void Configuration::impl::accept ()
miles_ = ui_->miles_check_box->isChecked ();
quick_call_ = ui_->quick_call_check_box->isChecked ();
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
beacon_ = ui_->beacon_spin_box->value ();
heartbeat_ = ui_->heartbeat_spin_box->value ();
watchdog_ = ui_->tx_watchdog_spin_box->value ();
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
save_directory_ = ui_->save_path_display_label->text ();
@@ -2631,6 +2674,15 @@ void Configuration::impl::on_station_message_line_edit_textChanged(QString const
}
}
void Configuration::impl::on_groups_line_edit_textChanged(QString const &text)
{
QString upper = text.toUpper();
if(text != upper){
ui_->groups_line_edit->setText (upper);
}
}
void Configuration::impl::on_qth_message_line_edit_textChanged(QString const &text)
{
QString upper = text.toUpper();
+3 -2
View File
@@ -98,6 +98,7 @@ public:
QString my_callsign () const;
QString my_grid () const;
QString my_station () const;
QSet<QString> my_groups() const;
int activity_aging() const;
int callsign_aging() const;
QString my_qth () const;
@@ -120,7 +121,7 @@ public:
void set_spot_to_reporting_networks (bool);
bool transmit_directed() const;
bool autoreply_off_at_startup () const;
bool beacon_anywhere() const;
bool heartbeat_anywhere() const;
bool relay_off() const;
bool monitor_off_at_startup () const;
bool monitor_last_used () const;
@@ -134,7 +135,7 @@ public:
bool miles () const;
bool quick_call () const;
bool disable_TX_on_73 () const;
int beacon () const;
int heartbeat () const;
int watchdog () const;
bool TX_messages () const;
bool split_mode () const;
+900 -849
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
# Version number components
set (WSJTX_VERSION_MAJOR 0)
set (WSJTX_VERSION_MINOR 7)
set (WSJTX_VERSION_PATCH 5)
set (WSJTX_VERSION_MINOR 8)
set (WSJTX_VERSION_PATCH 0)
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
+23 -15
View File
@@ -22,8 +22,9 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
, message_ {string_.mid (column_qsoText + padding_).trimmed ()}
, is_standard_ {false}
, frameType_(Varicode::FrameUnknown)
, isBeacon_(false)
, isHeartbeat_(false)
, isAlt_(false)
, bits_{0}
{
if(message_.length() >= 1) {
message_ = message_.left (21).remove (QRegularExpression {"[<>]"});
@@ -51,21 +52,24 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
grid_c_string += QByteArray {6 - grid_c_string.size (), ' '};
is_standard_ = stdmsg_ (message_c_string.constData (), contest_mode_, grid_c_string.constData (), 22, 6);
// We're only going to unpack standard messages for CQs && beacons...
// We're only going to unpack standard messages for CQs && pings...
// TODO: jsherer - this is a hack for now...
if(is_standard_){
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
}
}
bits_ = bits();
tryUnpack();
}
DecodedText::DecodedText (QString const& js8callmessage):
DecodedText::DecodedText (QString const& js8callmessage, int bits):
frameType_(Varicode::FrameUnknown),
message_(js8callmessage),
isBeacon_(false),
isAlt_(false)
isHeartbeat_(false),
isAlt_(false),
bits_(bits)
{
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
@@ -80,7 +84,11 @@ bool DecodedText::tryUnpack(){
bool unpacked = false;
if(!unpacked){
unpacked = tryUnpackBeacon();
unpacked = tryUnpackData();
}
if(!unpacked){
unpacked = tryUnpackHeartbeat();
}
if(!unpacked){
@@ -91,14 +99,10 @@ bool DecodedText::tryUnpack(){
unpacked = tryUnpackDirected();
}
if(!unpacked){
unpacked = tryUnpackData();
}
return unpacked;
}
bool DecodedText::tryUnpackBeacon(){
bool DecodedText::tryUnpackHeartbeat(){
QString m = message().trimmed();
// directed calls will always be 12+ chars and contain no spaces.
@@ -109,17 +113,17 @@ bool DecodedText::tryUnpackBeacon(){
bool isAlt = false;
quint8 type = Varicode::FrameUnknown;
quint8 bits3 = 0;
QStringList parts = Varicode::unpackBeaconMessage(m, &type, &isAlt, &bits3);
QStringList parts = Varicode::unpackHeartbeatMessage(m, &type, &isAlt, &bits3);
if(parts.isEmpty() || parts.length() < 2){
return false;
}
// Beacon Alt Type
// Heartbeat Alt Type
// ---------------
// 1 0 BCN
// 1 1 CQ
isBeacon_ = true;
isHeartbeat_ = true;
isAlt_ = isAlt;
extra_ = parts.length() < 3 ? "" : parts.at(2);
@@ -131,7 +135,7 @@ bool DecodedText::tryUnpackBeacon(){
cmp.append(parts.at(1));
}
compound_ = cmp.join("/");
message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "BEACON").arg(extra_);
message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "HEARTBEAT").arg(extra_);
frameType_ = type;
return true;
}
@@ -212,6 +216,10 @@ bool DecodedText::tryUnpackData(){
return false;
}
if((bits_ & Varicode::JS8CallData) != Varicode::JS8CallData){
return false;
}
quint8 type = Varicode::FrameUnknown;
QString data = Varicode::unpackDataMessage(m, &type);
+5 -4
View File
@@ -31,10 +31,10 @@ class DecodedText
{
public:
explicit DecodedText (QString const& message, bool, QString const& my_grid);
explicit DecodedText (QString const& js8callmessage);
explicit DecodedText (QString const& js8callmessage, int bits);
bool tryUnpack();
bool tryUnpackBeacon();
bool tryUnpackHeartbeat();
bool tryUnpackCompound();
bool tryUnpackDirected();
bool tryUnpackData();
@@ -45,7 +45,7 @@ public:
QString compoundCall() const { return compound_; }
bool isCompound() const { return !compound_.isEmpty(); }
bool isBeacon() const { return isBeacon_; }
bool isHeartbeat() const { return isHeartbeat_; }
bool isAlt() const { return isAlt_; }
QStringList directedMessage() const { return directed_; }
@@ -99,7 +99,7 @@ private:
column_qsoText = 22 };
quint8 frameType_;
bool isBeacon_;
bool isHeartbeat_;
bool isAlt_;
QString compound_;
QString extra_;
@@ -109,6 +109,7 @@ private:
bool contest_mode_;
QString message_;
bool is_standard_;
int bits_;
};
#endif // DECODEDTEXT_H
+1 -1
View File
@@ -256665,7 +256665,7 @@ const Tuple JSC::list[262144] = {
{"ALLCOCK", 7, 255496},
{"ALLCLASSIFIEDS", 14, 205858},
{"ALLCHIN", 7, 102391},
{"ALLCALL", 7, 81},
{"@ALLCALL", 7, 81},
{"ALLBUSINESS", 11, 110217},
{"ALLBRITTON", 10, 248655},
{"ALLBRIGHT", 9, 249916},
+1 -1
View File
@@ -102,7 +102,7 @@ const Tuple JSC::map[262144] = {
{"589", 3, 78},
{"579", 3, 79},
{"569", 3, 80},
{"ALLCALL", 7, 81},
{"@ALLCALL", 7, 81},
{"BEACON", 6, 82},
{"CQCQCQ", 6, 83},
{"CPY?", 4, 84},
+9 -35
View File
@@ -60,25 +60,15 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
if(mod(params%nranera,2).eq.1) ntrials=3*10**(params%nranera/2)
if(params%nranera.eq.0) ntrials=0
nfail=0
10 if (params%nagain) then
open(13,file=trim(temp_dir)//'/decoded.txt',status='unknown', &
position='append',iostat=ios)
else
open(13,file=trim(temp_dir)//'/decoded.txt',status='unknown',iostat=ios)
endif
10 nfail=0
if(params%nmode.eq.8) then
inquire(file=trim(temp_dir)//'/houndcallers.txt',exist=ex)
if(.not.ex) then
c2fox=' '
g2fox=' '
nsnrfox=-99
nfreqfox=-99
n30z=0
nwrap=0
nfox=0
endif
open(19,file=trim(temp_dir)//'/houndcallers.txt',status='unknown')
c2fox=' '
g2fox=' '
nsnrfox=-99
nfreqfox=-99
n30z=0
nwrap=0
nfox=0
endif
if(ios.ne.0) then
@@ -125,12 +115,9 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
else
nDkm=9999
endif
write(19,1004) c2fox(j),g2fox(j),nsnrfox(j),nfreqfox(j),nDkm,m
1004 format(a12,1x,a4,i5,i6,i7,i3)
endif
enddo
nfox=j
flush(19)
endif
go to 800
endif
@@ -255,8 +242,6 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
write(*,1010) nsynced,ndecoded
1010 format('<DecodeFinished>',2i4)
call flush(6)
close(13)
close(19)
if(params%nmode.eq.4 .or. params%nmode.eq.65) close(14)
return
@@ -369,9 +354,6 @@ contains
write(*,1009) params%nutc,snr,dt,freq,csync,decoded,nft
1009 format(i4.4,i4,f5.1,i5,1x,a2,1x,a22,i2)
endif
write(13,1011) params%nutc,nint(sync),snr,dt,float(freq),drift, &
decoded,nft
1011 format(i4.4,i4,i5,f6.2,f8.0,i4,3x,a22,' QRA64',i3)
go to 100
endif
@@ -415,9 +397,6 @@ contains
write(*,1010) params%nutc,snr,dt,freq,csync,decoded,cflags
1010 format(i4.4,i4,f5.1,i5,1x,a2,1x,a22,1x,a3)
endif
write(13,1012) params%nutc,nint(sync),snr,dt,float(freq),drift, &
decoded,ft,nsum,nsmo
1012 format(i4.4,i4,i5,f6.2,f8.0,i4,3x,a22,' JT65',3i3)
100 call flush(6)
@@ -443,8 +422,6 @@ contains
!$omp critical(decode_results)
write(*,1000) params%nutc,snr,dt,nint(freq),decoded
1000 format(i4.4,i4,f5.1,i5,1x,'@ ',1x,a22)
write(13,1002) params%nutc,nint(sync),snr,dt,freq,drift,decoded
1002 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a22,' JT9')
call flush(6)
!$omp end critical(decode_results)
select type(this)
@@ -503,8 +480,6 @@ contains
1000 format(i6.6,i4,f5.1,i5,' ~ ',1x,a22,1x,a2)
if(i0.gt.0) write(*,1001) params%nutc,snr,dt,nint(freq),decoded0
1001 format(i6.6,i4,f5.1,i5,' ~ ',1x,a37)
write(13,1002) params%nutc,nint(sync),snr,dt,freq,0,decoded0
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FT8')
i1=index(decoded0,' ')
i2=i1 + index(decoded0(i1+1:),' ')
@@ -538,8 +513,7 @@ contains
endif
call flush(6)
call flush(13)
select type(this)
type is (counting_ft8_decoder)
this%decoded = this%decoded + 1
-126
View File
@@ -1,126 +0,0 @@
Quick Start for DXpedition Mode
-------------------------------
These notes are intended for operators already familiar with WSJT-X
and FT8 mode. QSOs between the Dxpedition ("Fox") and other stations
("Hounds") are completed with as little as one transmission per Hound,
as in the following examples:
----------------------------------------------------------------------------
Fox (300-600 Hz) Hounds
----------------------------------------------------------------------------
1. CQ KH1DX AJ10
2. KH1DX K1ABC FN42, KH1DX W9XYZ EN37, ...
3. K1ABC KH1DX -13
4. KH1DX K1ABC R-11
5. K1ABC RR73; W9XYZ <KH1DX> -17
6. KH1DX W9XYZ R-16
7. W9XYZ RR73; G4AAA <KH1DX> -09
8. ...
----------------------------------------------------------------------------
Everybody sets dial frequency to an agreed number and uses CAT control
with Split Operation (either *Rig* or *Fake It*). Fox transmits up to
5 signals simultaneously, at audio frequencies 300, 360, ... 540
Hz. Hounds make initial calls (e.g., line 2 above) anywhere in the
range 1000 - 4000 Hz. They send "R+rpt" 350 Hz above the frequency
where Fox called them.
INSTRUCTIONS FOR FOX
--------------------
1. Start WSJT-X in FT8 mode. Select *Fox* on the *Settings ->
Advanced tab*. On the main window, check *Tx even/1st*, *Auto Seq*,
and *Hold Tx Freq*; uncheck *Call 1st*. Set *Tx 300 Hz* and select
Tab 3.
2. In Fox mode the left window (called "Band Activity" in normal FT8
mode) is labeled "Stations calling DXpedition <MyCall>". It will be
filled with a sorted list of calling Hounds. You can sort by Call,
Grid, S/N, Distance, or Random order by using the comboBox at top
right of Tab 3. You can limit the displayed Hound callsigns to those
no stronger than *Max dB*. Fox might use this feature to discourage
Hounds from engaging in a QRO arms race.
3. *N Slots* sets the number of simultaneous Fox signals to be used.
Fox carries out as many as *N Slots* QSOs simultaneously.
4. *Repeats* sets the maximum number of repeat transmissions of the
same message. A QSO is aborted when this number would be exceeded.
5. The *CQ* comboBox on Tab 3 offers a selection of directed CQ
messages. *Reset* clears the QSO queue.
6. The Fox operator's main task is to select Hounds to be called and
worked. The text box on Tab 3 holds the "QSO queue": a list of Hound
calls to be worked. Hit Enter to select the top callsign from the
sorted list of callers (left window), or double-click on any
particular call. Either actiion moves that Hound into the "QSO
queue".
7. The right window displays decodes of signals below 1000 Hz.
Normally these should include only Hound messages containing "R+rpt"
and Fox's own transmissions.
8. To get things started, toggle *Enable Tx* to red. If a Hound call
is available in the QSO queue, that station will be called. If the
QSO queue is empty, Fox calls CQ.
9. If you're using Nslots = 2 or higher, your signal no longer has
a constant envelope. To avoid producing intermod sidebands you need
to ensure linearity in your Tx system. One way to get things about right
is to use the WSJT-X *Tune* button to generate a pure tone. Reduce the
Tx audio level until your power output decreases by 10% or so. Use this
level for your Fox transmissions.
NOTE: If you are generating Nslots signals, the average power in each one
will be 1/Nslots^2 of its normal value for single-signal transmissions.
Nslots Relative dB
-------------------
1 0
2 -6
3 -9.5
4 -12
5 -14
The following features are not yet implemented for Fox:
1. Enforce all required settings
2. Tx message timeout
3. Manual abort of selected QSO
4. All Tx and Rx messages to all.txt
5. Additional sort criteria for Hound calls
6. Selectable timeout for keeping Hounds in the sorted list
7. Display number of active callers
8. Display QSO rate
INSTRUCTIONS FOR HOUND
----------------------
1. Start WSJT-X in FT8 mode. Select *Hound* On the *Settings ->
Advanced* tab. On the main window check *Auto Seq* and uncheck *Tx
even/1st*, *Call 1st*, and *Hold Tx Freq*. Set *Tx nnnn Hz* to some
frequency between 1000 and 4000 Hz, and select *Tab 1*. Enter Fox's
callsign and locator in DX Call and DX Grid, select Tx1, and start
*Monitor*.
2. When you have copied Fox, hit *Enable Tx* to call him. You may
keep calling until he answers. You may wish to move your TxFreq
around, hoping to find a clear calling frequency.
3. When you are called by Fox with a signal report, your next
transmission will automatically be sent as Tx3 ("R+rpt"). When Fox
receives that message he responds with "RR73", and your QSO is
complete!
The following features are not yet implemented for Hound:
1. Force all required settings
2. React properly to directed CQs from Fox
3. Disable Tx2, 4, 5, 6
4. For Tx1, enforce TxFreq >= 1000 Hz
+1 -1
View File
@@ -16,7 +16,7 @@ subroutine chkcrc12a(decoded,nbadcrc)
i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32)
i1Dec8BitBytes(11)=0
icrc12=crc12(c_loc(i1Dec8BitBytes),11) !CRC12 computed from 75 msg bits
! icrc12=xor(icrc12, 41) ! TODO: jsherer - could change the crc here
icrc12=xor(icrc12, 42) ! TODO: jsherer - could change the crc here
nbadcrc=1
if(ncrc12.eq.icrc12) nbadcrc=0
+2 -2
View File
@@ -22,9 +22,9 @@ subroutine extractmessage174(decoded,msgreceived,ncrcflag)
i1Dec8BitBytes(10)=iand(i1Dec8BitBytes(10),128+64+32)
i1Dec8BitBytes(11)=0
icrc12=crc12(c_loc(i1Dec8BitBytes),11) !CRC12 computed from 75 msg bits
! icrc12=xor(icrc12, 41) ! TODO: jsherer - could change the crc here
icrc12=xor(icrc12, 42) ! TODO: jsherer - could change the crc here
if(ncrc12.eq.icrc12 .or. sum(decoded(57:87)).eq.0) then !### Kludge ###
if(ncrc12.eq.icrc12) then ! .or. sum(decoded(57:87)).eq.0) then !### Kludge ###
! CRC12 checks out --- unpack 72-bit message
do ibyte=1,12
itmp=0
+16 -6
View File
@@ -5,15 +5,23 @@ subroutine ft8_downsample(dd,newdat,f0,c1)
parameter (NMAX=15*12000,NSPS=1920)
parameter (NFFT1=192000,NFFT2=3200) !192000/60 = 3200
logical newdat
logical newdat,first
complex c1(0:NFFT2-1)
complex cx(0:NFFT1/2)
real dd(NMAX),x(NFFT1)
real dd(NMAX),x(NFFT1),taper(0:100)
equivalence (x,cx)
save cx
data first/.true./
save cx,first,taper
if(first) then
pi=4.0*atan(1.0)
do i=0,100
taper(i)=0.5*(1.0+cos(i*pi/100))
enddo
first=.false.
endif
if(newdat) then
! Data in dd have changed, recompute the long FFT
! Data in dd have changed, recompute the long FFT
x(1:NMAX)=dd
x(NMAX+1:NFFT1)=0. !Zero-pad the x array
call four2a(cx,NFFT1,1,-1,0) !r2c FFT to freq domain
@@ -23,9 +31,9 @@ subroutine ft8_downsample(dd,newdat,f0,c1)
df=12000.0/NFFT1
baud=12000.0/NSPS
i0=nint(f0/df)
ft=f0+8.0*baud
ft=f0+8.5*baud
it=min(nint(ft/df),NFFT1/2)
fb=f0-1.0*baud
fb=f0-1.5*baud
ib=max(1,nint(fb/df))
k=0
c1=0.
@@ -33,6 +41,8 @@ subroutine ft8_downsample(dd,newdat,f0,c1)
c1(k)=cx(i)
k=k+1
enddo
c1(0:100)=c1(0:100)*taper(100:0:-1)
c1(k-1-100:k-1)=c1(k-1-100:k-1)*taper
c1=cshift(c1,i0-ib)
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
fac=1.0/sqrt(float(NFFT1)*NFFT2)
+4 -3
View File
@@ -28,7 +28,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
integer nappasses(0:5) !Number of decoding passes to use for each QSO state
integer naptypes(0:5,4) ! (nQSOProgress, decoding pass) maximum of 4 passes for now
integer*1, target:: i1hiscall(12)
complex cd0(3200)
complex cd0(0:3199)
complex ctwk(32)
complex csymb(32)
logical first,newdat,lsubtract,lapon,lapcqonly,nagain
@@ -126,7 +126,8 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
do k=1,NN
i1=ibest+(k-1)*32
csymb=cmplx(0.0,0.0)
if( i1.ge.1 .and. i1+31 .le. NP2 ) csymb=cd0(i1:i1+31)
!if( i1.ge.1 .and. i1+31 .le. NP2 ) csymb=cd0(i1:i1+31)
if( i1.ge.0 .and. i1+31 .le. NP2-1 ) csymb=cd0(i1:i1+31)
call four2a(csymb,32,1,-1,1)
s2(0:7,k)=abs(csymb(1:8))/1e3
enddo
@@ -181,7 +182,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
bmetap(i2)=r2
bmetap(i1)=r1
! Max log metric
psl=log(ps)
psl=log(ps+1e-32)
r1=max(psl(1),psl(3),psl(5),psl(7))-max(psl(0),psl(2),psl(4),psl(6))
r2=max(psl(2),psl(3),psl(6),psl(7))-max(psl(0),psl(1),psl(4),psl(5))
r4=max(psl(4),psl(5),psl(6),psl(7))-max(psl(0),psl(1),psl(2),psl(3))
+1 -1
View File
@@ -38,7 +38,7 @@ subroutine genft8(msg,mygrid,bcontest,i3bit,msgsent,msgbits,itone)
i1Msg8BitBytes(10)=iand(i1Msg8BitBytes(10),128+64+32)
i1Msg8BitBytes(11)=0
icrc12=crc12(c_loc(i1Msg8BitBytes),11)
! icrc12=xor(icrc12, 41) ! TODO: jsherer - could change the crc here
icrc12=xor(icrc12, 42) ! TODO: jsherer - could change the crc here
! For reference, here's how to check the CRC
! i1Msg8BitBytes(10)=icrc12/256
+1 -1
View File
@@ -91,7 +91,7 @@ allocate ( rxdata(N), llr(N) )
i1Msg8BitBytes(10:11)=0
checksum = crc12 (c_loc (i1Msg8BitBytes), 11)
! checksum = xor(checksum, 41) ! TODO: jsherer - could change the crc here
checksum = xor(checksum, 42) ! TODO: jsherer - could change the crc here
! For reference, the next 3 lines show how to check the CRC
i1Msg8BitBytes(10)=checksum/256
i1Msg8BitBytes(11)=iand (checksum,255)
+12 -19
View File
@@ -37,11 +37,6 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,s,candidate,ncand,sbase)
savg=savg + s(1:NH1,j) !Average spectrum
enddo
call baseline(savg,nfa,nfb,sbase)
! savg=savg/NHSYM
! do i=1,NH1
! write(51,3051) i*df,savg(i),db(savg(i))
!3051 format(f10.3,e12.3,f12.3)
! enddo
ia=max(1,nint(nfa/df))
ib=nint(nfb/df)
@@ -49,6 +44,9 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,s,candidate,ncand,sbase)
nfos=NFFT1/NSPS ! # frequency bin oversampling factor
jstrt=0.5/tstep
candidate0=0.
k=0
do i=ia,ib
do j=-JZ,+JZ
ta=0.
@@ -95,20 +93,18 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,s,candidate,ncand,sbase)
iz=ib-ia+1
call indexx(red(ia:ib),iz,indx)
ibase=indx(nint(0.40*iz)) - 1 + ia
if(ibase.lt.1) ibase=1
if(ibase.gt.nh1) ibase=nh1
base=red(ibase)
red=red/base
candidate0=0.
k=0
do i=1,200
ji=iz+1-1
if(ji.eq.0) exit
n=ia + indx(ji) - 1
if(red(n).lt.syncmin) exit
if(k.lt.200) k=k+1
candidate0(1,k)=n*df
candidate0(2,k)=(jpeak(n)-1)*tstep
candidate0(3,k)=red(n)
do i=1,min(200,iz)
n=ia + indx(iz+1-i) - 1
if(red(n).lt.syncmin.or.isnan(red(n)).or.k.eq.200) exit
k=k+1
candidate0(1,k)=n*df
candidate0(2,k)=(jpeak(n)-1)*tstep
candidate0(3,k)=red(n)
enddo
ncand=k
@@ -123,9 +119,6 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,s,candidate,ncand,sbase)
if(candidate0(3,i).lt.candidate0(3,j)) candidate0(3,i)=0.
endif
enddo
! write(*,3001) i,candidate0(1,i-1),candidate0(1,i),candidate0(3,i-1), &
! candidate0(3,i)
!3001 format(i2,4f8.1)
endif
enddo
+2
View File
@@ -138,10 +138,12 @@ bool ADIF::match(QString const& call, QString const& band, QString const& mode)
(
((mode.compare("JT65",Qt::CaseInsensitive)==0) ||
(mode.compare("JT9",Qt::CaseInsensitive)==0) ||
(mode.compare("JS8",Qt::CaseInsensitive)==0) ||
(mode.compare("FT8",Qt::CaseInsensitive)==0))
&&
((q.mode.compare("JT65",Qt::CaseInsensitive)==0) ||
(q.mode.compare("JT9",Qt::CaseInsensitive)==0) ||
(q.mode.compare("JS8",Qt::CaseInsensitive)==0) ||
(q.mode.compare("FT8",Qt::CaseInsensitive)==0))
)
|| (mode.compare(q.mode,Qt::CaseInsensitive)==0)
+5 -18
View File
@@ -33,19 +33,6 @@ void LogBook::init()
_log.load();
_setAlreadyWorkedFromLog();
/*
int QSOcount = _log.getCount();
int count = _worked.getWorkedCount();
qDebug() << QSOcount << "QSOs and" << count << "countries worked in file" << logFilename;
*/
// QString call = "ok1ct";
// QString countryName;
// bool callWorkedBefore,countryWorkedBefore;
// match(/*in*/call, /*out*/ countryName,callWorkedBefore,countryWorkedBefore);
// qDebug() << countryName;
}
@@ -59,11 +46,14 @@ void LogBook::_setAlreadyWorkedFromLog()
if (countryName.length() > 0)
{
_worked.setAsWorked(countryName);
//qDebug() << countryName << " worked " << c;
}
}
}
bool LogBook::hasWorkedBefore(const QString &call, const QString &band, const QString &mode){
return _log.match(call, band, mode);
}
void LogBook::match(/*in*/const QString call,
/*out*/ QString &countryName,
bool &callWorkedBefore,
@@ -71,11 +61,10 @@ void LogBook::match(/*in*/const QString call,
{
if (call.length() > 0)
{
QString currentMode = "JT9"; // JT65 == JT9 in ADIF::match()
QString currentMode = ""; // match any mode
QString currentBand = ""; // match any band
callWorkedBefore = _log.match(call,currentBand,currentMode);
countryName = _countries.find(call);
// qDebug() << "B" << countryName;
if (countryName.length() > 0) // country was found
countryWorkedBefore = _worked.getHasWorked(countryName);
@@ -85,12 +74,10 @@ void LogBook::match(/*in*/const QString call,
countryWorkedBefore = false;
}
}
//qDebug() << "Logbook:" << call << ":" << countryName << "Cty B4:" << countryWorkedBefore << "call B4:" << callWorkedBefore;
}
void LogBook::addAsWorked(const QString call, const QString band, const QString mode, const QString date)
{
//qDebug() << "adding " << call << " as worked";
_log.add(call,band,mode,date);
QString countryName = _countries.find(call);
if (countryName.length() > 0)
+1
View File
@@ -20,6 +20,7 @@ class LogBook
{
public:
void init();
bool hasWorkedBefore(const QString &call, const QString &band, const QString &mode);
void match(/*in*/ const QString call,
/*out*/ QString &countryName,
bool &callWorkedBefore,
+693 -533
View File
File diff suppressed because it is too large Load Diff
+35 -20
View File
@@ -136,7 +136,7 @@ public slots:
void cacheActivity(QString key);
void restoreActivity(QString key);
void clearActivity();
void createAllcallTableRow(QTableWidget *table, bool selected);
void createAllcallTableRows(QTableWidget *table, const QString &selectedCall);
void displayTextForFreq(QString text, int freq, QDateTime date, bool isTx, bool isNewLine, bool isLast);
void writeNoticeTextToUI(QDateTime date, QString text);
int writeMessageTextToUI(QDateTime date, QString text, int freq, bool bold, int block=-1);
@@ -144,17 +144,18 @@ public slots:
void prependMessageText(QString text);
void addMessageText(QString text, bool clear=false, bool selectFirstPlaceholder=false);
void enqueueMessage(int priority, QString message, int freq, Callback c);
void enqueueBeacon(QString message);
void enqueueHeartbeat(QString message);
void resetMessage();
void resetMessageUI();
void restoreMessage();
void initializeDummyData();
bool ensureCallsignSet(bool alert=true);
bool ensureSelcalCallsignSelected(bool alert=true);
bool ensureKeyNotStuck(QString const& text);
void createMessage(QString const& text);
void createMessageTransmitQueue(QString const& text);
void resetMessageTransmitQueue();
QString popMessageFrame();
QPair<QString, int> popMessageFrame();
protected:
void keyPressEvent (QKeyEvent *) override;
void closeEvent(QCloseEvent *) override;
@@ -209,8 +210,6 @@ private slots:
void decode();
void decodeBusy(bool b);
void on_EraseButton_clicked();
void band_activity_cleared ();
void rx_frequency_activity_cleared ();
void set_dateTimeQSO(int m_ntx);
void set_ntx(int n);
void on_txrb1_toggled(bool status);
@@ -246,6 +245,7 @@ private slots:
void on_actionSave_decoded_triggered();
void on_actionQuickDecode_toggled (bool);
void on_actionMediumDecode_toggled (bool);
void on_actionDeepDecode_toggled (bool);
void on_actionDeepestDecode_toggled (bool);
void bumpFqso(int n);
void on_actionErase_ALL_TXT_triggered();
@@ -267,6 +267,7 @@ private slots:
void on_clearAction_triggered(QObject * sender);
void on_cqMacroButton_clicked();
void on_replyMacroButton_clicked();
void on_snrMacroButton_clicked();
void on_qthMacroButton_clicked();
void on_qtcMacroButton_clicked();
void setShowColumn(QString tableKey, QString columnKey, bool value);
@@ -278,6 +279,8 @@ private slots:
void buildBandActivitySortByMenu(QMenu * menu);
void buildCallActivitySortByMenu(QMenu * menu);
void buildQueryMenu(QMenu *, QString callsign);
QMap<QString, QString> buildMacroValues();
QString replaceMacros(QString const &text, QMap<QString, QString> values, bool prune);
void buildSavedMessagesMenu(QMenu *menu);
void buildRelayMenu(QMenu *menu);
QAction* buildRelayAction(QString call);
@@ -295,14 +298,15 @@ private slots:
void on_nextFreeTextMsg_currentTextChanged (QString const&);
void on_extFreeTextMsgEdit_currentTextChanged (QString const&);
int currentFreqOffset();
QStringList buildMessageFrames(QString const& text);
QList<QPair<QString, int>> buildMessageFrames(QString const& text);
bool prepareNextMessageFrame();
bool isFreqOffsetFree(int f, int bw);
int findFreeFreqOffset(int fmin, int fmax, int bw);
void scheduleBeacon(bool first=false);
void pauseBeacon();
void checkBeacon();
void prepareBeacon();
void scheduleHeartbeat(bool first=false);
void pauseHeartbeat();
void unpauseHeartbeat();
void checkHeartbeat();
void prepareHeartbeat();
QString calculateDistance(QString const& grid, int *pDistance=nullptr);
void on_driftSpinBox_valueChanged(int n);
void on_driftSyncButton_clicked();
@@ -314,7 +318,7 @@ private slots:
void on_tuneButton_clicked (bool);
void on_pbR2T_clicked();
void on_pbT2R_clicked();
void on_beaconButton_clicked();
void on_heartbeatButton_clicked();
void on_selcalButton_clicked();
void acceptQSO (QDateTime const&, QString const& call, QString const& grid
, Frequency dial_freq, QString const& mode
@@ -411,7 +415,7 @@ private:
private:
void astroUpdate ();
void writeAllTxt(QString message);
void writeAllTxt(QString message, int bits);
void hideMenus(bool b);
NetworkAccessManager m_network_manager;
@@ -549,6 +553,7 @@ private:
bool m_sentFirst73;
int m_currentMessageType;
QString m_currentMessage;
int m_currentMessageBits;
int m_lastMessageType;
QString m_lastMessageSent;
bool m_bShMsgs;
@@ -643,7 +648,7 @@ private:
QTimer minuteTimer;
QTimer splashTimer;
QTimer p1Timer;
QTimer beaconTimer;
QTimer heartbeatTimer;
QString m_path;
QString m_baseCall;
@@ -684,6 +689,7 @@ private:
QDateTime utcTimestamp;
int snr;
int bits;
float tdrift;
};
struct CommandDetail
@@ -700,6 +706,7 @@ private:
QString grid;
QString text;
QString extra;
float tdrift;
};
struct ActivityDetail
@@ -715,6 +722,7 @@ private:
QDateTime utcTimestamp;
int snr;
bool shouldDisplay;
float tdrift;
};
struct MessageBuffer {
@@ -734,7 +742,8 @@ private:
QString m_txTextDirtyLastSelectedCall;
QString m_lastTxMessage;
QDateTime m_lastTxTime;
int m_timeDeltaMsMMA;
int m_timeDeltaMsMMA_N;
enum Priority {
PriorityLow = 10,
@@ -765,7 +774,7 @@ private:
QMap<QString, QVariant> m_showColumnsCache; // table column:key -> show boolean
QMap<QString, QVariant> m_sortCache; // table key -> sort by
QPriorityQueue<PrioritizedMessage> m_txMessageQueue; // messages to be sent
QQueue<QString> m_txFrameQueue; // frames to be sent
QQueue<QPair<QString, int>> m_txFrameQueue; // frames to be sent
QQueue<ActivityDetail> m_rxActivityQueue; // all rx activity queue
QQueue<CommandDetail> m_rxCommandQueue; // command queue for processing commands
QQueue<CallDetail> m_rxCallQueue; // call detail queue for spots to pskreporter
@@ -778,14 +787,14 @@ private:
QMap<int, QList<ActivityDetail>> m_bandActivity; // freq -> [(text, last timestamp), ...]
QMap<int, MessageBuffer> m_messageBuffer; // freq -> (cmd, [frames, ...])
QMap<QString, CallDetail> m_callActivity; // call -> (last freq, last timestamp)
QQueue<QString> m_txBeaconQueue; // beacon frames to be sent
QQueue<QString> m_txHeartbeatQueue; // ping frames to be sent
QMap<QString, QDateTime> m_aprsCallCache;
QMap<QString, QMap<QString, CallDetail>> m_callActivityCache; // band -> call activity
QMap<QString, QMap<int, QList<ActivityDetail>>> m_bandActivityCache; // band -> band activity
QMap<QString, QString> m_rxTextCache; // band -> rx text
QSet<QString> m_callSeenBeacon; // call
QSet<QString> m_callSeenHeartbeat; // call
int m_previousFreq;
bool m_shouldRestoreFreq;
bool m_bandHopped;
@@ -809,9 +818,9 @@ private:
QQueue<QString> m_foxQSOinProgress; //QSOs in progress: Fox has sent a report
QQueue<qint64> m_foxRateQueue;
bool m_nextBeaconQueued = false;
bool m_nextBeaconPaused = false;
QDateTime m_nextBeacon;
bool m_nextHeartbeatQueued = false;
bool m_nextHeartPaused = false;
QDateTime m_nextHeartbeat;
QDateTime m_dateTimeQSOOn;
QDateTime m_dateTimeLastTX;
@@ -828,6 +837,7 @@ private:
bool m_tx_when_ready;
bool m_transmitting;
bool m_tune;
bool m_deadAirTone;
bool m_tx_watchdog; // true when watchdog triggered
bool m_block_pwr_tooltip;
bool m_PwrBandSetOK;
@@ -890,6 +900,8 @@ private:
void markOffsetDirected(int offset, bool isAllCall);
void clearOffsetDirected(int offset);
void processActivity(bool force=false);
void observeTimeDeltaForAverage(float delta);
void resetTimeDeltaAverage();
void processRxActivity();
void processCompoundActivity();
void processBufferedActivity();
@@ -932,6 +944,9 @@ private:
void add_child_to_event_filter (QObject *);
void remove_child_from_event_filter (QObject *);
void setup_status_bar (bool vhf);
void resetIdleTimer();
void incrementIdleTimer();
void tx_watchdog (bool triggered);
qint64 nWidgets(QString t);
void displayWidgets(qint64 n);
+124 -28
View File
@@ -406,7 +406,7 @@ color : white;
</widget>
</item>
<item>
<widget class="QLabel" name="labBeacon">
<widget class="QLabel" name="labHeartbeat">
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
@@ -415,7 +415,7 @@ color : white;
}</string>
</property>
<property name="text">
<string>Next Beacon: disabled</string>
<string>Next Heartbeat: disabled</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -988,7 +988,7 @@ background-color: #00ff00;
</widget>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="beaconButton">
<widget class="QPushButton" name="heartbeatButton">
<property name="enabled">
<bool>true</bool>
</property>
@@ -1011,7 +1011,7 @@ background-color: #00ff00;
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable the automatic beacon&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable the automatic heartbeat transmission&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
@@ -1043,7 +1043,7 @@ background-color: #6699ff;
}</string>
</property>
<property name="text">
<string>BEACON</string>
<string>HB</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -1248,6 +1248,9 @@ background-color: #00ff00;
<attribute name="horizontalHeaderDefaultSectionSize">
<number>30</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
@@ -1274,7 +1277,12 @@ background-color: #00ff00;
</column>
<column>
<property name="text">
<string>Text</string>
<string>Rx TDrift</string>
</property>
</column>
<column>
<property name="text">
<string>Message(s)</string>
</property>
</column>
</widget>
@@ -1395,6 +1403,9 @@ QTextEdit[transmitting=&quot;true&quot;] {
<attribute name="horizontalHeaderDefaultSectionSize">
<number>30</number>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
@@ -1411,7 +1422,10 @@ QTextEdit[transmitting=&quot;true&quot;] {
</column>
<column>
<property name="text">
<string>Offset</string>
<string/>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</column>
<column>
@@ -1424,6 +1438,16 @@ QTextEdit[transmitting=&quot;true&quot;] {
<string>SNR</string>
</property>
</column>
<column>
<property name="text">
<string>Offset</string>
</property>
</column>
<column>
<property name="text">
<string>Rx TDrift</string>
</property>
</column>
<column>
<property name="text">
<string>Grid</string>
@@ -1500,6 +1524,22 @@ QTextEdit[transmitting=&quot;true&quot;] {
</property>
</spacer>
</item>
<item row="1" column="13">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="11">
<widget class="QPushButton" name="queryButton">
<property name="minimumSize">
@@ -1647,6 +1687,22 @@ color:#555;
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="snrMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Send an SNR message&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>SNR</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_5">
@@ -2000,10 +2056,23 @@ background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2ecc71, stop:1 #00FF
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="driftAvgLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Avg RX TDrift: 0 ms</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="driftSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -2052,7 +2121,7 @@ background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2ecc71, stop:1 #00FF
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Sync Time Drift to TX Start</string>
<string>Sync Time Drift to Now (TX Start)</string>
</property>
</widget>
</item>
@@ -2068,7 +2137,7 @@ background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2ecc71, stop:1 #00FF
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Sync Time Drift to TX End</string>
<string>Sync Time Drift to Now (TX End)</string>
</property>
</widget>
</item>
@@ -4656,10 +4725,11 @@ list. The list can be maintained in Settings (F2).</string>
<bool>false</bool>
</property>
<property name="title">
<string>Decode</string>
<string>&amp;Decode</string>
</property>
<addaction name="actionQuickDecode"/>
<addaction name="actionMediumDecode"/>
<addaction name="actionDeepDecode"/>
<addaction name="actionDeepestDecode"/>
<addaction name="separator"/>
<addaction name="actionInclude_averaging"/>
@@ -4863,10 +4933,10 @@ list. The list can be maintained in Settings (F2).</string>
<bool>false</bool>
</property>
<property name="text">
<string>Fast</string>
<string>Fast (1x)</string>
</property>
<property name="visible">
<bool>false</bool>
<bool>true</bool>
</property>
</action>
<action name="actionNone">
@@ -4903,9 +4973,6 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionKeyboard_shortcuts">
<property name="visible">
<bool>false</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
@@ -4915,11 +4982,11 @@ list. The list can be maintained in Settings (F2).</string>
<property name="shortcut">
<string>F3</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionSpecial_mouse_commands">
<property name="visible">
<bool>false</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
@@ -4929,6 +4996,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="shortcut">
<string>F5</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionJT9">
<property name="checkable">
@@ -4960,7 +5030,7 @@ list. The list can be maintained in Settings (F2).</string>
<bool>false</bool>
</property>
<property name="text">
<string>Normal</string>
<string>Normal (2x)</string>
</property>
</action>
<action name="actionDeepestDecode">
@@ -4968,10 +5038,10 @@ list. The list can be maintained in Settings (F2).</string>
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
<bool>false</bool>
</property>
<property name="text">
<string>Deep</string>
<string>Deepest (4x)</string>
</property>
</action>
<action name="actionMonitor_OFF_at_startup">
@@ -5189,6 +5259,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>Enable averaging</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionInclude_correlation">
<property name="checkable">
@@ -5197,6 +5270,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>Enable deep search</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionWSPR">
<property name="checkable">
@@ -5242,9 +5318,6 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="download_samples_action">
<property name="visible">
<bool>false</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
@@ -5254,6 +5327,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="whatsThis">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Download sample audio files demonstrating the various modes.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionMSK144">
<property name="checkable">
@@ -5272,15 +5348,15 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionRelease_Notes">
<property name="visible">
<bool>false</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Release Notes</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionEnable_AP_DXcall">
<property name="checkable">
@@ -5289,6 +5365,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>Enable AP for DX Call</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionFreqCal">
<property name="checkable">
@@ -5367,6 +5446,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>Enable AP</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionEnable_AP_JT65">
<property name="checkable">
@@ -5375,6 +5457,9 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>Enable AP</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</action>
<action name="actionSolve_FreqCal">
<property name="text">
@@ -5515,6 +5600,17 @@ list. The list can be maintained in Settings (F2).</string>
<string>Open Save Directory...</string>
</property>
</action>
<action name="actionDeepDecode">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Deep (3x)</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
+1 -1
View File
@@ -578,7 +578,7 @@ void CPlotter::DrawOverlay() //DrawOverlay()
x1=XfromFreq(500);
x2=XfromFreq(1000);
if(x1<=m_w and x2>0) {
painter0.setPen(penBlue); //Mark beacon range
painter0.setPen(penBlue); //Mark ping range
painter0.drawLine(x1+1,26,x2-2,26);
painter0.drawLine(x1+1,28,x2-2,28);
}
+4 -2
View File
@@ -33,6 +33,8 @@ QString version (bool include_patch)
QString program_title (QString const& revision)
{
QString id {"JS8Call de KN4CRD (v%1)"};
return id.arg(QCoreApplication::applicationVersion ());
QString id {"%1 DE KN4CRD (v%2)"};
id = id.arg(QCoreApplication::applicationName());
id = id.arg(QCoreApplication::applicationVersion ());
return id;
}
+343 -179
View File
@@ -28,36 +28,51 @@
#include "varicode.h"
#include "jsc.h"
#include <cmath>
const int nalphabet = 41;
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"}; // alphabet to encode _into_ for FT8 freetext transmission
QString alphabet72 = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+/?."};
QString grid_pattern = {R"((?<grid>[A-X]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*)+)"};
QString orig_compound_callsign_pattern = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){2,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"};
QString compound_callsign_pattern = {R"((?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
QString base_callsign_pattern = {R"((?<callsign>\b(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)\b))"};
//QString compound_callsign_pattern = {R"((?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
QString compound_callsign_pattern = {R"((?<callsign>(?:[@]?|\b)(?<extended>[A-Z0-9\/@][A-Z0-9\/]{0,2}[\/]?[A-Z0-9\/]{0,3}[\/]?[A-Z0-9\/]{0,3})\b))"};
QString pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
QString alphanumeric = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "}; // callsign and grid alphabet
QString alphanumeric = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ /@"}; // callsign and grid alphabet
QMap<QString, int> directed_cmds = {
// any changes here need to be made also in the directed regular xpression for parsing
// ?*^&@
{" SNR?", 0 }, // query snr
{"?", 0 }, // compat
{" QTH?", 1 }, // query qth
{"@", 1 }, // compat
{" QTC?", 2 }, // query station message
{"&", 2 }, // compat
{" GRID?", 4 }, // query grid
{"^", 4 }, // compat
{" STATUS?", 6 }, // query idle message
{"*", 6 }, // compat
{"?", 0 }, // query snr
{"@", 1 }, // query qth
{"&", 2 }, // query station message
{"$", 3 }, // query station(s) heard
{"^", 4 }, // query grid
{">", 5 }, // relay message
{"*", 6 }, // query idle message
//{"!", 7 }, // alert message
{"#", 8 }, // all or nothing message
// {"=", 9 }, // unused
//{"!", 7 }, // alert message
//{"$", 3 }, // query station(s) heard
//{"=", 9 }, // unused
{" ACTIVE", 10 }, // i have been active in the past 10 minutes
{" IDLE", 11 }, // i have not been active in the past 10 minutes
{" BEACON", -1 }, // this is my beacon (unused except for faux processing of beacons as directed commands)
{" BEACON ACK", 12 }, // i received your beacon at this snr
{" BEACON REQ", 13 }, // can you transmit a beacon to callsign?
{" HEARTBEAT", -1 }, // this is my ping (unused except for faux processing of pings as directed commands)
{" HEARTBEAT ACK", 12 }, // i acknowledge your ping at this SNR
{" HEARTBEAT REQ", 13 }, // can you transmit a ping to callsign?
{" APRS:", 14 }, // send an aprs packet
@@ -67,7 +82,7 @@ QMap<QString, int> directed_cmds = {
{" FB", 18 }, // fine business
{" HW CPY?", 19 }, // how do you copy?
{" HEARING", 20 }, // i am hearing the following stations
{" SK", 20 }, // end of contact
{" RR", 21 }, // roger roger
{" QSL?", 22 }, // do you copy?
{" QSL", 23 }, // i copy
@@ -89,24 +104,24 @@ QSet<int> snr_cmds = {12, 25};
QMap<int, int> checksum_cmds = {
{ 5, 16 },
{ 8, 32 },
{ 8, 16 },
{ 13, 16 },
{ 14, 16 },
{ 15, 0 }
};
QString callsign_pattern = QString("(?<callsign>[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:BEACON (ACK|REQ)|AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|QRZ[?]|(?:(?:ACK|73|YES|NO|SNR|QSL|RR|HEARING|FB|QTH|QTC|GRID|ACTIVE|IDLE)(?=[ ]|$))|[?@&$%#^>* ]))?");
QString callsign_pattern = QString("(?<callsign>[@]?[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:HEARTBEAT (ACK|REQ)|AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|QRZ[?]|SNR[?]|QTC[?]|QTH[?]|GRID[?]|STATUS[?]|(?:(?:ACK|73|YES|NO|SNR|QSL|RR|SK|FB|QTH|QTC|GRID|ACTIVE|IDLE)(?=[ ]|$))|[?*^&@#> ]))?");
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_num_pattern = QString("(?<num>(?<=SNR|BEACON ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
QString optional_num_pattern = QString("(?<num>(?<=SNR|HEARTBEAT ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
QRegularExpression directed_re("^" +
callsign_pattern +
optional_cmd_pattern +
optional_num_pattern);
QRegularExpression beacon_re(R"(^\s*(?<type>CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|BEACON)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)");
QRegularExpression heartbeat_re(R"(^\s*(?<type>CQCQCQ|CQ QRPP?|CQ DX|CQ TEST|CQ( CQ){0,2}|HEARTBEAT)(?:\s(?<grid>[A-R]{2}[0-9]{2}))?\b)");
QRegularExpression compound_re("^\\s*[`]" +
callsign_pattern +
@@ -174,8 +189,7 @@ quint16 nmaxgrid = (1<<15)-1;
QMap<QString, quint32> basecalls = {
{ "<....>", nbasecall + 1 }, // incomplete callsign
{ "ALLCALL", nbasecall + 2 },
{ "GROUPCALL", nbasecall + 3 },
{ "@ALLCALL", nbasecall + 2 }, // ALLCALL group
};
QMap<quint32, QString> cqs = {
@@ -334,6 +348,9 @@ QStringList Varicode::parseCallsigns(QString const &input){
continue;
}
QString callsign = match.captured("callsign").trimmed();
if(!Varicode::isValidCallsign(callsign, nullptr)){
continue;
}
QRegularExpression m(grid_pattern);
if(m.match(callsign).hasMatch()){
continue;
@@ -621,14 +638,14 @@ QString Varicode::pack72bits(quint64 value, quint8 rem){
// 21 bits for the data + 1 bit for a flag indicator
// giving us a total of 5.5 bits per character
quint32 Varicode::packAlphaNumeric22(QString const& value, bool isFlag){
QString word = QString(value).replace(QRegExp("[^A-Z0-9]"), "");
QString word = QString(value).replace(QRegExp("[^A-Z0-9/ ]"), "");
if(word.length() < 4){
word = word + QString(" ").repeated(4-word.length());
}
quint32 a = 37 * 37 * 37 * alphanumeric.indexOf(word.at(0));
quint32 b = 37 * 37 * alphanumeric.indexOf(word.at(1));
quint32 c = 37 * alphanumeric.indexOf(word.at(2));
quint32 a = 38 * 38 * 38 * alphanumeric.indexOf(word.at(0));
quint32 b = 38 * 38 * alphanumeric.indexOf(word.at(1));
quint32 c = 38 * alphanumeric.indexOf(word.at(2));
quint32 d = alphanumeric.indexOf(word.at(3));
quint32 packed = a + b + c + d;
@@ -643,25 +660,116 @@ QString Varicode::unpackAlphaNumeric22(quint32 packed, bool *isFlag){
if(isFlag) *isFlag = packed & 1;
packed = packed >> 1;
quint32 tmp = packed % 37;
quint32 tmp = packed % 38;
word[3] = alphanumeric.at(tmp);
packed = packed / 37;
packed = packed / 38;
tmp = packed % 37;
tmp = packed % 38;
word[2] = alphanumeric.at(tmp);
packed = packed / 37;
packed = packed / 38;
tmp = packed % 37;
tmp = packed % 38;
word[1] = alphanumeric.at(tmp);
packed = packed / 37;
packed = packed / 38;
tmp = packed % 37;
tmp = packed % 38;
word[0] = alphanumeric.at(tmp);
packed = packed / 37;
packed = packed / 38;
return QString(word, 4);
}
// pack a 10-digit alpha-numeric + space + forward-slash into a 50 bit value
// optionally start with an @
//
// [39][38][38][02][38][38][38][02][38][38][38]
// [K] [N] [4] [ ] [C] [R] [D] [/] [Q] [R] [P]
// [V] [E] [3] [/] [L] [B] [9] [ ] [Y] [H] [X]
// [@] [R] [A] [ ] [C] [E] [S] [ ] [ ] [ ] [ ]
//
// giving us a total of 4.5-5.55 bits per character
quint64 Varicode::packAlphaNumeric50(QString const& value){
QString word = QString(value).replace(QRegExp("[^A-Z0-9 /@]"), "");
if(word.length() > 3 && word.at(3) != '/'){
word.insert(3, ' ');
}
if(word.length() > 7 && word.at(7) != '/'){
word.insert(7, ' ');
}
if(word.length() < 11){
word = word + QString(" ").repeated(11-word.length());
}
quint64 a = (quint64)38 * 38 * 38 * 2 * 38 * 38 * 38 * 2 * 38 * 38 * alphanumeric.indexOf(word.at(0));
quint64 b = (quint64)38 * 38 * 38 * 2 * 38 * 38 * 38 * 2 * 38 * alphanumeric.indexOf(word.at(1));
quint64 c = (quint64)38 * 38 * 38 * 2 * 38 * 38 * 38 * 2 * alphanumeric.indexOf(word.at(2));
quint64 d = (quint64)38 * 38 * 38 * 2 * 38 * 38 * 38 * (int)(word.at(3) == '/');
quint64 e = (quint64)38 * 38 * 38 * 2 * 38 * 38 * alphanumeric.indexOf(word.at(4));
quint64 f = (quint64)38 * 38 * 38 * 2 * 38 * alphanumeric.indexOf(word.at(5));
quint64 g = (quint64)38 * 38 * 38 * 2 * alphanumeric.indexOf(word.at(6));
quint64 h = (quint64)38 * 38 * 38 * (int)(word.at(7) == '/');
quint64 i = (quint64)38 * 38 * alphanumeric.indexOf(word.at(8));
quint64 j = (quint64)38 * alphanumeric.indexOf(word.at(9));
quint64 k = (quint64)alphanumeric.indexOf(word.at(10));
quint64 packed = a + b + c + d + e + f + g + h + i + j + k;
return packed;
}
QString Varicode::unpackAlphaNumeric50(quint64 packed){
QChar word[11];
quint64 tmp = packed % 38;
word[10] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 38;
word[9] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 38;
word[8] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 2;
word[7] = tmp ? '/' : ' ';
packed = packed / 2;
tmp = packed % 38;
word[6] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 38;
word[5] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 38;
word[4] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 2;
word[3] = tmp ? '/' : ' ';
packed = packed / 2;
tmp = packed % 38;
word[2] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 38;
word[1] = alphanumeric.at(tmp);
packed = packed / 38;
tmp = packed % 39;
word[0] = alphanumeric.at(tmp);
packed = packed / 39;
auto value = QString(word, 11);
return value.replace(" ", "");
}
// pack a callsign into a 28-bit value
quint32 Varicode::packCallsign(QString const& value){
quint32 packed = 0;
@@ -882,8 +990,8 @@ quint8 Varicode::packCmd(quint8 cmd, quint8 num, bool *pPackedNum){
// 8 bits - 1 bit flag + 1 bit type + 6 bit number
// [1][X][6]
// X = 0 == SNR
// X = 1 == BEACON ACK
value = ((1 << 1) | (int)(cmdStr == " BEACON ACK")) << 6;
// X = 1 == ACK
value = ((1 << 1) | (int)(cmdStr == " HEARTBEAT ACK")) << 6;
value = value + (num & ((1<<6)-1));
if(pPackedNum) *pPackedNum = true;
} else {
@@ -901,7 +1009,7 @@ quint8 Varicode::unpackCmd(quint8 value, quint8 *pNum){
auto cmd = directed_cmds[" SNR"];
if(value & (1<<6)){
cmd = directed_cmds[" BEACON ACK"];
cmd = directed_cmds[" HEARTBEAT ACK"];
}
return cmd;
} else {
@@ -930,14 +1038,91 @@ int Varicode::isCommandChecksumed(const QString &cmd){
return checksum_cmds[directed_cmds[cmd]];
}
bool isValidCompoundCallsign(QStringRef callsign){
// compound calls cannot be > 9 characters after removing the /
if(callsign.toString().replace("/", "").length() > 9){
return false;
}
// compound is valid when it is:
// 1) a group call (starting with @)
// 2) an actual compound call (containing /) that is not a base call
// 3) is greater than two characters and has an alphanumeric character sequence
//
// this is so arbitrary words < 10 characters in length don't end up coded as callsigns
if(callsign.contains("/")){
auto base = callsign.toString().left(callsign.indexOf("/"));
return !basecalls.contains(base);
}
if(callsign.startsWith("@")){
return true;
}
if(callsign.length() > 2 && QRegularExpression("[0-9][A-Z]|[A-Z][0-9]").match(callsign).hasMatch()){
return true;
}
return false;
}
bool Varicode::isValidCallsign(const QString &callsign, bool *pIsCompound){
if(basecalls.contains(callsign)){
if(pIsCompound) *pIsCompound = false;
return true;
}
auto match = QRegularExpression(base_callsign_pattern).match(callsign);
if(match.hasMatch() && (match.capturedLength() == callsign.length())){
if(pIsCompound) *pIsCompound = false;
return callsign.length() > 2 && QRegularExpression("[0-9][A-Z]|[A-Z][0-9]").match(callsign).hasMatch();
}
match = QRegularExpression("^" + compound_callsign_pattern).match(callsign);
if(match.hasMatch() && (match.capturedLength() == callsign.length())){
bool isValid = isValidCompoundCallsign(match.capturedRef(0));
qDebug() << "is valid compound??" << match.capturedRef(0) << isValid;
if(pIsCompound) *pIsCompound = isValid;
return isValid;
}
if(pIsCompound) *pIsCompound = false;
return false;
}
bool Varicode::isCompoundCallsign(const QString &callsign){
if(basecalls.contains(callsign)){
return false;
}
auto match = QRegularExpression(base_callsign_pattern).match(callsign);
if(match.hasMatch() && (match.capturedLength() == callsign.length())){
return false;
}
match = QRegularExpression("^" + compound_callsign_pattern).match(callsign);
if(!match.hasMatch() || (match.capturedLength() != callsign.length())){
return false;
}
bool isValid = isValidCompoundCallsign(match.capturedRef(0));
qDebug() << "is valid compound?" << match.capturedRef(0) << isValid;
return isValid;
}
// CQCQCQ EM73
// CQ DX EM73
// CQ QRP EM73
// BEACON EM73
QString Varicode::packBeaconMessage(QString const &text, const QString &callsign, int *n){
// HEARTBEAT EM73
QString Varicode::packHeartbeatMessage(QString const &text, const QString &callsign, int *n){
QString frame;
auto parsedText = beacon_re.match(text);
auto parsedText = heartbeat_re.match(text);
if(!parsedText.hasMatch()){
if(n) *n = 0;
return frame;
@@ -945,32 +1130,19 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign
auto extra = parsedText.captured("grid");
// Beacon Alt Type
// Heartbeat Alt Type
// ---------------
// 1 0 BEACON
// 1 0 HEARTBEAT
// 1 1 CQCQCQ
auto type = parsedText.captured("type");
auto isAlt = type.startsWith("CQ");
auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign);
if(!parsedCall.hasMatch()){
if(callsign.isEmpty()){
if(n) *n = 0;
return frame;
}
QString base = parsedCall.captured("base");
bool isPrefix = false;
QString fix = parsedCall.captured("prefix");
if(!fix.isEmpty()){
isPrefix = true;
}
if(!isPrefix){
fix = parsedCall.captured("suffix");
}
quint16 packed_extra = nmaxgrid; // which will display an empty string
if(extra.length() == 4 && QRegularExpression(grid_pattern).match(extra).hasMatch()){
packed_extra = Varicode::packGrid(extra);
@@ -982,7 +1154,7 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign
quint8 cqNumber = cqs.key(type, 0);
frame = packCompoundFrame(base, fix, isPrefix, FrameBeacon, packed_extra, cqNumber);
frame = packCompoundFrame(callsign, FrameHeartbeat, packed_extra, cqNumber);
if(frame.isEmpty()){
if(n) *n = 0;
return frame;
@@ -992,13 +1164,13 @@ QString Varicode::packBeaconMessage(QString const &text, const QString &callsign
return frame;
}
QStringList Varicode::unpackBeaconMessage(const QString &text, quint8 *pType, bool * isAlt, quint8 * pBits3){
quint8 type = FrameBeacon;
QStringList Varicode::unpackHeartbeatMessage(const QString &text, quint8 *pType, bool * isAlt, quint8 * pBits3){
quint8 type = FrameHeartbeat;
quint16 num = nmaxgrid;
quint8 bits3 = 0;
QStringList unpacked = unpackCompoundFrame(text, &type, &num, &bits3);
if(unpacked.isEmpty() || type != FrameBeacon){
if(unpacked.isEmpty() || type != FrameHeartbeat){
return QStringList{};
}
@@ -1021,7 +1193,7 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
qDebug() << "trying to pack compound message" << text;
auto parsedText = compound_re.match(text);
if(!parsedText.hasMatch()){
qDebug() << "no match for compound message";
qDebug() << "no match for compound message" << text;
if(n) *n = 0;
return frame;
}
@@ -1033,30 +1205,9 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
QString cmd = parsedText.captured("cmd");
QString num = parsedText.captured("num").trimmed();
QString base;
QString fix;
bool isPrefix = false;
if(basecalls.contains(callsign)){
// if it's a basecall, use it verbatim with no prefix/suffix
base = callsign;
fix = "";
} else {
// otherwise, parse the callsign for prefix/suffix
auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign);
if(!parsedCall.hasMatch()){
if(n) *n = 0;
return frame;
}
base = parsedCall.captured("base");
fix = parsedCall.captured("prefix");
if(!fix.isEmpty()){
isPrefix = true;
}
if(!isPrefix){
fix = parsedCall.captured("suffix");
}
if(callsign.isEmpty()){
if(n) *n = 0;
return frame;
}
quint8 type = FrameCompound;
@@ -1074,7 +1225,7 @@ QString Varicode::packCompoundMessage(QString const &text, int *n){
extra = Varicode::packGrid(grid);
}
frame = Varicode::packCompoundFrame(base, fix, isPrefix, type, extra, 0);
frame = Varicode::packCompoundFrame(callsign, type, extra, 0);
if(n) *n = parsedText.captured(0).length();
return frame;
@@ -1110,7 +1261,7 @@ QStringList Varicode::unpackCompoundMessage(const QString &text, quint8 *pType,
return unpacked;
}
QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, quint8 type, quint16 num, quint8 bits3){
QString Varicode::packCompoundFrame(const QString &callsign, quint8 type, quint16 num, quint8 bits3){
QString frame;
// needs to be a compound type...
@@ -1119,10 +1270,8 @@ QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString &
}
quint8 packed_flag = type;
quint32 packed_base = Varicode::packCallsign(baseCallsign);
quint32 packed_fix = Varicode::packAlphaNumeric22(fix, isPrefix);
if(packed_base == 0 || packed_fix == 0){
quint64 packed_callsign = Varicode::packAlphaNumeric50(callsign);
if(packed_callsign == 0){
return frame;
}
@@ -1133,11 +1282,10 @@ QString Varicode::packCompoundFrame(const QString &baseCallsign, const QString &
quint8 packed_5 = num & mask5;
quint8 packed_8 = (packed_5 << 3) | bits3;
// [3][28][22][11],[5][3] = 72
// [3][50][11],[5][3] = 72
auto bits = (
Varicode::intToBits(packed_flag, 3) +
Varicode::intToBits(packed_base, 28) +
Varicode::intToBits(packed_fix, 22) +
Varicode::intToBits(packed_callsign, 50) +
Varicode::intToBits(packed_11, 11)
);
@@ -1151,7 +1299,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu
return unpacked;
}
// [3][28][22][11],[5][3] = 72
// [3][50][11],[5][3] = 72
quint8 packed_8 = 0;
auto bits = Varicode::intToBits(Varicode::unpack72bits(text, &packed_8), 64);
@@ -1160,19 +1308,15 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu
quint8 packed_flag = Varicode::bitsToInt(bits.mid(0, 3));
// needs to be a beacon type...
// needs to be a ping type...
if(packed_flag == FrameDataCompressed || packed_flag == FrameDataUncompressed || packed_flag == FrameDirected){
return unpacked;
}
quint32 packed_base = Varicode::bitsToInt(bits.mid(3, 28));
quint32 packed_fix = Varicode::bitsToInt(bits.mid(31, 22));
quint64 packed_callsign = Varicode::bitsToInt(bits.mid(3, 50));
quint16 packed_11 = Varicode::bitsToInt(bits.mid(53, 11));
QString base = Varicode::unpackCallsign(packed_base).trimmed();
bool isPrefix = false;
QString fix = Varicode::unpackAlphaNumeric22(packed_fix, &isPrefix).trimmed();
QString callsign = Varicode::unpackAlphaNumeric50(packed_callsign);
quint16 num = (packed_11 << 5) | packed_5;
@@ -1180,15 +1324,8 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu
if(pNum) *pNum = num;
if(pBits3) *pBits3 = packed_3;
if(isPrefix){
unpacked.append(fix);
}
unpacked.append(base);
if(!isPrefix){
unpacked.append(fix);
}
unpacked.append(callsign);
unpacked.append("");
return unpacked;
}
@@ -1197,7 +1334,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, quint8 *pType, qu
// J1Y?
// KN4CRD: J1Y$
// KN4CRD: J1Y! HELLO WORLD
QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, QString *pTo, QString * pCmd, QString *pNum, int *n){
QString Varicode::packDirectedMessage(const QString &text, const QString &mycall, QString *pTo, bool *pToCompound, QString * pCmd, QString *pNum, int *n){
QString frame;
auto match = directed_re.match(text);
@@ -1206,7 +1343,11 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi
return frame;
}
QString from = callsign;
QString from = mycall;
bool isFromCompound = Varicode::isCompoundCallsign(from);
if(isFromCompound){
from = "<....>";
}
QString to = match.captured("callsign");
QString cmd = match.captured("cmd");
QString num = match.captured("num");
@@ -1217,25 +1358,27 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &callsi
return frame;
}
// validate "to" callsign
auto parsedTo = QRegularExpression(compound_callsign_pattern).match(to);
bool validToCallsign = (to != callsign) && (basecalls.contains(to) || parsedTo.hasMatch());
// ensure we have a valid callsign
bool isToCompound = false;
bool validToCallsign = (to != mycall) && Varicode::isValidCallsign(to, &isToCompound);
if(!validToCallsign){
qDebug() << "to" << to << "is not a valid callsign";
if(n) *n = 0;
return frame;
}
if(basecalls.contains(to)){
if(pTo) *pTo = to;
} else if(parsedTo.hasMatch()){
if(pTo) *pTo = parsedTo.captured(0);
// return back the parsed "to" field
if(pTo) *pTo = to;
if(pToCompound) *pToCompound = isToCompound;
auto parsedBase = parsedTo.captured("base");
if(parsedBase.length() != to.length()){
to = "<....>"; // parsedBase;
}
// then replace the current processing with a placeholder that we _can_ pack into a directed command,
// because later we'll send the "to" field in a compound frame using the results of this directed pack
if(isToCompound){
to = "<....>";
}
qDebug() << "directed" << validToCallsign << isToCompound << to;
// validate command
if(!Varicode::isCommandAllowed(cmd) && !Varicode::isCommandAllowed(cmd.trimmed())){
if(n) *n = 0;
@@ -1328,10 +1471,8 @@ QString packHuffMessage(const QString &input, int *n){
QString frame;
// [3][69] = 72
QVector<bool> frameDataBits;
QVector<bool> frameHeaderBits = Varicode::intToBits(Varicode::FrameDataUncompressed, 3);
// [1][71] = 72
QVector<bool> frameBits = {false};
int i = 0;
@@ -1341,6 +1482,7 @@ QString packHuffMessage(const QString &input, int *n){
for(it = input.constBegin(); it != input.constEnd(); it++){
auto ch = (*it).toUpper();
if(!validChars.contains(ch)){
if(n) *n = 0;
return frame;
}
}
@@ -1349,32 +1491,28 @@ QString packHuffMessage(const QString &input, int *n){
foreach(auto pair, Varicode::huffEncode(Varicode::defaultHuffTable(), input)){
auto charN = pair.first;
auto charBits = pair.second;
if(frameHeaderBits.length() + frameDataBits.length() + charBits.length() < frameSize){
frameDataBits += charBits;
if(frameBits.length() + charBits.length() < frameSize){
frameBits += charBits;
i += charN;
continue;
}
break;
}
QVector<bool> framePadBits;
qDebug() << "Huff bits" << frameBits.length() << "chars" << i;
int pad = frameSize - frameHeaderBits.length() - frameDataBits.length();
int pad = frameSize - frameBits.length();
if(pad){
// the way we will pad is this...
// set the bit after the frame to 0 and every bit after that a 1
// to unpad, seek from the end of the bits until you hit a zero... the rest is the actual frame.
for(int i = 0; i < pad; i++){
framePadBits.append(i == 0 ? (bool)0 : (bool)1);
frameBits.append(i == 0 ? (bool)0 : (bool)1);
}
}
qDebug() << "Huff bits" << frameDataBits.length() << "chars" << i;
QVector<bool> allBits = frameHeaderBits + frameDataBits + framePadBits;
quint64 value = Varicode::bitsToInt(allBits.constBegin(), 64);
quint8 rem = (quint8)Varicode::bitsToInt(allBits.constBegin() + 64, 8);
quint64 value = Varicode::bitsToInt(frameBits.constBegin(), 64);
quint8 rem = (quint8)Varicode::bitsToInt(frameBits.constBegin() + 64, 8);
frame = Varicode::pack72bits(value, rem);
if(n) *n = i;
@@ -1387,9 +1525,8 @@ QString packCompressedMessage(const QString &input, int *n){
QString frame;
QVector<bool> frameBits;
frameBits.append(Varicode::intToBits(Varicode::FrameDataCompressed, 3));
// [1][71] = 72
QVector<bool> frameBits = {true};
int i = 0;
foreach(auto pair, JSC::compress(input)){
@@ -1405,7 +1542,7 @@ QString packCompressedMessage(const QString &input, int *n){
break;
}
qDebug() << "Compressed bits" << frameBits.length() - 3 << "chars" << i;
qDebug() << "Compressed bits" << frameBits.length() << "chars" << i;
int pad = frameSize - frameBits.length();
if(pad){
@@ -1455,36 +1592,40 @@ QString Varicode::unpackDataMessage(const QString &text, quint8 *pType){
quint64 value = Varicode::unpack72bits(text, &rem);
auto bits = Varicode::intToBits(value, 64) + Varicode::intToBits(rem, 8);
quint8 type = Varicode::bitsToInt(bits.mid(0, 3));
bool compressed = bits.at(0);
int n = bits.lastIndexOf(0);
bits = bits.mid(3, n-3);
bits = bits.mid(1, n-1);
if(type == FrameDataUncompressed){
if(compressed){
unpacked = JSC::decompress(bits);
if(pType) *pType = Varicode::FrameDataCompressed;
} else {
// huff decode the bits (without escapes)
unpacked = Varicode::huffDecode(Varicode::defaultHuffTable(), bits);
if(pType) *pType = type;
} else if(type == FrameDataCompressed) {
unpacked = JSC::decompress(bits);
if(pType) *pType = type;
if(pType) *pType = Varicode::FrameDataUncompressed;
}
return unpacked;
}
// TODO: remove the dependence on providing all this data?
QStringList Varicode::buildMessageFrames(
QList<QPair<QString, int>> Varicode::buildMessageFrames(
QString const& mycall,
QString const& basecall,
//QString const& basecall,
QString const& mygrid,
bool compound,
//bool compound,
QString const& selectedCall,
QString const& text
){
#define ALLOW_SEND_COMPOUND 1
#define ALLOW_SEND_COMPOUND_DIRECTED 1
#define AUTO_PREPEND_DIRECTED 1
#define AUTO_REMOVE_MYCALL 1
QStringList frames;
bool mycallCompound = Varicode::isCompoundCallsign(mycall);
QList<QPair<QString, int>> frames;
foreach(QString line, text.split(QRegExp("[\\r\\n]"), QString::SkipEmptyParts)){
// once we find a directed call, data encode the rest of the line.
@@ -1493,35 +1634,36 @@ QStringList Varicode::buildMessageFrames(
// do the same for when we have sent data...
bool hasData = false;
#if AUTO_REMOVE_MYCALL
// remove our callsign from the start of the line...
if(line.startsWith(mycall + ":") || line.startsWith(mycall + " ")){
line = lstrip(line.mid(mycall.length() + 1));
}
if(line.startsWith(basecall + ":") || line.startsWith(basecall + " ")){
line = lstrip(line.mid(basecall.length() + 1));
}
// remove trailing whitespace as long as there are characters left afterwards
auto rline = rstrip(line);
if(!rline.isEmpty()){
line = rline;
}
#endif
#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("GROUPCALL") ||
line.startsWith("BEACON") ||
line.startsWith("@ALLCALL") ||
line.contains("HEARTBEAT") ||
Varicode::startsWithCQ(line)
);
#if AUTO_PREPEND_DIRECTED_ALLOW_TEXT_CALLSIGNS
auto calls = Varicode::parseCallsigns(line);
bool lineStartsWithStandardCall = !calls.isEmpty() && line.startsWith(calls.first()) && calls.first().length() > 2;
#else
bool lineStartsWithStandardCall = false;
#endif
if(lineStartsWithBaseCall || lineStartsWithStandardCall){
// pass
@@ -1547,7 +1689,7 @@ QStringList Varicode::buildMessageFrames(
bool useDat = false;
int l = 0;
QString bcnFrame = Varicode::packBeaconMessage(line, mycall, &l);
QString bcnFrame = Varicode::packHeartbeatMessage(line, mycall, &l);
#if ALLOW_SEND_COMPOUND
int o = 0;
@@ -1558,8 +1700,8 @@ QStringList Varicode::buildMessageFrames(
QString dirCmd;
QString dirTo;
QString dirNum;
QString dirFrame = Varicode::packDirectedMessage(line, basecall, &dirTo, &dirCmd, &dirNum, &n);
bool dirToCompound = dirTo.contains("/");
bool dirToCompound = false;
QString dirFrame = Varicode::packDirectedMessage(line, mycall, &dirTo, &dirToCompound, &dirCmd, &dirNum, &n);
if(dirToCompound){
qDebug() << "directed message to field is compound" << dirTo;
}
@@ -1597,18 +1739,21 @@ QStringList Varicode::buildMessageFrames(
}
if(useBcn){
frames.append(frame);
frames.append({ frame, Varicode::JS8Call });
line = line.mid(l);
}
#if ALLOW_SEND_COMPOUND
if(useCmp){
frames.append(frame);
frames.append({ frame, Varicode::JS8Call });
line = line.mid(o);
}
#endif
if(useDir){
bool shouldUseStandardFrame = true;
#if ALLOW_SEND_COMPOUND_DIRECTED
/**
* We have a few special cases when we are sending to a compound call, or our call is a compound call, or both.
* CASE 0: Non-compound: KN4CRD: J1Y ACK
@@ -1628,27 +1773,28 @@ QStringList Varicode::buildMessageFrames(
* -> One standard compound frame, followed by a compound directed frame
* -> <KN4CRD/P EM73> then <J1Y/P ACK>
**/
bool shouldUseStandardFrame = true;
if(compound || dirToCompound){
if(mycallCompound || dirToCompound){
qDebug() << "compound?" << mycallCompound << dirToCompound;
// Cases 1, 2, 3 all send a standard compound frame first...
QString deCompoundMessage = QString("`%1 %2").arg(mycall).arg(mygrid);
QString deCompoundFrame = Varicode::packCompoundMessage(deCompoundMessage, nullptr);
if(!deCompoundFrame.isEmpty()){
frames.append(deCompoundFrame);
frames.append({ deCompoundFrame, Varicode::JS8Call });
}
// Followed, by a standard OR compound directed message...
QString dirCompoundMessage = QString("`%1%2%3").arg(dirTo).arg(dirCmd).arg(dirNum); //.replace(" ", " ");
QString dirCompoundMessage = QString("`%1%2%3").arg(dirTo).arg(dirCmd).arg(dirNum);
QString dirCompoundFrame = Varicode::packCompoundMessage(dirCompoundMessage, nullptr);
if(!dirCompoundFrame.isEmpty()){
frames.append(dirCompoundFrame);
frames.append({ dirCompoundFrame, Varicode::JS8Call });
}
shouldUseStandardFrame = false;
}
#endif
if(shouldUseStandardFrame) {
// otherwise, just send the standard directed frame
frames.append(frame);
frames.append({ frame, Varicode::JS8Call });
}
line = line.mid(n);
@@ -1676,21 +1822,32 @@ QStringList Varicode::buildMessageFrames(
}
if(useDat){
frames.append(frame);
frames.append({ frame, Varicode::JS8CallData });
line = line.mid(m);
}
}
}
if(!frames.isEmpty()){
frames.first().second |= Varicode::JS8CallFirst;
frames.last().second |= Varicode::JS8CallLast;
}
return frames;
}
BuildMessageFramesThread::BuildMessageFramesThread(const QString &mycall, const QString &basecall, const QString &mygrid, bool compound, const QString &selectedCall, const QString &text, QObject *parent):
BuildMessageFramesThread::BuildMessageFramesThread(
const QString &mycall,
//const QString &basecall,
const QString &mygrid,
//bool compound,
const QString &selectedCall,
const QString &text, QObject *parent):
QThread(parent),
m_mycall{mycall},
m_basecall{basecall},
//m_basecall{basecall},
m_mygrid{mygrid},
m_compound{compound},
//m_compound{compound},
m_selectedCall{selectedCall},
m_text{text}
{
@@ -1699,12 +1856,19 @@ BuildMessageFramesThread::BuildMessageFramesThread(const QString &mycall, const
void BuildMessageFramesThread::run(){
auto results = Varicode::buildMessageFrames(
m_mycall,
m_basecall,
//m_basecall,
m_mygrid,
m_compound,
//m_compound,
m_selectedCall,
m_text
);
emit resultReady(results);
QList<QString> frames;
QList<int> bits;
foreach(auto pair, results){
frames.append(pair.first);
bits.append(pair.second);
}
emit resultReady(frames, bits);
}
+21 -15
View File
@@ -12,6 +12,7 @@
#include <QVector>
#include <QThread>
class Varicode
{
public:
@@ -20,12 +21,12 @@ public:
JS8Call = 0, // [000] <- any other frame of the message
JS8CallFirst = 1, // [001] <- the first frame of a message
JS8CallLast = 2, // [010] <- the last frame of a message
JS8CallReserved = 4, // [100] <- a reserved flag for future use...
JS8CallData = 4, // [100] <- raw data frame (no frame type header)
};
enum FrameType {
FrameUnknown = 255, // [11111111] <- only used as a sentinel
FrameBeacon = 0, // [000]
FrameHeartbeat = 0, // [000]
FrameCompound = 1, // [001]
FrameCompoundDirected = 2, // [010]
FrameDirected = 3, // [011]
@@ -39,7 +40,7 @@ public:
static QString frameTypeString(quint8 type) {
const char* FrameTypeStrings[] = {
"FrameBeacon",
"FrameHeartbeat",
"FrameCompound",
"FrameCompoundDirected",
"FrameDirected",
@@ -109,6 +110,9 @@ public:
static quint32 packAlphaNumeric22(QString const& value, bool isFlag);
static QString unpackAlphaNumeric22(quint32 packed, bool *isFlag);
static quint64 packAlphaNumeric50(QString const& value);
static QString unpackAlphaNumeric50(quint64 packed);
static quint32 packCallsign(QString const& value);
static QString unpackCallsign(quint32 value);
@@ -126,27 +130,29 @@ public:
static bool isCommandAllowed(const QString &cmd);
static bool isCommandBuffered(const QString &cmd);
static int isCommandChecksumed(const QString &cmd);
static bool isValidCallsign(const QString &callsign, bool *pIsCompound);
static bool isCompoundCallsign(const QString &callsign);
static QString packBeaconMessage(QString const &text, QString const&callsign, int *n);
static QStringList unpackBeaconMessage(const QString &text, quint8 *pType, bool *isAlt, quint8 *pBits3);
static QString packHeartbeatMessage(QString const &text, QString const&callsign, int *n);
static QStringList unpackHeartbeatMessage(const QString &text, quint8 *pType, bool *isAlt, quint8 *pBits3);
static QString packCompoundMessage(QString const &text, int *n);
static QStringList unpackCompoundMessage(const QString &text, quint8 *pType, quint8 *pBits3);
static QString packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, quint8 type, quint16 num, quint8 bits3);
static QString packCompoundFrame(const QString &callsign, quint8 type, quint16 num, quint8 bits3);
static QStringList unpackCompoundFrame(const QString &text, quint8 *pType, quint16 *pNum, quint8 *pBits3);
static QString packDirectedMessage(QString const& text, QString const& callsign, QString *pTo, QString * pCmd, QString *pNum, int *n);
static QString packDirectedMessage(QString const& text, QString const& mycall, QString *pTo, bool *pToCompound, QString * pCmd, QString *pNum, int *n);
static QStringList unpackDirectedMessage(QString const& text, quint8 *pType);
static QString packDataMessage(QString const& text, int *n);
static QString unpackDataMessage(QString const& text, quint8 *pType);
static QStringList buildMessageFrames(
static QList<QPair<QString, int>> buildMessageFrames(
QString const& mycall,
QString const& basecall,
//QString const& basecall,
QString const& mygrid,
bool compound,
//bool compound,
QString const& selectedCall,
QString const& text
);
@@ -158,21 +164,21 @@ class BuildMessageFramesThread : public QThread
Q_OBJECT
public:
BuildMessageFramesThread(QString const& mycall,
QString const& basecall,
//QString const& basecall,
QString const& mygrid,
bool compound,
//bool compound,
QString const& selectedCall,
QString const& text,
QObject *parent=nullptr);
void run() override;
signals:
void resultReady(const QStringList s);
void resultReady(QStringList, QList<int>);
private:
QString m_mycall;
QString m_basecall;
//QString m_basecall;
QString m_mygrid;
bool m_compound;
//bool m_compound;
QString m_selectedCall;
QString m_text;
};