Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 <cmath>
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
#include "varicode.h"
|
#include "varicode.h"
|
||||||
|
|
||||||
const int PACKET_TIMEOUT_SECONDS = 300;
|
const int PACKET_TIMEOUT_SECONDS = 300;
|
||||||
|
|
||||||
APRSISClient::APRSISClient(QString host, quint16 port, QObject *parent):
|
APRSISClient::APRSISClient(QString host, quint16 port, QObject *parent):
|
||||||
QTcpSocket(parent),
|
QTcpSocket(parent)
|
||||||
m_host(host),
|
|
||||||
m_port(port)
|
|
||||||
{
|
{
|
||||||
|
setServer(host, port);
|
||||||
|
|
||||||
connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports);
|
connect(&m_timer, &QTimer::timeout, this, &APRSISClient::sendReports);
|
||||||
m_timer.setInterval(60*1000); // every 60 seconds
|
m_timer.setInterval(60*1000); // every 60 seconds
|
||||||
m_timer.start();
|
m_timer.start();
|
||||||
@@ -37,7 +38,7 @@ QString APRSISClient::loginFrame(QString callsign){
|
|||||||
auto loginFrame = QString("user %1 pass %2 ver %3\n");
|
auto loginFrame = QString("user %1 pass %2 ver %3\n");
|
||||||
loginFrame = loginFrame.arg(callsign);
|
loginFrame = loginFrame.arg(callsign);
|
||||||
loginFrame = loginFrame.arg(hashCallsign(callsign));
|
loginFrame = loginFrame.arg(hashCallsign(callsign));
|
||||||
loginFrame = loginFrame.arg("FT8Call");
|
loginFrame = loginFrame.arg("JS8Call");
|
||||||
return loginFrame;
|
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){
|
void APRSISClient::enqueueSpot(QString theircall, QString grid, QString comment){
|
||||||
if(m_localCall.isEmpty()) return;
|
if(m_localCall.isEmpty()) return;
|
||||||
|
|
||||||
auto geo = APRSISClient::grid2aprs(grid);
|
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(theircall);
|
||||||
spotFrame = spotFrame.arg(m_localCall);
|
spotFrame = spotFrame.arg(stripSSID(m_localCall));
|
||||||
spotFrame = spotFrame.arg(geo.first);
|
spotFrame = spotFrame.arg(geo.first);
|
||||||
spotFrame = spotFrame.arg(geo.second);
|
spotFrame = spotFrame.arg(geo.second);
|
||||||
spotFrame = spotFrame.arg(comment.left(43));
|
spotFrame = spotFrame.arg(comment.left(43));
|
||||||
@@ -201,15 +224,15 @@ void APRSISClient::enqueueThirdParty(QString theircall, QString payload){
|
|||||||
return;
|
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(theircall);
|
||||||
frame = frame.arg(m_localCall);
|
frame = frame.arg(stripSSID(m_localCall));
|
||||||
frame = frame.arg(payload);
|
frame = frame.arg(payload);
|
||||||
enqueueRaw(frame);
|
enqueueRaw(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void APRSISClient::enqueueRaw(QString aprsFrame){
|
void APRSISClient::enqueueRaw(QString aprsFrame){
|
||||||
m_frameQueue.enqueue({ aprsFrame, QDateTime::currentDateTimeUtc() });
|
m_frameQueue.enqueue({ aprsFrame, DriftingDateTime::currentDateTimeUtc() });
|
||||||
}
|
}
|
||||||
|
|
||||||
void APRSISClient::processQueue(bool disconnect){
|
void APRSISClient::processQueue(bool disconnect){
|
||||||
@@ -225,6 +248,7 @@ void APRSISClient::processQueue(bool disconnect){
|
|||||||
// 4. disconnect
|
// 4. disconnect
|
||||||
|
|
||||||
if(state() != QTcpSocket::ConnectedState){
|
if(state() != QTcpSocket::ConnectedState){
|
||||||
|
qDebug() << "APRSISClient Connecting:" << m_host << m_port;
|
||||||
connectToHost(m_host, m_port);
|
connectToHost(m_host, m_port);
|
||||||
if(!waitForConnected(5000)){
|
if(!waitForConnected(5000)){
|
||||||
qDebug() << "APRSISClient Connection Error:" << errorString();
|
qDebug() << "APRSISClient Connection Error:" << errorString();
|
||||||
@@ -263,7 +287,7 @@ void APRSISClient::processQueue(bool disconnect){
|
|||||||
auto timestamp = pair.second;
|
auto timestamp = pair.second;
|
||||||
|
|
||||||
// if the packet is older than the timeout, drop it.
|
// 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;
|
qDebug() << "APRSISClient Packet Timeout:" << frame;
|
||||||
m_frameQueue.dequeue();
|
m_frameQueue.dequeue();
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public:
|
|||||||
static QString loginFrame(QString callsign);
|
static QString loginFrame(QString callsign);
|
||||||
static QPair<float, float> grid2deg(QString grid);
|
static QPair<float, float> grid2deg(QString grid);
|
||||||
static QPair<QString, QString> grid2aprs(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){
|
void setServer(QString host, quint16 port){
|
||||||
if(state() == QTcpSocket::ConnectedState){
|
if(state() == QTcpSocket::ConnectedState){
|
||||||
@@ -25,6 +27,8 @@ public:
|
|||||||
|
|
||||||
m_host = host;
|
m_host = host;
|
||||||
m_port = port;
|
m_port = port;
|
||||||
|
|
||||||
|
qDebug() << "APRSISClient Server Change:" << m_host << m_port;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPaused(bool paused){
|
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_PACKAGE_DESCRIPTION_SUMMARY "@PROJECT_SUMMARY_DESCRIPTION@")
|
||||||
set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING")
|
set (CPACK_RESOURCE_FILE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING")
|
||||||
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
||||||
set (CPACK_PACKAGE_EXECUTABLES ft8call "@PROJECT_NAME@")
|
set (CPACK_PACKAGE_EXECUTABLES js8call "@PROJECT_NAME@")
|
||||||
set (CPACK_CREATE_DESKTOP_LINKS ft8call)
|
set (CPACK_CREATE_DESKTOP_LINKS js8call)
|
||||||
set (CPACK_STRIP_FILES TRUE)
|
set (CPACK_STRIP_FILES TRUE)
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -24,7 +24,7 @@ if (CPACK_GENERATOR MATCHES "NSIS")
|
|||||||
set (CPACK_SET_DESTDIR FALSE)
|
set (CPACK_SET_DESTDIR FALSE)
|
||||||
set (CPACK_STRIP_FILES FALSE) # breaks Qt packaging on Windows
|
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)
|
if(CMAKE_CL_64)
|
||||||
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES64")
|
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
|
# set the install/unistall icon used for the installer itself
|
||||||
# There is a bug in NSI that does not handle full unix paths properly.
|
# 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_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\js8call.ico")
|
||||||
set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\ft8call.ico")
|
set (CPACK_NSIS_MUI_UNIICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\js8call.ico")
|
||||||
# set the package header icon for MUI
|
# set the package header icon for MUI
|
||||||
set (CPACK_PACKAGE_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\installer_logo.bmp")
|
set (CPACK_PACKAGE_ICON "@PROJECT_SOURCE_DIR@/icons/windows-icons\\installer_logo.bmp")
|
||||||
# tell cpack to create links to the doc files
|
# tell cpack to create links to the doc files
|
||||||
set (CPACK_NSIS_MENU_LINKS
|
set (CPACK_NSIS_MENU_LINKS
|
||||||
"@PROJECT_MANUAL_DIRECTORY_URL@/@PROJECT_MANUAL@" "@PROJECT_NAME@ Documentation"
|
|
||||||
"@PROJECT_HOMEPAGE@" "@PROJECT_NAME@ Web Site"
|
"@PROJECT_HOMEPAGE@" "@PROJECT_NAME@ Web Site"
|
||||||
)
|
)
|
||||||
# Use the icon from wsjtx for add-remove programs
|
# 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_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_URL_INFO_ABOUT "@PROJECT_HOMEPAGE@")
|
||||||
set (CPACK_NSIS_CONTACT "${CPACK_PACKAGE_CONTACT}")
|
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)
|
set (CPACK_NSIS_MODIFY_PATH ON)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -67,11 +66,11 @@ endif ()
|
|||||||
if ("${CPACK_GENERATOR}" STREQUAL "DragNDrop")
|
if ("${CPACK_GENERATOR}" STREQUAL "DragNDrop")
|
||||||
set (CPACK_DMG_VOLUME_NAME "@PROJECT_NAME@")
|
set (CPACK_DMG_VOLUME_NAME "@PROJECT_NAME@")
|
||||||
set (CPACK_DMG_BACKGROUND_IMAGE "@PROJECT_SOURCE_DIR@/icons/Darwin/DragNDrop Background.png")
|
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_BUNDLE_NAME "@WSJTX_BUNDLE_NAME@")
|
||||||
set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/ft8call.icns")
|
set (CPACK_PACKAGE_ICON "@PROJECT_BINARY_DIR@/js8call.icns")
|
||||||
set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/ft8call.icns")
|
set (CPACK_BUNDLE_ICON "@PROJECT_BINARY_DIR@/js8call.icns")
|
||||||
set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-ft8call-startup.sh")
|
set (CPACK_BUNDLE_STARTUP_COMMAND "@PROJECT_SOURCE_DIR@/Mac-js8call-startup.sh")
|
||||||
set (CPACK_PACKAGING_INSTALL_PREFIX "/")
|
set (CPACK_PACKAGING_INSTALL_PREFIX "/")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@@ -79,9 +78,9 @@ if ("${CPACK_GENERATOR}" STREQUAL "WIX")
|
|||||||
# Reset CPACK_PACKAGE_VERSION to deal with WiX restriction.
|
# Reset CPACK_PACKAGE_VERSION to deal with WiX restriction.
|
||||||
# But the file names still use the full CMake_VERSION value:
|
# But the file names still use the full CMake_VERSION value:
|
||||||
set (CPACK_PACKAGE_FILE_NAME
|
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
|
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)
|
if (NOT CPACK_WIX_SIZEOF_VOID_P)
|
||||||
set (CPACK_WIX_SIZEOF_VOID_P "@CMAKE_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.")
|
Do not override this if you intend to build an official deployable installer.")
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
project (ft8call C CXX Fortran)
|
project (js8call C CXX Fortran)
|
||||||
|
|
||||||
#
|
#
|
||||||
# CMake policies
|
# CMake policies
|
||||||
@@ -45,80 +45,15 @@ message (STATUS "Building ${CMAKE_PROJECT_NAME}-${wsjtx_VERSION}")
|
|||||||
#
|
#
|
||||||
# project information
|
# project information
|
||||||
#
|
#
|
||||||
set (PROJECT_NAME "FT8Call")
|
set (PROJECT_NAME "JS8Call")
|
||||||
set (PROJECT_VENDOR "Jordan Sherer, KN4CRD")
|
set (PROJECT_VENDOR "Jordan Sherer, KN4CRD")
|
||||||
set (PROJECT_CONTACT "Jordan Sherer <kn4crd@gmail.com>")
|
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_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_HOMEPAGE https://groups.io/g/js8call)
|
||||||
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_SUMMARY_DESCRIPTION "${PROJECT_NAME} - Digital Modes for Weak Signal Communicaitons in Amateur Radio.")
|
set (PROJECT_SUMMARY_DESCRIPTION "${PROJECT_NAME} - Digital Modes for Weak Signal Communicaitons in Amateur Radio.")
|
||||||
set (PROJECT_DESCRIPTION "${PROJECT_SUMMARY_DESCRIPTION}
|
set (PROJECT_DESCRIPTION "${PROJECT_SUMMARY_DESCRIPTION}
|
||||||
${PROJECT_NAME} is a computer program designed to facilitate basic amateur
|
${PROJECT_NAME} is a computer program designed to facilitate amateur
|
||||||
radio communication using very weak signals. The first four letters in
|
radio communication using very weak signals.")
|
||||||
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.")
|
|
||||||
|
|
||||||
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake/Modules ${CMAKE_MODULE_PATH})
|
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_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_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_SOFT_KEYING "Apply a ramp to CW keying envelope to reduce transients." ON)
|
||||||
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation.")
|
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation." ON)
|
||||||
option (WSJT_GENERATE_DOCS "Generate documentation files." ON)
|
|
||||||
option (WSJT_RIG_NONE_CAN_SPLIT "Allow split operation with \"None\" as rig.")
|
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)
|
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
|
CMAKE_DEPENDENT_OPTION (WSJT_ENABLE_EXPERIMENTAL_FEATURES "Enable features not fully ready for public releases." ON
|
||||||
is_debug_build OFF)
|
is_debug_build OFF)
|
||||||
CMAKE_DEPENDENT_OPTION (WSJT_CREATE_WINMAIN
|
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 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
|
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
|
HintedSpinBox.cpp
|
||||||
RestrictedSpinBox.cpp
|
RestrictedSpinBox.cpp
|
||||||
HelpTextWindow.cpp
|
HelpTextWindow.cpp
|
||||||
SampleDownloader.cpp
|
|
||||||
SampleDownloader/DirectoryDelegate.cpp
|
|
||||||
SampleDownloader/Directory.cpp
|
|
||||||
SampleDownloader/FileNode.cpp
|
|
||||||
SampleDownloader/RemoteFile.cpp
|
|
||||||
DisplayManual.cpp
|
DisplayManual.cpp
|
||||||
MultiSettings.cpp
|
MultiSettings.cpp
|
||||||
MaidenheadLocatorValidator.cpp
|
MaidenheadLocatorValidator.cpp
|
||||||
@@ -268,16 +197,17 @@ set (wsjt_qtmm_CXXSRCS
|
|||||||
Audio/BWFFile.cpp
|
Audio/BWFFile.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set (jt9_FSRCS
|
set (js8_FSRCS
|
||||||
lib/jt9.f90
|
lib/jt9.f90
|
||||||
lib/jt9a.f90
|
lib/jt9a.f90
|
||||||
)
|
)
|
||||||
|
|
||||||
set (jt9_CXXSRCS
|
set (js8_CXXSRCS
|
||||||
lib/ipcomm.cpp
|
lib/ipcomm.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set (wsjtx_CXXSRCS
|
set (wsjtx_CXXSRCS
|
||||||
|
DriftingDateTime.cpp
|
||||||
logbook/adif.cpp
|
logbook/adif.cpp
|
||||||
logbook/countrydat.cpp
|
logbook/countrydat.cpp
|
||||||
logbook/countriesworked.cpp
|
logbook/countriesworked.cpp
|
||||||
@@ -304,6 +234,9 @@ set (wsjtx_CXXSRCS
|
|||||||
messageaveraging.cpp
|
messageaveraging.cpp
|
||||||
WsprTxScheduler.cpp
|
WsprTxScheduler.cpp
|
||||||
varicode.cpp
|
varicode.cpp
|
||||||
|
jsc.cpp
|
||||||
|
jsc_list.cpp
|
||||||
|
jsc_map.cpp
|
||||||
SelfDestructMessageBox.cpp
|
SelfDestructMessageBox.cpp
|
||||||
messagereplydialog.cpp
|
messagereplydialog.cpp
|
||||||
keyeater.cpp
|
keyeater.cpp
|
||||||
@@ -673,32 +606,6 @@ set (wsjtx_UISRCS
|
|||||||
messagereplydialog.ui
|
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
|
set (qcp_CXXSRCS
|
||||||
qcustomplot-source/qcustomplot.cpp
|
qcustomplot-source/qcustomplot.cpp
|
||||||
)
|
)
|
||||||
@@ -707,7 +614,7 @@ set (all_CXXSRCS
|
|||||||
${wsjt_CXXSRCS}
|
${wsjt_CXXSRCS}
|
||||||
${wsjt_qt_CXXSRCS}
|
${wsjt_qt_CXXSRCS}
|
||||||
${wsjt_qtmm_CXXSRCS}
|
${wsjt_qtmm_CXXSRCS}
|
||||||
${jt9_CXXSRCS}
|
${js8_CXXSRCS}
|
||||||
${wsjtx_CXXSRCS}
|
${wsjtx_CXXSRCS}
|
||||||
${qcp_CXXSRCS}
|
${qcp_CXXSRCS}
|
||||||
)
|
)
|
||||||
@@ -720,11 +627,8 @@ set (all_C_and_CXXSRCS
|
|||||||
)
|
)
|
||||||
|
|
||||||
set (TOP_LEVEL_RESOURCES
|
set (TOP_LEVEL_RESOURCES
|
||||||
shortcuts.txt
|
|
||||||
mouse_commands.txt
|
|
||||||
prefixes.txt
|
|
||||||
cty.dat
|
cty.dat
|
||||||
icons/Darwin/FT8Call.iconset/icon_128x128.png
|
icons/Darwin/JS8Call.iconset/icon_128x128.png
|
||||||
contrib/gpl-v3-logo.svg
|
contrib/gpl-v3-logo.svg
|
||||||
artwork/splash.png
|
artwork/splash.png
|
||||||
)
|
)
|
||||||
@@ -891,14 +795,6 @@ if (WIN32)
|
|||||||
find_package (Qt5AxContainer REQUIRED)
|
find_package (Qt5AxContainer REQUIRED)
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
#
|
|
||||||
# sub-directories
|
|
||||||
#
|
|
||||||
add_subdirectory (samples)
|
|
||||||
if (WSJT_GENERATE_DOCS)
|
|
||||||
add_subdirectory (doc)
|
|
||||||
endif (WSJT_GENERATE_DOCS)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Library building setup
|
# Library building setup
|
||||||
@@ -1146,7 +1042,7 @@ if (${OPENMP_FOUND} OR APPLE)
|
|||||||
if (APPLE)
|
if (APPLE)
|
||||||
# On Mac we don't have working OpenMP support in the C/C++
|
# 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
|
# 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
|
target_compile_options (wsjt_fort_omp
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<$<COMPILE_LANGUAGE:Fortran>:-fopenmp> # assumes GNU style Fortran compiler
|
$<$<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})
|
add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS})
|
||||||
target_link_libraries (wsjt_qtmm Qt5::Multimedia)
|
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)
|
add_executable (ldpcsim174 lib/ft8/ldpcsim174.f90 wsjtx.rc)
|
||||||
target_link_libraries (ldpcsim174 wsjt_fort wsjt_cxx)
|
target_link_libraries (ldpcsim174 wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
add_executable (ldpcsim144 lib/ldpcsim144.f90 wsjtx.rc)
|
add_executable (js8 ${js8_FSRCS} ${js8_CXXSRCS} 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)
|
|
||||||
if (${OPENMP_FOUND} OR APPLE)
|
if (${OPENMP_FOUND} OR APPLE)
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# On Mac we don't have working OpenMP support in the C/C++
|
# On Mac we don't have working OpenMP support in the C/C++
|
||||||
# compilers so we have to manually set the correct linking flags
|
# compilers so we have to manually set the correct linking flags
|
||||||
# and libraries to get OpenMP support in jt9.
|
# and libraries to get OpenMP support in js8.
|
||||||
set_target_properties (jt9
|
set_target_properties (js8
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||||
LINK_LIBRARIES "gomp;gcc_s.1" # assume GNU libgcc OpenMP
|
LINK_LIBRARIES "gomp;gcc_s.1" # assume GNU libgcc OpenMP
|
||||||
)
|
)
|
||||||
target_compile_options (jt9
|
target_compile_options (js8
|
||||||
PRIVATE
|
PRIVATE
|
||||||
$<$<COMPILE_LANGUAGE:Fortran>:-fopenmp> # assumes GNU style Fortran compiler
|
$<$<COMPILE_LANGUAGE:Fortran>:-fopenmp> # assumes GNU style Fortran compiler
|
||||||
)
|
)
|
||||||
else (APPLE)
|
else (APPLE)
|
||||||
if (OpenMP_C_FLAGS)
|
if (OpenMP_C_FLAGS)
|
||||||
set_target_properties (jt9
|
set_target_properties (js8
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
||||||
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
set_target_properties (jt9
|
set_target_properties (js8
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||||
)
|
)
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# set_target_properties (jt9 PROPERTIES
|
# set_target_properties (js8 PROPERTIES
|
||||||
# LINK_FLAGS -Wl,--stack,16777216
|
# LINK_FLAGS -Wl,--stack,16777216
|
||||||
# )
|
# )
|
||||||
endif ()
|
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)
|
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)
|
endif (${OPENMP_FOUND} OR APPLE)
|
||||||
|
|
||||||
# build the main application
|
# build the main application
|
||||||
add_executable (ft8call MACOSX_BUNDLE
|
add_executable (js8call MACOSX_BUNDLE
|
||||||
${wsjtx_CXXSRCS}
|
${wsjtx_CXXSRCS}
|
||||||
${wsjtx_GENUISRCS}
|
${wsjtx_GENUISRCS}
|
||||||
wsjtx.rc
|
wsjtx.rc
|
||||||
@@ -1314,10 +1119,10 @@ add_executable (ft8call MACOSX_BUNDLE
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (WSJT_CREATE_WINMAIN)
|
if (WSJT_CREATE_WINMAIN)
|
||||||
set_target_properties (ft8call PROPERTIES WIN32_EXECUTABLE ON)
|
set_target_properties (js8call PROPERTIES WIN32_EXECUTABLE ON)
|
||||||
endif (WSJT_CREATE_WINMAIN)
|
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_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Darwin/Info.plist.in"
|
||||||
MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
|
MACOSX_BUNDLE_INFO_STRING "${WSJTX_DESCRIPTION_SUMMARY}"
|
||||||
MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
|
MACOSX_BUNDLE_ICON_FILE "${WSJTX_ICON_FILE}"
|
||||||
@@ -1330,113 +1135,49 @@ set_target_properties (ft8call PROPERTIES
|
|||||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.k1jt.wsjtx"
|
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)
|
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 ()
|
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)
|
if (OpenMP_C_FLAGS)
|
||||||
set_target_properties (ft8call PROPERTIES
|
set_target_properties (js8call PROPERTIES
|
||||||
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
COMPILE_FLAGS "${OpenMP_C_FLAGS}"
|
||||||
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
LINK_FLAGS "${OpenMP_C_FLAGS}"
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
set_target_properties (ft8call PROPERTIES
|
set_target_properties (js8call PROPERTIES
|
||||||
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/fortran_modules_omp
|
||||||
)
|
)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
set_target_properties (ft8call PROPERTIES
|
set_target_properties (js8call PROPERTIES
|
||||||
LINK_FLAGS -Wl,--stack,16777216
|
LINK_FLAGS -Wl,--stack,16777216
|
||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
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
|
# if (UNIX)
|
||||||
# add_library (wsjtx_udp SHARED ${UDP_library_CXXSRCS})
|
# if (NOT WSJT_SKIP_MANPAGES)
|
||||||
add_library (wsjtx_udp-static STATIC ${UDP_library_CXXSRCS})
|
# add_subdirectory (manpages)
|
||||||
#target_include_directories (wsjtx_udp
|
# add_dependencies (js8call manpages)
|
||||||
# INTERFACE
|
# endif (NOT WSJT_SKIP_MANPAGES)
|
||||||
# $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/wsjtx>
|
# if (NOT APPLE)
|
||||||
# )
|
# add_subdirectory (debian)
|
||||||
target_include_directories (wsjtx_udp-static
|
# add_dependencies (js8call debian)
|
||||||
INTERFACE
|
# endif (NOT APPLE)
|
||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/wsjtx>
|
# endif (UNIX)
|
||||||
)
|
|
||||||
#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)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# installation
|
# installation
|
||||||
#
|
#
|
||||||
install (TARGETS ft8call
|
install (TARGETS js8call
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||||
BUNDLE DESTINATION . COMPONENT runtime
|
BUNDLE DESTINATION . COMPONENT runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
# install (TARGETS wsjtx_udp EXPORT udp
|
install (TARGETS js8 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||||
# 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
|
|
||||||
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1444,24 +1185,21 @@ install (PROGRAMS
|
|||||||
${RIGCTL_EXE}
|
${RIGCTL_EXE}
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
RENAME rigctl-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
|
RENAME rigctl-local${CMAKE_EXECUTABLE_SUFFIX}
|
||||||
)
|
)
|
||||||
|
|
||||||
install (PROGRAMS
|
install (PROGRAMS
|
||||||
${RIGCTLD_EXE}
|
${RIGCTLD_EXE}
|
||||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
RENAME rigctld-wsjtx${CMAKE_EXECUTABLE_SUFFIX}
|
RENAME rigctld-local${CMAKE_EXECUTABLE_SUFFIX}
|
||||||
)
|
)
|
||||||
|
|
||||||
install (FILES
|
install (FILES
|
||||||
README
|
README
|
||||||
COPYING
|
COPYING
|
||||||
AUTHORS
|
|
||||||
THANKS
|
|
||||||
NEWS
|
|
||||||
INSTALL
|
INSTALL
|
||||||
BUGS
|
INSTALL-WSJTX
|
||||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
)
|
)
|
||||||
@@ -1508,7 +1246,7 @@ add_custom_target (uninstall
|
|||||||
# explicitly say that the wsjt_qt depends on custom target, this is
|
# explicitly say that the wsjt_qt depends on custom target, this is
|
||||||
# done indirectly so that the revisiontag target gets built exactly
|
# done indirectly so that the revisiontag target gets built exactly
|
||||||
# once per build
|
# once per build
|
||||||
add_dependencies(wsjt_qt revisiontag)
|
# add_dependencies(wsjt_qt)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -1521,23 +1259,23 @@ configure_file (
|
|||||||
|
|
||||||
|
|
||||||
if (NOT WIN32 AND NOT APPLE)
|
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
|
# menu with an icon
|
||||||
install (
|
install (
|
||||||
FILES ft8call.desktop
|
FILES js8call.desktop
|
||||||
DESTINATION /usr/share/applications
|
DESTINATION /usr/share/applications
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
)
|
)
|
||||||
install (
|
install (
|
||||||
FILES icons/Unix/ft8call_icon.png
|
FILES icons/Unix/js8call_icon.png
|
||||||
DESTINATION /usr/share/pixmaps
|
DESTINATION /usr/share/pixmaps
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
)
|
)
|
||||||
|
|
||||||
execute_process(COMMAND ln -s /opt/ft8call/bin/ft8call lft8call)
|
execute_process(COMMAND ln -s /opt/js8call/bin/js8call ljs8call)
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
${CMAKE_BINARY_DIR}/lft8call DESTINATION /usr/bin/ RENAME ft8call
|
${CMAKE_BINARY_DIR}/ljs8call DESTINATION /usr/bin/ RENAME js8call
|
||||||
#COMPONENT runtime
|
#COMPONENT runtime
|
||||||
)
|
)
|
||||||
endif (NOT WIN32 AND NOT APPLE)
|
endif (NOT WIN32 AND NOT APPLE)
|
||||||
|
|||||||
@@ -626,6 +626,7 @@ private:
|
|||||||
bool spot_to_reporting_networks_;
|
bool spot_to_reporting_networks_;
|
||||||
bool transmit_directed_;
|
bool transmit_directed_;
|
||||||
bool autoreply_off_at_startup_;
|
bool autoreply_off_at_startup_;
|
||||||
|
bool beacon_anywhere_;
|
||||||
bool relay_disabled_;
|
bool relay_disabled_;
|
||||||
bool monitor_off_at_startup_;
|
bool monitor_off_at_startup_;
|
||||||
bool monitor_last_used_;
|
bool monitor_last_used_;
|
||||||
@@ -756,6 +757,7 @@ void Configuration::set_spot_to_reporting_networks (bool spot)
|
|||||||
|
|
||||||
bool Configuration::transmit_directed() const { return m_->transmit_directed_; }
|
bool Configuration::transmit_directed() const { return m_->transmit_directed_; }
|
||||||
bool Configuration::autoreply_off_at_startup () const {return m_->autoreply_off_at_startup_;}
|
bool Configuration::autoreply_off_at_startup () const {return m_->autoreply_off_at_startup_;}
|
||||||
|
bool Configuration::beacon_anywhere() const { return m_->beacon_anywhere_;}
|
||||||
bool Configuration::relay_off() const { return m_->relay_disabled_; }
|
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_off_at_startup () const {return m_->monitor_off_at_startup_;}
|
||||||
bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;}
|
bool Configuration::monitor_last_used () const {return m_->rig_is_dummy_ || m_->monitor_last_used_;}
|
||||||
@@ -1329,6 +1331,7 @@ void Configuration::impl::initialize_models ()
|
|||||||
ui_->psk_reporter_check_box->setChecked (spot_to_reporting_networks_);
|
ui_->psk_reporter_check_box->setChecked (spot_to_reporting_networks_);
|
||||||
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
ui_->transmit_directed_check_box->setChecked(transmit_directed_);
|
||||||
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
ui_->autoreply_off_check_box->setChecked (autoreply_off_at_startup_);
|
||||||
|
ui_->beacon_anywhere_check_box->setChecked(beacon_anywhere_);
|
||||||
ui_->relay_disabled_check_box->setChecked(relay_disabled_);
|
ui_->relay_disabled_check_box->setChecked(relay_disabled_);
|
||||||
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
ui_->monitor_off_check_box->setChecked (monitor_off_at_startup_);
|
||||||
ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
|
ui_->monitor_last_used_check_box->setChecked (monitor_last_used_);
|
||||||
@@ -1336,16 +1339,12 @@ void Configuration::impl::initialize_models ()
|
|||||||
ui_->stations_table_view->setEnabled(ui_->auto_switch_bands_check_box->isChecked());
|
ui_->stations_table_view->setEnabled(ui_->auto_switch_bands_check_box->isChecked());
|
||||||
ui_->report_in_comments_check_box->setChecked (report_in_comments_);
|
ui_->report_in_comments_check_box->setChecked (report_in_comments_);
|
||||||
ui_->prompt_to_log_check_box->setChecked (prompt_to_log_);
|
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_->clear_DX_check_box->setChecked (clear_DX_);
|
||||||
ui_->miles_check_box->setChecked (miles_);
|
ui_->miles_check_box->setChecked (miles_);
|
||||||
ui_->quick_call_check_box->setChecked (quick_call_);
|
ui_->quick_call_check_box->setChecked (quick_call_);
|
||||||
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
||||||
ui_->beacon_spin_box->setValue (beacon_);
|
ui_->beacon_spin_box->setValue (beacon_);
|
||||||
ui_->tx_watchdog_spin_box->setValue (watchdog_);
|
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_->enable_VHF_features_check_box->setChecked(enable_VHF_features_);
|
||||||
ui_->decode_at_52s_check_box->setChecked(decode_at_52s_);
|
ui_->decode_at_52s_check_box->setChecked(decode_at_52s_);
|
||||||
ui_->single_decode_check_box->setChecked(single_decode_);
|
ui_->single_decode_check_box->setChecked(single_decode_);
|
||||||
@@ -1589,6 +1588,7 @@ void Configuration::impl::read_settings ()
|
|||||||
|
|
||||||
transmit_directed_ = settings_->value ("TransmitDirected", true).toBool();
|
transmit_directed_ = settings_->value ("TransmitDirected", true).toBool();
|
||||||
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
autoreply_off_at_startup_ = settings_->value ("AutoreplyOFF", false).toBool ();
|
||||||
|
beacon_anywhere_ = settings_->value("BeaconAnywhere", false).toBool();
|
||||||
relay_disabled_ = settings_->value ("RelayOFF", false).toBool ();
|
relay_disabled_ = settings_->value ("RelayOFF", false).toBool ();
|
||||||
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
monitor_off_at_startup_ = settings_->value ("MonitorOFF", false).toBool ();
|
||||||
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
monitor_last_used_ = settings_->value ("MonitorLastUsed", false).toBool ();
|
||||||
@@ -1753,6 +1753,7 @@ void Configuration::impl::write_settings ()
|
|||||||
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
|
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
|
||||||
settings_->setValue ("TransmitDirected", transmit_directed_);
|
settings_->setValue ("TransmitDirected", transmit_directed_);
|
||||||
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
settings_->setValue ("AutoreplyOFF", autoreply_off_at_startup_);
|
||||||
|
settings_->setValue ("BeaconAnywhere", beacon_anywhere_);
|
||||||
settings_->setValue ("RelayOFF", relay_disabled_);
|
settings_->setValue ("RelayOFF", relay_disabled_);
|
||||||
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
settings_->setValue ("MonitorOFF", monitor_off_at_startup_);
|
||||||
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
settings_->setValue ("MonitorLastUsed", monitor_last_used_);
|
||||||
@@ -2216,6 +2217,7 @@ void Configuration::impl::accept ()
|
|||||||
tx_qsy_allowed_ = ui_->tx_qsy_check_box->isChecked ();
|
tx_qsy_allowed_ = ui_->tx_qsy_check_box->isChecked ();
|
||||||
transmit_directed_ = ui_->transmit_directed_check_box->isChecked();
|
transmit_directed_ = ui_->transmit_directed_check_box->isChecked();
|
||||||
autoreply_off_at_startup_ = ui_->autoreply_off_check_box->isChecked ();
|
autoreply_off_at_startup_ = ui_->autoreply_off_check_box->isChecked ();
|
||||||
|
beacon_anywhere_ = ui_->beacon_anywhere_check_box->isChecked();
|
||||||
relay_disabled_ = ui_->relay_disabled_check_box->isChecked();
|
relay_disabled_ = ui_->relay_disabled_check_box->isChecked();
|
||||||
monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
|
monitor_off_at_startup_ = ui_->monitor_off_check_box->isChecked ();
|
||||||
monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
|
monitor_last_used_ = ui_->monitor_last_used_check_box->isChecked ();
|
||||||
@@ -2223,16 +2225,12 @@ void Configuration::impl::accept ()
|
|||||||
log_as_DATA_ = ui_->log_as_RTTY_check_box->isChecked ();
|
log_as_DATA_ = ui_->log_as_RTTY_check_box->isChecked ();
|
||||||
report_in_comments_ = ui_->report_in_comments_check_box->isChecked ();
|
report_in_comments_ = ui_->report_in_comments_check_box->isChecked ();
|
||||||
prompt_to_log_ = ui_->prompt_to_log_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 ();
|
clear_DX_ = ui_->clear_DX_check_box->isChecked ();
|
||||||
miles_ = ui_->miles_check_box->isChecked ();
|
miles_ = ui_->miles_check_box->isChecked ();
|
||||||
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
||||||
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
||||||
beacon_ = ui_->beacon_spin_box->value ();
|
beacon_ = ui_->beacon_spin_box->value ();
|
||||||
watchdog_ = ui_->tx_watchdog_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 ());
|
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
|
||||||
save_directory_ = ui_->save_path_display_label->text ();
|
save_directory_ = ui_->save_path_display_label->text ();
|
||||||
azel_directory_ = ui_->azel_path_display_label->text ();
|
azel_directory_ = ui_->azel_path_display_label->text ();
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ public:
|
|||||||
void set_spot_to_reporting_networks (bool);
|
void set_spot_to_reporting_networks (bool);
|
||||||
bool transmit_directed() const;
|
bool transmit_directed() const;
|
||||||
bool autoreply_off_at_startup () const;
|
bool autoreply_off_at_startup () const;
|
||||||
|
bool beacon_anywhere() const;
|
||||||
bool relay_off() const;
|
bool relay_off() const;
|
||||||
bool monitor_off_at_startup () const;
|
bool monitor_off_at_startup () const;
|
||||||
bool monitor_last_used () const;
|
bool monitor_last_used () const;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>692</width>
|
<width>750</width>
|
||||||
<height>735</height>
|
<height>735</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@@ -251,31 +251,12 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="display_group_box">
|
<widget class="QGroupBox" name="behaviour_group_box">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Display</string>
|
<string>Behavior</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_4">
|
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||||
<item row="4" column="0">
|
<item>
|
||||||
<widget class="QCheckBox" name="DXCC_check_box">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="visible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Show if decoded stations are new DXCC entities or worked before.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Show &DXCC entity and worked before status</string>
|
|
||||||
</property>
|
|
||||||
<property name="checked">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="miles_check_box">
|
<widget class="QCheckBox" name="miles_check_box">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@@ -288,67 +269,6 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="TX_messages_check_box">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="visible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Show outgoing transmitted messages in the Rx frequency window.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Tx messages to Rx frequency window</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QCheckBox" name="ppfx_check_box">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="visible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Show principal prefix instead of country name</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QCheckBox" name="insert_blank_check_box">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="visible">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Include a separator line between periods in the band activity window.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Blank line between decoding periods</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line_7">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="behaviour_group_box">
|
|
||||||
<property name="title">
|
|
||||||
<string>Behavior</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="autoreply_off_check_box">
|
<widget class="QCheckBox" name="autoreply_off_check_box">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -422,6 +342,13 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="beacon_anywhere_check_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow transmitting beacons outside of beacon sub-channel (500Hz - 1000Hz)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_155">
|
<layout class="QHBoxLayout" name="horizontalLayout_155">
|
||||||
<item>
|
<item>
|
||||||
@@ -530,10 +457,10 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>15</number>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>60</number>
|
<number>1440</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<number>1</number>
|
<number>1</number>
|
||||||
@@ -550,7 +477,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label_10">
|
<widget class="QLabel" name="label_10">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Tx watchdog:</string>
|
<string>Idle watchdog timer:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>tx_watchdog_spin_box</cstring>
|
<cstring>tx_watchdog_spin_box</cstring>
|
||||||
@@ -560,7 +487,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="tx_watchdog_spin_box">
|
<widget class="QSpinBox" name="tx_watchdog_spin_box">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Number of minutes before unattended transmissions are aborted</p></body></html></string>
|
<string><html><head/><body><p>Number of minutes before unattended transmissions are aborted (beacons, auto-replies, etc).</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="specialValueText">
|
<property name="specialValueText">
|
||||||
<string>Disabled</string>
|
<string>Disabled</string>
|
||||||
@@ -571,8 +498,11 @@
|
|||||||
<property name="prefix">
|
<property name="prefix">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>1440</number>
|
||||||
|
</property>
|
||||||
<property name="singleStep">
|
<property name="singleStep">
|
||||||
<number>5</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@@ -1573,7 +1503,7 @@ radio interface behave as expected.</string>
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="audio_tab">
|
<widget class="QWidget" name="audio_tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>A&udio</string>
|
<string>&Audio</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="toolTip">
|
<attribute name="toolTip">
|
||||||
<string>Audio interface settings</string>
|
<string>Audio interface settings</string>
|
||||||
@@ -1844,88 +1774,6 @@ both here.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tx_macros_tab">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Tx &Macros</string>
|
|
||||||
</attribute>
|
|
||||||
<attribute name="toolTip">
|
|
||||||
<string>Canned free text messages setup</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QPushButton" name="add_macro_push_button">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Add</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLineEdit" name="add_macro_line_edit"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QPushButton" name="delete_macro_push_button">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Delete</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="3">
|
|
||||||
<widget class="QListView" name="macros_list_view">
|
|
||||||
<property name="contextMenuPolicy">
|
|
||||||
<enum>Qt::ActionsContextMenu</enum>
|
|
||||||
</property>
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Drag and drop items to rearrange order
|
|
||||||
Right click for item specific actions
|
|
||||||
Click, SHIFT+Click and, CRTL+Click to select items</string>
|
|
||||||
</property>
|
|
||||||
<property name="styleSheet">
|
|
||||||
<string notr="true">QListView {
|
|
||||||
show-decoration-selected: 1; /* make the selection span the entire width of the view */
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::item:alternate {
|
|
||||||
background: #EEEEEE;
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::item:selected {
|
|
||||||
border: 1px solid #6a6ea9;
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::item:selected:!active {
|
|
||||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
|
||||||
stop: 0 #ABAFE5, stop: 1 #8588B2);
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::item:selected:active {
|
|
||||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
|
||||||
stop: 0 #6a6ea9, stop: 1 #888dd9);
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::item:hover {
|
|
||||||
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
|
||||||
stop: 0 #FAFBFE, stop: 1 #DCDEF1);
|
|
||||||
}</string>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::InternalMove</enum>
|
|
||||||
</property>
|
|
||||||
<property name="defaultDropAction">
|
|
||||||
<enum>Qt::MoveAction</enum>
|
|
||||||
</property>
|
|
||||||
<property name="alternatingRowColors">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="selectionMode">
|
|
||||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
|
||||||
</property>
|
|
||||||
<property name="uniformItemSizes">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="reporting_tab">
|
<widget class="QWidget" name="reporting_tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Reportin&g</string>
|
<string>Reportin&g</string>
|
||||||
@@ -2340,7 +2188,7 @@ for assessing propagation and system performance.</string>
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="frequencies_tab">
|
<widget class="QWidget" name="frequencies_tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Frequencies</string>
|
<string>&Frequencies</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<attribute name="toolTip">
|
<attribute name="toolTip">
|
||||||
<string>Default frequencies and band specific station details setup</string>
|
<string>Default frequencies and band specific station details setup</string>
|
||||||
@@ -2572,9 +2420,91 @@ Right click for insert and delete options.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="tx_macros_tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Saved &Messages</string>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="toolTip">
|
||||||
|
<string>Canned free text messages setup</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QPushButton" name="add_macro_push_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLineEdit" name="add_macro_line_edit"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QPushButton" name="delete_macro_push_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>&Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0" colspan="3">
|
||||||
|
<widget class="QListView" name="macros_list_view">
|
||||||
|
<property name="contextMenuPolicy">
|
||||||
|
<enum>Qt::ActionsContextMenu</enum>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Drag and drop items to rearrange order
|
||||||
|
Right click for item specific actions
|
||||||
|
Click, SHIFT+Click and, CRTL+Click to select items</string>
|
||||||
|
</property>
|
||||||
|
<property name="styleSheet">
|
||||||
|
<string notr="true">QListView {
|
||||||
|
show-decoration-selected: 1; /* make the selection span the entire width of the view */
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::item:alternate {
|
||||||
|
background: #EEEEEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::item:selected {
|
||||||
|
border: 1px solid #6a6ea9;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::item:selected:!active {
|
||||||
|
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||||
|
stop: 0 #ABAFE5, stop: 1 #8588B2);
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::item:selected:active {
|
||||||
|
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||||
|
stop: 0 #6a6ea9, stop: 1 #888dd9);
|
||||||
|
}
|
||||||
|
|
||||||
|
QListView::item:hover {
|
||||||
|
background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
|
||||||
|
stop: 0 #FAFBFE, stop: 1 #DCDEF1);
|
||||||
|
}</string>
|
||||||
|
</property>
|
||||||
|
<property name="dragDropMode">
|
||||||
|
<enum>QAbstractItemView::InternalMove</enum>
|
||||||
|
</property>
|
||||||
|
<property name="defaultDropAction">
|
||||||
|
<enum>Qt::MoveAction</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
|
</property>
|
||||||
|
<property name="uniformItemSizes">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Notifications</string>
|
<string>&Notifications</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_16">
|
<layout class="QVBoxLayout" name="verticalLayout_16">
|
||||||
<item>
|
<item>
|
||||||
@@ -2724,7 +2654,7 @@ Right click for insert and delete options.</string>
|
|||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="colors_tab">
|
<widget class="QWidget" name="colors_tab">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>UI</string>
|
<string>&UI</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_15">
|
<layout class="QVBoxLayout" name="verticalLayout_15">
|
||||||
<item>
|
<item>
|
||||||
@@ -2845,7 +2775,7 @@ Right click for insert and delete options.</string>
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Directed Message Color</string>
|
<string>Directed Messages Background</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -2858,7 +2788,7 @@ Right click for insert and delete options.</string>
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>CQ Color</string>
|
<string>CQ Background</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@@ -3549,10 +3479,6 @@ soundcard changes</string>
|
|||||||
<tabstop>use_dynamic_grid</tabstop>
|
<tabstop>use_dynamic_grid</tabstop>
|
||||||
<tabstop>region_combo_box</tabstop>
|
<tabstop>region_combo_box</tabstop>
|
||||||
<tabstop>type_2_msg_gen_combo_box</tabstop>
|
<tabstop>type_2_msg_gen_combo_box</tabstop>
|
||||||
<tabstop>insert_blank_check_box</tabstop>
|
|
||||||
<tabstop>miles_check_box</tabstop>
|
|
||||||
<tabstop>TX_messages_check_box</tabstop>
|
|
||||||
<tabstop>DXCC_check_box</tabstop>
|
|
||||||
<tabstop>monitor_off_check_box</tabstop>
|
<tabstop>monitor_off_check_box</tabstop>
|
||||||
<tabstop>monitor_last_used_check_box</tabstop>
|
<tabstop>monitor_last_used_check_box</tabstop>
|
||||||
<tabstop>quick_call_check_box</tabstop>
|
<tabstop>quick_call_check_box</tabstop>
|
||||||
@@ -3689,12 +3615,12 @@ soundcard changes</string>
|
|||||||
</connection>
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
<buttongroups>
|
<buttongroups>
|
||||||
|
<buttongroup name="CAT_handshake_button_group"/>
|
||||||
|
<buttongroup name="CAT_data_bits_button_group"/>
|
||||||
|
<buttongroup name="split_mode_button_group"/>
|
||||||
|
<buttongroup name="TX_audio_source_button_group"/>
|
||||||
|
<buttongroup name="TX_mode_button_group"/>
|
||||||
<buttongroup name="PTT_method_button_group"/>
|
<buttongroup name="PTT_method_button_group"/>
|
||||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
<buttongroup name="CAT_stop_bits_button_group"/>
|
||||||
<buttongroup name="TX_audio_source_button_group"/>
|
|
||||||
<buttongroup name="CAT_data_bits_button_group"/>
|
|
||||||
<buttongroup name="TX_mode_button_group"/>
|
|
||||||
<buttongroup name="split_mode_button_group"/>
|
|
||||||
<buttongroup name="CAT_handshake_button_group"/>
|
|
||||||
</buttongroups>
|
</buttongroups>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "NetworkServerLookup.hpp"
|
#include "NetworkServerLookup.hpp"
|
||||||
|
|
||||||
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
|
#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>");
|
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
|
||||||
|
|
||||||
bool tx {!on};
|
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
|
// we must now wait for Tx on the rig, we will wait a short while
|
||||||
// before bailing out
|
// 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>");
|
auto reply = command_with_reply ("<command:9>CmdSendTx<parameters:0>");
|
||||||
if (0 == reply.indexOf ("<CmdTX:"))
|
if (0 == reply.indexOf ("<CmdTX:"))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
WSJTX_BUNDLE="`echo "$0" | sed -e 's/\/Contents\/MacOS\/.*//'`"
|
WSJTX_BUNDLE="`echo "$0" | sed -e 's/\/Contents\/MacOS\/.*//'`"
|
||||||
WSJTX_RESOURCES="$WSJTX_BUNDLE/Contents/Resources"
|
WSJTX_RESOURCES="$WSJTX_BUNDLE/Contents/Resources"
|
||||||
WSJTX_TEMP="/tmp/ft8call/$UID"
|
WSJTX_TEMP="/tmp/js8call/$UID"
|
||||||
|
|
||||||
echo "running $0"
|
echo "running $0"
|
||||||
echo "WSJTX_BUNDLE: $WSJTX_BUNDLE"
|
echo "WSJTX_BUNDLE: $WSJTX_BUNDLE"
|
||||||
@@ -13,4 +13,4 @@ export "DYLD_LIBRARY_PATH=$WSJTX_RESOURCES/lib"
|
|||||||
export "PATH=$WSJTX_RESOURCES/bin:$PATH"
|
export "PATH=$WSJTX_RESOURCES/bin:$PATH"
|
||||||
|
|
||||||
#export
|
#export
|
||||||
exec "$WSJTX_RESOURCES/bin/ft8call"
|
exec "$WSJTX_RESOURCES/bin/js8call"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
Notes on FT8Call Installation for Mac OS X
|
Notes on JS8Call Installation for Mac OS X
|
||||||
(replace all instances of WSJT-X with FT8Call)
|
(replace all instances of WSJT-X with JS8Call)
|
||||||
|
|
||||||
Notes on WSJT-X Installation for Mac OS X
|
Notes on WSJT-X Installation for Mac OS X
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "moc_Detector.cpp"
|
#include "moc_Detector.cpp"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -42,7 +44,7 @@ bool Detector::reset ()
|
|||||||
void Detector::clear ()
|
void Detector::clear ()
|
||||||
{
|
{
|
||||||
// set index to roughly where we are in time (1ms resolution)
|
// 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));
|
// 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 = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
|
||||||
dec_data.params.kin = 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
|
// we take the time of the data as the following assuming no latency
|
||||||
// delivering it to us (not true but close enough for us)
|
// delivering it to us (not true but close enough for us)
|
||||||
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
qint64 now (DriftingDateTime::currentMSecsSinceEpoch ());
|
||||||
|
|
||||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
unsigned secondInToday ((now % 86400000LL) / 1000);
|
||||||
return secondInToday % m_period;
|
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 =
|
FrequencyList_v2::FrequencyItems const default_frequency_list =
|
||||||
{
|
{
|
||||||
{ 1842000, Modes::FT8CALL, IARURegions::ALL}, // 2 above
|
{ 1842000, Modes::JS8, IARURegions::ALL}, // 2 above
|
||||||
{ 3578000, Modes::FT8CALL, IARURegions::ALL}, // 5 above
|
{ 3578000, Modes::JS8, IARURegions::ALL}, // 5 above
|
||||||
{ 7078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
{ 7078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||||
{10130000, Modes::FT8CALL, IARURegions::ALL}, // 6 below
|
{10130000, Modes::JS8, IARURegions::ALL}, // 6 below
|
||||||
{14078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
{14078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||||
{18104000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
{18104000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||||
{21078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
{21078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||||
{24922000, Modes::FT8CALL, IARURegions::ALL}, // 9 above
|
{24922000, Modes::JS8, IARURegions::ALL}, // 9 above
|
||||||
{28078000, Modes::FT8CALL, IARURegions::ALL}, // 4 above
|
{28078000, Modes::JS8, IARURegions::ALL}, // 4 above
|
||||||
{50318000, Modes::FT8CALL, IARURegions::ALL}, // 5 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 @@
|
|||||||
|
Detailed instructions coming soon. In the meantime, follow INSTALL-WSJTX instructions, substituting the WSJT-X repository with this one.
|
||||||
__ __ ______ _____ ________ __ __
|
|
||||||
| \ _ | \ / \ | \| \ | \ | \
|
|
||||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
|
||||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
|
||||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
|
||||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
|
||||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
|
||||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
|
||||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|||||||
@@ -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 <QHostAddress>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "pimpl_impl.hpp"
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
#include "moc_MessageClient.cpp"
|
#include "moc_MessageClient.cpp"
|
||||||
@@ -232,7 +234,7 @@ void MessageClient::impl::heartbeat ()
|
|||||||
Message m("PING", "", QMap<QString, QVariant>{
|
Message m("PING", "", QMap<QString, QVariant>{
|
||||||
{"NAME", QVariant(QApplication::applicationName())},
|
{"NAME", QVariant(QApplication::applicationName())},
|
||||||
{"VERSION", QVariant(QApplication::applicationVersion())},
|
{"VERSION", QVariant(QApplication::applicationVersion())},
|
||||||
{"UTC", QVariant(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch())}
|
{"UTC", QVariant(DriftingDateTime::currentDateTimeUtc().toMSecsSinceEpoch())}
|
||||||
});
|
});
|
||||||
writeDatagram (m.toJson(), server_, server_port_);
|
writeDatagram (m.toJson(), server_, server_port_);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
|
||||||
#include "udp_export.h"
|
|
||||||
#include "Radio.hpp"
|
#include "Radio.hpp"
|
||||||
|
|
||||||
#include "pimpl_h.hpp"
|
#include "pimpl_h.hpp"
|
||||||
@@ -23,7 +22,7 @@ class QString;
|
|||||||
// applications that use the Qt framework. Other applications should
|
// applications that use the Qt framework. Other applications should
|
||||||
// use this classes' implementation as a reference implementation.
|
// use this classes' implementation as a reference implementation.
|
||||||
//
|
//
|
||||||
class UDP_EXPORT MessageServer
|
class MessageServer
|
||||||
: public QObject
|
: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
@@ -96,7 +95,7 @@ public:
|
|||||||
Q_SIGNAL void error (QString const&) const;
|
Q_SIGNAL void error (QString const&) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class UDP_NO_EXPORT impl;
|
class impl;
|
||||||
pimpl<impl> m_;
|
pimpl<impl> m_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace
|
|||||||
char const * const mode_names[] =
|
char const * const mode_names[] =
|
||||||
{
|
{
|
||||||
"All",
|
"All",
|
||||||
"FT8CALL",
|
"JS8",
|
||||||
};
|
};
|
||||||
std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]);
|
std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public:
|
|||||||
enum Mode
|
enum Mode
|
||||||
{
|
{
|
||||||
ALL, // matches with all modes
|
ALL, // matches with all modes
|
||||||
FT8CALL,
|
JS8,
|
||||||
MODES_END_SENTINAL_AND_COUNT // this must be last
|
MODES_END_SENTINAL_AND_COUNT // this must be last
|
||||||
};
|
};
|
||||||
Q_ENUM (Mode)
|
Q_ENUM (Mode)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include "soundout.h"
|
#include "soundout.h"
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "moc_Modulator.cpp"
|
#include "moc_Modulator.cpp"
|
||||||
|
|
||||||
extern float gran(); // Noise generator (for tests only)
|
extern float gran(); // Noise generator (for tests only)
|
||||||
@@ -49,7 +51,7 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||||||
{
|
{
|
||||||
Q_ASSERT (stream);
|
Q_ASSERT (stream);
|
||||||
// Time according to this computer which becomes our base time
|
// Time according to this computer which becomes our base time
|
||||||
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
qint64 ms0 = DriftingDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||||
|
|
||||||
if (m_state != Idle)
|
if (m_state != Idle)
|
||||||
{
|
{
|
||||||
@@ -176,7 +178,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
if(m_TRperiod==3) slowCwId=false;
|
if(m_TRperiod==3) slowCwId=false;
|
||||||
bool fastCwId=false;
|
bool fastCwId=false;
|
||||||
static bool bCwId=false;
|
static bool bCwId=false;
|
||||||
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
qint64 ms = DriftingDateTime::currentMSecsSinceEpoch();
|
||||||
float tsec=0.001*(ms % (1000*m_TRperiod));
|
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 and (icw[0]>0) and (tsec>(m_TRperiod-5.0))) fastCwId=true;
|
||||||
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
|
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;
|
if (m_ic > i1) m_amp = 0.0;
|
||||||
|
|
||||||
sample=qRound(m_amp*qSin(m_phi));
|
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);
|
samples = load(postProcessSample(sample), samples);
|
||||||
++framesGenerated;
|
++framesGenerated;
|
||||||
++m_ic;
|
++m_ic;
|
||||||
|
|||||||
@@ -1,80 +1,35 @@
|
|||||||
|
# JS8Call
|
||||||
|
|
||||||
__ __ ______ _____ ________ __ __
|
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.
|
||||||
| \ _ | \ / \ | \| \ | \ | \
|
|
||||||
| $$ / \ | $$| $$$$$$\ \$$$$$ \$$$$$$$$ | $$ | $$
|
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.
|
||||||
| $$/ $\| $$| $$___\$$ | $$ | $$ ______ \$$\/ $$
|
|
||||||
| $$ $$$\ $$ \$$ \ __ | $$ | $$| \ >$$ $$
|
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.
|
||||||
| $$ $$\$$\$$ _\$$$$$$\| \ | $$ | $$ \$$$$$$/ $$$$\
|
|
||||||
| $$$$ \$$$$| \__| $$| $$__| $$ | $$ | $$ \$$\
|
* Read more on the original design inspiration here: https://github.com/jsherer/js8call
|
||||||
| $$$ \$$$ \$$ $$ \$$ $$ | $$ | $$ | $$
|
|
||||||
\$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$
|
* For release announcements and discussion, join the JS8Call mailing list here: https://groups.io/g/js8call
|
||||||
|
|
||||||
|
|
||||||
|
# Notice
|
||||||
|
|
||||||
Copyright (C) 2001 - 2017 by Joe Taylor, K1JT.
|
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/
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
Project web site:
|
# History
|
||||||
|
|
||||||
http://www.physics.princeton.edu/pulsar/K1JT/wsjtx.html
|
|
||||||
|
|
||||||
Project mailing list (shared with other applications from the same
|
|
||||||
team):
|
|
||||||
|
|
||||||
https://groups.yahoo.com/neo/groups/wsjtgroup
|
|
||||||
|
|
||||||
|
* 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 <QLocale>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
#include "udp_export.h"
|
|
||||||
|
|
||||||
class QVariant;
|
class QVariant;
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
@@ -26,7 +24,7 @@ namespace Radio
|
|||||||
//
|
//
|
||||||
// Qt type registration
|
// Qt type registration
|
||||||
//
|
//
|
||||||
void UDP_NO_EXPORT register_types ();
|
void register_types ();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Frequency type conversion.
|
// Frequency type conversion.
|
||||||
@@ -34,27 +32,27 @@ namespace Radio
|
|||||||
// QVariant argument is convertible to double and is assumed to
|
// QVariant argument is convertible to double and is assumed to
|
||||||
// be scaled by (10 ** -scale).
|
// 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 ());
|
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 ());
|
bool * ok = nullptr, QLocale const& = QLocale ());
|
||||||
|
|
||||||
//
|
//
|
||||||
// Frequency type formatting
|
// Frequency type formatting
|
||||||
//
|
//
|
||||||
QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
QString frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
QString frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
QString pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
QString pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
QString pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||||
|
|
||||||
//
|
//
|
||||||
// Callsigns
|
// Callsigns
|
||||||
//
|
//
|
||||||
bool UDP_EXPORT is_callsign (QString const&);
|
bool is_callsign (QString const&);
|
||||||
bool UDP_EXPORT is_compound_callsign (QString const&);
|
bool is_compound_callsign (QString const&);
|
||||||
QString UDP_EXPORT base_callsign (QString);
|
QString base_callsign (QString);
|
||||||
QString UDP_EXPORT effective_prefix (QString);
|
QString effective_prefix (QString);
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_DECLARE_METATYPE (Radio::Frequency);
|
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 <QDebug>
|
||||||
#include <QDebugStateSaver>
|
#include <QDebugStateSaver>
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "pimpl_impl.hpp"
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
#include "Radio.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;}))
|
, [&band] (Station const& s) {return s.band_name_ == band;}))
|
||||||
{
|
{
|
||||||
// not found so add it
|
// 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;
|
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
|
# Version number components
|
||||||
set (WSJTX_VERSION_MAJOR 0)
|
set (WSJTX_VERSION_MAJOR 0)
|
||||||
set (WSJTX_VERSION_MINOR 6)
|
set (WSJTX_VERSION_MINOR 7)
|
||||||
set (WSJTX_VERSION_PATCH 4)
|
set (WSJTX_VERSION_PATCH 2)
|
||||||
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions
|
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
|
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
#include "Bands.hpp"
|
#include "Bands.hpp"
|
||||||
#include "FrequencyList.hpp"
|
#include "FrequencyList.hpp"
|
||||||
#include "WsprTxScheduler.h"
|
#include "WsprTxScheduler.h"
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "pimpl_impl.hpp"
|
#include "pimpl_impl.hpp"
|
||||||
#include "moc_WSPRBandHopping.cpp"
|
#include "moc_WSPRBandHopping.cpp"
|
||||||
|
|
||||||
@@ -333,7 +335,7 @@ void WSPRBandHopping::set_tx_percent (int new_value)
|
|||||||
// determine the parameters of the hop, if any
|
// determine the parameters of the hop, if any
|
||||||
auto WSPRBandHopping::next_hop (bool tx_enabled) -> Hop
|
auto WSPRBandHopping::next_hop (bool tx_enabled) -> Hop
|
||||||
{
|
{
|
||||||
auto const& now = QDateTime::currentDateTimeUtc ();
|
auto const& now = DriftingDateTime::currentDateTimeUtc ();
|
||||||
auto const& date = now.date ();
|
auto const& date = now.date ();
|
||||||
auto year = date.year ();
|
auto year = date.year ();
|
||||||
auto month = date.month ();
|
auto month = date.month ();
|
||||||
|
|||||||
@@ -13,27 +13,28 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
ui->labelTxt->setText ("<h2>" + QString {"FT8Call v"
|
ui->labelTxt->setText ("<h2>" + QString {"JS8Call v"
|
||||||
+ QCoreApplication::applicationVersion ()
|
+ QCoreApplication::applicationVersion ()
|
||||||
+ " " + revision ()}.simplified () + "</h2><br />"
|
+ " " + 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/>"
|
"restructured and redesigned for message passing. <br/>"
|
||||||
"It is not supported by nor endorsed by the WSJT-X "
|
"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 "
|
"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/>"
|
"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/>"
|
"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 "
|
"and FSQCall <br/>and would not exist without the hard work and "
|
||||||
"dedication of the many <br/>developers in the amateur radio "
|
"dedication of the many <br/>developers in the amateur radio "
|
||||||
"community.<br /><br />"
|
"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 />"
|
"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, "
|
"KC9QNE, "
|
||||||
"KI6SSI, "
|
"KI6SSI, "
|
||||||
|
"K0OG, "
|
||||||
"LB9YH, "
|
"LB9YH, "
|
||||||
"M0IAX, "
|
"M0IAX, "
|
||||||
"N0JDS, "
|
"N0JDS, "
|
||||||
@@ -41,7 +42,7 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
|
|||||||
"VA3OSO, "
|
"VA3OSO, "
|
||||||
"VK1MIC, "
|
"VK1MIC, "
|
||||||
"W0FW,</strong><br/><br/>and the many other amateur radio operators who have given "
|
"W0FW,</strong><br/><br/>and the many other amateur radio operators who have given "
|
||||||
"FT8Call a chance.");
|
"JS8Call a chance.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CAboutDlg::~CAboutDlg()
|
CAboutDlg::~CAboutDlg()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>About FT8Call</string>
|
<string>About JS8Call</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<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 128 -compress none \) \
|
||||||
\( -clone 1 -resize 256 -compress Zip \) \
|
\( -clone 1 -resize 256 -compress Zip \) \
|
||||||
-delete 1 -delete 0 \
|
-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
|
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 | \
|
#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
|
# 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
|
# Mac
|
||||||
#
|
#
|
||||||
mkdir -p ../icons/Darwin/FT8Call.iconset
|
mkdir -p ../icons/Darwin/JS8Call.iconset
|
||||||
inkscape -z -e ../icons/Darwin/FT8Call.iconset/icon_16x16.png -w 16 -h 16 icon_128.svg
|
inkscape -z -e ../icons/Darwin/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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/JS8Call.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
|
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/FT8Call.iconset/*
|
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Darwin/JS8Call.iconset/*
|
||||||
# generic globe iconset for utilities
|
# generic globe iconset for utilities
|
||||||
mkdir -p ../icons/Darwin/wsjt.iconset
|
mkdir -p ../icons/Darwin/wsjt.iconset
|
||||||
inkscape -z -e ../icons/Darwin/wsjt.iconset/icon_16x16.png -w 16 -h 16 icon_128.svg
|
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
|
# KDE & Gnome
|
||||||
#
|
#
|
||||||
inkscape -z -e ../icons/Unix/ft8call_icon.png -w 128 -h 128 icon_1024.svg
|
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/ft8call_icon.png
|
identify -format '%f %p/%n %m %C/%Q %r %G %A %z\n' ../icons/Unix/js8call_icon.png
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString
|
|||||||
tryUnpack();
|
tryUnpack();
|
||||||
}
|
}
|
||||||
|
|
||||||
DecodedText::DecodedText (QString const& ft8callmessage):
|
DecodedText::DecodedText (QString const& js8callmessage):
|
||||||
frameType_(Varicode::FrameUnknown),
|
frameType_(Varicode::FrameUnknown),
|
||||||
message_(ft8callmessage),
|
message_(js8callmessage),
|
||||||
isBeacon_(false),
|
isBeacon_(false),
|
||||||
isAlt_(false)
|
isAlt_(false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class DecodedText
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit DecodedText (QString const& message, bool, QString const& my_grid);
|
explicit DecodedText (QString const& message, bool, QString const& my_grid);
|
||||||
explicit DecodedText (QString const& ft8callmessage);
|
explicit DecodedText (QString const& js8callmessage);
|
||||||
|
|
||||||
bool tryUnpack();
|
bool tryUnpack();
|
||||||
bool tryUnpackBeacon();
|
bool tryUnpackBeacon();
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
|
||||||
|
#include "DriftingDateTime.h"
|
||||||
|
|
||||||
#include "qt_helpers.hpp"
|
#include "qt_helpers.hpp"
|
||||||
|
|
||||||
#include "moc_displaytext.cpp"
|
#include "moc_displaytext.cpp"
|
||||||
@@ -288,13 +290,13 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
|
|||||||
t2.sprintf("%4d",txFreq);
|
t2.sprintf("%4d",txFreq);
|
||||||
QString t;
|
QString t;
|
||||||
if(bFastMode or modeTx=="FT8") {
|
if(bFastMode or modeTx=="FT8") {
|
||||||
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||||
" Tx " + t2 + t1 + text;
|
" Tx " + t2 + t1 + text;
|
||||||
} else if(modeTx.mid(0,6)=="FT8fox") {
|
} else if(modeTx.mid(0,6)=="FT8fox") {
|
||||||
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
|
||||||
" Tx" + modeTx.mid(7) + " " + text;
|
" Tx" + modeTx.mid(7) + " " + text;
|
||||||
} else {
|
} else {
|
||||||
t = QDateTime::currentDateTimeUtc().toString("hhmm") + \
|
t = DriftingDateTime::currentDateTimeUtc().toString("hhmm") + \
|
||||||
" Tx " + t2 + t1 + text;
|
" Tx " + t2 + t1 + text;
|
||||||
}
|
}
|
||||||
appendText (t, color_ReceivedMsg);
|
appendText (t, color_ReceivedMsg);
|
||||||
@@ -302,7 +304,7 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
|
|||||||
|
|
||||||
void DisplayText::displayQSY(QString text)
|
void DisplayText::displayQSY(QString text)
|
||||||
{
|
{
|
||||||
QString t = QDateTime::currentDateTimeUtc().toString("hhmmss") + " " + text;
|
QString t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + " " + text;
|
||||||
appendText (t, "hotpink");
|
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 |