Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf11d66f60 | |||
| 371aa1e20c | |||
| 8348f61a94 | |||
| bf28918096 | |||
| 571aa6446d | |||
| 4290dd6e2f | |||
| de66664635 | |||
| 9e9c996813 | |||
| f67ea3803d | |||
| fa00e0dfd6 | |||
| ceaa76c497 | |||
| 9c9a5c2d8b | |||
| d611d83bb9 | |||
| 253b60217f | |||
| 39a536bb91 | |||
| 115a9d65f7 | |||
| 796920cb6b | |||
| c1c7d85195 | |||
| c5a6f76b1e | |||
| 5b198351be | |||
| 246d53201c | |||
| 999a239e67 | |||
| f091cb28ef | |||
| f415b0c94f | |||
| 0bf2afa5f8 | |||
| 706a9b1ebd | |||
| 4cec8b80a3 | |||
| 9e68b8c402 | |||
| 478ba82df7 | |||
| 7aef92dd68 | |||
| 01249bd115 | |||
| 9bee00c5dd | |||
| b9b274f2d6 | |||
| 4a17062487 | |||
| 1c73ce2c90 | |||
| 07a29c7f1d | |||
| f7a941406c | |||
| adecb88c29 | |||
| 857e19ed94 | |||
| f47224979e | |||
| c0833aa753 | |||
| 20d931a9ca | |||
| 1b2f8a1c6c | |||
| 95e75741ed | |||
| dc75c08081 | |||
| 8c204e317b | |||
| a672668c3a | |||
| 8638b53e45 | |||
| 394a6d045f | |||
| 29bbedcc8f | |||
| d66b4ffb37 | |||
| c0c4693782 | |||
| 9223d3da40 | |||
| 83e3f5ddbc | |||
| 0f4057aa97 | |||
| 591629e369 | |||
| 4a96ab3b13 | |||
| e955cff24f | |||
| fe405cfba8 | |||
| 92117aa791 | |||
| d4c2d9a871 | |||
| b2e2b91d31 | |||
| fa864c50cd | |||
| 1c6d1babe6 | |||
| ac27d1a9b6 | |||
| 28eb082655 | |||
| 9a945c156d | |||
| 83c742f7ec | |||
| 7a788c05c8 | |||
| 7b409a6ff4 | |||
| 87a631f5f0 | |||
| efd6b54ba7 | |||
| 553f2400e5 | |||
| 5c84e79e5b | |||
| 052b81ec8f | |||
| 7ecc550bc2 | |||
| b8267372e4 | |||
| 512dffabf4 | |||
| 50a3a56d2d | |||
| deb228948d | |||
| 58032b6ae4 | |||
| 7845736c05 | |||
| 77eb65d6b3 | |||
| 252c21b818 | |||
| 82c3b23e44 | |||
| 5764170975 | |||
| a2c85256e8 |
@@ -10,6 +10,7 @@ auto CallsignValidator::validate (QString& input, int& pos) const -> State
|
||||
{
|
||||
auto match = re_.match (input, 0, QRegularExpression::PartialPreferCompleteMatch);
|
||||
input = input.toUpper ();
|
||||
if (input.count(QLatin1Char('/')) > 1) return Invalid;
|
||||
if (match.hasMatch ()) return Acceptable;
|
||||
if (!input.size () || match.hasPartialMatch ()) return Intermediate;
|
||||
pos = input.size ();
|
||||
|
||||
+113
-2
@@ -140,6 +140,7 @@
|
||||
#include <QSettings>
|
||||
#include <QAudioDeviceInfo>
|
||||
#include <QAudioInput>
|
||||
#include <QDebug>
|
||||
#include <QDialog>
|
||||
#include <QAction>
|
||||
#include <QFileDialog>
|
||||
@@ -160,7 +161,6 @@
|
||||
#include <QColorDialog>
|
||||
#include <QSerialPortInfo>
|
||||
#include <QScopedPointer>
|
||||
#include <QDebug>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
@@ -181,6 +181,8 @@
|
||||
#include "MaidenheadLocatorValidator.hpp"
|
||||
#include "CallsignValidator.hpp"
|
||||
|
||||
#include "varicode.h"
|
||||
|
||||
#include "ui_Configuration.h"
|
||||
#include "moc_Configuration.cpp"
|
||||
|
||||
@@ -430,6 +432,8 @@ private:
|
||||
Q_SLOT void on_add_macro_push_button_clicked (bool = false);
|
||||
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_qth_message_line_edit_textChanged(QString const&);
|
||||
Q_SLOT void on_add_macro_line_edit_editingFinished ();
|
||||
Q_SLOT void delete_macro ();
|
||||
void delete_selected_macros (QModelIndexList);
|
||||
@@ -529,6 +533,11 @@ private:
|
||||
// configuration fields that we publish
|
||||
QString my_callsign_;
|
||||
QString my_grid_;
|
||||
QString my_station_;
|
||||
int my_dBm_;
|
||||
QString my_qth_;
|
||||
int callsign_aging_;
|
||||
int activity_aging_;
|
||||
QColor color_CQ_;
|
||||
QColor next_color_CQ_;
|
||||
QColor color_MyCall_;
|
||||
@@ -548,6 +557,7 @@ private:
|
||||
bool id_after_73_;
|
||||
bool tx_QSY_allowed_;
|
||||
bool spot_to_psk_reporter_;
|
||||
bool autoreply_off_at_startup_;
|
||||
bool monitor_off_at_startup_;
|
||||
bool monitor_last_used_;
|
||||
bool log_as_RTTY_;
|
||||
@@ -658,6 +668,7 @@ void Configuration::set_spot_to_psk_reporter (bool spot)
|
||||
}
|
||||
}
|
||||
|
||||
bool Configuration::autoreply_off_at_startup () const {return m_->autoreply_off_at_startup_;}
|
||||
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_;}
|
||||
bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;}
|
||||
@@ -817,6 +828,30 @@ QString Configuration::my_grid() const
|
||||
return the_grid;
|
||||
}
|
||||
|
||||
QString Configuration::my_station() const
|
||||
{
|
||||
return m_->my_station_;
|
||||
}
|
||||
|
||||
int Configuration::my_dBm() const {
|
||||
return m_->my_dBm_;
|
||||
}
|
||||
|
||||
QString Configuration::my_qth() const
|
||||
{
|
||||
return m_->my_qth_;
|
||||
}
|
||||
|
||||
int Configuration::callsign_aging() const
|
||||
{
|
||||
return m_->callsign_aging_;
|
||||
}
|
||||
|
||||
int Configuration::activity_aging() const
|
||||
{
|
||||
return m_->activity_aging_;
|
||||
}
|
||||
|
||||
void Configuration::set_location (QString const& grid_descriptor)
|
||||
{
|
||||
// change the dynamic grid
|
||||
@@ -955,6 +990,8 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
|
||||
ui_->callsign_line_edit->setValidator (new CallsignValidator {this});
|
||||
ui_->grid_line_edit->setValidator (new MaidenheadLocatorValidator {this});
|
||||
ui_->add_macro_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
ui_->station_message_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
ui_->qth_message_line_edit->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
|
||||
ui_->udp_server_port_spin_box->setMinimum (1);
|
||||
ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
|
||||
@@ -1125,10 +1162,49 @@ void Configuration::impl::initialize_models ()
|
||||
{
|
||||
pal.setColor (QPalette::Base, Qt::white);
|
||||
}
|
||||
|
||||
QMap<int, int> dbm2mw = {
|
||||
{0 , 1},
|
||||
{3 , 2},
|
||||
{7 , 5},
|
||||
{10 , 10},
|
||||
{13 , 20},
|
||||
{17 , 50},
|
||||
{20 , 100},
|
||||
{23 , 200},
|
||||
{27 , 500},
|
||||
{30 , 1000}, // 1W
|
||||
{33 , 2000}, // 2W
|
||||
{37 , 5000}, // 5W
|
||||
{40 , 10000}, // 10W
|
||||
{43 , 20000}, // 20W
|
||||
{47 , 50000}, // 50W
|
||||
{50 , 100000}, // 100W
|
||||
{53 , 200000}, // 200W
|
||||
{57 , 500000}, // 500W
|
||||
{60 , 1000000}, // 1000W
|
||||
};
|
||||
|
||||
ui_->station_power_combo_box->clear();
|
||||
ui_->station_power_combo_box->addItem(QString(""), -1);
|
||||
|
||||
foreach(auto dbm, dbm2mw.keys()){
|
||||
ui_->station_power_combo_box->addItem(QString("%1 (%2 dBm)").arg(Varicode::formatPWR(dbm)).arg(dbm), dbm);
|
||||
|
||||
if(dbm == my_dBm_){
|
||||
ui_->station_power_combo_box->setCurrentIndex(ui_->station_power_combo_box->count()-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ui_->callsign_line_edit->setPalette (pal);
|
||||
ui_->grid_line_edit->setPalette (pal);
|
||||
ui_->callsign_line_edit->setText (my_callsign_);
|
||||
ui_->grid_line_edit->setText (my_grid_);
|
||||
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_->qth_message_line_edit->setText (my_qth_.toUpper());
|
||||
ui_->use_dynamic_grid->setChecked(use_dynamic_grid_);
|
||||
ui_->labCQ->setStyleSheet(QString("background: %1").arg(color_CQ_.name()));
|
||||
ui_->labMyCall->setStyleSheet(QString("background: %1").arg(color_MyCall_.name()));
|
||||
@@ -1147,6 +1223,7 @@ void Configuration::impl::initialize_models ()
|
||||
ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
|
||||
ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_);
|
||||
ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_);
|
||||
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
||||
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
||||
ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
|
||||
ui_->log_as_RTTY_check_box->setChecked (log_as_RTTY_);
|
||||
@@ -1247,6 +1324,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_dBm_ = settings_->value("MyPower", -1).toInt();
|
||||
callsign_aging_ = settings_->value ("CallsignAging", 0).toInt ();
|
||||
activity_aging_ = settings_->value ("ActivityAging", 2).toInt ();
|
||||
my_qth_ = settings_->value("MyQTH", QString {}).toString();
|
||||
next_color_CQ_ = color_CQ_ = settings_->value("colorCQ","#66ff66").toString();
|
||||
next_color_MyCall_ = color_MyCall_ = settings_->value("colorMyCall","#ff6666").toString();
|
||||
next_color_TxMsg_ = color_TxMsg_ = settings_->value("colorTxMsg","#ffff00").toString();
|
||||
@@ -1340,6 +1422,7 @@ void Configuration::impl::read_settings ()
|
||||
|
||||
type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value<Configuration::Type2MsgGen> ();
|
||||
|
||||
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
||||
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
||||
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
||||
spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool ();
|
||||
@@ -1399,7 +1482,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", 5).toInt ();
|
||||
beacon_ = settings_->value ("TxBeacon", 15).toInt ();
|
||||
watchdog_ = settings_->value ("TxWatchdog", 0).toInt ();
|
||||
TX_messages_ = settings_->value ("Tx2QSO", true).toBool ();
|
||||
enable_VHF_features_ = settings_->value("VHFUHF",false).toBool ();
|
||||
@@ -1433,6 +1516,11 @@ void Configuration::impl::write_settings ()
|
||||
|
||||
settings_->setValue ("MyCall", my_callsign_);
|
||||
settings_->setValue ("MyGrid", my_grid_);
|
||||
settings_->setValue ("MyStation", my_station_);
|
||||
settings_->setValue ("MyPower", my_dBm_);
|
||||
settings_->setValue ("MyQTH", my_qth_);
|
||||
settings_->setValue ("CallsignAging", callsign_aging_);
|
||||
settings_->setValue ("ActivityAging", activity_aging_);
|
||||
settings_->setValue("colorCQ",color_CQ_);
|
||||
settings_->setValue("colorMyCall",color_MyCall_);
|
||||
settings_->setValue("colorTxMsg",color_TxMsg_);
|
||||
@@ -1471,6 +1559,7 @@ void Configuration::impl::write_settings ()
|
||||
settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_));
|
||||
settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_));
|
||||
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
|
||||
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
||||
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
||||
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
||||
settings_->setValue ("PSKReporter", spot_to_psk_reporter_);
|
||||
@@ -1884,6 +1973,11 @@ 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_dBm_ = ui_->station_power_combo_box->currentData().toInt();
|
||||
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
||||
callsign_aging_ = ui_->callsign_aging_spin_box->value();
|
||||
activity_aging_ = ui_->activity_aging_spin_box->value();
|
||||
spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked ();
|
||||
id_interval_ = ui_->CW_id_interval_spin_box->value ();
|
||||
ntrials_ = ui_->sbNtrials->value ();
|
||||
@@ -1893,6 +1987,7 @@ void Configuration::impl::accept ()
|
||||
RxBandwidth_ = ui_->sbBandwidth->value ();
|
||||
id_after_73_ = ui_->CW_id_after_73_check_box->isChecked ();
|
||||
tx_QSY_allowed_ = ui_->tx_QSY_check_box->isChecked ();
|
||||
autoreply_off_at_startup_ = ui_->autoreply_off_check_box->isChecked ();
|
||||
monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
|
||||
monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
|
||||
type_2_msg_gen_ = static_cast<Type2MsgGen> (ui_->type_2_msg_gen_combo_box->currentIndex ());
|
||||
@@ -2167,6 +2262,22 @@ void Configuration::impl::on_sound_output_combo_box_currentTextChanged (QString
|
||||
default_audio_output_device_selected_ = QAudioDeviceInfo::defaultOutputDevice ().deviceName () == text;
|
||||
}
|
||||
|
||||
void Configuration::impl::on_station_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->station_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_qth_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->qth_message_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_add_macro_line_edit_editingFinished ()
|
||||
{
|
||||
ui_->add_macro_line_edit->setText (ui_->add_macro_line_edit->text ().toUpper ());
|
||||
|
||||
@@ -96,6 +96,11 @@ public:
|
||||
|
||||
QString my_callsign () const;
|
||||
QString my_grid () const;
|
||||
QString my_station () const;
|
||||
int my_dBm() const;
|
||||
int activity_aging() const;
|
||||
int callsign_aging() const;
|
||||
QString my_qth () const;
|
||||
QFont text_font () const;
|
||||
QFont decoded_text_font () const;
|
||||
qint32 id_interval () const;
|
||||
@@ -108,6 +113,7 @@ public:
|
||||
bool tx_QSY_allowed () const;
|
||||
bool spot_to_psk_reporter () const;
|
||||
void set_spot_to_psk_reporter (bool);
|
||||
bool autoreply_off_at_startup () const;
|
||||
bool monitor_off_at_startup () const;
|
||||
bool monitor_last_used () const;
|
||||
bool log_as_RTTY () const;
|
||||
|
||||
+616
-444
File diff suppressed because it is too large
Load Diff
@@ -804,9 +804,9 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
|
||||
if (UNK != mode)
|
||||
{
|
||||
auto new_mode = map_mode (mode);
|
||||
// TRACE_CAT ("HamlibTransceiver", "rig_set_split_freq_mode freq = " << tx
|
||||
// << " mode = " << rig_strrmode (new_mode));
|
||||
// error_check (rig_set_split_freq_mode (rig_.data (), RIG_VFO_CURR, tx, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX frequency and mode"));
|
||||
TRACE_CAT ("HamlibTransceiver", "rig_set_split_freq_mode freq = " << tx
|
||||
<< " mode = " << rig_strrmode (new_mode));
|
||||
error_check (rig_set_split_freq_mode (rig_.data (), RIG_VFO_CURR, tx, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX frequency and mode"));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace
|
||||
"QRA64",
|
||||
"FreqCal",
|
||||
"FT8",
|
||||
"FT8Free"
|
||||
};
|
||||
std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
QRA64,
|
||||
FreqCal,
|
||||
FT8,
|
||||
FT8Free,
|
||||
MODES_END_SENTINAL_AND_COUNT // this must be last
|
||||
};
|
||||
Q_ENUM (Mode)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Version number components
|
||||
set (WSJTX_VERSION_MAJOR 0)
|
||||
set (WSJTX_VERSION_MINOR 1)
|
||||
set (WSJTX_VERSION_MINOR 3)
|
||||
set (WSJTX_VERSION_PATCH 0)
|
||||
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions
|
||||
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
|
||||
|
||||
+88
-16
@@ -52,36 +52,108 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
|
||||
, contest_mode_
|
||||
, grid_c_string.constData ()
|
||||
, 22, 6);
|
||||
|
||||
// We're only going to unpack standard messages for CQs && beacons...
|
||||
// TODO: jsherer - this is a hack for now...
|
||||
if(is_standard_){
|
||||
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_standard_){
|
||||
tryUnpackDirected();
|
||||
}
|
||||
tryUnpack();
|
||||
}
|
||||
|
||||
void DecodedText::tryUnpackDirected(){
|
||||
DecodedText::DecodedText (QString const& ft8callmessage){
|
||||
message_ = ft8callmessage;
|
||||
is_standard_ = false;
|
||||
tryUnpack();
|
||||
}
|
||||
|
||||
bool DecodedText::tryUnpack(){
|
||||
if(is_standard_){
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unpacked = false;
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackCompound();
|
||||
}
|
||||
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackDirected();
|
||||
}
|
||||
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackData();
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
bool DecodedText::tryUnpackCompound(){
|
||||
QString m = message().trimmed();
|
||||
|
||||
// directed calls will always be 12+ chars and contain no spaces.
|
||||
if(m.length() < 12 || m.contains(' ')){
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList parts = Varicode::unpackDirectedMessage(m);
|
||||
QStringList parts = Varicode::unpackCompoundMessage(m);
|
||||
|
||||
if(parts.isEmpty()){
|
||||
return;
|
||||
if(parts.isEmpty() || parts.length() < 2){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parts.length() == 3){
|
||||
// replace it with the correct unpacked (query)
|
||||
message_ = QString("%1: %2%3").arg(parts.at(0), parts.at(1), parts.at(2));
|
||||
} else {
|
||||
// replace it with the correct unpacked (freetext)
|
||||
message_ = QString(parts.join(QChar()));
|
||||
}
|
||||
compound_ = QString("%1/%2").arg(parts.at(0), parts.at(1));
|
||||
message_ = QString("%1: ").arg(compound_);
|
||||
return true;
|
||||
}
|
||||
|
||||
directed_ = parts;
|
||||
bool DecodedText::tryUnpackDirected(){
|
||||
QString m = message().trimmed();
|
||||
|
||||
// directed calls will always be 12+ chars and contain no spaces.
|
||||
if(m.length() < 12 || m.contains(' ')){
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList parts = Varicode::unpackDirectedMessage(m);
|
||||
|
||||
if(parts.isEmpty()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parts.length() == 3){
|
||||
// replace it with the correct unpacked (directed)
|
||||
message_ = QString("%1: %2%3 ").arg(parts.at(0), parts.at(1), parts.at(2));
|
||||
} else if(parts.length() == 4){
|
||||
// replace it with the correct unpacked (directed numeric)
|
||||
message_ = QString("%1: %2%3 %4 ").arg(parts.at(0), parts.at(1), parts.at(2), parts.at(3));
|
||||
} else {
|
||||
// replace it with the correct unpacked (freetext)
|
||||
message_ = QString(parts.join(""));
|
||||
}
|
||||
|
||||
directed_ = parts;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodedText::tryUnpackData(){
|
||||
QString m = message().trimmed();
|
||||
|
||||
// data frames calls will always be 12+ chars and contain no spaces.
|
||||
if(m.length() < 12 || m.contains(' ')){
|
||||
return false;
|
||||
}
|
||||
|
||||
QString data = Varicode::unpackDataMessage(m);
|
||||
|
||||
if(data.isEmpty()){
|
||||
return false;
|
||||
}
|
||||
|
||||
message_ = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList DecodedText::messageWords () const
|
||||
|
||||
+10
-2
@@ -31,11 +31,18 @@ class DecodedText
|
||||
{
|
||||
public:
|
||||
explicit DecodedText (QString const& message, bool, QString const& my_grid);
|
||||
explicit DecodedText (QString const& ft8callmessage);
|
||||
|
||||
void tryUnpackDirected();
|
||||
bool tryUnpack();
|
||||
bool tryUnpackCompound();
|
||||
bool tryUnpackDirected();
|
||||
bool tryUnpackData();
|
||||
|
||||
QString compoundCall() const { return compound_; }
|
||||
bool isCompoundMessage() const { return !compound_.isEmpty(); }
|
||||
|
||||
QStringList directedMessage() const { return directed_; }
|
||||
bool isDirectedMessage() const { return !directed_.isEmpty(); }
|
||||
bool isDirectedMessage() const { return !directed_.isEmpty() && directed_.length() > 2; }
|
||||
|
||||
QString string() const { return string_; }
|
||||
QString message() const { return message_; }
|
||||
@@ -84,6 +91,7 @@ private:
|
||||
column_mode = 19,
|
||||
column_qsoText = 22 };
|
||||
|
||||
QString compound_;
|
||||
QStringList directed_;
|
||||
QString string_;
|
||||
int padding_;
|
||||
|
||||
+3
-1
@@ -423,7 +423,9 @@ subroutine packbits(dbits,nsymd,m0,sym)
|
||||
|
||||
itype=1
|
||||
if(bcontest) then
|
||||
call to_contest_msg(msg0,msg)
|
||||
!call to_contest_msg(msg0,msg)
|
||||
! this causes problems with freetext ala, KN4CRD DE KN4CRD -13 R
|
||||
msg=msg0
|
||||
else
|
||||
msg=msg0
|
||||
end if
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>377</width>
|
||||
<height>257</height>
|
||||
<width>600</width>
|
||||
<height>285</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -16,6 +16,12 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>600</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
|
||||
+947
-289
File diff suppressed because it is too large
Load Diff
+55
-19
@@ -116,13 +116,15 @@ public slots:
|
||||
void readFromStdout();
|
||||
void p1ReadFromStdout();
|
||||
void setXIT(int n, Frequency base = 0u);
|
||||
void setFreqForRestore(int freq, bool shouldRestore);
|
||||
void setFreq4(int rxFreq, int txFreq);
|
||||
void msgAvgDecode2();
|
||||
void fastPick(int x0, int x1, int y);
|
||||
|
||||
QString lookupCallInCompoundCache(QString const &call);
|
||||
void clearActivity();
|
||||
int logRxTxMessageText(QDateTime date, bool isFree, QString text, int freq, bool tx, int block=-1);
|
||||
void addMessageText(QString text);
|
||||
int logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block=-1);
|
||||
void addMessageText(QString text, bool clear=false);
|
||||
void resetMessage();
|
||||
void resetMessageUI();
|
||||
void createMessage(QString const& text);
|
||||
@@ -197,6 +199,7 @@ private slots:
|
||||
void on_txb5_doubleClicked ();
|
||||
void on_txb6_clicked();
|
||||
void on_startTxButton_toggled(bool checked);
|
||||
void toggleTx(bool start);
|
||||
void splitAndSendNextMessage();
|
||||
void on_rbNextFreeTextMsg_toggled (bool status);
|
||||
void on_lookupButton_clicked();
|
||||
@@ -236,10 +239,9 @@ private slots:
|
||||
void on_rbFreeText_clicked(bool checked);
|
||||
void on_clearAction_triggered(QObject * sender);
|
||||
void on_cqMacroButton_clicked();
|
||||
void on_deMacroButton_clicked();
|
||||
void on_replyMacroButton_clicked();
|
||||
void on_qtcMacroButton_clicked();
|
||||
void on_qthMacroButton_clicked();
|
||||
void on_snrMacroButton_clicked();
|
||||
void buildQueryMenu(QMenu *);
|
||||
void on_queryButton_pressed();
|
||||
void on_macrosMacroButton_pressed();
|
||||
void on_tableWidgetRXAll_cellClicked(int row, int col);
|
||||
@@ -252,14 +254,17 @@ private slots:
|
||||
void on_nextFreeTextMsg_currentTextChanged (QString const&);
|
||||
void on_extFreeTextMsg_currentTextChanged (QString const&);
|
||||
void on_extFreeTextMsgEdit_currentTextChanged (QString const&);
|
||||
QStringList buildFT8MessageFrames(QString const& text);
|
||||
int currentFreq();
|
||||
int countFT8MessageFrames(QString const& text);
|
||||
QPair<QStringList, QStringList> buildFT8MessageFrames(QString const& text);
|
||||
QString parseFT8Message(QString input, bool *isFree);
|
||||
bool prepareNextMessageFrame();
|
||||
bool isFreqOffsetFree(int f, int bw);
|
||||
int findFreeFreqOffset(int fmin, int fmax, int bw);
|
||||
void scheduleBeacon(bool first=false);
|
||||
void setBeaconTimer(QDateTime timestamp);
|
||||
void prepareBeacon();
|
||||
void scheduleBacon(bool first=false);
|
||||
void setBaconTimer(QDateTime timestamp);
|
||||
void pauseBacon();
|
||||
void prepareBacon();
|
||||
QString calculateDistance(QString const& grid);
|
||||
void on_rptSpinBox_valueChanged(int n);
|
||||
void killFile();
|
||||
@@ -636,6 +641,7 @@ private:
|
||||
struct CallDetail
|
||||
{
|
||||
QString call;
|
||||
QString through;
|
||||
QString grid;
|
||||
int freq;
|
||||
QDateTime utcTimestamp;
|
||||
@@ -646,44 +652,51 @@ private:
|
||||
{
|
||||
QString from;
|
||||
QString to;
|
||||
QString command;
|
||||
QString cmd;
|
||||
int freq;
|
||||
QDateTime utcTimestamp;
|
||||
int snr;
|
||||
QString text;
|
||||
};
|
||||
|
||||
struct ActivityDetail
|
||||
{
|
||||
bool isFree;
|
||||
bool isLowConfidence;
|
||||
QString firstCall;
|
||||
QString secondCall;
|
||||
bool isCompound;
|
||||
int bits;
|
||||
int freq;
|
||||
QString text;
|
||||
QDateTime utcTimestamp;
|
||||
int snr;
|
||||
};
|
||||
|
||||
struct RXDetail
|
||||
{
|
||||
bool isFree;
|
||||
int freq;
|
||||
QString text;
|
||||
QDateTime utcTimestamp;
|
||||
struct MessageBuffer {
|
||||
CommandDetail cmd;
|
||||
QList<ActivityDetail> msgs;
|
||||
};
|
||||
|
||||
bool m_rxDirty;
|
||||
int m_txFrameCount;
|
||||
QString m_lastTxMessage;
|
||||
QDateTime m_lastTxTime;
|
||||
|
||||
|
||||
QQueue<QString> m_txFrameQueue;
|
||||
QQueue<RXDetail> m_rxFrameQueue;
|
||||
QQueue<ActivityDetail> m_rxFrameQueue;
|
||||
QQueue<CommandDetail> m_rxCommandQueue;
|
||||
QMap<QString, QString> m_compoundCallCache; // base callsign -> compound callsign
|
||||
QCache<QString, QDateTime> m_txAllcallCommandCache; // callsign -> last tx
|
||||
QCache<int, QDateTime> m_rxRecentCache; // freq -> last rx
|
||||
QCache<int, QDateTime> m_rxDirectedCache; // freq -> last directed rx
|
||||
QCache<QString, int> m_rxCallCache; // call -> last freq seen
|
||||
QMap<int, int> m_rxFrameBlockNumbers; // freq -> block
|
||||
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)
|
||||
QSet<QString> m_callSeenBeacon; // call
|
||||
int m_previousFreq;
|
||||
bool m_shouldRestoreFreq;
|
||||
QMap<QString,FoxQSO> m_foxQSO;
|
||||
QMap<QString,QString> m_loggedByFox;
|
||||
|
||||
@@ -692,6 +705,7 @@ private:
|
||||
QQueue<QString> m_foxRR73Queue;
|
||||
QQueue<qint64> m_foxRateQueue;
|
||||
|
||||
bool m_nextBeaconPaused = false;
|
||||
QDateTime m_nextBeacon;
|
||||
QDateTime m_dateTimeQSOOn;
|
||||
QDateTime m_dateTimeLastTX;
|
||||
@@ -748,6 +762,7 @@ private:
|
||||
void rigFailure (QString const& reason);
|
||||
void pskSetLocal ();
|
||||
void pskPost(DecodedText const& decodedtext);
|
||||
void pskLogReport(QString mode, int offset, int snr, QString callsign, QString grid);
|
||||
void displayDialFrequency ();
|
||||
void transmitDisplay (bool);
|
||||
void processMessage(DecodedText const&, Qt::KeyboardModifiers = 0);
|
||||
@@ -827,6 +842,27 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
class EnterKeyPressEater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event){
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return){
|
||||
emit this->enterKeyPressed(keyEvent, obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// standard event processing
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
public:
|
||||
Q_SIGNAL void enterKeyPressed(QKeyEvent *evt, QObject *obj);
|
||||
};
|
||||
|
||||
extern int killbyname(const char* progName);
|
||||
extern void getDev(int* numDevices,char hostAPI_DeviceName[][50],
|
||||
int minChan[], int maxChan[],
|
||||
|
||||
+575
-405
File diff suppressed because it is too large
Load Diff
+612
-136
@@ -30,93 +30,298 @@
|
||||
const int nalphabet = 41;
|
||||
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"};
|
||||
QString grid_pattern = {R"((?<grid>[A-R]{2}[0-9]{2})+)"};
|
||||
QString callsign_pattern1 = {R"((?<callsign>[A-Z0-9/]{2,}))"};
|
||||
QString callsign_pattern2 = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){3,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"};
|
||||
QString callsign_pattern3 = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
|
||||
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([A-Z0-9]{1,4}\/)?([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?(\/[A-Z0-9]{1,4})?)\b)"};
|
||||
QString pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
|
||||
QString callsign_alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "};
|
||||
|
||||
QMap<QString, int> directed_cmds = {
|
||||
// any changes here need to be made also in the directed regular xpression for parsing
|
||||
{"?", 0 }, // query snr
|
||||
{"$", 1 }, // query stations heard
|
||||
{"@", 2 }, // query qth
|
||||
{"&", 3 }, // query station message
|
||||
{"|", 4 }, // relay message
|
||||
{":+", 5 }, // report +snr
|
||||
{":-", 6 }, // report -snr
|
||||
{":ACK", 7 }, // ack message
|
||||
{":NACK", 8 }, // nack message
|
||||
// ...
|
||||
{" ", 31 }, // send freetext
|
||||
|
||||
// directed queries
|
||||
{"?", 0 }, // query ack
|
||||
{"@", 1 }, // query qth
|
||||
{"&", 2 }, // query station message
|
||||
{"$", 3 }, // query station(s) heard
|
||||
{"^", 4 }, // query snr
|
||||
{"%", 5 }, // query pwr
|
||||
{"|", 6 }, // relay message?
|
||||
{"!", 7 }, // alert message?
|
||||
|
||||
// {"=", 8 }, // unused? (can we even use equals?)
|
||||
// {"/", 9 }, // unused? (can we even use stroke?)
|
||||
|
||||
// directed responses
|
||||
{" ACK", 23 }, // acknowledged
|
||||
{" PWR", 24 }, // power level
|
||||
{" SNR", 25 }, // seen a station at the provided snr
|
||||
{" NO", 26 }, // negative confirm
|
||||
{" YES", 27 }, // confirm
|
||||
{" 73", 28 }, // best regards, end of contact
|
||||
{" RR", 29 }, // confirm message
|
||||
{" AGN?", 30 }, // repeat message
|
||||
{" ", 31 }, // send freetext
|
||||
};
|
||||
|
||||
QSet<int> allowed_cmds = {0, 2, 31};
|
||||
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 23, 24, 25, 26, 27, 28, 29, 30, 31};
|
||||
|
||||
QRegularExpression directed_re(R"(^(?:(?<from>[A-Z0-9/]+):\s?)?(?<to>[A-Z0-9/]+)(?<cmd>([?$@&| ]|:N?ACK|:[-+])))");
|
||||
QSet<int> buffered_cmds = {6, 7};
|
||||
|
||||
QMap<QChar, QString> huff = {
|
||||
QRegularExpression directed_re("^"
|
||||
"(?<to>[A-Z0-9/]+)"
|
||||
"(?<cmd>\\s?(?:AGN[?]|RR|73|YES|NO|SNR|PWR|ACK|[?@&$^%|! ]))"
|
||||
"(?<pwr>\\s?\\d+\\s?[KM]?W)?"
|
||||
"(?<num>\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"
|
||||
);
|
||||
|
||||
QMap<QChar, QString> hufftable = {
|
||||
// char code weight
|
||||
{' ' , "001" }, // 1300
|
||||
{'E' , "000" }, // 1270.2
|
||||
{'T' , "1100" }, // 905.6
|
||||
{'A' , "1010" }, // 816.7
|
||||
{'O' , "0111" }, // 750.7
|
||||
{'I' , "0101" }, // 696.6
|
||||
{'N' , "0100" }, // 674.9
|
||||
{'S' , "11111" }, // 632.7
|
||||
{'H' , "11110" }, // 609.4
|
||||
{'R' , "11101" }, // 598.7
|
||||
{'D' , "10111" }, // 425.3
|
||||
{'L' , "10110" }, // 402.5
|
||||
{'C' , "111001" }, // 278.2
|
||||
{'U' , "111000" }, // 275.8
|
||||
{'M' , "110111" }, // 240.6
|
||||
{'W' , "110110" }, // 236.0
|
||||
{'F' , "110100" }, // 222.8
|
||||
{'G' , "100111" }, // 201.5
|
||||
{'Q' , "100110" }, // 200
|
||||
{'Y' , "011010" }, // 197.4
|
||||
{'P' , "011001" }, // 192.9
|
||||
{'B' , "011000" }, // 149.2
|
||||
{'!' , "0110111" }, // 100
|
||||
{'.' , "1000000" }, // 100
|
||||
{'0' , "1000001" }, // 100
|
||||
{'1' , "1000010" }, // 100
|
||||
{'2' , "1000011" }, // 100
|
||||
{'3' , "1000100" }, // 100
|
||||
{'4' , "1000101" }, // 100
|
||||
{'5' , "1000110" }, // 100
|
||||
{'6' , "1000111" }, // 100
|
||||
{'7' , "1001000" }, // 100
|
||||
{'8' , "1001001" }, // 100
|
||||
{'9' , "1001010" }, // 100
|
||||
{'?' , "1001011" }, // 100
|
||||
{'^' , "1101010" }, // 100 <- shift
|
||||
{'V' , "0110110" }, // 97.8
|
||||
{'K' , "11010111" }, // 77.2
|
||||
{'J' , "1101011010" }, // 15.3
|
||||
{'X' , "1101011001" }, // 15.0
|
||||
{'Z' , "11010110110" }, // 7.4
|
||||
{':' , "11010110000" }, // 5
|
||||
{'+' , "110101100011" }, // 5
|
||||
{'-' , "110101101110" }, // 5
|
||||
{'/' , "110101101111" }, // 5
|
||||
{'\x04' , "110101100010" }, // 1 <- eot
|
||||
{ ' ' , "000" }, // 1300
|
||||
{ 'E' , "001" }, // 1270.2
|
||||
{ 'T' , "1100" }, // 905.6
|
||||
{ 'A' , "1010" }, // 816.7
|
||||
{ 'O' , "0111" }, // 750.7
|
||||
{ 'I' , "0101" }, // 696.6
|
||||
{ 'N' , "0100" }, // 674.9
|
||||
{ 'S' , "11111" }, // 632.7
|
||||
{ 'H' , "11110" }, // 609.4
|
||||
{ 'R' , "11101" }, // 598.7
|
||||
{ 'D' , "10111" }, // 425.3
|
||||
{ 'L' , "10110" }, // 402.5
|
||||
{ 'C' , "111001" }, // 278.2
|
||||
{ 'U' , "111000" }, // 275.8
|
||||
{ 'M' , "110111" }, // 240.6
|
||||
{ 'W' , "110110" }, // 236.0
|
||||
{ 'F' , "110100" }, // 222.8
|
||||
{ 'G' , "100111" }, // 201.5
|
||||
{ 'Q' , "100110" }, // 200
|
||||
{ 'Y' , "011010" }, // 197.4
|
||||
{ 'P' , "011001" }, // 192.9
|
||||
{ 'B' , "011000" }, // 149.2
|
||||
{ '\\' , "0110111" }, // 100 <- escape
|
||||
{ '.' , "1000000" }, // 100
|
||||
{ '0' , "1000001" }, // 100
|
||||
{ '1' , "1000010" }, // 100
|
||||
{ '2' , "1000011" }, // 100
|
||||
{ '3' , "1000100" }, // 100
|
||||
{ '4' , "1000101" }, // 100
|
||||
{ '5' , "1000110" }, // 100
|
||||
{ '6' , "1000111" }, // 100
|
||||
{ '7' , "1001000" }, // 100
|
||||
{ '8' , "1001001" }, // 100
|
||||
{ '9' , "1001010" }, // 100
|
||||
{ '?' , "1001011" }, // 100
|
||||
{ '/' , "1101010" }, // 100
|
||||
{ 'V' , "0110110" }, // 97.8
|
||||
{ 'K' , "11010111" }, // 77.2
|
||||
{ 'J' , "1101011010" }, // 15.3
|
||||
{ 'X' , "1101011001" }, // 15.0
|
||||
{ 'Z' , "11010110110" }, // 7.4
|
||||
{ ':' , "11010110000" }, // 5
|
||||
{ '+' , "110101100011" }, // 5
|
||||
{ '-' , "110101101110" }, // 5
|
||||
{ '!' , "110101101111" }, // 5
|
||||
{ '\x04' , "110101100010" }, // 1 <- eot
|
||||
|
||||
/*
|
||||
A-Z 0-9 Space \\ ? / : - + !
|
||||
special chars that are escaped will be added here too...
|
||||
*/
|
||||
};
|
||||
|
||||
QChar huffeot = '\x04';
|
||||
/*
|
||||
original: Space \\ ? / : - + !
|
||||
needed: ^,&@#$%'"()<>|*[]{}=;_~`
|
||||
*/
|
||||
QMap<QString, QChar> huffescapes = {
|
||||
{ "\\ ", '^' },
|
||||
{ "\\E", ',' },
|
||||
{ "\\T", '&' },
|
||||
{ "\\A", '@' },
|
||||
{ "\\O", '#' },
|
||||
{ "\\I", '$' },
|
||||
{ "\\N", '%' },
|
||||
{ "\\S", '\'' },
|
||||
{ "\\H", '\"' },
|
||||
{ "\\R", '(' },
|
||||
{ "\\D", ')' },
|
||||
{ "\\L", '<' },
|
||||
{ "\\C", '>' },
|
||||
{ "\\U", '|' },
|
||||
{ "\\M", '*' },
|
||||
{ "\\W", '[' },
|
||||
{ "\\F", ']' },
|
||||
{ "\\G", '{' },
|
||||
{ "\\Q", '}' },
|
||||
{ "\\Y", '=' },
|
||||
{ "\\P", ';' },
|
||||
{ "\\B", '_' },
|
||||
{ "\\.", '~' },
|
||||
{ "\\0", '`' },
|
||||
|
||||
#if 0
|
||||
// reserved <= 14 bits
|
||||
{ "\\1", '' },
|
||||
{ "\\2", '' },
|
||||
{ "\\3", '' },
|
||||
{ "\\4", '' },
|
||||
{ "\\5", '' },
|
||||
{ "\\6", '' },
|
||||
{ "\\7", '' },
|
||||
{ "\\8", '' },
|
||||
{ "\\9", '' },
|
||||
{ "\\?", '' },
|
||||
{ "\\/", '' },
|
||||
{ "\\V", '' },
|
||||
#endif
|
||||
};
|
||||
|
||||
QChar ESC = '\\'; // Escape char
|
||||
QChar EOT = '\x04'; // EOT char
|
||||
|
||||
quint32 nbasecall = 37 * 36 * 10 * 27 * 27 * 27;
|
||||
|
||||
QMap<QString, quint32> basecalls = {
|
||||
{ "CQ DX", nbasecall + 1 },
|
||||
{ "<....>", nbasecall + 1 }, // incomplete callsign
|
||||
{ "CQCQCQ", nbasecall + 2 },
|
||||
{ "ALLCALL", nbasecall + 3 },
|
||||
};
|
||||
|
||||
QMap<int, int> dbm2mw = {
|
||||
{0 , 1},
|
||||
{3 , 2},
|
||||
{7 , 5},
|
||||
{10 , 10},
|
||||
{13 , 20},
|
||||
{17 , 50},
|
||||
{20 , 100},
|
||||
{23 , 200},
|
||||
{27 , 500},
|
||||
{30 , 1000}, // 1W
|
||||
{33 , 2000}, // 2W
|
||||
{37 , 5000}, // 5W
|
||||
{40 , 10000}, // 10W
|
||||
{43 , 20000}, // 20W
|
||||
{47 , 50000}, // 50W
|
||||
{50 , 100000}, // 100W
|
||||
{53 , 200000}, // 200W
|
||||
{57 , 500000}, // 500W
|
||||
{60 , 1000000}, // 1000W
|
||||
};
|
||||
|
||||
|
||||
QMap<QChar, QString> initializeEscapes(QMap<QChar, QString> huff, QMap<QString, QChar> escapes){
|
||||
QMap<QChar, QString> newhuff(huff);
|
||||
foreach(auto escapeString, escapes.keys()){
|
||||
auto ch = escapes[escapeString];
|
||||
auto encoded = Varicode::huffEncode(huff, escapeString);
|
||||
auto bits = Varicode::bitsListToBits(encoded);
|
||||
newhuff[ch] = Varicode::bitsToStr(bits);
|
||||
}
|
||||
|
||||
#if PRINT_VARICODE_ALPHABET
|
||||
auto keys = newhuff.keys();
|
||||
qSort(keys.begin(), keys.end(), [newhuff](QChar a, QChar b){
|
||||
return newhuff[a].length() < newhuff[b].length();
|
||||
});
|
||||
foreach(auto ch, keys){
|
||||
qDebug() << ch << newhuff[ch] << newhuff[ch].length();
|
||||
}
|
||||
#endif
|
||||
|
||||
return newhuff;
|
||||
}
|
||||
|
||||
QMap<QChar, QString> hufftableescaped = initializeEscapes(hufftable, huffescapes);
|
||||
|
||||
/*
|
||||
* UTILITIES
|
||||
*/
|
||||
|
||||
int mwattsToDbm(int mwatts){
|
||||
int dbm = 0;
|
||||
auto values = dbm2mw.values();
|
||||
qSort(values);
|
||||
foreach(auto mw, values){
|
||||
if(mw < mwatts){ continue; }
|
||||
dbm = dbm2mw.key(mw);
|
||||
break;
|
||||
}
|
||||
|
||||
return dbm;
|
||||
}
|
||||
|
||||
int dbmTomwatts(int dbm){
|
||||
if(dbm2mw.contains(dbm)){
|
||||
return dbm2mw[dbm];
|
||||
}
|
||||
auto iter = dbm2mw.lowerBound(dbm);
|
||||
if(iter == dbm2mw.end()){
|
||||
return dbm2mw.last();
|
||||
}
|
||||
return iter.value();
|
||||
}
|
||||
|
||||
/*
|
||||
* VARICODE
|
||||
*/
|
||||
|
||||
QString Varicode::formatSNR(int snr){
|
||||
if(snr < -60 || snr > 60){
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QString("%1%2").arg(snr >= 0 ? "+" : "").arg(snr, snr < 0 ? 3 : 2, 10, QChar('0'));
|
||||
}
|
||||
|
||||
QString Varicode::formatPWR(int dbm){
|
||||
if(dbm < 0 || dbm > 60){
|
||||
return QString();
|
||||
}
|
||||
|
||||
int mwatts = dbmTomwatts(dbm);
|
||||
if(mwatts < 1000){
|
||||
return QString("%1mW").arg(mwatts);
|
||||
}
|
||||
|
||||
return QString("%1W").arg(mwatts/1000);
|
||||
}
|
||||
|
||||
QString Varicode::checksum16(QString const &input){
|
||||
auto fromBytes = input.toLocal8Bit();
|
||||
auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_16_KERMIT());
|
||||
auto checksum = Varicode::pack16bits(crc);
|
||||
if(checksum.length() < 3){
|
||||
checksum += QString(" ").repeated(3-checksum.length());
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
bool Varicode::checksum16Valid(QString const &checksum, QString const &input){
|
||||
auto fromBytes = input.toLocal8Bit();
|
||||
auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_16_KERMIT());
|
||||
return Varicode::pack16bits(crc) == checksum;
|
||||
}
|
||||
|
||||
QString Varicode::checksum32(QString const &input){
|
||||
auto fromBytes = input.toLocal8Bit();
|
||||
auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_32_BZIP2());
|
||||
auto checksum = Varicode::pack32bits(crc);
|
||||
if(checksum.length() < 6){
|
||||
checksum += QString(" ").repeated(6-checksum.length());
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
bool Varicode::checksum32Valid(QString const &checksum, QString const &input){
|
||||
auto fromBytes = input.toLocal8Bit();
|
||||
auto crc = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_32_BZIP2());
|
||||
return Varicode::pack32bits(crc) == checksum;
|
||||
}
|
||||
|
||||
QStringList Varicode::parseCallsigns(QString const &input){
|
||||
QStringList callsigns;
|
||||
QRegularExpression re(callsign_pattern2);
|
||||
QRegularExpression re(compound_callsign_pattern);
|
||||
QRegularExpressionMatchIterator iter = re.globalMatch(input);
|
||||
while(iter.hasNext()){
|
||||
QRegularExpressionMatch match = iter.next();
|
||||
@@ -151,7 +356,7 @@ QStringList Varicode::parseGrids(const QString &input){
|
||||
return grids;
|
||||
}
|
||||
|
||||
QList<QVector<bool>> Varicode::huffEncode(QString const& text){
|
||||
QList<QVector<bool>> Varicode::huffEncode(QMap<QChar, QString> const &huff, QString const& text){
|
||||
QList<QVector<bool>> out;
|
||||
|
||||
foreach(auto ch, text){
|
||||
@@ -164,28 +369,22 @@ QList<QVector<bool>> Varicode::huffEncode(QString const& text){
|
||||
return out;
|
||||
}
|
||||
|
||||
QVector<bool> Varicode::huffFlatten(QList<QVector<bool>> &list){
|
||||
QVector<bool> out;
|
||||
foreach(auto vec, list){
|
||||
out += vec;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
QString Varicode::huffDecode(QMap<QChar, QString> const &huff, QVector<bool> const& bitvec, int pad){
|
||||
QString text;
|
||||
|
||||
QString Varicode::huffDecode(QVector<bool> const& bitvec){
|
||||
QString out;
|
||||
|
||||
QString bits = bitsToStr(bitvec);
|
||||
QString bits = bitsToStr(bitvec).mid(0, bitvec.length()-pad);
|
||||
|
||||
// TODO: jsherer - this is naive...
|
||||
while(bits.length() > 0){
|
||||
bool found = false;
|
||||
foreach(auto key, huff.keys()){
|
||||
if(bits.startsWith(huff[key])){
|
||||
if(key == huffeot){
|
||||
if(key == EOT){
|
||||
text.append(" ");
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
out.append(key);
|
||||
text.append(key);
|
||||
bits = bits.mid(huff[key].length());
|
||||
found = true;
|
||||
}
|
||||
@@ -195,9 +394,42 @@ QString Varicode::huffDecode(QVector<bool> const& bitvec){
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
return text;
|
||||
}
|
||||
|
||||
QString Varicode::huffUnescape(QString const &input){
|
||||
QString text = input;
|
||||
// unescape alternate alphabet
|
||||
foreach(auto escaped, huffescapes.keys()){
|
||||
text = text.replace(escaped, huffescapes[escaped]);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
QString Varicode::huffEscape(QString const &input){
|
||||
QString text = input;
|
||||
// escape alternate alphabet
|
||||
foreach(auto unescaped, huffescapes.values()){
|
||||
text = text.replace(unescaped, huffescapes.key(unescaped));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
QSet<QChar> Varicode::huffValidChars(){
|
||||
return QSet<QChar>::fromList(hufftableescaped.keys());
|
||||
}
|
||||
|
||||
bool Varicode::huffShouldEscape(QString const &input){
|
||||
foreach(auto ch, huffescapes.values()){
|
||||
if(input.contains(ch)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// convert char* array of 0 bytes and 1 bytes to bool vector
|
||||
QVector<bool> Varicode::bytesToBits(char *bitvec, int n){
|
||||
QVector<bool> bits;
|
||||
@@ -259,29 +491,54 @@ quint64 Varicode::bitsToInt(QVector<bool>::ConstIterator start, int n){
|
||||
return v;
|
||||
}
|
||||
|
||||
QVector<bool> Varicode::bitsListToBits(QList<QVector<bool>> &list){
|
||||
QVector<bool> out;
|
||||
foreach(auto vec, list){
|
||||
out += vec;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
quint8 Varicode::unpack5bits(QString const& value){
|
||||
return alphabet.indexOf(value.at(0));
|
||||
}
|
||||
|
||||
// pack a 5-bit value from 0 to 31 into a single character
|
||||
QString Varicode::pack5bits(quint8 packed){
|
||||
return alphabet.at(packed % nalphabet);
|
||||
return alphabet.at(packed % 32);
|
||||
}
|
||||
|
||||
quint8 Varicode::unpack6bits(QString const& value){
|
||||
return alphabet.indexOf(value.at(0));
|
||||
}
|
||||
|
||||
// pack a 6-bit value from 0 to 40 into a single character
|
||||
QString Varicode::pack6bits(quint8 packed){
|
||||
return alphabet.at(packed % 41);
|
||||
}
|
||||
|
||||
quint16 Varicode::unpack16bits(QString const& value){
|
||||
int a = alphabet.indexOf(value.at(0));
|
||||
int b = alphabet.indexOf(value.at(1));
|
||||
int c = alphabet.indexOf(value.at(2));
|
||||
return (nalphabet*nalphabet) * a + nalphabet*b + c;
|
||||
|
||||
int unpacked = (nalphabet * nalphabet) * a + nalphabet * b + c;
|
||||
if(unpacked > (1<<16)-1){
|
||||
// BASE-41 can produce a value larger than 16 bits... ala "???" == 70643
|
||||
return 0;
|
||||
}
|
||||
|
||||
return unpacked & ((1<<16)-1);
|
||||
}
|
||||
|
||||
// pack a 16-bit value into a three character sequence
|
||||
QString Varicode::pack16bits(quint16 packed){
|
||||
QString out;
|
||||
quint16 tmp = packed / (nalphabet*nalphabet);
|
||||
quint16 tmp = packed / (nalphabet * nalphabet);
|
||||
|
||||
out.append(alphabet.at(tmp));
|
||||
|
||||
tmp = (packed - (tmp * (nalphabet*nalphabet))) / nalphabet;
|
||||
tmp = (packed - (tmp * (nalphabet * nalphabet))) / nalphabet;
|
||||
out.append(alphabet.at(tmp));
|
||||
|
||||
tmp = packed % nalphabet;
|
||||
@@ -310,6 +567,37 @@ QString Varicode::pack64bits(quint64 packed){
|
||||
return pack32bits(a) + pack32bits(b);
|
||||
}
|
||||
|
||||
|
||||
// //
|
||||
// --- //
|
||||
// //
|
||||
|
||||
|
||||
// pack a 4-digit alpha-numeric callsign prefix/suffix into a 22 bit value
|
||||
quint32 Varicode::packCallsignPrefixSuffix(QString const& value){
|
||||
quint8 mask6 = (1<<6)-1;
|
||||
|
||||
QString prefix = QString(value).replace(QRegExp("[^A-Z0-9]"), "");
|
||||
if(prefix.length() < 4){
|
||||
prefix = prefix + QString(".").repeated(4-prefix.length());
|
||||
}
|
||||
|
||||
// [16][6] = 22 bits
|
||||
auto left = prefix.left(3);
|
||||
auto right = prefix.right(1); // guaranteed to be in our alphabet...
|
||||
|
||||
return ((quint32)Varicode::unpack16bits(left) << 6) | (Varicode::unpack6bits(right) & mask6);
|
||||
}
|
||||
|
||||
QString Varicode::unpackCallsignPrefixSuffix(quint32 packed){
|
||||
quint32 mask22 = ((1<<16)-1) << 6;
|
||||
quint32 mask6 = ((1<<6)-1);
|
||||
quint16 a = (packed & mask22) >> 6;
|
||||
quint16 b = packed & mask6 ;
|
||||
return QString(Varicode::pack16bits(a) + Varicode::pack6bits(b)).replace(".", "");
|
||||
}
|
||||
|
||||
// pack a callsign into a 28-bit value
|
||||
quint32 Varicode::packCallsign(QString const& value){
|
||||
quint32 packed = 0;
|
||||
|
||||
@@ -356,7 +644,7 @@ quint32 Varicode::packCallsign(QString const& value){
|
||||
}
|
||||
|
||||
QString matched;
|
||||
QRegularExpression m(callsign_pattern3);
|
||||
QRegularExpression m(pack_callsign_pattern);
|
||||
foreach(auto permutation, permutations){
|
||||
auto match = m.match(permutation);
|
||||
if(match.hasMatch()){
|
||||
@@ -481,6 +769,7 @@ QPair<float, float> grid2deg(QString const &grid){
|
||||
return longLat;
|
||||
}
|
||||
|
||||
// pack a 4-digit maidenhead grid locator into a 15-bit value
|
||||
quint16 Varicode::packGrid(QString const& grid){
|
||||
// TODO: validate grid...
|
||||
|
||||
@@ -505,50 +794,162 @@ QString Varicode::unpackGrid(quint16 value){
|
||||
return deg2grid(dlong, dlat).left(4);
|
||||
}
|
||||
|
||||
QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, int *n){
|
||||
bool Varicode::isCommandAllowed(const QString &cmd){
|
||||
return directed_cmds.contains(cmd) && allowed_cmds.contains(directed_cmds[cmd]);
|
||||
}
|
||||
|
||||
bool Varicode::isCommandBuffered(const QString &cmd){
|
||||
return directed_cmds.contains(cmd) && buffered_cmds.contains(directed_cmds[cmd]);
|
||||
}
|
||||
|
||||
QString Varicode::packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num){
|
||||
QString frame;
|
||||
|
||||
quint8 packed_is_data = 0;
|
||||
quint8 packed_is_compound = 1;
|
||||
quint8 packed_is_prefix = (int)isPrefix;
|
||||
quint32 packed_base = Varicode::packCallsign(baseCallsign);
|
||||
quint32 packed_fix = Varicode::packCallsignPrefixSuffix(fix);
|
||||
|
||||
if(packed_base == 0 || packed_fix == 0){
|
||||
return frame;
|
||||
}
|
||||
|
||||
quint16 mask11 = ((1<<11)-1)<<5;
|
||||
quint8 mask5 = (1<<5)-1;
|
||||
|
||||
quint16 packed_11 = (num & mask11) >> 5;
|
||||
quint8 packed_5 = num & mask5;
|
||||
|
||||
// [1][1][1][28][22][11],[5] = 69
|
||||
auto bits = (
|
||||
Varicode::intToBits(packed_is_data, 1) +
|
||||
Varicode::intToBits(packed_is_compound, 1) +
|
||||
Varicode::intToBits(packed_is_prefix, 1) +
|
||||
Varicode::intToBits(packed_base, 28) +
|
||||
Varicode::intToBits(packed_fix, 22) +
|
||||
Varicode::intToBits(packed_11, 11)
|
||||
);
|
||||
|
||||
return Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_5 % 32);
|
||||
}
|
||||
|
||||
QStringList Varicode::unpackCompoundMessage(const QString &text){
|
||||
QStringList unpacked;
|
||||
|
||||
if(text.length() < 13){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
// [1][1][1][28][22][11],[5] = 69
|
||||
auto bits = Varicode::bitsToStr(Varicode::intToBits(Varicode::unpack64bits(text.left(12)), 64));
|
||||
quint8 packed_5 = Varicode::unpack5bits(text.right(1));
|
||||
|
||||
quint8 is_data = Varicode::bitsToInt(Varicode::strToBits(bits.left(1)));
|
||||
if(is_data != 0){
|
||||
return unpacked;
|
||||
}
|
||||
quint8 is_compound = Varicode::bitsToInt(Varicode::strToBits(bits.mid(1,1)));
|
||||
if(is_compound != 1){
|
||||
return unpacked;
|
||||
}
|
||||
quint8 is_prefix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(2,1)));
|
||||
quint32 packed_base = Varicode::bitsToInt(Varicode::strToBits(bits.mid(3, 28)));
|
||||
quint32 packed_fix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(31, 22)));
|
||||
quint8 packed_11 = Varicode::bitsToInt(Varicode::strToBits(bits.mid(53, 11)));
|
||||
|
||||
QString base = Varicode::unpackCallsign(packed_base).trimmed();
|
||||
QString fix = Varicode::unpackCallsignPrefixSuffix(packed_fix);
|
||||
quint16 num = (packed_11 << 5) | packed_5;
|
||||
|
||||
if(is_prefix){
|
||||
unpacked.append(fix);
|
||||
}
|
||||
unpacked.append(base);
|
||||
if(!is_prefix){
|
||||
unpacked.append(fix);
|
||||
}
|
||||
unpacked.append(QString("%1").arg(num));
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
QString Varicode::packDirectedMessage(const QString &text, const QString &baseCallsign, QString * pCmd, int *n){
|
||||
QString frame;
|
||||
|
||||
auto match = directed_re.match(text);
|
||||
if(match.hasMatch()){
|
||||
QString from = match.captured("from");
|
||||
if(from.isEmpty()){
|
||||
from = callsign;
|
||||
}
|
||||
QString to = match.captured("to");
|
||||
QString cmd = match.captured("cmd");
|
||||
|
||||
bool validToCallsign = basecalls.contains(to) || QRegularExpression(callsign_pattern2).match(to).hasMatch();
|
||||
if(!validToCallsign || !directed_cmds.contains(cmd) || !allowed_cmds.contains(directed_cmds[cmd])){
|
||||
*n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
auto fromBytes = from.toLocal8Bit();
|
||||
auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU());
|
||||
|
||||
quint8 packed_flag = 0;
|
||||
quint32 packed_from = Varicode::packCallsign(from);
|
||||
quint32 packed_to = Varicode::packCallsign(to);
|
||||
|
||||
if(packed_from == 0 || packed_to == 0){
|
||||
*n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
quint8 packed_cmd = directed_cmds[cmd];
|
||||
quint8 packed_extra = fromCRC;
|
||||
|
||||
// [3][28][28][5],[5] = 69
|
||||
auto bits = (
|
||||
Varicode::intToBits(packed_flag, 3) +
|
||||
Varicode::intToBits(packed_from, 28) +
|
||||
Varicode::intToBits(packed_to, 28) +
|
||||
Varicode::intToBits(packed_cmd & 31, 5)
|
||||
);
|
||||
frame = Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_extra & 31);
|
||||
*n = match.captured(0).length();
|
||||
if(!match.hasMatch()){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
return frame;
|
||||
|
||||
QString from = baseCallsign;
|
||||
QString to = match.captured("to");
|
||||
QString cmd = match.captured("cmd");
|
||||
QString num = match.captured("num").trimmed();
|
||||
QString pwr = match.captured("pwr").trimmed().toUpper();
|
||||
|
||||
// validate callsign
|
||||
bool validToCallsign = (to != baseCallsign) && (basecalls.contains(to) || QRegularExpression(compound_callsign_pattern).match(to).hasMatch());
|
||||
if(!validToCallsign){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
// validate command
|
||||
if(!Varicode::isCommandAllowed(cmd)){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
// packing general number...
|
||||
int inum = -31;
|
||||
bool hasnum = false;
|
||||
if(!num.isEmpty()){
|
||||
inum = qMax(-30, qMin(num.toInt(&hasnum, 10), 30));
|
||||
}
|
||||
|
||||
// if we are packing a PWR command, pack pwr as dbm
|
||||
int ipwr = -31;
|
||||
if(!pwr.isEmpty() && cmd.trimmed() == "PWR"){
|
||||
int factor = 1000;
|
||||
if(pwr.endsWith("KW")){
|
||||
factor = 1000000;
|
||||
}
|
||||
else if(pwr.endsWith("MW")){
|
||||
factor = 1;
|
||||
}
|
||||
ipwr = pwr.replace(QRegExp("[KM]?W", Qt::CaseInsensitive), "").toInt() * factor;
|
||||
inum = mwattsToDbm(ipwr) - 30;
|
||||
}
|
||||
|
||||
quint8 packed_is_data = 0;
|
||||
quint8 packed_is_compound = 0;
|
||||
quint8 packed_num_flag = inum < 0 ? 1 : 0;
|
||||
quint32 packed_from = Varicode::packCallsign(from);
|
||||
quint32 packed_to = Varicode::packCallsign(to);
|
||||
|
||||
if(packed_from == 0 || packed_to == 0){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
quint8 packed_cmd = directed_cmds[cmd];
|
||||
quint8 packed_extra = qAbs(inum);
|
||||
|
||||
// [1][1][1][28][28][5],[5] = 69
|
||||
auto bits = (
|
||||
Varicode::intToBits(packed_is_data, 1) +
|
||||
Varicode::intToBits(packed_is_compound, 1) +
|
||||
Varicode::intToBits(packed_num_flag, 1) +
|
||||
Varicode::intToBits(packed_from, 28) +
|
||||
Varicode::intToBits(packed_to, 28) +
|
||||
Varicode::intToBits(packed_cmd % 32, 5)
|
||||
);
|
||||
|
||||
if(pCmd) *pCmd = cmd;
|
||||
if(n) *n = match.captured(0).length();
|
||||
return Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_extra % 32);
|
||||
}
|
||||
|
||||
QStringList Varicode::unpackDirectedMessage(const QString &text){
|
||||
@@ -558,26 +959,101 @@ QStringList Varicode::unpackDirectedMessage(const QString &text){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
// [3][28][28][5],[5] = 69
|
||||
// [1][1][1][28][28][5],[5] = 69
|
||||
auto bits = Varicode::bitsToStr(Varicode::intToBits(Varicode::unpack64bits(text.left(12)), 64));
|
||||
quint8 extra = Varicode::unpack5bits(text.right(1));
|
||||
|
||||
quint8 flag = Varicode::bitsToInt(Varicode::strToBits(bits.left(3)));
|
||||
quint8 is_data = Varicode::bitsToInt(Varicode::strToBits(bits.left(1)));
|
||||
if(is_data != 0){
|
||||
return unpacked;
|
||||
}
|
||||
quint8 is_compound = Varicode::bitsToInt(Varicode::strToBits(bits.mid(1,1)));
|
||||
if(is_compound != 0){
|
||||
return unpacked;
|
||||
}
|
||||
quint8 num_flag = Varicode::bitsToInt(Varicode::strToBits(bits.mid(2,1)));
|
||||
quint32 packed_from = Varicode::bitsToInt(Varicode::strToBits(bits.mid(3, 28)));
|
||||
quint32 packed_to = Varicode::bitsToInt(Varicode::strToBits(bits.mid(31, 28)));
|
||||
quint8 packed_cmd = Varicode::bitsToInt(Varicode::strToBits(bits.mid(59, 5)));
|
||||
|
||||
QString from = Varicode::unpackCallsign(packed_from).trimmed();
|
||||
|
||||
auto fromBytes = from.toLocal8Bit();
|
||||
auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU());
|
||||
if(fromCRC != extra){
|
||||
return unpacked;
|
||||
}
|
||||
QString to = Varicode::unpackCallsign(packed_to).trimmed();
|
||||
QString cmd = directed_cmds.key(packed_cmd % 32);
|
||||
|
||||
unpacked.append(from);
|
||||
unpacked.append(Varicode::unpackCallsign(packed_to).trimmed());
|
||||
unpacked.append(directed_cmds.key(packed_cmd & 31));
|
||||
unpacked.append(to);
|
||||
unpacked.append(cmd);
|
||||
|
||||
int num = (num_flag ? -1 : 1) * extra;
|
||||
if(num != -31){
|
||||
// TODO: jsherer - should we decide which format to use on the command, or something else?
|
||||
if(packed_cmd == directed_cmds[" PWR"]){
|
||||
unpacked.append(Varicode::formatPWR(num + 30));
|
||||
} else if(packed_cmd == directed_cmds[" SNR"]) {
|
||||
unpacked.append(Varicode::formatSNR(num));
|
||||
} else {
|
||||
unpacked.append(QString("%1").arg(num));
|
||||
}
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
QString Varicode::packDataMessage(const QString &input, QString * out, int *n){
|
||||
QString frame;
|
||||
|
||||
// [1][63],[5] = 69
|
||||
quint8 is_data = 1;
|
||||
auto frameBits = (
|
||||
Varicode::intToBits(is_data, 1)
|
||||
);
|
||||
|
||||
int i = 0;
|
||||
|
||||
// we use the escaped table here, so they the escapes and the characters are packed together...
|
||||
foreach(auto charBits, Varicode::huffEncode(hufftableescaped, input)){
|
||||
if(frameBits.length() + charBits.length() < 63){
|
||||
frameBits += charBits;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int pad = 64 - frameBits.length();
|
||||
if(pad){
|
||||
frameBits += Varicode::intToBits(0, pad);
|
||||
}
|
||||
|
||||
frame = Varicode::pack64bits(Varicode::bitsToInt(frameBits)) + Varicode::pack5bits(pad % 32);
|
||||
*n = i;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
QString Varicode::unpackDataMessage(const QString &text){
|
||||
QString unpacked;
|
||||
|
||||
if(text.length() < 13){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
auto bits = Varicode::intToBits(Varicode::unpack64bits(text.left(12)), 64);
|
||||
quint8 pad = Varicode::unpack5bits(text.right(1));
|
||||
|
||||
quint8 is_data = (int)bits.at(0);
|
||||
if(is_data != 1){
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
// pop off the is_data bit
|
||||
bits.removeAt(0);
|
||||
|
||||
// huff decode the bits (without escapes)
|
||||
unpacked = Varicode::huffDecode(hufftable, bits, pad);
|
||||
|
||||
// then... unescape special characters
|
||||
unpacked = Varicode::huffUnescape(unpacked);
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
+44
-5
@@ -11,18 +11,41 @@
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
|
||||
class Varicode
|
||||
{
|
||||
public:
|
||||
enum FrameType{
|
||||
FT8 = 0, // [000]
|
||||
FT8Fox = 1, // [001]
|
||||
FT8Call = 2, // [010]
|
||||
FT8CallLast = 3, // [011] <- used to indicate last frame in transmission
|
||||
FT8CallReservedA = 4, // [100]
|
||||
FT8CallReservedB = 5, // [101]
|
||||
FT8CallReservedC = 6, // [110]
|
||||
FT8CallReservedD = 7, // [111]
|
||||
};
|
||||
|
||||
//Varicode();
|
||||
|
||||
static QString formatSNR(int snr);
|
||||
static QString formatPWR(int dbm);
|
||||
|
||||
static QString checksum16(QString const &input);
|
||||
static bool checksum16Valid(QString const &checksum, QString const &input);
|
||||
|
||||
static QString checksum32(QString const &input);
|
||||
static bool checksum32Valid(QString const &checksum, QString const &input);
|
||||
|
||||
static QStringList parseCallsigns(QString const &input);
|
||||
static QStringList parseGrids(QString const &input);
|
||||
|
||||
static QList<QVector<bool>> huffEncode(QString const& text);
|
||||
static QVector<bool> huffFlatten(QList<QVector<bool>> &list);
|
||||
static QString huffDecode(QVector<bool> const& bitvec);
|
||||
static QList<QVector<bool>> huffEncode(const QMap<QChar, QString> &huff, QString const& text);
|
||||
static QString huffDecode(const QMap<QChar, QString> &huff, QVector<bool> const& bitvec, int pad=0);
|
||||
|
||||
static QString huffUnescape(QString const &input);
|
||||
static QString huffEscape(QString const &input);
|
||||
static QSet<QChar> huffValidChars();
|
||||
static bool huffShouldEscape(QString const &input);
|
||||
|
||||
static QVector<bool> bytesToBits(char * bitvec, int n);
|
||||
static QVector<bool> strToBits(QString const& bitvec);
|
||||
@@ -31,10 +54,14 @@ public:
|
||||
static QVector<bool> intToBits(quint64 value, int expected=0);
|
||||
static quint64 bitsToInt(QVector<bool> const value);
|
||||
static quint64 bitsToInt(QVector<bool>::ConstIterator start, int n);
|
||||
static QVector<bool> bitsListToBits(QList<QVector<bool>> &list);
|
||||
|
||||
static quint8 unpack5bits(QString const& value);
|
||||
static QString pack5bits(quint8 packed);
|
||||
|
||||
static quint8 unpack6bits(QString const& value);
|
||||
static QString pack6bits(quint8 packed);
|
||||
|
||||
static quint16 unpack16bits(QString const& value);
|
||||
static QString pack16bits(quint16 packed);
|
||||
|
||||
@@ -44,14 +71,26 @@ public:
|
||||
static quint64 unpack64bits(QString const& value);
|
||||
static QString pack64bits(quint64 packed);
|
||||
|
||||
static quint32 packCallsignPrefixSuffix(QString const& value);
|
||||
static QString unpackCallsignPrefixSuffix(quint32 packed);
|
||||
|
||||
static quint32 packCallsign(QString const& value);
|
||||
static QString unpackCallsign(quint32 value);
|
||||
|
||||
static quint16 packGrid(QString const& value);
|
||||
static QString unpackGrid(quint16 value);
|
||||
|
||||
static QString packDirectedMessage(QString const& text, QString const& callsign, int *n);
|
||||
static bool isCommandAllowed(const QString &cmd);
|
||||
static bool isCommandBuffered(const QString &cmd);
|
||||
|
||||
static QString packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num);
|
||||
static QStringList unpackCompoundMessage(const QString &text);
|
||||
|
||||
static QString packDirectedMessage(QString const& text, QString const& callsign, QString * pCmd, int *n);
|
||||
static QStringList unpackDirectedMessage(QString const& text);
|
||||
|
||||
static QString packDataMessage(QString const& text, QString *out, int *n);
|
||||
static QString unpackDataMessage(QString const& text);
|
||||
};
|
||||
|
||||
#endif // VARICODE_H
|
||||
|
||||
Reference in New Issue
Block a user