Compare commits

...

90 Commits

Author SHA1 Message Date
Jordan Sherer c0c4693782 Widened the log qso dialog 2018-07-21 03:55:45 -04:00
Jordan Sherer 9223d3da40 Version bump to 0.2.0 2018-07-21 03:52:48 -04:00
Jordan Sherer 83e3f5ddbc SNR button sends a directed SNR message 2018-07-21 03:52:13 -04:00
Jordan Sherer 0f4057aa97 Make sure to clear seen beacon cache when changing bands 2018-07-21 03:45:56 -04:00
Jordan Sherer 591629e369 Disable heard list beaconing for now 2018-07-21 03:33:00 -04:00
Jordan Sherer 4a96ab3b13 Beaconing of heard list 2018-07-21 03:32:07 -04:00
Jordan Sherer e955cff24f Make sure selected call exists before using it 2018-07-21 02:31:30 -04:00
Jordan Sherer fe405cfba8 Added ability to encode power into dbm 2018-07-21 02:18:15 -04:00
Jordan Sherer 92117aa791 Added SNR directed command 2018-07-20 23:17:49 -04:00
Jordan Sherer d4c2d9a871 Log directed FT8Call messages to PSKReporter 2018-07-20 23:09:17 -04:00
Jordan Sherer b2e2b91d31 Refactored get current frequency 2018-07-20 22:17:41 -04:00
Jordan Sherer fa864c50cd Removed CQ DX from allcall 2018-07-20 16:23:48 -04:00
Jordan Sherer 1c6d1babe6 Set the QTH button to drop in the station location if provided 2018-07-20 16:11:21 -04:00
Jordan Sherer ac27d1a9b6 Brighter green for slider 2018-07-20 16:10:59 -04:00
Jordan Sherer 28eb082655 Further fix format of SNR 2018-07-20 16:04:14 -04:00
Jordan Sherer 9a945c156d Proper SNR formatting +00 +30 -09 2018-07-20 11:40:55 -04:00
Jordan Sherer 83c742f7ec Double click band activity now transfers the received message into the directed activity window and further transmissions on the offset will be added to the window automatically 2018-07-20 10:54:00 -04:00
Jordan Sherer 7a788c05c8 Added configuration options for controlling aging of the callsign and band activity windows 2018-07-20 10:13:12 -04:00
Jordan Sherer 7b409a6ff4 Fix varicode bug with empty messages 2018-07-20 09:38:34 -04:00
Jordan Sherer 87a631f5f0 Reorder directed and macro buttons 2018-07-20 09:38:10 -04:00
Jordan Sherer efd6b54ba7 Updated slider handle color 2018-07-20 09:00:36 -04:00
Jordan Sherer 553f2400e5 Updated text decoding to support more commands as well as numerical options for those commands 2018-07-19 23:14:11 -04:00
Jordan Sherer 5c84e79e5b Updated label text for qth message 2018-07-19 10:36:43 -04:00
Jordan Sherer 052b81ec8f Added 73 action and more appropriate all call responses. Added better handling of end of transmissions. 2018-07-19 10:35:00 -04:00
Jordan Sherer 7ecc550bc2 Added station qth to configuration options 2018-07-19 10:34:19 -04:00
Jordan Sherer b8267372e4 Added varicode encoding of messages
What this does is allow us to pack more than 13 characters in a single
transmission frame. Optimized using a Huffman encoding using weights of
alphabetical frequency, this will often allow us to send less than 5 bits
per character.
2018-07-19 03:44:08 -04:00
Jordan Sherer 512dffabf4 Added station message command processing 2018-07-19 02:09:19 -04:00
Jordan Sherer 50a3a56d2d Added station message to configuration 2018-07-19 00:39:31 -04:00
Jordan Sherer deb228948d Added ability to display when we receive the final transmission frame of a message 2018-07-18 16:45:27 -04:00
Jordan Sherer 58032b6ae4 Restrict low offset when not in split mode 2018-07-18 14:26:45 -04:00
Jordan Sherer 7845736c05 Smarter beacon scheduling for when editing a message or have recently transmitted 2018-07-18 09:04:58 -04:00
Jordan Sherer 77eb65d6b3 Display callsign prefix for transmitted directed messages 2018-07-16 09:14:28 -04:00
Jordan Sherer 252c21b818 Bump version to 0.1.1 2018-07-16 12:07:39 +00:00
Jordan Sherer 82c3b23e44 Merge branch 'ft8call-reorg' of https://bitbucket.org/widefido/wsjtx into ft8call-reorg 2018-07-16 11:59:06 +00:00
Jordan Sherer 5764170975 Disabled contest mode message packing which causes crashes when typing R 2018-07-16 11:58:41 +00:00
Jordan Sherer a2c85256e8 Only throttle ALLCALLs 2018-07-15 21:00:12 -04:00
Jordan Sherer 9419383ab0 Fixes for windows 2018-07-15 17:58:16 -04:00
Jordan Sherer 4c63129876 Fixed bug in beacon bumping 2018-07-15 16:48:27 -04:00
Jordan Sherer f2b876df49 Bump version to July 30 2018-07-15 16:12:57 -04:00
Jordan Sherer f229019ef5 Updated buttons while frame count is queued and is transmitting 2018-07-15 15:58:07 -04:00
Jordan Sherer 3260bb7b89 Use standard FT8 packing for standard FT8 messages, but apply a directed header for those messages that _can_ be directed 2018-07-15 15:43:29 -04:00
Jordan Sherer 2832572741 Process directed free text messages. Bump beacon on transmit. 2018-07-15 13:03:16 -04:00
Jordan Sherer 28df33b218 Better parsing of selected callsign. Blur edit box while transmitting 2018-07-15 11:00:57 -04:00
Jordan Sherer 6ee0820659 Disabled colors that are not displayed 2018-07-15 10:22:21 -04:00
Jordan Sherer 2c73ccde9b Huffman frame packing 2018-07-15 09:40:46 -04:00
Jordan Sherer 90ce372081 Added CRC-5 to directed messages to confirm message is valid 2018-07-14 22:05:08 -04:00
Jordan Sherer 15d75f7397 Try not to allocate every time... 2018-07-14 18:10:07 -04:00
Jordan Sherer f47c1a5024 Updated comment 2018-07-14 17:10:59 -04:00
Jordan Sherer c74ed5135b Parse callsigns from standard messages 2018-07-14 17:09:12 -04:00
Jordan Sherer b08ecd21e6 PSKReporter shouldnt include the rigname in the name. Added a cache to prevent allcall overrun 2018-07-14 09:26:39 -04:00
Jordan Sherer 1c98d47718 Bump EOL 2018-07-13 22:26:51 -04:00
Jordan Sherer 46e11f8d00 Complete rename to ft8call executable 2018-07-13 22:22:16 -04:00
Jordan Sherer 1e0a93e076 Update Help menu and Copyrights 2018-07-13 22:13:36 -04:00
Jordan Sherer f77f139abb Fix typo 2018-07-13 22:13:18 -04:00
Jordan Sherer 2ae74d8cde Added grid packing to varicode 2018-07-13 21:59:54 -04:00
Jordan Sherer 369b5fd73a Better beacon scheduling when receiving a directed message 2018-07-13 21:59:44 -04:00
Jordan Sherer 4de968d2f0 Try to prevent false directed decodes 2018-07-13 21:58:43 -04:00
Jordan Sherer 09b12701b5 Updated table call view. Added ALLCALL 2018-07-13 15:45:00 -04:00
Jordan Sherer d00eee440c Fixed accesses of invalid memory 2018-07-13 15:44:48 -04:00
Jordan Sherer c1df21f940 Better QSlider to match systems 2018-07-13 15:09:29 -04:00
Jordan Sherer 9117ad5381 New slider style 2018-07-13 14:43:57 -04:00
Jordan Sherer a96216e2f8 Refactored directed code into decoded text so the rest of the app can use it as is. 2018-07-13 10:32:58 -04:00
Jordan Sherer 4140114d8d Remove revision from PSKReporter report 2018-07-13 09:01:01 -04:00
Jordan Sherer bd34bf104f Bump version 2018-07-13 04:46:02 -04:00
Jordan Sherer 6a265efe48 Added command processing, code cleanup, new parsing of messages to be sent, etc. 2018-07-13 04:44:41 -04:00
Jordan Sherer 983790a3ff Added query toolbar button 2018-07-13 04:43:22 -04:00
Jordan Sherer 43b65d5ca9 Pack and unpack directed message commands naively 2018-07-13 04:42:23 -04:00
Jordan Sherer 8bc61902ac Added a priority queue 2018-07-13 02:44:31 -04:00
Jordan Sherer 4e0326ad6d Added callsign packing 2018-07-13 00:55:48 -04:00
Jordan Sherer 808782b965 Working through huffman interface 2018-07-12 20:31:45 -04:00
Jordan Sherer 707f577f31 Added huffman encoding utility 2018-07-12 18:02:54 -04:00
Jordan Sherer 705244786e Add bits accessor for decodedtext 2018-07-12 16:35:00 -04:00
Jordan Sherer 4e66e92671 Remove debug statement 2018-07-12 16:25:06 -04:00
Jordan Sherer 283e7fae77 Beacon every hour or less 2018-07-12 16:24:42 -04:00
Jordan Sherer 441936111a Output i3bits in FT8 decoder from JT9 2018-07-12 16:19:33 -04:00
Jordan Sherer f81a954d1d Ensure 10Hz offset for FT8 reception 2018-07-12 16:18:54 -04:00
Jordan Sherer 23c1730148 Disabled callsign parsing until we are ready 2018-07-12 15:23:12 -04:00
Jordan Sherer 05257d287f Parse callsigns in QSO and add to call panel 2018-07-12 15:14:41 -04:00
Jordan Sherer 64b5ebbdd2 Updated to refresh rx activity at least once per cycle 2018-07-12 14:15:18 -04:00
Jordan Sherer 91d6836fa5 Updated call activity detail 2018-07-12 10:35:41 -04:00
Jordan Sherer dd3613b214 Added bit packing to varicode 2018-07-12 09:54:56 -04:00
Jordan Sherer c96522c11b Added the start of varicode 2018-07-11 23:09:22 -04:00
Jordan Sherer 564b93f307 Removed unused count function 2018-07-11 22:53:21 -04:00
Jordan Sherer 04eaf61c13 Fixed the size of the top toolbar 2018-07-11 22:31:55 -04:00
Jordan Sherer b800dac9ce Contest should be false 2018-07-11 22:13:53 -04:00
Jordan Sherer 2cb78f5414 Extracted isMyCallIncluded 2018-07-11 10:40:29 -04:00
Jordan Sherer 494459ee11 Extracted isRecentlyDirected 2018-07-11 10:30:20 -04:00
Jordan Sherer 1cddbdf91e Experimenting with age in the band activity window 2018-07-10 22:32:35 -04:00
Jordan Sherer 74ea603801 Added clearAll action 2018-07-10 15:35:05 -04:00
Jordan Sherer 16854627d9 Updated PSKReporter spots to use FT8Call instead of WSJT-X for the application name 2018-07-10 15:23:52 -04:00
21 changed files with 4718 additions and 863 deletions
+7 -6
View File
@@ -9,8 +9,8 @@ set (CPACK_PACKAGE_CONTACT "@PROJECT_CONTACT@")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "@PROJECT_SUMMARY_DESCRIPTION@")
set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING")
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set (CPACK_PACKAGE_EXECUTABLES wsjtx-ft8call "@PROJECT_NAME@")
set (CPACK_CREATE_DESKTOP_LINKS wsjtx-ft8call)
set (CPACK_PACKAGE_EXECUTABLES ft8call "@PROJECT_NAME@")
set (CPACK_CREATE_DESKTOP_LINKS ft8call)
set (CPACK_STRIP_FILES TRUE)
#
@@ -21,6 +21,7 @@ set (CPACK_STRIP_FILES TRUE)
#set (CPACK_COMPONENT_RUNTIME_DESCRIPTION "@WSJTX_DESCRIPTION_SUMMARY@")
if (CPACK_GENERATOR MATCHES "NSIS")
set (CPACK_SET_DESTDIR FALSE)
set (CPACK_STRIP_FILES FALSE) # breaks Qt packaging on Windows
set (CPACK_NSIS_INSTALL_ROOT "C:\\FT8Call")
@@ -37,13 +38,13 @@ if (CPACK_GENERATOR MATCHES "NSIS")
"@PROJECT_HOMEPAGE@" "@PROJECT_NAME@ Web Site"
)
# Use the icon from wsjtx for add-remove programs
set (CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\wsjtx-ft8call.exe")
set (CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\ft8call.exe")
set (CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
set (CPACK_NSIS_HELP_LINK "@PROJECT_MANUAL_DIRECTORY_URL@/@PROJECT_MANUAL@")
set (CPACK_NSIS_URL_INFO_ABOUT "@PROJECT_HOMEPAGE@")
set (CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}")
set (CPACK_NSIS_MUI_FINISHPAGE_RUN "wsjtx-ft8call.exe")
set (CPACK_NSIS_MUI_FINISHPAGE_RUN "ft8call.exe")
set (CPACK_NSIS_MODIFY_PATH ON)
endif ()
@@ -67,9 +68,9 @@ if ("${CPACK_GENERATOR}" STREQUAL "WIX")
# Reset CPACK_PACKAGE_VERSION to deal with WiX restriction.
# But the file names still use the full CMake_VERSION value:
set (CPACK_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME}-@wsjtx-ft8call_VERSION@-${CPACK_SYSTEM_NAME}")
"${CPACK_PACKAGE_NAME}-@ft8call_VERSION@-${CPACK_SYSTEM_NAME}")
set (CPACK_SOURCE_PACKAGE_FILE_NAME
"${CPACK_PACKAGE_NAME}-@wsjtx-ft8call_VERSION@-Source")
"${CPACK_PACKAGE_NAME}-@ft8call_VERSION@-Source")
if (NOT CPACK_WIX_SIZEOF_VOID_P)
set (CPACK_WIX_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@")
+21 -20
View File
@@ -22,7 +22,7 @@ Change this to the newest SDK available that you can install on your system (10.
Do not override this if you intend to build an official deployable installer.")
endif (APPLE)
project (wsjtx-ft8call C CXX Fortran)
project (ft8call C CXX Fortran)
#
# CMake policies
@@ -303,6 +303,7 @@ set (wsjtx_CXXSRCS
astro.cpp
messageaveraging.cpp
WsprTxScheduler.cpp
varicode.cpp
mainwindow.cpp
Configuration.cpp
main.cpp
@@ -1298,7 +1299,7 @@ else (${OPENMP_FOUND} OR APPLE)
endif (${OPENMP_FOUND} OR APPLE)
# build the main application
add_executable (wsjtx-ft8call MACOSX_BUNDLE
add_executable (ft8call MACOSX_BUNDLE
${wsjtx_CXXSRCS}
${wsjtx_GENUISRCS}
wsjtx.rc
@@ -1307,10 +1308,10 @@ add_executable (wsjtx-ft8call MACOSX_BUNDLE
)
if (WSJT_CREATE_WINMAIN)
set_target_properties (wsjtx-ft8call PROPERTIES WIN32_EXECUTABLE ON)
set_target_properties (ft8call PROPERTIES WIN32_EXECUTABLE ON)
endif (WSJT_CREATE_WINMAIN)
set_target_properties (wsjtx-ft8call PROPERTIES
set_target_properties (ft8call PROPERTIES
MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
MACOSX_BUNDLE_BUNDLE_VERSION ${wsjtx_VERSION}
@@ -1321,27 +1322,27 @@ set_target_properties (wsjtx-ft8call PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER "org.k1jt.wsjtx"
)
target_include_directories (wsjtx-ft8call PRIVATE ${FFTW3_INCLUDE_DIRS})
target_include_directories (ft8call PRIVATE ${FFTW3_INCLUDE_DIRS})
if (APPLE)
target_link_libraries (wsjtx-ft8call wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
target_link_libraries (ft8call wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
else ()
target_link_libraries (wsjtx-ft8call wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
target_link_libraries (ft8call wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
if (OpenMP_C_FLAGS)
set_target_properties (wsjtx-ft8call PROPERTIES
set_target_properties (ft8call PROPERTIES
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
LINK_FLAGS "${OpenMP_C_FLAGS}"
)
endif ()
set_target_properties (wsjtx-ft8call PROPERTIES
set_target_properties (ft8call PROPERTIES
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
)
if (WIN32)
set_target_properties (wsjtx-ft8call PROPERTIES
set_target_properties (ft8call PROPERTIES
LINK_FLAGS -Wl,--stack,16777216
)
endif ()
endif ()
qt5_use_modules (wsjtx-ft8call SerialPort) # not sure why the interface link library syntax above doesn't work
qt5_use_modules (ft8call SerialPort) # not sure why the interface link library syntax above doesn't work
# make a library for WSJT-X UDP servers
# add_library (wsjtx_udp SHARED ${UDP_library_CXXSRCS})
@@ -1386,18 +1387,18 @@ endif (WSJT_CREATE_WINMAIN)
if (UNIX)
if (NOT WSJT_SKIP_MANPAGES)
add_subdirectory (manpages)
add_dependencies (wsjtx-ft8call manpages)
add_dependencies (ft8call manpages)
endif (NOT WSJT_SKIP_MANPAGES)
if (NOT APPLE)
add_subdirectory (debian)
add_dependencies (wsjtx-ft8call debian)
add_dependencies (ft8call debian)
endif (NOT APPLE)
endif (UNIX)
#
# installation
#
install (TARGETS wsjtx-ft8call
install (TARGETS ft8call
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
BUNDLE DESTINATION . COMPONENT runtime
)
@@ -1487,11 +1488,11 @@ add_custom_target (uninstall
# creates svnversion.h using cmake script
add_custom_target (revisiontag
COMMAND ${CMAKE_COMMAND} -D SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -D OUTPUT_DIR=${PROJECT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/getsvn.cmake
COMMENT "Generating Subversion revision information"
VERBATIM
)
# add_custom_target (revisiontag
# COMMAND ${CMAKE_COMMAND} -D SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -D OUTPUT_DIR=${PROJECT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/CMake/getsvn.cmake
# COMMENT "Generating Subversion revision information"
# VERBATIM
# )
# explicitly say that the wsjt_qt depends on custom target, this is
# done indirectly so that the revisiontag target gets built exactly
# once per build
@@ -1511,7 +1512,7 @@ if (NOT WIN32 AND NOT APPLE)
# install a desktop file so wsjtx appears in the application start
# menu with an icon
install (
FILES wsjtx-ft8call.desktop message_aggregator.desktop
FILES ft8call.desktop message_aggregator.desktop
DESTINATION share/applications
#COMPONENT runtime
)
+61 -1
View File
@@ -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"
@@ -430,6 +430,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 +531,10 @@ private:
// configuration fields that we publish
QString my_callsign_;
QString my_grid_;
QString my_station_;
QString my_qth_;
int callsign_aging_;
int activity_aging_;
QColor color_CQ_;
QColor next_color_CQ_;
QColor color_MyCall_;
@@ -817,6 +823,26 @@ QString Configuration::my_grid() const
return the_grid;
}
QString Configuration::my_station() const
{
return m_->my_station_;
}
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 +981,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 ());
@@ -1129,6 +1157,10 @@ void Configuration::impl::initialize_models ()
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()));
@@ -1247,6 +1279,10 @@ 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();
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();
@@ -1433,6 +1469,10 @@ void Configuration::impl::write_settings ()
settings_->setValue ("MyCall", my_callsign_);
settings_->setValue ("MyGrid", my_grid_);
settings_->setValue ("MyStation", my_station_);
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_);
@@ -1884,6 +1924,10 @@ 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_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 ();
@@ -2167,6 +2211,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 ());
+4
View File
@@ -96,6 +96,10 @@ public:
QString my_callsign () const;
QString my_grid () const;
QString my_station () 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;
+601 -425
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,6 +1,6 @@
# Version number components
set (WSJTX_VERSION_MAJOR 0)
set (WSJTX_VERSION_MINOR 0)
set (WSJTX_VERSION_PATCH 4)
set (WSJTX_VERSION_MINOR 2)
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
+1699
View File
File diff suppressed because it is too large Load Diff
+67 -1
View File
@@ -4,6 +4,8 @@
#include <QRegularExpression>
#include <QDebug>
#include <varicode.h>
extern "C" {
bool stdmsg_(char const * msg, bool contest_mode, char const * mygrid, fortran_charlen_t, fortran_charlen_t);
}
@@ -50,8 +52,72 @@ 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_){
bool unpacked = false;
if(!unpacked){
unpacked = tryUnpackDirected();
}
if(!unpacked){
unpacked = tryUnpackData();
}
}
}
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(QChar()));
}
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
{
+18 -7
View File
@@ -10,6 +10,7 @@
#define DECODEDTEXT_H
#include <QString>
#include <QStringList>
@@ -31,14 +32,21 @@ class DecodedText
public:
explicit DecodedText (QString const& message, bool, QString const& my_grid);
QString string() const { return string_; };
QStringList messageWords () const;
int indexOf(QString s) const { return string_.indexOf(s); };
int indexOf(QString s, int i) const { return string_.indexOf(s,i); };
QString mid(int f, int t) const { return string_.mid(f,t); };
QString left(int i) const { return string_.left(i); };
bool tryUnpackDirected();
bool tryUnpackData();
void clear() { string_.clear(); };
QStringList directedMessage() const { return directed_; }
bool isDirectedMessage() const { return !directed_.isEmpty(); }
QString string() const { return string_; }
QString message() const { return message_; }
QStringList messageWords () const;
int indexOf(QString s) const { return string_.indexOf(s); }
int indexOf(QString s, int i) const { return string_.indexOf(s,i); }
QString mid(int f, int t) const { return string_.mid(f,t); }
QString left(int i) const { return string_.left(i); }
void clear() { string_.clear(); }
QString CQersCall() const;
@@ -49,6 +57,8 @@ public:
bool isLowConfidence () const;
int frequencyOffset() const; // hertz offset from the tuned dial or rx frequency, aka audio frequency
int snr() const;
bool hasBits() const { return !string_.right(5).trimmed().isEmpty(); }
int bits() const { return string_.right(5).trimmed().toShort(); }
float dt() const;
// find and extract any report. Returns true if this is a standard message
@@ -75,6 +85,7 @@ private:
column_mode = 19,
column_qsoText = 22 };
QStringList directed_;
QString string_;
int padding_;
bool contest_mode_;
+2 -2
View File
@@ -1,8 +1,8 @@
[Desktop Entry]
Version=1.0
Name=wsjtx-ft8call
Name=ft8call
Comment=Amateur Radio Weak Signal Operating
Exec=wsjtx-ft8call
Exec=ft8call
Icon=wsjtx_icon
Terminal=false
X-MultipleArgs=false
+6
View File
@@ -380,6 +380,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
if(i3bit.eq.1) decoded(57:)=0
call extractmessage174(decoded,message,ncrcflag)
decoded=decoded0
! This needs fixing for messages with i3bit=1:
call genft8(message,mygrid6,bcontest,i3bit,msgsent,msgbits,itone)
if(lsubtract) call subtractft8(dd0,itone,f1,xdt2)
@@ -397,6 +398,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
if(.not.nagain) xsnr=xsnr2
if(xsnr .lt. -24.0) xsnr=-24.0
if(i3bit.eq.1) then
do i=1,12
i1hiscall(i)=ichar(hiscall12(i:i))
@@ -431,6 +433,10 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
else
msg37=message//' '
endif
if(i3bit.gt.1) then
msg37(22:22) = char(48 + i3bit)
endif
return
endif
+3 -1
View File
@@ -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
+2 -2
View File
@@ -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">
+821 -192
View File
File diff suppressed because it is too large Load Diff
+36 -6
View File
@@ -39,6 +39,7 @@
#include "MessageBox.hpp"
#include "NetworkAccessManager.hpp"
#include "qorderedmap.h"
#include "varicode.h"
#define NUM_JT4_SYMBOLS 206 //(72+31)*2, embedded sync
#define NUM_JT65_SYMBOLS 126 //63 data + 63 sync
@@ -121,7 +122,7 @@ public slots:
void clearActivity();
int logRxTxMessageText(QDateTime date, bool isFree, QString text, int freq, bool tx, int block=-1);
void addMessageText(QString text);
void addMessageText(QString text, bool clear=false);
void resetMessage();
void resetMessageUI();
void createMessage(QString const& text);
@@ -235,10 +236,12 @@ private slots:
void on_rbFreeText_clicked(bool checked);
void on_clearAction_triggered(QObject * sender);
void on_cqMacroButton_clicked();
void on_deMacroButton_clicked();
void on_qtcMacroButton_clicked();
void on_replyMacroButton_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);
void on_tableWidgetRXAll_cellDoubleClicked(int row, int col);
@@ -250,13 +253,14 @@ 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);
QString parseFT8Message(QString input);
int countFreeTextMsgs(QString input);
int currentFreq();
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();
QString calculateDistance(QString const& grid);
void on_rptSpinBox_valueChanged(int n);
@@ -640,10 +644,21 @@ private:
int snr;
};
struct CommandDetail
{
QString from;
QString to;
QString cmd;
int freq;
QDateTime utcTimestamp;
int snr;
};
struct ActivityDetail
{
bool isFree;
bool isLowConfidence;
int bits;
QString firstCall;
QString secondCall;
int freq;
@@ -655,19 +670,27 @@ private:
struct RXDetail
{
bool isFree;
bool isLowConfidence;
int bits;
int freq;
QString text;
QDateTime utcTimestamp;
};
bool m_rxDirty;
int m_txFrameCount;
QDateTime m_lastTxTime;
QQueue<QString> m_txFrameQueue;
QQueue<RXDetail> m_rxFrameQueue;
QQueue<CommandDetail> m_rxCommandQueue;
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<QString, CallDetail> m_callActivity; // call -> (last freq, last timestamp)
QSet<QString> m_callSeenBeacon; // call
QMap<QString,FoxQSO> m_foxQSO;
QMap<QString,QString> m_loggedByFox;
@@ -732,6 +755,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);
@@ -740,7 +764,13 @@ private:
void replayDecodes ();
void postDecode (bool is_new, QString const& message);
void displayTransmit();
void displayActivity();
void updateButtonDisplay();
bool isMyCallIncluded(QString const &text);
bool isAllCallIncluded(QString const &text);
QString callsignSelected();
bool isRecentOffset(int offset);
bool isDirectedOffset(int offset);
void displayActivity(bool force=false);
void postWSPRDecode (bool is_new, QStringList message_parts);
void enable_DXCC_entity (bool on);
void switch_mode (Mode);
+269 -140
View File
@@ -60,13 +60,13 @@
<property name="minimumSize">
<size>
<width>0</width>
<height>140</height>
<height>110</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>120</height>
<height>110</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_16">
@@ -93,6 +93,18 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>110</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>110</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
@@ -236,7 +248,7 @@ o</string>
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>2</number>
<number>0</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout_15">
<property name="spacing">
@@ -262,7 +274,7 @@ o</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>20</height>
</size>
</property>
</spacer>
@@ -270,7 +282,7 @@ o</string>
<item>
<widget class="QLabel" name="labUTC">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -284,10 +296,10 @@ color : white;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>2</number>
@@ -296,7 +308,7 @@ color : white;
<number>0</number>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt; 2015 Jun 17 &lt;/p&gt;&lt;p align=&quot;center&quot;&gt; 01:23:45 &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;2015 Jun 17 &lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;01:23:45 &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -337,7 +349,7 @@ color : white;
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>20</height>
</size>
</property>
</spacer>
@@ -591,31 +603,39 @@ background-color: #00ff00;
</widget>
</item>
<item row="1" column="2">
<widget class="QWidget" name="monitorContainer" native="true">
<widget class="QPushButton" name="monitorButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
<widget class="QWidget" name="monitorContainer" native="true">
<widget class="QPushButton" name="monitorButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>40</width>
<height>30</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
font-family: helvetica;
font-size: 9pt;
font-weight: bold;
@@ -641,41 +661,49 @@ background-color: #00ff00;
QPushButton:checked {
background-color: #00ff00;
}</string>
</property>
<property name="text">
<string>RX</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="monitorTxButton">
<property name="visible">
<bool>false</bool>
</property>
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
</property>
<property name="text">
<string>RX</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="monitorTxButton">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>40</width>
<height>30</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>30</height>
</size>
</property>
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
font-family: helvetica;
font-size: 9pt;
font-weight: bold;
@@ -690,15 +718,15 @@ max-width:40px;;
background-color: yellow;
}
</string>
</property>
<property name="text">
<string>TX</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</property>
<property name="text">
<string>TX</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="tuneButton">
@@ -799,7 +827,10 @@ background-color: yellow;
</font>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
@@ -895,7 +926,7 @@ background-color: yellow;
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color:#ffeaa7;</string>
<string notr="true">QTextEdit { background-color:#ffeaa7; }</string>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
@@ -925,6 +956,11 @@ background-color: yellow;
QTextEdit[readOnly=&quot;true&quot;] {
background:#EEE;
font-style:italic;
}
QTextEdit[transmitting=&quot;true&quot;] {
background:#EEE;
font-style:italic;
}</string>
</property>
<property name="acceptRichText">
@@ -941,6 +977,9 @@ QTextEdit[readOnly=&quot;true&quot;] {
<pointsize>12</pointsize>
</font>
</property>
<property name="autoScroll">
<bool>false</bool>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
@@ -957,7 +996,7 @@ QTextEdit[readOnly=&quot;true&quot;] {
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideNone</enum>
<enum>Qt::ElideRight</enum>
</property>
<property name="gridStyle">
<enum>Qt::DotLine</enum>
@@ -968,9 +1007,12 @@ QTextEdit[readOnly=&quot;true&quot;] {
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<attribute name="horizontalHeaderDefaultSectionSize">
<number>30</number>
</attribute>
<attribute name="horizontalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
@@ -987,6 +1029,16 @@ QTextEdit[readOnly=&quot;true&quot;] {
<string>Age</string>
</property>
</column>
<column>
<property name="text">
<string>SNR</string>
</property>
</column>
<column>
<property name="text">
<string>Grid</string>
</property>
</column>
<column>
<property name="text">
<string>Distance</string>
@@ -1026,7 +1078,7 @@ QTextEdit[readOnly=&quot;true&quot;] {
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="10">
<item row="1" column="12">
<widget class="QPushButton" name="stopTxButton">
<property name="minimumSize">
<size>
@@ -1039,49 +1091,23 @@ QTextEdit[readOnly=&quot;true&quot;] {
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="cqMacroButton">
<property name="minimumSize">
<item row="1" column="10">
<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>0</width>
<height>30</height>
<width>40</width>
<height>20</height>
</size>
</property>
<property name="text">
<string>CQ</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</spacer>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="qthMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>QTH</string>
</property>
</widget>
</item>
<item row="1" column="7">
<widget class="QPushButton" name="macrosMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>Macros</string>
</property>
</widget>
</item>
<item row="1" column="9">
<item row="1" column="11">
<widget class="QPushButton" name="startTxButton">
<property name="enabled">
<bool>false</bool>
@@ -1108,6 +1134,35 @@ background:yellow;
</property>
</widget>
</item>
<item row="1" column="8">
<widget class="QPushButton" name="macrosMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>Macros</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="qtcMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="visible">
<bool>true</bool>
</property>
<property name="text">
<string>QTC</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="replyMacroButton">
<property name="minimumSize">
@@ -1117,12 +1172,12 @@ background:yellow;
</size>
</property>
<property name="text">
<string>REPLY</string>
<string>Reply</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="deMacroButton">
<item row="1" column="5">
<widget class="QPushButton" name="qthMacroButton">
<property name="minimumSize">
<size>
<width>0</width>
@@ -1130,25 +1185,25 @@ background:yellow;
</size>
</property>
<property name="text">
<string>DE</string>
<string>QTH</string>
</property>
</widget>
</item>
<item row="1" column="8">
<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">
<item row="1" column="0">
<widget class="QPushButton" name="cqMacroButton">
<property name="minimumSize">
<size>
<width>40</width>
<height>20</height>
<width>0</width>
<height>30</height>
</size>
</property>
</spacer>
<property name="text">
<string>CQ</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QPushButton" name="snrMacroButton">
@@ -1163,6 +1218,19 @@ background:yellow;
</property>
</widget>
</item>
<item row="1" column="9">
<widget class="QPushButton" name="queryButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>Directed</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_5">
@@ -1349,19 +1417,53 @@ background:yellow;
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="leftMargin">
<number>4</number>
</property>
<item>
<widget class="QSlider" name="outAttenuation">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="toolTip">
<string>Adjust Tx audio level</string>
</property>
<property name="styleSheet">
<string notr="true">selection-background-color: rgb(70, 70, 70);</string>
<string notr="true">QSlider {
selection-background-color: rgb(170, 170, 170);
}
QSlider::groove:vertical{
width:60px;
/*background-color:transparent;*/
}
QSlider::handle:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #00FF00, stop:1 #27ae60);
border:1px solid #179e50;
width:30px;
height:30px;
margin:-2px -8px;
border-radius:2px;
}
QSlider::handle:vertical:hover {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #2ecc71, stop:1 #00FF00);
}</string>
</property>
<property name="maximum">
<number>450</number>
</property>
<property name="value">
<number>0</number>
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -4046,8 +4148,11 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionAbout">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>About WSJT-X</string>
<string>About FT8Call</string>
</property>
<property name="shortcut">
<string>Ctrl+F1</string>
@@ -4139,6 +4244,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionOnline_User_Guide">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Online User Guide</string>
</property>
@@ -4147,6 +4255,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionKeyboard_shortcuts">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Keyboard shortcuts</string>
</property>
@@ -4155,6 +4266,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionSpecial_mouse_commands">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Special mouse commands</string>
</property>
@@ -4364,6 +4478,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionShort_list_of_add_on_prefixes_and_suffixes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>List of Type 1 prefixes and suffixes</string>
</property>
@@ -4377,6 +4494,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionLocal_User_Guide">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Local User Guide</string>
</property>
@@ -4462,6 +4582,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="download_samples_action">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Download Samples ...</string>
</property>
@@ -4486,6 +4609,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionRelease_Notes">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Release Notes</string>
</property>
@@ -4617,6 +4743,9 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</action>
<action name="actionFT8_DXpedition_Mode_User_Guide">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>FT8 DXpedition Mode User Guide</string>
</property>
+253
View File
@@ -0,0 +1,253 @@
/****************************************************************************
**
** Copyright (C) 2015 Corentin Chary <corentin.chary@gmail.cm>
**
** This file could be part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QPRIORITY_QUEUE_H
#define QPRIORITY_QUEUE_H
#include <QtCore/qalgorithms.h>
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
QT_MODULE(Core)
template <class T> class QList;
#ifndef QT_NO_STL
#include <queue>
#include <vector>
#else
/* Fallback class used when stl is not available */
template <class T, typename LessThan = qLess < T > >
class QPriorityQueuePrivate {
public:
inline QPriorityQueuePrivate(LessThan l) : lessThan(l), d() {}
inline ~QPriorityQueuePrivate() {}
inline void clear() { return d.clear(); }
inline int size() const { return d.size(); }
inline bool empty() const { return d.empty(); }
inline T &top() { return d.front(); }
void pop();
void push(const T &value);
private:
inline int parent(int i) {
return (i - 1) / 2;
}
inline int leftChild(int i) {
return 2 * i + 1;
}
inline int rightChild(int i) {
return 2 * i + 2;
}
LessThan lessThan;
QList < T > d;
};
#endif
template <class T, typename LessThan = qLess < T > >
class Q_CORE_EXPORT QPriorityQueue
{
public:
inline QPriorityQueue(LessThan l = qLess < T >())
: lessThan(l), d(lessThan) { }
inline QPriorityQueue(const QPriorityQueue<T> &q)
: lessThan(q.lessThan), d(q.d) { }
inline ~QPriorityQueue() { }
QPriorityQueue<T> &operator=(const QPriorityQueue<T> &q) {
d = q.d; return *this;
}
inline int size() const { return d.size(); }
inline bool isEmpty() const { return empty(); }
// like qlist
void clear();
void append(const T &t) { return enqueue(t); }
void append(const QList<T> &t);
T takeFirst() { return dequeue(); }
inline int length() const { return size(); } // Same as count()
inline T& first() { return head(); }
inline const T& first() const { return head(); }
inline void removeFirst() { pop(); }
inline bool startsWith(const T &t) const
{ return !isEmpty() && first() == t; }
// like qqueue
inline void enqueue(const T &t) { push(t); }
inline T dequeue() { T t = d.top(); d.pop(); return t; }
inline T &head() { top(); }
inline const T &head() const { top(); }
// stl compatibility
typedef int size_type;
typedef T value_type;
inline bool empty() const { return d.empty(); }
inline const value_type& top() { Q_ASSERT(!isEmpty()); return d.top(); }
inline void push(const value_type& x) { return d.push(x); }
inline void pop() { Q_ASSERT(!isEmpty()); d.pop(); }
// comfort
inline QPriorityQueue<T> &operator+=(const T &t) {
enqueue(t); return *this;
}
inline QPriorityQueue<T> &operator<< (const T &t) {
enqueue(t); return *this;
}
inline QPriorityQueue<T> &operator>> (T &t) {
t = d.top(); d.pop(); return *this;
}
#ifndef QT_NO_STL
static inline QPriorityQueue<T>fromStdPriorityQueue(
const std::priority_queue<T> &q) {
QPriorityQueue<T> tmp; tmp.d = q; return tmp;
}
inline std::priority_queue<T> toStdPriorityQueue() const {
return d;
}
#endif
private:
LessThan lessThan;
#ifndef QT_NO_STL
std::priority_queue <T, std::vector < T >, LessThan> d;
#else
QPriorityQueuePrivate <T> d;
#endif
};
#ifndef QT_NO_STL
template <typename T, typename LessThan>
Q_INLINE_TEMPLATE void QPriorityQueue<T, LessThan>::clear()
{
d = std::priority_queue<T>(lessThan);
}
#else
template <typename T, typename LessThan>
Q_INLINE_TEMPLATE void QPriorityQueue<T, LessThan>::clear()
{
d.clear();
}
#endif
template <typename T, typename LessThan>
Q_OUTOFLINE_TEMPLATE void QPriorityQueue<T, LessThan>::append(const QList<T> &t)
{
foreach (T & e, t)
push(e);
}
// Re-implement std::priority_queue if STL not available, probably
// less efficient.
#ifdef QT_NO_STL
/*!
* Pop an element from the queue and reorder it using an
* inlined binary heap.
*
* \internal
*/
template <typename T, typename LessThan>
Q_OUTOFLINE_TEMPLATE void QPriorityQueuePrivate<T, LessThan>::pop()
{
int i = 0;
ssize_t size = d.size();;
if(!size)
return;
if(size == 1)
return d.clear();
d[0] = d.takeLast();
while(i < size - 1) {
int left = leftChild(i);
int right = rightChild(i);
bool validLeft = left < size;
bool validRight = right < size;
if(validLeft && lessThan(d.at(i), d.at(left)))
if(validRight && !lessThan(d.at(right), d.at(left))) {
d.swap(i, right);
i = right;
} else {
d.swap(i, left);
i = left;
}
else if(validRight && lessThan(d.at(i), d.at(right))) {
d.swap(i, right);
i = right;
}
else
break;
}
}
/*!
* Push an element with a given priority to the right place.
*
* \internal
*/
template <typename T, typename LessThan>
Q_OUTOFLINE_TEMPLATE void QPriorityQueuePrivate<T, LessThan>::push(const T &value)
{
int i = d.size();
d.append(value);
while(i != 0 && !lessThan(d.at(i), d.at(parent(i)))) {
d.swap(i, parent(i));
i = parent(i);
}
}
#endif
QT_END_NAMESPACE
QT_END_HEADER
#endif // QPRIORITY_QUEUE_H
+1 -56
View File
@@ -5,64 +5,9 @@
#include <QCoreApplication>
#include <QRegularExpression>
#include "svnversion.h"
namespace
{
QString revision_extract_number (QString const& s)
{
QString revision;
// try and match a number
QRegularExpression re {R"(^[$:]\w+: (\d+[^$]*)\$$)"};
auto match = re.match (s);
if (match.hasMatch ())
{
revision = 'r' + match.captured (1);
}
return revision;
}
}
QString revision (QString const& svn_rev_string)
{
QString result;
#if 0
auto revision_from_svn = revision_extract_number (svn_rev_string);
#if defined (CMAKE_BUILD)
QString svn_info {":Rev: " WSJTX_STRINGIZE (SVNVERSION) " $"};
auto revision_from_svn_info = revision_extract_number (svn_info);
if (!revision_from_svn_info.isEmpty ())
{
// we managed to get the revision number from svn info etc.
result = revision_from_svn_info;
}
else if (!revision_from_svn.isEmpty ())
{
// fall back to revision passed in if any
result = revision_from_svn;
}
else
{
// match anything
QRegularExpression re {R"(^[$:]\w+: ([^$]*)\$$)"};
auto match = re.match (svn_info);
if (match.hasMatch ())
{
result = match.captured (1);
}
}
#else
if (!revision_from_svn.isEmpty ())
{
// not CMake build so all we have is revision passed
result = revision_from_svn;
}
#endif
#endif
return result.trimmed ();
return "";
}
QString version (bool include_patch)
+765
View File
@@ -0,0 +1,765 @@
/**
* This file is part of FT8Call.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* (C) 2018 Jordan Sherer <kn4crd@gmail.com> - All Rights Reserved
*
**/
#include <QDebug>
#include <QMap>
#include <QSet>
#define CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
#include "crc.h"
#include "varicode.h"
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 callsign_alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "};
QMap<QString, int> directed_cmds = {
// any changes here need to be made also in the directed regular xpression for parsing
// directed queries
{"?", 0 }, // query snr
{"@", 1 }, // query qth
{"&", 2 }, // query station message
{"$", 3 }, // query station(s) heard
{"|", 4 }, // relay message
// directed responses
{" 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, 1, 2, 24, 25, 26, 27, 28, 29, 30, 31};
QRegularExpression directed_re("^"
"(?<to>[A-Z0-9/]+)"
"(?<cmd>\\s?(?:AGN[?]|RR|73|YES|NO|SNR|PWR|[?$@&| ]))"
"(?<pwr>\\s?\\d+\\s?[KM]?W)?"
"(?<num>\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"
);
QMap<QChar, QString> huff = {
// char code weight
{' ' , "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
{'.' , "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
};
QChar huffeot = '\x04';
quint32 nbasecall = 37 * 36 * 10 * 27 * 27 * 27;
QMap<QString, quint32> basecalls = {
{ "CQCQCQ", nbasecall + 1 },
{ "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
};
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();
}
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);
}
QStringList Varicode::parseCallsigns(QString const &input){
QStringList callsigns;
QRegularExpression re(callsign_pattern2);
QRegularExpressionMatchIterator iter = re.globalMatch(input);
while(iter.hasNext()){
QRegularExpressionMatch match = iter.next();
if(!match.hasMatch()){
continue;
}
QString callsign = match.captured("callsign");
QRegularExpression m(grid_pattern);
if(m.match(callsign).hasMatch()){
continue;
}
callsigns.append(callsign);
}
return callsigns;
}
QStringList Varicode::parseGrids(const QString &input){
QStringList grids;
QRegularExpression re(grid_pattern);
QRegularExpressionMatchIterator iter = re.globalMatch(input);
while(iter.hasNext()){
QRegularExpressionMatch match = iter.next();
if(!match.hasMatch()){
continue;
}
auto grid = match.captured("grid");
if(grid == "RR73"){
continue;
}
grids.append(grid);
}
return grids;
}
QList<QVector<bool>> Varicode::huffEncode(QString const& text){
QList<QVector<bool>> out;
foreach(auto ch, text){
if(!huff.contains(ch)){
continue;
}
out.append(strToBits(huff[ch]));
}
return out;
}
QVector<bool> Varicode::huffFlatten(QList<QVector<bool>> &list){
QVector<bool> out;
foreach(auto vec, list){
out += vec;
}
return out;
}
QString Varicode::huffDecode(QVector<bool> const& bitvec, int pad){
QString out;
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){
out.append(" ");
found = false;
break;
}
out.append(key);
bits = bits.mid(huff[key].length());
found = true;
}
}
if(!found){
break;
}
}
return out;
}
// convert char* array of 0 bytes and 1 bytes to bool vector
QVector<bool> Varicode::bytesToBits(char *bitvec, int n){
QVector<bool> bits;
for(int i = 0; i < n; i++){
bits.append(bitvec[i] == 0x01);
}
return bits;
}
// convert string of 0s and 1s to bool vector
QVector<bool> Varicode::strToBits(QString const& bitvec){
QVector<bool> bits;
foreach(auto ch, bitvec){
bits.append(ch == '1');
}
return bits;
}
QString Varicode::bitsToStr(QVector<bool> const& bitvec){
QString bits;
foreach(auto bit, bitvec){
bits.append(bit ? "1" : "0");
}
return bits;
}
QVector<bool> Varicode::intToBits(quint64 value, int expected){
QVector<bool> bits;
while(value){
bits.prepend((bool)(value & 1));
value = value >> 1;
}
if(expected){
while(bits.count() < expected){
bits.prepend((bool) 0);
}
}
return bits;
}
quint64 Varicode::bitsToInt(QVector<bool> const value){
quint64 v = 0;
foreach(bool bit, value){
v = (v << 1) + (int)(bit);
}
return v;
}
quint64 Varicode::bitsToInt(QVector<bool>::ConstIterator start, int n){
quint64 v = 0;
for(int i = 0; i < n; i++){
int bit = (int)(*start);
v = (v << 1) + (int)(bit);
start++;
}
return v;
}
quint8 Varicode::unpack5bits(QString const& value){
return alphabet.indexOf(value.at(0));
}
QString Varicode::pack5bits(quint8 packed){
return alphabet.at(packed % nalphabet);
}
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;
}
QString Varicode::pack16bits(quint16 packed){
QString out;
quint16 tmp = packed / (nalphabet*nalphabet);
out.append(alphabet.at(tmp));
tmp = (packed - (tmp * (nalphabet*nalphabet))) / nalphabet;
out.append(alphabet.at(tmp));
tmp = packed % nalphabet;
out.append(alphabet.at(tmp));
return out;
}
quint32 Varicode::unpack32bits(QString const& value){
return (quint32)(unpack16bits(value.left(3))) << 16 | unpack16bits(value.right(3));
}
QString Varicode::pack32bits(quint32 packed){
quint16 a = (packed & 0xFFFF0000) >> 16;
quint16 b = packed & 0xFFFF;
return pack16bits(a) + pack16bits(b);
}
quint64 Varicode::unpack64bits(QString const& value){
return (quint64)(unpack32bits(value.left(6))) << 32 | unpack32bits(value.right(6));
}
QString Varicode::pack64bits(quint64 packed){
quint32 a = (packed & 0xFFFFFFFF00000000) >> 32;
quint32 b = packed & 0xFFFFFFFF;
return pack32bits(a) + pack32bits(b);
}
quint32 Varicode::packCallsign(QString const& value){
quint32 packed = 0;
QString callsign = value.toUpper().trimmed();
if(basecalls.contains(callsign)){
return basecalls[callsign];
}
// workaround for swaziland
if(callsign.startsWith("3DA0")){
callsign = "3D0" + callsign.mid(4);
}
// workaround for guinea
if(callsign.startsWith("3X") && 'A' <= callsign.at(2) && callsign.at(2) <= 'Z'){
callsign = "Q" + callsign.mid(2);
}
int slen = callsign.length();
if(slen < 2){
return packed;
}
if(slen > 6){
return packed;
}
QStringList permutations = { callsign };
if(slen == 2){
permutations.append(" " + callsign + " ");
}
if(slen == 3){
permutations.append(" " + callsign + " ");
permutations.append(callsign + " ");
}
if(slen == 4){
permutations.append(" " + callsign + " ");
permutations.append(callsign + " ");
}
if(slen == 5){
permutations.append(" " + callsign);
permutations.append(callsign + " ");
}
QString matched;
QRegularExpression m(callsign_pattern3);
foreach(auto permutation, permutations){
auto match = m.match(permutation);
if(match.hasMatch()){
matched = match.captured(0);
}
}
if(matched.isEmpty()){
return packed;
}
if(matched.length() < 6){
return packed;
}
packed = callsign_alphabet.indexOf(matched.at(0));
packed = 36*packed + callsign_alphabet.indexOf(matched.at(1));
packed = 10*packed + callsign_alphabet.indexOf(matched.at(2));
packed = 27*packed + callsign_alphabet.indexOf(matched.at(3)) - 10;
packed = 27*packed + callsign_alphabet.indexOf(matched.at(4)) - 10;
packed = 27*packed + callsign_alphabet.indexOf(matched.at(5)) - 10;
return packed;
}
QString Varicode::unpackCallsign(quint32 value){
foreach(auto key, basecalls.keys()){
if(basecalls[key] == value){
return key;
}
}
QChar word[6];
quint32 tmp = value % 27 + 10;
word[5] = callsign_alphabet.at(tmp);
value = value/27;
tmp = value % 27 + 10;
word[4] = callsign_alphabet.at(tmp);
value = value/27;
tmp = value % 27 + 10;
word[3] = callsign_alphabet.at(tmp);
value = value/27;
tmp = value % 10;
word[2] = callsign_alphabet.at(tmp);
value = value/10;
tmp = value % 36;
word[1] = callsign_alphabet.at(tmp);
value = value/36;
tmp = value;
word[0] = callsign_alphabet.at(tmp);
QString callsign(word, 6);
if(callsign.startsWith("3D0")){
callsign = "3DA0" + callsign.mid(3);
}
if(callsign.startsWith("Q") and 'A' <= callsign.at(1) && callsign.at(1) <= 'Z'){
callsign = "3X" + callsign.mid(1);
}
return callsign;
}
QString deg2grid(float dlong, float dlat){
QChar grid[6];
if(dlong < -180){
dlong += 360;
}
if(dlong > 180){
dlong -= 360;
}
int nlong = int(60.0*(180.0-dlong)/5);
int n1 = nlong/240;
int n2 = (nlong-240*n1)/24;
int n3 = (nlong-240*n1-24*n2);
grid[0] = QChar('A' + n1);
grid[2] = QChar('0' + n2);
grid[4] = QChar('a' + n3);
int nlat=int(60.0*(dlat+90)/2.5);
n1 = nlat/240;
n2 = (nlat-240*n1)/24;
n3 = (nlat-240*n1-24*n2);
grid[1] = QChar('A' + n1);
grid[3] = QChar('0' + n2);
grid[5] = QChar('a' + n3);
return QString(grid, 6);
}
QPair<float, float> grid2deg(QString const &grid){
QPair<float, float> longLat;
QString g = grid;
if(g.length() < 6){
g = grid.left(4) + "mm";
}
g = g.left(4).toUpper() + g.right(2).toLower();
int nlong = 180 - 20 * (g.at(0).toLatin1() - 'A');
int n20d = 2 * (g.at(2).toLatin1() - '0');
float xminlong = 5 * (g.at(4).toLatin1() - 'a' + 0.5);
float dlong = nlong - n20d - xminlong/60.0;
int nlat = -90 + 10*(g.at(1).toLatin1() - 'A') + g.at(3).toLatin1() - '0';
float xminlat = 2.5 * (g.at(5).toLatin1() - 'a' + 0.5);
float dlat = nlat + xminlat/60.0;
longLat.first = dlong;
longLat.second = dlat;
return longLat;
}
quint16 Varicode::packGrid(QString const& grid){
// TODO: validate grid...
// TODO: encode non-grid data...
auto pair = grid2deg(grid.left(4));
int ilong = pair.first;
int ilat = pair.second + 90;
return ((ilong + 180)/2) * 180 + ilat;
}
QString Varicode::unpackGrid(quint16 value){
if(value > 180*180){
// TODO: decode non-grid data...
return "";
}
float dlat = value % 180 - 90;
float dlong = value / 180 * 2 - 180 + 2;
return deg2grid(dlong, dlat).left(4);
}
bool Varicode::isCommandAllowed(const QString &cmd){
return directed_cmds.contains(cmd) && allowed_cmds.contains(directed_cmds[cmd]);
}
QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, int *n){
QString frame;
auto match = directed_re.match(text);
if(match.hasMatch()){
QString from = callsign;
QString to = match.captured("to");
QString cmd = match.captured("cmd");
QString num = match.captured("num").trimmed();
QString pwr = match.captured("pwr").trimmed();
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 into 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"), "").toInt() * factor;
inum = mwattsToDbm(ipwr) - 30;
}
if(to == callsign){
*n = 0;
return frame;
}
bool validToCallsign = basecalls.contains(to) || QRegularExpression(callsign_pattern2).match(to).hasMatch();
if(!validToCallsign || !Varicode::isCommandAllowed(cmd)){
*n = 0;
return frame;
}
// TODO: jsherer - we don't need this CRC... the FT8 msg already has a 12 bit CRC...
//auto fromBytes = from.toLocal8Bit();
//auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU());
quint8 packed_is_data = 0;
quint8 packed_flag = inum < 0 ? 1 : 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 = qAbs(inum);
// [1][2][28][28][5],[5] = 69
auto bits = (
Varicode::intToBits(packed_is_data, 1) +
Varicode::intToBits(packed_flag, 2) +
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();
return frame;
}
return frame;
}
QStringList Varicode::unpackDirectedMessage(const QString &text){
QStringList unpacked;
if(text.length() < 13){
return unpacked;
}
// [1][2][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 is_data = Varicode::bitsToInt(Varicode::strToBits(bits.left(1)));
if(is_data != 0){
return unpacked;
}
quint8 flag = Varicode::bitsToInt(Varicode::strToBits(bits.mid(1,2)));
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();
// TODO: jsherer - we don't need this CRC... the FT8 msg already has a 12 bit CRC...
//auto fromBytes = from.toLocal8Bit();
//auto fromCRC = CRC::Calculate(fromBytes.data(), fromBytes.length(), CRC::CRC_5_ITU());
//if(fromCRC != extra){
// return unpacked;
//}
unpacked.append(from);
unpacked.append(Varicode::unpackCallsign(packed_to).trimmed());
unpacked.append(directed_cmds.key(packed_cmd & 31));
int 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 &text, int *n){
QString frame;
// [1][63],[5] = 69
quint8 is_data = 1;
auto frameBits = (
Varicode::intToBits(is_data, 1)
);
int i = 0;
foreach(auto charBits, Varicode::huffEncode(text)){
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 & 31);
*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);
unpacked = Varicode::huffDecode(bits, pad);
return unpacked;
}
+74
View File
@@ -0,0 +1,74 @@
#ifndef VARICODE_H
#define VARICODE_H
/**
* (C) 2018 Jordan Sherer <kn4crd@gmail.com> - All Rights Reserved
**/
#include <QBitArray>
#include <QRegularExpression>
#include <QRegExp>
#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 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, int pad=0);
static QVector<bool> bytesToBits(char * bitvec, int n);
static QVector<bool> strToBits(QString const& bitvec);
static QString bitsToStr(QVector<bool> const& bitvec);
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 quint8 unpack5bits(QString const& value);
static QString pack5bits(quint8 packed);
static quint16 unpack16bits(QString const& value);
static QString pack16bits(quint16 packed);
static quint32 unpack32bits(QString const& value);
static QString pack32bits(quint32 packed);
static quint64 unpack64bits(QString const& value);
static QString pack64bits(quint64 packed);
static quint32 packCallsign(QString const& value);
static QString unpackCallsign(quint32 value);
static quint16 packGrid(QString const& value);
static QString unpackGrid(quint16 value);
static bool isCommandAllowed(const QString &cmd);
static QString packDirectedMessage(QString const& text, QString const& callsign, int *n);
static QStringList unpackDirectedMessage(QString const& text);
static QString packDataMessage(QString const& text, int *n);
static QString unpackDataMessage(QString const& text);
};
#endif // VARICODE_H
+6 -2
View File
@@ -67,7 +67,8 @@ SOURCES += \
echoplot.cpp echograph.cpp fastgraph.cpp fastplot.cpp Modes.cpp \
WSPRBandHopping.cpp MessageAggregator.cpp SampleDownloader.cpp qt_helpers.cpp\
MultiSettings.cpp PhaseEqualizationDialog.cpp IARURegions.cpp MessageBox.cpp \
EqualizationToolsDialog.cpp
EqualizationToolsDialog.cpp \
varicode.cpp
HEADERS += qt_helpers.hpp \
pimpl_h.hpp pimpl_impl.hpp \
@@ -84,7 +85,10 @@ HEADERS += qt_helpers.hpp \
messageaveraging.h echoplot.h echograph.h fastgraph.h fastplot.h Modes.hpp WSPRBandHopping.hpp \
WsprTxScheduler.h SampleDownloader.hpp MultiSettings.hpp PhaseEqualizationDialog.hpp \
IARURegions.hpp MessageBox.hpp EqualizationToolsDialog.hpp \
qorderedmap.h
qorderedmap.h \
varicode.h \
qpriorityqueue.h \
crc.h
INCLUDEPATH += qmake_only