Compare commits
227 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d7b187269 | |||
| 13308a38f8 | |||
| 804605e9e6 | |||
| 4e981da9c8 | |||
| 6436e163bd | |||
| 379a0fa78f | |||
| 1c4a2ab7d8 | |||
| 54f6bdb0af | |||
| 5a0e2a8b14 | |||
| 1a9c611195 | |||
| c70661461e | |||
| 1f866e14f1 | |||
| c0e8a791e6 | |||
| 74523985bb | |||
| 8d28ea345a | |||
| 33446297fa | |||
| 6f648d5a60 | |||
| f1f618bbcd | |||
| ac7da998a2 | |||
| b3cd705fb6 | |||
| 604e366d4a | |||
| 8d73805dce | |||
| 5c491dc10d | |||
| d611259a25 | |||
| 00f2d46167 | |||
| e4cba50144 | |||
| 440311a75b | |||
| 38fc98702b | |||
| 6201de8c12 | |||
| 4321bd5e75 | |||
| 9f7fd2e7e2 | |||
| 65a2411c46 | |||
| d076eedeb8 | |||
| 7da30200f5 | |||
| cac5f798a3 | |||
| 41fb7481ed | |||
| 09effda8e4 | |||
| eecf7f65c7 | |||
| 0618b1cc09 | |||
| 00685b9117 | |||
| a5a6c95a44 | |||
| 8612f2cd32 | |||
| 5fa60659f3 | |||
| 08629019d4 | |||
| 7293d1e796 | |||
| aff001ce96 | |||
| 31d80a9b1d | |||
| 9010fa07c0 | |||
| 121bb0fdb5 | |||
| 47a7c06854 | |||
| c4089c0af9 | |||
| 5c28d154ff | |||
| 6dfd037736 | |||
| 3767b347d9 | |||
| ff12fd2240 | |||
| 6a0fea1d0a | |||
| f11e02c966 | |||
| 0763ecfe37 | |||
| 4c3ed5c719 | |||
| 0064176736 | |||
| 67386a09c7 | |||
| 3c81108cc6 | |||
| 71ed0c1f10 | |||
| 20a44171e4 | |||
| 644633721f | |||
| 8529ef0982 | |||
| 8bba574963 | |||
| 80d798d7e3 | |||
| 23a554220b | |||
| 7e903349ae | |||
| 55810e63a6 | |||
| 9162d851d4 | |||
| 2022908509 | |||
| 2e2551519d | |||
| 18052773f1 | |||
| 51010e7310 | |||
| a3eaf1cf74 | |||
| c731fa51ce | |||
| a5f59019d3 | |||
| 4a87edc4c7 | |||
| 2da25c3171 | |||
| 87a6e10bd6 | |||
| cf9c74b99e | |||
| dae50d3a99 | |||
| 83268fbdaf | |||
| 3b2242e258 | |||
| c42c3b7ad1 | |||
| 9496d5ed81 | |||
| 83174bc6fa | |||
| d74850b8e1 | |||
| 2f1dca3c49 | |||
| 4a38778808 | |||
| 960b9b14d1 | |||
| 9e93745587 | |||
| 8aa28108ea | |||
| 4168e1937c | |||
| f4b640df75 | |||
| cf1cece11c | |||
| 59893f6dfa | |||
| ead5c225b5 | |||
| 4744952a1a | |||
| a30bff5589 | |||
| e7e2f8357f | |||
| 4ffa82f695 | |||
| 3e38067561 | |||
| 52177a0513 | |||
| f79ca47a93 | |||
| f0e30dc7b0 | |||
| d20fe46447 | |||
| aa2c41f124 | |||
| ad563c0aed | |||
| 60b58fb50f | |||
| ca79a450ca | |||
| 3c8c80a5d7 | |||
| fc43c7fbb4 | |||
| 353d75ac49 | |||
| 2d392dd676 | |||
| 18fac2e4ba | |||
| 5275c0916f | |||
| 676aa3c78e | |||
| 4e8a65b5ee | |||
| 329ca0b23f | |||
| 29cb01697a | |||
| e27e7baec9 | |||
| 9cd17a4441 | |||
| 9d371c474a | |||
| 5e54b85fe0 | |||
| 286e32bf4e | |||
| 48dbe28031 | |||
| f95df562f7 | |||
| 1f210bba63 | |||
| 4df77ac774 | |||
| aa151c10e1 | |||
| db5e9e6520 | |||
| f22d51b53e | |||
| 68f9b2a5ff | |||
| 444abf57ff | |||
| ffb6bb679a | |||
| 34773a3b98 | |||
| b2e623a488 | |||
| 0e82edc9ea | |||
| cfa93839df | |||
| 6a8b3960d7 | |||
| 97ccbbd049 | |||
| f4090ece63 | |||
| c7a1f6c72a | |||
| 1a61dc53cc | |||
| cbb6cf9a24 | |||
| 3c46f8e276 | |||
| f56af06140 | |||
| 877c6e1d9d | |||
| 75bf51072a | |||
| d5c22a0c91 | |||
| 5d136fa1b8 | |||
| 05f0c3f111 | |||
| 1935f610d3 | |||
| 6b20cda1c0 | |||
| bbae64f923 | |||
| cb48947e9f | |||
| beec31c9b8 | |||
| 383f5d0943 | |||
| cb682748c7 | |||
| 6cce8230f2 | |||
| 0ca7c332f6 | |||
| 075b041b0d | |||
| 302e59e403 | |||
| 1461f00a84 | |||
| 10f4049e68 | |||
| f1db556598 | |||
| 93299ec7da | |||
| 289d38538a | |||
| 60fde40f58 | |||
| 646848cba1 | |||
| b5d7aa8fa8 | |||
| 6bb3c22bec | |||
| 1fd3b60ba3 | |||
| fe1cbcf6c6 | |||
| 119499afa4 | |||
| c26a042a92 | |||
| 48c33157f4 | |||
| 8c5ba5edf0 | |||
| 3ba51a1085 | |||
| 7fd792ba82 | |||
| 4e1ff07d7f | |||
| 7f992433dc | |||
| 2cb1665e2e | |||
| 862e702b2d | |||
| 2f6ed1b89c | |||
| 489cf3a85c | |||
| 188d775b7f | |||
| ec4df58787 | |||
| 4e1c04f74a | |||
| d042fffe75 | |||
| 2199b8b057 | |||
| 344635c75f | |||
| 41977d6b61 | |||
| ef8bc26474 | |||
| d000925ecb | |||
| b3fd715063 | |||
| 85ccc14549 | |||
| e5f4cd4753 | |||
| 4426dc77dc | |||
| 17f07caf72 | |||
| 420f8d0fcc | |||
| 434942ad29 | |||
| aab859a580 | |||
| f71a5f2174 | |||
| cd30ac1d3d | |||
| f6138f4c4b | |||
| 573fa4f8f9 | |||
| 522f5cf743 | |||
| a0abda9313 | |||
| 8591512800 | |||
| db650e970a | |||
| a5b2eeab5d | |||
| f3f051d17c | |||
| 0451f6aca9 | |||
| 25e7631c09 | |||
| fe1509c94b | |||
| 8b94c01ff6 | |||
| 2ab31a663a | |||
| f6edea8753 | |||
| 00cede3763 | |||
| 79bc33bcb7 | |||
| d08096a934 | |||
| 4e094b791c | |||
| 1817e298b1 |
@@ -2,15 +2,16 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
#include "varicode.h"
|
||||
|
||||
const int PACKET_TIMEOUT_SECONDS = 300;
|
||||
|
||||
APRSISClient::APRSISClient(QString host, quint16 port, QObject *parent):
|
||||
QTcpSocket(parent),
|
||||
m_host(host),
|
||||
m_port(port)
|
||||
QTcpSocket(parent)
|
||||
{
|
||||
setServer(host, port);
|
||||
|
||||
connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports);
|
||||
m_timer.setInterval(60*1000); // every 60 seconds
|
||||
m_timer.start();
|
||||
@@ -37,7 +38,7 @@ QString APRSISClient::loginFrame(QString callsign){
|
||||
auto loginFrame = QString("user %1 pass %2 ver %3\n");
|
||||
loginFrame = loginFrame.arg(callsign);
|
||||
loginFrame = loginFrame.arg(hashCallsign(callsign));
|
||||
loginFrame = loginFrame.arg("FT8Call");
|
||||
loginFrame = loginFrame.arg("JS8Call");
|
||||
return loginFrame;
|
||||
}
|
||||
|
||||
@@ -183,13 +184,35 @@ QPair<QString, QString> APRSISClient::grid2aprs(QString grid){
|
||||
};
|
||||
}
|
||||
|
||||
QString APRSISClient::stripSSID(QString call){
|
||||
return QString(call.split("-").first().toUpper());
|
||||
}
|
||||
|
||||
QString APRSISClient::replaceCallsignSuffixWithSSID(QString call, QString base){
|
||||
if(call != base){
|
||||
QRegularExpression re("[/](?<ssid>(P|\\d+))");
|
||||
auto matcher = re.globalMatch(call);
|
||||
if(matcher.hasNext()){
|
||||
auto match = matcher.next();
|
||||
auto ssid = match.captured("ssid");
|
||||
if(ssid == "P"){
|
||||
ssid = "16";
|
||||
}
|
||||
call = base + "-" + ssid;
|
||||
} else {
|
||||
call = base;
|
||||
}
|
||||
}
|
||||
return call;
|
||||
}
|
||||
|
||||
void APRSISClient::enqueueSpot(QString theircall, QString grid, QString comment){
|
||||
if(m_localCall.isEmpty()) return;
|
||||
|
||||
auto geo = APRSISClient::grid2aprs(grid);
|
||||
auto spotFrame = QString("%1>%2,APRS,TCPIP*:=%3/%4nFT8CALL %5\n");
|
||||
auto spotFrame = QString("%1>APRS,%2,TCPIP*:=%3/%4nJS8 %5\n");
|
||||
spotFrame = spotFrame.arg(theircall);
|
||||
spotFrame = spotFrame.arg(m_localCall);
|
||||
spotFrame = spotFrame.arg(stripSSID(m_localCall));
|
||||
spotFrame = spotFrame.arg(geo.first);
|
||||
spotFrame = spotFrame.arg(geo.second);
|
||||
spotFrame = spotFrame.arg(comment.left(43));
|
||||
@@ -201,15 +224,15 @@ void APRSISClient::enqueueThirdParty(QString theircall, QString payload){
|
||||
return;
|
||||
}
|
||||
|
||||
auto frame = QString("%1>%2,APRS,TCPIP*:%3\n");
|
||||
auto frame = QString("%1>APRS,%2,TCPIP*:%3\n");
|
||||
frame = frame.arg(theircall);
|
||||
frame = frame.arg(m_localCall);
|
||||
frame = frame.arg(stripSSID(m_localCall));
|
||||
frame = frame.arg(payload);
|
||||
enqueueRaw(frame);
|
||||
}
|
||||
|
||||
void APRSISClient::enqueueRaw(QString aprsFrame){
|
||||
m_frameQueue.enqueue({ aprsFrame, QDateTime::currentDateTimeUtc() });
|
||||
m_frameQueue.enqueue({ aprsFrame, DriftingDateTime::currentDateTimeUtc() });
|
||||
}
|
||||
|
||||
void APRSISClient::processQueue(bool disconnect){
|
||||
@@ -225,6 +248,7 @@ void APRSISClient::processQueue(bool disconnect){
|
||||
// 4. disconnect
|
||||
|
||||
if(state() != QTcpSocket::ConnectedState){
|
||||
qDebug() << "APRSISClient Connecting:" << m_host << m_port;
|
||||
connectToHost(m_host, m_port);
|
||||
if(!waitForConnected(5000)){
|
||||
qDebug() << "APRSISClient Connection Error:" << errorString();
|
||||
@@ -263,7 +287,7 @@ void APRSISClient::processQueue(bool disconnect){
|
||||
auto timestamp = pair.second;
|
||||
|
||||
// if the packet is older than the timeout, drop it.
|
||||
if(timestamp.secsTo(QDateTime::currentDateTimeUtc()) > PACKET_TIMEOUT_SECONDS){
|
||||
if(timestamp.secsTo(DriftingDateTime::currentDateTimeUtc()) > PACKET_TIMEOUT_SECONDS){
|
||||
qDebug() << "APRSISClient Packet Timeout:" << frame;
|
||||
m_frameQueue.dequeue();
|
||||
continue;
|
||||
|
||||
@@ -17,6 +17,8 @@ public:
|
||||
static QString loginFrame(QString callsign);
|
||||
static QPair<float, float> grid2deg(QString grid);
|
||||
static QPair<QString, QString> grid2aprs(QString grid);
|
||||
static QString stripSSID(QString call);
|
||||
static QString replaceCallsignSuffixWithSSID(QString call, QString base);
|
||||
|
||||
void setServer(QString host, quint16 port){
|
||||
if(state() == QTcpSocket::ConnectedState){
|
||||
@@ -25,6 +27,8 @@ public:
|
||||
|
||||
m_host = host;
|
||||
m_port = port;
|
||||
|
||||
qDebug() << "APRSISClient Server Change:" << m_host << m_port;
|
||||
}
|
||||
|
||||
void setPaused(bool paused){
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
Joe Taylor, K1JT <k1jt@arrl.net>
|
||||
|
||||
See also about.cpp or "Help->About WSJT-X" in the application for
|
||||
details of other contributions.
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
There are some defects remaining in WSJT-X.
|
||||
@@ -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 ft8call "@PROJECT_NAME@")
|
||||
set (CPACK_CREATE_DESKTOP_LINKS ft8call)
|
||||
set (CPACK_PACKAGE_EXECUTABLES js8call "@PROJECT_NAME@")
|
||||
set (CPACK_CREATE_DESKTOP_LINKS js8call)
|
||||
set (CPACK_STRIP_FILES TRUE)
|
||||
|
||||
#
|
||||
@@ -24,7 +24,7 @@ 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")
|
||||
# set (CPACK_NSIS_INSTALL_ROOT "C:\\JS8Call")
|
||||
|
||||
if(CMAKE_CL_64)
|
||||
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
|
||||
@@ -38,23 +38,22 @@ if (CPACK_GENERATOR MATCHES "NSIS")
|
||||
|
||||
# set the install/unistall icon used for the installer itself
|
||||
# There is a bug in NSI that does not handle full unix paths properly.
|
||||
set (CPACK_NSIS_MUI_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\ft8call.ico")
|
||||
set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\ft8call.ico")
|
||||
set (CPACK_NSIS_MUI_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\js8call.ico")
|
||||
set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\js8call.ico")
|
||||
# set the package header icon for MUI
|
||||
set (CPACK_PACKAGE_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\installer_logo.bmp")
|
||||
# tell cpack to create links to the doc files
|
||||
set (CPACK_NSIS_MENU_LINKS
|
||||
"@PROJECT_MANUAL_DIRECTORY_URL@/@PROJECT_MANUAL@" "@PROJECT_NAME@ Documentation"
|
||||
"@PROJECT_HOMEPAGE@" "@PROJECT_NAME@ Web Site"
|
||||
)
|
||||
# Use the icon from wsjtx for add-remove programs
|
||||
set (CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\ft8call.exe")
|
||||
set (CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\js8call.exe")
|
||||
|
||||
set (CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}")
|
||||
set (CPACK_NSIS_HELP_LINK "@PROJECT_MANUAL_DIRECTORY_URL@/@PROJECT_MANUAL@")
|
||||
set (CPACK_NSIS_HELP_LINK "@PROJECT_HOMEPAGE@")
|
||||
set (CPACK_NSIS_URL_INFO_ABOUT "@PROJECT_HOMEPAGE@")
|
||||
set (CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}")
|
||||
set (CPACK_NSIS_MUI_FINISHPAGE_RUN "ft8call.exe")
|
||||
set (CPACK_NSIS_MUI_FINISHPAGE_RUN "js8call.exe")
|
||||
set (CPACK_NSIS_MODIFY_PATH ON)
|
||||
endif ()
|
||||
|
||||
@@ -67,11 +66,11 @@ endif ()
|
||||
if ("${CPACK_GENERATOR}" STREQUAL "DragNDrop")
|
||||
set (CPACK_DMG_VOLUME_NAME "@PROJECT_NAME@")
|
||||
set (CPACK_DMG_BACKGROUND_IMAGE "@PROJECT_SOURCE_DIR@/icons/Darwin/DragNDrop Background.png")
|
||||
set (CPACK_DMG_DS_STORE "@PROJECT_SOURCE_DIR@/Darwin/ft8call_DMG.DS_Store")
|
||||
set (CPACK_DMG_DS_STORE "@PROJECT_SOURCE_DIR@/Darwin/js8call_DMG.DS_Store")
|
||||
set (CPACK_BUNDLE_NAME "@WSJTX_BUNDLE_NAME@")
|
||||
set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/ft8call.icns")
|
||||
set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/ft8call.icns")
|
||||
set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-ft8call-startup.sh")
|
||||
set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/js8call.icns")
|
||||
set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/js8call.icns")
|
||||
set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-js8call-startup.sh")
|
||||
set (CPACK_PACKAGING_INSTALL_PREFIX "/")
|
||||
endif ()
|
||||
|
||||
@@ -79,9 +78,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}-@ft8call_VERSION@-${CPACK_SYSTEM_NAME}")
|
||||
"${CPACK_PACKAGE_NAME}-@js8call_VERSION@-${CPACK_SYSTEM_NAME}")
|
||||
set (CPACK_SOURCE_PACKAGE_FILE_NAME
|
||||
"${CPACK_PACKAGE_NAME}-@ft8call_VERSION@-Source")
|
||||
"${CPACK_PACKAGE_NAME}-@js8call_VERSION@-Source")
|
||||
|
||||
if (NOT CPACK_WIX_SIZEOF_VOID_P)
|
||||
set (CPACK_WIX_SIZEOF_VOID_P "@CMAKE_SIZEOF_VOID_P@")
|
||||
|
||||
@@ -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 (ft8call C CXX Fortran)
|
||||
project (js8call C CXX Fortran)
|
||||
|
||||
#
|
||||
# CMake policies
|
||||
@@ -45,80 +45,15 @@ message (STATUS "Building ${CMAKE_PROJECT_NAME}-${wsjtx_VERSION}")
|
||||
#
|
||||
# project information
|
||||
#
|
||||
set (PROJECT_NAME "FT8Call")
|
||||
set (PROJECT_NAME "JS8Call")
|
||||
set (PROJECT_VENDOR "Jordan Sherer, KN4CRD")
|
||||
set (PROJECT_CONTACT "Jordan Sherer <kn4crd@gmail.com>")
|
||||
set (PROJECT_COPYRIGHT "Copyright (C) 2001-2018 by Joe Taylor, K1JT, (C) 2018 by Jordan Sherer, KN4CRD")
|
||||
set (PROJECT_HOMEPAGE http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html)
|
||||
set (PROJECT_MANUAL wsjtx-main)
|
||||
set (PROJECT_MANUAL_DIRECTORY_URL http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/)
|
||||
set (PROJECT_SAMPLES_URL http://downloads.sourceforge.net/project/wsjt/)
|
||||
set (PROJECT_SAMPLES_UPLOAD_DEST frs.sourceforge.net:/home/frs/project/wsjt/)
|
||||
set (PROJECT_HOMEPAGE https://groups.io/g/js8call)
|
||||
set (PROJECT_SUMMARY_DESCRIPTION "${PROJECT_NAME} - Digital Modes for Weak Signal Communicaitons in Amateur Radio.")
|
||||
set (PROJECT_DESCRIPTION "${PROJECT_SUMMARY_DESCRIPTION}
|
||||
${PROJECT_NAME} is a computer program designed to facilitate basic amateur
|
||||
radio communication using very weak signals. The first four letters in
|
||||
the program name stand for `(W)eak (S)ignal communication by
|
||||
K1(JT),` while the suffix `-X` indicates that ${PROJECT_NAME} started as
|
||||
an extended and experimental branch of the program
|
||||
WSJT.
|
||||
.
|
||||
${PROJECT_NAME} Version 1.8 offers nine different protocols or modes: FT8,
|
||||
JT4, JT9, JT65, QRA64, ISCAT, MSK144, WSPR, and Echo.
|
||||
The first five are designed for making reliable QSOs under extreme
|
||||
weak-signal conditions. They use nearly identical message structure
|
||||
and source encoding. JT65 and QRA64 were designed for EME
|
||||
(`moonbounce`) on the VHF/UHF bands and have also proven very
|
||||
effective for worldwide QRP communication on the HF bands. QRA64 has
|
||||
a number of advantages over JT65, including better performance on the
|
||||
very weakest signals. We imagine that over time it may replace JT65
|
||||
for EME use. JT9 was originally designed for the LF, MF, and lower HF
|
||||
bands. Its submode JT9A is 2 dB more sensitive than JT65 while using
|
||||
less than 10% of the bandwidth. JT4 offers a wide variety of tone
|
||||
spacings and has proven highly effective for EME on microwave bands up
|
||||
to 24 GHz. These four `slow` modes use one-minute timed sequences
|
||||
of alternating transmission and reception, so a minimal QSO takes four
|
||||
to six minutes — two or three transmissions by each station, one
|
||||
sending in odd UTC minutes and the other even. FT8 is operationally
|
||||
similar but four times faster (15-second T/R sequences) and less
|
||||
sensitive by a few dB. On the HF bands, world-wide QSOs are possible
|
||||
with any of these modes using power levels of a few watts (or even
|
||||
milliwatts) and compromise antennas. On VHF bands and higher, QSOs
|
||||
are possible (by EME and other propagation types) at signal levels 10
|
||||
to 15 dB below those required for CW.
|
||||
.
|
||||
ISCAT, MSK144, and optionally submodes JT9E-H are `fast`
|
||||
protocols designed to take advantage of brief signal enhancements from
|
||||
ionized meteor trails, aircraft scatter, and other types of scatter
|
||||
propagation. These modes use timed sequences of 5, 10, 15, or 30 s
|
||||
duration. User messages are transmitted repeatedly at high rate (up
|
||||
to 250 characters per second, for MSK144) to make good use of the
|
||||
shortest meteor-trail reflections or `pings`. ISCAT uses free-form
|
||||
messages up to 28 characters long, while MSK144 uses the same
|
||||
structured messages as the slow modes and optionally an abbreviated
|
||||
format with hashed callsigns.
|
||||
.
|
||||
WSPR (pronounced `whisper`) stands for (W)eak (S)ignal
|
||||
(P)ropagation (R)eporter. The WSPR protocol was designed for probing
|
||||
potential propagation paths using low-power transmissions. WSPR
|
||||
messages normally carry the transmitting station’s callsign, grid
|
||||
locator, and transmitter power in dBm, and they can be decoded at
|
||||
signal-to-noise ratios as low as -28 dB in a 2500 Hz bandwidth. WSPR
|
||||
users with internet access can automatically upload reception
|
||||
reports to a central database called wsprnet that provides a mapping
|
||||
facility, archival storage, and many other features.
|
||||
.
|
||||
Echo mode allows you to detect and measure your own station's echoes
|
||||
from the moon, even if they are far below the audible threshold.
|
||||
.
|
||||
${PROJECT_NAME} provides spectral displays for receiver passbands as wide as
|
||||
5 kHz, flexible rig control for nearly all modern radios used by
|
||||
amateurs, and a wide variety of special aids such as automatic Doppler
|
||||
tracking for EME QSOs and Echo testing. The program runs equally well
|
||||
on Windows, Macintosh, and Linux systems, and installation packages
|
||||
are available for all three platforms.
|
||||
.
|
||||
Be sure to read the online ${PROJECT_NAME} User's Guide.")
|
||||
${PROJECT_NAME} is a computer program designed to facilitate amateur
|
||||
radio communication using very weak signals.")
|
||||
|
||||
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake/Modules ${CMAKE_MODULE_PATH})
|
||||
|
||||
@@ -159,8 +94,7 @@ option (WSJT_TRACE_CAT "Debugging option that turns on CAT diagnostics.")
|
||||
option (WSJT_TRACE_CAT_POLLS "Debugging option that turns on CAT diagnostics during polling.")
|
||||
option (WSJT_HAMLIB_TRACE "Debugging option that turns on minimal Hamlib internal diagnostics.")
|
||||
option (WSJT_SOFT_KEYING "Apply a ramp to CW keying envelope to reduce transients." ON)
|
||||
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation.")
|
||||
option (WSJT_GENERATE_DOCS "Generate documentation files." ON)
|
||||
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation." ON)
|
||||
option (WSJT_RIG_NONE_CAN_SPLIT "Allow split operation with \"None\" as rig.")
|
||||
|
||||
CMAKE_DEPENDENT_OPTION (WSJT_HAMLIB_VERBOSE_TRACE "Debugging option that turns on full Hamlib internal diagnostics." OFF WSJT_HAMLIB_TRACE OFF)
|
||||
@@ -169,7 +103,7 @@ CMAKE_DEPENDENT_OPTION (WSJT_QDEBUG_IN_RELEASE "Leave Qt debugging statements in
|
||||
CMAKE_DEPENDENT_OPTION (WSJT_ENABLE_EXPERIMENTAL_FEATURES "Enable features not fully ready for public releases." ON
|
||||
is_debug_build OFF)
|
||||
CMAKE_DEPENDENT_OPTION (WSJT_CREATE_WINMAIN
|
||||
"The wsjtx target is normally built as GUI executable with a WinMain entry point on Windows,
|
||||
"The target is normally built as GUI executable with a WinMain entry point on Windows,
|
||||
if you want a console application instead then set this option to OFF.
|
||||
|
||||
If you just want to see the debug output from the application then the easiest way is to
|
||||
@@ -249,11 +183,6 @@ set (wsjt_qt_CXXSRCS
|
||||
HintedSpinBox.cpp
|
||||
RestrictedSpinBox.cpp
|
||||
HelpTextWindow.cpp
|
||||
SampleDownloader.cpp
|
||||
SampleDownloader/DirectoryDelegate.cpp
|
||||
SampleDownloader/Directory.cpp
|
||||
SampleDownloader/FileNode.cpp
|
||||
SampleDownloader/RemoteFile.cpp
|
||||
DisplayManual.cpp
|
||||
MultiSettings.cpp
|
||||
MaidenheadLocatorValidator.cpp
|
||||
@@ -268,16 +197,17 @@ set (wsjt_qtmm_CXXSRCS
|
||||
Audio/BWFFile.cpp
|
||||
)
|
||||
|
||||
set (jt9_FSRCS
|
||||
set (js8_FSRCS
|
||||
lib/jt9.f90
|
||||
lib/jt9a.f90
|
||||
)
|
||||
|
||||
set (jt9_CXXSRCS
|
||||
set (js8_CXXSRCS
|
||||
lib/ipcomm.cpp
|
||||
)
|
||||
|
||||
set (wsjtx_CXXSRCS
|
||||
DriftingDateTime.cpp
|
||||
logbook/adif.cpp
|
||||
logbook/countrydat.cpp
|
||||
logbook/countriesworked.cpp
|
||||
@@ -304,6 +234,9 @@ set (wsjtx_CXXSRCS
|
||||
messageaveraging.cpp
|
||||
WsprTxScheduler.cpp
|
||||
varicode.cpp
|
||||
jsc.cpp
|
||||
jsc_list.cpp
|
||||
jsc_map.cpp
|
||||
SelfDestructMessageBox.cpp
|
||||
messagereplydialog.cpp
|
||||
keyeater.cpp
|
||||
@@ -673,32 +606,6 @@ set (wsjtx_UISRCS
|
||||
messagereplydialog.ui
|
||||
)
|
||||
|
||||
set (UDP_library_CXXSRCS
|
||||
Radio.cpp
|
||||
RadioMetaType.cpp
|
||||
NetworkMessage.cpp
|
||||
MessageServer.cpp
|
||||
)
|
||||
|
||||
set (UDP_library_HEADERS
|
||||
Radio.hpp
|
||||
MessageServer.hpp
|
||||
${PROJECT_BINARY_DIR}/udp_export.h
|
||||
)
|
||||
|
||||
set (message_aggregator_CXXSRCS
|
||||
UDPExamples/MessageAggregator.cpp
|
||||
UDPExamples/MessageAggregatorMainWindow.cpp
|
||||
UDPExamples/DecodesModel.cpp
|
||||
UDPExamples/BeaconsModel.cpp
|
||||
UDPExamples/ClientWidget.cpp
|
||||
MaidenheadLocatorValidator.cpp
|
||||
)
|
||||
|
||||
set (message_aggregator_STYLESHEETS
|
||||
UDPExamples/qss/default.qss
|
||||
)
|
||||
|
||||
set (qcp_CXXSRCS
|
||||
qcustomplot-source/qcustomplot.cpp
|
||||
)
|
||||
@@ -707,7 +614,7 @@ set (all_CXXSRCS
|
||||
${wsjt_CXXSRCS}
|
||||
${wsjt_qt_CXXSRCS}
|
||||
${wsjt_qtmm_CXXSRCS}
|
||||
${jt9_CXXSRCS}
|
||||
${js8_CXXSRCS}
|
||||
${wsjtx_CXXSRCS}
|
||||
${qcp_CXXSRCS}
|
||||
)
|
||||
@@ -720,11 +627,8 @@ set (all_C_and_CXXSRCS
|
||||
)
|
||||
|
||||
set (TOP_LEVEL_RESOURCES
|
||||
shortcuts.txt
|
||||
mouse_commands.txt
|
||||
prefixes.txt
|
||||
cty.dat
|
||||
icons/Darwin/FT8Call.iconset/icon_128x128.png
|
||||
icons/Darwin/JS8Call.iconset/icon_128x128.png
|
||||
contrib/gpl-v3-logo.svg
|
||||
artwork/splash.png
|
||||
)
|
||||
@@ -891,14 +795,6 @@ if (WIN32)
|
||||
find_package (Qt5AxContainer REQUIRED)
|
||||
endif (WIN32)
|
||||
|
||||
#
|
||||
# sub-directories
|
||||
#
|
||||
add_subdirectory (samples)
|
||||
if (WSJT_GENERATE_DOCS)
|
||||
add_subdirectory (doc)
|
||||
endif (WSJT_GENERATE_DOCS)
|
||||
|
||||
|
||||
#
|
||||
# Library building setup
|
||||
@@ -1146,7 +1042,7 @@ if (${OPENMP_FOUND} OR APPLE)
|
||||
if (APPLE)
|
||||
# On Mac we don't have working OpenMP support in the C/C++
|
||||
# compilers so we have to manually set the correct flags to get
|
||||
# OpenMP support in jt9.
|
||||
# OpenMP support in js8.
|
||||
target_compile_options (wsjt_fort_omp
|
||||
PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:Fortran>:-fopenmp> # assumes GNU style Fortran compiler
|
||||
@@ -1172,140 +1068,49 @@ endif (WIN32)
|
||||
add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS})
|
||||
target_link_libraries (wsjt_qtmm Qt5::Multimedia)
|
||||
|
||||
add_executable (jt4sim lib/jt4sim.f90 wsjtx.rc)
|
||||
target_link_libraries (jt4sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (jt65sim lib/jt65sim.f90 wsjtx.rc)
|
||||
target_link_libraries (jt65sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (qra64sim lib/qra/qra64/qra64sim.f90 wsjtx.rc)
|
||||
target_link_libraries (qra64sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (jt49sim lib/jt49sim.f90 wsjtx.rc)
|
||||
target_link_libraries (jt49sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (allsim lib/allsim.f90 wsjtx.rc)
|
||||
target_link_libraries (allsim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (jt65code lib/jt65code.f90 wsjtx.rc)
|
||||
target_link_libraries (jt65code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (qra64code lib/qra64code.f90 wsjtx.rc)
|
||||
target_link_libraries (qra64code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (jt9code lib/jt9code.f90 wsjtx.rc)
|
||||
target_link_libraries (jt9code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (wsprcode lib/wsprcode/wsprcode.f90 lib/wsprcode/nhash.c
|
||||
wsjtx.rc)
|
||||
target_link_libraries (wsprcode wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (wsprd ${wsprd_CSRCS})
|
||||
target_include_directories (wsprd PRIVATE ${FFTW3_INCLUDE_DIRS})
|
||||
target_link_libraries (wsprd ${FFTW3_LIBRARIES})
|
||||
|
||||
add_executable (wsprsim ${wsprsim_CSRCS})
|
||||
|
||||
add_executable (jt4code lib/jt4code.f90 wsjtx.rc)
|
||||
target_link_libraries (jt4code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144code lib/msk144code.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (jt65 lib/jt65.f90 lib/jt65_test.f90 wsjtx.rc)
|
||||
target_link_libraries (jt65 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ldpcsim40 lib/ldpcsim40.f90 wsjtx.rc)
|
||||
target_link_libraries (ldpcsim40 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ldpcsim120 lib/fsk4hf/ldpcsim120.f90 wsjtx.rc)
|
||||
target_link_libraries (ldpcsim120 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ldpcsim174 lib/ft8/ldpcsim174.f90 wsjtx.rc)
|
||||
target_link_libraries (ldpcsim174 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ldpcsim144 lib/ldpcsim144.f90 wsjtx.rc)
|
||||
target_link_libraries (ldpcsim144 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ldpcsim168 lib/fsk4hf/ldpcsim168.f90 wsjtx.rc)
|
||||
target_link_libraries (ldpcsim168 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (fsk4hf lib/fsk4hf/fsk4hf.f90 wsjtx.rc)
|
||||
target_link_libraries (fsk4hf wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ft8code lib/ft8/ft8code.f90 wsjtx.rc)
|
||||
target_link_libraries (ft8code wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc)
|
||||
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (wsprlfsim lib/fsk4hf/wsprlfsim.f90 wsjtx.rc)
|
||||
target_link_libraries (wsprlfsim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (wspr_fsk8d lib/fsk4hf/wspr_fsk8d.f90 wsjtx.rc)
|
||||
target_link_libraries (wspr_fsk8d wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (wspr_fsk8_sim lib/fsk4hf/wspr_fsk8_sim.f90 wsjtx.rc)
|
||||
target_link_libraries (wspr_fsk8_sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (mskhfsim lib/fsk4hf/mskhfsim.f90 wsjtx.rc)
|
||||
target_link_libraries (mskhfsim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144sd lib/msk144sd.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144sd wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (msk144d2 lib/msk144d2.f90 wsjtx.rc)
|
||||
target_link_libraries (msk144d2 wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (fmtave lib/fmtave.f90 wsjtx.rc)
|
||||
|
||||
add_executable (fcal lib/fcal.f90 wsjtx.rc)
|
||||
|
||||
add_executable (fmeasure lib/fmeasure.f90 wsjtx.rc)
|
||||
|
||||
add_executable (jt9 ${jt9_FSRCS} ${jt9_CXXSRCS} wsjtx.rc)
|
||||
add_executable (js8 ${js8_FSRCS} ${js8_CXXSRCS} wsjtx.rc)
|
||||
if (${OPENMP_FOUND} OR APPLE)
|
||||
if (APPLE)
|
||||
# On Mac we don't have working OpenMP support in the C/C++
|
||||
# compilers so we have to manually set the correct linking flags
|
||||
# and libraries to get OpenMP support in jt9.
|
||||
set_target_properties (jt9
|
||||
# and libraries to get OpenMP support in js8.
|
||||
set_target_properties (js8
|
||||
PROPERTIES
|
||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||
LINK_LIBRARIES "gomp;gcc_s.1" # assume GNU libgcc OpenMP
|
||||
)
|
||||
target_compile_options (jt9
|
||||
target_compile_options (js8
|
||||
PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:Fortran>:-fopenmp> # assumes GNU style Fortran compiler
|
||||
)
|
||||
else (APPLE)
|
||||
if (OpenMP_C_FLAGS)
|
||||
set_target_properties (jt9
|
||||
set_target_properties (js8
|
||||
PROPERTIES
|
||||
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
||||
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
||||
)
|
||||
endif ()
|
||||
set_target_properties (jt9
|
||||
set_target_properties (js8
|
||||
PROPERTIES
|
||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||
)
|
||||
endif (APPLE)
|
||||
if (WIN32)
|
||||
# set_target_properties (jt9 PROPERTIES
|
||||
# set_target_properties (js8 PROPERTIES
|
||||
# LINK_FLAGS -Wl,--stack,16777216
|
||||
# )
|
||||
endif ()
|
||||
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx Qt5::Core)
|
||||
target_link_libraries (js8 wsjt_fort_omp wsjt_cxx Qt5::Core)
|
||||
else (${OPENMP_FOUND} OR APPLE)
|
||||
target_link_libraries (jt9 wsjt_fort wsjt_cxx Qt5::Core)
|
||||
target_link_libraries (js8 wsjt_fort wsjt_cxx Qt5::Core)
|
||||
endif (${OPENMP_FOUND} OR APPLE)
|
||||
|
||||
# build the main application
|
||||
add_executable (ft8call MACOSX_BUNDLE
|
||||
add_executable (js8call MACOSX_BUNDLE
|
||||
${wsjtx_CXXSRCS}
|
||||
${wsjtx_GENUISRCS}
|
||||
wsjtx.rc
|
||||
@@ -1314,10 +1119,10 @@ add_executable (ft8call MACOSX_BUNDLE
|
||||
)
|
||||
|
||||
if (WSJT_CREATE_WINMAIN)
|
||||
set_target_properties (ft8call PROPERTIES WIN32_EXECUTABLE ON)
|
||||
set_target_properties (js8call PROPERTIES WIN32_EXECUTABLE ON)
|
||||
endif (WSJT_CREATE_WINMAIN)
|
||||
|
||||
set_target_properties (ft8call PROPERTIES
|
||||
set_target_properties (js8call PROPERTIES
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Darwin/Info.plist.in"
|
||||
MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
|
||||
MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
|
||||
@@ -1330,113 +1135,49 @@ set_target_properties (ft8call PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.k1jt.wsjtx"
|
||||
)
|
||||
|
||||
target_include_directories (ft8call PRIVATE ${FFTW3_INCLUDE_DIRS})
|
||||
target_include_directories (js8call PRIVATE ${FFTW3_INCLUDE_DIRS})
|
||||
if (APPLE)
|
||||
target_link_libraries (ft8call wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
|
||||
target_link_libraries (js8call wsjt_fort wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
|
||||
else ()
|
||||
target_link_libraries (ft8call wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
|
||||
target_link_libraries (js8call wsjt_fort_omp wsjt_cxx wsjt_qt wsjt_qtmm ${hamlib_LIBRARIES} ${FFTW3_LIBRARIES})
|
||||
if (OpenMP_C_FLAGS)
|
||||
set_target_properties (ft8call PROPERTIES
|
||||
set_target_properties (js8call PROPERTIES
|
||||
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
||||
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
||||
)
|
||||
endif ()
|
||||
set_target_properties (ft8call PROPERTIES
|
||||
set_target_properties (js8call PROPERTIES
|
||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||
)
|
||||
if (WIN32)
|
||||
set_target_properties (ft8call PROPERTIES
|
||||
set_target_properties (js8call PROPERTIES
|
||||
LINK_FLAGS -Wl,--stack,16777216
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
qt5_use_modules (ft8call SerialPort) # not sure why the interface link library syntax above doesn't work
|
||||
qt5_use_modules (js8call 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})
|
||||
add_library (wsjtx_udp-static STATIC ${UDP_library_CXXSRCS})
|
||||
#target_include_directories (wsjtx_udp
|
||||
# INTERFACE
|
||||
# $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/wsjtx>
|
||||
# )
|
||||
target_include_directories (wsjtx_udp-static
|
||||
INTERFACE
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/wsjtx>
|
||||
)
|
||||
#set_target_properties (wsjtx_udp PROPERTIES
|
||||
# PUBLIC_HEADER "${UDP_library_HEADERS}"
|
||||
# )
|
||||
set_target_properties (wsjtx_udp-static PROPERTIES
|
||||
OUTPUT_NAME wsjtx_udp
|
||||
)
|
||||
target_compile_definitions (wsjtx_udp-static PUBLIC UDP_STATIC_DEFINE)
|
||||
#qt5_use_modules (wsjtx_udp Network)
|
||||
qt5_use_modules (wsjtx_udp-static Network Gui)
|
||||
generate_export_header (wsjtx_udp-static BASE_NAME udp)
|
||||
|
||||
add_executable (udp_daemon UDPExamples/UDPDaemon.cpp UDPExamples/udp_daemon.rc ${WSJTX_ICON_FILE})
|
||||
target_link_libraries (udp_daemon wsjtx_udp-static)
|
||||
|
||||
add_resources (message_aggregator_RESOURCES /qss ${message_aggregator_STYLESHEETS})
|
||||
configure_file (UDPExamples/message_aggregator.qrc.in message_aggregator.qrc @ONLY)
|
||||
qt5_add_resources (message_aggregator_RESOURCES_RCC ${CMAKE_CURRENT_BINARY_DIR}/message_aggregator.qrc)
|
||||
add_executable (message_aggregator
|
||||
${message_aggregator_CXXSRCS}
|
||||
${WSJTX_ICON_FILE}
|
||||
UDPExamples/message_aggregator.rc
|
||||
${message_aggregator_RESOURCES_RCC}
|
||||
)
|
||||
target_link_libraries (message_aggregator Qt5::Widgets wsjtx_udp-static)
|
||||
|
||||
if (WSJT_CREATE_WINMAIN)
|
||||
set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON)
|
||||
endif (WSJT_CREATE_WINMAIN)
|
||||
|
||||
if (UNIX)
|
||||
if (NOT WSJT_SKIP_MANPAGES)
|
||||
add_subdirectory (manpages)
|
||||
add_dependencies (ft8call manpages)
|
||||
endif (NOT WSJT_SKIP_MANPAGES)
|
||||
if (NOT APPLE)
|
||||
add_subdirectory (debian)
|
||||
add_dependencies (ft8call debian)
|
||||
endif (NOT APPLE)
|
||||
endif (UNIX)
|
||||
# if (UNIX)
|
||||
# if (NOT WSJT_SKIP_MANPAGES)
|
||||
# add_subdirectory (manpages)
|
||||
# add_dependencies (js8call manpages)
|
||||
# endif (NOT WSJT_SKIP_MANPAGES)
|
||||
# if (NOT APPLE)
|
||||
# add_subdirectory (debian)
|
||||
# add_dependencies (js8call debian)
|
||||
# endif (NOT APPLE)
|
||||
# endif (UNIX)
|
||||
|
||||
|
||||
#
|
||||
# installation
|
||||
#
|
||||
install (TARGETS ft8call
|
||||
install (TARGETS js8call
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
BUNDLE DESTINATION . COMPONENT runtime
|
||||
)
|
||||
|
||||
# install (TARGETS wsjtx_udp EXPORT udp
|
||||
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
# PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/wsjtx
|
||||
# )
|
||||
# install (TARGETS wsjtx_udp-static EXPORT udp-static
|
||||
# DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
# )
|
||||
|
||||
# install (EXPORT udp NAMESPACE wsjtx::
|
||||
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wsjtx
|
||||
# )
|
||||
# install (EXPORT udp-static NAMESPACE wsjtx::
|
||||
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wsjtx
|
||||
# )
|
||||
|
||||
install (TARGETS udp_daemon message_aggregator
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
)
|
||||
|
||||
install (TARGETS jt9 ft8code jt65code qra64code qra64sim jt9code jt4code
|
||||
msk144code wsprd wspr_fsk8d fmtave fcal fmeasure
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
install (TARGETS js8 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
)
|
||||
|
||||
@@ -1444,24 +1185,21 @@ install (PROGRAMS
|
||||
${RIGCTL_EXE}
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
#COMPONENT runtime
|
||||
RENAME rigctl-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
|
||||
RENAME rigctl-local${CMAKE_EXECUTABLE_SUFFIX}
|
||||
)
|
||||
|
||||
install (PROGRAMS
|
||||
${RIGCTLD_EXE}
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
#COMPONENT runtime
|
||||
RENAME rigctld-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
|
||||
RENAME rigctld-local${CMAKE_EXECUTABLE_SUFFIX}
|
||||
)
|
||||
|
||||
install (FILES
|
||||
README
|
||||
COPYING
|
||||
AUTHORS
|
||||
THANKS
|
||||
NEWS
|
||||
INSTALL
|
||||
BUGS
|
||||
INSTALL-WSJTX
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||
#COMPONENT runtime
|
||||
)
|
||||
@@ -1508,7 +1246,7 @@ add_custom_target (uninstall
|
||||
# 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
|
||||
add_dependencies(wsjt_qt revisiontag)
|
||||
# add_dependencies(wsjt_qt)
|
||||
|
||||
|
||||
#
|
||||
@@ -1521,23 +1259,23 @@ configure_file (
|
||||
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
# install a desktop file so ft8call appears in the application start
|
||||
# install a desktop file so js8call appears in the application start
|
||||
# menu with an icon
|
||||
install (
|
||||
FILES ft8call.desktop
|
||||
FILES js8call.desktop
|
||||
DESTINATION /usr/share/applications
|
||||
#COMPONENT runtime
|
||||
)
|
||||
install (
|
||||
FILES icons/Unix/ft8call_icon.png
|
||||
FILES icons/Unix/js8call_icon.png
|
||||
DESTINATION /usr/share/pixmaps
|
||||
#COMPONENT runtime
|
||||
)
|
||||
|
||||
execute_process(COMMAND ln -s /opt/ft8call/bin/ft8call lft8call)
|
||||
execute_process(COMMAND ln -s /opt/js8call/bin/js8call ljs8call)
|
||||
|
||||
install(FILES
|
||||
${CMAKE_BINARY_DIR}/lft8call DESTINATION /usr/bin/ RENAME ft8call
|
||||
${CMAKE_BINARY_DIR}/ljs8call DESTINATION /usr/bin/ RENAME js8call
|
||||
#COMPONENT runtime
|
||||
)
|
||||
endif (NOT WIN32 AND NOT APPLE)
|
||||
|
||||
@@ -454,6 +454,7 @@ private:
|
||||
Q_SLOT void on_delete_macro_push_button_clicked (bool = false);
|
||||
Q_SLOT void on_PTT_method_button_group_buttonClicked (int);
|
||||
Q_SLOT void on_station_message_line_edit_textChanged(QString const&);
|
||||
Q_SLOT void on_groups_line_edit_textChanged(QString const&);
|
||||
Q_SLOT void on_qth_message_line_edit_textChanged(QString const&);
|
||||
Q_SLOT void on_cq_message_line_edit_textChanged(QString const&);
|
||||
Q_SLOT void on_reply_message_line_edit_textChanged(QString const&);
|
||||
@@ -584,6 +585,7 @@ private:
|
||||
QString my_callsign_;
|
||||
QString my_grid_;
|
||||
QString my_station_;
|
||||
QStringList my_groups_;
|
||||
QString my_qth_;
|
||||
QString cq_;
|
||||
QString reply_;
|
||||
@@ -626,6 +628,7 @@ private:
|
||||
bool spot_to_reporting_networks_;
|
||||
bool transmit_directed_;
|
||||
bool autoreply_off_at_startup_;
|
||||
bool heartbeat_anywhere_;
|
||||
bool relay_disabled_;
|
||||
bool monitor_off_at_startup_;
|
||||
bool monitor_last_used_;
|
||||
@@ -639,7 +642,7 @@ private:
|
||||
bool miles_;
|
||||
bool quick_call_;
|
||||
bool disable_TX_on_73_;
|
||||
int beacon_;
|
||||
int heartbeat_;
|
||||
int watchdog_;
|
||||
bool TX_messages_;
|
||||
bool enable_VHF_features_;
|
||||
@@ -756,6 +759,7 @@ void Configuration::set_spot_to_reporting_networks (bool spot)
|
||||
|
||||
bool Configuration::transmit_directed() const { return m_->transmit_directed_; }
|
||||
bool Configuration::autoreply_off_at_startup () const {return m_->autoreply_off_at_startup_;}
|
||||
bool Configuration::heartbeat_anywhere() const { return m_->heartbeat_anywhere_;}
|
||||
bool Configuration::relay_off() const { return m_->relay_disabled_; }
|
||||
bool Configuration::monitor_off_at_startup () const {return m_->monitor_off_at_startup_;}
|
||||
bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;}
|
||||
@@ -769,7 +773,7 @@ bool Configuration::clear_DX () const {return m_->clear_DX_;}
|
||||
bool Configuration::miles () const {return m_->miles_;}
|
||||
bool Configuration::quick_call () const {return m_->quick_call_;}
|
||||
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
|
||||
int Configuration::beacon () const {return m_->beacon_;}
|
||||
int Configuration::heartbeat () const {return m_->heartbeat_;}
|
||||
int Configuration::watchdog () const {return m_->watchdog_;}
|
||||
bool Configuration::TX_messages () const {return m_->TX_messages_;}
|
||||
bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features_;}
|
||||
@@ -920,7 +924,7 @@ QString Configuration::my_grid() const
|
||||
if (m_->use_dynamic_info_ && m_->dynamic_grid_.size () >= 4) {
|
||||
grid = m_->dynamic_grid_;
|
||||
}
|
||||
return grid;
|
||||
return grid.trimmed();
|
||||
}
|
||||
|
||||
QString Configuration::my_station() const
|
||||
@@ -929,22 +933,26 @@ QString Configuration::my_station() const
|
||||
if(m_->use_dynamic_info_ && !m_->dynamic_qtc_.isEmpty()){
|
||||
station = m_->dynamic_qtc_;
|
||||
}
|
||||
return station;
|
||||
return station.trimmed();
|
||||
}
|
||||
|
||||
QSet<QString> Configuration::my_groups() const {
|
||||
return QSet<QString>::fromList(m_->my_groups_);
|
||||
}
|
||||
|
||||
QString Configuration::my_qth() const
|
||||
{
|
||||
return m_->my_qth_;
|
||||
return m_->my_qth_.trimmed();
|
||||
}
|
||||
|
||||
QString Configuration::cq_message() const
|
||||
{
|
||||
return m_->cq_;
|
||||
return m_->cq_.trimmed();
|
||||
}
|
||||
|
||||
QString Configuration::reply_message() const
|
||||
{
|
||||
return m_->reply_;
|
||||
return m_->reply_.trimmed();
|
||||
}
|
||||
|
||||
int Configuration::callsign_aging() const
|
||||
@@ -1298,6 +1306,7 @@ void Configuration::impl::initialize_models ()
|
||||
ui_->callsign_aging_spin_box->setValue(callsign_aging_);
|
||||
ui_->activity_aging_spin_box->setValue(activity_aging_);
|
||||
ui_->station_message_line_edit->setText (my_station_.toUpper());
|
||||
ui_->groups_line_edit->setText(my_groups_.join(", "));
|
||||
ui_->qth_message_line_edit->setText (my_qth_.toUpper());
|
||||
ui_->cq_message_line_edit->setText(cq_.toUpper());
|
||||
ui_->reply_message_line_edit->setText (reply_.toUpper());
|
||||
@@ -1329,6 +1338,7 @@ void Configuration::impl::initialize_models ()
|
||||
ui_->psk_reporter_check_box->setChecked (spot_to_reporting_networks_);
|
||||
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
||||
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
||||
ui_->heartbeat_anywhere_check_box->setChecked(heartbeat_anywhere_);
|
||||
ui_->relay_disabled_check_box->setChecked(relay_disabled_);
|
||||
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
||||
ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
|
||||
@@ -1336,16 +1346,12 @@ void Configuration::impl::initialize_models ()
|
||||
ui_->stations_table_view->setEnabled(ui_->auto_switch_bands_check_box->isChecked());
|
||||
ui_->report_in_comments_check_box->setChecked (report_in_comments_);
|
||||
ui_->prompt_to_log_check_box->setChecked (prompt_to_log_);
|
||||
ui_->insert_blank_check_box->setChecked (insert_blank_);
|
||||
ui_->DXCC_check_box->setChecked (DXCC_);
|
||||
ui_->ppfx_check_box->setChecked (ppfx_);
|
||||
ui_->clear_DX_check_box->setChecked (clear_DX_);
|
||||
ui_->miles_check_box->setChecked (miles_);
|
||||
ui_->quick_call_check_box->setChecked (quick_call_);
|
||||
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
||||
ui_->beacon_spin_box->setValue (beacon_);
|
||||
ui_->heartbeat_spin_box->setValue (heartbeat_);
|
||||
ui_->tx_watchdog_spin_box->setValue (watchdog_);
|
||||
ui_->TX_messages_check_box->setChecked (TX_messages_);
|
||||
ui_->enable_VHF_features_check_box->setChecked(enable_VHF_features_);
|
||||
ui_->decode_at_52s_check_box->setChecked(decode_at_52s_);
|
||||
ui_->single_decode_check_box->setChecked(single_decode_);
|
||||
@@ -1438,10 +1444,11 @@ void Configuration::impl::read_settings ()
|
||||
my_callsign_ = settings_->value ("MyCall", QString {}).toString ();
|
||||
my_grid_ = settings_->value ("MyGrid", QString {}).toString ();
|
||||
my_station_ = settings_->value("MyStation", QString {}).toString();
|
||||
my_groups_ = settings_->value("MyGroups", QStringList{}).toStringList();
|
||||
callsign_aging_ = settings_->value ("CallsignAging", 0).toInt ();
|
||||
activity_aging_ = settings_->value ("ActivityAging", 2).toInt ();
|
||||
my_qth_ = settings_->value("MyQTH", QString {}).toString();
|
||||
cq_ = settings_->value("CQMessage", QString {"CQCQCQ"}).toString();
|
||||
cq_ = settings_->value("CQMessage", QString {"CQCQCQ <MYGRID4>"}).toString();
|
||||
reply_ = settings_->value("Reply", QString {"HW CPY?"}).toString();
|
||||
next_color_cq_ = color_cq_ = settings_->value("colorCQ","#66ff66").toString();
|
||||
next_color_mycall_ = color_mycall_ = settings_->value("colorMyCall","#ff6666").toString();
|
||||
@@ -1589,6 +1596,7 @@ void Configuration::impl::read_settings ()
|
||||
|
||||
transmit_directed_ = settings_->value ("TransmitDirected", true).toBool();
|
||||
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
||||
heartbeat_anywhere_ = settings_->value("BeaconAnywhere", false).toBool();
|
||||
relay_disabled_ = settings_->value ("RelayOFF", false).toBool ();
|
||||
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
||||
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
||||
@@ -1649,8 +1657,8 @@ void Configuration::impl::read_settings ()
|
||||
miles_ = settings_->value ("Miles", false).toBool ();
|
||||
quick_call_ = settings_->value ("QuickCall", false).toBool ();
|
||||
disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
|
||||
beacon_ = settings_->value ("TxBeacon", 30).toInt ();
|
||||
watchdog_ = settings_->value ("TxWatchdog", 0).toInt ();
|
||||
heartbeat_ = settings_->value ("TxBeacon", 30).toInt ();
|
||||
watchdog_ = settings_->value ("TxIdleWatchdog", 60).toInt ();
|
||||
if(watchdog_){
|
||||
watchdog_ = qMax(5, watchdog_);
|
||||
}
|
||||
@@ -1692,6 +1700,7 @@ void Configuration::impl::write_settings ()
|
||||
settings_->setValue ("MyCall", my_callsign_);
|
||||
settings_->setValue ("MyGrid", my_grid_);
|
||||
settings_->setValue ("MyStation", my_station_);
|
||||
settings_->setValue ("MyGroups", my_groups_);
|
||||
settings_->setValue ("MyQTH", my_qth_);
|
||||
settings_->setValue ("CQMessage", cq_);
|
||||
settings_->setValue ("Reply", reply_);
|
||||
@@ -1753,6 +1762,7 @@ void Configuration::impl::write_settings ()
|
||||
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
|
||||
settings_->setValue ("TransmitDirected", transmit_directed_);
|
||||
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
||||
settings_->setValue ("BeaconAnywhere", heartbeat_anywhere_);
|
||||
settings_->setValue ("RelayOFF", relay_disabled_);
|
||||
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
||||
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
||||
@@ -1781,8 +1791,8 @@ void Configuration::impl::write_settings ()
|
||||
settings_->setValue ("Miles", miles_);
|
||||
settings_->setValue ("QuickCall", quick_call_);
|
||||
settings_->setValue ("73TxDisable", disable_TX_on_73_);
|
||||
settings_->setValue ("TxBeacon", beacon_);
|
||||
settings_->setValue ("TxWatchdog", watchdog_);
|
||||
settings_->setValue ("TxBeacon", heartbeat_);
|
||||
settings_->setValue ("TxIdleWatchdog", watchdog_);
|
||||
settings_->setValue ("Tx2QSO", TX_messages_);
|
||||
settings_->setValue ("CATForceDTR", rig_params_.force_dtr);
|
||||
settings_->setValue ("DTR", rig_params_.dtr_high);
|
||||
@@ -1947,8 +1957,39 @@ void Configuration::impl::set_rig_invariants ()
|
||||
|| TransceiverFactory::basic_transceiver_name_ != rig);
|
||||
}
|
||||
|
||||
|
||||
QStringList splitGroups(QString groupsString, bool filter){
|
||||
QStringList groups;
|
||||
if(groupsString.isEmpty()){
|
||||
return groups;
|
||||
}
|
||||
|
||||
foreach(QString group, groupsString.split(",")){
|
||||
auto g = group.trimmed();
|
||||
if(filter && !g.startsWith("@")){
|
||||
continue;
|
||||
}
|
||||
groups.append(group.trimmed());
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
bool Configuration::impl::validate ()
|
||||
{
|
||||
auto callsign = ui_->callsign_line_edit->text().trimmed();
|
||||
if(!Varicode::isValidCallsign(callsign, nullptr) || callsign.startsWith("@")){
|
||||
MessageBox::critical_message (this, tr ("The callsign format you provided is not supported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach(auto group, splitGroups(ui_->groups_line_edit->text(), false)){
|
||||
if(!Varicode::isCompoundCallsign(group)){
|
||||
MessageBox::critical_message (this, QString("%1 is not a valid group").arg(group));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_->sound_input_combo_box->currentIndex () < 0
|
||||
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioInput).empty ())
|
||||
{
|
||||
@@ -2200,6 +2241,9 @@ void Configuration::impl::accept ()
|
||||
my_callsign_ = ui_->callsign_line_edit->text ();
|
||||
my_grid_ = ui_->grid_line_edit->text ();
|
||||
my_station_ = ui_->station_message_line_edit->text().toUpper();
|
||||
|
||||
my_groups_ = splitGroups(ui_->groups_line_edit->text().toUpper().trimmed(), true);
|
||||
|
||||
cq_ = ui_->cq_message_line_edit->text().toUpper();
|
||||
reply_ = ui_->reply_message_line_edit->text().toUpper();
|
||||
my_qth_ = ui_->qth_message_line_edit->text().toUpper();
|
||||
@@ -2216,6 +2260,7 @@ void Configuration::impl::accept ()
|
||||
tx_qsy_allowed_ = ui_->tx_qsy_check_box->isChecked ();
|
||||
transmit_directed_ = ui_->transmit_directed_check_box->isChecked();
|
||||
autoreply_off_at_startup_ = ui_->autoreply_off_check_box->isChecked ();
|
||||
heartbeat_anywhere_ = ui_->heartbeat_anywhere_check_box->isChecked();
|
||||
relay_disabled_ = ui_->relay_disabled_check_box->isChecked();
|
||||
monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
|
||||
monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
|
||||
@@ -2223,16 +2268,12 @@ void Configuration::impl::accept ()
|
||||
log_as_DATA_ = ui_->log_as_RTTY_check_box->isChecked ();
|
||||
report_in_comments_ = ui_->report_in_comments_check_box->isChecked ();
|
||||
prompt_to_log_ = ui_->prompt_to_log_check_box->isChecked ();
|
||||
insert_blank_ = ui_->insert_blank_check_box->isChecked ();
|
||||
DXCC_ = ui_->DXCC_check_box->isChecked ();
|
||||
ppfx_ = ui_->ppfx_check_box->isChecked ();
|
||||
clear_DX_ = ui_->clear_DX_check_box->isChecked ();
|
||||
miles_ = ui_->miles_check_box->isChecked ();
|
||||
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
||||
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
||||
beacon_ = ui_->beacon_spin_box->value ();
|
||||
heartbeat_ = ui_->heartbeat_spin_box->value ();
|
||||
watchdog_ = ui_->tx_watchdog_spin_box->value ();
|
||||
TX_messages_ = ui_->TX_messages_check_box->isChecked ();
|
||||
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
|
||||
save_directory_ = ui_->save_path_display_label->text ();
|
||||
azel_directory_ = ui_->azel_path_display_label->text ();
|
||||
@@ -2340,14 +2381,23 @@ void Configuration::impl::reject ()
|
||||
|
||||
void Configuration::impl::on_font_push_button_clicked ()
|
||||
{
|
||||
next_font_ = QFontDialog::getFont (0, next_font_, this);
|
||||
|
||||
next_font_ = QFontDialog::getFont (0, next_font_, this
|
||||
, tr ("Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, QFontDialog::DontUseNativeDialog
|
||||
#endif
|
||||
);
|
||||
ui_->font_push_button->setText(QString("Application Font (%1 %2)").arg(next_font_.family()).arg(next_font_.pointSize()));
|
||||
}
|
||||
|
||||
void Configuration::impl::on_tableFontButton_clicked ()
|
||||
{
|
||||
next_table_font_ = QFontDialog::getFont (0, next_table_font_, this);
|
||||
next_table_font_ = QFontDialog::getFont (0, next_table_font_, this
|
||||
, tr ("Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, QFontDialog::DontUseNativeDialog
|
||||
#endif
|
||||
);
|
||||
ui_->tableFontButton->setText(QString("Table Font (%1 %2)").arg(next_table_font_.family()).arg(next_table_font_.pointSize()));
|
||||
}
|
||||
|
||||
@@ -2455,7 +2505,7 @@ void Configuration::impl::on_rxFontButton_clicked ()
|
||||
next_rx_text_font_ = QFontDialog::getFont (0, next_rx_text_font_ , this
|
||||
, tr ("Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, 0
|
||||
, QFontDialog::DontUseNativeDialog
|
||||
#endif
|
||||
);
|
||||
ui_->rxFontButton->setText(QString("Font (%1 %2)").arg(next_rx_text_font_.family()).arg(next_rx_text_font_.pointSize()));
|
||||
@@ -2497,7 +2547,7 @@ void Configuration::impl::on_txFontButton_clicked ()
|
||||
next_tx_text_font_ = QFontDialog::getFont (0, next_tx_text_font_ , this
|
||||
, tr ("Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, 0
|
||||
, QFontDialog::DontUseNativeDialog
|
||||
#endif
|
||||
);
|
||||
|
||||
@@ -2509,7 +2559,7 @@ void Configuration::impl::on_composeFontButton_clicked ()
|
||||
next_compose_text_font_ = QFontDialog::getFont (0, next_compose_text_font_ , this
|
||||
, tr ("Font Chooser")
|
||||
#if QT_VERSION >= 0x050201
|
||||
, 0
|
||||
, QFontDialog::DontUseNativeDialog
|
||||
#endif
|
||||
);
|
||||
ui_->composeFontButton->setText(QString("Font (%1 %2)").arg(next_compose_text_font_.family()).arg(next_compose_text_font_.pointSize()));
|
||||
@@ -2624,6 +2674,15 @@ void Configuration::impl::on_station_message_line_edit_textChanged(QString const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Configuration::impl::on_groups_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
if(text != upper){
|
||||
ui_->groups_line_edit->setText (upper);
|
||||
}
|
||||
}
|
||||
|
||||
void Configuration::impl::on_qth_message_line_edit_textChanged(QString const &text)
|
||||
{
|
||||
QString upper = text.toUpper();
|
||||
|
||||
@@ -98,6 +98,7 @@ public:
|
||||
QString my_callsign () const;
|
||||
QString my_grid () const;
|
||||
QString my_station () const;
|
||||
QSet<QString> my_groups() const;
|
||||
int activity_aging() const;
|
||||
int callsign_aging() const;
|
||||
QString my_qth () const;
|
||||
@@ -120,6 +121,7 @@ public:
|
||||
void set_spot_to_reporting_networks (bool);
|
||||
bool transmit_directed() const;
|
||||
bool autoreply_off_at_startup () const;
|
||||
bool heartbeat_anywhere() const;
|
||||
bool relay_off() const;
|
||||
bool monitor_off_at_startup () const;
|
||||
bool monitor_last_used () const;
|
||||
@@ -133,7 +135,7 @@ public:
|
||||
bool miles () const;
|
||||
bool quick_call () const;
|
||||
bool disable_TX_on_73 () const;
|
||||
int beacon () const;
|
||||
int heartbeat () const;
|
||||
int watchdog () const;
|
||||
bool TX_messages () const;
|
||||
bool split_mode () const;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <QThread>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "NetworkServerLookup.hpp"
|
||||
|
||||
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
|
||||
@@ -151,10 +153,10 @@ void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
|
||||
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
|
||||
|
||||
bool tx {!on};
|
||||
auto start = QDateTime::currentMSecsSinceEpoch ();
|
||||
auto start = DriftingDateTime::currentMSecsSinceEpoch ();
|
||||
// we must now wait for Tx on the rig, we will wait a short while
|
||||
// before bailing out
|
||||
while (tx != on && QDateTime::currentMSecsSinceEpoch () - start < 1000)
|
||||
while (tx != on && DriftingDateTime::currentMSecsSinceEpoch () - start < 1000)
|
||||
{
|
||||
auto reply = command_with_reply ("<command:9>CmdSendTx<parameters:0>");
|
||||
if (0 == reply.indexOf ("<CmdTX:"))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
WSJTX_BUNDLE="`echo "$0" | sed -e 's/\/Contents\/MacOS\/.*//'`"
|
||||
WSJTX_RESOURCES="$WSJTX_BUNDLE/Contents/Resources"
|
||||
WSJTX_TEMP="/tmp/ft8call/$UID"
|
||||
WSJTX_TEMP="/tmp/js8call/$UID"
|
||||
|
||||
echo "running $0"
|
||||
echo "WSJTX_BUNDLE: $WSJTX_BUNDLE"
|
||||
@@ -13,4 +13,4 @@ export "DYLD_LIBRARY_PATH=$WSJTX_RESOURCES/lib"
|
||||
export "PATH=$WSJTX_RESOURCES/bin:$PATH"
|
||||
|
||||
#export
|
||||
exec "$WSJTX_RESOURCES/bin/ft8call"
|
||||
exec "$WSJTX_RESOURCES/bin/js8call"
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
Notes on FT8Call Installation for Mac OS X
|
||||
(replace all instances of WSJT-X with FT8Call)
|
||||
Notes on JS8Call Installation for Mac OS X
|
||||
(replace all instances of WSJT-X with JS8Call)
|
||||
|
||||
Notes on WSJT-X Installation for Mac OS X
|
||||
-----------------------------------------
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <QDebug>
|
||||
#include "commons.h"
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "moc_Detector.cpp"
|
||||
|
||||
extern "C" {
|
||||
@@ -42,7 +44,7 @@ bool Detector::reset ()
|
||||
void Detector::clear ()
|
||||
{
|
||||
// set index to roughly where we are in time (1ms resolution)
|
||||
// qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
||||
// qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
||||
// unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
|
||||
// dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
|
||||
dec_data.params.kin = 0;
|
||||
@@ -125,7 +127,7 @@ unsigned Detector::secondInPeriod () const
|
||||
{
|
||||
// we take the time of the data as the following assuming no latency
|
||||
// delivering it to us (not true but close enough for us)
|
||||
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
||||
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
||||
|
||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
||||
return secondInToday % m_period;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
qint64 driftms = 0;
|
||||
|
||||
QDateTime DriftingDateTime::currentDateTime(){
|
||||
return QDateTime::currentDateTime().addMSecs(driftms);
|
||||
}
|
||||
|
||||
QDateTime DriftingDateTime::currentDateTimeUtc(){
|
||||
return QDateTime::currentDateTimeUtc().addMSecs(driftms);
|
||||
}
|
||||
|
||||
qint64 DriftingDateTime::currentMSecsSinceEpoch(){
|
||||
return QDateTime::currentMSecsSinceEpoch() + driftms;
|
||||
}
|
||||
|
||||
qint64 DriftingDateTime::drift(){
|
||||
return driftms;
|
||||
}
|
||||
|
||||
void DriftingDateTime::setDrift(qint64 ms){
|
||||
driftms = ms;
|
||||
}
|
||||
|
||||
qint64 DriftingDateTime::incrementDrift(qint64 msdelta){
|
||||
driftms += msdelta;
|
||||
return driftms;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef DRIFTINGDATETIME_H
|
||||
#define DRIFTINGDATETIME_H
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
class DriftingDateTime /*: QDateTime*/
|
||||
{
|
||||
public:
|
||||
static QDateTime currentDateTime();
|
||||
static QDateTime currentDateTimeUtc();
|
||||
static qint64 currentMSecsSinceEpoch();
|
||||
|
||||
static qint64 drift();
|
||||
static void setDrift(qint64 ms);
|
||||
static qint64 incrementDrift(qint64 msdelta);
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif // DRIFTINGDATETIME_H
|
||||
@@ -26,16 +26,16 @@ namespace
|
||||
{
|
||||
FrequencyList_v2::FrequencyItems const default_frequency_list =
|
||||
{
|
||||
{ 1842000, Modes::FT8CALL, IARURegions::ALL}, // 2 above
|
||||
{ 3578000, Modes::FT8CALL, IARURegions::ALL}, // 5 above
|
||||
{ 7078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
||||
{10130000, Modes::FT8CALL, IARURegions::ALL}, // 6 below
|
||||
{14078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
||||
{18104000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
||||
{21078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
||||
{24922000, Modes::FT8CALL, IARURegions::ALL}, // 9 above
|
||||
{28078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
||||
{50318000, Modes::FT8CALL, IARURegions::ALL}, // 5 above
|
||||
{ 1842000, Modes::JS8, IARURegions::ALL}, // 2 above
|
||||
{ 3578000, Modes::JS8, IARURegions::ALL}, // 5 above
|
||||
{ 7078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||
{10130000, Modes::JS8, IARURegions::ALL}, // 6 below
|
||||
{14078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||
{18104000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||
{21078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||
{24922000, Modes::JS8, IARURegions::ALL}, // 9 above
|
||||
{28078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||
{50318000, Modes::JS8, IARURegions::ALL}, // 5 above
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
JT4 JT9 9+65 JT65 QRA SCAT M144 WSPR Echo
|
||||
---------------------------------------------------------------------
|
||||
0. txFirstCheckBox 11 111 1 11 11 11 1
|
||||
1. TxFreqSpinBox 11 111 1 11 11
|
||||
2. RxFreqSpinBox 11 111 1 11 11 1
|
||||
3. sbFtol 1 11 1 11 11 1
|
||||
4. rptSpinBox 11 111 1 11 11 11 1
|
||||
5. sbTR 11 1
|
||||
6. sbCQRxFreq 1
|
||||
7. cbShMsgs 1 1
|
||||
8. cbFast9 11
|
||||
9. cbAutoSeq 1
|
||||
10. cbTx6 1
|
||||
11. pbTxMode 1
|
||||
12. pbR2T 11 11 1 11 11
|
||||
13. pbT2R 11 11 1 11 11
|
||||
14. cbTxLock 1 11 1 11 11
|
||||
15. sbSubMode 1 1 1 11 11
|
||||
16. syncSpinBox 1 1 1 11 11
|
||||
17. WSPR_Controls_Widget 1
|
||||
18. ClrAvgButton 11 1
|
||||
---------------------------------------------------------------------
|
||||
19. FastNormalDeep 11 11 1 11 1 1
|
||||
20. IncludeAveraging 1 1
|
||||
21. IncludeCorrelation 1 1
|
||||
22. EchoGraph 1
|
||||
---------------------------------------------------------------------
|
||||
|
||||
For each mode:
|
||||
Column 1 applies when VHF features is OFF (or col 2 absent)
|
||||
Column 2 (if present) applies when VHF features is ON
|
||||
Column 3 (JT9 only) applies for submodes E-H with Fast checked
|
||||
@@ -1,416 +1 @@
|
||||
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
Installing WSJT-X
|
||||
=================
|
||||
|
||||
Binary packages of WSJT-X are available from the project web site:
|
||||
|
||||
http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html
|
||||
|
||||
|
||||
Building from Source
|
||||
====================
|
||||
|
||||
On Linux systems some of the prerequisite libraries are available in
|
||||
the mainstream distribution repositories. They are Qt v5 and FFTW v3.
|
||||
For MS Windows see the section "Building from Source on MS Windows"
|
||||
below. For Apple Mac see the section "Building from Source on Apple
|
||||
Mac".
|
||||
|
||||
Qt v5, preferably v5.1 or later is required to build WSJT-X.
|
||||
|
||||
Qt v5 multimedia support and serial port is necessary as well as the
|
||||
core Qt v5 components, normally installing the Qt multimedia
|
||||
development package and Qt serialport development package are
|
||||
sufficient to pull in all the required Qt components and dependants as
|
||||
a single transaction. On some systems the Qt multimedia plugin
|
||||
component is separate in the distribution repository an it may also
|
||||
need installing.
|
||||
|
||||
The single precision FFTW v3 library libfftw3f is required along with
|
||||
the libfftw library development package. Normally installing the
|
||||
library development package pulls in all the FFTW v3 libraries
|
||||
including the single precision variant.
|
||||
|
||||
The Hamlib library optionally requires the libusb-1.0 library, if the
|
||||
development version (libusb-1.0-dev) is available Hamlib will
|
||||
configure its custom USB device back end drivers. Most rigs do not
|
||||
require this so normally you can choose not to install libusb-1.0-dev
|
||||
but if you have a SoftRock USB or similar SDR that uses a custom USB
|
||||
interface then it is required.
|
||||
|
||||
The Hamlib library is required. Currently WSJT-X needs to be built
|
||||
using a forked version of the Hamlib git master. This fork contains
|
||||
patches not yet accepted by the Hamlib development team which are
|
||||
essential for correct operation of WSJT-X. To build the Hamlib fork
|
||||
from sources something like the following recipe should suffice:
|
||||
|
||||
$ mkdir ~/hamlib-prefix
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
$ mkdir ../build
|
||||
$ cd ../build
|
||||
$ ../src/configure --prefix=$HOME/hamlib-prefix \
|
||||
--disable-shared --enable-static \
|
||||
--without-cxx-binding --disable-winradio \
|
||||
CFLAGS="-g -O2 -fdata-sections -ffunction-sections" \
|
||||
LDFLAGS="-Wl,--gc-sections"
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
This will build a binary hamlib package located at ~/hamlib-prefix so
|
||||
you will need to add that to your CMAKE_PREFIX_PATH variable in your
|
||||
WSJT-X build. On Linux that is probably the only path you have on
|
||||
CMAKE_PREFIX_PATH unless you are using a locally installed Qt
|
||||
installation.
|
||||
|
||||
To get the sources either download and extract a source tarball from
|
||||
the project web site or preferably fetch the sources directly from the
|
||||
project's subversion repository. The project svn repository has a
|
||||
non-standard layout in that the WSJT-X project is not on the trunk,
|
||||
instead the main code line is in a branch at ^/branches/wsjtx
|
||||
|
||||
$ mkdir -p ~/wsjtx-prefix/build
|
||||
$ cd ~/wsjtx-prefix
|
||||
$ svn checkout svn://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx src
|
||||
|
||||
To build WSJT-X you will need CMake and asciidoc installed.
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix ../src
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
The recipe above will install into /usr by default, if you wish to
|
||||
install in you own directory you can add a prefix-path to the
|
||||
configure step like:
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix \
|
||||
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix ../src
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
this will install WSJT-X at ~/wsjtx-prefix.
|
||||
|
||||
|
||||
Building from Source on MS Windows
|
||||
==================================
|
||||
|
||||
Because building on MS Windows is quite complicated there is an
|
||||
Software Development Kit available that provides all the prerequisite
|
||||
libraries and tools for building WSJT-X. This SDK is called JT-SDK-QT
|
||||
which is documented here:
|
||||
|
||||
http://physics.princeton.edu/pulsar/K1JT/wsjtx-doc/dev-guide-main.html
|
||||
|
||||
If you need to build Hamlib rather than use the Hamlib kit included in
|
||||
the JT-SDK the following recipe should help. Reasons for building
|
||||
Hamlib from source might include picking up the very latest patches or
|
||||
building a different branch that you wish to contribute to.
|
||||
|
||||
Hamlib optionally depends upon libusb-1.0, see "Building from Source"
|
||||
above for more details. If you wish to include support for the
|
||||
optional custom USB Hamlib rig drivers then you must install
|
||||
libusb-1.0 before building Hamlib. The package may be obtained from
|
||||
http://libusb.info/, install it in a convenient location like
|
||||
C:\Tools.
|
||||
|
||||
On Windows there is a complication in that the compilers used to build
|
||||
Qt and WSJT-X are the MinGW ones bundled with the Qt package but
|
||||
Hamlib needs to be build from an MSYS shell with the tools required to
|
||||
build an autotools project. This means that you need to tell the
|
||||
Hamlib configuration to use the Qt bundled MinGW compilers (if you
|
||||
don't then the thread support library use by Hamlib will be
|
||||
incompatible with that used by Qt and WSJT-X). So on Windows the
|
||||
Hamlib build recipe is something like:
|
||||
|
||||
In an MSYS shell:-
|
||||
|
||||
$ mkdir ~/hamib-prefix
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
$ mkdir ../build
|
||||
$ cd ../build
|
||||
../src/configure --prefix=$HOME/hamlib-prefix \
|
||||
--disable-shared --enable-static \
|
||||
--without-cxx-binding --disable-winradio \
|
||||
CC=<path-to-Qt-MinGW-tools>/gcc \
|
||||
CXX=<path-to-Qt-MinGW-tools>/g++ \
|
||||
CFLAGS="-g -O2 -fdata-sections -ffunction-sections -I<path-to-libusb-1.0>/include" \
|
||||
LDFLAGS="-Wl,--gc-sections" \
|
||||
LIBUSB_LIBS="-L<path-to-libusb-1.0>/MinGW32/dll -lusb-1.0"
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
NOTE: <path-to-Qt-MinGQ-tools> should be substituted with the actual
|
||||
path to your Qt bundled tools e.g on my system it is
|
||||
C:\Tools\Qt\Tools\mingw530_32\bin
|
||||
|
||||
NOTE: <path-to-libusb-1.0> should be substituted with the actual path
|
||||
to your libusb-1.0 installation directory e.g. on my system it is
|
||||
C:\Tools\libusb-1.0.20
|
||||
|
||||
This will leave a Hamlib binary package installed at
|
||||
c:/Users/<user-name>/hamlib-prefix which is what needs to be on your
|
||||
CMAKE_PREFIX_PATH. On Windows you almost certainly will be using a
|
||||
CMake tool chain file and this is where you will need to specify the
|
||||
Hamlib binary location as one of the paths in CMAKE_PREFIX_PATH.
|
||||
|
||||
|
||||
Building from Source on Apple Mac
|
||||
=================================
|
||||
|
||||
These instructions are adapted from my Evernote page at:
|
||||
|
||||
https://www.evernote.com/pub/bsomervi/wsjt-xmacbuilds
|
||||
|
||||
There are several ways to get the required GNU and other open source
|
||||
tools and libraries installed, my preference is MacPorts because it is
|
||||
easy to use and does everything we need.
|
||||
|
||||
You will need Xcode, MacPorts, CMake and, Qt. The Xcode install
|
||||
instructions are included in the MacPorts documentation.
|
||||
|
||||
MacPorts
|
||||
--------
|
||||
Install MacPorts from instructions here:
|
||||
|
||||
http://www.macports.org/install.php
|
||||
|
||||
More detailed instructions are available in the documentation:
|
||||
|
||||
https://guide.macports.org
|
||||
|
||||
The ports that need to be installed are:
|
||||
|
||||
autoconf
|
||||
automake
|
||||
libtool
|
||||
pkgconfig
|
||||
texinfo
|
||||
gcc5
|
||||
fftw-3-single +gcc5
|
||||
asciidoc
|
||||
libusb-devel
|
||||
|
||||
These are installed by typing:
|
||||
|
||||
$ sudo port install autoconf automake \
|
||||
libtool pkgconfig texinfo gcc5 asciidoc \
|
||||
fftw-3-single +gcc5 libusb-devel
|
||||
|
||||
Once complete you should have all the tools required to build WSJT-X.
|
||||
|
||||
Uninstalling MacPorts
|
||||
---------------------
|
||||
If at some point you wish to remove the ports from your machine. The
|
||||
instructions are here:
|
||||
|
||||
https://guide.macports.org/#installing.macports.uninstalling .
|
||||
|
||||
Hamlib
|
||||
------
|
||||
First fetch hamlib from the repository, in this case my fork of Hamlib
|
||||
3 until the official repository has all the fixes we need:
|
||||
|
||||
$ mkdir -p ~/hamlib-prefix/build
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
|
||||
The integration branch is my system testing branch which has all my
|
||||
latest published changes.
|
||||
|
||||
To build:
|
||||
|
||||
$ cd ~/hamlib-prefix/build
|
||||
$ ../src/configure \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--disable-winradio \
|
||||
--prefix=$HOME/hamlib-prefix \
|
||||
CFLAGS="-g -O2 -mmacosx-version-min=10.7 -I/opt/local/include" \
|
||||
LIBUSB_LIBS="-L/opt/local/lib -lusb-1.0"
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
The above commands will build hamlib and install it into
|
||||
~/hamlib-prefix. If `make install-strip` fails, try `make install`.
|
||||
|
||||
|
||||
Qt
|
||||
--
|
||||
|
||||
NOTE: As of Qt v5.4 building Qt from source on Mac OS X is no longer
|
||||
necessary since the Qt team have switched to using the modern libc++
|
||||
Standard C++ Library for all distributable run time
|
||||
components. Instead you may simply download a binary installer for OS
|
||||
X 64-bit. The binary installer is here:
|
||||
|
||||
http://www.qt.io/download
|
||||
|
||||
The binary Qt distributions prior to Qt v5.4 from
|
||||
http://www.qt.io/download unfortunately are built to use the libstdc++
|
||||
C++ support library, WSJT-X uses a less geriatric C++ dialect which
|
||||
uses the libc++ C++ support library. This means that you need to
|
||||
build Qt from sources. This is not difficult but does take some time.
|
||||
|
||||
Download the Qt source tarball from
|
||||
http://www.qt.io/download-open-source/, the link is about half way
|
||||
down the page, you want the full sources tar ball shown as a 'tar.gz'
|
||||
link.
|
||||
|
||||
Unpack the sources and cd into the top level directory then type:
|
||||
|
||||
$ ./configure -prefix ~/local/qt-macx-clang -opensource \
|
||||
-confirm-license -platform macx-clang -silent -nomake tests \
|
||||
-nomake examples -sdk macosx10.10 -skip qtwebkit \
|
||||
-skip qtwebkit-examples -skip qtquick1 -skip qtconnectivity \
|
||||
-skip qtlocation -skip qtsensors -skip qtscript \
|
||||
-skip qtwebsockets -skip qtwebengine -skip qtwebchannel \
|
||||
-skip qtwayland -skip qtquickcontrols -skip qtdeclarative \
|
||||
-skip qtxmlpatterns -skip qtenginio
|
||||
$ make -j4
|
||||
$ make install
|
||||
|
||||
If you are building on 10.8 or don't have the 10.10 Mac SDK (Xcode 6)
|
||||
available, you can substitute '-sdk macosx10.9' above.
|
||||
|
||||
The build above will take a few hours to complete.
|
||||
|
||||
|
||||
CMake
|
||||
-----
|
||||
Although CMake is available via MacPorts I prefer to use the binary
|
||||
installer from cake.org as the MacPorts port doesn't include the
|
||||
graphical CMake tool cmake-gui which I find quite useful.
|
||||
|
||||
Fetch the latest CMake universal 64-bit DMG from
|
||||
http://www.cmake.org/download/ open the DMG then drag and drop the
|
||||
application bundle onto the supplied /Applications link.
|
||||
|
||||
To complete the install process you need to run the CMake-gui
|
||||
application as root from a terminal shell as follows:
|
||||
|
||||
$ sudo "/Applications/CMake.app/Contents/MacOS/cmake" --install
|
||||
|
||||
that installs the CMake command line tools which you can verify by
|
||||
typing into a terminal window:
|
||||
|
||||
$ cmake --version
|
||||
|
||||
If the install command above fails with a "No such file or directory"
|
||||
error, that probably means that /usr/local/bin does not exist. You can
|
||||
create it correctly with the following commands:
|
||||
|
||||
$ sudo mkdir -p /usr/local/bin
|
||||
$ sudo chmod 755 /usr/local/bin
|
||||
$ sudo chgrp wheel /usr/local/bin
|
||||
|
||||
and then retry the install command.
|
||||
|
||||
|
||||
WSJT-X
|
||||
------
|
||||
First fetch the source from the repository:
|
||||
|
||||
$ mkdir -p ~/wsjtx-prefix/build
|
||||
$ cd ~/wsjtx-prefix
|
||||
$ svn checkout svn://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx src
|
||||
|
||||
this links to the Subversion repository in a read-only fashion, if you
|
||||
intend to contribute to the project then you probably want to get a
|
||||
developer login and use a read-write checkout. Even if you don't it
|
||||
can be upgraded at a later date.
|
||||
|
||||
The checkout is of the latest code on the project trunk, i.e. the
|
||||
development branch. You can easily switch the checkout to another
|
||||
branch or even a tag if you want to build a prior published
|
||||
generation. For now we will build the latest development sources. To
|
||||
configure:
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ FC=gfortran-mp-5 \
|
||||
cmake \
|
||||
-D CMAKE_PREFIX_PATH="~/Qt/5.7/clang_64;~/hamlib-prefix;/opt/local" \
|
||||
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix \
|
||||
-D CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk \
|
||||
~/wsjtx-prefix/src
|
||||
|
||||
Substitute the Mac OS X SDK version you have installed in the above
|
||||
command if you have a different version from 10.11.
|
||||
|
||||
The CMAKE_PREFIX_PATH variable specifies where CMake should look first
|
||||
for other packages, the two elements may be different depending where
|
||||
you have installed Qt and what version you have (~/local/qt-macx-clang
|
||||
if you have built Qt from sources as described above in the Qt
|
||||
section) and where you installed Hamlib (i.e. the --prefix configure
|
||||
option above in the Hamlib section).
|
||||
|
||||
If you already have the fftw3-dev package installed on your system it
|
||||
may well get selected in preference to the one you built above in the
|
||||
MacPorts installation. It is unlikely that a prior installation of
|
||||
libfftw3f is correctly configured for use in a WSJT-X package, the
|
||||
CMAKE_PREFIX_PATH above is augmented with the MacPorts installation
|
||||
location (/opt/local) to ensure the correct libfftw3f.dylib and
|
||||
headers are located.
|
||||
|
||||
To build:
|
||||
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
which installs the WSJT-X application bundle into ~/wsjtx-prefix
|
||||
|
||||
|
||||
Updating and Rebuilding Hamlib
|
||||
==============================
|
||||
|
||||
From time to time new fixes will be pushed to the Hamlib fork
|
||||
repository integration branch. To pick them up type:
|
||||
|
||||
$ cd ~/hamlib-prefix/src
|
||||
$ git pull
|
||||
|
||||
To rebuild hamlib with the changed sources:
|
||||
|
||||
$ cd ~/hamlib-prefix/build
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
|
||||
Updating and Rebuilding WSJT-X
|
||||
==============================
|
||||
|
||||
To update to the latest sources type:
|
||||
|
||||
$ cd ~/wsjtx-prefix/src
|
||||
$ svn update
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
|
||||
73
|
||||
Bill
|
||||
G4WJS.
|
||||
Detailed instructions coming soon. In the meantime, follow INSTALL-WSJTX instructions, substituting the WSJT-X repository with this one.
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
Installing WSJT-X
|
||||
=================
|
||||
|
||||
Binary packages of WSJT-X are available from the project web site:
|
||||
|
||||
http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html
|
||||
|
||||
|
||||
Building from Source
|
||||
====================
|
||||
|
||||
On Linux systems some of the prerequisite libraries are available in
|
||||
the mainstream distribution repositories. They are Qt v5 and FFTW v3.
|
||||
For MS Windows see the section "Building from Source on MS Windows"
|
||||
below. For Apple Mac see the section "Building from Source on Apple
|
||||
Mac".
|
||||
|
||||
Qt v5, preferably v5.1 or later is required to build WSJT-X.
|
||||
|
||||
Qt v5 multimedia support and serial port is necessary as well as the
|
||||
core Qt v5 components, normally installing the Qt multimedia
|
||||
development package and Qt serialport development package are
|
||||
sufficient to pull in all the required Qt components and dependants as
|
||||
a single transaction. On some systems the Qt multimedia plugin
|
||||
component is separate in the distribution repository an it may also
|
||||
need installing.
|
||||
|
||||
The single precision FFTW v3 library libfftw3f is required along with
|
||||
the libfftw library development package. Normally installing the
|
||||
library development package pulls in all the FFTW v3 libraries
|
||||
including the single precision variant.
|
||||
|
||||
The Hamlib library optionally requires the libusb-1.0 library, if the
|
||||
development version (libusb-1.0-dev) is available Hamlib will
|
||||
configure its custom USB device back end drivers. Most rigs do not
|
||||
require this so normally you can choose not to install libusb-1.0-dev
|
||||
but if you have a SoftRock USB or similar SDR that uses a custom USB
|
||||
interface then it is required.
|
||||
|
||||
The Hamlib library is required. Currently WSJT-X needs to be built
|
||||
using a forked version of the Hamlib git master. This fork contains
|
||||
patches not yet accepted by the Hamlib development team which are
|
||||
essential for correct operation of WSJT-X. To build the Hamlib fork
|
||||
from sources something like the following recipe should suffice:
|
||||
|
||||
$ mkdir ~/hamlib-prefix
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
$ mkdir ../build
|
||||
$ cd ../build
|
||||
$ ../src/configure --prefix=$HOME/hamlib-prefix \
|
||||
--disable-shared --enable-static \
|
||||
--without-cxx-binding --disable-winradio \
|
||||
CFLAGS="-g -O2 -fdata-sections -ffunction-sections" \
|
||||
LDFLAGS="-Wl,--gc-sections"
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
This will build a binary hamlib package located at ~/hamlib-prefix so
|
||||
you will need to add that to your CMAKE_PREFIX_PATH variable in your
|
||||
WSJT-X build. On Linux that is probably the only path you have on
|
||||
CMAKE_PREFIX_PATH unless you are using a locally installed Qt
|
||||
installation.
|
||||
|
||||
To get the sources either download and extract a source tarball from
|
||||
the project web site or preferably fetch the sources directly from the
|
||||
project's subversion repository. The project svn repository has a
|
||||
non-standard layout in that the WSJT-X project is not on the trunk,
|
||||
instead the main code line is in a branch at ^/branches/wsjtx
|
||||
|
||||
$ mkdir -p ~/wsjtx-prefix/build
|
||||
$ cd ~/wsjtx-prefix
|
||||
$ svn checkout svn://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx src
|
||||
|
||||
To build WSJT-X you will need CMake and asciidoc installed.
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix ../src
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
The recipe above will install into /usr by default, if you wish to
|
||||
install in you own directory you can add a prefix-path to the
|
||||
configure step like:
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix \
|
||||
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix ../src
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
this will install WSJT-X at ~/wsjtx-prefix.
|
||||
|
||||
|
||||
Building from Source on MS Windows
|
||||
==================================
|
||||
|
||||
Because building on MS Windows is quite complicated there is an
|
||||
Software Development Kit available that provides all the prerequisite
|
||||
libraries and tools for building WSJT-X. This SDK is called JT-SDK-QT
|
||||
which is documented here:
|
||||
|
||||
http://physics.princeton.edu/pulsar/K1JT/wsjtx-doc/dev-guide-main.html
|
||||
|
||||
If you need to build Hamlib rather than use the Hamlib kit included in
|
||||
the JT-SDK the following recipe should help. Reasons for building
|
||||
Hamlib from source might include picking up the very latest patches or
|
||||
building a different branch that you wish to contribute to.
|
||||
|
||||
Hamlib optionally depends upon libusb-1.0, see "Building from Source"
|
||||
above for more details. If you wish to include support for the
|
||||
optional custom USB Hamlib rig drivers then you must install
|
||||
libusb-1.0 before building Hamlib. The package may be obtained from
|
||||
http://libusb.info/, install it in a convenient location like
|
||||
C:\Tools.
|
||||
|
||||
On Windows there is a complication in that the compilers used to build
|
||||
Qt and WSJT-X are the MinGW ones bundled with the Qt package but
|
||||
Hamlib needs to be build from an MSYS shell with the tools required to
|
||||
build an autotools project. This means that you need to tell the
|
||||
Hamlib configuration to use the Qt bundled MinGW compilers (if you
|
||||
don't then the thread support library use by Hamlib will be
|
||||
incompatible with that used by Qt and WSJT-X). So on Windows the
|
||||
Hamlib build recipe is something like:
|
||||
|
||||
In an MSYS shell:-
|
||||
|
||||
$ mkdir ~/hamib-prefix
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
$ mkdir ../build
|
||||
$ cd ../build
|
||||
../src/configure --prefix=$HOME/hamlib-prefix \
|
||||
--disable-shared --enable-static \
|
||||
--without-cxx-binding --disable-winradio \
|
||||
CC=<path-to-Qt-MinGW-tools>/gcc \
|
||||
CXX=<path-to-Qt-MinGW-tools>/g++ \
|
||||
CFLAGS="-g -O2 -fdata-sections -ffunction-sections -I<path-to-libusb-1.0>/include" \
|
||||
LDFLAGS="-Wl,--gc-sections" \
|
||||
LIBUSB_LIBS="-L<path-to-libusb-1.0>/MinGW32/dll -lusb-1.0"
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
NOTE: <path-to-Qt-MinGQ-tools> should be substituted with the actual
|
||||
path to your Qt bundled tools e.g on my system it is
|
||||
C:\Tools\Qt\Tools\mingw530_32\bin
|
||||
|
||||
NOTE: <path-to-libusb-1.0> should be substituted with the actual path
|
||||
to your libusb-1.0 installation directory e.g. on my system it is
|
||||
C:\Tools\libusb-1.0.20
|
||||
|
||||
This will leave a Hamlib binary package installed at
|
||||
c:/Users/<user-name>/hamlib-prefix which is what needs to be on your
|
||||
CMAKE_PREFIX_PATH. On Windows you almost certainly will be using a
|
||||
CMake tool chain file and this is where you will need to specify the
|
||||
Hamlib binary location as one of the paths in CMAKE_PREFIX_PATH.
|
||||
|
||||
|
||||
Building from Source on Apple Mac
|
||||
=================================
|
||||
|
||||
These instructions are adapted from my Evernote page at:
|
||||
|
||||
https://www.evernote.com/pub/bsomervi/wsjt-xmacbuilds
|
||||
|
||||
There are several ways to get the required GNU and other open source
|
||||
tools and libraries installed, my preference is MacPorts because it is
|
||||
easy to use and does everything we need.
|
||||
|
||||
You will need Xcode, MacPorts, CMake and, Qt. The Xcode install
|
||||
instructions are included in the MacPorts documentation.
|
||||
|
||||
MacPorts
|
||||
--------
|
||||
Install MacPorts from instructions here:
|
||||
|
||||
http://www.macports.org/install.php
|
||||
|
||||
More detailed instructions are available in the documentation:
|
||||
|
||||
https://guide.macports.org
|
||||
|
||||
The ports that need to be installed are:
|
||||
|
||||
autoconf
|
||||
automake
|
||||
libtool
|
||||
pkgconfig
|
||||
texinfo
|
||||
gcc5
|
||||
fftw-3-single +gcc5
|
||||
asciidoc
|
||||
libusb-devel
|
||||
|
||||
These are installed by typing:
|
||||
|
||||
$ sudo port install autoconf automake \
|
||||
libtool pkgconfig texinfo gcc5 asciidoc \
|
||||
fftw-3-single +gcc5 libusb-devel
|
||||
|
||||
Once complete you should have all the tools required to build WSJT-X.
|
||||
|
||||
Uninstalling MacPorts
|
||||
---------------------
|
||||
If at some point you wish to remove the ports from your machine. The
|
||||
instructions are here:
|
||||
|
||||
https://guide.macports.org/#installing.macports.uninstalling .
|
||||
|
||||
Hamlib
|
||||
------
|
||||
First fetch hamlib from the repository, in this case my fork of Hamlib
|
||||
3 until the official repository has all the fixes we need:
|
||||
|
||||
$ mkdir -p ~/hamlib-prefix/build
|
||||
$ cd ~/hamlib-prefix
|
||||
$ git clone git://git.code.sf.net/u/bsomervi/hamlib src
|
||||
$ cd src
|
||||
$ git checkout integration
|
||||
$ ./bootstrap
|
||||
|
||||
The integration branch is my system testing branch which has all my
|
||||
latest published changes.
|
||||
|
||||
To build:
|
||||
|
||||
$ cd ~/hamlib-prefix/build
|
||||
$ ../src/configure \
|
||||
--enable-static \
|
||||
--disable-shared \
|
||||
--disable-winradio \
|
||||
--prefix=$HOME/hamlib-prefix \
|
||||
CFLAGS="-g -O2 -mmacosx-version-min=10.7 -I/opt/local/include" \
|
||||
LIBUSB_LIBS="-L/opt/local/lib -lusb-1.0"
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
The above commands will build hamlib and install it into
|
||||
~/hamlib-prefix. If `make install-strip` fails, try `make install`.
|
||||
|
||||
|
||||
Qt
|
||||
--
|
||||
|
||||
NOTE: As of Qt v5.4 building Qt from source on Mac OS X is no longer
|
||||
necessary since the Qt team have switched to using the modern libc++
|
||||
Standard C++ Library for all distributable run time
|
||||
components. Instead you may simply download a binary installer for OS
|
||||
X 64-bit. The binary installer is here:
|
||||
|
||||
http://www.qt.io/download
|
||||
|
||||
The binary Qt distributions prior to Qt v5.4 from
|
||||
http://www.qt.io/download unfortunately are built to use the libstdc++
|
||||
C++ support library, WSJT-X uses a less geriatric C++ dialect which
|
||||
uses the libc++ C++ support library. This means that you need to
|
||||
build Qt from sources. This is not difficult but does take some time.
|
||||
|
||||
Download the Qt source tarball from
|
||||
http://www.qt.io/download-open-source/, the link is about half way
|
||||
down the page, you want the full sources tar ball shown as a 'tar.gz'
|
||||
link.
|
||||
|
||||
Unpack the sources and cd into the top level directory then type:
|
||||
|
||||
$ ./configure -prefix ~/local/qt-macx-clang -opensource \
|
||||
-confirm-license -platform macx-clang -silent -nomake tests \
|
||||
-nomake examples -sdk macosx10.10 -skip qtwebkit \
|
||||
-skip qtwebkit-examples -skip qtquick1 -skip qtconnectivity \
|
||||
-skip qtlocation -skip qtsensors -skip qtscript \
|
||||
-skip qtwebsockets -skip qtwebengine -skip qtwebchannel \
|
||||
-skip qtwayland -skip qtquickcontrols -skip qtdeclarative \
|
||||
-skip qtxmlpatterns -skip qtenginio
|
||||
$ make -j4
|
||||
$ make install
|
||||
|
||||
If you are building on 10.8 or don't have the 10.10 Mac SDK (Xcode 6)
|
||||
available, you can substitute '-sdk macosx10.9' above.
|
||||
|
||||
The build above will take a few hours to complete.
|
||||
|
||||
|
||||
CMake
|
||||
-----
|
||||
Although CMake is available via MacPorts I prefer to use the binary
|
||||
installer from cake.org as the MacPorts port doesn't include the
|
||||
graphical CMake tool cmake-gui which I find quite useful.
|
||||
|
||||
Fetch the latest CMake universal 64-bit DMG from
|
||||
http://www.cmake.org/download/ open the DMG then drag and drop the
|
||||
application bundle onto the supplied /Applications link.
|
||||
|
||||
To complete the install process you need to run the CMake-gui
|
||||
application as root from a terminal shell as follows:
|
||||
|
||||
$ sudo "/Applications/CMake.app/Contents/MacOS/cmake" --install
|
||||
|
||||
that installs the CMake command line tools which you can verify by
|
||||
typing into a terminal window:
|
||||
|
||||
$ cmake --version
|
||||
|
||||
If the install command above fails with a "No such file or directory"
|
||||
error, that probably means that /usr/local/bin does not exist. You can
|
||||
create it correctly with the following commands:
|
||||
|
||||
$ sudo mkdir -p /usr/local/bin
|
||||
$ sudo chmod 755 /usr/local/bin
|
||||
$ sudo chgrp wheel /usr/local/bin
|
||||
|
||||
and then retry the install command.
|
||||
|
||||
|
||||
WSJT-X
|
||||
------
|
||||
First fetch the source from the repository:
|
||||
|
||||
$ mkdir -p ~/wsjtx-prefix/build
|
||||
$ cd ~/wsjtx-prefix
|
||||
$ svn checkout svn://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx src
|
||||
|
||||
this links to the Subversion repository in a read-only fashion, if you
|
||||
intend to contribute to the project then you probably want to get a
|
||||
developer login and use a read-write checkout. Even if you don't it
|
||||
can be upgraded at a later date.
|
||||
|
||||
The checkout is of the latest code on the project trunk, i.e. the
|
||||
development branch. You can easily switch the checkout to another
|
||||
branch or even a tag if you want to build a prior published
|
||||
generation. For now we will build the latest development sources. To
|
||||
configure:
|
||||
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ FC=gfortran-mp-5 \
|
||||
cmake \
|
||||
-D CMAKE_PREFIX_PATH="~/Qt/5.7/clang_64;~/hamlib-prefix;/opt/local" \
|
||||
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix \
|
||||
-D CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk \
|
||||
~/wsjtx-prefix/src
|
||||
|
||||
Substitute the Mac OS X SDK version you have installed in the above
|
||||
command if you have a different version from 10.11.
|
||||
|
||||
The CMAKE_PREFIX_PATH variable specifies where CMake should look first
|
||||
for other packages, the two elements may be different depending where
|
||||
you have installed Qt and what version you have (~/local/qt-macx-clang
|
||||
if you have built Qt from sources as described above in the Qt
|
||||
section) and where you installed Hamlib (i.e. the --prefix configure
|
||||
option above in the Hamlib section).
|
||||
|
||||
If you already have the fftw3-dev package installed on your system it
|
||||
may well get selected in preference to the one you built above in the
|
||||
MacPorts installation. It is unlikely that a prior installation of
|
||||
libfftw3f is correctly configured for use in a WSJT-X package, the
|
||||
CMAKE_PREFIX_PATH above is augmented with the MacPorts installation
|
||||
location (/opt/local) to ensure the correct libfftw3f.dylib and
|
||||
headers are located.
|
||||
|
||||
To build:
|
||||
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
which installs the WSJT-X application bundle into ~/wsjtx-prefix
|
||||
|
||||
|
||||
Updating and Rebuilding Hamlib
|
||||
==============================
|
||||
|
||||
From time to time new fixes will be pushed to the Hamlib fork
|
||||
repository integration branch. To pick them up type:
|
||||
|
||||
$ cd ~/hamlib-prefix/src
|
||||
$ git pull
|
||||
|
||||
To rebuild hamlib with the changed sources:
|
||||
|
||||
$ cd ~/hamlib-prefix/build
|
||||
$ make
|
||||
$ make install-strip
|
||||
|
||||
|
||||
Updating and Rebuilding WSJT-X
|
||||
==============================
|
||||
|
||||
To update to the latest sources type:
|
||||
|
||||
$ cd ~/wsjtx-prefix/src
|
||||
$ svn update
|
||||
$ cd ~/wsjtx-prefix/build
|
||||
$ cmake --build .
|
||||
$ cmake --build . --target install
|
||||
|
||||
|
||||
73
|
||||
Bill
|
||||
G4WJS.
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <QHostAddress>
|
||||
#include <QColor>
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "moc_MessageClient.cpp"
|
||||
@@ -232,7 +234,7 @@ void MessageClient::impl::heartbeat ()
|
||||
Message m("PING", "", QMap<QString, QVariant>{
|
||||
{"NAME", QVariant(QApplication::applicationName())},
|
||||
{"VERSION", QVariant(QApplication::applicationVersion())},
|
||||
{"UTC", QVariant(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch())}
|
||||
{"UTC", QVariant(DriftingDateTime::currentDateTimeUtc().toMSecsSinceEpoch())}
|
||||
});
|
||||
writeDatagram (m.toJson(), server_, server_port_);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <QHostAddress>
|
||||
#include <QColor>
|
||||
|
||||
#include "udp_export.h"
|
||||
#include "Radio.hpp"
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
@@ -23,7 +22,7 @@ class QString;
|
||||
// applications that use the Qt framework. Other applications should
|
||||
// use this classes' implementation as a reference implementation.
|
||||
//
|
||||
class UDP_EXPORT MessageServer
|
||||
class MessageServer
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
@@ -96,7 +95,7 @@ public:
|
||||
Q_SIGNAL void error (QString const&) const;
|
||||
|
||||
private:
|
||||
class UDP_NO_EXPORT impl;
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace
|
||||
char const * const mode_names[] =
|
||||
{
|
||||
"All",
|
||||
"FT8CALL",
|
||||
"JS8",
|
||||
};
|
||||
std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
enum Mode
|
||||
{
|
||||
ALL, // matches with all modes
|
||||
FT8CALL,
|
||||
JS8,
|
||||
MODES_END_SENTINAL_AND_COUNT // this must be last
|
||||
};
|
||||
Q_ENUM (Mode)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "soundout.h"
|
||||
#include "commons.h"
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "moc_Modulator.cpp"
|
||||
|
||||
extern float gran(); // Noise generator (for tests only)
|
||||
@@ -49,7 +51,7 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
||||
{
|
||||
Q_ASSERT (stream);
|
||||
// Time according to this computer which becomes our base time
|
||||
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||
qint64 ms0 = DriftingDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||
|
||||
if (m_state != Idle)
|
||||
{
|
||||
@@ -176,7 +178,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
||||
if(m_TRperiod==3) slowCwId=false;
|
||||
bool fastCwId=false;
|
||||
static bool bCwId=false;
|
||||
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
||||
qint64 ms = DriftingDateTime::currentMSecsSinceEpoch();
|
||||
float tsec=0.001*(ms % (1000*m_TRperiod));
|
||||
if(m_bFastMode and (icw[0]>0) and (tsec>(m_TRperiod-5.0))) fastCwId=true;
|
||||
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
|
||||
@@ -289,10 +291,6 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
||||
if (m_ic > i1) m_amp = 0.0;
|
||||
|
||||
sample=qRound(m_amp*qSin(m_phi));
|
||||
if(m_toneSpacing < 0) sample=qRound(m_amp*foxcom_.wave[m_ic]);
|
||||
|
||||
// if(m_ic < 100) qDebug() << "Mod C" << m_ic << m_amp << foxcom_.wave[m_ic] << sample;
|
||||
|
||||
samples = load(postProcessSample(sample), samples);
|
||||
++framesGenerated;
|
||||
++m_ic;
|
||||
|
||||
@@ -1,80 +1,35 @@
|
||||
# JS8Call
|
||||
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
Copyright (C) 2001 - 2017 by Joe Taylor, K1JT.
|
||||
FT8 has taken over the airwaves as the digital communication mode for making QSOs over HF/VHF/UHF. The mode has been widely popular as the latest offering in K1JT’s WSJT-X application. FT8 is based on the same foundation as JT65, JT9, and WSPR modes for weak signal communication, but transmits faster with only slightly reduced sensitivity.
|
||||
|
||||
WSJT-X is a computer program designed to facilitate basic amateur
|
||||
radio communication using very weak signals. The first four letters in
|
||||
the program name stand for “Weak Signal communication by K1JT,” while
|
||||
the suffix “-X” indicates that WSJT-X started as an extended (and
|
||||
experimental) branch of the program WSJT.
|
||||
While FT8 is an incredibly robust weak signal mode, it is designed heavily to take advantage of short band openings on HF/VHF/UHF and only offers a minimal QSO framework. However, many operators are using these weak signal qualities to make successful QSOs on the HF bands where other modes fail.
|
||||
|
||||
WSJT-X Version 1.6 offers five protocols or “modes”: JT4, JT9, JT65
|
||||
WSPR, and Echo. The first three are designed for making reliable QSOs
|
||||
under extreme weak-signal conditions. They use nearly identical
|
||||
message structure and source encoding. JT65 was designed for EME
|
||||
(“moonbounce”) on the VHF/UHF bands and has also proven very effective
|
||||
for worldwide QRP communication on the HF bands. JT9 is optimized for
|
||||
the LF, MF, and lower HF bands. It is 2 dB more sensitive than JT65
|
||||
while using less than 10% of the bandwidth. JT4 offers a wide variety
|
||||
of tone spacings and has proved very effective for EME on microwave
|
||||
bands up to 24 GHz. All three of these modes use one-minute timed
|
||||
sequences of alternating transmission and reception, so a minimal QSO
|
||||
takes four to six minutes — two or three transmissions by each
|
||||
station, one sending in odd UTC minutes and the other even. On the HF
|
||||
bands, world-wide QSOs are possible using power levels of a few watts
|
||||
and compromise antennas. On VHF bands and higher, QSOs are possible
|
||||
(by EME and other propagation types) at signal levels 10 to 15 dB
|
||||
below those required for CW.
|
||||
JS8Call is an experiment to test the feasibility of a digital mode with the robustness of FT8, combined with a messaging and network protocol layer for weak signal communication on HF, using keyboard-to-keyboard style interface. JS8Call is heavily inspired by WSJT-X, Fldigi, and FSQCall and would not exist without the hard work and dedication of the many developers in the amateur radio community.
|
||||
|
||||
WSPR (pronounced “whisper”) stands for Weak Signal Propagation
|
||||
Reporter. The WSPR protocol was designed for probing potential
|
||||
propagation paths using low-power transmissions. WSPR messages
|
||||
normally carry the transmitting station’s callsign, grid locator, and
|
||||
transmitter power in dBm, and they can be decoded at signal-to-noise
|
||||
ratios as low as -28 dB in a 2500 Hz bandwidth. WSPR users with
|
||||
internet access can automatically upload their reception reports to a
|
||||
central database called {wsprnet} that provides a mapping facility,
|
||||
archival storage, and many other features.
|
||||
* Read more on the original design inspiration here: https://github.com/jsherer/js8call
|
||||
|
||||
Echo mode allows you to detect and measure your own lunar echoes, even
|
||||
if they are far below the audible threshold.
|
||||
|
||||
WSJT-X provides spectral displays for passbands up to 5 kHz, flexible
|
||||
rig control for nearly all modern radios used by amateurs, and a wide
|
||||
variety of special aids such as automatic Doppler tracking for EME
|
||||
QSOs and Echo testing. The program runs equally well on Windows,
|
||||
Macintosh, and Linux systems, and installation packages are available
|
||||
for all three platforms.
|
||||
|
||||
WSJT-X is an open-source project released under the GPL license (See
|
||||
COPYING). If you have programming or documentation skills or would
|
||||
like to contribute to the project in other ways, please make your
|
||||
interests known to the development team. The project’s source-code
|
||||
repository can be found at https://sourceforge.net/projects/wsjt, and
|
||||
most communication among the developers takes place on the email
|
||||
reflector https://sourceforge.net/p/wsjt/mailman. User-level
|
||||
questions and answers, and general communication among users is found
|
||||
on the https://groups.yahoo.com/neo/groups/wsjtgroup/info email
|
||||
reflector.
|
||||
* For release announcements and discussion, join the JS8Call mailing list here: https://groups.io/g/js8call
|
||||
|
||||
|
||||
Project web site:
|
||||
# Notice
|
||||
|
||||
http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html
|
||||
JS8Call is a derivative of the WSJT-X application, restructured and redesigned for message passing using FT8 modulation. It is not supported by nor endorsed by the WSJT-X development group. While the WSJT-X group maintains copyright over the original work and code, JS8Call is a derivative work licensed under and in accordance with the terms of the GPLv3 license. The source code modifications are public and can be found in this repository: https://bitbucket.org/widefido/wsjtx/
|
||||
|
||||
Project mailing list (shared with other applications from the same
|
||||
team):
|
||||
|
||||
https://groups.yahoo.com/neo/groups/wsjtgroup
|
||||
# History
|
||||
|
||||
* July 6, 2017 - The initial idea of using a modification to the FT8 protocol to support long-form QSOs was developed by Jordan, KN4CRD, and submitted to the WSJT-X mailing list: https://sourceforge.net/p/wsjt/mailman/message/35931540/
|
||||
* August 31, 2017 - Jordan, KN4CRD, did a little development and modified WSJT-X to support long-form QSOs using the existing FT8 protocol: https://sourceforge.net/p/wsjt/mailman/message/36020051/ He sent a video example to the WSJT-X group: https://widefido.wistia.com/medias/7bb1uq62ga
|
||||
* January 8, 2018 - Jordan, KN4CRD, started working on the design of a long-form QSO application built on top of FT8 with a redesigned interface.
|
||||
* February 9, 2018 - Jordan, KN4CRD, submitted question to the WSJT-X group to see if there was any interest in pursuing the idea: https://sourceforge.net/p/wsjt/mailman/message/36221549/
|
||||
* February 10, 2018 - Jordan KN4CRD, Julian OH8STN, John N0JDS, and the Portable Digital QRP group did an experiment using FSQ. The idea of JS8Call, combining FT8, long-form QSOs, and FSQCall like features was born.
|
||||
* February 11, 2018 - Jordan, KN4CRD, inquired about the idea of integrating long-form messages into WSJT-X: https://sourceforge.net/p/wsjt/mailman/message/36223372/
|
||||
* February 12, 2018 - Joe Taylor, K1JT, wrote back: https://sourceforge.net/p/wsjt/mailman/message/36224507/ saying that “Please don't let my comment discourage you from proceeding as you wish, toward something new.”
|
||||
* March 4, 2018 - Jordan, KN4CRD, published a design document for JS8Call: https://github.com/jsherer/js8call
|
||||
* July 6, 2018 - Version 0.0.1 of JS8Call released to the development group
|
||||
* July 15, 2018 - Version 0.1 released - a dozen testers
|
||||
* July 21, 2018 - Version 0.2 released - 75 testers
|
||||
* July 27, 2018 - Version 0.3 released - 150 testers
|
||||
* August 12, 2018 - Version 0.4 released - (“leaked” on QRZ) - 500 testers
|
||||
* September 2, 2018 - Version 0.5 released - 3000 testers
|
||||
* September 14, 2018 - Version 0.6 released - 5000 testers
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include <QLocale>
|
||||
#include <QList>
|
||||
|
||||
#include "udp_export.h"
|
||||
|
||||
class QVariant;
|
||||
class QString;
|
||||
|
||||
@@ -26,7 +24,7 @@ namespace Radio
|
||||
//
|
||||
// Qt type registration
|
||||
//
|
||||
void UDP_NO_EXPORT register_types ();
|
||||
void register_types ();
|
||||
|
||||
//
|
||||
// Frequency type conversion.
|
||||
@@ -34,27 +32,27 @@ namespace Radio
|
||||
// QVariant argument is convertible to double and is assumed to
|
||||
// be scaled by (10 ** -scale).
|
||||
//
|
||||
Frequency UDP_EXPORT frequency (QVariant const&, int scale,
|
||||
Frequency frequency (QVariant const&, int scale,
|
||||
bool * ok = nullptr, QLocale const& = QLocale ());
|
||||
FrequencyDelta UDP_EXPORT frequency_delta (QVariant const&, int scale,
|
||||
FrequencyDelta frequency_delta (QVariant const&, int scale,
|
||||
bool * ok = nullptr, QLocale const& = QLocale ());
|
||||
|
||||
//
|
||||
// Frequency type formatting
|
||||
//
|
||||
QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||
QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
|
||||
//
|
||||
// Callsigns
|
||||
//
|
||||
bool UDP_EXPORT is_callsign (QString const&);
|
||||
bool UDP_EXPORT is_compound_callsign (QString const&);
|
||||
QString UDP_EXPORT base_callsign (QString);
|
||||
QString UDP_EXPORT effective_prefix (QString);
|
||||
bool is_callsign (QString const&);
|
||||
bool is_compound_callsign (QString const&);
|
||||
QString base_callsign (QString);
|
||||
QString effective_prefix (QString);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE (Radio::Frequency);
|
||||
|
||||
@@ -1,410 +0,0 @@
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
Copyright 2001 - 2018 by Joe Taylor, K1JT.
|
||||
|
||||
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.9.1
|
||||
June 1, 2018
|
||||
-----------------------------
|
||||
|
||||
This critical bug fix release repairs an unintended restriction in the FT8
|
||||
DXpedition mode. It supersedes v1.9.0 and must be used for DXpedition Fox
|
||||
operators.
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.9.0
|
||||
May 28, 2018
|
||||
-----------------------------
|
||||
|
||||
Changes from WSJT-X Version 1.9.0-rc4 include the following:
|
||||
- Display in the right text window of MSK144 messages addressed to
|
||||
"MyCall" has been restored.
|
||||
|
||||
- Fox is not allowed to transmit in any of the default FT8 sub-bands.
|
||||
|
||||
- Fox can now work Hounds using compound callsigns.
|
||||
|
||||
- Fox can now transmit free-text messages (and any standard FT8 message)
|
||||
by using Tab 1 or Tab 2.
|
||||
|
||||
- Added a checkbox to enable more frequent programmed CQs by Fox.
|
||||
Default is OFF.
|
||||
|
||||
- Alt+N keyboard shortcut has been restored.
|
||||
|
||||
- MacOS program crash on unexpected decode request has been fixed.
|
||||
|
||||
|
||||
- Several minor bug fixes and added convenience features.
|
||||
|
||||
- Hamlib, support for TRXManger added.
|
||||
|
||||
- Hamlib, improved support for flrig.
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.9.0-rc4
|
||||
April 30, 2018
|
||||
---------------------------------
|
||||
|
||||
Changes from WSJT-X Version 1.9.0-rc3 include the following:
|
||||
|
||||
- Corrected a number of flaws in Fox and Hound behavior, FT8
|
||||
DXpedition Mode
|
||||
|
||||
- Decoded CQ calls where a prefix has been used as a suffix should
|
||||
have the DXCC entity name assigned correctly in almost all cases
|
||||
|
||||
- Improved AFC capability for the wider JT65 sub-modes to help with
|
||||
drifting signals
|
||||
|
||||
- Better support for macOS using hi-DPI Retina displays
|
||||
|
||||
- New UDP message that allows external applications to highlight
|
||||
decoded callsigns
|
||||
|
||||
- Main-screen geometry and state of the "splitter" setting between its
|
||||
two text windows is now properly restored after program restart.
|
||||
|
||||
- New simulator jt49sim[.exe] replaces jt4sim and jt9sim
|
||||
|
||||
- Correct S/N measurements for the JT9 slow/wide submodes
|
||||
|
||||
- Other minor bug fixes
|
||||
|
||||
- Updated copy of cty.dat
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.9.0-rc3
|
||||
March 18, 2018
|
||||
---------------------------------
|
||||
|
||||
Changes from WSJT-X Version 1.9.0-rc2 include the following:
|
||||
|
||||
- Corrected a number of flaws in Fox behavior, FT8 DXpedition Mode
|
||||
|
||||
- Allow Hounds to use compound callsigns
|
||||
|
||||
- Write debugging information to FoxQSO.txt.
|
||||
|
||||
- Fix the "Blue Decode Button" bug
|
||||
|
||||
- Allow partial processing of incoming UDP Reply messages so that
|
||||
non-CQ/QRZ decodes can be processed. The processing is the same as
|
||||
double-clicking the same decoded message within WSJT-X except that
|
||||
"Enable Tx" will not be enabled.
|
||||
|
||||
- Send DX grid locator to wsjt_status.txt, for use by applications like
|
||||
PstRotatorAZ
|
||||
|
||||
- Correct the display of DXCC status of KG4 calls
|
||||
|
||||
- Updated copy of cty.dat
|
||||
|
||||
- Updates to documentation
|
||||
|
||||
- Other minor bug fixes
|
||||
|
||||
- This release contains updated Hamlib functionality including changes
|
||||
to the Yaesu FT-817 back end that allows the uBITx kit transceiver
|
||||
to be CAT controlled by WSJT-X.
|
||||
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.9.0-rc2
|
||||
February 26, 2018
|
||||
---------------------------------
|
||||
|
||||
Changes from WSJT-X Version 1.8.0 include the following:
|
||||
|
||||
- New FT8 DXpedition Mode to facilitate high QSO rates in pileup
|
||||
situations
|
||||
|
||||
- Decoding improvements for JT65 mode, including a priori (AP)
|
||||
decoding when VHF/UHF/Microwave features are enabled
|
||||
|
||||
- Optional Auto-Sequencing in JT4, JT9, and JT65 when
|
||||
VHF/UHF/Microwave features are enabled
|
||||
|
||||
- Better suppression of low-confidence false decodes generated by AP
|
||||
decoding in FT8 mode
|
||||
|
||||
- Improved decoding performance for WSPR mode, especially effective at
|
||||
LF and MF
|
||||
|
||||
- Minor adjustments to auto-sequencing behavior
|
||||
|
||||
- More flexible Doppler control features for EME
|
||||
|
||||
- Improved waterfall sensitivity for very weak signals
|
||||
|
||||
- Automatic real-time forwarding of logged information to N1MM Logger+
|
||||
|
||||
- Expanded and improved UDP messages sent to companion programs
|
||||
|
||||
- Bug fixes and other minor tweaks to user interface
|
||||
|
||||
A primary purpose of this beta release is to allow field testing of
|
||||
FT8 DXpedition Mode. Instructions for this mode are posted here:
|
||||
|
||||
http://physics.princeton.edu/pulsar/k1jt/FT8_DXpedition_Mode.pdf
|
||||
|
||||
Contacts in FT8 DXpedition Mode must use WSJT-X v1.9.0 at both ends of
|
||||
the QSO. Please report any anomalous behavior to email list
|
||||
wsjt-devel@lists.sourceforge.net. You must be a subscriber in order
|
||||
to post there.
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.8.0
|
||||
October 27, 2017
|
||||
-----------------------------
|
||||
|
||||
This is the full General Availability release of WSJT-X Version 1.8.0.
|
||||
|
||||
Changes from WSJT-X Version 1.8.0-rc3 are very minor:
|
||||
|
||||
- Right-click on the Wide Graph now pops up a Context Menu. Select
|
||||
the item *Set Rx & Tx Offset* to complete a one-handed setting of
|
||||
both red and green frequency markers.
|
||||
|
||||
- Several clarifications and additions to Tool Tips and the User Guide.
|
||||
|
||||
|
||||
We recommend that all users should upgrade to WSJT-X Version 1.8.0.
|
||||
|
||||
If you upgrade from v1.8.0-rc1 it may be necessary to do a one-time
|
||||
reset of the default list of suggested operating frequencies. Go to
|
||||
*File->Settings->Frequencies*, right click on the table and select
|
||||
*Reset*.
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.8.0-rc3
|
||||
October 16, 2017
|
||||
---------------------------------
|
||||
|
||||
Most (but not all) changes since Version 1.8.0-rc2 involve user
|
||||
control of the increasingly popular FT8 mode. The "RC3" release also
|
||||
includes minor bug fixes and updates to the WSJT-X User Guide.
|
||||
|
||||
The following list includes all of the more important changes:
|
||||
|
||||
- New optimization of GUI for simplex and split behavior in FT8 mode.
|
||||
|
||||
1. Checkbox "Lock Tx Freq" on main window is relabeled "Hold Tx Freq".
|
||||
|
||||
2. Double-clicking on decoded messages that do not contain your own
|
||||
call moves both Rx and Tx frequencies. If the first callsign is
|
||||
your own call, only Rx freq moves.
|
||||
|
||||
3. Double-clicking on decoded messages moves the Rx frequency. If
|
||||
"Hold Tx Freq" is checked, Tx frequency is moved only if CTRL was
|
||||
held down.
|
||||
|
||||
4. Clicking on the waterfall moves Rx and Tx frequencies as
|
||||
before: Rx only on a simple click, Tx only on SHIFT-click, and
|
||||
both on CTRL-click. This happens even if "Hold Tx Freq" is
|
||||
checked.
|
||||
|
||||
- Add a semi-automated "FreqCal" procedure: see *Solve for calibration
|
||||
parameters* on the Tools menu.
|
||||
|
||||
- Improv auto-sequencing behavior: stop and on-frequency
|
||||
transmission if a called station comes back to someone else.
|
||||
|
||||
- Improve S/N estimation in some situations involving QRM.
|
||||
|
||||
- Fix an initialization issue with user-modified application fonts.
|
||||
|
||||
- Fix an issue with Tx5 message generation with Type 2 compound calls.
|
||||
|
||||
- Enhance and improve the ADIF parser of logbook records. Update
|
||||
the band limits as per ADIF 3.0.6 specification.
|
||||
|
||||
- Increase the FT8 DT range to +/- 2.5 s.
|
||||
|
||||
- Do not allow window manager events to close the astronomical data
|
||||
window.
|
||||
|
||||
- Add an "Erase" item to the context (right-click) menu for decoded
|
||||
text.
|
||||
|
||||
- Extend UDP messages with an "off air" boolean field indicating that
|
||||
the decode was derived from a .WAV file playback rather than an on air
|
||||
reception.
|
||||
|
||||
- Extend reference applications to use the new off air decode message
|
||||
field.
|
||||
|
||||
- Improve performance of FT8 decoder, especially for overlapping
|
||||
signals.
|
||||
|
||||
- Allow specialized use of "x2 Tone Spacing" in FT8 and slow JT9
|
||||
modes.
|
||||
|
||||
- Move "NA VHF Contest Mode" checkbox to main screen. Query the
|
||||
operator if d > 10000 km.
|
||||
|
||||
- Adjust UI to improve portability with font size changes and between
|
||||
platforms.
|
||||
|
||||
- Extend UDP Reply message to support keyboard modifiers. This allows
|
||||
UDP servers to emulate keyboard modified double-clicks on decoded
|
||||
messages, e.g. ALT+double-click for replying to a CQ or QRZ call
|
||||
without changing ones Tx frequency offset.
|
||||
|
||||
- Update the cty.dat file (21st Sept 2017).
|
||||
|
||||
- Ensure that Fast Graph is properly initialized.
|
||||
|
||||
- Better handling of worked before and country name display. Appended
|
||||
text is added at a fixed column unless the message overlaps in which
|
||||
case the appended information floats to the right.
|
||||
|
||||
- Restore printing of MSK144 decode quality information.
|
||||
|
||||
- Display Echo Graph automatically when Echo mode is started.
|
||||
|
||||
- Fix a bug that prevented double-click on a JT65 EME-style "OOO"
|
||||
message from populating the Tx message boxes.
|
||||
|
||||
- Fixed behavior with double-click on 'CQ <AA-ZZ> <call> <grid>.'
|
||||
|
||||
- Update the "blank line" divider with band ID at 4*TRperiod/5.
|
||||
|
||||
- Fix cty.dat lookups that were not honouring exact match flags
|
||||
|
||||
- Add some further Copyright protections.
|
||||
|
||||
- Fix a bug involving "firstcall contains mycall" but not equal to mycall.
|
||||
|
||||
- Fix an issue with editing IARU regions in the working frequencies table.
|
||||
|
||||
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.8.0-rc2
|
||||
September 2, 2017
|
||||
---------------------------------
|
||||
|
||||
Implementation of FT8 and its auto-sequencing feature is now more
|
||||
capable and more polished. The decoder is faster and better: it now
|
||||
includes signal subtraction, multi-pass decoding, and the use of
|
||||
accumulated "a priori" information as a QSO progresses. Sensitivity
|
||||
extends downward as far as -24 dB in some circumstances. Overlapping
|
||||
signals 2 and 3 deep are frequently decoded at essentially the same
|
||||
frequency. On a crowded band we sometimes see more than 30 decodes in
|
||||
a single 15-second interval, over a 2 kHz window. The North American
|
||||
VHF Contesting Mode has been extended to include both FT8 and MSK144
|
||||
modes.
|
||||
|
||||
The "RC2" release also includes many minor bug fixes and an
|
||||
extensively updated WSJT-X User Guide.
|
||||
|
||||
Depending on what code revision you upgrade from, it may be necessary
|
||||
to do a one-time reset of the default list of suggested operating
|
||||
frequencies. Go to *File->Settings->Frequencies*, right click on
|
||||
the table and select *Reset*.
|
||||
|
||||
|
||||
Release: WSJT-X Version 1.8.0
|
||||
-----------------------------
|
||||
|
||||
NEW FEATURES IN WSJT-X Version 1.8.0
|
||||
------------------------------------
|
||||
|
||||
1. New mode called FT8: sensitivity down to -20 dB on the AWGN
|
||||
channel; QSOs 4 times faster than JT65 or JT9; auto-sequencing
|
||||
includes an option to respond automatically to first decoded
|
||||
reply to your CQ.
|
||||
|
||||
2. New mode for accurate Frequency Calibration of your radio.
|
||||
|
||||
3. Improved performance of decoders for JT65, QRA64, and MSK144.
|
||||
MSK144 includes facilities for amplitide and phase equalization
|
||||
and an "SWL" mode for short-format messages.
|
||||
|
||||
4. Options to minimize screen space used by Main and Wide Graph
|
||||
windows.
|
||||
|
||||
5. Enhanced management scheme for table of operating frequencies, and
|
||||
a new set of default frequencies specific to the three IARU
|
||||
Regions.
|
||||
|
||||
6. Improved CAT control for many rigs, including those controlled
|
||||
through Commander or OmniRig.
|
||||
|
||||
7. New keyboard shortcuts to set "Tx even/1st" ON or OFF.
|
||||
|
||||
8. A number of (mostly minor) bug fixes and tweaks to the user
|
||||
interface. For example: new behavior for the audio level slider;
|
||||
correctly logged QSO start times in certain situations; correct
|
||||
control of FT-891/991 and some other radios via rigctld.
|
||||
|
||||
At the time of the v1.8.0-rc1 release the following tasks are yet to
|
||||
be completed:
|
||||
|
||||
1. Updates to WSJT-X User Guide.
|
||||
2. Sample files for FT8.
|
||||
3. Enhanced decoding using AP ("a priori") information.
|
||||
4. Signal subtraction and multi-pass decoding.
|
||||
5. Option to Auto-respond to the weakest responder to your CQ.
|
||||
|
||||
|
||||
Installation packages for Windows, Linux, OS X, and Raspbian can be
|
||||
downloaded from the WSJT web site:
|
||||
http://physics.princeton.edu/pulsar/K1JT/wsjtx.html
|
||||
|
||||
Please send bug reports to either wsjtgroup@yahoogroups.com or
|
||||
wsjt-devel@lists.sourceforge.net. Such reports should include a full
|
||||
prescription of steps to reproduce the undesired behavior. You must
|
||||
be a subscriber to post to either of these lists.
|
||||
|
||||
|
||||
Brief Description of the FT8 Protocol
|
||||
-------------------------------------
|
||||
|
||||
WSJT-X Version 1.8.0 includes a new mode called FT8, developed by K9AN
|
||||
and K1JT. The mode name "FT8" stands for "Franke and Taylor, 8-FSK
|
||||
modulation". FT8 uses 15-second T/R sequences and provides 50% or
|
||||
better decoding probability down to -20 dB on an AWGN channel. An
|
||||
auto-sequencing facility includes an option to respond automatically
|
||||
to the first decoded reply to your CQ. FT8 QSOs are 4 times faster
|
||||
than those made with JT65 or JT9. FT8 is an excellent mode for HF
|
||||
DXing and for situations like multi-hop E_s on 6 meters, where deep
|
||||
QSB may make fast and reliable completion of QSOs desirable.
|
||||
|
||||
Some important characteristics of FT8:
|
||||
|
||||
- T/R sequence length: 15 s
|
||||
- Message length: 75 bits + 12-bit CRC
|
||||
- FEC code: LDPC(174,87)
|
||||
- Modulation: 8-FSK, tone spacing 6.25 Hz
|
||||
- Constant-envelope waveform
|
||||
- Occupied bandwidth: 50 Hz
|
||||
- Synchronization: 7x7 Costas arrays at start, middle, and end
|
||||
- Transmission duration: 79*1920/12000 = 12.64 s
|
||||
- Decoding threshold: -20 dB; several dB lower with AP decoding
|
||||
- Multi-decoder finds and decodes all FT8 signals in passband
|
||||
- Optional auto-sequencing and auto-reply to a CQ response
|
||||
- Operational behavior similar to JT9, JT65
|
||||
|
||||
We plan to implement signal subtraction, two-pass decoding, and use of
|
||||
a priori (AP) information in the decoder. These features are not yet
|
||||
activated in v1.8.0.
|
||||
|
||||
We haven't yet finalized what the three extra bits in the message
|
||||
payload will be used for. Suggestions are welcome!
|
||||
|
||||
-- Joe, K1JT, for the WSJT Development Team
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
#include "SampleDownloader.hpp"
|
||||
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
#include "SettingsGroup.hpp"
|
||||
#include "SampleDownloader/Directory.hpp"
|
||||
#include "MessageBox.hpp"
|
||||
|
||||
#include "moc_SampleDownloader.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const title = "Download Samples";
|
||||
}
|
||||
|
||||
class SampleDownloader::impl final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit impl (QSettings * settings, Configuration const *, QNetworkAccessManager *, QWidget * parent);
|
||||
~impl () {save_window_state ();}
|
||||
|
||||
void refresh ()
|
||||
{
|
||||
show ();
|
||||
raise ();
|
||||
activateWindow ();
|
||||
directory_.refresh (http_only_check_box_.isChecked ());
|
||||
}
|
||||
|
||||
protected:
|
||||
void closeEvent (QCloseEvent * e) override
|
||||
{
|
||||
save_window_state ();
|
||||
QDialog::closeEvent (e);
|
||||
}
|
||||
|
||||
private:
|
||||
void save_window_state ()
|
||||
{
|
||||
SettingsGroup g (settings_, title);
|
||||
settings_->setValue ("geometry", saveGeometry ());
|
||||
settings_->setValue ("SamplesURL", url_line_edit_.text ());
|
||||
settings_->setValue ("HTTPOnly", http_only_check_box_.isChecked ());
|
||||
}
|
||||
|
||||
Q_SLOT void button_clicked (QAbstractButton *);
|
||||
|
||||
QSettings * settings_;
|
||||
Directory directory_;
|
||||
QGridLayout main_layout_;
|
||||
QVBoxLayout left_layout_;
|
||||
QDialogButtonBox button_box_;
|
||||
QWidget details_widget_;
|
||||
QFormLayout details_layout_;
|
||||
QLineEdit url_line_edit_;
|
||||
QCheckBox http_only_check_box_;
|
||||
};
|
||||
|
||||
#include "SampleDownloader.moc"
|
||||
|
||||
SampleDownloader::SampleDownloader (QSettings * settings, Configuration const * configuration
|
||||
, QNetworkAccessManager * network_manager, QWidget * parent)
|
||||
: m_ {settings, configuration, network_manager, parent}
|
||||
{
|
||||
}
|
||||
|
||||
SampleDownloader::~SampleDownloader ()
|
||||
{
|
||||
}
|
||||
|
||||
void SampleDownloader::show ()
|
||||
{
|
||||
m_->refresh ();
|
||||
}
|
||||
|
||||
SampleDownloader::impl::impl (QSettings * settings
|
||||
, Configuration const * configuration
|
||||
, QNetworkAccessManager * network_manager
|
||||
, QWidget * parent)
|
||||
: QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint}
|
||||
, settings_ {settings}
|
||||
, directory_ {configuration, network_manager}
|
||||
, button_box_ {QDialogButtonBox::Close, Qt::Vertical}
|
||||
{
|
||||
setWindowTitle (windowTitle () + ' ' + tr (title));
|
||||
resize (500, 600);
|
||||
{
|
||||
SettingsGroup g {settings_, title};
|
||||
restoreGeometry (settings_->value ("geometry", saveGeometry ()).toByteArray ());
|
||||
url_line_edit_.setText (settings_->value ("SamplesURL", PROJECT_SAMPLES_URL).toString ());
|
||||
http_only_check_box_.setChecked (settings_->value ("HTTPOnly", false).toBool ());
|
||||
directory_.url_root (url_line_edit_.text ());
|
||||
}
|
||||
|
||||
setWindowTitle (QApplication::applicationName () + " - " + tr ("Download Samples"));
|
||||
|
||||
button_box_.button (QDialogButtonBox::Close)->setDefault (true);
|
||||
button_box_.addButton ("&Abort", QDialogButtonBox::DestructiveRole);
|
||||
button_box_.addButton ("&Refresh", QDialogButtonBox::ResetRole);
|
||||
left_layout_.addWidget (&directory_);
|
||||
|
||||
auto details_button = button_box_.addButton ("&Details", QDialogButtonBox::HelpRole);
|
||||
details_button->setCheckable (true);
|
||||
details_widget_.hide ();
|
||||
details_layout_.setMargin (0);
|
||||
details_layout_.addRow ("Base URL for samples:", &url_line_edit_);
|
||||
details_layout_.addRow ("Only use HTTP:", &http_only_check_box_);
|
||||
http_only_check_box_.setToolTip ("Check this is you get SSL/TLS errors");
|
||||
details_widget_.setLayout (&details_layout_);
|
||||
|
||||
main_layout_.addLayout (&left_layout_, 0, 0);
|
||||
main_layout_.addWidget (&button_box_, 0, 1);
|
||||
main_layout_.addWidget (&details_widget_, 1, 0, 1, 2);
|
||||
main_layout_.setRowStretch (1, 2);
|
||||
setLayout (&main_layout_);
|
||||
|
||||
connect (&button_box_, &QDialogButtonBox::clicked, this, &SampleDownloader::impl::button_clicked);
|
||||
connect (details_button, &QAbstractButton::clicked, &details_widget_, &QWidget::setVisible);
|
||||
connect (&url_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||
if (directory_.url_root (url_line_edit_.text ()))
|
||||
{
|
||||
directory_.refresh (http_only_check_box_.isChecked ());
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("Input Error"), tr ("Invalid URL format"));
|
||||
}
|
||||
});
|
||||
connect (&http_only_check_box_, &QAbstractButton::toggled, [this] (bool checked) {
|
||||
directory_.refresh (checked);
|
||||
});
|
||||
}
|
||||
|
||||
void SampleDownloader::impl::button_clicked (QAbstractButton * button)
|
||||
{
|
||||
switch (button_box_.buttonRole (button))
|
||||
{
|
||||
case QDialogButtonBox::RejectRole:
|
||||
hide ();
|
||||
break;
|
||||
|
||||
case QDialogButtonBox::DestructiveRole:
|
||||
directory_.abort ();
|
||||
break;
|
||||
|
||||
case QDialogButtonBox::ResetRole:
|
||||
directory_.refresh (http_only_check_box_.isChecked ());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef SAMPLE_DOWNLOADER_HPP__
|
||||
#define SAMPLE_DOWNLOADER_HPP__
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QSettings;
|
||||
class QWidget;
|
||||
class QNetworkAccessManager;
|
||||
class Configuration;
|
||||
|
||||
//
|
||||
// SampleDownloader - A Dialog to maintain sample files
|
||||
//
|
||||
// This uses a Qt Dialog window that contains a tree view of the
|
||||
// available sample files on a web or ftp server. The files can be
|
||||
// installed locally by ticking a check box or removed from the local
|
||||
// machine by un-ticking the check boxes.
|
||||
//
|
||||
// The class requires a pointer to an open QSettings instance where it
|
||||
// will save its persistent state, a pointer to a WSJT-X Configuration
|
||||
// instance that is used to obtain configuration information like the
|
||||
// current file save location and, a pointer to a
|
||||
// QNetworkAccessManager instance which is used for network requests.
|
||||
//
|
||||
// An instance of SampleDownloader need not be destroyed after use,
|
||||
// just call SampleDownloader::show() to make the dialog visible
|
||||
// again.
|
||||
//
|
||||
class SampleDownloader final
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
SampleDownloader (QSettings * settings
|
||||
, Configuration const *
|
||||
, QNetworkAccessManager *
|
||||
, QWidget * parent = nullptr);
|
||||
~SampleDownloader ();
|
||||
|
||||
Q_SLOT void show ();
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,319 +0,0 @@
|
||||
#include "Directory.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QHeaderView>
|
||||
#include <QStringList>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QAuthenticator>
|
||||
#include <QNetworkReply>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QTreeWidgetItemIterator>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Configuration.hpp"
|
||||
#include "DirectoryNode.hpp"
|
||||
#include "FileNode.hpp"
|
||||
#include "revision_utils.hpp"
|
||||
#include "MessageBox.hpp"
|
||||
|
||||
#include "moc_Directory.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * samples_dir_name = "samples";
|
||||
QString const contents_file_name = "contents_" + version (false) + ".json";
|
||||
}
|
||||
|
||||
Directory::Directory (Configuration const * configuration
|
||||
, QNetworkAccessManager * network_manager
|
||||
, QWidget * parent)
|
||||
: QTreeWidget {parent}
|
||||
, configuration_ {configuration}
|
||||
, network_manager_ {network_manager}
|
||||
, http_only_ {false}
|
||||
, root_dir_ {configuration_->save_directory ()}
|
||||
, contents_ {this
|
||||
, network_manager_
|
||||
, QDir {root_dir_.absoluteFilePath (samples_dir_name)}.absoluteFilePath (contents_file_name)}
|
||||
{
|
||||
dir_icon_.addPixmap (style ()->standardPixmap (QStyle::SP_DirClosedIcon), QIcon::Normal, QIcon::Off);
|
||||
dir_icon_.addPixmap (style ()->standardPixmap (QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On);
|
||||
file_icon_.addPixmap (style ()->standardPixmap (QStyle::SP_FileIcon));
|
||||
|
||||
setColumnCount (2);
|
||||
setHeaderLabels ({"File", "Progress"});
|
||||
header ()->setSectionResizeMode (QHeaderView::ResizeToContents);
|
||||
setItemDelegate (&item_delegate_);
|
||||
|
||||
connect (network_manager_, &QNetworkAccessManager::authenticationRequired
|
||||
, this, &Directory::authentication);
|
||||
connect (this, &Directory::itemChanged, [this] (QTreeWidgetItem * item) {
|
||||
switch (item->type ())
|
||||
{
|
||||
case FileNode::Type:
|
||||
{
|
||||
FileNode * node = static_cast<FileNode *> (item);
|
||||
if (!node->sync (node->checkState (0) == Qt::Checked))
|
||||
{
|
||||
FileNode::sync_blocker b {node};
|
||||
node->setCheckState (0, node->checkState (0) == Qt::Checked ? Qt::Unchecked : Qt::Checked);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool Directory::url_root (QUrl root)
|
||||
{
|
||||
if (!root.path ().endsWith ('/'))
|
||||
{
|
||||
root.setPath (root.path () + '/');
|
||||
}
|
||||
bool valid = root.isValid ();
|
||||
if (valid)
|
||||
{
|
||||
url_root_ = root;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
void Directory::error (QString const& title, QString const& message)
|
||||
{
|
||||
MessageBox::warning_message (this, title, message);
|
||||
}
|
||||
|
||||
bool Directory::refresh (bool http_only)
|
||||
{
|
||||
abort ();
|
||||
clear ();
|
||||
// update locations
|
||||
root_dir_ = configuration_->save_directory ();
|
||||
QDir contents_dir {root_dir_.absoluteFilePath (samples_dir_name)};
|
||||
contents_.local_file_path (contents_dir.absoluteFilePath (contents_file_name));
|
||||
contents_.http_only (http_only_ = http_only);
|
||||
QUrl url {url_root_.resolved (QDir {root_dir_.relativeFilePath (samples_dir_name)}.filePath (contents_file_name))};
|
||||
if (url.isValid ())
|
||||
{
|
||||
return contents_.sync (url, true, true); // attempt to fetch contents
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this
|
||||
, tr ("URL Error")
|
||||
, tr ("Invalid URL:\n\"%1\"")
|
||||
.arg (url.toDisplayString ()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Directory::download_finished (bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
QFile contents {contents_.local_file_path ()};
|
||||
if (contents.open (QFile::ReadOnly | QFile::Text))
|
||||
{
|
||||
QJsonParseError json_status;
|
||||
auto content = QJsonDocument::fromJson (contents.readAll (), &json_status);
|
||||
if (json_status.error)
|
||||
{
|
||||
MessageBox::warning_message (this
|
||||
, tr ("JSON Error")
|
||||
, tr ("Contents file syntax error %1 at character offset %2")
|
||||
.arg (json_status.errorString ()).arg (json_status.offset));
|
||||
return;
|
||||
}
|
||||
if (!content.isArray ())
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents file top level must be a JSON array"));
|
||||
return;
|
||||
}
|
||||
QTreeWidgetItem * parent {invisibleRootItem ()};
|
||||
parent = new DirectoryNode {parent, samples_dir_name};
|
||||
parent->setIcon (0, dir_icon_);
|
||||
parent->setExpanded (true);
|
||||
parse_entries (content.array (), root_dir_.relativeFilePath (samples_dir_name), parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("File System Error")
|
||||
, tr ("Failed to open \"%1\"\nError: %2 - %3")
|
||||
.arg (contents.fileName ())
|
||||
.arg (contents.error ())
|
||||
.arg (contents.errorString ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::parse_entries (QJsonArray const& entries, QDir const& dir, QTreeWidgetItem * parent)
|
||||
{
|
||||
if (dir.isRelative () && !dir.path ().startsWith ('.'))
|
||||
{
|
||||
for (auto const& value: entries)
|
||||
{
|
||||
if (value.isObject ())
|
||||
{
|
||||
auto const& entry = value.toObject ();
|
||||
auto const& name = entry["name"].toString ();
|
||||
if (name.size () && !name.contains (QRegularExpression {R"([/:;])"}))
|
||||
{
|
||||
auto const& type = entry["type"].toString ();
|
||||
if ("file" == type)
|
||||
{
|
||||
QUrl url {url_root_.resolved (dir.filePath (name))};
|
||||
if (url.isValid ())
|
||||
{
|
||||
auto node = new FileNode {parent, network_manager_
|
||||
, QDir {root_dir_.filePath (dir.path ())}.absoluteFilePath (name)
|
||||
, url, http_only_};
|
||||
FileNode::sync_blocker b {node};
|
||||
node->setIcon (0, file_icon_);
|
||||
node->setCheckState (0, node->local () ? Qt::Checked : Qt::Unchecked);
|
||||
update (parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this
|
||||
, tr ("URL Error")
|
||||
, tr ("Invalid URL:\n\"%1\"")
|
||||
.arg (url.toDisplayString ()));
|
||||
}
|
||||
}
|
||||
else if ("directory" == type)
|
||||
{
|
||||
auto node = new DirectoryNode {parent, name};
|
||||
node->setIcon (0, dir_icon_);
|
||||
auto const& entries = entry["entries"];
|
||||
if (entries.isArray ())
|
||||
{
|
||||
parse_entries (entries.toArray ()
|
||||
, QDir {root_dir_.relativeFilePath (dir.path ())}.filePath (name)
|
||||
, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents entries must be a JSON array"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents entries must have a valid type"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents entries must have a valid name"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents entries must be JSON objects"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("JSON Error")
|
||||
, tr ("Contents directories must be relative and within \"%1\"")
|
||||
.arg (samples_dir_name));
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::abort ()
|
||||
{
|
||||
QTreeWidgetItemIterator iter {this};
|
||||
while (*iter)
|
||||
{
|
||||
if ((*iter)->type () == FileNode::Type)
|
||||
{
|
||||
auto * node = static_cast<FileNode *> (*iter);
|
||||
node->abort ();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
//
|
||||
// traverse the passed subtree accumulating the number of items, the
|
||||
// number we have size data for, the bytes downloaded so far and the
|
||||
// maximum bytes to expect
|
||||
//
|
||||
int recurse_children (QTreeWidgetItem const * item, int * counted
|
||||
, qint64 * bytes, qint64 * max)
|
||||
{
|
||||
int items {0};
|
||||
for (int index {0}; index < item->childCount (); ++index)
|
||||
{
|
||||
auto const * child = item->child (index);
|
||||
if (child->type () == FileNode::Type) // only count files
|
||||
{
|
||||
++items;
|
||||
if (auto size = child->data (1, Qt::UserRole).toLongLong ())
|
||||
{
|
||||
*max += size;
|
||||
++*counted;
|
||||
}
|
||||
*bytes += child->data (1, Qt::DisplayRole).toLongLong ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// recurse into sub-directory subtrees
|
||||
items += recurse_children (child, counted, bytes, max);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::update (QTreeWidgetItem * item)
|
||||
{
|
||||
// iterate the tree under item and accumulate the progress
|
||||
if (item)
|
||||
{
|
||||
Q_ASSERT (item->type () == DirectoryNode::Type);
|
||||
qint64 max {0};
|
||||
qint64 bytes {0};
|
||||
int counted {0};
|
||||
|
||||
// get the count, progress and size of children
|
||||
int items {recurse_children (item, &counted, &bytes, &max)};
|
||||
|
||||
// estimate size of items not yet downloaded as average of
|
||||
// those actually present
|
||||
if (counted)
|
||||
{
|
||||
max += (items - counted) * max / counted;
|
||||
}
|
||||
|
||||
// save as our progress
|
||||
item->setData (1, Qt::UserRole, max);
|
||||
item->setData (1, Qt::DisplayRole, bytes);
|
||||
|
||||
// recurse up to top
|
||||
update (item->parent ());
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::authentication (QNetworkReply * /* reply */
|
||||
, QAuthenticator * /* authenticator */)
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("Network Error"), tr ("Authentication required"));
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#ifndef SAMPLE_DOWNLOADER_DIRECTORY_HPP__
|
||||
#define SAMPLE_DOWNLOADER_DIRECTORY_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QTreeWidget>
|
||||
#include <QIcon>
|
||||
#include <QSize>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
|
||||
#include "DirectoryDelegate.hpp"
|
||||
#include "RemoteFile.hpp"
|
||||
|
||||
class Configuration;
|
||||
class QNetworkAccessManager;
|
||||
class QTreeWidgetItem;
|
||||
class QNetworkReply;
|
||||
class QAuthenticator;
|
||||
class QJsonArray;
|
||||
|
||||
class Directory final
|
||||
: public QTreeWidget
|
||||
, protected RemoteFile::ListenerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Directory (Configuration const * configuration
|
||||
, QNetworkAccessManager * network_manager
|
||||
, QWidget * parent = nullptr);
|
||||
|
||||
QSize sizeHint () const override {return {400, 500};}
|
||||
|
||||
bool url_root (QUrl);
|
||||
bool refresh (bool http_only);
|
||||
void abort ();
|
||||
void update (QTreeWidgetItem * item);
|
||||
|
||||
protected:
|
||||
void error (QString const& title, QString const& message) override;
|
||||
bool redirect_request (QUrl const&) override {return true;} // allow
|
||||
void download_finished (bool success) override;
|
||||
|
||||
private:
|
||||
Q_SLOT void authentication (QNetworkReply *, QAuthenticator *);
|
||||
void parse_entries (QJsonArray const& entries, QDir const& dir, QTreeWidgetItem * parent);
|
||||
|
||||
Configuration const * configuration_;
|
||||
QNetworkAccessManager * network_manager_;
|
||||
bool http_only_;
|
||||
QDir root_dir_;
|
||||
QUrl url_root_;
|
||||
RemoteFile contents_;
|
||||
DirectoryDelegate item_delegate_;
|
||||
QIcon dir_icon_;
|
||||
QIcon file_icon_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,44 +0,0 @@
|
||||
#include "DirectoryDelegate.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
#include <QStyle>
|
||||
#include <QModelIndex>
|
||||
#include <QPainter>
|
||||
#include <QStyleOptionViewItem>
|
||||
#include <QStyleOptionProgressBar>
|
||||
|
||||
void DirectoryDelegate::paint (QPainter * painter, QStyleOptionViewItem const& option
|
||||
, QModelIndex const& index) const
|
||||
{
|
||||
if (1 == index.column ())
|
||||
{
|
||||
QStyleOptionProgressBar progress_bar_option;
|
||||
progress_bar_option.rect = option.rect;
|
||||
progress_bar_option.state = QStyle::State_Enabled;
|
||||
progress_bar_option.direction = QApplication::layoutDirection ();
|
||||
progress_bar_option.fontMetrics = QApplication::fontMetrics ();
|
||||
progress_bar_option.minimum = 0;
|
||||
progress_bar_option.maximum = 100;
|
||||
auto progress = index.data ().toLongLong ();
|
||||
if (progress > 0)
|
||||
{
|
||||
auto percent = int (progress * 100 / index.data (Qt::UserRole).toLongLong ());
|
||||
progress_bar_option.progress = percent;
|
||||
progress_bar_option.text = QString::number (percent) + '%';
|
||||
progress_bar_option.textVisible = true;
|
||||
progress_bar_option.textAlignment = Qt::AlignCenter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not started
|
||||
progress_bar_option.progress = -1;
|
||||
}
|
||||
QApplication::style ()->drawControl (QStyle::CE_ProgressBar, &progress_bar_option, painter);
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::paint (painter, option, index);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#ifndef DIRECTORY_DELEGATE_HPP__
|
||||
#define DIRECTORY_DELEGATE_HPP__
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
class QObject;
|
||||
class QStyleOptionVoew;
|
||||
class QModelIndex;
|
||||
class QPainter;
|
||||
|
||||
//
|
||||
// Styled item delegate that renders a progress bar in column #1
|
||||
//
|
||||
// model column #1 DisplayRole is the progress in bytes
|
||||
// model column #1 UserRole is the expected number of bytes
|
||||
//
|
||||
class DirectoryDelegate final
|
||||
: public QStyledItemDelegate
|
||||
{
|
||||
public:
|
||||
explicit DirectoryDelegate (QObject * parent = nullptr)
|
||||
: QStyledItemDelegate {parent}
|
||||
{
|
||||
}
|
||||
|
||||
void paint (QPainter * painter, QStyleOptionViewItem const& option
|
||||
, QModelIndex const& index) const override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,51 +0,0 @@
|
||||
#ifndef DIRECTORY_NODE_HPP__
|
||||
#define DIRECTORY_NODE_HPP__
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QString>
|
||||
|
||||
//
|
||||
// Tree widget item representing a file system directory.
|
||||
//
|
||||
// It renders the directory name in the first column and progress
|
||||
// information in the 2nd column. The progress information consists of
|
||||
// two 64 bit integer values, the 1st in the DisplayRole is the number
|
||||
// of bytes received and the 2nd in the UserRole the total bytes
|
||||
// expected. The progress information is not automatically
|
||||
// maintained, see the Directory class for an example of how to
|
||||
// dynamically maintain the DirectoryNode progress values. The 1st
|
||||
// column also renders a tristate check box that controls the first
|
||||
// column check boxes of child items.
|
||||
//
|
||||
class DirectoryNode final
|
||||
: public QTreeWidgetItem
|
||||
{
|
||||
public:
|
||||
explicit DirectoryNode (QTreeWidgetItem * parent, QString const& name)
|
||||
: QTreeWidgetItem {parent, Type}
|
||||
{
|
||||
setFlags (flags () | Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
|
||||
setText (0, name);
|
||||
setCheckState (0, Qt::Unchecked);
|
||||
|
||||
// initialize as empty, the owning QTreeWidget must maintain these
|
||||
// progress values
|
||||
setData (1, Qt::DisplayRole, 0ll); // progress in bytes
|
||||
setData (1, Qt::UserRole, 0ll); // expected bytes
|
||||
}
|
||||
|
||||
bool operator == (QString const& name) const
|
||||
{
|
||||
return name == text (0);
|
||||
}
|
||||
|
||||
static int constexpr Type {UserType};
|
||||
};
|
||||
|
||||
inline
|
||||
bool operator == (QString const& lhs, DirectoryNode const& rhs)
|
||||
{
|
||||
return rhs == lhs;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "FileNode.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "Directory.hpp"
|
||||
#include "MessageBox.hpp"
|
||||
|
||||
FileNode::FileNode (QTreeWidgetItem * parent
|
||||
, QNetworkAccessManager * network_manager
|
||||
, QString const& local_file_path
|
||||
, QUrl const& url
|
||||
, bool http_only)
|
||||
: QTreeWidgetItem {parent, Type}
|
||||
, remote_file_ {this, network_manager, local_file_path, http_only}
|
||||
, block_sync_ {false}
|
||||
{
|
||||
sync_blocker b {this};
|
||||
setFlags (flags () | Qt::ItemIsUserCheckable);
|
||||
setText (0, QFileInfo {local_file_path}.fileName ()); // display
|
||||
setData (0, Qt::UserRole, url);
|
||||
setData (0, Qt::UserRole + 1, local_file_path); // local absolute path
|
||||
setCheckState (0, Qt::Unchecked);
|
||||
}
|
||||
|
||||
void FileNode::error (QString const& title, QString const& message)
|
||||
{
|
||||
MessageBox::warning_message (treeWidget (), title, message);
|
||||
}
|
||||
|
||||
bool FileNode::sync (bool local)
|
||||
{
|
||||
if (block_sync_)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return remote_file_.sync (data (0, Qt::UserRole).toUrl (), local);
|
||||
}
|
||||
|
||||
void FileNode::download_progress (qint64 bytes_received, qint64 total_bytes)
|
||||
{
|
||||
sync_blocker b {this};
|
||||
setData (1, Qt::UserRole, total_bytes);
|
||||
if (bytes_received < 0)
|
||||
{
|
||||
setData (1, Qt::DisplayRole, 0ll);
|
||||
setCheckState (0, Qt::Unchecked);
|
||||
}
|
||||
else
|
||||
{
|
||||
setData (1, Qt::DisplayRole, bytes_received);
|
||||
}
|
||||
static_cast<Directory *> (treeWidget ())->update (parent ());
|
||||
}
|
||||
|
||||
void FileNode::download_finished (bool success)
|
||||
{
|
||||
sync_blocker b {this};
|
||||
if (!success)
|
||||
{
|
||||
setData (1, Qt::UserRole, 0ll);
|
||||
setData (1, Qt::DisplayRole, 0ll);
|
||||
}
|
||||
setCheckState (0, success ? Qt::Checked : Qt::Unchecked);
|
||||
static_cast<Directory *> (treeWidget ())->update (parent ());
|
||||
}
|
||||
|
||||
void FileNode::abort ()
|
||||
{
|
||||
sync_blocker b {this};
|
||||
remote_file_.abort ();
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
#ifndef FILE_NODE_HPP__
|
||||
#define FILE_NODE_HPP__
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
#include "RemoteFile.hpp"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QString;
|
||||
class QUrl;
|
||||
|
||||
//
|
||||
// A holder for a RemoteFile object linked to a QTreeWidget row.
|
||||
//
|
||||
// It renders the file name in first column and holds download
|
||||
// progress data in the second column. The progress information is a
|
||||
// 64 bit integer number of bytes in the DisplayRole and a total bytes
|
||||
// expected in the UserRole. The first column also renders a check box
|
||||
// that downloads the file when checked and removes the downloaded
|
||||
// file when unchecked. The URL and local absolute file path are
|
||||
// stored in the UserData and UserData+1 roles of the first column.
|
||||
//
|
||||
class FileNode final
|
||||
: public QTreeWidgetItem
|
||||
, protected RemoteFile::ListenerInterface
|
||||
{
|
||||
public:
|
||||
explicit FileNode (QTreeWidgetItem * parent
|
||||
, QNetworkAccessManager * network_manager
|
||||
, QString const& local_path
|
||||
, QUrl const& url
|
||||
, bool http_only);
|
||||
|
||||
bool local () const {return remote_file_.local ();}
|
||||
bool sync (bool local);
|
||||
void abort ();
|
||||
|
||||
static int constexpr Type {UserType + 1};
|
||||
|
||||
//
|
||||
// Clients may use this RAII class to block nested calls to sync
|
||||
// which may be troublesome, e.g. when UI updates cause recursion.
|
||||
//
|
||||
struct sync_blocker
|
||||
{
|
||||
sync_blocker (FileNode * node) : node_ {node} {node_->block_sync_ = true;}
|
||||
sync_blocker (sync_blocker const&) = delete;
|
||||
sync_blocker& operator = (sync_blocker const&) = delete;
|
||||
~sync_blocker () {node_->block_sync_ = false;}
|
||||
private:
|
||||
FileNode * node_;
|
||||
};
|
||||
|
||||
protected:
|
||||
void error (QString const& title, QString const& message) override;
|
||||
bool redirect_request (QUrl const&) override {return true;} // allow
|
||||
void download_progress (qint64 bytes_received, qint64 total_bytes) override;
|
||||
void download_finished (bool success) override;
|
||||
|
||||
private:
|
||||
RemoteFile remote_file_; // active download
|
||||
bool block_sync_;
|
||||
|
||||
friend struct sync_blocker;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,5 +0,0 @@
|
||||
A UI for downloading sample files from a web server.
|
||||
|
||||
Works in concert with samples/CMakeLists.txt which generates the JSON
|
||||
contents description file and has a build target upload-samples that
|
||||
uploads the samples and content file to the project files server.
|
||||
@@ -1,271 +0,0 @@
|
||||
#include "RemoteFile.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QDir>
|
||||
#include <QByteArray>
|
||||
|
||||
#include "moc_RemoteFile.cpp"
|
||||
|
||||
RemoteFile::RemoteFile (ListenerInterface * listener, QNetworkAccessManager * network_manager
|
||||
, QString const& local_file_path, bool http_only, QObject * parent)
|
||||
: QObject {parent}
|
||||
, listener_ {listener}
|
||||
, network_manager_ {network_manager}
|
||||
, local_file_ {local_file_path}
|
||||
, http_only_ {http_only}
|
||||
, is_valid_ {false}
|
||||
, redirect_count_ {0}
|
||||
, file_ {local_file_path}
|
||||
{
|
||||
local_file_.setCaching (false);
|
||||
}
|
||||
|
||||
void RemoteFile::local_file_path (QString const& name)
|
||||
{
|
||||
QFileInfo new_file {name};
|
||||
new_file.setCaching (false);
|
||||
if (new_file != local_file_)
|
||||
{
|
||||
if (local_file_.exists ())
|
||||
{
|
||||
QFile file {local_file_.absoluteFilePath ()};
|
||||
if (!file.rename (new_file.absoluteFilePath ()))
|
||||
{
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot rename file:\n\"%1\"\nto: \"%2\"\nError(%3): %4")
|
||||
.arg (file.fileName ())
|
||||
.arg (new_file.absoluteFilePath ())
|
||||
.arg (file.error ())
|
||||
.arg (file.errorString ()));
|
||||
}
|
||||
}
|
||||
std::swap (local_file_, new_file);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteFile::local () const
|
||||
{
|
||||
auto is_local = (reply_ && !reply_->isFinished ()) || local_file_.exists ();
|
||||
if (is_local)
|
||||
{
|
||||
auto size = local_file_.size ();
|
||||
listener_->download_progress (size, size);
|
||||
listener_->download_finished (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
listener_->download_progress (-1, 0);
|
||||
}
|
||||
return is_local;
|
||||
}
|
||||
|
||||
bool RemoteFile::sync (QUrl const& url, bool local, bool force)
|
||||
{
|
||||
if (local)
|
||||
{
|
||||
if (!reply_ || reply_->isFinished ()) // not active download
|
||||
{
|
||||
if (force || !local_file_.exists () || url != url_)
|
||||
{
|
||||
url_ = url;
|
||||
redirect_count_ = 0;
|
||||
Q_ASSERT (!is_valid_);
|
||||
download (url_);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reply_ && reply_->isRunning ())
|
||||
{
|
||||
reply_->abort ();
|
||||
}
|
||||
if (local_file_.exists ())
|
||||
{
|
||||
auto path = local_file_.absoluteDir ();
|
||||
if (path.remove (local_file_.fileName ()))
|
||||
{
|
||||
listener_->download_progress (-1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot delete file:\n\"%1\"")
|
||||
.arg (local_file_.absoluteFilePath ()));
|
||||
return false;
|
||||
}
|
||||
path.rmpath (".");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteFile::download (QUrl url)
|
||||
{
|
||||
if (QNetworkAccessManager::Accessible != network_manager_->networkAccessible ()) {
|
||||
// try and recover network access for QNAM
|
||||
network_manager_->setNetworkAccessible (QNetworkAccessManager::Accessible);
|
||||
}
|
||||
|
||||
if (url.isValid () && (!QSslSocket::supportsSsl () || http_only_))
|
||||
{
|
||||
url.setScheme ("http");
|
||||
}
|
||||
QNetworkRequest request {url};
|
||||
request.setRawHeader ("User-Agent", "WSJT Sample Downloader");
|
||||
request.setOriginatingObject (this);
|
||||
|
||||
// this blocks for a second or two the first time it is used on
|
||||
// Windows - annoying
|
||||
if (!is_valid_)
|
||||
{
|
||||
reply_ = network_manager_->head (request);
|
||||
}
|
||||
else
|
||||
{
|
||||
reply_ = network_manager_->get (request);
|
||||
}
|
||||
|
||||
connect (reply_.data (), &QNetworkReply::finished, this, &RemoteFile::reply_finished);
|
||||
connect (reply_.data (), &QNetworkReply::readyRead, this, &RemoteFile::store);
|
||||
connect (reply_.data (), &QNetworkReply::downloadProgress
|
||||
, [this] (qint64 bytes_received, qint64 total_bytes) {
|
||||
// report progress of wanted file
|
||||
if (is_valid_)
|
||||
{
|
||||
listener_->download_progress (bytes_received, total_bytes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void RemoteFile::abort ()
|
||||
{
|
||||
if (reply_ && reply_->isRunning ())
|
||||
{
|
||||
reply_->abort ();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteFile::reply_finished ()
|
||||
{
|
||||
if (!reply_) return; // we probably deleted it in an
|
||||
// earlier call
|
||||
QUrl redirect_url {reply_->attribute (QNetworkRequest::RedirectionTargetAttribute).toUrl ()};
|
||||
if (reply_->error () == QNetworkReply::NoError && !redirect_url.isEmpty ())
|
||||
{
|
||||
if (listener_->redirect_request (redirect_url))
|
||||
{
|
||||
if (++redirect_count_ < 10) // maintain sanity
|
||||
{
|
||||
// follow redirect
|
||||
download (reply_->url ().resolved (redirect_url));
|
||||
}
|
||||
else
|
||||
{
|
||||
listener_->download_finished (false);
|
||||
listener_->error (tr ("Network Error")
|
||||
, tr ("Too many redirects: %1")
|
||||
.arg (redirect_url.toDisplayString ()));
|
||||
is_valid_ = false; // reset
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
listener_->download_finished (false);
|
||||
listener_->error (tr ("Network Error")
|
||||
, tr ("Redirect not followed: %1")
|
||||
.arg (redirect_url.toDisplayString ()));
|
||||
is_valid_ = false; // reset
|
||||
}
|
||||
}
|
||||
else if (reply_->error () != QNetworkReply::NoError)
|
||||
{
|
||||
file_.cancelWriting ();
|
||||
file_.commit ();
|
||||
listener_->download_finished (false);
|
||||
is_valid_ = false; // reset
|
||||
// report errors that are not due to abort
|
||||
if (QNetworkReply::OperationCanceledError != reply_->error ())
|
||||
{
|
||||
listener_->error (tr ("Network Error"), reply_->errorString ());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto path = QFileInfo {file_.fileName ()}.absoluteDir ();
|
||||
if (is_valid_ && !file_.commit ())
|
||||
{
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot commit changes to:\n\"%1\"")
|
||||
.arg (file_.fileName ()));
|
||||
path.rmpath ("."); // tidy empty directories
|
||||
listener_->download_finished (false);
|
||||
is_valid_ = false; // reset
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_valid_)
|
||||
{
|
||||
// now get the body content
|
||||
is_valid_ = true;
|
||||
download (reply_->url ().resolved (redirect_url));
|
||||
}
|
||||
else
|
||||
{
|
||||
listener_->download_finished (true);
|
||||
is_valid_ = false; // reset
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reply_ && reply_->isFinished ())
|
||||
{
|
||||
reply_->deleteLater ();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteFile::store ()
|
||||
{
|
||||
if (is_valid_)
|
||||
{
|
||||
if (!file_.isOpen ())
|
||||
{
|
||||
// create temporary file in the final location
|
||||
auto path = QFileInfo {file_.fileName ()}.absoluteDir ();
|
||||
if (path.mkpath ("."))
|
||||
{
|
||||
if (!file_.open (QSaveFile::WriteOnly))
|
||||
{
|
||||
abort ();
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot open file:\n\"%1\"\nError(%2): %3")
|
||||
.arg (path.path ())
|
||||
.arg (file_.error ())
|
||||
.arg (file_.errorString ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
abort ();
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot make path:\n\"%1\"")
|
||||
.arg (path.path ()));
|
||||
}
|
||||
}
|
||||
if (file_.write (reply_->read (reply_->bytesAvailable ())) < 0)
|
||||
{
|
||||
abort ();
|
||||
listener_->error (tr ("File System Error")
|
||||
, tr ("Cannot write to file:\n\"%1\"\nError(%2): %3")
|
||||
.arg (file_.fileName ())
|
||||
.arg (file_.error ())
|
||||
.arg (file_.errorString ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
#ifndef REMOTE_FILE_HPP__
|
||||
#define REMOTE_FILE_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QFileInfo>
|
||||
#include <QSaveFile>
|
||||
#include <QPointer>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
//
|
||||
// Synchronize an individual file specified by a URL to the local file
|
||||
// system
|
||||
//
|
||||
class RemoteFile final
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
//
|
||||
// Clients of RemoteFile must provide an instance of this
|
||||
// interface. It may be used to receive information and requests
|
||||
// from the RemoteFile instance as it does its work.
|
||||
//
|
||||
class ListenerInterface
|
||||
{
|
||||
protected:
|
||||
ListenerInterface () {}
|
||||
|
||||
public:
|
||||
virtual void error (QString const& title, QString const& message) = 0;
|
||||
virtual bool redirect_request (QUrl const&) {return false;} // disallow
|
||||
virtual void download_progress (qint64 /* bytes_received */, qint64 /* total_bytes */) {}
|
||||
virtual void download_finished (bool /* success */) {}
|
||||
};
|
||||
|
||||
explicit RemoteFile (ListenerInterface * listener, QNetworkAccessManager * network_manager
|
||||
, QString const& local_file_path, bool http_only = false
|
||||
, QObject * parent = nullptr);
|
||||
|
||||
// true if local file exists or will do very soon
|
||||
bool local () const;
|
||||
|
||||
// download/remove the local file
|
||||
bool sync (QUrl const& url, bool local = true, bool force = false);
|
||||
|
||||
// abort an active download
|
||||
void abort ();
|
||||
|
||||
// change the local location, this will rename if the file exists locally
|
||||
void local_file_path (QString const&);
|
||||
|
||||
QString local_file_path () const {return local_file_.absoluteFilePath ();}
|
||||
QUrl url () const {return url_;}
|
||||
|
||||
// always use an http scheme for remote URLs
|
||||
void http_only (bool flag = true) {http_only_ = flag;}
|
||||
|
||||
private:
|
||||
void download (QUrl url);
|
||||
void reply_finished ();
|
||||
|
||||
Q_SLOT void store ();
|
||||
|
||||
Q_SIGNAL void redirect (QUrl const&, unsigned redirect_count);
|
||||
Q_SIGNAL void downloadProgress (qint64 bytes_received, qint64 total_bytes);
|
||||
Q_SIGNAL void finished ();
|
||||
|
||||
ListenerInterface * listener_;
|
||||
QNetworkAccessManager * network_manager_;
|
||||
QFileInfo local_file_;
|
||||
bool http_only_;
|
||||
QUrl url_;
|
||||
QPointer<QNetworkReply> reply_;
|
||||
bool is_valid_;
|
||||
unsigned redirect_count_;
|
||||
QSaveFile file_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <QDebug>
|
||||
#include <QDebugStateSaver>
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
#include "Radio.hpp"
|
||||
@@ -631,7 +633,7 @@ bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction act
|
||||
, [&band] (Station const& s) {return s.band_name_ == band;}))
|
||||
{
|
||||
// not found so add it
|
||||
add (Station {band, 0, QDateTime::currentDateTimeUtc(), QDateTime::currentDateTimeUtc(), QString {}});
|
||||
add (Station {band, 0, DriftingDateTime::currentDateTimeUtc(), DriftingDateTime::currentDateTimeUtc(), QString {}});
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
|
||||
__ __ ______ _____ ________ __ __
|
||||
| \ _ | \ / \ | \| \ | \ | \
|
||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
||||
|
||||
|
||||
|
||||
Thanks to:
|
||||
|
||||
The FFTW library (http://www.fftw.org) without which the efficient
|
||||
generation of discrete fast Fourier transformations essential to the
|
||||
decoding DSP algorithms of WSJT-X would be a considerable part of the
|
||||
project.
|
||||
|
||||
The Qt project (http://qt-project.org) that allows us to deliver a
|
||||
rich industrial strength cross platform GUI application written in
|
||||
C++.
|
||||
|
||||
Nate Bargmann, N0NB, and the Hamlib developer team for their
|
||||
excellent library and for prompt review and acceptance of the many
|
||||
pull requests for upstream patches to Hamlib.
|
||||
|
||||
Dave Bernstein, AA6YQ, for being so receptive to suggestions
|
||||
allowing WSJT-X to cooperate with his excellent Amateur Radio DX and
|
||||
award chasing suite (http://www.dxlabsuite.com).
|
||||
|
||||
Laurie Cowcher, VK3AMA, for developing the partner applications
|
||||
JTAlertX and JTMacrosX (http://www.hamapps.com) that make DX chasing
|
||||
with WSJT-X such an efficient and pleasurable experience.
|
||||
|
||||
The CMake build and packaging tools (http://www.cmake.org) for
|
||||
their comprehensive scripting tools that make automation of building
|
||||
and packaging on all supported platforms possible.
|
||||
|
||||
The NSIS MS Windows installer scripting and generator tools
|
||||
(http://nsis.sourceforge.net) that, through the CPack NSIS generator,
|
||||
allows us to build a comprehensive Windows installer package.
|
||||
|
||||
The GNU Compiler Collection (http://gcc.gnu.org) that allows us to
|
||||
compile and link C++, Fortran and C code to the latest Standards and
|
||||
with high quality optimization.
|
||||
|
||||
The clang C++ & C compiler front ends and LLVM compiler back end
|
||||
tools (http://clang.llvm.org) that provide us with another, gcc
|
||||
compatible, high quality C++ and C compiler and Standard Library suite
|
||||
which, particularly on Apple Mac, allows our code to be ported to the
|
||||
maximum number of platforms.
|
||||
|
||||
The MinGW project (http://www.mingw.org) that ports the gcc
|
||||
compilers and related GNU tools to the MS Windows environment allowing
|
||||
a high quality C++, Fortran and C application to be portable between
|
||||
native MS Windows and other platforms such as Linux and Apple Mac.
|
||||
@@ -1,135 +0,0 @@
|
||||
#include "BeaconsModel.hpp"
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QFont>
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const headings[] = {
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Client"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Time"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Snr"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "DT"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Frequency"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Drift"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Grid"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Power"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Live"),
|
||||
QT_TRANSLATE_NOOP ("BeaconsModel", "Callsign"),
|
||||
};
|
||||
|
||||
QString live_string (bool off_air)
|
||||
{
|
||||
return off_air ? QT_TRANSLATE_NOOP ("BeaconsModel", "no") : QT_TRANSLATE_NOOP ("BeaconsModel", "yes");
|
||||
}
|
||||
|
||||
QFont text_font {"Courier", 10};
|
||||
|
||||
QList<QStandardItem *> make_row (QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, Frequency frequency, qint32 drift, QString const& callsign
|
||||
, QString const& grid, qint32 power, bool off_air)
|
||||
{
|
||||
auto time_item = new QStandardItem {time.toString ("hh:mm")};
|
||||
time_item->setData (time);
|
||||
time_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto snr_item = new QStandardItem {QString::number (snr)};
|
||||
snr_item->setData (snr);
|
||||
snr_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto dt = new QStandardItem {QString::number (delta_time)};
|
||||
dt->setData (delta_time);
|
||||
dt->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto freq = new QStandardItem {Radio::pretty_frequency_MHz_string (frequency)};
|
||||
freq->setData (frequency);
|
||||
freq->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto dri = new QStandardItem {QString::number (drift)};
|
||||
dri->setData (drift);
|
||||
dri->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto gd = new QStandardItem {grid};
|
||||
gd->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto pwr = new QStandardItem {QString::number (power)};
|
||||
pwr->setData (power);
|
||||
pwr->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto live = new QStandardItem {live_string (off_air)};
|
||||
live->setTextAlignment (Qt::AlignHCenter);
|
||||
|
||||
QList<QStandardItem *> row {
|
||||
new QStandardItem {client_id}, time_item, snr_item, dt, freq, dri, gd, pwr, live, new QStandardItem {callsign}};
|
||||
Q_FOREACH (auto& item, row)
|
||||
{
|
||||
item->setEditable (false);
|
||||
item->setFont (text_font);
|
||||
item->setTextAlignment (item->textAlignment () | Qt::AlignVCenter);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
BeaconsModel::BeaconsModel (QObject * parent)
|
||||
: QStandardItemModel {0, sizeof (headings) / sizeof (headings[0]), parent}
|
||||
{
|
||||
int column {0};
|
||||
for (auto const& heading : headings)
|
||||
{
|
||||
setHeaderData (column++, Qt::Horizontal, tr (heading));
|
||||
}
|
||||
}
|
||||
|
||||
void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, Frequency frequency, qint32 drift, QString const& callsign
|
||||
, QString const& grid, qint32 power, bool off_air)
|
||||
{
|
||||
if (!is_new)
|
||||
{
|
||||
int target_row {-1};
|
||||
for (auto row = 0; row < rowCount (); ++row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
auto row_time = item (row, 1)->data ().toTime ();
|
||||
if (row_time == time
|
||||
&& item (row, 2)->data ().toInt () == snr
|
||||
&& item (row, 3)->data ().toFloat () == delta_time
|
||||
&& item (row, 4)->data ().value<Frequency> () == frequency
|
||||
&& data (index (row, 5)).toInt () == drift
|
||||
&& data (index (row, 7)).toString () == grid
|
||||
&& data (index (row, 8)).toInt () == power
|
||||
&& data (index (row, 6)).toString () == live_string (off_air)
|
||||
&& data (index (row, 9)).toString () == callsign)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (time <= row_time)
|
||||
{
|
||||
target_row = row; // last row with same time
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_row >= 0)
|
||||
{
|
||||
insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
|
||||
}
|
||||
|
||||
void BeaconsModel::clear_decodes (QString const& client_id)
|
||||
{
|
||||
for (auto row = rowCount () - 1; row >= 0; --row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
removeRow (row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_BeaconsModel.cpp"
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef WSJTX_UDP_BEACONS_MODEL_HPP__
|
||||
#define WSJTX_UDP_BEACONS_MODEL_HPP__
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
|
||||
using Frequency = MessageServer::Frequency;
|
||||
|
||||
class QString;
|
||||
class QTime;
|
||||
|
||||
//
|
||||
// Beacons Model - simple data model for all beacon spots
|
||||
//
|
||||
// The model is a basic table with uniform row format. Rows consist of
|
||||
// QStandardItem instances containing the string representation of the
|
||||
// column data and if the underlying field is not a string then the
|
||||
// UserRole+1 role contains the underlying data item.
|
||||
//
|
||||
// Two slots are provided to add a new decode and remove all spots for
|
||||
// a client.
|
||||
//
|
||||
class BeaconsModel
|
||||
: public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit BeaconsModel (QObject * parent = nullptr);
|
||||
|
||||
Q_SLOT void add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, Frequency frequency, qint32 drift, QString const& callsign, QString const& grid
|
||||
, qint32 power, bool off_air);
|
||||
Q_SLOT void clear_decodes (QString const& client_id);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,310 +0,0 @@
|
||||
#include "ClientWidget.hpp"
|
||||
|
||||
#include <QRegExp>
|
||||
#include <QColor>
|
||||
|
||||
#include "MaidenheadLocatorValidator.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
//QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
|
||||
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>]*"};
|
||||
QRegularExpression cq_re {"(CQ|CQDX|QRZ)[^A-Z0-9/]+"};
|
||||
|
||||
void update_dynamic_property (QWidget * widget, char const * property, QVariant const& value)
|
||||
{
|
||||
widget->setProperty (property, value);
|
||||
widget->style ()->unpolish (widget);
|
||||
widget->style ()->polish (widget);
|
||||
widget->update ();
|
||||
}
|
||||
}
|
||||
|
||||
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id)
|
||||
: client_id_ {client_id}
|
||||
, rx_df_ (-1)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant ClientWidget::IdFilterModel::data (QModelIndex const& proxy_index, int role) const
|
||||
{
|
||||
if (role == Qt::BackgroundRole)
|
||||
{
|
||||
switch (proxy_index.column ())
|
||||
{
|
||||
case 8: // message
|
||||
{
|
||||
auto message = QSortFilterProxyModel::data (proxy_index).toString ();
|
||||
if (base_call_re_.pattern ().size ()
|
||||
&& message.contains (base_call_re_))
|
||||
{
|
||||
return QColor {255,200,200};
|
||||
}
|
||||
if (message.contains (cq_re))
|
||||
{
|
||||
return QColor {200, 255, 200};
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // DF
|
||||
if (qAbs (QSortFilterProxyModel::data (proxy_index).toInt () - rx_df_) <= 10)
|
||||
{
|
||||
return QColor {255, 200, 200};
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QSortFilterProxyModel::data (proxy_index, role);
|
||||
}
|
||||
|
||||
bool ClientWidget::IdFilterModel::filterAcceptsRow (int source_row
|
||||
, QModelIndex const& source_parent) const
|
||||
{
|
||||
auto source_index_col0 = sourceModel ()->index (source_row, 0, source_parent);
|
||||
return sourceModel ()->data (source_index_col0).toString () == client_id_;
|
||||
}
|
||||
|
||||
void ClientWidget::IdFilterModel::de_call (QString const& call)
|
||||
{
|
||||
if (call != call_)
|
||||
{
|
||||
beginResetModel ();
|
||||
if (call.size ())
|
||||
{
|
||||
base_call_re_.setPattern ("[^A-Z0-9]*" + Radio::base_callsign (call) + "[^A-Z0-9]*");
|
||||
}
|
||||
else
|
||||
{
|
||||
base_call_re_.setPattern (QString {});
|
||||
}
|
||||
call_ = call;
|
||||
endResetModel ();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientWidget::IdFilterModel::rx_df (int df)
|
||||
{
|
||||
if (df != rx_df_)
|
||||
{
|
||||
beginResetModel ();
|
||||
rx_df_ = df;
|
||||
endResetModel ();
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
QString make_title (QString const& id, QString const& version, QString const& revision)
|
||||
{
|
||||
QString title {id};
|
||||
if (version.size ())
|
||||
{
|
||||
title += QString {" v%1"}.arg (version);
|
||||
}
|
||||
if (revision.size ())
|
||||
{
|
||||
title += QString {" (%1)"}.arg (revision);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
||||
, QString const& id, QString const& version, QString const& revision
|
||||
, QListWidget const * calls_of_interest, QWidget * parent)
|
||||
: QDockWidget {make_title (id, version, revision), parent}
|
||||
, id_ {id}
|
||||
, calls_of_interest_ {calls_of_interest}
|
||||
, decodes_proxy_model_ {id_}
|
||||
, decodes_table_view_ {new QTableView}
|
||||
, beacons_table_view_ {new QTableView}
|
||||
, message_line_edit_ {new QLineEdit}
|
||||
, grid_line_edit_ {new QLineEdit}
|
||||
, decodes_stack_ {new QStackedLayout}
|
||||
, auto_off_button_ {new QPushButton {tr ("&Auto Off")}}
|
||||
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
|
||||
, de_label_ {new QLabel}
|
||||
, mode_label_ {new QLabel}
|
||||
, fast_mode_ {false}
|
||||
, frequency_label_ {new QLabel}
|
||||
, dx_label_ {new QLabel}
|
||||
, rx_df_label_ {new QLabel}
|
||||
, tx_df_label_ {new QLabel}
|
||||
, report_label_ {new QLabel}
|
||||
, columns_resized_ {false}
|
||||
{
|
||||
// set up widgets
|
||||
decodes_proxy_model_.setSourceModel (decodes_model);
|
||||
decodes_table_view_->setModel (&decodes_proxy_model_);
|
||||
decodes_table_view_->verticalHeader ()->hide ();
|
||||
decodes_table_view_->hideColumn (0);
|
||||
decodes_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
|
||||
auto form_layout = new QFormLayout;
|
||||
form_layout->addRow (tr ("Free text:"), message_line_edit_);
|
||||
form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_);
|
||||
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this});
|
||||
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
|
||||
Q_EMIT do_free_text (id_, text, false);
|
||||
});
|
||||
connect (message_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||
Q_EMIT do_free_text (id_, message_line_edit_->text (), true);
|
||||
});
|
||||
connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||
Q_EMIT location (id_, grid_line_edit_->text ());
|
||||
});
|
||||
|
||||
auto decodes_page = new QWidget;
|
||||
auto decodes_layout = new QVBoxLayout {decodes_page};
|
||||
decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
decodes_layout->addWidget (decodes_table_view_);
|
||||
decodes_layout->addLayout (form_layout);
|
||||
|
||||
auto beacons_proxy_model = new IdFilterModel {id_};
|
||||
beacons_proxy_model->setSourceModel (beacons_model);
|
||||
beacons_table_view_->setModel (beacons_proxy_model);
|
||||
beacons_table_view_->verticalHeader ()->hide ();
|
||||
beacons_table_view_->hideColumn (0);
|
||||
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
|
||||
auto beacons_page = new QWidget;
|
||||
auto beacons_layout = new QVBoxLayout {beacons_page};
|
||||
beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
beacons_layout->addWidget (beacons_table_view_);
|
||||
|
||||
decodes_stack_->addWidget (decodes_page);
|
||||
decodes_stack_->addWidget (beacons_page);
|
||||
|
||||
// stack alternative views
|
||||
auto content_layout = new QVBoxLayout;
|
||||
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
content_layout->addLayout (decodes_stack_);
|
||||
|
||||
// set up controls
|
||||
auto control_button_box = new QDialogButtonBox;
|
||||
control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole);
|
||||
control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole);
|
||||
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||
Q_EMIT do_halt_tx (id_, true);
|
||||
});
|
||||
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||
Q_EMIT do_halt_tx (id_, false);
|
||||
});
|
||||
content_layout->addWidget (control_button_box);
|
||||
|
||||
// set up status area
|
||||
auto status_bar = new QStatusBar;
|
||||
status_bar->addPermanentWidget (de_label_);
|
||||
status_bar->addPermanentWidget (mode_label_);
|
||||
status_bar->addPermanentWidget (frequency_label_);
|
||||
status_bar->addPermanentWidget (dx_label_);
|
||||
status_bar->addPermanentWidget (rx_df_label_);
|
||||
status_bar->addPermanentWidget (tx_df_label_);
|
||||
status_bar->addPermanentWidget (report_label_);
|
||||
content_layout->addWidget (status_bar);
|
||||
connect (this, &ClientWidget::topLevelChanged, status_bar, &QStatusBar::setSizeGripEnabled);
|
||||
|
||||
// set up central widget
|
||||
auto content_widget = new QFrame;
|
||||
content_widget->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
||||
content_widget->setLayout (content_layout);
|
||||
setWidget (content_widget);
|
||||
// setMinimumSize (QSize {550, 0});
|
||||
setFeatures (DockWidgetMovable | DockWidgetFloatable);
|
||||
setAllowedAreas (Qt::BottomDockWidgetArea);
|
||||
setFloating (true);
|
||||
|
||||
// connect up table view signals
|
||||
connect (decodes_table_view_, &QTableView::doubleClicked, this, [this] (QModelIndex const& index) {
|
||||
Q_EMIT do_reply (decodes_proxy_model_.mapToSource (index), QApplication::keyboardModifiers () >> 24);
|
||||
});
|
||||
|
||||
// tell new client about calls of interest
|
||||
for (int row = 0; row < calls_of_interest_->count (); ++row)
|
||||
{
|
||||
Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text (), QColor {Qt::blue}, QColor {Qt::yellow});
|
||||
}
|
||||
}
|
||||
|
||||
ClientWidget::~ClientWidget ()
|
||||
{
|
||||
for (int row = 0; row < calls_of_interest_->count (); ++row)
|
||||
{
|
||||
// tell client to forget calls of interest
|
||||
Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text ());
|
||||
}
|
||||
}
|
||||
|
||||
void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
||||
, QString const& report, QString const& tx_mode, bool tx_enabled
|
||||
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
|
||||
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode)
|
||||
{
|
||||
if (id == id_)
|
||||
{
|
||||
fast_mode_ = fast_mode;
|
||||
decodes_proxy_model_.de_call (de_call);
|
||||
decodes_proxy_model_.rx_df (rx_df);
|
||||
de_label_->setText (de_call.size () >= 0 ? QString {"DE: %1%2"}.arg (de_call)
|
||||
.arg (de_grid.size () ? '(' + de_grid + ')' : QString {}) : QString {});
|
||||
mode_label_->setText (QString {"Mode: %1%2%3%4"}
|
||||
.arg (mode)
|
||||
.arg (sub_mode)
|
||||
.arg (fast_mode && !mode.contains (QRegularExpression {R"(ISCAT|MSK144)"}) ? "fast" : "")
|
||||
.arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')'));
|
||||
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
||||
dx_label_->setText (dx_call.size () >= 0 ? QString {"DX: %1%2"}.arg (dx_call)
|
||||
.arg (dx_grid.size () ? '(' + dx_grid + ')' : QString {}) : QString {});
|
||||
rx_df_label_->setText (rx_df >= 0 ? QString {"Rx: %1"}.arg (rx_df) : "");
|
||||
tx_df_label_->setText (tx_df >= 0 ? QString {"Tx: %1"}.arg (tx_df) : "");
|
||||
report_label_->setText ("SNR: " + report);
|
||||
update_dynamic_property (frequency_label_, "transmitting", transmitting);
|
||||
auto_off_button_->setEnabled (tx_enabled);
|
||||
halt_tx_button_->setEnabled (transmitting);
|
||||
update_dynamic_property (mode_label_, "decoding", decoding);
|
||||
update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientWidget::decode_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
||||
, float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/
|
||||
, QString const& /*message*/, bool /*low_confidence*/, bool /*off_air*/)
|
||||
{
|
||||
if (client_id == id_ && !columns_resized_)
|
||||
{
|
||||
decodes_stack_->setCurrentIndex (0);
|
||||
decodes_table_view_->resizeColumnsToContents ();
|
||||
columns_resized_ = true;
|
||||
}
|
||||
decodes_table_view_->scrollToBottom ();
|
||||
}
|
||||
|
||||
void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
||||
, float /*delta_time*/, Frequency /*delta_frequency*/, qint32 /*drift*/
|
||||
, QString const& /*callsign*/, QString const& /*grid*/, qint32 /*power*/
|
||||
, bool /*off_air*/)
|
||||
{
|
||||
if (client_id == id_ && !columns_resized_)
|
||||
{
|
||||
decodes_stack_->setCurrentIndex (1);
|
||||
beacons_table_view_->resizeColumnsToContents ();
|
||||
columns_resized_ = true;
|
||||
}
|
||||
beacons_table_view_->scrollToBottom ();
|
||||
}
|
||||
|
||||
void ClientWidget::clear_decodes (QString const& client_id)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
columns_resized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_ClientWidget.cpp"
|
||||
@@ -1,94 +0,0 @@
|
||||
#ifndef WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||
#define WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||
|
||||
#include <QObject>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QModelIndex;
|
||||
class QColor;
|
||||
|
||||
using Frequency = MessageServer::Frequency;
|
||||
|
||||
class ClientWidget
|
||||
: public QDockWidget
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
||||
, QString const& id, QString const& version, QString const& revision
|
||||
, QListWidget const * calls_of_interest, QWidget * parent = nullptr);
|
||||
~ClientWidget ();
|
||||
|
||||
bool fast_mode () const {return fast_mode_;}
|
||||
|
||||
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
||||
, QString const& report, QString const& tx_mode, bool tx_enabled
|
||||
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
|
||||
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode);
|
||||
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr
|
||||
, float delta_time, quint32 delta_frequency, QString const& mode
|
||||
, QString const& message, bool low_confidence, bool off_air);
|
||||
Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime, qint32 snr
|
||||
, float delta_time, Frequency delta_frequency, qint32 drift
|
||||
, QString const& callsign, QString const& grid, qint32 power
|
||||
, bool off_air);
|
||||
Q_SLOT void clear_decodes (QString const& client_id);
|
||||
|
||||
Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier);
|
||||
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only);
|
||||
Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
|
||||
Q_SIGNAL void location (QString const& id, QString const& text);
|
||||
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
|
||||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||
, bool last_only = false);
|
||||
|
||||
private:
|
||||
QString id_;
|
||||
QListWidget const * calls_of_interest_;
|
||||
class IdFilterModel final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
IdFilterModel (QString const& client_id);
|
||||
|
||||
void de_call (QString const&);
|
||||
void rx_df (int);
|
||||
|
||||
QVariant data (QModelIndex const& proxy_index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override;
|
||||
|
||||
private:
|
||||
QString client_id_;
|
||||
QString call_;
|
||||
QRegularExpression base_call_re_;
|
||||
int rx_df_;
|
||||
} decodes_proxy_model_;
|
||||
QTableView * decodes_table_view_;
|
||||
QTableView * beacons_table_view_;
|
||||
QLineEdit * message_line_edit_;
|
||||
QLineEdit * grid_line_edit_;
|
||||
QStackedLayout * decodes_stack_;
|
||||
QAbstractButton * auto_off_button_;
|
||||
QAbstractButton * halt_tx_button_;
|
||||
QLabel * de_label_;
|
||||
QLabel * mode_label_;
|
||||
bool fast_mode_;
|
||||
QLabel * frequency_label_;
|
||||
QLabel * dx_label_;
|
||||
QLabel * rx_df_label_;
|
||||
QLabel * tx_df_label_;
|
||||
QLabel * report_label_;
|
||||
bool columns_resized_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,153 +0,0 @@
|
||||
#include "DecodesModel.hpp"
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <QModelIndex>
|
||||
#include <QTime>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
#include <QList>
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const headings[] = {
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Client"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Time"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Snr"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "DT"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "DF"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Md"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Confidence"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Live"),
|
||||
QT_TRANSLATE_NOOP ("DecodesModel", "Message"),
|
||||
};
|
||||
|
||||
QString confidence_string (bool low_confidence)
|
||||
{
|
||||
return low_confidence ? QT_TRANSLATE_NOOP ("DecodesModel", "low") : QT_TRANSLATE_NOOP ("DecodesModel", "high");
|
||||
}
|
||||
|
||||
QString live_string (bool off_air)
|
||||
{
|
||||
return off_air ? QT_TRANSLATE_NOOP ("DecodesModel", "no") : QT_TRANSLATE_NOOP ("DecodesModel", "yes");
|
||||
}
|
||||
|
||||
QFont text_font {"Courier", 10};
|
||||
|
||||
QList<QStandardItem *> make_row (QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode, QString const& message
|
||||
, bool low_confidence, bool off_air, bool is_fast)
|
||||
{
|
||||
auto time_item = new QStandardItem {time.toString (is_fast || "~" == mode ? "hh:mm:ss" : "hh:mm")};
|
||||
time_item->setData (time);
|
||||
time_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto snr_item = new QStandardItem {QString::number (snr)};
|
||||
snr_item->setData (snr);
|
||||
snr_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto dt = new QStandardItem {QString::number (delta_time)};
|
||||
dt->setData (delta_time);
|
||||
dt->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto df = new QStandardItem {QString::number (delta_frequency)};
|
||||
df->setData (delta_frequency);
|
||||
df->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto md = new QStandardItem {mode};
|
||||
md->setTextAlignment (Qt::AlignHCenter);
|
||||
|
||||
auto confidence = new QStandardItem {confidence_string (low_confidence)};
|
||||
confidence->setTextAlignment (Qt::AlignHCenter);
|
||||
|
||||
auto live = new QStandardItem {live_string (off_air)};
|
||||
live->setTextAlignment (Qt::AlignHCenter);
|
||||
|
||||
QList<QStandardItem *> row {
|
||||
new QStandardItem {client_id}, time_item, snr_item, dt, df, md, confidence, live, new QStandardItem {message}};
|
||||
Q_FOREACH (auto& item, row)
|
||||
{
|
||||
item->setEditable (false);
|
||||
item->setFont (text_font);
|
||||
item->setTextAlignment (item->textAlignment () | Qt::AlignVCenter);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
DecodesModel::DecodesModel (QObject * parent)
|
||||
: QStandardItemModel {0, sizeof (headings) / sizeof (headings[0]), parent}
|
||||
{
|
||||
int column {0};
|
||||
for (auto const& heading : headings)
|
||||
{
|
||||
setHeaderData (column++, Qt::Horizontal, tr (heading));
|
||||
}
|
||||
}
|
||||
|
||||
void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode, QString const& message
|
||||
, bool low_confidence, bool off_air, bool is_fast)
|
||||
{
|
||||
if (!is_new)
|
||||
{
|
||||
int target_row {-1};
|
||||
for (auto row = 0; row < rowCount (); ++row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
auto row_time = item (row, 1)->data ().toTime ();
|
||||
if (row_time == time
|
||||
&& item (row, 2)->data ().toInt () == snr
|
||||
&& item (row, 3)->data ().toFloat () == delta_time
|
||||
&& item (row, 4)->data ().toUInt () == delta_frequency
|
||||
&& data (index (row, 5)).toString () == mode
|
||||
&& data (index (row, 7)).toString () == confidence_string (low_confidence)
|
||||
&& data (index (row, 6)).toString () == live_string (off_air)
|
||||
&& data (index (row, 8)).toString () == message)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (time <= row_time)
|
||||
{
|
||||
target_row = row; // last row with same time
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_row >= 0)
|
||||
{
|
||||
insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, delta_frequency, mode
|
||||
, message, low_confidence, off_air, is_fast));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
appendRow (make_row (client_id, time, snr, delta_time, delta_frequency, mode, message, low_confidence
|
||||
, off_air, is_fast));
|
||||
}
|
||||
|
||||
void DecodesModel::clear_decodes (QString const& client_id)
|
||||
{
|
||||
for (auto row = rowCount () - 1; row >= 0; --row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
removeRow (row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DecodesModel::do_reply (QModelIndex const& source, quint8 modifiers)
|
||||
{
|
||||
auto row = source.row ();
|
||||
Q_EMIT reply (data (index (row, 0)).toString ()
|
||||
, item (row, 1)->data ().toTime ()
|
||||
, item (row, 2)->data ().toInt ()
|
||||
, item (row, 3)->data ().toFloat ()
|
||||
, item (row, 4)->data ().toInt ()
|
||||
, data (index (row, 5)).toString ()
|
||||
, data (index (row, 8)).toString ()
|
||||
, confidence_string (true) == data (index (row, 7)).toString ()
|
||||
, modifiers);
|
||||
}
|
||||
|
||||
#include "moc_DecodesModel.cpp"
|
||||
@@ -1,44 +0,0 @@
|
||||
#ifndef WSJTX_UDP_DECODES_MODEL_HPP__
|
||||
#define WSJTX_UDP_DECODES_MODEL_HPP__
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
|
||||
using Frequency = MessageServer::Frequency;
|
||||
|
||||
class QTime;
|
||||
class QString;
|
||||
class QModelIndex;
|
||||
|
||||
//
|
||||
// Decodes Model - simple data model for all decodes
|
||||
//
|
||||
// The model is a basic table with uniform row format. Rows consist of
|
||||
// QStandardItem instances containing the string representation of the
|
||||
// column data and if the underlying field is not a string then the
|
||||
// UserRole+1 role contains the underlying data item.
|
||||
//
|
||||
// Three slots are provided to add a new decode, remove all decodes
|
||||
// for a client and, to build a reply to CQ message for a given row
|
||||
// which is emitted as a signal respectively.
|
||||
//
|
||||
class DecodesModel
|
||||
: public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit DecodesModel (QObject * parent = nullptr);
|
||||
|
||||
Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode, QString const& message
|
||||
, bool low_confidence, bool off_air, bool is_fast);
|
||||
Q_SLOT void clear_decodes (QString const& client_id);
|
||||
Q_SLOT void do_reply (QModelIndex const& source, quint8 modifiers);
|
||||
|
||||
Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
|
||||
, QString const& mode, QString const& message, bool low_confidence, quint8 modifiers);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,90 +0,0 @@
|
||||
//
|
||||
// MessageAggregator - an example application that utilizes the WSJT-X
|
||||
// messaging facility
|
||||
//
|
||||
// This application is only provided as a simple GUI application
|
||||
// example to demonstrate the WSJT-X messaging facility. It allows the
|
||||
// user to set the server details either as a unicast UDP server or,
|
||||
// if a multicast group address is provided, as a multicast server.
|
||||
// The benefit of the multicast server is that multiple servers can be
|
||||
// active at once each receiving all WSJT-X broadcast messages and
|
||||
// each able to respond to individual WSJT_X clients. To utilize the
|
||||
// multicast group features each WSJT-X client must set the same
|
||||
// multicast group address as the UDP server address for example
|
||||
// 239.255.0.0 for a site local multicast group.
|
||||
//
|
||||
// The UI is a small panel to input the service port number and
|
||||
// optionally the multicast group address. Below that a table
|
||||
// representing the log entries where any QSO logged messages
|
||||
// broadcast from WSJT-X clients are displayed. The bottom of the
|
||||
// application main window is a dock area where a dock window will
|
||||
// appear for each WSJT-X client, this window contains a table of the
|
||||
// current decode messages broadcast from that WSJT-X client and a
|
||||
// status line showing the status update messages broadcast from the
|
||||
// WSJT-X client. The dock windows may be arranged in a tab bar, side
|
||||
// by side, below each other or, completely detached from the dock
|
||||
// area as floating windows. Double clicking the dock window title bar
|
||||
// or dragging and dropping with the mouse allows these different
|
||||
// arrangements.
|
||||
//
|
||||
// The application also provides a simple menu bar including a view
|
||||
// menu that allows each dock window to be hidden or revealed.
|
||||
//
|
||||
|
||||
#include <clocale>
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include <QFile>
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
|
||||
#include "MessageAggregatorMainWindow.hpp"
|
||||
|
||||
// deduce the size of an array
|
||||
template<class T, size_t N>
|
||||
inline
|
||||
size_t size (T (&)[N]) {return N;}
|
||||
|
||||
int main (int argc, char * argv[])
|
||||
{
|
||||
QApplication app {argc, argv};
|
||||
try
|
||||
{
|
||||
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
|
||||
// consistent format, do this after
|
||||
// instantiating QApplication so
|
||||
// that GUI has correct l18n
|
||||
|
||||
app.setApplicationName ("WSJT-X Reference UDP Message Aggregator Server");
|
||||
app.setApplicationVersion ("1.0");
|
||||
|
||||
QObject::connect (&app, SIGNAL (lastWindowClosed ()), &app, SLOT (quit ()));
|
||||
|
||||
{
|
||||
QFile file {":/qss/default.qss"};
|
||||
if (!file.open (QFile::ReadOnly))
|
||||
{
|
||||
throw std::runtime_error {
|
||||
QString {"failed to open \"" + file.fileName () + "\": " + file.errorString ()}
|
||||
.toLocal8Bit ().constData ()};
|
||||
}
|
||||
app.setStyleSheet (file.readAll());
|
||||
}
|
||||
|
||||
MessageAggregatorMainWindow window;
|
||||
return app.exec ();
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
QMessageBox::critical (nullptr, app.applicationName (), e.what ());
|
||||
std::cerr << "Error: " << e.what () << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
QMessageBox::critical (nullptr, app.applicationName (), QObject::tr ("Unexpected error"));
|
||||
std::cerr << "Unexpected error\n";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
#include "MessageAggregatorMainWindow.hpp"
|
||||
|
||||
#include <QtWidgets>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "DecodesModel.hpp"
|
||||
#include "BeaconsModel.hpp"
|
||||
#include "ClientWidget.hpp"
|
||||
|
||||
using port_type = MessageServer::port_type;
|
||||
|
||||
namespace
|
||||
{
|
||||
char const * const headings[] = {
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Time On"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Time Off"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Callsign"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Grid"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Name"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Frequency"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Mode"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Sent"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Rec'd"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Power"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Operator"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Call"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Grid"),
|
||||
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Comments"),
|
||||
};
|
||||
}
|
||||
|
||||
MessageAggregatorMainWindow::MessageAggregatorMainWindow ()
|
||||
: log_ {new QStandardItemModel {0, 14, this}}
|
||||
, decodes_model_ {new DecodesModel {this}}
|
||||
, beacons_model_ {new BeaconsModel {this}}
|
||||
, server_ {new MessageServer {this}}
|
||||
, multicast_group_line_edit_ {new QLineEdit}
|
||||
, log_table_view_ {new QTableView}
|
||||
, add_call_of_interest_action_ {new QAction {tr ("&Add callsign"), this}}
|
||||
, delete_call_of_interest_action_ {new QAction {tr ("&Delete callsign"), this}}
|
||||
, last_call_of_interest_action_ {new QAction {tr ("&Highlight last only"), this}}
|
||||
, call_of_interest_bg_colour_action_ {new QAction {tr ("&Background colour"), this}}
|
||||
, call_of_interest_fg_colour_action_ {new QAction {tr ("&Foreground colour"), this}}
|
||||
{
|
||||
// logbook
|
||||
int column {0};
|
||||
for (auto const& heading : headings)
|
||||
{
|
||||
log_->setHeaderData (column++, Qt::Horizontal, tr (heading));
|
||||
}
|
||||
connect (server_, &MessageServer::qso_logged, this, &MessageAggregatorMainWindow::log_qso);
|
||||
|
||||
// menu bar
|
||||
auto file_menu = menuBar ()->addMenu (tr ("&File"));
|
||||
|
||||
auto exit_action = new QAction {tr ("E&xit"), this};
|
||||
exit_action->setShortcuts (QKeySequence::Quit);
|
||||
exit_action->setToolTip (tr ("Exit the application"));
|
||||
file_menu->addAction (exit_action);
|
||||
connect (exit_action, &QAction::triggered, this, &MessageAggregatorMainWindow::close);
|
||||
|
||||
view_menu_ = menuBar ()->addMenu (tr ("&View"));
|
||||
|
||||
// central layout
|
||||
auto central_layout = new QVBoxLayout;
|
||||
|
||||
// server details
|
||||
auto port_spin_box = new QSpinBox;
|
||||
port_spin_box->setMinimum (1);
|
||||
port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
|
||||
auto group_box_layout = new QFormLayout;
|
||||
group_box_layout->addRow (tr ("Port number:"), port_spin_box);
|
||||
group_box_layout->addRow (tr ("Multicast Group (blank for unicast server):"), multicast_group_line_edit_);
|
||||
auto group_box = new QGroupBox {tr ("Server Details")};
|
||||
group_box->setLayout (group_box_layout);
|
||||
central_layout->addWidget (group_box);
|
||||
|
||||
log_table_view_->setModel (log_);
|
||||
log_table_view_->verticalHeader ()->hide ();
|
||||
central_layout->addWidget (log_table_view_);
|
||||
|
||||
// central widget
|
||||
auto central_widget = new QWidget;
|
||||
central_widget->setLayout (central_layout);
|
||||
|
||||
// main window setup
|
||||
setCentralWidget (central_widget);
|
||||
setDockOptions (AnimatedDocks | AllowNestedDocks | AllowTabbedDocks);
|
||||
setTabPosition (Qt::BottomDockWidgetArea, QTabWidget::North);
|
||||
|
||||
QDockWidget * calls_dock {new QDockWidget {tr ("Calls of Interest"), this}};
|
||||
calls_dock->setAllowedAreas (Qt::RightDockWidgetArea);
|
||||
calls_of_interest_ = new QListWidget {calls_dock};
|
||||
calls_of_interest_->setContextMenuPolicy (Qt::ActionsContextMenu);
|
||||
calls_of_interest_->insertAction (nullptr, add_call_of_interest_action_);
|
||||
connect (add_call_of_interest_action_, &QAction::triggered, [this] () {
|
||||
auto item = new QListWidgetItem {};
|
||||
item->setFlags (item->flags () | Qt::ItemIsEditable);
|
||||
item->setData (Qt::UserRole, QString {});
|
||||
calls_of_interest_->addItem (item);
|
||||
calls_of_interest_->editItem (item);
|
||||
});
|
||||
calls_of_interest_->insertAction (nullptr, delete_call_of_interest_action_);
|
||||
connect (delete_call_of_interest_action_, &QAction::triggered, [this] () {
|
||||
for (auto item : calls_of_interest_->selectedItems ())
|
||||
{
|
||||
auto old_call = item->data (Qt::UserRole);
|
||||
if (old_call.isValid ()) change_highlighting (old_call.toString ());
|
||||
delete item;
|
||||
}
|
||||
});
|
||||
calls_of_interest_->insertAction (nullptr, last_call_of_interest_action_);
|
||||
connect (last_call_of_interest_action_, &QAction::triggered, [this] () {
|
||||
for (auto item : calls_of_interest_->selectedItems ())
|
||||
{
|
||||
auto old_call = item->data (Qt::UserRole);
|
||||
change_highlighting (old_call.toString ());
|
||||
change_highlighting (old_call.toString ()
|
||||
, item->background ().color (), item->foreground ().color (), true);
|
||||
delete item;
|
||||
}
|
||||
});
|
||||
calls_of_interest_->insertAction (nullptr, call_of_interest_bg_colour_action_);
|
||||
connect (call_of_interest_bg_colour_action_, &QAction::triggered, [this] () {
|
||||
for (auto item : calls_of_interest_->selectedItems ())
|
||||
{
|
||||
auto old_call = item->data (Qt::UserRole);
|
||||
auto new_colour = QColorDialog::getColor (item->background ().color ()
|
||||
, this, tr ("Select background color"));
|
||||
if (new_colour.isValid ())
|
||||
{
|
||||
change_highlighting (old_call.toString (), new_colour, item->foreground ().color ());
|
||||
item->setBackground (new_colour);
|
||||
}
|
||||
}
|
||||
});
|
||||
calls_of_interest_->insertAction (nullptr, call_of_interest_fg_colour_action_);
|
||||
connect (call_of_interest_fg_colour_action_, &QAction::triggered, [this] () {
|
||||
for (auto item : calls_of_interest_->selectedItems ())
|
||||
{
|
||||
auto old_call = item->data (Qt::UserRole);
|
||||
auto new_colour = QColorDialog::getColor (item->foreground ().color ()
|
||||
, this, tr ("Select foreground color"));
|
||||
if (new_colour.isValid ())
|
||||
{
|
||||
change_highlighting (old_call.toString (), item->background ().color (), new_colour);
|
||||
item->setForeground (new_colour);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect (calls_of_interest_, &QListWidget::itemChanged, [this] (QListWidgetItem * item) {
|
||||
auto old_call = item->data (Qt::UserRole);
|
||||
auto new_call = item->text ().toUpper ();
|
||||
if (new_call != old_call)
|
||||
{
|
||||
// tell all clients
|
||||
if (old_call.isValid ())
|
||||
{
|
||||
change_highlighting (old_call.toString ());
|
||||
}
|
||||
item->setData (Qt::UserRole, new_call);
|
||||
item->setText (new_call);
|
||||
auto bg = item->listWidget ()->palette ().text ().color ();
|
||||
auto fg = item->listWidget ()->palette ().base ().color ();
|
||||
item->setBackground (bg);
|
||||
item->setForeground (fg);
|
||||
change_highlighting (new_call, bg, fg);
|
||||
}
|
||||
});
|
||||
|
||||
calls_dock->setWidget (calls_of_interest_);
|
||||
addDockWidget (Qt::RightDockWidgetArea, calls_dock);
|
||||
view_menu_->addAction (calls_dock->toggleViewAction ());
|
||||
view_menu_->addSeparator ();
|
||||
|
||||
// connect up server
|
||||
connect (server_, &MessageServer::error, [this] (QString const& message) {
|
||||
QMessageBox::warning (this, QApplication::applicationName (), tr ("Network Error"), message);
|
||||
});
|
||||
connect (server_, &MessageServer::client_opened, this, &MessageAggregatorMainWindow::add_client);
|
||||
connect (server_, &MessageServer::client_closed, this, &MessageAggregatorMainWindow::remove_client);
|
||||
connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::clear_decodes);
|
||||
connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::clear_decodes);
|
||||
connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time
|
||||
, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode
|
||||
, QString const& message, bool low_confidence
|
||||
, bool off_air) {
|
||||
decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message
|
||||
, low_confidence, off_air, dock_widgets_[id]->fast_mode ());});
|
||||
connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot);
|
||||
connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes);
|
||||
connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes);
|
||||
connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply);
|
||||
|
||||
// UI behaviour
|
||||
connect (port_spin_box, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged)
|
||||
, [this] (port_type port) {server_->start (port);});
|
||||
connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this, port_spin_box] () {
|
||||
server_->start (port_spin_box->value (), QHostAddress {multicast_group_line_edit_->text ()});
|
||||
});
|
||||
|
||||
port_spin_box->setValue (2237); // start up in unicast mode
|
||||
show ();
|
||||
}
|
||||
|
||||
void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call
|
||||
, QString const& dx_grid, Frequency dial_frequency, QString const& mode
|
||||
, QString const& report_sent, QString const& report_received
|
||||
, QString const& tx_power, QString const& comments
|
||||
, QString const& name, QDateTime time_on, QString const& operator_call
|
||||
, QString const& my_call, QString const& my_grid)
|
||||
{
|
||||
QList<QStandardItem *> row;
|
||||
row << new QStandardItem {time_on.toString ("dd-MMM-yyyy hh:mm:ss")}
|
||||
<< new QStandardItem {time_off.toString ("dd-MMM-yyyy hh:mm:ss")}
|
||||
<< new QStandardItem {dx_call}
|
||||
<< new QStandardItem {dx_grid}
|
||||
<< new QStandardItem {name}
|
||||
<< new QStandardItem {Radio::frequency_MHz_string (dial_frequency)}
|
||||
<< new QStandardItem {mode}
|
||||
<< new QStandardItem {report_sent}
|
||||
<< new QStandardItem {report_received}
|
||||
<< new QStandardItem {tx_power}
|
||||
<< new QStandardItem {operator_call}
|
||||
<< new QStandardItem {my_call}
|
||||
<< new QStandardItem {my_grid}
|
||||
<< new QStandardItem {comments};
|
||||
log_->appendRow (row);
|
||||
log_table_view_->resizeColumnsToContents ();
|
||||
log_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
log_table_view_->scrollToBottom ();
|
||||
}
|
||||
|
||||
void MessageAggregatorMainWindow::add_client (QString const& id, QString const& version, QString const& revision)
|
||||
{
|
||||
auto dock = new ClientWidget {decodes_model_, beacons_model_, id, version, revision, calls_of_interest_, this};
|
||||
dock->setAttribute (Qt::WA_DeleteOnClose);
|
||||
auto view_action = dock->toggleViewAction ();
|
||||
view_action->setEnabled (true);
|
||||
view_menu_->addAction (view_action);
|
||||
addDockWidget (Qt::BottomDockWidgetArea, dock);
|
||||
connect (server_, &MessageServer::status_update, dock, &ClientWidget::update_status);
|
||||
connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added);
|
||||
connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added);
|
||||
connect (server_, &MessageServer::clear_decodes, dock, &ClientWidget::clear_decodes);
|
||||
connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply);
|
||||
connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx);
|
||||
connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text);
|
||||
connect (dock, &ClientWidget::location, server_, &MessageServer::location);
|
||||
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
|
||||
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
|
||||
dock_widgets_[id] = dock;
|
||||
server_->replay (id); // request decodes and status
|
||||
}
|
||||
|
||||
void MessageAggregatorMainWindow::remove_client (QString const& id)
|
||||
{
|
||||
auto iter = dock_widgets_.find (id);
|
||||
if (iter != std::end (dock_widgets_))
|
||||
{
|
||||
(*iter)->close ();
|
||||
dock_widgets_.erase (iter);
|
||||
}
|
||||
}
|
||||
|
||||
MessageAggregatorMainWindow::~MessageAggregatorMainWindow ()
|
||||
{
|
||||
for (auto client : dock_widgets_)
|
||||
{
|
||||
delete client;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageAggregatorMainWindow::change_highlighting (QString const& call, QColor const& bg, QColor const& fg
|
||||
, bool last_only)
|
||||
{
|
||||
for (auto id : dock_widgets_.keys ())
|
||||
{
|
||||
server_->highlight_callsign (id, call, bg, fg, last_only);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_MessageAggregatorMainWindow.cpp"
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef WSJTX_MESSAGE_AGGREGATOR_MAIN_WINDOW_MODEL_HPP__
|
||||
#define WSJTX_MESSAGE_AGGREGATOR_MAIN_WINDOW_MODEL_HPP__
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
|
||||
class QDateTime;
|
||||
class QStandardItemModel;
|
||||
class QMenu;
|
||||
class DecodesModel;
|
||||
class BeaconsModel;
|
||||
class QLineEdit;
|
||||
class QTableView;
|
||||
class ClientWidget;
|
||||
class QListWidget;
|
||||
|
||||
using Frequency = MessageServer::Frequency;
|
||||
|
||||
class MessageAggregatorMainWindow
|
||||
: public QMainWindow
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
MessageAggregatorMainWindow ();
|
||||
~MessageAggregatorMainWindow ();
|
||||
|
||||
Q_SLOT void log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call, QString const& dx_grid
|
||||
, Frequency dial_frequency, QString const& mode, QString const& report_sent
|
||||
, QString const& report_received, QString const& tx_power, QString const& comments
|
||||
, QString const& name, QDateTime time_on, QString const& operator_call
|
||||
, QString const& my_call, QString const& my_grid);
|
||||
|
||||
private:
|
||||
void add_client (QString const& id, QString const& version, QString const& revision);
|
||||
void remove_client (QString const& id);
|
||||
void change_highlighting (QString const& call, QColor const& bg = QColor {}, QColor const& fg = QColor {},
|
||||
bool last_only = false);
|
||||
|
||||
// maps client id to widgets
|
||||
using ClientsDictionary = QHash<QString, ClientWidget *>;
|
||||
ClientsDictionary dock_widgets_;
|
||||
|
||||
QStandardItemModel * log_;
|
||||
QMenu * view_menu_;
|
||||
DecodesModel * decodes_model_;
|
||||
BeaconsModel * beacons_model_;
|
||||
MessageServer * server_;
|
||||
QLineEdit * multicast_group_line_edit_;
|
||||
QTableView * log_table_view_;
|
||||
QListWidget * calls_of_interest_;
|
||||
QAction * add_call_of_interest_action_;
|
||||
QAction * delete_call_of_interest_action_;
|
||||
QAction * last_call_of_interest_action_;
|
||||
QAction * call_of_interest_bg_colour_action_;
|
||||
QAction * call_of_interest_fg_colour_action_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,245 +0,0 @@
|
||||
//
|
||||
// UDPDaemon - an example console application that utilizes the WSJT-X
|
||||
// messaging facility
|
||||
//
|
||||
// This application is only provided as a simple console application
|
||||
// example to demonstrate the WSJT-X messaging facility. It allows
|
||||
// the user to set the server details either as a unicast UDP server
|
||||
// or, if a multicast group address is provided, as a multicast
|
||||
// server. The benefit of the multicast server is that multiple
|
||||
// servers can be active at once each receiving all WSJT-X broadcast
|
||||
// messages and each able to respond to individual WSJT_X clients. To
|
||||
// utilize the multicast group features each WSJT-X client must set
|
||||
// the same multicast group address as the UDP server address for
|
||||
// example 239.255.0.0 for a site local multicast group.
|
||||
//
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDateTime>
|
||||
#include <QTime>
|
||||
#include <QHash>
|
||||
#include <QDebug>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
#include "Radio.hpp"
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
|
||||
using port_type = MessageServer::port_type;
|
||||
using Frequency = MessageServer::Frequency;
|
||||
|
||||
class Client
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Client (QString const& id, QObject * parent = nullptr)
|
||||
: QObject {parent}
|
||||
, id_ {id}
|
||||
, dial_frequency_ {0u}
|
||||
{
|
||||
}
|
||||
|
||||
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& /*dx_call*/
|
||||
, QString const& /*report*/, QString const& /*tx_mode*/, bool /*tx_enabled*/
|
||||
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
|
||||
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
|
||||
, bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/)
|
||||
{
|
||||
if (id == id_)
|
||||
{
|
||||
if (f != dial_frequency_)
|
||||
{
|
||||
std::cout << tr ("%1: Dial frequency changed to %2").arg (id_).arg (f).toStdString () << std::endl;
|
||||
dial_frequency_ = f;
|
||||
}
|
||||
if (mode + sub_mode != mode_)
|
||||
{
|
||||
std::cout << tr ("%1: Mode changed to %2").arg (id_).arg (mode + sub_mode).toStdString () << std::endl;
|
||||
mode_ = mode + sub_mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime time, qint32 snr
|
||||
, float delta_time, quint32 delta_frequency, QString const& mode
|
||||
, QString const& message, bool low_confidence, bool off_air)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr
|
||||
<< "Dt:" << delta_time << "Df:" << delta_frequency
|
||||
<< "mode:" << mode << "Confidence:" << (low_confidence ? "low" : "high")
|
||||
<< "On air:" << !off_air;
|
||||
std::cout << tr ("%1: Decoded %2").arg (id_).arg (message).toStdString () << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime time, qint32 snr
|
||||
, float delta_time, Frequency delta_frequency, qint32 drift, QString const& callsign
|
||||
, QString const& grid, qint32 power, bool off_air)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr
|
||||
<< "Dt:" << delta_time << "Df:" << delta_frequency
|
||||
<< "drift:" << drift;
|
||||
std::cout << tr ("%1: WSPR decode %2 grid %3 power: %4").arg (id_).arg (callsign).arg (grid).arg (power).toStdString ()
|
||||
<< "On air:" << !off_air << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void qso_logged (QString const&client_id, QDateTime time_off, QString const& dx_call, QString const& dx_grid
|
||||
, Frequency dial_frequency, QString const& mode, QString const& report_sent
|
||||
, QString const& report_received, QString const& tx_power
|
||||
, QString const& comments, QString const& name, QDateTime time_on
|
||||
, QString const& operator_call, QString const& my_call, QString const& my_grid)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
qDebug () << "time_on:" << time_on << "time_off:" << time_off << "dx_call:" << dx_call << "grid:" << dx_grid
|
||||
<< "freq:" << dial_frequency << "mode:" << mode << "rpt_sent:" << report_sent
|
||||
<< "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments
|
||||
<< "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call
|
||||
<< "my_grid:" << my_grid;
|
||||
std::cout << QByteArray {80, '-'}.data () << '\n';
|
||||
std::cout << tr ("%1: Logged %2 grid: %3 power: %4 sent: %5 recd: %6 freq: %7 time_off: %8 op: %9 my_call: %10 my_grid: %11")
|
||||
.arg (id_).arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received)
|
||||
.arg (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call)
|
||||
.arg (my_call).arg (my_grid).toStdString ()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void logged_ADIF (QString const&client_id, QByteArray const& ADIF)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
qDebug () << "ADIF:" << ADIF;
|
||||
std::cout << QByteArray {80, '-'}.data () << '\n';
|
||||
std::cout << ADIF.data () << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QString id_;
|
||||
Frequency dial_frequency_;
|
||||
QString mode_;
|
||||
};
|
||||
|
||||
class Server
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Server (port_type port, QHostAddress const& multicast_group)
|
||||
: server_ {new MessageServer {this}}
|
||||
{
|
||||
// connect up server
|
||||
connect (server_, &MessageServer::error, [this] (QString const& message) {
|
||||
std::cerr << tr ("Network Error: %1").arg ( message).toStdString () << std::endl;
|
||||
});
|
||||
connect (server_, &MessageServer::client_opened, this, &Server::add_client);
|
||||
connect (server_, &MessageServer::client_closed, this, &Server::remove_client);
|
||||
|
||||
server_->start (port, multicast_group);
|
||||
}
|
||||
|
||||
private:
|
||||
void add_client (QString const& id, QString const& version, QString const& revision)
|
||||
{
|
||||
auto client = new Client {id};
|
||||
connect (server_, &MessageServer::status_update, client, &Client::update_status);
|
||||
connect (server_, &MessageServer::decode, client, &Client::decode_added);
|
||||
connect (server_, &MessageServer::WSPR_decode, client, &Client::beacon_spot_added);
|
||||
connect (server_, &MessageServer::qso_logged, client, &Client::qso_logged);
|
||||
connect (server_, &MessageServer::logged_ADIF, client, &Client::logged_ADIF);
|
||||
clients_[id] = client;
|
||||
server_->replay (id);
|
||||
std::cout << "Discovered WSJT-X instance: " << id.toStdString ();
|
||||
if (version.size ())
|
||||
{
|
||||
std::cout << " v" << version.toStdString ();
|
||||
}
|
||||
if (revision.size ())
|
||||
{
|
||||
std::cout << " (" << revision.toStdString () << ")";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void remove_client (QString const& id)
|
||||
{
|
||||
auto iter = clients_.find (id);
|
||||
if (iter != std::end (clients_))
|
||||
{
|
||||
clients_.erase (iter);
|
||||
(*iter)->deleteLater ();
|
||||
}
|
||||
std::cout << "Removed WSJT-X instance: " << id.toStdString () << std::endl;
|
||||
}
|
||||
|
||||
MessageServer * server_;
|
||||
|
||||
// maps client id to clients
|
||||
QHash<QString, Client *> clients_;
|
||||
};
|
||||
|
||||
#include "UDPDaemon.moc"
|
||||
|
||||
int main (int argc, char * argv[])
|
||||
{
|
||||
QCoreApplication app {argc, argv};
|
||||
try
|
||||
{
|
||||
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
|
||||
// consistent format, do this after
|
||||
// instantiating QApplication so
|
||||
// that GUI has correct l18n
|
||||
|
||||
app.setApplicationName ("WSJT-X UDP Message Server Daemon");
|
||||
app.setApplicationVersion ("1.0");
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription ("\nWSJT-X UDP Message Server Daemon.");
|
||||
auto help_option = parser.addHelpOption ();
|
||||
auto version_option = parser.addVersionOption ();
|
||||
|
||||
QCommandLineOption port_option (QStringList {"p", "port"},
|
||||
app.translate ("UDPDaemon",
|
||||
"Where <PORT> is the UDP service port number to listen on.\n"
|
||||
"The default service port is 2237."),
|
||||
app.translate ("UDPDaemon", "PORT"),
|
||||
"2237");
|
||||
parser.addOption (port_option);
|
||||
|
||||
QCommandLineOption multicast_addr_option (QStringList {"g", "multicast-group"},
|
||||
app.translate ("UDPDaemon",
|
||||
"Where <GROUP> is the multicast group to join.\n"
|
||||
"The default is a unicast server listening on all interfaces."),
|
||||
app.translate ("UDPDaemon", "GROUP"));
|
||||
parser.addOption (multicast_addr_option);
|
||||
|
||||
parser.process (app);
|
||||
|
||||
Server server {static_cast<port_type> (parser.value (port_option).toUInt ()), QHostAddress {parser.value (multicast_addr_option)}};
|
||||
|
||||
return app.exec ();
|
||||
}
|
||||
catch (std::exception const & e)
|
||||
{
|
||||
std::cerr << "Error: " << e.what () << '\n';
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << "Unexpected error\n";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource>@message_aggregator_RESOURCES@
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -1 +0,0 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "../icons/windows-icons/ft8call.ico"
|
||||
@@ -1,13 +0,0 @@
|
||||
/* default stylesheet for the message aggregator application */
|
||||
|
||||
[transmitting="true"] {
|
||||
background-color: yellow
|
||||
}
|
||||
|
||||
[decoding="true"] {
|
||||
background-color: cyan
|
||||
}
|
||||
|
||||
[watchdog_timeout="true"] {
|
||||
background-color: red
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
IDI_ICON1 ICON DISCARDABLE "../icons/windows-icons/ft8call.ico"
|
||||
@@ -1,6 +1,6 @@
|
||||
# Version number components
|
||||
set (WSJTX_VERSION_MAJOR 0)
|
||||
set (WSJTX_VERSION_MINOR 6)
|
||||
set (WSJTX_VERSION_PATCH 5)
|
||||
set (WSJTX_VERSION_MINOR 8)
|
||||
set (WSJTX_VERSION_PATCH 1)
|
||||
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions
|
||||
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "Bands.hpp"
|
||||
#include "FrequencyList.hpp"
|
||||
#include "WsprTxScheduler.h"
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
#include "moc_WSPRBandHopping.cpp"
|
||||
|
||||
@@ -333,7 +335,7 @@ void WSPRBandHopping::set_tx_percent (int new_value)
|
||||
// determine the parameters of the hop, if any
|
||||
auto WSPRBandHopping::next_hop (bool tx_enabled) -> Hop
|
||||
{
|
||||
auto const& now = QDateTime::currentDateTimeUtc ();
|
||||
auto const& now = DriftingDateTime::currentDateTimeUtc ();
|
||||
auto const& date = now.date ();
|
||||
auto year = date.year ();
|
||||
auto month = date.month ();
|
||||
|
||||
@@ -13,27 +13,28 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->labelTxt->setText ("<h2>" + QString {"FT8Call v"
|
||||
ui->labelTxt->setText ("<h2>" + QString {"JS8Call v"
|
||||
+ QCoreApplication::applicationVersion ()
|
||||
+ " " + revision ()}.simplified () + "</h2><br />"
|
||||
|
||||
"FT8Call is a derivative of the WSJT-X application, "
|
||||
"JS8Call is a derivative of the WSJT-X application, "
|
||||
"restructured and redesigned for message passing. <br/>"
|
||||
"It is not supported by nor endorsed by the WSJT-X "
|
||||
"development group. <br/>FT8Call is "
|
||||
"development group. <br/>JS8Call is "
|
||||
"licensed under and in accordance with the terms "
|
||||
"of the <a href=\"https://www.gnu.org/licenses/gpl-3.0.txt\">GPLv3 license</a>.<br/>"
|
||||
"The source code modifications are public and can be found in <a href=\"https://bitbucket.org/widefido/wsjtx/\">this repository</a>.<br/><br/>"
|
||||
|
||||
"FT8Call is heavily inspired by WSJT-X, Fldigi, "
|
||||
"JS8Call is heavily inspired by WSJT-X, Fldigi, "
|
||||
"and FSQCall <br/>and would not exist without the hard work and "
|
||||
"dedication of the many <br/>developers in the amateur radio "
|
||||
"community.<br /><br />"
|
||||
"FT8Call stands on the shoulder of giants...the takeoff angle "
|
||||
"JS8Call stands on the shoulder of giants...the takeoff angle "
|
||||
"is better up there.<br /><br />"
|
||||
"A special thanks goes out to the FT8Call development team:<br/><br/><strong>"
|
||||
"A special thanks goes out to the JS8Call development team:<br/><br/><strong>"
|
||||
"KC9QNE, "
|
||||
"KI6SSI, "
|
||||
"K0OG, "
|
||||
"LB9YH, "
|
||||
"M0IAX, "
|
||||
"N0JDS, "
|
||||
@@ -41,7 +42,7 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
||||
"VA3OSO, "
|
||||
"VK1MIC, "
|
||||
"W0FW,</strong><br/><br/>and the many other amateur radio operators who have given "
|
||||
"FT8Call a chance.");
|
||||
"JS8Call a chance.");
|
||||
}
|
||||
|
||||
CAboutDlg::~CAboutDlg()
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About FT8Call</string>
|
||||
<string>About JS8Call</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import socket
|
||||
|
||||
KKEY = 0x73e2
|
||||
|
||||
def do_hash(callsign):
|
||||
rootCall = callsign.split("-")[0].upper() + '\0'
|
||||
|
||||
hash = KKEY
|
||||
i = 0
|
||||
length = len(rootCall)
|
||||
|
||||
while (i+1 < length):
|
||||
hash ^= ord(rootCall[i])<<8
|
||||
hash ^= ord(rootCall[i+1])
|
||||
i += 2
|
||||
|
||||
return int(hash & 0x7fff)
|
||||
|
||||
HOST = 'rotate.aprs2.net'
|
||||
#HOST = 'rotate.aprs.net'
|
||||
PORT = 14580
|
||||
|
||||
print "Connecting..."
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((HOST, PORT))
|
||||
|
||||
print "Connected..."
|
||||
|
||||
data = s.recv(1024)
|
||||
print data
|
||||
|
||||
call = 'KN4CRD'
|
||||
pw = do_hash(call)
|
||||
ver = "FT8Call"
|
||||
|
||||
login = "user {} pass {} ver {}\n".format(call, pw, ver)
|
||||
s.send(login)
|
||||
|
||||
print "Login sent...", login
|
||||
|
||||
data = s.recv(1024)
|
||||
print data
|
||||
|
||||
if 0:
|
||||
message = "KN4CRD>OH8STN,APRS,TCPIP*::EMAIL-2 :KN4CRD@GMAIL.COM TESTING!{04}\n"
|
||||
s.send(message)
|
||||
|
||||
if 0:
|
||||
message = "{}>APRS,TCPIP*::EMAIL-2 :kn4crd@gmail.com testing456{{01}}\n".format(call)
|
||||
message = "KN4CRD>APRS,TCPIP*::EMAIL-2 :KN4CRD@GMAIL.COM TESTING!{02}\n"
|
||||
s.send(message)
|
||||
|
||||
if 0:
|
||||
payload = ":This is a test message"
|
||||
message = "{}>APRS,TCPIP*::{} {}\n".format(call, call, payload)
|
||||
s.send(message)
|
||||
if 1:
|
||||
position = "=3352.45N/08422.71Wn"
|
||||
status = "FT8CALL VIA XX9XXX/XXXX 14.082500MHz -20dB"
|
||||
payload = "".join((position, status))
|
||||
message = "{}>OH8STN,APRS,TCPIP*:{}\n".format(call, payload)
|
||||
s.send(message)
|
||||
|
||||
print "Spot sent...", message
|
||||
|
||||
data = s.recv(1024)
|
||||
print data
|
||||
|
||||
s.close()
|
||||
|
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 115 KiB |
@@ -25,9 +25,9 @@ convert '/tmp/image-%d.png[0-1]' -background transparent \
|
||||
\( -clone 1 -resize 128 -compress none \) \
|
||||
\( -clone 1 -resize 256 -compress Zip \) \
|
||||
-delete 1 -delete 0 \
|
||||
-alpha remove ../icons/windows-icons/ft8call.ico
|
||||
-alpha remove ../icons/windows-icons/js8call.ico
|
||||
rm /tmp/image-0.png /tmp/image-1.png
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/ft8call.ico
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/js8call.ico
|
||||
#
|
||||
#inkscape -z -e /dev/stdout -w 150 -h 57 -b white installer_logo.svg | tail -n +4 | \
|
||||
# convert png:- -resize 150x57 +matte BMP3:../icons/windows-icons/installer_logo.bmp
|
||||
@@ -37,18 +37,18 @@ identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/windows-icons/instal
|
||||
#
|
||||
# Mac
|
||||
#
|
||||
mkdir -p ../icons/Darwin/FT8Call.iconset
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_16x16.png -w 16 -h 16 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_16x16@2x.png -w 32 -h 32 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_32x32.png -w 32 -h 32 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_32x32@2x.png -w 64 -h 64 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_128x128.png -w 128 -h 128 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_128x128@2x.png -w 256 -h 256 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_256x256.png -w 256 -h 256 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_256x256@2x.png -w 512 -h 512 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_512x512.png -w 512 -h 512 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_512x512@2x.png -w 1024 -h 1024 icon_1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Darwin/FT8Call.iconset/*
|
||||
mkdir -p ../icons/Darwin/JS8Call.iconset
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_16x16.png -w 16 -h 16 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_16x16@2x.png -w 32 -h 32 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_32x32.png -w 32 -h 32 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_32x32@2x.png -w 64 -h 64 icon_128.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_128x128.png -w 128 -h 128 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_128x128@2x.png -w 256 -h 256 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_256x256.png -w 256 -h 256 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_256x256@2x.png -w 512 -h 512 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_512x512.png -w 512 -h 512 icon_1024.svg
|
||||
inkscape -z -e ../icons/Darwin/JS8Call.iconset/icon_512x512@2x.png -w 1024 -h 1024 icon_1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Darwin/JS8Call.iconset/*
|
||||
# generic globe iconset for utilities
|
||||
mkdir -p ../icons/Darwin/wsjt.iconset
|
||||
inkscape -z -e ../icons/Darwin/wsjt.iconset/icon_16x16.png -w 16 -h 16 icon_128.svg
|
||||
@@ -69,5 +69,5 @@ identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' "../icons/Darwin/DragNDrop Ba
|
||||
#
|
||||
# KDE & Gnome
|
||||
#
|
||||
inkscape -z -e ../icons/Unix/ft8call_icon.png -w 128 -h 128 icon_1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Unix/ft8call_icon.png
|
||||
inkscape -z -e ../icons/Unix/js8call_icon.png -w 128 -h 128 icon_1024.svg
|
||||
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Unix/js8call_icon.png
|
||||
|
||||
@@ -22,8 +22,9 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
|
||||
, message_ {string_.mid (column_qsoText + padding_).trimmed ()}
|
||||
, is_standard_ {false}
|
||||
, frameType_(Varicode::FrameUnknown)
|
||||
, isBeacon_(false)
|
||||
, isHeartbeat_(false)
|
||||
, isAlt_(false)
|
||||
, bits_{0}
|
||||
{
|
||||
if(message_.length() >= 1) {
|
||||
message_ = message_.left (21).remove (QRegularExpression {"[<>]"});
|
||||
@@ -51,21 +52,24 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
|
||||
grid_c_string += QByteArray {6 - grid_c_string.size (), ' '};
|
||||
is_standard_ = stdmsg_ (message_c_string.constData (), contest_mode_, grid_c_string.constData (), 22, 6);
|
||||
|
||||
// We're only going to unpack standard messages for CQs && beacons...
|
||||
// We're only going to unpack standard messages for CQs && pings...
|
||||
// TODO: jsherer - this is a hack for now...
|
||||
if(is_standard_){
|
||||
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
|
||||
}
|
||||
}
|
||||
|
||||
bits_ = bits();
|
||||
|
||||
tryUnpack();
|
||||
}
|
||||
|
||||
DecodedText::DecodedText (QString const& ft8callmessage):
|
||||
DecodedText::DecodedText (QString const& js8callmessage, int bits):
|
||||
frameType_(Varicode::FrameUnknown),
|
||||
message_(ft8callmessage),
|
||||
isBeacon_(false),
|
||||
isAlt_(false)
|
||||
message_(js8callmessage),
|
||||
isHeartbeat_(false),
|
||||
isAlt_(false),
|
||||
bits_(bits)
|
||||
{
|
||||
is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
|
||||
|
||||
@@ -80,7 +84,11 @@ bool DecodedText::tryUnpack(){
|
||||
|
||||
bool unpacked = false;
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackBeacon();
|
||||
unpacked = tryUnpackData();
|
||||
}
|
||||
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackHeartbeat();
|
||||
}
|
||||
|
||||
if(!unpacked){
|
||||
@@ -91,14 +99,10 @@ bool DecodedText::tryUnpack(){
|
||||
unpacked = tryUnpackDirected();
|
||||
}
|
||||
|
||||
if(!unpacked){
|
||||
unpacked = tryUnpackData();
|
||||
}
|
||||
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
bool DecodedText::tryUnpackBeacon(){
|
||||
bool DecodedText::tryUnpackHeartbeat(){
|
||||
QString m = message().trimmed();
|
||||
|
||||
// directed calls will always be 12+ chars and contain no spaces.
|
||||
@@ -109,17 +113,17 @@ bool DecodedText::tryUnpackBeacon(){
|
||||
bool isAlt = false;
|
||||
quint8 type = Varicode::FrameUnknown;
|
||||
quint8 bits3 = 0;
|
||||
QStringList parts = Varicode::unpackBeaconMessage(m, &type, &isAlt, &bits3);
|
||||
QStringList parts = Varicode::unpackHeartbeatMessage(m, &type, &isAlt, &bits3);
|
||||
|
||||
if(parts.isEmpty() || parts.length() < 2){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Beacon Alt Type
|
||||
// Heartbeat Alt Type
|
||||
// ---------------
|
||||
// 1 0 BCN
|
||||
// 1 1 CQ
|
||||
isBeacon_ = true;
|
||||
isHeartbeat_ = true;
|
||||
isAlt_ = isAlt;
|
||||
extra_ = parts.length() < 3 ? "" : parts.at(2);
|
||||
|
||||
@@ -131,7 +135,7 @@ bool DecodedText::tryUnpackBeacon(){
|
||||
cmp.append(parts.at(1));
|
||||
}
|
||||
compound_ = cmp.join("/");
|
||||
message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "BEACON").arg(extra_);
|
||||
message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "HEARTBEAT").arg(extra_);
|
||||
frameType_ = type;
|
||||
return true;
|
||||
}
|
||||
@@ -197,6 +201,8 @@ bool DecodedText::tryUnpackDirected(){
|
||||
message_ = QString(parts.join(""));
|
||||
}
|
||||
|
||||
message_ = message_.replace("APRS: ", "APRS:");
|
||||
|
||||
directed_ = parts;
|
||||
frameType_ = type;
|
||||
return true;
|
||||
@@ -210,6 +216,10 @@ bool DecodedText::tryUnpackData(){
|
||||
return false;
|
||||
}
|
||||
|
||||
if((bits_ & Varicode::JS8CallData) != Varicode::JS8CallData){
|
||||
return false;
|
||||
}
|
||||
|
||||
quint8 type = Varicode::FrameUnknown;
|
||||
QString data = Varicode::unpackDataMessage(m, &type);
|
||||
|
||||
|
||||
@@ -31,10 +31,10 @@ class DecodedText
|
||||
{
|
||||
public:
|
||||
explicit DecodedText (QString const& message, bool, QString const& my_grid);
|
||||
explicit DecodedText (QString const& ft8callmessage);
|
||||
explicit DecodedText (QString const& js8callmessage, int bits);
|
||||
|
||||
bool tryUnpack();
|
||||
bool tryUnpackBeacon();
|
||||
bool tryUnpackHeartbeat();
|
||||
bool tryUnpackCompound();
|
||||
bool tryUnpackDirected();
|
||||
bool tryUnpackData();
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
QString compoundCall() const { return compound_; }
|
||||
bool isCompound() const { return !compound_.isEmpty(); }
|
||||
|
||||
bool isBeacon() const { return isBeacon_; }
|
||||
bool isHeartbeat() const { return isHeartbeat_; }
|
||||
bool isAlt() const { return isAlt_; }
|
||||
|
||||
QStringList directedMessage() const { return directed_; }
|
||||
@@ -99,7 +99,7 @@ private:
|
||||
column_qsoText = 22 };
|
||||
|
||||
quint8 frameType_;
|
||||
bool isBeacon_;
|
||||
bool isHeartbeat_;
|
||||
bool isAlt_;
|
||||
QString compound_;
|
||||
QString extra_;
|
||||
@@ -109,6 +109,7 @@ private:
|
||||
bool contest_mode_;
|
||||
QString message_;
|
||||
bool is_standard_;
|
||||
int bits_;
|
||||
};
|
||||
|
||||
#endif // DECODEDTEXT_H
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
|
||||
#include "DriftingDateTime.h"
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
|
||||
#include "moc_displaytext.cpp"
|
||||
@@ -288,13 +290,13 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
|
||||
t2.sprintf("%4d",txFreq);
|
||||
QString t;
|
||||
if(bFastMode or modeTx=="FT8") {
|
||||
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||
t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||
" Tx " + t2 + t1 + text;
|
||||
} else if(modeTx.mid(0,6)=="FT8fox") {
|
||||
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||
t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||
" Tx" + modeTx.mid(7) + " " + text;
|
||||
} else {
|
||||
t = QDateTime::currentDateTimeUtc().toString("hhmm") + \
|
||||
t = DriftingDateTime::currentDateTimeUtc().toString("hhmm") + \
|
||||
" Tx " + t2 + t1 + text;
|
||||
}
|
||||
appendText (t, color_ReceivedMsg);
|
||||
@@ -302,7 +304,7 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
|
||||
|
||||
void DisplayText::displayQSY(QString text)
|
||||
{
|
||||
QString t = QDateTime::currentDateTimeUtc().toString("hhmmss") + " " + text;
|
||||
QString t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + " " + text;
|
||||
appendText (t, "hotpink");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
set (LANGUAGES
|
||||
en
|
||||
)
|
||||
|
||||
set (common_SRCS
|
||||
common/communication.adoc
|
||||
common/license.adoc
|
||||
common/links.adoc
|
||||
)
|
||||
|
||||
set (UG_SRCS
|
||||
docinfo.html
|
||||
docinfo.xml
|
||||
acknowledgements.adoc
|
||||
astro_data.adoc
|
||||
config-details.adoc
|
||||
controls-functions-center.adoc
|
||||
controls-functions-left.adoc
|
||||
controls-functions-main-window.adoc
|
||||
controls-functions-menus.adoc
|
||||
controls-functions-messages.adoc
|
||||
controls-functions-status-bar.adoc
|
||||
controls-functions-wide-graph.adoc
|
||||
cooperating-programs.adoc
|
||||
decoder_notes.adoc
|
||||
faq.adoc
|
||||
font-sizes.adoc
|
||||
install-from-source.adoc
|
||||
install-linux.adoc
|
||||
install-mac.adoc
|
||||
install-windows.adoc
|
||||
introduction.adoc
|
||||
measurement_tools.adoc
|
||||
protocols.adoc
|
||||
logging.adoc
|
||||
make-qso.adoc
|
||||
measurement_tools.adoc
|
||||
new_features.adoc
|
||||
platform-dependencies.adoc
|
||||
protocols.adoc
|
||||
settings-advanced.adoc
|
||||
settings-audio.adoc
|
||||
settings-colors.adoc
|
||||
settings-frequencies.adoc
|
||||
settings-general.adoc
|
||||
settings-radio.adoc
|
||||
settings-reporting.adoc
|
||||
settings-txmacros.adoc
|
||||
support.adoc
|
||||
system-requirements.adoc
|
||||
transceiver-setup.adoc
|
||||
tutorial-example1.adoc
|
||||
tutorial-example2.adoc
|
||||
tutorial-example3.adoc
|
||||
tutorial-main-window.adoc
|
||||
tutorial-wide-graph-settings.adoc
|
||||
utilities.adoc
|
||||
vhf-features.adoc
|
||||
wsjtx-main.adoc
|
||||
wspr.adoc
|
||||
)
|
||||
|
||||
set (UG_IMGS
|
||||
images/130610_2343-wav-80.png
|
||||
images/170709_135615.wav.png
|
||||
images/AstroData_2.png
|
||||
images/Astronomical_data.png
|
||||
images/auto-seq.png
|
||||
images/band-settings.png
|
||||
images/colors.png
|
||||
images/config-menu.png
|
||||
images/decode-menu.png
|
||||
images/decodes.png
|
||||
images/download_samples.png
|
||||
images/echo_144.png
|
||||
images/file-menu.png
|
||||
images/FreqCal.png
|
||||
images/FreqCal_Graph.png
|
||||
images/FreqCal_Results.png
|
||||
images/freemsg.png
|
||||
images/ft8_decodes.png
|
||||
images/help-menu.png
|
||||
images/JT4F.png
|
||||
images/JT65B.png
|
||||
images/keyboard-shortcuts.png
|
||||
images/MSK144.png
|
||||
images/QRA64.png
|
||||
images/WSPR_WideGraphControls.png
|
||||
images/WSPR_1a.png
|
||||
images/WSPR_2.png
|
||||
images/jtalert.png
|
||||
images/keyboard-shortcuts.png
|
||||
images/log-qso.png
|
||||
images/MacAppMenu.png
|
||||
images/main-ui.png
|
||||
images/main-ui-controls.png
|
||||
images/misc-controls-center.png
|
||||
images/misc-main-ui.png
|
||||
images/mode-menu.png
|
||||
images/new-msg-box.png
|
||||
images/psk-reporter.png
|
||||
images/r3666-config-screen-80.png
|
||||
images/r3666-main-ui-80.png
|
||||
images/r4148-txmac-ui.png
|
||||
images/RadioTab.png
|
||||
images/reporting.png
|
||||
images/save-menu.png
|
||||
images/settings-advanced.png
|
||||
images/settings-audio.png
|
||||
images/settings-frequencies.png
|
||||
images/settings-general.png
|
||||
images/setup-menu.png
|
||||
images/special-mouse-commands.png
|
||||
images/status-bar-a.png
|
||||
images/tools-menu.png
|
||||
images/traditional-msg-box.png
|
||||
images/tx-macros.png
|
||||
images/VHF_controls.png
|
||||
images/view-menu.png
|
||||
images/wide-graph-controls.png
|
||||
|
||||
)
|
||||
|
||||
find_program (ASCIIDOCTOR_EXECUTABLE NAMES asciidoctor)
|
||||
if (NOT ASCIIDOCTOR_EXECUTABLE)
|
||||
message (FATAL_ERROR "asciidoctor is required to build the documentation
|
||||
|
||||
Building the documenation may optionally be turned off by setting the CMake
|
||||
option WSJT_GENERATE_DOCS to OFF.")
|
||||
endif (NOT ASCIIDOCTOR_EXECUTABLE)
|
||||
|
||||
find_program (FOPUB_EXECUTABLE NAMES fopub)
|
||||
|
||||
include (CMakeParseArguments)
|
||||
|
||||
# generate a document from asciidoc text files(s)
|
||||
#
|
||||
# HTML - generate an HTML document
|
||||
# PDF - generate a PDF document
|
||||
# SOURCE - top level asciidoc file
|
||||
# ASCIIDOCTOR_OPTIONS - asciidoctor command options
|
||||
# DEPENDS - dependent files
|
||||
function (document)
|
||||
cmake_parse_arguments (_args "HTML" "SOURCE;LANG;OUTPUT" "ASCIIDOCTOR_OPTIONS;PDF;DEPENDS" ${ARGN})
|
||||
get_filename_component (_source_path ${_args_SOURCE} PATH)
|
||||
get_filename_component (_source_name ${_args_SOURCE} NAME)
|
||||
get_filename_component (_output_name_we ${_args_SOURCE} NAME_WE)
|
||||
|
||||
# HTML
|
||||
if (${_args_HTML})
|
||||
set (_html_file ${CMAKE_CURRENT_BINARY_DIR}/${_output_name_we}_${lang}.html)
|
||||
add_custom_command (
|
||||
OUTPUT ${_html_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_source_path}/${lang}
|
||||
COMMAND ${ASCIIDOCTOR_EXECUTABLE} ${_args_ASCIIDOCTOR_OPTIONS}
|
||||
-b html5
|
||||
-a VERSION_MAJOR=${WSJTX_VERSION_MAJOR}
|
||||
-a VERSION_MINOR=${WSJTX_VERSION_MINOR}
|
||||
-a VERSION_PATCH=${WSJTX_VERSION_PATCH}
|
||||
-a VERSION=${wsjtx_VERSION}
|
||||
--out-file=${_html_file} ${_source_name}
|
||||
DEPENDS ${_args_DEPENDS}
|
||||
COMMENT "Generating ${_html_file}"
|
||||
)
|
||||
list (APPEND _output_files ${_html_file})
|
||||
endif (${_args_HTML})
|
||||
|
||||
# PDF
|
||||
if (_args_PDF AND EXISTS ${FOPUB_EXECUTABLE})
|
||||
set (_docbook_file ${CMAKE_CURRENT_BINARY_DIR}/${_output_name_we}_${lang}.xml)
|
||||
set (_pdf_file_we ${CMAKE_CURRENT_BINARY_DIR}/${_output_name_we}_${lang})
|
||||
if (${lang} MATCHES "^(en|es|fr)$") # en-us, fr-ca and es-{mx,co} use US-Letter or equivalent
|
||||
set (_usl_commands
|
||||
COMMAND ${FOPUB_EXECUTABLE} ARGS ${_docbook_file} ${_args_PDF} -param paper.type USLetter
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E rename ${_pdf_file_we}.pdf '${_pdf_file_we} \(USLetter\).pdf'
|
||||
)
|
||||
list (APPEND _output_files "${_pdf_file_we} (USLetter).pdf")
|
||||
endif ()
|
||||
list (APPEND _output_files "${_pdf_file_we}.pdf")
|
||||
add_custom_command (
|
||||
OUTPUT ${_docbook_file} ${_output_files}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${_source_path}/${lang}
|
||||
COMMAND ${ASCIIDOCTOR_EXECUTABLE} ARGS ${_args_ASCIIDOCTOR_OPTIONS}
|
||||
-b docbook
|
||||
-a data-uri!
|
||||
-a VERSION_MAJOR=${WSJTX_VERSION_MAJOR}
|
||||
-a VERSION_MINOR=${WSJTX_VERSION_MINOR}
|
||||
-a VERSION_PATCH=${WSJTX_VERSION_PATCH}
|
||||
-a VERSION=${wsjtx_VERSION}
|
||||
-D ${CMAKE_CURRENT_BINARY_DIR}
|
||||
-o ${_docbook_file} ${_source_name}
|
||||
${_usl_commands}
|
||||
COMMAND ${FOPUB_EXECUTABLE} ARGS ${_docbook_file} ${_args_PDF} -param paper.type A4
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E rename ${_pdf_file_we}.pdf '${_pdf_file_we}.pdf'
|
||||
DEPENDS ${_args_DEPENDS}
|
||||
COMMENT "Generating ${_output_files}"
|
||||
)
|
||||
endif (_args_PDF AND EXISTS ${FOPUB_EXECUTABLE})
|
||||
|
||||
set (${_args_OUTPUT} ${_output_files} PARENT_SCOPE)
|
||||
endfunction (document)
|
||||
|
||||
set (htmls)
|
||||
set (pdfs)
|
||||
foreach (lang ${LANGUAGES})
|
||||
set (_sources)
|
||||
foreach (_src ${UG_SRCS} ${UG_IMGS})
|
||||
list (APPEND _sources "user_guide/${lang}/${_src}")
|
||||
endforeach ()
|
||||
document(
|
||||
HTML
|
||||
SOURCE user_guide/wsjtx-main.adoc
|
||||
LANG "${lang}"
|
||||
OUTPUT html
|
||||
ASCIIDOCTOR_OPTIONS -d book -a data-uri -a toc=left -a max-width=1024px
|
||||
DEPENDS ${common_SRCS} ${_sources}
|
||||
)
|
||||
document(
|
||||
PDF -param body.font.master 11 -param body.font.family "'Noto Sans, Helvetica, sans-serif'" -param title.font.family "'Noto Serif, Times New Roman, serif'" -param page.margin.inner 1cm -param page.margin.outer 1cm -param page.margin.top 0.75cm -param page.margin.bottom 0.5cm -param generate.toc 0
|
||||
SOURCE user_guide/wsjtx-main.adoc
|
||||
LANG "${lang}"
|
||||
OUTPUT pdf
|
||||
ASCIIDOCTOR_OPTIONS -d book
|
||||
DEPENDS ${common_SRCS} ${_sources}
|
||||
)
|
||||
list (APPEND htmls "${html}")
|
||||
list (APPEND pdfs "${pdf}")
|
||||
endforeach ()
|
||||
|
||||
add_custom_target (docs ALL DEPENDS ${htmls} ${pdfs})
|
||||
|
||||
foreach (_html ${htmls})
|
||||
get_filename_component (_path ${_html} PATH)
|
||||
get_filename_component (_nwe ${_html} NAME_WE)
|
||||
get_filename_component (_ext ${_html} EXT)
|
||||
string (REGEX REPLACE "_en$" "" _nwe ${_nwe})
|
||||
install (FILES
|
||||
${_html}
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||
RENAME ${_nwe}-${wsjtx_VERSION}${_ext}
|
||||
#COMPONENT runtime
|
||||
)
|
||||
endforeach ()
|
||||
@@ -1,40 +0,0 @@
|
||||
This folder contains the sources of WSJT-X documentation. To build
|
||||
these you will need the asciidoctor tool installed.
|
||||
|
||||
If you do not wish to build the documentation, it is possible to skip
|
||||
this directory in the WSJT-X build by setting the CMake option
|
||||
WSJT_GENERATE_DOCS to OFF in your build tree.
|
||||
|
||||
|
||||
On Linux
|
||||
========
|
||||
|
||||
You will probably have these installed already if you are building the
|
||||
WSJT-X manpages, if you are not you will just need to install
|
||||
asciidoc:
|
||||
|
||||
sudo apt-get install asciidoctor
|
||||
|
||||
or
|
||||
|
||||
sudo yum install asciidoctor
|
||||
|
||||
or whatever your distribution and package management requires.
|
||||
|
||||
|
||||
On Mac OS X
|
||||
===========
|
||||
|
||||
I recommend MacPorts:
|
||||
|
||||
sudo port install rb-rubygems
|
||||
sudo gem install asciidoctor
|
||||
|
||||
|
||||
On Windows
|
||||
==========
|
||||
|
||||
The asciidoctor tool is a Ruby script so you will need to install a
|
||||
version of Ruby. The gem tool is a good way to install asciidoctor:
|
||||
|
||||
gem install asciidoctor
|
||||
@@ -1,17 +0,0 @@
|
||||
The following are established communication channels for the WSJT Group.
|
||||
For updates and other information on the continuing development of _WSJT_,
|
||||
_MAP65_, _WSPR_, _WSPR-X_, and _WSJT-X_ you are invited to subscribe to the
|
||||
{dev_mail_list}.
|
||||
|
||||
.General Information
|
||||
* Main Site: {homepage}
|
||||
* Development Site: {projsummary}
|
||||
|
||||
.Development Related
|
||||
* Project Manager, email: {joe_taylor}
|
||||
* Development Email: {devmail1}
|
||||
* Development Mailing List (join): {dev_mail_list}
|
||||
* Repository Updates, (join): {dev_mail_svn}
|
||||
|
||||
.Community Related
|
||||
* Discussion Board: {wsjt_yahoo_group}
|
||||
@@ -1,29 +0,0 @@
|
||||
include::./links.adoc[]
|
||||
_{prog}_ is free software: you may redistribute 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.
|
||||
|
||||
_{prog}_ 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 documentation. If not, see {gnu_gpl}.
|
||||
|
||||
Except where otherwise noted, all algorithms, protocol designs, source
|
||||
code, and supporting files contained in the _{prog}_ package are the
|
||||
intellectual property of the program's authors. The authors assert
|
||||
*Copyright ownership* of this material, whether or not such copyright
|
||||
notice appears in each individual file. Others who make fair use of
|
||||
our work under terms of the GNU General Public License must display
|
||||
the following copyright notice prominently:
|
||||
|
||||
*The algorithms, source code, look-and-feel of _{prog}_ and related
|
||||
programs, and protocol specifications for the modes FSK441, FT8, JT4,
|
||||
JT6M, JT9, JT65, JTMS, QRA64, ISCAT, and MSK144 are Copyright (C)
|
||||
2001-2018 by one or more of the following authors: Joseph Taylor,
|
||||
K1JT; Bill Somerville, G4WJS; Steven Franke, K9AN; Nico Palermo,
|
||||
IV3NWV; Greg Beam, KI7MT; Michael Black, W9MDB; Edson Pereira, PY2SDR;
|
||||
Philip Karn, KA9Q; and other members of the WSJT Development Group.*
|
||||
@@ -1,128 +0,0 @@
|
||||
////
|
||||
Link file to hold all links
|
||||
File Location: ./doc/common/links.adoc
|
||||
Usage example: include::../common/links.adoc[]
|
||||
Syntax: [link-id] [link] [displayed text]
|
||||
|
||||
Example:
|
||||
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter]
|
||||
|
||||
[link-id] = :pskreporter:
|
||||
[link] http://pskreporter.info/pskmap.html
|
||||
[displayed text] PSK Reporter
|
||||
|
||||
Perform searches from the doc root directory: doc
|
||||
Search: grep -rl --exclude-dir="*\.svn" {pskreporter} .
|
||||
grep -rl --exclude-dir="*\.svn" PSK Reporter .
|
||||
grep -rl --exclude-dir="*\.svn" {devsvn} .
|
||||
grep -rl --exclude-dir="*\.svn" {kvasd} .
|
||||
grep -rl --exclude-dir="*\.svn" {ntpsetup} .
|
||||
|
||||
Include links.adoc: grep -rl --exclude-dir="*\.svn" pskreporter .
|
||||
Exclude links.adoc: grep -rl --exclude-dir="*\.svn" {pskreporter} .
|
||||
|
||||
Note(s):
|
||||
a). Don't forget a space then "." at the end.
|
||||
|
||||
b). To include links.adoc file itself, remove the brackets {} from
|
||||
the pattern search:
|
||||
|
||||
c). Look at each of the files listed to ensure the [displayed text]
|
||||
still makes sense in the caption. If not, just add another link. Be
|
||||
aware of the translators requirements by trying to keep the displayed
|
||||
text language agnostic or neutral e.g. use a proper noun like a
|
||||
persons name or a document title in the language of the linked
|
||||
document. Do not use plain English generic words for link text link
|
||||
"here".
|
||||
|
||||
d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
|
||||
|
||||
////
|
||||
|
||||
// General URL's
|
||||
//:launchpadac6sl: https://launchpad.net/~jnogatch/+archive/wsjtx[WSJT-X Linux Packages]
|
||||
:alarmejt: http://f5jmh.free.fr/index.php?page=english[AlarmeJT]
|
||||
:asciidoc_cheatsheet: http://powerman.name/doc/asciidoc[AsciiDoc Cheatsheet]
|
||||
:asciidoc_help: http://www.methods.co.nz/asciidoc/userguide.html[AsciiDoc User Guide]
|
||||
:asciidoc_questions: http://www.methods.co.nz/asciidoc/faq.html[AsciiDoc FAQ]
|
||||
:asciidoc_syntax: http://xpt.sourceforge.net/techdocs/nix/tool/asciidoc-syn/ascs01-AsciiDocMarkupSyntaxQuickSummary/single/[AsciiDoc Syntax]
|
||||
:asciidoctor_style: http://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Styles Guide]
|
||||
:asciidoctor_syntax: http://asciidoctor.org/docs/asciidoc-writers-guide/#delimited-blocks[AsciiDoctor Syntax Guide]
|
||||
:cc_by_sa: http://creativecommons.org/licenses/by-sa/3.0/[Commons Attribution-ShareAlike 3.0 Unported License]
|
||||
:debian32: http://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_i386.deb[wsjtx_{VERSION}_i386.deb]
|
||||
:debian64: http://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_amd64.deb[wsjtx_{VERSION}_amd64.deb]
|
||||
:raspbian: http://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_armhf.deb[wsjtx_{VERSION}_armhf.deb]
|
||||
:debian: http://www.debian.org/[Debian]
|
||||
:dev_guide: http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide]
|
||||
:devsvn1: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN]
|
||||
:devsvn: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[SourceForge]
|
||||
:dimension4: http://www.thinkman.com/dimension4/[Thinking Man Software]
|
||||
:download: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[Download Page]
|
||||
:dxatlas: http://www.dxatlas.com/[Afreet Software, Inc.]
|
||||
:dxlcommander: http://www.dxlabsuite.com/commander/[Commander]
|
||||
:dxlsuite: http://www.dxlabsuite.com/[DX Lab Suite]
|
||||
:fedora32: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-i686.rpm[wsjtx-{VERSION}-i686.rpm]
|
||||
:fedora64: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-x86_64.rpm[wsjtx-{VERSION}-x86_64.rpm]
|
||||
:fmt_arrl: http://www.arrl.org/frequency-measuring-test[ARRL FMT Info]
|
||||
:fmt_group: https://groups.yahoo.com/neo/groups/FMT-nuts/info[FMT Group]
|
||||
:fmt_k5cm: http://www.k5cm.com/[FMT Event Info]
|
||||
:fmt_wspr: http://www.physics.princeton.edu/pulsar/K1JT/FMT_User.pdf[Accurate Frequency Measurements with your WSPR Setup]
|
||||
:ft8_tips: http://www.physics.princeton.edu/pulsar/K1JT/FT8_Operating_Tips.pdf[here]
|
||||
:ft8_DXped: http://physics.princeton.edu/pulsar/k1jt/FT8_DXpedition_Mode.pdf[FT8 DXpedition Mode]
|
||||
:gnu_gpl: http://www.gnu.org/licenses/gpl-3.0.txt[GNU General Public License]
|
||||
:homepage: http://physics.princeton.edu/pulsar/K1JT/[WSJT Home Page]
|
||||
:hrd: http://www.hrdsoftwarellc.com/[Ham Radio Deluxe]
|
||||
:jt4eme: http://physics.princeton.edu/pulsar/K1JT/WSJT-X_1.6.0_for_JT4_v7.pdf[Using WSJT-X for JT4 EME Operation]
|
||||
:jt65protocol: http://physics.princeton.edu/pulsar/K1JT/JT65.pdf[QEX]
|
||||
:jtalert: http://hamapps.com/[JT-Alert]
|
||||
:launchpadki7mt: https://launchpad.net/~ki7mt[KI7MT PPA's]
|
||||
:log4om: http://www.log4om.com[Log4OM]
|
||||
:lunarEchoes: http://physics.princeton.edu/pulsar/K1JT/LunarEchoes_QEX.pdf[QEX]
|
||||
:msk144: http://physics.princeton.edu/pulsar/k1jt/MSK144_Protocol_QEX.pdf[QEX]
|
||||
:msys_url: http://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/[MSYS Download]
|
||||
:ntpsetup: http://www.satsignal.eu/ntp/setup.html[Network Time Protocol Setup]
|
||||
:osx_instructions: http://physics.princeton.edu/pulsar/K1JT/OSX_Readme[Mac OS X Install Instructions]
|
||||
:ppa: http://en.wikipedia.org/wiki/Personal_Package_Archive[PPA]
|
||||
:projsummary: http://sourceforge.net/projects/wsjt/[Project Summary]
|
||||
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter]
|
||||
:sourceforge: https://sourceforge.net/user/registration[SourceForge]
|
||||
:sourceforge-jtsdk: https://sourceforge.net/projects/jtsdk[SourceForge JTSDK]
|
||||
:ubuntu_sdk: https://launchpad.net/~ubuntu-sdk-team/+archive/ppa[Ubuntu SDK Notice]
|
||||
:wsjt_yahoo_group: https://groups.yahoo.com/neo/groups/wsjtgroup/info[WSJT Group]
|
||||
:wsjtx: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X]
|
||||
:wspr0_guide: http://www.physics.princeton.edu/pulsar/K1JT/WSPR0_Instructions.TXT[WSPR0 Guide]
|
||||
:wspr: http://physics.princeton.edu/pulsar/K1JT/wspr.html[WSPR Home Page]
|
||||
:wsprnet: http://wsprnet.org/drupal/[WSPRnet]
|
||||
:wsprnet_activity: http://wsprnet.org/drupal/wsprnet/activity[WSPRnet Activity page]
|
||||
|
||||
// Download Links
|
||||
:cty_dat: http://www.country-files.com/cty/[Amateur Radio Country Files]
|
||||
:jtbridge: http://jt-bridge.eller.nu/[JT-Bridge]
|
||||
:jtsdk_doc: http://physics.princeton.edu/pulsar/K1JT/JTSDK-DOC.exe[Download]
|
||||
:jtsdk_installer: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/JTSDK-2.0.0-B2-Win32.exe/download[Download]
|
||||
:jtsdk_omnirig: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/OmniRig.zip/download[Download]
|
||||
:jtsdk_py: http://physics.princeton.edu/pulsar/K1JT/JTSDK-PY.exe[Download]
|
||||
:jtsdk_qt: http://physics.princeton.edu/pulsar/K1JT/JTSDK-QT.exe[Download]
|
||||
:jtsdk_vcredist: http://sourceforge.net/projects/jtsdk/files/win32/2.0.0/base/contrib/vcredist_x86.exe/download[Download]
|
||||
:nh6z: http://www.nh6z.net/Amatuer_Radio_Station_NH6Z/Other_Peoples_Software.html[here]
|
||||
:omnirig: http://www.dxatlas.com/OmniRig/Files/OmniRig.zip[Omni-Rig]
|
||||
:osx: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-Darwin.dmg[wsjtx-{VERSION}-Darwin.dmg]
|
||||
:QRA64_EME: http://physics.princeton.edu/pulsar/K1JT/QRA64_EME.pdf[QRA64 for microwave EME]
|
||||
:svn: http://subversion.apache.org/packages.html#windows[Subversion]
|
||||
:win32: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win32.exe[wsjtx-{VERSION}-win32.exe]
|
||||
:wsjt-devel: https://lists.sourceforge.net/lists/listinfo/wsjt-devel[here]
|
||||
:wsjt_svn: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[WSJT Source Repository]
|
||||
:wspr_code: http://physics.princeton.edu/pulsar/K1JT/WSPRcode.exe[WSPRcode.exe]
|
||||
:wspr_svn: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/branches/wspr/[WSPR Source Repository]
|
||||
|
||||
// MAIL-TO links
|
||||
:alex_efros: mailto:powerman@powerman.name[Alex Efros]
|
||||
:bill_somerville: mailto:g4wjs -at- c l a s s d e s i g n -dot- com [G4WJS]
|
||||
:dev_mail_list: http://sourceforge.net/mailarchive/forum.php?forum_name=wsjt-devel[WSJT Developers Email List]
|
||||
:dev_mail_svn: https://sourceforge.net/auth/subscriptions/[WSJT SVN Archives]
|
||||
:devmail: mailto:wsjt-devel@lists.sourceforge.net[wsjt-devel@lists.sourceforge.net]
|
||||
:devmail1: mailto:wsjt-devel@lists.sourceforge.net[Post Message]
|
||||
:wsjtgroup_mail: mailto:wsjtgroup@yahoogroups.com[Post Message]
|
||||
:greg_beam: mailto:ki7mt@yahoo.com[KI7MT]
|
||||
:joe_taylor: mailto:joe@princeton.edu[K1JT]
|
||||
:stuart_rackman: mailto:srackham@gmail.com[Stuart Rackham]
|
||||
@@ -1,29 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
The _WSJT_ project was started in 2001. Since 2005 it has been an
|
||||
Open Source project, and it now includes programs _WSJT_, _MAP65_,
|
||||
_WSPR_, _WSJT-X_, and _WSPR-X_. All code is licensed under the
|
||||
GNU Public License (GPL). Many users of these programs, too numerous
|
||||
to mention here individually, have contributed suggestions and advice
|
||||
that have greatly aided the development of _WSJT_ and its sister
|
||||
programs.
|
||||
|
||||
For _WSJT-X_ in particular, we acknowledge contributions from *AC6SL,
|
||||
AE4JY, DJ0OT, G3WDG, G4KLA, G4WJS, IV3NWV, IW3RAB, K3WYC, K9AN,
|
||||
KA6MAL, KA9Q, KB1ZMX, KD6EKQ, KI7MT, KK1D, ND0B, PY2SDR, VE1SKY, VK3ACF,
|
||||
VK4BDJ, VK7MO, W4TI, W4TV, and W9MDB*. Each of these amateurs has helped to
|
||||
bring the program’s design, code, testing, and/or documentation to
|
||||
its present state.
|
||||
|
||||
Most of the color palettes for the _WSJT-X_ waterfall were copied from
|
||||
the excellent, well documented, open-source program _fldigi_, by *W1HKJ*
|
||||
and friends.
|
||||
|
||||
We use development tools and libraries from many sources. We
|
||||
particularly wish to acknowledge importance of the GNU Compiler
|
||||
Collection from the Free Software Foundation, the "clang" compiler
|
||||
from LLVM at the University of Illinois, and the Qt Project from Digia
|
||||
PLC. Other important resources include the FFTW library by Matteo
|
||||
Frigo and Steven G. Johnson; SLALIB, the Positional Astronomy Library
|
||||
by P. T. Wallace; and a high-precision planetary ephemeris and
|
||||
associated software from NASA's Jet Propulsion Laboratory.
|
||||
@@ -1,57 +0,0 @@
|
||||
A text box entitled Astronomical Data provides information needed for
|
||||
tracking the sun or moon, compensating for EME Doppler shift, and
|
||||
estimating EME Doppler spread and path degradation. Toggle the
|
||||
*Astronomical data* on the *View* menu to display or hide this window.
|
||||
|
||||
image::AstroData_2.png[align="center",alt="Astronomical Data"]
|
||||
|
||||
Available information includes the current UTC *Date* and time; *Az*
|
||||
and *El*, azimuth and elevation of the moon at your own location, in
|
||||
degrees; *SelfDop*, *Width*, and *Delay*, the Doppler shift, full
|
||||
limb-to-limb Doppler spread in Hz, and delay of your own EME echoes in
|
||||
seconds; and *DxAz* and *DxEl*, *DxDop*, and *DxWid*, corresponding
|
||||
parameters for a station located at the *DX Grid* entered on the main
|
||||
window. These numbers are followed by *Dec*, the declination of the
|
||||
moon; *SunAz* and *SunEl*, the azimuth and elevation of the Sun;
|
||||
*Freq*, your stated operating frequency in MHz; *Tsky*, the estimated
|
||||
sky background temperature in the direction of the moon, scaled to the
|
||||
operating frequency; *Dpol*, the spatial polarization offset in
|
||||
degrees; *MNR*, the maximum non-reciprocity of the EME path in dB,
|
||||
owing to a combination of Faraday rotation and spatial polarization;
|
||||
and finally *Dgrd*, an estimate of the signal degradation in dB,
|
||||
relative to the best possible time with the moon at perigee in a cold
|
||||
part of the sky.
|
||||
|
||||
On the higher microwave bands, where Faraday rotation is minimal and
|
||||
linear polarization is often used, spatial offset will reduce signal
|
||||
levels. Some stations have implemented mechanical polarisation
|
||||
adjustment to overcome this loss, and the amount of rotation needed is
|
||||
predicted in real time by the value of *Dpol*. Positive Dpol means
|
||||
that the antenna should be rotated in a clockwise direction looking
|
||||
from behind the antenna towards the moon. For a dish antenna, the
|
||||
feed should similarly be rotated clockwise looking into the mouth of
|
||||
the feed. A negative value for Dpol means anticlockwise rotation.
|
||||
|
||||
|
||||
The state of the art for establishing three-dimensional locations of
|
||||
the sun, moon, and planets at a specified time is embodied in a
|
||||
numerical model of the solar system maintained at the Jet Propulsion
|
||||
Laboratory. The model has been numerically integrated to produce
|
||||
tabular data that can be interpolated with very high accuracy. For
|
||||
example, the celestial coordinates of the moon or a planet can be
|
||||
determined at a specified time to within about 0.0000003 degrees. The
|
||||
JPL ephemeris tables and interpolation routines have been incorporated
|
||||
into _WSJT-X_. Further details on accuracy, especially concerning
|
||||
calculated EME Doppler shifts, are described in {lunarEchoes} for
|
||||
November-December, 2016.
|
||||
|
||||
The sky background temperatures reported by _WSJT-X_ are derived from
|
||||
the all-sky 408 MHz map of Haslam et al. (Astronomy and Astrophysics
|
||||
Supplement Series, 47, 1, 1982), scaled by frequency to the -2.6
|
||||
power. This map has angular resolution of about 1 degree, and of
|
||||
course most amateur EME antennas have much broader beamwidths than
|
||||
this. Your antenna will therefore smooth out the hot spots
|
||||
considerably, and the observed extremes of sky temperature will be
|
||||
less. Unless you understand your sidelobes and ground reflections
|
||||
extremely well, it is unlikely that more accurate sky temperatures
|
||||
would be of much practical use.
|
||||
@@ -1,4 +0,0 @@
|
||||
// Status=review
|
||||
[[CONFIG_DETAILS]]
|
||||
|
||||
Are we here?
|
||||
@@ -1,80 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
At the center of the main window are a number of controls used when
|
||||
making QSOs. Controls not relevant to a particular mode or submode
|
||||
may be "grayed out" (disabled) or removed from the display.
|
||||
|
||||
//.Misc Controls Center
|
||||
image::misc-controls-center.png[align="center",alt="Misc Controls Center"]
|
||||
|
||||
* Check *Tx even* to transmit in even-numbered UTC minutes or
|
||||
sequences, starting at 0. Uncheck this box to transmit in the odd
|
||||
sequences. The correct selection is made automatically when you
|
||||
double-click on a decoded text line, as described in the
|
||||
<<TUTORIAL,Basic Operating Tutorial>>.
|
||||
|
||||
* The Tx and Rx audio frequencies can be set automatically by
|
||||
double-clicking on decoded text or a signal in the waterfall. They
|
||||
can also be adjusted using the spinner controls.
|
||||
|
||||
* You can force Tx frequency to the current Rx frequency by clicking
|
||||
the *Tx<-Rx* button, and vice-versa for *Rx<-Tx*. The on-the-air
|
||||
frequency of your lowest JT9 or JT65 tone is the sum of dial frequency
|
||||
and audio Tx frequency.
|
||||
|
||||
* Check the box *Lock Tx=Rx* to make the frequencies always track one
|
||||
another.
|
||||
|
||||
IMPORTANT: In general we do not recommend using *Lock Tx=Rx* since it
|
||||
encourages poor radio etiquette when running a frequency. With this
|
||||
box checked, your own Tx frequency will move around following your
|
||||
callers.
|
||||
|
||||
* For modes lacking a multi-decode feature, or when *Enable
|
||||
VHF/UHF/Microwave features* has been checked on the *Settings ->
|
||||
General* tab, the *F Tol* control sets a frequency toilerance range
|
||||
over which decoding will be attempted, centered on the Rx frequency.
|
||||
|
||||
* The *Report* control lets you change a signal report that has been
|
||||
inserted automatically. Typical reports for the various modes fall in
|
||||
the range –30 to +20 dB. Remember that JT65 reports saturate at an
|
||||
upper limit of -1 dB.
|
||||
|
||||
TIP: Consider reducing power if your QSO partner reports your
|
||||
signal above -5 dB in one of the _WSJT-X_ slow modes. These are
|
||||
supposed to be weak signal modes!
|
||||
|
||||
* In some circumstances, especially on VHF and higher bands, you can
|
||||
select a supported submode of the active mode by using the *Submode*
|
||||
control. The *Sync* control sets a minimum threshold for establishing
|
||||
time and frequency synchronization with a received signal.
|
||||
|
||||
* Spinner control *T/R xx s* sets sequence lengths for transmission
|
||||
and reception in ISCAT, MSK144, and the fast JT9 modes.
|
||||
|
||||
* With *Split operation* activated on the *Settings -> Radio* tab, in
|
||||
MSK144 and the fast JT9 submodes you can activate the spinner control
|
||||
*Tx CQ nnn* by checking the box to its right. The program will then
|
||||
generate something like `CQ nnn K1ABC FN42` for your CQ message, where
|
||||
`nnn` is the kHz portion of your current operating frequency,
|
||||
in the range 010 to 999. Your CQ
|
||||
message *Tx6* will then be transmitted at the calling frequency
|
||||
selected in the *Tx CQ nnn* spinner control. All other messages will
|
||||
be transmitted at your current operating frequency. On reception,
|
||||
when you double-click on a message like `CQ nnn K1ABC FN42` your rig
|
||||
will QSY to the specified frequency so you can call the station at his
|
||||
specified response frequency.
|
||||
|
||||
* Checkboxes at bottom center of the main window control special
|
||||
features for particular operating modes:
|
||||
|
||||
** *Sh* enables shorthand messages in JT4, JT65, QRA64 and MSK144 modes
|
||||
|
||||
** *Fast* enables fast JT9 submodes
|
||||
|
||||
** *Auto Seq* enables auto-sequencing of Tx messages
|
||||
|
||||
** *Call 1st* enables automatic response to the first decoded
|
||||
responder to your CQ
|
||||
|
||||
** *Tx6* toggles between two types of shorthand messages in JT4 mode
|
||||
@@ -1,45 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
Controls related to frequency selection, received audio level, the
|
||||
station being called, and date and time are found at lower left of the
|
||||
main window:
|
||||
|
||||
//.Misc Controls Left
|
||||
image::misc-main-ui.png[align="center",alt="Misc Menu Items"]
|
||||
|
||||
* A drop-down list of frequencies and bands at upper left lets you
|
||||
select the operating band and sets dial frequency to a value taken
|
||||
from the *Frequencies* tab on the *Settings* window. If CAT control
|
||||
is active the radio's dial frequency will be set accordingly; if not,
|
||||
you must tune the radio manually.
|
||||
|
||||
* Alternatively, you can enter a frequency (in MHz) or band name in
|
||||
recognized ADIF format, for example 630m, 20m, or 70cm. The band-name
|
||||
format works only if a working frequency has been set for that band
|
||||
and mode, in which case the first such match is selected.
|
||||
|
||||
* You can also enter a frequency increment in kHz above the currently
|
||||
displayed integer MHz. For example, if the displayed frequency is
|
||||
10,368.100, enter `165k` (don't forget the `k`!) to QSY to 10,368.165.
|
||||
|
||||
* A small colored circle appears in green if the CAT control is
|
||||
activated and functional. The green circle contains the character S
|
||||
if the rig is detected to be in *Split* mode. The circle becomes red
|
||||
if you have requested CAT control but communication with the radio has
|
||||
been lost.
|
||||
|
||||
TIP: Many Icom rigs cannot be queried for split status, current VFO or
|
||||
split transmit frequency. When using _WSJT-X_ with such radios you
|
||||
should not change the current VFO, split status or dial frequency
|
||||
using controls on the radio.
|
||||
|
||||
* If *DX Grid* contains a valid Maidenhead locator, the corresponding
|
||||
great-circle azimuth and distance from your location are displayed.
|
||||
|
||||
* The program can maintain a database of callsigns and locators for
|
||||
future reference. Click *Add* to insert the present call and locator
|
||||
in the database; click *Lookup* to retrieve the locator for a
|
||||
previously stored call. This feature is mainly useful for situations
|
||||
in which the number of active stations is modest and reasonably
|
||||
stable, such as EME (Earth-Moon-Earth) communication. The callsign
|
||||
file name is `CALL3.TXT`.
|
||||
@@ -1,69 +0,0 @@
|
||||
// Status=review
|
||||
The following controls appear just under the decoded text windows on
|
||||
the main screen:
|
||||
|
||||
//.Main UI
|
||||
image::main-ui-controls.png[align="center",width=650,alt="Main UI Controls"]
|
||||
|
||||
* When *CQ only* is checked, only messages from stations calling CQ will
|
||||
be displayed in the left text panel.
|
||||
|
||||
* *Log QSO* raises a dialog window pre-filled with known information
|
||||
about a QSO you have nearly completed. You can edit or add to this
|
||||
information before clicking *OK* to log the QSO. If you check *Prompt
|
||||
me to log QSO* on the *Settings -> Reporting* tab, the program will
|
||||
raise the confirmation screen automatically when you send a message
|
||||
containing +73+. *Start Date* and *Start Time* are set when you click
|
||||
to send the *Tx 2* or *Tx 3* message, and backed up by one or two
|
||||
sequence lengths, respectively. (Note that the actual start time may
|
||||
have been earlier if repeats of early transmissions were required.)
|
||||
End date and time are set when the *Log QSO* screen is invoked.
|
||||
|
||||
//.Log QSO Window
|
||||
image::log-qso.png[align="center",alt="Log QSO"]
|
||||
|
||||
* *Stop* will terminate normal data acquisition in case you want to
|
||||
freeze the waterfall or open and explore a previously recorded audio
|
||||
file.
|
||||
|
||||
* *Monitor* toggles normal receive operation on or off. This button
|
||||
is highlighted in green when the _WSJT-X_ is receiving. If you are
|
||||
using CAT control, toggling *Monitor* OFF relinquishes control of the
|
||||
rig; if *Monitor returns to last used frequency* is selected on the
|
||||
*Settings | General* tab, toggling *Monitor* back ON will return to
|
||||
the original frequency.
|
||||
|
||||
* *Erase* clears the right-hand decoded text window.
|
||||
Double-clicking *Erase* clears both text windows.
|
||||
|
||||
TIP: Right-clicking on either text window brings up a context menu
|
||||
with several options (including *Erase*) which operate on that window
|
||||
alone.
|
||||
|
||||
* *Clear Avg* is present only in modes that support message averaging.
|
||||
It provides a way to erase the accumulating information, thus
|
||||
preparing to start a new average.
|
||||
|
||||
* *Decode* tells the program to repeat the decoding procedure at the
|
||||
Rx frequency (green marker on waterfall scale), using the most recently
|
||||
completed sequence of received data.
|
||||
|
||||
* *Enable Tx* toggles automatic T/R sequencing mode on or off and
|
||||
highlights the button in red when ON. A transmission will start at
|
||||
the beginning of the selected (odd or even) sequence, or immediately
|
||||
if appropriate. Toggling the button to OFF during a transmission
|
||||
allows the current transmission to finish.
|
||||
|
||||
* *Halt Tx* terminates a transmission immediately and disables
|
||||
automatic T/R sequencing.
|
||||
|
||||
* *Tune* toggles the program into Tx mode and generates an unmodulated
|
||||
carrier at the specified Tx frequency (red marker on waterfall scale).
|
||||
This process is useful for adjusting an antenna tuner or tuning an
|
||||
amplifier. The button is highlighted in red while *Tune* is active.
|
||||
Toggle the button a second time or click *Halt Tx* to terminate the
|
||||
*Tune* process. Note that activating *Tune* interrupts a receive
|
||||
sequence and will prevent decoding during that sequence.
|
||||
|
||||
* Uncheck the box *Menus* to make the top-of-window menus disappear,
|
||||
leaving more vertical space for decoded messages.
|
||||
@@ -1,66 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
Menus at top of the main window offer many options for configuration
|
||||
and operation. Most of the items are self-explanatory; a few
|
||||
additional details are provided below. Keyboard shortcuts for some
|
||||
frequently used menu items are listed at the right edge of the menu.
|
||||
|
||||
==== _WSJT-X_ menu
|
||||
image::MacAppMenu.png[align="left",alt="Mac App Menu"]
|
||||
|
||||
This menu appears on the Macintosh only. *Settings* appears here,
|
||||
labeled as *Preferences*, rather than on the *File* menu. *About
|
||||
WSJT-X* appears here rather than on the *Help* menu.
|
||||
|
||||
[[FILE_MENU]]
|
||||
==== File menu
|
||||
image::file-menu.png[align="left",alt="File Menu"]
|
||||
|
||||
[[CONFIG_MENU]]
|
||||
==== Configuration Menu
|
||||
image::config-menu.png[align="left",alt="File Menu"]
|
||||
|
||||
Many users prefer to create and use entries on the *Configurations*
|
||||
menu for switching between modes. Simply *Clone* the *Default* entry,
|
||||
*Rename* it as desired, and then make all desired settings for that
|
||||
configuration. These settings will be restored whenever you select
|
||||
that configuration.
|
||||
|
||||
As well as switching between configurations while running _WSJT-X_ you
|
||||
can also start the application from the command line in any desired
|
||||
configuration. Use the command-line option
|
||||
`--config <configuration-name>`, or `-c` for short, as in these
|
||||
examples for configurations `FT8` and `Echo`:
|
||||
|
||||
wsjtx --config FT8
|
||||
wsjtx -c Echo
|
||||
|
||||
[[VIEW_MENU]]
|
||||
==== View Menu
|
||||
image::view-menu.png[align="left",alt="View Menu"]
|
||||
|
||||
[[MODE_MENU]]
|
||||
==== Mode Menu
|
||||
image::mode-menu.png[align="left",alt="Mode Menu"]
|
||||
|
||||
[[DECODE_MENU]]
|
||||
==== Decode Menu
|
||||
image::decode-menu.png[align="left",alt="Decode Menu"]
|
||||
|
||||
[[SAVE_MENU]]
|
||||
[[SAVE-WAV]]
|
||||
==== Save Menu
|
||||
image::save-menu.png[align="left",alt="Save Menu"]
|
||||
|
||||
==== Tools Menu
|
||||
image::tools-menu.png[align="left",alt="Tools Menu"]
|
||||
|
||||
[[HELP_MENU]]
|
||||
==== Help Menu
|
||||
image::help-menu.png[align="left",alt="Help Menu"]
|
||||
|
||||
===== Keyboard Shortcuts (F3)
|
||||
image::keyboard-shortcuts.png[align="left",alt="Keyboard Shortcuts"]
|
||||
|
||||
===== Special Mouse Commands (F5)
|
||||
image::special-mouse-commands.png[align="left",alt="Special Mouse Commands"]
|
||||
@@ -1,61 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
Two arrangements of controls are provided for generating and selecting
|
||||
Tx messages. Controls familiar to users of program _WSJT_
|
||||
appear on *Tab 1*, providing six fields for message entry.
|
||||
Pre-formatted messages for the standard minimal QSO are generated when
|
||||
you click *Generate Std Msgs* or double-click on an appropriate line
|
||||
in one of the decoded text windows.
|
||||
|
||||
//.Traditional Message Menu
|
||||
image::traditional-msg-box.png[align="center",alt="Traditional Message Menu"]
|
||||
|
||||
* Select the next message to be transmitted (at the start of your next
|
||||
Tx sequence) by clicking on the circle under *Next*.
|
||||
|
||||
* To change to a specified Tx message immediately during a
|
||||
transmission, click on a rectangular button under the *Now* label.
|
||||
Changing a Tx message in mid-stream will slightly reduce the chance of
|
||||
a correct decode, but it is usually OK if done in the first 10-20% of
|
||||
a transmission.
|
||||
|
||||
* All six Tx message fields are editable. You can modify an
|
||||
automatically generated message or enter a desired message, keeping in
|
||||
mind the limits on message content. See <<PROTOCOLS,Protocol
|
||||
Specifications>> for details.
|
||||
|
||||
* Click on the pull-down arrow for message #5 to select one of the
|
||||
pre-stored messages entered on the *Settings | Tx Macros* tab.
|
||||
Pressing *Enter* on a modified message #5 automatically adds that
|
||||
message to the stored macros.
|
||||
|
||||
* In some circumstances it may be desirable to make your QSOs as
|
||||
shiort as possible. To configure the program to start contacts with
|
||||
message #2, disable message #1 by double-clicking on its round
|
||||
radio-button or rectangular *Tx 1* button. Similarly, to send RR73
|
||||
rather than RRR for message #4, double-click on one of its buttons.
|
||||
|
||||
The second arrangement of controls for generating and selecting
|
||||
Tx messages appears on *Tab 2* of the Message Control Panel:
|
||||
|
||||
//.New Message Menu
|
||||
image::new-msg-box.png[align="center",alt="New Message Menu"]
|
||||
|
||||
With this setup you normally follow a top-to-bottom sequence of
|
||||
transmissions from the left column if you are calling CQ, or the right
|
||||
column if answering a CQ.
|
||||
|
||||
* Clicking a button puts the appropriate message in the *Gen Msg* box.
|
||||
If you are already transmitting, the Tx message is changed
|
||||
immediately.
|
||||
|
||||
* You can enter and transmit anything (up to 13 characters, including
|
||||
spaces) in the *Free Msg* box.
|
||||
|
||||
* Click on the pull-down arrow in the *Free Msg* box to select a
|
||||
pre-stored macro. Pressing *Enter* on a modified message here
|
||||
automatically adds that message to the table of stored macros.
|
||||
|
||||
TIP: During a transmission the actual message being sent always
|
||||
appears in the first box of the status bar (bottom left of the main
|
||||
screen).
|
||||
@@ -1,23 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
A *Status Bar* at the bottom edge of the main window provides useful
|
||||
information about operating conditions.
|
||||
|
||||
//.Status Bar
|
||||
image::status-bar-a.png[align="left",alt="Status Bar"]
|
||||
|
||||
Labels on the *Status Bar* display such information as the program's
|
||||
current operating state, configuration name, operating mode, and the
|
||||
content of your most recent transmitted message. The first label
|
||||
(operating state) can be Receiving, Tx (for Transmitting), Tune, or
|
||||
the name of file opened from the *File* menu; this label is
|
||||
highlighted in green for Receiving, yellow for Tx, red for Tune, and
|
||||
light blue for a file name. When transmitting, the Tx message is
|
||||
displayed exactly as it will be decoded by receiving stations. The
|
||||
second label (as shown above) will be absent if you are using the
|
||||
*Default* setting on the *Configurations* menu. A progress bar shows
|
||||
the elapsed fraction of a Tx or Rx sequence. Finally, if the Watchdog
|
||||
(WD) timer was enabled on the *settings | General* tab, a label in the
|
||||
lower right-hand corner displays the number of minutes remaining
|
||||
before timeout.
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
The following controls appear at the bottom of the Wide Graph window.
|
||||
Decoding occurs only in the displayed frequency range; otherwise, with
|
||||
the exceptions of *Start NNN Hz* and of *JT65 nnnn JT9* when operating
|
||||
in JT9+JT65 mode, controls on the Wide Graph window have no effect on
|
||||
the decoding process.
|
||||
|
||||
image::wide-graph-controls.png[align="center",alt="Wide Graph Controls"]
|
||||
|
||||
- *Bins/Pixel* controls the displayed frequency resolution. Set this
|
||||
value to 1 for the highest possible resolution, or to higher numbers
|
||||
to compress the spectral display. Normal operation with a convenient
|
||||
window size works well at 2 to 8 bins per pixel.
|
||||
|
||||
- *JT65 nnnn JT9* sets the dividing point (blue marker) for wide-band
|
||||
decoding of JT65 and JT9 signals in *JT9+JT65* mode. The decoder
|
||||
looks for JT65 signals everywhere, but JT9 signals only above this
|
||||
frequency. This setting is stored separately for each band.
|
||||
|
||||
- *Start nnn Hz* sets the low-frequency starting point of the
|
||||
waterfall frequency scale.
|
||||
|
||||
- *N Avg* is the number of successive spectra to be averaged before
|
||||
updating the display. Values around 5 are suitable for normal JT9 and
|
||||
JT65 operation. Adjust *N Avg* to make the waterfall move faster or
|
||||
slower, as desired.
|
||||
|
||||
- A dropdown list below the *Palette* label lets you select from a
|
||||
wide range of waterfall color palettes.
|
||||
|
||||
- Click *Adjust* to activate a window that allows you to create a
|
||||
user-defined palette.
|
||||
|
||||
- Check *Flatten* if you want _WSJT-X_ to compensate for a sloping or
|
||||
uneven response across the received passband. For this feature to
|
||||
work properly, you should restrict the range of displayed frequencies
|
||||
so that only the active part of the spectrum is shown.
|
||||
|
||||
- Select *Current* or *Cumulative* for the spectrum displayed in the
|
||||
bottom one-third of the Wide Graph window. *Current* is the average
|
||||
spectrum over the most recent *N Avg* FFT calculations. *Cumulative*
|
||||
is the average spectrum since the start of the present UTC minute.
|
||||
*Linear Avg* is useful in JT4 mode, especially when short-form
|
||||
messages are used.
|
||||
|
||||
- Four sliders control reference levels and scaling for waterfall
|
||||
colors and the spectrum plot. Values around midscale are usually
|
||||
about right, depending on the input signal level, the chosen palette,
|
||||
and your own preferences. Hover the mouse over a control to display a
|
||||
tip reminding you of its function.
|
||||
|
||||
- *Smoothing* is active only when *Linear Average* has been selected.
|
||||
Smoothing the displayed spectrum over more than one bin can enhance
|
||||
your ability to detect weak EME signals with Doppler spread more than
|
||||
a few Hz.
|
||||
|
||||
[[CONTROLS_FAST]]
|
||||
=== Fast Graph
|
||||
|
||||
The waterfall palette used for the Fast Graph is the same as the one
|
||||
selected on the Wide Graph. Three sliders at the bottom of the Fast
|
||||
Graph window can be used to optimize gain and zero-offset for the
|
||||
displayed information. Hover the mouse over a control to display a
|
||||
tip reminding you of its function. Clicking the *Auto Level* button
|
||||
will produce reasonable settings as a starting point.
|
||||
|
||||
image::fast-graph-controls.png[align="center",alt="Fast Graph Controls"]
|
||||
|
||||
[[CONTROLS_ECHO]]
|
||||
=== Echo Graph
|
||||
|
||||
The following controls appear at the bottom of the Echo Graph:
|
||||
|
||||
image::echo-graph-controls.png[align="center",alt="EchoGraph Controls"]
|
||||
|
||||
- *Bins/Pixel* controls the displayed frequency resolution. Set this
|
||||
value to 1 for the highest possible resolution, or to higher numbers
|
||||
to compress the spectral display.
|
||||
|
||||
- *Gain* and *Zero* sliders control scaling and offset of plotted
|
||||
spectra.
|
||||
|
||||
- *Smooth* values greater than 0 apply running averages to the plotted
|
||||
spectra, thereby smoothing the curves over multiple bins.
|
||||
|
||||
- Label *N* shows the number of echo pulses averaged.
|
||||
|
||||
- Click the *Colors* button to cycle through 6 possible choices of
|
||||
color and line width for the plots.
|
||||
|
||||
[[CONTROLS_MISCELLANEOUS]]
|
||||
=== Miscellaneous
|
||||
|
||||
Most windows can be resized as desired. If you are short of screen
|
||||
space you can make the Main Window and Wide Graph smaller by hiding
|
||||
some controls and labels. To enable this feature type *Ctrl+M* with
|
||||
focus on the appropriate window. (For the Main Window you can select
|
||||
*Hide menus and labels* on the *View* menu.) Type *Ctrl+M* again
|
||||
to make the controls visible once more.
|
||||
@@ -1,39 +0,0 @@
|
||||
_WSJT-X_ is programmed to cooperate closely with several other useful
|
||||
programs.
|
||||
|
||||
* {dxlsuite}, {omnirig}, and {hrd} were described in the section on
|
||||
<<RADIO,rig control>>.
|
||||
|
||||
* {pskreporter}, by Philip Gladstone, is a web server that gathers
|
||||
reception reports sent by various other programs, including _WSJT-X_.
|
||||
The information is made available in near real time on a world map,
|
||||
and also as statistical summaries of various kinds. A number of
|
||||
options are available to the user; for example, you can request a map
|
||||
showing world-wide JT65 activity on all amateur bands over the
|
||||
past hour. Such a map might look like this, where different colors
|
||||
represent different bands:
|
||||
|
||||
image::psk-reporter.png[align="left",alt="PSK Reporter"]
|
||||
|
||||
The following screen shot shows the PSK Reporter map configured to
|
||||
show MSK144 reports:
|
||||
|
||||
image::psk-reporter_2.png[align="left",alt="PSK Reporter"]
|
||||
|
||||
* {jtalert}, by VK3AMA, is available only for Windows. It provides
|
||||
many operating aids including automatic logging to several third-party
|
||||
logging programs, audio and visual alerts following a number of
|
||||
optional alert conditions (decoding of a new DXCC, new state, etc.),
|
||||
and convenient direct access to web services such as callsign lookup.
|
||||
|
||||
image::jtalert.png[align="left",alt="JTAlert-X image"]
|
||||
|
||||
* {alarmejt}, by F5JMH, is available only for Linux. The program keeps
|
||||
its own logbook. It fetches contact information from _WSJT-X_ and
|
||||
provides visual alerts for new DXCC entities and grid squares on the
|
||||
current band, as well as other options.
|
||||
|
||||
* {jtbridge}, by SM0THU, is available for OS X. It works together with
|
||||
logging applications Aether, MacLoggerDX, RUMlog or RUMlogNG. It
|
||||
checks QSO and QSL status of the call and DXCC entity, as well as many
|
||||
other features.
|
||||
@@ -1,155 +0,0 @@
|
||||
=== AP Decoding
|
||||
|
||||
The _WSJT-X_ decoders for JT65, QRA64, and FT8 include optional
|
||||
procedures that use naturally accumulating information during a
|
||||
minimal QSO. This _a priori_ (AP) information increases sensitivity
|
||||
of the decoder by up to 4 dB, at the cost of a slightly higher rate of
|
||||
false decodes.
|
||||
|
||||
For example: when you decide to answer a CQ, you already know your own
|
||||
callsign and that of your potential QSO partner. The software
|
||||
therefore "`knows`" what to expect for 57 of the 72 message bits (28
|
||||
bits for each of two callsigns, 1 bit for message type) in the next
|
||||
received message. The decoder's task can thus be reduced to
|
||||
determining the remaining 15 bits of the message and ensuring that the
|
||||
resulting solution is reliable.
|
||||
|
||||
AP decoding starts by setting AP bits to the hypothesized values, as
|
||||
if they had been received correctly. We then determine whether the
|
||||
remaining message and parity bits are consistent with the hypothesized
|
||||
AP bits, with a specified level of confidence. Successful AP decodes
|
||||
are labeled with an end-of-line indicator of the form `aP`, where `P`
|
||||
is one of the single-digit AP decoding types listed in Table 1. For
|
||||
example, `a2` indicates that the successful decode used MyCall as
|
||||
hypothetically known information.
|
||||
|
||||
[[FT8_AP_INFO_TABLE]]
|
||||
.FT8 AP information types
|
||||
[width="35%",cols="h10,<m20",frame=topbot,options="header"]
|
||||
|===============================================
|
||||
|aP | Message components
|
||||
|a1 | CQ     ?     ?
|
||||
|a2 | MyCall     ?     ?
|
||||
|a3 | MyCall DxCall     ?
|
||||
|a4 | MyCall DxCall RRR
|
||||
|a5 | MyCall DxCall 73
|
||||
|a6 | MyCall DxCall RR73
|
||||
|===============================================
|
||||
|
||||
If a codeword is found that is judged to have high (but not
|
||||
overwhelmingly high) probability of being correct, a `?` character is
|
||||
appended when the decoded message is displayed. To avoid misleading
|
||||
spots of occasional false decodes, messages so marked are not
|
||||
forwarded to {pskreporter}.
|
||||
|
||||
Table 2 lists the six possible QSO states that are tracked by the
|
||||
_WSJT-X_ auto-sequencer, along with the type of AP decoding that would
|
||||
be attempted in each state.
|
||||
|
||||
[[FT8_AP_DECODING_TYPES_TABLE]]
|
||||
.FT8 AP decoding types for each QSO state
|
||||
[width="35%",cols="h10,<m20",frame=topbot,options="header"]
|
||||
|===========================================
|
||||
|State |AP type
|
||||
|CALLING STN | 2, 3
|
||||
|REPORT | 2, 3
|
||||
|ROGER_REPORT | 3, 4, 5, 6
|
||||
|ROGERS | 3, 4, 5, 6
|
||||
|SIGNOFF | 3, 1, 2
|
||||
|CALLING CQ | 1, 2
|
||||
|===========================================
|
||||
|
||||
Decoding with _a priori_ information behaves slightly differently
|
||||
in JT65. Some details are provided in Tables 3 and 4.
|
||||
|
||||
[[JT65_AP_INFO_TABLE]]
|
||||
.JT65 AP information types
|
||||
[width="35%",cols="h10,<m20",frame=topbot,options="header"]
|
||||
|===============================================
|
||||
|aP | Message components
|
||||
|a1 | CQ     ?     ?
|
||||
|a2 | MyCall     ?     ?
|
||||
|a3 | MyCall DxCall     ?
|
||||
|a4 | MyCall DxCall RRR
|
||||
|a5 | MyCall DxCall 73
|
||||
|a6 | MyCall DxCall DxGrid
|
||||
|a7 | CQ DxCall DxGrid
|
||||
|===============================================
|
||||
|
||||
[[JT65_AP_DECODING_TYPES_TABLE]]
|
||||
.JT65 AP decoding types for each QSO state
|
||||
[width="35%",cols="h10,<m20",frame=topbot,options="header"]
|
||||
|===========================================
|
||||
|State |AP type
|
||||
|CALLING STN | 2, 3, 6, 7
|
||||
|REPORT | 2, 3
|
||||
|ROGER_REPORT | 3, 4, 5
|
||||
|ROGERS | 3, 4, 5
|
||||
|SIGNOFF | 2, 3, 4, 5
|
||||
|CALLING CQ | 1, 2, 6
|
||||
|===========================================
|
||||
|
||||
|
||||
=== Decoded Lines
|
||||
|
||||
Displayed information accompanying decoded messages generally includes UTC,
|
||||
signal-to-noise ratio in dB, time offset DT in seconds, and
|
||||
audio frequency in Hz. Some modes include additional information such
|
||||
as frequency offset from nominal (DF), frequency drift (Drift or F1),
|
||||
or distance (km or mi).
|
||||
|
||||
There may also be some cryptic characters with special meanings
|
||||
summarized in the following Table:
|
||||
|
||||
[[DECODED_LINES_TABLE]]
|
||||
.Notations used on decoded text lines
|
||||
[width="50%",cols="h,3*^",frame=topbot,options="header"]
|
||||
|===========================================
|
||||
|Mode |Mode character|Sync character|End of line information
|
||||
|FT8 | ~ | | ?   aP
|
||||
|JT4 | $ | *, # | f, fN, dNC
|
||||
|JT9 | @ | |
|
||||
|JT65 | # | |
|
||||
|JT65 VHF| # | *, # | f, fN, dNC
|
||||
|QRA64 | : | * | R
|
||||
|ISCAT | | * | M N C T
|
||||
|MSK144 | & | | N H E
|
||||
|===========================================
|
||||
Sync character::
|
||||
`*` - Normal sync +
|
||||
`#` - Alternate sync
|
||||
|
||||
End of line information::
|
||||
`?` - Decoded with lower confidence +
|
||||
`a` - Decoded with aid of some a priori (AP) information +
|
||||
`C` - Confidence indicator [ISCAT and Deep Search; (0-9,*)] +
|
||||
`d` - Deep Search algorithm +
|
||||
`E` - Size of MSK eye diagram opening - if negative, the eye is closed +
|
||||
`f` - Franke-Taylor or Fano algorithm +
|
||||
`H` - Number of bit errors corrected +
|
||||
`M` - Message length (characters) +
|
||||
`N` - Number of Rx intervals or frames averaged +
|
||||
`P` - Number indicating type of AP information (Table 1, above) +
|
||||
`R` - Return code from QRA64 decoder +
|
||||
`T` - Length of analyzed region (s)
|
||||
|
||||
Table 6 below shows the meaning of the return codes R in QRA64 mode.
|
||||
|
||||
[[QRA64_AP_INFO_TABLE]]
|
||||
.QRA64 AP return codes
|
||||
[width="35%",cols="h10,<m20",frame=topbot,options="header"]
|
||||
|===============================================
|
||||
|rc | Message components
|
||||
|0 | ?     ?     ?
|
||||
|1 | CQ     ?     ?
|
||||
|2 | CQ     ?
|
||||
|3 | MyCall     ?     ?
|
||||
|4 | MyCall     ?
|
||||
|5 | MyCall DxCall     ?
|
||||
|6 | ?     DxCall     ?
|
||||
|7 | ?     DxCall
|
||||
|8 | MyCall DxCall DxGrid
|
||||
|9 | CQ DxCall     ?
|
||||
|10 | CQ DxCall
|
||||
|11 | CQ DxCall DxGrid
|
||||
|===============================================
|
||||
@@ -1,17 +0,0 @@
|
||||
<style>
|
||||
html, body {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
@@ -1,77 +0,0 @@
|
||||
////
|
||||
Questions:
|
||||
Should be short one liners (in the .adoc file) ending with ?::
|
||||
If your question is too long for one line, consider multiple questions or rephrase
|
||||
|
||||
Answers:
|
||||
Can be bullet or paragraphs. Bullets make for easier reading.
|
||||
|
||||
Bullet Usage:
|
||||
* = a circle bullet single intent
|
||||
** = circle bullet double indent
|
||||
. = should be avoided as the questions are numbered
|
||||
.. = bullet a, b, c, and so on, double indent
|
||||
|
||||
Alternatives: Use a * Bullet, followed by .. for example, then have
|
||||
a multi-section answer using the * as the section header
|
||||
|
||||
* Section Header 1
|
||||
.. Possible Answer a
|
||||
.. Possible Answer b
|
||||
* Section Header 2
|
||||
.. Possible Answer a
|
||||
.. Possible Answer b
|
||||
|
||||
Link Usage:
|
||||
Use the common/links.adoc for href links to maintain consistency. Try to avoid
|
||||
apostrophes ` or ' as it breaks AsciiDoc syntax without special escaping
|
||||
and they do not translate into other languages well.
|
||||
|
||||
////
|
||||
[qanda]
|
||||
My displayed spectrum is flatter when I do not check the *Flatten* box. What's wrong?::
|
||||
|
||||
_WSJT-X_ does not expect a steep filter edge within the displayed
|
||||
passband. Use a wider IF filter or reduce the displayed passband by
|
||||
decreasing *Bins/Pixel*, increasing *Start*, or reducing the width of
|
||||
the *Wide Graph*. You might also choose to re-center the filter
|
||||
passband, if such control is available.
|
||||
|
||||
How should I configure _WSJT-X_ to run multiple instances?::
|
||||
|
||||
Start _WSJT-X_ from a command-prompt window, assigning each instance a
|
||||
unique identifier as in the following two-instance example. This
|
||||
procedure will isolate the *Settings* file and the writable file
|
||||
location for each instance of _WSJT-X_.
|
||||
|
||||
wsjtx --rig-name=TS2000
|
||||
wsjtx --rig-name=FT847
|
||||
|
||||
Rig control through _OmniRig_ seems to fail when I click *Test CAT*. What can I do about it?::
|
||||
|
||||
_Omni-Rig_ apparently has a bug that appears when you click *Test
|
||||
CAT*. Forget using *Test CAT* and just click *OK*. _Omni-Rig_ then
|
||||
behaves normally.
|
||||
|
||||
I am using _WSJT-X_ with _Ham Radio Deluxe_. All seems well until I start HRD Logbook or DM780 running in parallel; then CAT control becomes unreliable.::
|
||||
|
||||
You may see delays up to 20 seconds or so in frequency changes or
|
||||
other radio commands, due to a bug in HRD. HRD folks are aware of the
|
||||
problem, and are working to resolve it.
|
||||
|
||||
I am running _WSJT-X_ under Ubuntu. The program starts, but menu bar is missing from the top of the main window and the hot-keys don't work.::
|
||||
|
||||
Ubuntu's new "`Unity`" desktop puts the menu for the currently active
|
||||
window at the top of the primary display screen. You can restore menu
|
||||
bars to their traditional locations by typing the following in a
|
||||
command-prompt window:
|
||||
|
||||
sudo apt remove appmenu-qt5
|
||||
|
||||
+
|
||||
Alternatively, you can disable the common menu bar for just _WSJT-X_
|
||||
by starting the application with the environment variable
|
||||
QT_QPA_PLATFORMTHEME set to empty (the space after the '=' character
|
||||
is necessary):
|
||||
|
||||
QT_QPA_PLATFORMTHEME= wsjtx
|
||||
@@ -1,15 +0,0 @@
|
||||
// Status=review
|
||||
|
||||
- You can control the program's font sizes by using a text editor
|
||||
(e.g., Windows Notepad or similar) to create a one-line file named
|
||||
+fonts.txt+ in the _WSJT-X_ installation directory. Enter a single
|
||||
line of text with four numbers separated by spaces. The first two
|
||||
numbers control the font size (in points) and weight (on a 0 – 100
|
||||
scale) of most labels on the user interface. The last two numbers
|
||||
control size and weight of text in the *Band Activity* and *Rx
|
||||
Frequency* windows.
|
||||
|
||||
- By default the four numbers are "`8 50 10 50`". If you need larger
|
||||
fonts in the user interface and bold text in the decode windows, try
|
||||
something like "`10 50 12 100`" (without the quotes).
|
||||
|
||||
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 153 KiB |