Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d7b187269 | |||
| 13308a38f8 | |||
| 804605e9e6 | |||
| 4e981da9c8 | |||
| 6436e163bd | |||
| 379a0fa78f | |||
| 1c4a2ab7d8 | |||
| 54f6bdb0af | |||
| 5a0e2a8b14 | |||
| 1a9c611195 | |||
| c70661461e | |||
| 1f866e14f1 | |||
| c0e8a791e6 | |||
| 74523985bb | |||
| 8d28ea345a | |||
| 33446297fa | |||
| 6f648d5a60 | |||
| f1f618bbcd | |||
| ac7da998a2 | |||
| b3cd705fb6 | |||
| 604e366d4a | |||
| 8d73805dce | |||
| 5c491dc10d | |||
| d611259a25 | |||
| 00f2d46167 | |||
| e4cba50144 | |||
| 440311a75b | |||
| 38fc98702b | |||
| 6201de8c12 | |||
| 4321bd5e75 | |||
| 9f7fd2e7e2 | |||
| 65a2411c46 | |||
| d076eedeb8 | |||
| 7da30200f5 | |||
| cac5f798a3 | |||
| 41fb7481ed | |||
| 09effda8e4 | |||
| eecf7f65c7 | |||
| 0618b1cc09 | |||
| 00685b9117 | |||
| a5a6c95a44 | |||
| 8612f2cd32 | |||
| 5fa60659f3 | |||
| 08629019d4 | |||
| 7293d1e796 | |||
| aff001ce96 | |||
| 31d80a9b1d | |||
| 9010fa07c0 | |||
| 121bb0fdb5 | |||
| 47a7c06854 | |||
| c4089c0af9 | |||
| 5c28d154ff | |||
| 6dfd037736 | |||
| 3767b347d9 | |||
| ff12fd2240 | |||
| 6a0fea1d0a | |||
| f11e02c966 | |||
| 0763ecfe37 | |||
| 4c3ed5c719 | |||
| 0064176736 | |||
| 67386a09c7 | |||
| 3c81108cc6 | |||
| 71ed0c1f10 | |||
| 20a44171e4 | |||
| 644633721f |
+65
-13
@@ -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
@@ -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;
|
||||
|
||||
+904
-850
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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 1)
|
||||
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
@@ -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
@@ -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
@@ -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
@@ -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
-61
@@ -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
|
||||
@@ -103,35 +93,6 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
||||
n30min=minval(n30fox(1:nfox))
|
||||
n30max=maxval(n30fox(1:nfox))
|
||||
endif
|
||||
j=0
|
||||
rewind 19
|
||||
if(nfox.eq.0) then
|
||||
endfile 19
|
||||
rewind 19
|
||||
else
|
||||
do i=1,nfox
|
||||
n=n30fox(i)
|
||||
if(n30max-n30fox(i).le.4) then
|
||||
j=j+1
|
||||
c2fox(j)=c2fox(i)
|
||||
g2fox(j)=g2fox(i)
|
||||
nsnrfox(j)=nsnrfox(i)
|
||||
nfreqfox(j)=nfreqfox(i)
|
||||
n30fox(j)=n
|
||||
m=n30max-n
|
||||
if(len(trim(g2fox(j))).eq.4) then
|
||||
call azdist(mygrid,g2fox(j),0.d0,nAz,nEl,nDmiles,nDkm, &
|
||||
nHotAz,nHotABetter)
|
||||
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 +216,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 +328,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 +371,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 +396,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 +454,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 +487,7 @@ contains
|
||||
endif
|
||||
|
||||
call flush(6)
|
||||
call flush(13)
|
||||
|
||||
|
||||
select type(this)
|
||||
type is (counting_ft8_decoder)
|
||||
this%decoded = this%decoded + 1
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
+3
-3
@@ -98,9 +98,9 @@ void LogQSO::accept()
|
||||
QString hisCall,hisGrid,mode,rptSent,rptRcvd,dateOn,dateOff,timeOn,timeOff,band,operator_call;
|
||||
QString comments,name;
|
||||
|
||||
hisCall=ui->call->text();
|
||||
hisGrid=ui->grid->text();
|
||||
mode=ui->mode->text();
|
||||
hisCall=ui->call->text().toUpper();
|
||||
hisGrid=ui->grid->text().toUpper();
|
||||
mode=ui->mode->text().toUpper();
|
||||
rptSent=ui->sent->text();
|
||||
rptRcvd=ui->rcvd->text();
|
||||
m_dateTimeOn = ui->start_date_time->dateTime ();
|
||||
|
||||
+702
-531
File diff suppressed because it is too large
Load Diff
+35
-20
@@ -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
@@ -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><html><head/><body><p>Enable or disable the automatic beacon</p></body></html></string>
|
||||
<string><html><head/><body><p>Enable or disable the automatic heartbeat transmission</p></body></html></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="true"] {
|
||||
<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="true"] {
|
||||
</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="true"] {
|
||||
<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="true"] {
|
||||
</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><html><head/><body><p>Send an SNR message</p></body></html></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><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle.</p></body></html></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><html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle.</p></body></html></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>&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><html><head/><body><p>Download sample audio files demonstrating the various modes.</p></body></html></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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
+344
-179
@@ -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,41 @@ 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
|
||||
#define AUTO_PREPEND_DIRECTED_ALLOW_TEXT_CALLSIGNS 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 +1635,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 +1690,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 +1701,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 +1740,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 +1774,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 +1823,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 +1857,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
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user