Merge branch 'release-2.3.0'

This commit is contained in:
Bill Somerville 2020-09-26 21:51:46 +01:00
commit 450f44c9ab
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
169 changed files with 31280 additions and 13674 deletions

11
.gitignore vendored
View File

@ -1,6 +1,9 @@
~* ~*
TAGS TAGS
tags tags
GPATH
GRTAGS
GTAGS
*~ *~
junk* junk*
jnq* jnq*
@ -9,5 +12,13 @@ jnq*
*.mod *.mod
*.pro.user *.pro.user
*.txt *.txt
*.bak
!**/CMakeLists.txt
__pycache__
cmake-build-debug cmake-build-debug
cmake-build-release cmake-build-release
CMakeFiles
fnd
lib/77bit/tmp
lib/tmp
lib/ftrsd

5
Audio/Audio.pri Normal file
View File

@ -0,0 +1,5 @@
SOURCES += Audio/AudioDevice.cpp Audio/BWFFile.cpp Audio/soundin.cpp \
Audio/soundout.cpp
HEADERS += Audio/AudioDevice.hpp Audio/BWFFile.hpp Audio/soundin.h \
Audio/soundout.h

View File

@ -7,4 +7,3 @@ bool AudioDevice::initialize (OpenMode mode, Channel channel)
// open and ensure we are unbuffered if possible // open and ensure we are unbuffered if possible
return QIODevice::open (mode | QIODevice::Unbuffered); return QIODevice::open (mode | QIODevice::Unbuffered);
} }

View File

@ -33,8 +33,8 @@ public:
Channel channel () const {return m_channel;} Channel channel () const {return m_channel;}
protected: protected:
AudioDevice (QObject * parent = 0) AudioDevice (QObject * parent = nullptr)
: QIODevice (parent) : QIODevice {parent}
{ {
} }
@ -43,23 +43,23 @@ protected:
qint16 const * begin (reinterpret_cast<qint16 const *> (source)); qint16 const * begin (reinterpret_cast<qint16 const *> (source));
for ( qint16 const * i = begin; i != begin + numFrames * (bytesPerFrame () / sizeof (qint16)); i += bytesPerFrame () / sizeof (qint16)) for ( qint16 const * i = begin; i != begin + numFrames * (bytesPerFrame () / sizeof (qint16)); i += bytesPerFrame () / sizeof (qint16))
{ {
switch (m_channel) switch (m_channel)
{ {
case Mono: case Mono:
*dest++ = *i; *dest++ = *i;
break; break;
case Right: case Right:
*dest++ = *(i + 1); *dest++ = *(i + 1);
break; break;
case Both: // should be able to happen but if it case Both: // should be able to happen but if it
// does we'll take left // does we'll take left
Q_ASSERT (Both == m_channel); Q_ASSERT (Both == m_channel);
case Left: case Left:
*dest++ = *i; *dest++ = *i;
break; break;
} }
} }
} }
@ -68,23 +68,23 @@ protected:
switch (m_channel) switch (m_channel)
{ {
case Mono: case Mono:
*dest++ = sample; *dest++ = sample;
break; break;
case Left: case Left:
*dest++ = sample; *dest++ = sample;
*dest++ = 0; *dest++ = 0;
break; break;
case Right: case Right:
*dest++ = 0; *dest++ = 0;
*dest++ = sample; *dest++ = sample;
break; break;
case Both: case Both:
*dest++ = sample; *dest++ = sample;
*dest++ = sample; *dest++ = sample;
break; break;
} }
return dest; return dest;
} }

View File

@ -1,5 +1,7 @@
#include "soundin.h" #include "soundin.h"
#include <cstdlib>
#include <cmath>
#include <QAudioDeviceInfo> #include <QAudioDeviceInfo>
#include <QAudioFormat> #include <QAudioFormat>
#include <QAudioInput> #include <QAudioInput>
@ -8,11 +10,9 @@
#include "moc_soundin.cpp" #include "moc_soundin.cpp"
bool SoundInput::audioError () const bool SoundInput::checkStream ()
{ {
bool result (true); bool result (false);
Q_ASSERT_X (m_stream, "SoundInput", "programming error");
if (m_stream) if (m_stream)
{ {
switch (m_stream->error ()) switch (m_stream->error ())
@ -34,14 +34,15 @@ bool SoundInput::audioError () const
break; break;
case QAudio::NoError: case QAudio::NoError:
result = false; result = true;
break; break;
} }
} }
return result; return result;
} }
void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink, unsigned downSampleFactor, AudioDevice::Channel channel) void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink
, unsigned downSampleFactor, AudioDevice::Channel channel)
{ {
Q_ASSERT (sink); Q_ASSERT (sink);
@ -62,28 +63,37 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
Q_EMIT error (tr ("Requested input audio format is not valid.")); Q_EMIT error (tr ("Requested input audio format is not valid."));
return; return;
} }
else if (!device.isFormatSupported (format))
if (!device.isFormatSupported (format))
{ {
// qDebug () << "Nearest supported audio format:" << device.nearestFormat (format); // qDebug () << "Nearest supported audio format:" << device.nearestFormat (format);
Q_EMIT error (tr ("Requested input audio format is not supported on device.")); Q_EMIT error (tr ("Requested input audio format is not supported on device."));
return; return;
} }
// qDebug () << "Selected audio input format:" << format; // qDebug () << "Selected audio input format:" << format;
m_stream.reset (new QAudioInput {device, format}); m_stream.reset (new QAudioInput {device, format});
if (audioError ()) if (!checkStream ())
{ {
return; return;
} }
connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged); connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged);
connect (m_stream.data(), &QAudioInput::notify, [this] () {checkStream ();});
m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer)); //qDebug () << "SoundIn default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
if (sink->initialize (QIODevice::WriteOnly, channel)) // the Windows MME version of QAudioInput uses 1/5 of the buffer
// size for period size other platforms seem to optimize themselves
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer * 5));
#else
Q_UNUSED (framesPerBuffer);
#endif
if (m_sink->initialize (QIODevice::WriteOnly, channel))
{ {
m_stream->start (sink); m_stream->start (sink);
audioError (); checkStream ();
cummulative_lost_usec_ = -1;
//qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize () << "peirod size:" << m_stream->periodSize ();
} }
else else
{ {
@ -96,7 +106,7 @@ void SoundInput::suspend ()
if (m_stream) if (m_stream)
{ {
m_stream->suspend (); m_stream->suspend ();
audioError (); checkStream ();
} }
} }
@ -111,14 +121,12 @@ void SoundInput::resume ()
if (m_stream) if (m_stream)
{ {
m_stream->resume (); m_stream->resume ();
audioError (); checkStream ();
} }
} }
void SoundInput::handleStateChanged (QAudio::State newState) const void SoundInput::handleStateChanged (QAudio::State newState)
{ {
// qDebug () << "SoundInput::handleStateChanged: newState:" << newState;
switch (newState) switch (newState)
{ {
case QAudio::IdleState: case QAudio::IdleState:
@ -126,6 +134,7 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
break; break;
case QAudio::ActiveState: case QAudio::ActiveState:
reset (false);
Q_EMIT status (tr ("Receiving")); Q_EMIT status (tr ("Receiving"));
break; break;
@ -140,7 +149,7 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
#endif #endif
case QAudio::StoppedState: case QAudio::StoppedState:
if (audioError ()) if (!checkStream ())
{ {
Q_EMIT status (tr ("Error")); Q_EMIT status (tr ("Error"));
} }
@ -152,6 +161,28 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
} }
} }
void SoundInput::reset (bool report_dropped_frames)
{
if (m_stream)
{
auto elapsed_usecs = m_stream->elapsedUSecs ();
while (std::abs (elapsed_usecs - m_stream->processedUSecs ())
> 24 * 60 * 60 * 500000ll) // half day
{
// QAudioInput::elapsedUSecs() wraps after 24 hours
elapsed_usecs += 24 * 60 * 60 * 1000000ll;
}
// don't report first time as we don't yet known latency
if (cummulative_lost_usec_ != std::numeric_limits<qint64>::min () && report_dropped_frames)
{
auto lost_usec = elapsed_usecs - m_stream->processedUSecs () - cummulative_lost_usec_;
Q_EMIT dropped_frames (m_stream->format ().framesForDuration (lost_usec), lost_usec);
//qDebug () << "SoundInput::reset: frames dropped:" << m_stream->format ().framesForDuration (lost_usec) << "sec:" << lost_usec / 1.e6;
}
cummulative_lost_usec_ = elapsed_usecs - m_stream->processedUSecs ();
}
}
void SoundInput::stop() void SoundInput::stop()
{ {
if (m_stream) if (m_stream)
@ -159,11 +190,6 @@ void SoundInput::stop()
m_stream->stop (); m_stream->stop ();
} }
m_stream.reset (); m_stream.reset ();
if (m_sink)
{
m_sink->close ();
}
} }
SoundInput::~SoundInput () SoundInput::~SoundInput ()

View File

@ -2,6 +2,7 @@
#ifndef SOUNDIN_H__ #ifndef SOUNDIN_H__
#define SOUNDIN_H__ #define SOUNDIN_H__
#include <limits>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QDateTime> #include <QDateTime>
@ -23,7 +24,7 @@ class SoundInput
public: public:
SoundInput (QObject * parent = nullptr) SoundInput (QObject * parent = nullptr)
: QObject {parent} : QObject {parent}
, m_sink {nullptr} , cummulative_lost_usec_ {std::numeric_limits<qint64>::min ()}
{ {
} }
@ -35,18 +36,21 @@ public:
Q_SLOT void suspend (); Q_SLOT void suspend ();
Q_SLOT void resume (); Q_SLOT void resume ();
Q_SLOT void stop (); Q_SLOT void stop ();
Q_SLOT void reset (bool report_dropped_frames);
Q_SIGNAL void error (QString message) const; Q_SIGNAL void error (QString message) const;
Q_SIGNAL void status (QString message) const; Q_SIGNAL void status (QString message) const;
Q_SIGNAL void dropped_frames (qint32 dropped, qint64 usec);
private: private:
// used internally // used internally
Q_SLOT void handleStateChanged (QAudio::State) const; Q_SLOT void handleStateChanged (QAudio::State);
bool audioError () const; bool checkStream ();
QScopedPointer<QAudioInput> m_stream; QScopedPointer<QAudioInput> m_stream;
QPointer<AudioDevice> m_sink; QPointer<AudioDevice> m_sink;
qint64 cummulative_lost_usec_;
}; };
#endif #endif

View File

@ -7,20 +7,13 @@
#include <qmath.h> #include <qmath.h>
#include <QDebug> #include <QDebug>
#include "Audio/AudioDevice.hpp"
#include "moc_soundout.cpp" #include "moc_soundout.cpp"
/* bool SoundOutput::checkStream () const
#if defined (WIN32)
# define MS_BUFFERED 1000u
#else
# define MS_BUFFERED 2000u
#endif
*/
# define MS_BUFFERED 200u
bool SoundOutput::audioError () const
{ {
bool result (true); bool result {false};
Q_ASSERT_X (m_stream, "SoundOutput", "programming error"); Q_ASSERT_X (m_stream, "SoundOutput", "programming error");
if (m_stream) { if (m_stream) {
@ -43,69 +36,83 @@ bool SoundOutput::audioError () const
break; break;
case QAudio::NoError: case QAudio::NoError:
result = false; result = true;
break; break;
} }
} }
return result; return result;
} }
void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered) void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered)
{ {
Q_ASSERT (0 < channels && channels < 3); Q_ASSERT (0 < channels && channels < 3);
m_device = device;
m_msBuffered = msBuffered; m_channels = channels;
m_framesBuffered = frames_buffered;
QAudioFormat format (device.preferredFormat ());
// qDebug () << "Preferred audio output format:" << format;
format.setChannelCount (channels);
format.setCodec ("audio/pcm");
format.setSampleRate (48000);
format.setSampleType (QAudioFormat::SignedInt);
format.setSampleSize (16);
format.setByteOrder (QAudioFormat::Endian (QSysInfo::ByteOrder));
if (!format.isValid ())
{
Q_EMIT error (tr ("Requested output audio format is not valid."));
}
if (!device.isFormatSupported (format))
{
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
}
// qDebug () << "Selected audio output format:" << format;
m_stream.reset (new QAudioOutput (device, format));
audioError ();
m_stream->setVolume (m_volume);
m_stream->setNotifyInterval(100);
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
// qDebug() << "A" << m_volume << m_stream->notifyInterval();
} }
void SoundOutput::restart (QIODevice * source) void SoundOutput::restart (QIODevice * source)
{ {
Q_ASSERT (m_stream); if (!m_device.isNull ())
{
QAudioFormat format (m_device.preferredFormat ());
// qDebug () << "Preferred audio output format:" << format;
format.setChannelCount (m_channels);
format.setCodec ("audio/pcm");
format.setSampleRate (48000);
format.setSampleType (QAudioFormat::SignedInt);
format.setSampleSize (16);
format.setByteOrder (QAudioFormat::Endian (QSysInfo::ByteOrder));
if (!format.isValid ())
{
Q_EMIT error (tr ("Requested output audio format is not valid."));
}
else if (!m_device.isFormatSupported (format))
{
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
}
else
{
// qDebug () << "Selected audio output format:" << format;
m_stream.reset (new QAudioOutput (m_device, format));
checkStream ();
m_stream->setVolume (m_volume);
m_stream->setNotifyInterval(1000);
error_ = false;
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
connect (m_stream.data(), &QAudioOutput::notify, [this] () {checkStream ();});
// qDebug() << "A" << m_volume << m_stream->notifyInterval();
}
}
if (!m_stream)
{
if (!error_)
{
error_ = true; // only signal error once
Q_EMIT error (tr ("No audio output device configured."));
}
return;
}
else
{
error_ = false;
}
//
// This buffer size is critical since for proper sound streaming. If
// it is too short; high activity levels on the machine can starve
// the audio buffer. On the other hand the Windows implementation
// seems to take the length of the buffer in time to stop the audio
// stream even if reset() is used.
//
// 2 seconds seems a reasonable compromise except for Windows
// where things are probably broken.
//
// we have to set this before every start on the stream because the // we have to set this before every start on the stream because the
// Windows implementation seems to forget the buffer size after a // Windows implementation seems to forget the buffer size after a
// stop. // stop.
m_stream->setBufferSize (m_stream->format().bytesForDuration((m_msBuffered ? m_msBuffered : MS_BUFFERED) * 1000)); //qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
// qDebug() << "B" << m_stream->bufferSize() << if (m_framesBuffered)
// m_stream->periodSize() << m_stream->notifyInterval(); {
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered));
#endif
}
m_stream->setCategory ("production"); m_stream->setCategory ("production");
m_stream->start (source); m_stream->start (source);
// qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
} }
void SoundOutput::suspend () void SoundOutput::suspend ()
@ -113,7 +120,7 @@ void SoundOutput::suspend ()
if (m_stream && QAudio::ActiveState == m_stream->state ()) if (m_stream && QAudio::ActiveState == m_stream->state ())
{ {
m_stream->suspend (); m_stream->suspend ();
audioError (); checkStream ();
} }
} }
@ -122,7 +129,7 @@ void SoundOutput::resume ()
if (m_stream && QAudio::SuspendedState == m_stream->state ()) if (m_stream && QAudio::SuspendedState == m_stream->state ())
{ {
m_stream->resume (); m_stream->resume ();
audioError (); checkStream ();
} }
} }
@ -131,7 +138,7 @@ void SoundOutput::reset ()
if (m_stream) if (m_stream)
{ {
m_stream->reset (); m_stream->reset ();
audioError (); checkStream ();
} }
} }
@ -139,9 +146,10 @@ void SoundOutput::stop ()
{ {
if (m_stream) if (m_stream)
{ {
m_stream->reset ();
m_stream->stop (); m_stream->stop ();
audioError ();
} }
m_stream.reset ();
} }
qreal SoundOutput::attenuation () const qreal SoundOutput::attenuation () const
@ -171,8 +179,6 @@ void SoundOutput::resetAttenuation ()
void SoundOutput::handleStateChanged (QAudio::State newState) void SoundOutput::handleStateChanged (QAudio::State newState)
{ {
// qDebug () << "SoundOutput::handleStateChanged: newState:" << newState;
switch (newState) switch (newState)
{ {
case QAudio::IdleState: case QAudio::IdleState:
@ -194,7 +200,7 @@ void SoundOutput::handleStateChanged (QAudio::State newState)
#endif #endif
case QAudio::StoppedState: case QAudio::StoppedState:
if (audioError ()) if (!checkStream ())
{ {
Q_EMIT status (tr ("Error")); Q_EMIT status (tr ("Error"));
} }

View File

@ -7,6 +7,7 @@
#include <QAudioOutput> #include <QAudioOutput>
#include <QAudioDeviceInfo> #include <QAudioDeviceInfo>
class QIODevice;
class QAudioDeviceInfo; class QAudioDeviceInfo;
// An instance of this sends audio data to a specified soundcard. // An instance of this sends audio data to a specified soundcard.
@ -18,15 +19,16 @@ class SoundOutput
public: public:
SoundOutput () SoundOutput ()
: m_msBuffered {0u} : m_framesBuffered {0}
, m_volume {1.0} , m_volume {1.0}
, error_ {false}
{ {
} }
qreal attenuation () const; qreal attenuation () const;
public Q_SLOTS: public Q_SLOTS:
void setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered = 0u); void setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered = 0);
void restart (QIODevice *); void restart (QIODevice *);
void suspend (); void suspend ();
void resume (); void resume ();
@ -40,15 +42,18 @@ Q_SIGNALS:
void status (QString message) const; void status (QString message) const;
private: private:
bool audioError () const; bool checkStream () const;
private Q_SLOTS: private Q_SLOTS:
void handleStateChanged (QAudio::State); void handleStateChanged (QAudio::State);
private: private:
QAudioDeviceInfo m_device;
unsigned m_channels;
QScopedPointer<QAudioOutput> m_stream; QScopedPointer<QAudioOutput> m_stream;
unsigned m_msBuffered; int m_framesBuffered;
qreal m_volume; qreal m_volume;
bool error_;
}; };
#endif #endif

View File

@ -34,6 +34,12 @@
# #
# $Id: FindFFTW3.cmake 15918 2010-06-25 11:12:42Z loose $ # $Id: FindFFTW3.cmake 15918 2010-06-25 11:12:42Z loose $
# Compatibily with old style MinGW packages with no .dll.a files
# needed since CMake v3.17 because of fix for #20019
if (MINGW)
set (CMAKE_FIND_LIBRARY_SUFFIXES ".dll" ".dll.a" ".a" ".lib")
endif ()
# Use double precision by default. # Use double precision by default.
if (FFTW3_FIND_COMPONENTS MATCHES "^$") if (FFTW3_FIND_COMPONENTS MATCHES "^$")
set (_components double) set (_components double)

View File

@ -222,9 +222,12 @@ set (WSJT_QT_CONF_DESTINATION ${QT_CONF_DESTINATION} CACHE PATH "Path for the qt
# #
# Project sources # Project sources
# #
set (fort_qt_CXXSRCS
lib/shmem.cpp
)
set (wsjt_qt_CXXSRCS set (wsjt_qt_CXXSRCS
qt_helpers.cpp qt_helpers.cpp
lib/shmem.cpp
widgets/MessageBox.cpp widgets/MessageBox.cpp
MetaDataRegistry.cpp MetaDataRegistry.cpp
Network/NetworkServerLookup.cpp Network/NetworkServerLookup.cpp
@ -289,6 +292,8 @@ set (wsjt_qt_CXXSRCS
logbook/AD1CCty.cpp logbook/AD1CCty.cpp
logbook/WorkedBefore.cpp logbook/WorkedBefore.cpp
logbook/Multiplier.cpp logbook/Multiplier.cpp
Network/NetworkAccessManager.cpp
widgets/LazyFillComboBox.cpp
) )
set (wsjt_qtmm_CXXSRCS set (wsjt_qtmm_CXXSRCS
@ -302,7 +307,7 @@ set (jt9_FSRCS
set (wsjtx_CXXSRCS set (wsjtx_CXXSRCS
logbook/logbook.cpp logbook/logbook.cpp
Network/psk_reporter.cpp Network/PSKReporter.cpp
Modulator/Modulator.cpp Modulator/Modulator.cpp
Detector/Detector.cpp Detector/Detector.cpp
widgets/logqso.cpp widgets/logqso.cpp
@ -358,6 +363,8 @@ endif (WIN32)
set (wsjt_FSRCS set (wsjt_FSRCS
# put module sources first in the hope that they get rebuilt before use # put module sources first in the hope that they get rebuilt before use
lib/types.f90
lib/C_interface_module.f90
lib/shmem.f90 lib/shmem.f90
lib/crc.f90 lib/crc.f90
lib/fftw3mod.f90 lib/fftw3mod.f90
@ -369,6 +376,7 @@ set (wsjt_FSRCS
lib/jt65_mod.f90 lib/jt65_mod.f90
lib/ft8_decode.f90 lib/ft8_decode.f90
lib/ft4_decode.f90 lib/ft4_decode.f90
lib/fst4_decode.f90
lib/jt9_decode.f90 lib/jt9_decode.f90
lib/options.f90 lib/options.f90
lib/packjt.f90 lib/packjt.f90
@ -395,6 +403,7 @@ set (wsjt_FSRCS
lib/badmsg.f90 lib/badmsg.f90
lib/ft8/baseline.f90 lib/ft8/baseline.f90
lib/ft4/ft4_baseline.f90 lib/ft4/ft4_baseline.f90
lib/blanker.f90
lib/bpdecode40.f90 lib/bpdecode40.f90
lib/bpdecode128_90.f90 lib/bpdecode128_90.f90
lib/ft8/bpdecode174_91.f90 lib/ft8/bpdecode174_91.f90
@ -544,6 +553,7 @@ set (wsjt_FSRCS
lib/qra64a.f90 lib/qra64a.f90
lib/refspectrum.f90 lib/refspectrum.f90
lib/savec2.f90 lib/savec2.f90
lib/sec0.f90
lib/sec_midn.f90 lib/sec_midn.f90
lib/setup65.f90 lib/setup65.f90
lib/sh65.f90 lib/sh65.f90
@ -595,6 +605,21 @@ set (wsjt_FSRCS
lib/wqencode.f90 lib/wqencode.f90
lib/wspr_downsample.f90 lib/wspr_downsample.f90
lib/zplot9.f90 lib/zplot9.f90
lib/fst4/decode240_101.f90
lib/fst4/decode240_74.f90
lib/fst4/encode240_101.f90
lib/fst4/encode240_74.f90
lib/fst4/fst4sim.f90
lib/fst4/gen_fst4wave.f90
lib/fst4/genfst4.f90
lib/fst4/get_fst4_bitmetrics.f90
lib/fst4/get_fst4_bitmetrics2.f90
lib/fst4/ldpcsim240_101.f90
lib/fst4/ldpcsim240_74.f90
lib/fst4/osd240_101.f90
lib/fst4/osd240_74.f90
lib/fst4/get_crc24.f90
lib/fst4/fst4_baseline.f90
) )
# temporary workaround for a gfortran v7.3 ICE on Fedora 27 64-bit # temporary workaround for a gfortran v7.3 ICE on Fedora 27 64-bit
@ -710,6 +735,7 @@ set (qcp_CXXSRCS
set (all_CXXSRCS set (all_CXXSRCS
${wsjt_CXXSRCS} ${wsjt_CXXSRCS}
${fort_qt_CXXSRCS}
${wsjt_qt_CXXSRCS} ${wsjt_qt_CXXSRCS}
${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_CXXSRCS}
${wsjtx_CXXSRCS} ${wsjtx_CXXSRCS}
@ -724,10 +750,6 @@ set (all_C_and_CXXSRCS
) )
set (TOP_LEVEL_RESOURCES set (TOP_LEVEL_RESOURCES
shortcuts.txt
mouse_commands.txt
prefixes.txt
cty.dat
icons/Darwin/wsjtx.iconset/icon_128x128.png icons/Darwin/wsjtx.iconset/icon_128x128.png
contrib/gpl-v3-logo.svg contrib/gpl-v3-logo.svg
artwork/splash.png artwork/splash.png
@ -846,6 +868,8 @@ endif (APPLE)
# #
# find some useful tools # find some useful tools
# #
include (CheckSymbolExists)
find_program(CTAGS ctags) find_program(CTAGS ctags)
find_program(ETAGS etags) find_program(ETAGS etags)
@ -866,7 +890,7 @@ find_package (OpenMP)
# #
# fftw3 single precision library # fftw3 single precision library
# #
find_package (FFTW3 COMPONENTS double single threads REQUIRED) find_package (FFTW3 COMPONENTS single threads REQUIRED)
# #
# libhamlib setup # libhamlib setup
@ -881,6 +905,10 @@ message (STATUS "hamlib_INCLUDE_DIRS: ${hamlib_INCLUDE_DIRS}")
message (STATUS "hamlib_LIBRARIES: ${hamlib_LIBRARIES}") message (STATUS "hamlib_LIBRARIES: ${hamlib_LIBRARIES}")
message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}") message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}")
set (CMAKE_REQUIRED_INCLUDES "${hamlib_INCLUDE_DIRS}")
set (CMAKE_REQUIRED_LIBRARIES "${hamlib_LIBRARIES}")
check_symbol_exists (rig_set_cache_timeout_ms "hamlib/rig.h" HAVE_HAMLIB_CACHING)
# #
# Qt5 setup # Qt5 setup
@ -903,7 +931,9 @@ endif ()
if (WSJT_GENERATE_DOCS) if (WSJT_GENERATE_DOCS)
add_subdirectory (doc) add_subdirectory (doc)
endif (WSJT_GENERATE_DOCS) endif (WSJT_GENERATE_DOCS)
if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
add_subdirectory (tests)
endif ()
# #
# Library building setup # Library building setup
@ -1097,14 +1127,19 @@ add_custom_target (etags COMMAND ${ETAGS} -o ${CMAKE_SOURCE_DIR}/TAGS -R ${sourc
# Qt i18n - always include the country generic if any regional variant is included # Qt i18n - always include the country generic if any regional variant is included
set (LANGUAGES set (LANGUAGES
ca # Catalan
da # Danish
en # English (we need this to stop en # English (we need this to stop
# translation loaders loading the # translation loaders loading the
# second preference UI languge, it # second preference UI languge, it
# doesn't need to be populated) # doesn't need to be populated)
en_GB # English UK en_GB # English UK
es # Spanish es # Spanish
ca # Catalan it # Italian
ja # Japanese ja # Japanese
#no # Norwegian
#pt # Portuguese
#sv # Swedish
zh # Chinese zh # Chinese
zh_HK # Chinese per Hong Kong zh_HK # Chinese per Hong Kong
it # Italian it # Italian
@ -1127,6 +1162,7 @@ if (UPDATE_TRANSLATIONS)
qt5_create_translation ( qt5_create_translation (
QM_FILES ${wsjt_qt_UISRCS} ${wsjtx_UISRCS} ${wsjt_qt_CXXSRCS} ${wsjtx_CXXSRCS} QM_FILES ${wsjt_qt_UISRCS} ${wsjtx_UISRCS} ${wsjt_qt_CXXSRCS} ${wsjtx_CXXSRCS}
${TS_FILES} ${TS_FILES}
OPTIONS -I${CMAKE_CURRENT_SOURCE_DIR}
) )
else () else ()
qt5_add_translation (QM_FILES ${TS_FILES}) qt5_add_translation (QM_FILES ${TS_FILES})
@ -1227,6 +1263,11 @@ if (WIN32)
target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase) target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase)
endif (WIN32) endif (WIN32)
# build a library of package Qt functionality used in Fortran utilities
add_library (fort_qt STATIC ${fort_qt_CXXSRCS})
target_link_libraries (fort_qt Qt5::Core)
# build a library of WSJT Qt multimedia components
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)
@ -1274,9 +1315,9 @@ if (${OPENMP_FOUND} OR APPLE)
LINK_FLAGS -Wl,--stack,16777216 LINK_FLAGS -Wl,--stack,16777216
) )
endif () endif ()
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx wsjt_qt) target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt)
else (${OPENMP_FOUND} OR APPLE) else (${OPENMP_FOUND} OR APPLE)
target_link_libraries (jt9 wsjt_fort wsjt_cxx Qt5::Core) target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
endif (${OPENMP_FOUND} OR APPLE) endif (${OPENMP_FOUND} OR APPLE)
if(WSJT_BUILD_UTILS) if(WSJT_BUILD_UTILS)
@ -1346,6 +1387,15 @@ target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
add_executable (record_time_signal Audio/tools/record_time_signal.cpp) add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt) target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
add_executable (fst4sim lib/fst4/fst4sim.f90 wsjtx.rc)
target_link_libraries (fst4sim wsjt_fort wsjt_cxx)
add_executable (ldpcsim240_101 lib/fst4/ldpcsim240_101.f90 wsjtx.rc)
target_link_libraries (ldpcsim240_101 wsjt_fort wsjt_cxx)
add_executable (ldpcsim240_74 lib/fst4/ldpcsim240_74.f90 wsjtx.rc)
target_link_libraries (ldpcsim240_74 wsjt_fort wsjt_cxx)
endif(WSJT_BUILD_UTILS) endif(WSJT_BUILD_UTILS)
# build the main application # build the main application
@ -1485,7 +1535,7 @@ install (TARGETS jt9 wsprd fmtave fcal fmeasure
if(WSJT_BUILD_UTILS) if(WSJT_BUILD_UTILS)
install (TARGETS ft8code jt65code qra64code qra64sim jt9code jt4code install (TARGETS ft8code jt65code qra64code qra64sim jt9code jt4code
msk144code msk144code fst4sim
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
) )
@ -1525,6 +1575,7 @@ install (FILES
) )
install (FILES install (FILES
cty.dat
contrib/Ephemeris/JPLEPH contrib/Ephemeris/JPLEPH
DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
#COMPONENT runtime #COMPONENT runtime
@ -1805,11 +1856,11 @@ endif ()
set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}") set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran3 (>=4.8.2), libqt5serialport5 (>=5.5), libqt5multimedia5-plugins (>=5.5), libqt5widgets5 (>=5.5), libqt5sql5-sqlite (>=5.5), libusb-1.0-0, libudev1, libc6 (>=2.19)") set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran5 (>=10), libfftw3-single3 (>=3.3.8), libgomp1 (>=10), libqt5serialport5 (>=5.12.8), libqt5multimedia5-plugins (>=5.12.8), libqt5widgets5 (>=5.12.8), libqt5network5 (>=5.12.8), libqt5printsupport5 (>=5.12.8), libqt5sql5-sqlite (>=5.12.8), libusb-1.0-0 (>=1.0.23)")
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtserialport >= 5.5, qt5-qtmultimedia >= 5.5, qt5-qtsvg, libusb, systemd-udev, glibc >= 2, libgfortran >= 4.8.2") set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase >= 5.13.2, qt5-qtserialport >= 5.13.2, qt5-qtmultimedia >= 5.13.2, qt5-qtsvg >= 5.13.2, libusbx >= 1.0.23, libgfortran >= 10.0.1, libgomp >= 10.0.1, fftw-libs-single >= 3.3.8")
set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1) set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1)
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in" configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"

View File

@ -135,8 +135,11 @@
#include <cmath> #include <cmath>
#include <QApplication> #include <QApplication>
#include <QCursor>
#include <QMetaType> #include <QMetaType>
#include <QList> #include <QList>
#include <QPair>
#include <QVariant>
#include <QSettings> #include <QSettings>
#include <QAudioDeviceInfo> #include <QAudioDeviceInfo>
#include <QAudioInput> #include <QAudioInput>
@ -186,6 +189,7 @@
#include "Network/LotWUsers.hpp" #include "Network/LotWUsers.hpp"
#include "models/DecodeHighlightingModel.hpp" #include "models/DecodeHighlightingModel.hpp"
#include "logbook/logbook.h" #include "logbook/logbook.h"
#include "widgets/LazyFillComboBox.hpp"
#include "ui_Configuration.h" #include "ui_Configuration.h"
#include "moc_Configuration.cpp" #include "moc_Configuration.cpp"
@ -401,6 +405,7 @@ class Configuration::impl final
public: public:
using FrequencyDelta = Radio::FrequencyDelta; using FrequencyDelta = Radio::FrequencyDelta;
using port_type = Configuration::port_type; using port_type = Configuration::port_type;
using audio_info_type = QPair<QAudioDeviceInfo, QList<QVariant> >;
explicit impl (Configuration * self explicit impl (Configuration * self
, QNetworkAccessManager * network_manager , QNetworkAccessManager * network_manager
@ -429,9 +434,13 @@ private:
void read_settings (); void read_settings ();
void write_settings (); void write_settings ();
bool load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *); void find_audio_devices ();
QAudioDeviceInfo find_audio_device (QAudio::Mode, QComboBox *, QString const& device_name);
void load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *);
void update_audio_channels (QComboBox const *, int, QComboBox *, bool); void update_audio_channels (QComboBox const *, int, QComboBox *, bool);
void find_tab (QWidget *);
void initialize_models (); void initialize_models ();
bool split_mode () const bool split_mode () const
{ {
@ -477,8 +486,6 @@ private:
Q_SLOT void on_force_DTR_combo_box_currentIndexChanged (int); Q_SLOT void on_force_DTR_combo_box_currentIndexChanged (int);
Q_SLOT void on_force_RTS_combo_box_currentIndexChanged (int); Q_SLOT void on_force_RTS_combo_box_currentIndexChanged (int);
Q_SLOT void on_rig_combo_box_currentIndexChanged (int); Q_SLOT void on_rig_combo_box_currentIndexChanged (int);
Q_SLOT void on_sound_input_combo_box_currentTextChanged (QString const&);
Q_SLOT void on_sound_output_combo_box_currentTextChanged (QString const&);
Q_SLOT void on_add_macro_push_button_clicked (bool = false); Q_SLOT void on_add_macro_push_button_clicked (bool = false);
Q_SLOT void on_delete_macro_push_button_clicked (bool = false); Q_SLOT void on_delete_macro_push_button_clicked (bool = false);
Q_SLOT void on_PTT_method_button_group_buttonClicked (int); Q_SLOT void on_PTT_method_button_group_buttonClicked (int);
@ -604,6 +611,7 @@ private:
bool id_after_73_; bool id_after_73_;
bool tx_QSY_allowed_; bool tx_QSY_allowed_;
bool spot_to_psk_reporter_; bool spot_to_psk_reporter_;
bool psk_reporter_tcpip_;
bool monitor_off_at_startup_; bool monitor_off_at_startup_;
bool monitor_last_used_; bool monitor_last_used_;
bool log_as_RTTY_; bool log_as_RTTY_;
@ -646,11 +654,13 @@ private:
bool pwrBandTuneMemory_; bool pwrBandTuneMemory_;
QAudioDeviceInfo audio_input_device_; QAudioDeviceInfo audio_input_device_;
bool default_audio_input_device_selected_; QAudioDeviceInfo next_audio_input_device_;
AudioDevice::Channel audio_input_channel_; AudioDevice::Channel audio_input_channel_;
AudioDevice::Channel next_audio_input_channel_;
QAudioDeviceInfo audio_output_device_; QAudioDeviceInfo audio_output_device_;
bool default_audio_output_device_selected_; QAudioDeviceInfo next_audio_output_device_;
AudioDevice::Channel audio_output_channel_; AudioDevice::Channel audio_output_channel_;
AudioDevice::Channel next_audio_output_channel_;
friend class Configuration; friend class Configuration;
}; };
@ -701,6 +711,7 @@ bool Configuration::spot_to_psk_reporter () const
// rig must be open and working to spot externally // rig must be open and working to spot externally
return is_transceiver_online () && m_->spot_to_psk_reporter_; return is_transceiver_online () && m_->spot_to_psk_reporter_;
} }
bool Configuration::psk_reporter_tcpip () const {return m_->psk_reporter_tcpip_;}
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_;}
bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;} bool Configuration::log_as_RTTY () const {return m_->log_as_RTTY_;}
@ -850,6 +861,16 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s
} }
} }
void Configuration::invalidate_audio_input_device (QString /* error */)
{
m_->audio_input_device_ = QAudioDeviceInfo {};
}
void Configuration::invalidate_audio_output_device (QString /* error */)
{
m_->audio_output_device_ = QAudioDeviceInfo {};
}
bool Configuration::valid_n1mm_info () const bool Configuration::valid_n1mm_info () const
{ {
// do very rudimentary checking on the n1mm server name and port number. // do very rudimentary checking on the n1mm server name and port number.
@ -895,7 +916,7 @@ auto Configuration::special_op_id () const -> SpecialOperatingActivity
void Configuration::set_location (QString const& grid_descriptor) void Configuration::set_location (QString const& grid_descriptor)
{ {
// change the dynamic grid // change the dynamic grid
qDebug () << "Configuration::set_location - location:" << grid_descriptor; // qDebug () << "Configuration::set_location - location:" << grid_descriptor;
m_->dynamic_grid_ = grid_descriptor.trimmed (); m_->dynamic_grid_ = grid_descriptor.trimmed ();
} }
@ -974,8 +995,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
, transceiver_command_number_ {0} , transceiver_command_number_ {0}
, degrade_ {0.} // initialize to zero each run, not , degrade_ {0.} // initialize to zero each run, not
// saved in settings // saved in settings
, default_audio_input_device_selected_ {false}
, default_audio_output_device_selected_ {false}
{ {
ui_->setupUi (this); ui_->setupUi (this);
@ -1025,6 +1044,21 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
// this must be done after the default paths above are set // this must be done after the default paths above are set
read_settings (); read_settings ();
connect (ui_->sound_input_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &next_audio_input_device_);
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_);
QGuiApplication::restoreOverrideCursor ();
});
connect (ui_->sound_output_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &next_audio_output_device_);
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_);
QGuiApplication::restoreOverrideCursor ();
});
// set up LoTW users CSV file fetching // set up LoTW users CSV file fetching
connect (&lotw_users_, &LotWUsers::load_finished, [this] () { connect (&lotw_users_, &LotWUsers::load_finished, [this] () {
ui_->LotW_CSV_fetch_push_button->setEnabled (true); ui_->LotW_CSV_fetch_push_button->setEnabled (true);
@ -1128,7 +1162,9 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
ui_->frequencies_table_view->setModel (&next_frequencies_); ui_->frequencies_table_view->setModel (&next_frequencies_);
ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder); ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true); ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
@ -1168,7 +1204,9 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
stations_.sort (StationList::band_column); stations_.sort (StationList::band_column);
ui_->stations_table_view->setModel (&next_stations_); ui_->stations_table_view->setModel (&next_stations_);
ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->stations_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents); ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->stations_table_view->verticalHeader ()->setResizeContentsPrecision (0);
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder); ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
// stations delegates // stations delegates
@ -1187,21 +1225,14 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
// //
ui_->highlighting_list_view->setModel (&next_decode_highlighing_model_); ui_->highlighting_list_view->setModel (&next_decode_highlighing_model_);
//
// load combo boxes with audio setup choices
//
default_audio_input_device_selected_ = load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &audio_input_device_);
default_audio_output_device_selected_ = load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &audio_output_device_);
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_);
ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_);
enumerate_rigs (); enumerate_rigs ();
initialize_models (); initialize_models ();
audio_input_device_ = next_audio_input_device_;
audio_input_channel_ = next_audio_input_channel_;
audio_output_device_ = next_audio_output_device_;
audio_output_channel_ = next_audio_output_channel_;
transceiver_thread_ = new QThread {this}; transceiver_thread_ = new QThread {this};
transceiver_thread_->start (); transceiver_thread_->start ();
} }
@ -1215,6 +1246,16 @@ Configuration::impl::~impl ()
void Configuration::impl::initialize_models () void Configuration::impl::initialize_models ()
{ {
next_audio_input_device_ = audio_input_device_;
next_audio_input_channel_ = audio_input_channel_;
next_audio_output_device_ = audio_output_device_;
next_audio_output_channel_ = audio_output_channel_;
restart_sound_input_device_ = false;
restart_sound_output_device_ = false;
{
SettingsGroup g {settings_, "Configuration"};
find_audio_devices ();
}
auto pal = ui_->callsign_line_edit->palette (); auto pal = ui_->callsign_line_edit->palette ();
if (my_callsign_.isEmpty ()) if (my_callsign_.isEmpty ())
{ {
@ -1236,11 +1277,13 @@ void Configuration::impl::initialize_models ()
ui_->sbDegrade->setValue (degrade_); ui_->sbDegrade->setValue (degrade_);
ui_->sbBandwidth->setValue (RxBandwidth_); ui_->sbBandwidth->setValue (RxBandwidth_);
ui_->PTT_method_button_group->button (rig_params_.ptt_type)->setChecked (true); ui_->PTT_method_button_group->button (rig_params_.ptt_type)->setChecked (true);
ui_->save_path_display_label->setText (save_directory_.absolutePath ()); ui_->save_path_display_label->setText (save_directory_.absolutePath ());
ui_->azel_path_display_label->setText (azel_directory_.absolutePath ()); ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
ui_->CW_id_after_73_check_box->setChecked (id_after_73_); ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_); ui_->tx_QSY_check_box->setChecked (tx_QSY_allowed_);
ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_); ui_->psk_reporter_check_box->setChecked (spot_to_psk_reporter_);
ui_->psk_reporter_tcpip_check_box->setChecked (psk_reporter_tcpip_);
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_);
ui_->log_as_RTTY_check_box->setChecked (log_as_RTTY_); ui_->log_as_RTTY_check_box->setChecked (log_as_RTTY_);
@ -1387,67 +1430,12 @@ void Configuration::impl::read_settings ()
save_directory_.setPath (settings_->value ("SaveDir", default_save_directory_.absolutePath ()).toString ()); save_directory_.setPath (settings_->value ("SaveDir", default_save_directory_.absolutePath ()).toString ());
azel_directory_.setPath (settings_->value ("AzElDir", default_azel_directory_.absolutePath ()).toString ()); azel_directory_.setPath (settings_->value ("AzElDir", default_azel_directory_.absolutePath ()).toString ());
{
//
// retrieve audio input device
//
auto saved_name = settings_->value ("SoundInName").toString ();
// deal with special Windows default audio devices
auto default_device = QAudioDeviceInfo::defaultInputDevice ();
if (saved_name == default_device.deviceName ())
{
audio_input_device_ = default_device;
default_audio_input_device_selected_ = true;
}
else
{
default_audio_input_device_selected_ = false;
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (QAudio::AudioInput)) // available audio input devices
{
if (p.deviceName () == saved_name)
{
audio_input_device_ = p;
}
}
}
}
{
//
// retrieve audio output device
//
auto saved_name = settings_->value("SoundOutName").toString();
// deal with special Windows default audio devices
auto default_device = QAudioDeviceInfo::defaultOutputDevice ();
if (saved_name == default_device.deviceName ())
{
audio_output_device_ = default_device;
default_audio_output_device_selected_ = true;
}
else
{
default_audio_output_device_selected_ = false;
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) // available audio output devices
{
if (p.deviceName () == saved_name)
{
audio_output_device_ = p;
}
}
}
}
// retrieve audio channel info
audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ());
audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ());
type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value<Configuration::Type2MsgGen> (); type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value<Configuration::Type2MsgGen> ();
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 ();
spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool (); spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool ();
psk_reporter_tcpip_ = settings_->value ("PSKReporterTCPIP", false).toBool ();
id_after_73_ = settings_->value ("After73", false).toBool (); id_after_73_ = settings_->value ("After73", false).toBool ();
tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool (); tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool ();
use_dynamic_grid_ = settings_->value ("AutoGrid", false).toBool (); use_dynamic_grid_ = settings_->value ("AutoGrid", false).toBool ();
@ -1543,6 +1531,33 @@ void Configuration::impl::read_settings ()
pwrBandTuneMemory_ = settings_->value("pwrBandTuneMemory",false).toBool (); pwrBandTuneMemory_ = settings_->value("pwrBandTuneMemory",false).toBool ();
} }
void Configuration::impl::find_audio_devices ()
{
//
// retrieve audio input device
//
auto saved_name = settings_->value ("SoundInName").toString ();
if (next_audio_input_device_.deviceName () != saved_name || next_audio_input_device_.isNull ())
{
next_audio_input_device_ = find_audio_device (QAudio::AudioInput, ui_->sound_input_combo_box, saved_name);
next_audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ());
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
ui_->sound_input_channel_combo_box->setCurrentIndex (next_audio_input_channel_);
}
//
// retrieve audio output device
//
saved_name = settings_->value("SoundOutName").toString();
if (next_audio_output_device_.deviceName () != saved_name || next_audio_output_device_.isNull ())
{
next_audio_output_device_ = find_audio_device (QAudio::AudioOutput, ui_->sound_output_combo_box, saved_name);
next_audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ());
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_output_channel_combo_box->setCurrentIndex (next_audio_output_channel_);
}
}
void Configuration::impl::write_settings () void Configuration::impl::write_settings ()
{ {
SettingsGroup g {settings_, "Configuration"}; SettingsGroup g {settings_, "Configuration"};
@ -1562,31 +1577,21 @@ void Configuration::impl::write_settings ()
settings_->setValue ("PTTport", rig_params_.ptt_port); settings_->setValue ("PTTport", rig_params_.ptt_port);
settings_->setValue ("SaveDir", save_directory_.absolutePath ()); settings_->setValue ("SaveDir", save_directory_.absolutePath ());
settings_->setValue ("AzElDir", azel_directory_.absolutePath ()); settings_->setValue ("AzElDir", azel_directory_.absolutePath ());
if (!audio_input_device_.isNull ())
if (default_audio_input_device_selected_)
{
settings_->setValue ("SoundInName", QAudioDeviceInfo::defaultInputDevice ().deviceName ());
}
else
{ {
settings_->setValue ("SoundInName", audio_input_device_.deviceName ()); settings_->setValue ("SoundInName", audio_input_device_.deviceName ());
settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_));
} }
if (!audio_output_device_.isNull ())
if (default_audio_output_device_selected_)
{
settings_->setValue ("SoundOutName", QAudioDeviceInfo::defaultOutputDevice ().deviceName ());
}
else
{ {
settings_->setValue ("SoundOutName", audio_output_device_.deviceName ()); settings_->setValue ("SoundOutName", audio_output_device_.deviceName ());
settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_));
} }
settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_));
settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_));
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_)); settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
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_);
settings_->setValue ("PSKReporter", spot_to_psk_reporter_); settings_->setValue ("PSKReporter", spot_to_psk_reporter_);
settings_->setValue ("PSKReporterTCPIP", psk_reporter_tcpip_);
settings_->setValue ("After73", id_after_73_); settings_->setValue ("After73", id_after_73_);
settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_); settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_);
settings_->setValue ("Macros", macros_.stringList ()); settings_->setValue ("Macros", macros_.stringList ());
@ -1653,6 +1658,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_); settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_);
settings_->setValue ("Region", QVariant::fromValue (region_)); settings_->setValue ("Region", QVariant::fromValue (region_));
settings_->setValue ("AutoGrid", use_dynamic_grid_); settings_->setValue ("AutoGrid", use_dynamic_grid_);
settings_->sync ();
} }
void Configuration::impl::set_rig_invariants () void Configuration::impl::set_rig_invariants ()
@ -1785,17 +1791,27 @@ void Configuration::impl::set_rig_invariants ()
bool Configuration::impl::validate () bool Configuration::impl::validate ()
{ {
if (ui_->sound_input_combo_box->currentIndex () < 0 if (ui_->sound_input_combo_box->currentIndex () < 0
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioInput).empty ()) && next_audio_input_device_.isNull ())
{ {
find_tab (ui_->sound_input_combo_box);
MessageBox::critical_message (this, tr ("Invalid audio input device"));
return false;
}
if (ui_->sound_input_channel_combo_box->currentIndex () < 0
&& next_audio_input_device_.isNull ())
{
find_tab (ui_->sound_input_combo_box);
MessageBox::critical_message (this, tr ("Invalid audio input device")); MessageBox::critical_message (this, tr ("Invalid audio input device"));
return false; return false;
} }
if (ui_->sound_output_combo_box->currentIndex () < 0 if (ui_->sound_output_combo_box->currentIndex () < 0
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioOutput).empty ()) && next_audio_output_device_.isNull ())
{ {
MessageBox::critical_message (this, tr ("Invalid audio out device")); find_tab (ui_->sound_output_combo_box);
return false; MessageBox::information_message (this, tr ("Invalid audio output device"));
// don't reject as we can work without an audio output
} }
if (!ui_->PTT_method_button_group->checkedButton ()->isEnabled ()) if (!ui_->PTT_method_button_group->checkedButton ()->isEnabled ())
@ -1817,16 +1833,7 @@ bool Configuration::impl::validate ()
if (ui_->rbField_Day->isEnabled () && ui_->rbField_Day->isChecked () && if (ui_->rbField_Day->isEnabled () && ui_->rbField_Day->isChecked () &&
!ui_->Field_Day_Exchange->hasAcceptableInput ()) !ui_->Field_Day_Exchange->hasAcceptableInput ())
{ {
for (auto * parent = ui_->Field_Day_Exchange->parentWidget (); parent; parent = parent->parentWidget ()) find_tab (ui_->Field_Day_Exchange);
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
ui_->Field_Day_Exchange->setFocus ();
MessageBox::critical_message (this, tr ("Invalid Contest Exchange") MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
, tr ("You must input a valid ARRL Field Day exchange")); , tr ("You must input a valid ARRL Field Day exchange"));
return false; return false;
@ -1835,16 +1842,7 @@ bool Configuration::impl::validate ()
if (ui_->rbRTTY_Roundup->isEnabled () && ui_->rbRTTY_Roundup->isChecked () && if (ui_->rbRTTY_Roundup->isEnabled () && ui_->rbRTTY_Roundup->isChecked () &&
!ui_->RTTY_Exchange->hasAcceptableInput ()) !ui_->RTTY_Exchange->hasAcceptableInput ())
{ {
for (auto * parent = ui_->RTTY_Exchange->parentWidget (); parent; parent = parent->parentWidget ()) find_tab (ui_->RTTY_Exchange);
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
ui_->RTTY_Exchange->setFocus ();
MessageBox::critical_message (this, tr ("Invalid Contest Exchange") MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
, tr ("You must input a valid ARRL RTTY Roundup exchange")); , tr ("You must input a valid ARRL RTTY Roundup exchange"));
return false; return false;
@ -1865,6 +1863,7 @@ int Configuration::impl::exec ()
rig_changed_ = false; rig_changed_ = false;
initialize_models (); initialize_models ();
return QDialog::exec(); return QDialog::exec();
} }
@ -1962,85 +1961,67 @@ void Configuration::impl::accept ()
// related configuration parameters // related configuration parameters
rig_is_dummy_ = TransceiverFactory::basic_transceiver_name_ == rig_params_.rig_name; rig_is_dummy_ = TransceiverFactory::basic_transceiver_name_ == rig_params_.rig_name;
// Check to see whether SoundInThread must be restarted,
// and save user parameters.
{ {
auto const& device_name = ui_->sound_input_combo_box->currentText (); auto const& selected_device = ui_->sound_input_combo_box->currentData ().value<audio_info_type> ().first;
if (device_name != audio_input_device_.deviceName ()) if (selected_device != next_audio_input_device_)
{ {
auto const& default_device = QAudioDeviceInfo::defaultInputDevice (); next_audio_input_device_ = selected_device;
if (device_name == default_device.deviceName ())
{
audio_input_device_ = default_device;
}
else
{
bool found {false};
Q_FOREACH (auto const& d, QAudioDeviceInfo::availableDevices (QAudio::AudioInput))
{
if (device_name == d.deviceName ())
{
audio_input_device_ = d;
found = true;
}
}
if (!found)
{
audio_input_device_ = default_device;
}
}
restart_sound_input_device_ = true;
} }
} }
{ {
auto const& device_name = ui_->sound_output_combo_box->currentText (); auto const& selected_device = ui_->sound_output_combo_box->currentData ().value<audio_info_type> ().first;
if (device_name != audio_output_device_.deviceName ()) if (selected_device != next_audio_output_device_)
{ {
auto const& default_device = QAudioDeviceInfo::defaultOutputDevice (); next_audio_output_device_ = selected_device;
if (device_name == default_device.deviceName ())
{
audio_output_device_ = default_device;
}
else
{
bool found {false};
Q_FOREACH (auto const& d, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput))
{
if (device_name == d.deviceName ())
{
audio_output_device_ = d;
found = true;
}
}
if (!found)
{
audio_output_device_ = default_device;
}
}
restart_sound_output_device_ = true;
} }
} }
if (audio_input_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ())) if (next_audio_input_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ()))
{ {
audio_input_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ()); next_audio_input_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_input_channel_combo_box->currentIndex ());
}
Q_ASSERT (next_audio_input_channel_ <= AudioDevice::Right);
if (next_audio_output_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ()))
{
next_audio_output_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ());
}
Q_ASSERT (next_audio_output_channel_ <= AudioDevice::Both);
if (audio_input_device_ != next_audio_input_device_ || next_audio_input_device_.isNull ())
{
audio_input_device_ = next_audio_input_device_;
restart_sound_input_device_ = true; restart_sound_input_device_ = true;
} }
Q_ASSERT (audio_input_channel_ <= AudioDevice::Right); if (audio_input_channel_ != next_audio_input_channel_)
if (audio_output_channel_ != static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ()))
{ {
audio_output_channel_ = static_cast<AudioDevice::Channel> (ui_->sound_output_channel_combo_box->currentIndex ()); audio_input_channel_ = next_audio_input_channel_;
restart_sound_input_device_ = true;
}
if (audio_output_device_ != next_audio_output_device_ || next_audio_output_device_.isNull ())
{
audio_output_device_ = next_audio_output_device_;
restart_sound_output_device_ = true; restart_sound_output_device_ = true;
} }
Q_ASSERT (audio_output_channel_ <= AudioDevice::Both); if (audio_output_channel_ != next_audio_output_channel_)
{
audio_output_channel_ = next_audio_output_channel_;
restart_sound_output_device_ = true;
}
// qDebug () << "Configure::accept: audio i/p:" << audio_input_device_.deviceName ()
// << "chan:" << audio_input_channel_
// << "o/p:" << audio_output_device_.deviceName ()
// << "chan:" << audio_output_channel_
// << "reset i/p:" << restart_sound_input_device_
// << "reset o/p:" << restart_sound_output_device_;
my_callsign_ = ui_->callsign_line_edit->text (); my_callsign_ = ui_->callsign_line_edit->text ();
my_grid_ = ui_->grid_line_edit->text (); my_grid_ = ui_->grid_line_edit->text ();
FD_exchange_= ui_->Field_Day_Exchange->text ().toUpper (); FD_exchange_= ui_->Field_Day_Exchange->text ().toUpper ();
RTTY_exchange_= ui_->RTTY_Exchange->text ().toUpper (); RTTY_exchange_= ui_->RTTY_Exchange->text ().toUpper ();
spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked (); spot_to_psk_reporter_ = ui_->psk_reporter_check_box->isChecked ();
psk_reporter_tcpip_ = ui_->psk_reporter_tcpip_check_box->isChecked ();
id_interval_ = ui_->CW_id_interval_spin_box->value (); id_interval_ = ui_->CW_id_interval_spin_box->value ();
ntrials_ = ui_->sbNtrials->value (); ntrials_ = ui_->sbNtrials->value ();
txDelay_ = ui_->sbTxDelay->value (); txDelay_ = ui_->sbTxDelay->value ();
@ -2172,6 +2153,13 @@ void Configuration::impl::reject ()
} }
} }
// qDebug () << "Configure::reject: audio i/p:" << audio_input_device_.deviceName ()
// << "chan:" << audio_input_channel_
// << "o/p:" << audio_output_device_.deviceName ()
// << "chan:" << audio_output_channel_
// << "reset i/p:" << restart_sound_input_device_
// << "reset o/p:" << restart_sound_output_device_;
QDialog::reject (); QDialog::reject ();
} }
@ -2300,16 +2288,6 @@ void Configuration::impl::on_PTT_method_button_group_buttonClicked (int /* id */
set_rig_invariants (); set_rig_invariants ();
} }
void Configuration::impl::on_sound_input_combo_box_currentTextChanged (QString const& text)
{
default_audio_input_device_selected_ = QAudioDeviceInfo::defaultInputDevice ().deviceName () == text;
}
void Configuration::impl::on_sound_output_combo_box_currentTextChanged (QString const& text)
{
default_audio_output_device_selected_ = QAudioDeviceInfo::defaultOutputDevice ().deviceName () == text;
}
void Configuration::impl::on_add_macro_line_edit_editingFinished () void Configuration::impl::on_add_macro_line_edit_editingFinished ()
{ {
ui_->add_macro_line_edit->setText (ui_->add_macro_line_edit->text ().toUpper ()); ui_->add_macro_line_edit->setText (ui_->add_macro_line_edit->text ().toUpper ());
@ -2688,6 +2666,7 @@ void Configuration::impl::transceiver_frequency (Frequency f)
current_offset_ = stations_.offset (f); current_offset_ = stations_.offset (f);
cached_rig_state_.frequency (apply_calibration (f + current_offset_)); cached_rig_state_.frequency (apply_calibration (f + current_offset_));
// qDebug () << "Configuration::impl::transceiver_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
} }
@ -2713,6 +2692,7 @@ void Configuration::impl::transceiver_tx_frequency (Frequency f)
cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_)); cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_));
} }
// qDebug () << "Configuration::impl::transceiver_tx_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
} }
} }
@ -2721,6 +2701,7 @@ void Configuration::impl::transceiver_mode (MODE m)
{ {
cached_rig_state_.online (true); // we want the rig online cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.mode (m); cached_rig_state_.mode (m);
// qDebug () << "Configuration::impl::transceiver_mode: n:" << transceiver_command_number_ + 1 << "m:" << m;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
} }
@ -2729,6 +2710,7 @@ void Configuration::impl::transceiver_ptt (bool on)
cached_rig_state_.online (true); // we want the rig online cached_rig_state_.online (true); // we want the rig online
set_cached_mode (); set_cached_mode ();
cached_rig_state_.ptt (on); cached_rig_state_.ptt (on);
// qDebug () << "Configuration::impl::transceiver_ptt: n:" << transceiver_command_number_ + 1 << "on:" << on;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_); Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
} }
@ -2827,72 +2809,68 @@ void Configuration::impl::close_rig ()
} }
} }
// load the available audio devices into the selection combo box and // find the audio device that matches the specified name, also
// select the default device if the current device isn't set or isn't // populate into the selection combo box with any devices we find in
// available // the search
bool Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * combo_box, QAudioDeviceInfo * device) QAudioDeviceInfo Configuration::impl::find_audio_device (QAudio::Mode mode, QComboBox * combo_box
, QString const& device_name)
{ {
using std::copy; using std::copy;
using std::back_inserter; using std::back_inserter;
bool result {false}; if (device_name.size ())
{
Q_EMIT self_->enumerating_audio_devices ();
auto const& devices = QAudioDeviceInfo::availableDevices (mode);
Q_FOREACH (auto const& p, devices)
{
// qDebug () << "Configuration::impl::find_audio_device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
if (p.deviceName () == device_name)
{
// convert supported channel counts into something we can store in the item model
QList<QVariant> channel_counts;
auto scc = p.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->insertItem (0, device_name, QVariant::fromValue (audio_info_type {p, channel_counts}));
combo_box->setCurrentIndex (0);
return p;
}
}
// insert a place holder for the not found device
combo_box->insertItem (0, device_name + " (" + tr ("Not found", "audio device missing") + ")", QVariant::fromValue (audio_info_type {}));
combo_box->setCurrentIndex (0);
}
return {};
}
// load the available audio devices into the selection combo box
void Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * combo_box
, QAudioDeviceInfo * device)
{
using std::copy;
using std::back_inserter;
combo_box->clear (); combo_box->clear ();
Q_EMIT self_->enumerating_audio_devices ();
int current_index = -1; int current_index = -1;
int default_index = -1; auto const& devices = QAudioDeviceInfo::availableDevices (mode);
Q_FOREACH (auto const& p, devices)
int extra_items {0};
auto const& default_device = (mode == QAudio::AudioInput ? QAudioDeviceInfo::defaultInputDevice () : QAudioDeviceInfo::defaultOutputDevice ());
// deal with special default audio devices on Windows
if ("Default Input Device" == default_device.deviceName ()
|| "Default Output Device" == default_device.deviceName ())
{ {
default_index = 0; // qDebug () << "Configuration::impl::load_audio_devices: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
QList<QVariant> channel_counts;
auto scc = default_device.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->addItem (default_device.deviceName (), channel_counts);
++extra_items;
if (default_device == *device)
{
current_index = 0;
result = true;
}
}
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (mode))
{
// qDebug () << "Audio device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
// convert supported channel counts into something we can store in the item model // convert supported channel counts into something we can store in the item model
QList<QVariant> channel_counts; QList<QVariant> channel_counts;
auto scc = p.supportedChannelCounts (); auto scc = p.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts)); copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->addItem (p.deviceName (), channel_counts); combo_box->addItem (p.deviceName (), QVariant::fromValue (audio_info_type {p, channel_counts}));
if (p == *device) if (p == *device)
{ {
current_index = combo_box->count () - 1; current_index = combo_box->count () - 1;
} }
else if (p == default_device)
{
default_index = combo_box->count () - 1;
}
}
if (current_index < 0) // not found - use default
{
*device = default_device;
result = true;
current_index = default_index;
} }
combo_box->setCurrentIndex (current_index); combo_box->setCurrentIndex (current_index);
return result;
} }
// enable only the channels that are supported by the selected audio device // enable only the channels that are supported by the selected audio device
@ -2904,7 +2882,8 @@ void Configuration::impl::update_audio_channels (QComboBox const * source_combo_
combo_box->setItemData (i, combo_box_item_disabled, Qt::UserRole - 1); combo_box->setItemData (i, combo_box_item_disabled, Qt::UserRole - 1);
} }
Q_FOREACH (QVariant const& v, source_combo_box->itemData (index).toList ()) Q_FOREACH (QVariant const& v
, (source_combo_box->itemData (index).value<audio_info_type> ().second))
{ {
// enable valid options // enable valid options
int n {v.toInt ()}; int n {v.toInt ()};
@ -2924,6 +2903,20 @@ void Configuration::impl::update_audio_channels (QComboBox const * source_combo_
} }
} }
void Configuration::impl::find_tab (QWidget * target)
{
for (auto * parent = target->parentWidget (); parent; parent = parent->parentWidget ())
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
target->setFocus ();
}
// load all the supported rig names into the selection combo box // load all the supported rig names into the selection combo box
void Configuration::impl::enumerate_rigs () void Configuration::impl::enumerate_rigs ()
{ {

View File

@ -113,6 +113,7 @@ public:
bool id_after_73 () const; bool id_after_73 () const;
bool tx_QSY_allowed () const; bool tx_QSY_allowed () const;
bool spot_to_psk_reporter () const; bool spot_to_psk_reporter () const;
bool psk_reporter_tcpip () const;
bool monitor_off_at_startup () const; bool monitor_off_at_startup () const;
bool monitor_last_used () const; bool monitor_last_used () const;
bool log_as_RTTY () const; bool log_as_RTTY () const;
@ -259,6 +260,8 @@ public:
// i.e. the transceiver is ready for use. // i.e. the transceiver is ready for use.
Q_SLOT void sync_transceiver (bool force_signal = false, bool enforce_mode_and_split = false); Q_SLOT void sync_transceiver (bool force_signal = false, bool enforce_mode_and_split = false);
Q_SLOT void invalidate_audio_input_device (QString error);
Q_SLOT void invalidate_audio_output_device (QString error);
// //
// These signals indicate a font has been selected and accepted for // These signals indicate a font has been selected and accepted for
@ -292,6 +295,12 @@ public:
// the fault condition has been rectified or is transient. // the fault condition has been rectified or is transient.
Q_SIGNAL void transceiver_failure (QString const& reason) const; Q_SIGNAL void transceiver_failure (QString const& reason) const;
// signal announces audio devices are being enumerated
//
// As this can take some time, particularly on Linux, consumers
// might like to notify the user.
Q_SIGNAL void enumerating_audio_devices ();
private: private:
class impl; class impl;
pimpl<impl> m_; pimpl<impl> m_;

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>557</width> <width>554</width>
<height>561</height> <height>556</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -357,7 +357,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QCheckBox" name="enable_VHF_features_check_box"> <widget class="QCheckBox" name="enable_VHF_features_check_box">
<property name="text"> <property name="text">
<string>Enable VHF/UHF/Microwave features</string> <string>Enable VHF and submode features</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -577,6 +577,9 @@ quiet period when decoding is done.</string>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="accessibleName">
<string>Serial Port Parameters</string>
</property>
<property name="title"> <property name="title">
<string>Serial Port Parameters</string> <string>Serial Port Parameters</string>
</property> </property>
@ -659,6 +662,9 @@ quiet period when decoding is done.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of data bits used to communicate with your radio's CAT interface (usually eight).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of data bits used to communicate with your radio's CAT interface (usually eight).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Data bits</string>
</property>
<property name="title"> <property name="title">
<string>Data Bits</string> <string>Data Bits</string>
</property> </property>
@ -710,6 +716,9 @@ quiet period when decoding is done.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of stop bits used when communicating with your radio's CAT interface&lt;/p&gt;&lt;p&gt;(consult you radio's manual for details).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of stop bits used when communicating with your radio's CAT interface&lt;/p&gt;&lt;p&gt;(consult you radio's manual for details).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Stop bits</string>
</property>
<property name="title"> <property name="title">
<string>Stop Bits</string> <string>Stop Bits</string>
</property> </property>
@ -758,6 +767,9 @@ quiet period when decoding is done.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Flow control protocol used between this computer and your radio's CAT interface (usually &amp;quot;None&amp;quot; but some require &amp;quot;Hardware&amp;quot;).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Flow control protocol used between this computer and your radio's CAT interface (usually &amp;quot;None&amp;quot; but some require &amp;quot;Hardware&amp;quot;).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Handshake</string>
</property>
<property name="title"> <property name="title">
<string>Handshake</string> <string>Handshake</string>
</property> </property>
@ -824,6 +836,9 @@ a few, particularly some Kenwood rigs, require it).</string>
<property name="toolTip"> <property name="toolTip">
<string>Special control of CAT port control lines.</string> <string>Special control of CAT port control lines.</string>
</property> </property>
<property name="accessibleName">
<string>Force Control Lines</string>
</property>
<property name="title"> <property name="title">
<string>Force Control Lines</string> <string>Force Control Lines</string>
</property> </property>
@ -1350,7 +1365,7 @@ radio interface behave as expected.</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_6"> <layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="1"> <item row="1" column="1">
<widget class="QComboBox" name="sound_output_combo_box"> <widget class="LazyFillComboBox" name="sound_output_combo_box">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch> <horstretch>1</horstretch>
@ -1366,29 +1381,6 @@ transmitting periods.</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QComboBox" name="sound_input_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the audio CODEC to use for receiving.</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="sound_input_label">
<property name="text">
<string>&amp;Input:</string>
</property>
<property name="buddy">
<cstring>sound_input_combo_box</cstring>
</property>
</widget>
</item>
<item row="0" column="2"> <item row="0" column="2">
<widget class="QComboBox" name="sound_input_channel_combo_box"> <widget class="QComboBox" name="sound_input_channel_combo_box">
<property name="toolTip"> <property name="toolTip">
@ -1416,6 +1408,29 @@ transmitting periods.</string>
</item> </item>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="LazyFillComboBox" name="sound_input_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the audio CODEC to use for receiving.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sound_output_label">
<property name="text">
<string>Ou&amp;tput:</string>
</property>
<property name="buddy">
<cstring>sound_output_combo_box</cstring>
</property>
</widget>
</item>
<item row="1" column="2"> <item row="1" column="2">
<widget class="QComboBox" name="sound_output_channel_combo_box"> <widget class="QComboBox" name="sound_output_channel_combo_box">
<property name="toolTip"> <property name="toolTip">
@ -1446,13 +1461,13 @@ both here.</string>
</item> </item>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="0" column="0">
<widget class="QLabel" name="sound_output_label"> <widget class="QLabel" name="sound_input_label">
<property name="text"> <property name="text">
<string>Ou&amp;tput:</string> <string>&amp;Input:</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>sound_output_combo_box</cstring> <cstring>sound_input_combo_box</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -1493,7 +1508,8 @@ both here.</string>
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string> <string notr="true">background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);</string>
</property> </property>
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -1541,7 +1557,8 @@ both here.</string>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string> <string notr="true">background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);</string>
</property> </property>
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
@ -1800,20 +1817,27 @@ and DX Grid fields when a 73 or free text message is sent.</string>
<property name="title"> <property name="title">
<string>Network Services</string> <string>Network Services</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_17"> <layout class="QHBoxLayout" name="horizontalLayout_22">
<item row="0" column="0"> <item>
<widget class="QCheckBox" name="psk_reporter_check_box"> <widget class="QCheckBox" name="psk_reporter_check_box">
<property name="toolTip"> <property name="toolTip">
<string>The program can send your station details and all <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The program can send your station details and all decoded signals with grid squares as spots to the http://pskreporter.info web site.&lt;/p&gt;&lt;p&gt;This is used for reverse beacon analysis which is very useful for assessing propagation and system performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
decoded signals as spots to the http://pskreporter.info web site.
This is used for reverse beacon analysis which is very useful
for assessing propagation and system performance.</string>
</property> </property>
<property name="text"> <property name="text">
<string>Enable &amp;PSK Reporter Spotting</string> <string>Enable &amp;PSK Reporter Spotting</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="psk_reporter_tcpip_check_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Check this option if a reliable connection is needed&lt;/p&gt;&lt;p&gt;Most users do not need this, the default uses UDP which is more efficient. Only check this if you have evidence that UDP traffic from you to PSK Reporter is being lost.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use TCP/IP connection</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -2337,6 +2361,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;URL of the ARRL LotW user's last upload dates and times data file which is used to highlight decodes from stations that are known to upload their log file to LotW.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;URL of the ARRL LotW user's last upload dates and times data file which is used to highlight decodes from stations that are known to upload their log file to LotW.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>URL</string>
</property>
<property name="text"> <property name="text">
<string>https://lotw.arrl.org/lotw-user-activity.csv</string> <string>https://lotw.arrl.org/lotw-user-activity.csv</string>
</property> </property>
@ -2369,6 +2396,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Days since last upload</string>
</property>
<property name="suffix"> <property name="suffix">
<string> days</string> <string> days</string>
</property> </property>
@ -2504,6 +2534,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;FT8 DXpedition mode: Hound operator calling the DX.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;FT8 DXpedition mode: Hound operator calling the DX.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Hound</string>
</property>
<property name="text"> <property name="text">
<string>Hound</string> <string>Hound</string>
</property> </property>
@ -2526,6 +2559,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;North American VHF/UHF/Microwave contests and others in which a 4-character grid locator is the required exchange.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;North American VHF/UHF/Microwave contests and others in which a 4-character grid locator is the required exchange.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>NA VHF Contest</string>
</property>
<property name="text"> <property name="text">
<string>NA VHF Contest</string> <string>NA VHF Contest</string>
</property> </property>
@ -2539,6 +2575,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;FT8 DXpedition mode: Fox (DXpedition) operator.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;FT8 DXpedition mode: Fox (DXpedition) operator.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>Fox</string>
</property>
<property name="text"> <property name="text">
<string>Fox</string> <string>Fox</string>
</property> </property>
@ -2561,6 +2600,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;European VHF+ contests requiring a signal report, serial number, and 6-character locator.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;European VHF+ contests requiring a signal report, serial number, and 6-character locator.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>EU VHF Contest</string>
</property>
<property name="text"> <property name="text">
<string>EU VHF Contest</string> <string>EU VHF Contest</string>
</property> </property>
@ -2589,6 +2631,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ARRL RTTY Roundup and similar contests. Exchange is US state, Canadian province, or &amp;quot;DX&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ARRL RTTY Roundup and similar contests. Exchange is US state, Canadian province, or &amp;quot;DX&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>R T T Y Roundup</string>
</property>
<property name="text"> <property name="text">
<string>RTTY Roundup messages</string> <string>RTTY Roundup messages</string>
</property> </property>
@ -2614,6 +2659,9 @@ Right click for insert and delete options.</string>
<layout class="QFormLayout" name="formLayout_17"> <layout class="QFormLayout" name="formLayout_17">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="labRTTY"> <widget class="QLabel" name="labRTTY">
<property name="accessibleName">
<string>RTTY Roundup exchange</string>
</property>
<property name="text"> <property name="text">
<string>RTTY RU Exch:</string> <string>RTTY RU Exch:</string>
</property> </property>
@ -2652,6 +2700,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ARRL Field Day exchange: number of transmitters, Class, and ARRL/RAC section or &amp;quot;DX&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ARRL Field Day exchange: number of transmitters, Class, and ARRL/RAC section or &amp;quot;DX&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>A R R L Field Day</string>
</property>
<property name="text"> <property name="text">
<string>ARRL Field Day</string> <string>ARRL Field Day</string>
</property> </property>
@ -2677,6 +2728,9 @@ Right click for insert and delete options.</string>
<layout class="QFormLayout" name="formLayout_16"> <layout class="QFormLayout" name="formLayout_16">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="labFD"> <widget class="QLabel" name="labFD">
<property name="accessibleName">
<string>Field Day exchange</string>
</property>
<property name="text"> <property name="text">
<string>FD Exch:</string> <string>FD Exch:</string>
</property> </property>
@ -2719,6 +2773,9 @@ Right click for insert and delete options.</string>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;World-Wide Digi-mode contest&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;World-Wide Digi-mode contest&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="accessibleName">
<string>WW Digital Contest</string>
</property>
<property name="text"> <property name="text">
<string>WW Digi Contest</string> <string>WW Digi Contest</string>
</property> </property>
@ -2831,6 +2888,9 @@ Right click for insert and delete options.</string>
<height>50</height> <height>50</height>
</size> </size>
</property> </property>
<property name="accessibleName">
<string>Tone spacing</string>
</property>
<property name="title"> <property name="title">
<string>Tone spacing</string> <string>Tone spacing</string>
</property> </property>
@ -2869,6 +2929,9 @@ Right click for insert and delete options.</string>
<height>50</height> <height>50</height>
</size> </size>
</property> </property>
<property name="accessibleName">
<string>Waterfall spectra</string>
</property>
<property name="title"> <property name="title">
<string>Waterfall spectra</string> <string>Waterfall spectra</string>
</property> </property>
@ -2934,6 +2997,11 @@ Right click for insert and delete options.</string>
<extends>QListView</extends> <extends>QListView</extends>
<header>widgets/DecodeHighlightingListView.hpp</header> <header>widgets/DecodeHighlightingListView.hpp</header>
</customwidget> </customwidget>
<customwidget>
<class>LazyFillComboBox</class>
<extends>QComboBox</extends>
<header>widgets/LazyFillComboBox.hpp</header>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>configuration_tabs</tabstop> <tabstop>configuration_tabs</tabstop>
@ -2942,14 +3010,20 @@ Right click for insert and delete options.</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>decodes_from_top_check_box</tabstop>
<tabstop>insert_blank_check_box</tabstop>
<tabstop>miles_check_box</tabstop>
<tabstop>TX_messages_check_box</tabstop> <tabstop>TX_messages_check_box</tabstop>
<tabstop>DXCC_check_box</tabstop> <tabstop>DXCC_check_box</tabstop>
<tabstop>ppfx_check_box</tabstop>
<tabstop>font_push_button</tabstop> <tabstop>font_push_button</tabstop>
<tabstop>decoded_text_font_push_button</tabstop> <tabstop>decoded_text_font_push_button</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>
<tabstop>disable_TX_on_73_check_box</tabstop> <tabstop>disable_TX_on_73_check_box</tabstop>
<tabstop>force_call_1st_check_box</tabstop>
<tabstop>alternate_bindings_check_box</tabstop>
<tabstop>CW_id_after_73_check_box</tabstop> <tabstop>CW_id_after_73_check_box</tabstop>
<tabstop>enable_VHF_features_check_box</tabstop> <tabstop>enable_VHF_features_check_box</tabstop>
<tabstop>tx_QSY_check_box</tabstop> <tabstop>tx_QSY_check_box</tabstop>
@ -2968,8 +3042,8 @@ Right click for insert and delete options.</string>
<tabstop>CAT_one_stop_bit_radio_button</tabstop> <tabstop>CAT_one_stop_bit_radio_button</tabstop>
<tabstop>CAT_two_stop_bit_radio_button</tabstop> <tabstop>CAT_two_stop_bit_radio_button</tabstop>
<tabstop>CAT_handshake_default_radio_button</tabstop> <tabstop>CAT_handshake_default_radio_button</tabstop>
<tabstop>CAT_handshake_none_radio_button</tabstop>
<tabstop>CAT_handshake_xon_radio_button</tabstop> <tabstop>CAT_handshake_xon_radio_button</tabstop>
<tabstop>CAT_handshake_none_radio_button</tabstop>
<tabstop>CAT_handshake_hardware_radio_button</tabstop> <tabstop>CAT_handshake_hardware_radio_button</tabstop>
<tabstop>force_DTR_combo_box</tabstop> <tabstop>force_DTR_combo_box</tabstop>
<tabstop>force_RTS_combo_box</tabstop> <tabstop>force_RTS_combo_box</tabstop>
@ -3007,6 +3081,7 @@ Right click for insert and delete options.</string>
<tabstop>clear_DX_check_box</tabstop> <tabstop>clear_DX_check_box</tabstop>
<tabstop>opCallEntry</tabstop> <tabstop>opCallEntry</tabstop>
<tabstop>psk_reporter_check_box</tabstop> <tabstop>psk_reporter_check_box</tabstop>
<tabstop>psk_reporter_tcpip_check_box</tabstop>
<tabstop>udp_server_line_edit</tabstop> <tabstop>udp_server_line_edit</tabstop>
<tabstop>udp_server_port_spin_box</tabstop> <tabstop>udp_server_port_spin_box</tabstop>
<tabstop>accept_udp_requests_check_box</tabstop> <tabstop>accept_udp_requests_check_box</tabstop>
@ -3021,9 +3096,13 @@ Right click for insert and delete options.</string>
<tabstop>stations_table_view</tabstop> <tabstop>stations_table_view</tabstop>
<tabstop>highlighting_list_view</tabstop> <tabstop>highlighting_list_view</tabstop>
<tabstop>reset_highlighting_to_defaults_push_button</tabstop> <tabstop>reset_highlighting_to_defaults_push_button</tabstop>
<tabstop>highlight_by_mode_check_box</tabstop>
<tabstop>only_fields_check_box</tabstop>
<tabstop>include_WAE_check_box</tabstop>
<tabstop>rescan_log_push_button</tabstop>
<tabstop>LotW_CSV_URL_line_edit</tabstop> <tabstop>LotW_CSV_URL_line_edit</tabstop>
<tabstop>LotW_CSV_fetch_push_button</tabstop>
<tabstop>LotW_days_since_upload_spin_box</tabstop> <tabstop>LotW_days_since_upload_spin_box</tabstop>
<tabstop>LotW_CSV_fetch_push_button</tabstop>
<tabstop>sbNtrials</tabstop> <tabstop>sbNtrials</tabstop>
<tabstop>sbAggressive</tabstop> <tabstop>sbAggressive</tabstop>
<tabstop>cbTwoPass</tabstop> <tabstop>cbTwoPass</tabstop>
@ -3032,13 +3111,18 @@ Right click for insert and delete options.</string>
<tabstop>sbTxDelay</tabstop> <tabstop>sbTxDelay</tabstop>
<tabstop>cbx2ToneSpacing</tabstop> <tabstop>cbx2ToneSpacing</tabstop>
<tabstop>cbx4ToneSpacing</tabstop> <tabstop>cbx4ToneSpacing</tabstop>
<tabstop>rbLowSidelobes</tabstop>
<tabstop>rbMaxSensitivity</tabstop>
<tabstop>gbSpecialOpActivity</tabstop>
<tabstop>rbFox</tabstop> <tabstop>rbFox</tabstop>
<tabstop>rbHound</tabstop>
<tabstop>rbNA_VHF_Contest</tabstop> <tabstop>rbNA_VHF_Contest</tabstop>
<tabstop>rbEU_VHF_Contest</tabstop>
<tabstop>rbField_Day</tabstop> <tabstop>rbField_Day</tabstop>
<tabstop>Field_Day_Exchange</tabstop> <tabstop>Field_Day_Exchange</tabstop>
<tabstop>rbEU_VHF_Contest</tabstop>
<tabstop>rbRTTY_Roundup</tabstop> <tabstop>rbRTTY_Roundup</tabstop>
<tabstop>RTTY_Exchange</tabstop> <tabstop>RTTY_Exchange</tabstop>
<tabstop>rbWW_DIGI</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>
<connections> <connections>
@ -3108,13 +3192,13 @@ Right click for insert and delete options.</string>
</connection> </connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="CAT_handshake_button_group"/> <buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_audio_source_button_group"/> <buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="special_op_activity_button_group"/> <buttongroup name="special_op_activity_button_group"/>
<buttongroup name="TX_mode_button_group"/> <buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/> <buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="PTT_method_button_group"/> <buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="split_mode_button_group"/> <buttongroup name="split_mode_button_group"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -165,7 +165,13 @@ QString DecodedText::call() const
// get the second word, most likely the de call and the third word, most likely grid // get the second word, most likely the de call and the third word, most likely grid
void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) const void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) const
{ {
auto const& match = words_re.match (message_); auto msg = message_;
auto p = msg.indexOf ("; ");
if (p >= 0)
{
msg = msg.mid (p + 2);
}
auto const& match = words_re.match (msg);
call = match.captured ("word2"); call = match.captured ("word2");
grid = match.captured ("word3"); grid = match.captured ("word3");
if ("R" == grid) grid = match.captured ("word4"); if ("R" == grid) grid = match.captured ("word4");

3
Decoder/decodedtext.pri Normal file
View File

@ -0,0 +1,3 @@
SOURCES += Decoder/decodedtext.cpp
HEADERS += Decoder/decodedtext.h

View File

@ -36,7 +36,7 @@ void Detector::setBlockSize (unsigned n)
bool Detector::reset () bool Detector::reset ()
{ {
clear (); clear ();
// don't call base call reset because it calls seek(0) which causes // don't call base class reset because it calls seek(0) which causes
// a warning // a warning
return isOpen (); return isOpen ();
} }
@ -119,8 +119,7 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
remaining -= numFramesProcessed; remaining -= numFramesProcessed;
} }
// we drop any data past the end of the buffer on the floor until
// the next period starts
return maxSize; // we drop any data past the end of the buffer on return maxSize;
// the floor until the next period starts
} }

3
Detector/Detector.pri Normal file
View File

@ -0,0 +1,3 @@
SOURCES += Detector/Detector.cpp
HEADERS += Detector/Detector.hpp

View File

@ -292,7 +292,7 @@ EqualizationToolsDialog::impl::impl (EqualizationToolsDialog * self
| QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Close | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Close
, Qt::Vertical} , Qt::Vertical}
{ {
setWindowTitle (windowTitle () + ' ' + tr (title)); setWindowTitle (windowTitle () + ' ' + tr ("Equalization Tools"));
resize (500, 600); resize (500, 600);
{ {
SettingsGroup g {settings_, title}; SettingsGroup g {settings_, title};

14
INSTALL
View File

@ -28,7 +28,7 @@ For MS Windows see the section "Building from Source on MS Windows"
below. For Apple Mac see the section "Building from Source on Apple below. For Apple Mac see the section "Building from Source on Apple
Mac". Mac".
Qt v5, preferably v5.5 or later is required to build WSJT-X. Qt v5, preferably v5.9 or later is required to build WSJT-X.
Qt v5 multimedia support, serial port, and Linguist is necessary as Qt v5 multimedia support, serial port, and Linguist is necessary as
well as the core Qt v5 components, normally installing the Qt well as the core Qt v5 components, normally installing the Qt
@ -43,8 +43,8 @@ the libfftw library development package. Normally installing the
library development package pulls in all the FFTW v3 libraries library development package pulls in all the FFTW v3 libraries
including the single precision variant. including the single precision variant.
The Hamlib library optionally requires the libusb-1.0 library, if the The Hamlib library optionally requires the libusb-1.0-1 library, if
development version (libusb-1.0-dev) is available Hamlib will the development version (libusb-1.0-0-dev) is available Hamlib will
configure its custom USB device back end drivers. Most rigs do not 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 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 but if you have a SoftRock USB or similar SDR that uses a custom USB
@ -89,7 +89,8 @@ $ git clone git://git.code.sf.net/p/wsjt/wsjtx src
To build WSJT-X you will need CMake and asciidoc installed. To build WSJT-X you will need CMake and asciidoc installed.
$ cd ~/wsjtx-prefix/build $ cd ~/wsjtx-prefix/build
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix ../src $ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix -DWSJT_SKIP_MANPAGES=ON \
-DWSJT_GENERATE_DOCS=OFF ../src
$ cmake --build . $ cmake --build .
$ cmake --build . --target install $ cmake --build . --target install
@ -99,7 +100,8 @@ configure step like:
$ cd ~/wsjtx-prefix/build $ cd ~/wsjtx-prefix/build
$ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix \ $ cmake -D CMAKE_PREFIX_PATH=~/hamlib-prefix \
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix ../src -DWSJT_SKIP_MANPAGES=ON -DWSJT_GENERATE_DOCS=OFF \
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix ../src
$ cmake --build . $ cmake --build .
$ cmake --build . --target install $ cmake --build . --target install
@ -316,7 +318,7 @@ configure:
$ cd ~/wsjtx-prefix/build $ cd ~/wsjtx-prefix/build
$ FC=gfortran-mp-5 \ $ FC=gfortran-mp-5 \
cmake \ cmake \
-D CMAKE_PREFIX_PATH="~/Qt/5.7/clang_64;~/hamlib-prefix;/opt/local" \ -D CMAKE_PREFIX_PATH="~/Qt/5.9/clang_64;~/hamlib-prefix;/opt/local" \
-D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix \ -D CMAKE_INSTALL_PREFIX=~/wsjtx-prefix \
-D CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk \ -D CMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk \
~/wsjtx-prefix/src ~/wsjtx-prefix/src

View File

@ -45,11 +45,12 @@ Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds,
{ {
} }
void Modulator::start (unsigned symbolsLength, double framesPerSymbol, void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSymbol,
double frequency, double toneSpacing, double frequency, double toneSpacing,
SoundOutput * stream, Channel channel, SoundOutput * stream, Channel channel,
bool synchronize, bool fastMode, double dBSNR, double TRperiod) bool synchronize, bool fastMode, double dBSNR, double TRperiod)
{ {
// qDebug () << "mode:" << mode << "symbolsLength:" << symbolsLength << "framesPerSymbol:" << framesPerSymbol << "frequency:" << frequency << "toneSpacing:" << toneSpacing << "channel:" << channel << "synchronize:" << synchronize << "fastMode:" << fastMode << "dBSNR:" << dBSNR << "TRperiod:" << TRperiod;
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 = QDateTime::currentMSecsSinceEpoch() % 86400000;
@ -69,8 +70,8 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
m_bFastMode=fastMode; m_bFastMode=fastMode;
m_TRperiod=TRperiod; m_TRperiod=TRperiod;
unsigned delay_ms=1000; unsigned delay_ms=1000;
if(m_nsps==1920) delay_ms=500; //FT8 if(mode=="FT8" or (mode=="FST4" and m_nsps==720)) delay_ms=500; //FT8, FST4-15
if(m_nsps==576) delay_ms=300; //FT4 if(mode=="FT4") delay_ms=300; //FT4
// noise generator parameters // noise generator parameters
if (m_addNoise) { if (m_addNoise) {
@ -79,28 +80,39 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
if (m_snr > 1.0) m_fac = 3000.0 / m_snr; if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
} }
// round up to an exact portion of a second that allows for startup delays
m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
if(m_bFastMode) m_ic=0;
m_silentFrames = 0; m_silentFrames = 0;
// calculate number of silent frames to send, so that audio will start at m_ic=0;
// the nominal time "delay_ms" into the Tx sequence. if (!m_tuning && !m_bFastMode)
if (synchronize && !m_tuning && !m_bFastMode) { {
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000)); // calculate number of silent frames to send, so that audio will
} // start at the nominal time "delay_ms" into the Tx sequence.
if (synchronize)
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz") {
// << m_ic << m_silentFrames << m_silentFrames/48000.0 if(delay_ms > mstr) m_silentFrames = (delay_ms - mstr) * m_frameRate / 1000;
// << mstr << fmod(double(ms0),1000.0*m_period); }
// adjust for late starts
if(!m_silentFrames && mstr >= delay_ms)
{
m_ic = (mstr - delay_ms) * m_frameRate / 1000;
}
}
initialize (QIODevice::ReadOnly, channel); initialize (QIODevice::ReadOnly, channel);
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ? Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
Synchronizing : Active)); Synchronizing : Active));
// qDebug() << "delay_ms:" << delay_ms << "mstr:" << mstr << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_stream = stream; m_stream = stream;
if (m_stream) m_stream->restart (this); if (m_stream)
{
m_stream->restart (this);
}
else
{
qDebug () << "Modulator::start: no audio output stream assigned";
}
} }
void Modulator::tune (bool newState) void Modulator::tune (bool newState)
@ -152,21 +164,25 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
qint16 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint16))); qint16 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint16)));
qint64 framesGenerated (0); qint64 framesGenerated (0);
// if(m_ic==0) qDebug() << "Modulator::readData" << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod)); // if(m_ic==0) qDebug() << "aa" << 0.001*(QDateTime::currentMSecsSinceEpoch() % qint64(1000*m_TRperiod))
// << m_state << m_TRperiod << m_silentFrames << m_ic << foxcom_.wave[m_ic];
switch (m_state) switch (m_state)
{ {
case Synchronizing: case Synchronizing:
{ {
if (m_silentFrames) { // send silence up to first second if (m_silentFrames) { // send silence up to end of start delay
framesGenerated = qMin (m_silentFrames, numFrames); framesGenerated = qMin (m_silentFrames, numFrames);
for ( ; samples != end; samples = load (0, samples)) { // silence do
} {
m_silentFrames -= framesGenerated; samples = load (0, samples); // silence
return framesGenerated * bytesPerFrame (); } while (--m_silentFrames && samples != end);
if (!m_silentFrames)
{
Q_EMIT stateChanged ((m_state = Active));
}
} }
Q_EMIT stateChanged ((m_state = Active));
m_cwLevel = false; m_cwLevel = false;
m_ramp = 0; // prepare for CW wave shaping m_ramp = 0; // prepare for CW wave shaping
} }
@ -260,7 +276,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
qint16 sample; qint16 sample;
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) { while (samples != end && m_ic <= i1) {
isym=0; isym=0;
if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000 if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000
if(m_bFastMode) isym=isym%m_symbolsLength; if(m_bFastMode) isym=isym%m_symbolsLength;
@ -305,12 +321,19 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
m_amp=32767.0; m_amp=32767.0;
sample=qRound(m_amp*foxcom_.wave[m_ic]); sample=qRound(m_amp*foxcom_.wave[m_ic]);
} }
/*
if((m_ic<1000 or (4*m_symbolsLength*m_nsps - m_ic) < 1000) and (m_ic%10)==0) {
qDebug() << "cc" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz") << m_ic << sample;
}
*/
samples = load(postProcessSample(sample), samples); samples = load(postProcessSample(sample), samples);
++framesGenerated; ++framesGenerated;
++m_ic; ++m_ic;
} }
// qDebug() << "dd" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
// << m_ic << i1 << foxcom_.wave[m_ic] << framesGenerated;
if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
if (icw[0] == 0) { if (icw[0] == 0) {
// no CW ID to send // no CW ID to send

View File

@ -35,7 +35,7 @@ public:
void set_nsym(int n) {m_symbolsLength=n;} void set_nsym(int n) {m_symbolsLength=n;}
void set_ms0(qint64 ms) {m_ms0=ms;} void set_ms0(qint64 ms) {m_ms0=ms;}
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency, Q_SLOT void start (QString mode, unsigned symbolsLength, double framesPerSymbol, double frequency,
double toneSpacing, SoundOutput *, Channel = Mono, double toneSpacing, SoundOutput *, Channel = Mono,
bool synchronize = true, bool fastMode = false, bool synchronize = true, bool fastMode = false,
double dBSNR = 99., double TRperiod=60.0); double dBSNR = 99., double TRperiod=60.0);

3
Modulator/Modulator.pri Normal file
View File

@ -0,0 +1,3 @@
SOURCES += Modulator/Modulator.cpp
HEADERS += Modulator/Mpdulator.hpp

26
NEWS
View File

@ -13,6 +13,32 @@
Copyright 2001 - 2020 by Joe Taylor, K1JT. Copyright 2001 - 2020 by Joe Taylor, K1JT.
Release: WSJT-X 2.3.0-rc1
Sept 28, 2020
-------------------------
WSJT-X 2.3.0 is a program upgrade offering two new modes designed
especially for use on the LF and MF bands. FST4 is for 2-way QSOs,
and FST4W is for WSPR-like transmissions. Both modes offer a range of
options for T/R sequence lengths and threshold decoding sensitivities
extending well into the -40 dB range. Early tests have shown these
modes frequently spanning intercontinental distances on the 2200 m and
630 m bands. Further details and operating hints can be found in the
"Quick-Start Guide to FST4 and FST4W", posted on the WSJT web site:
https://physics.princeton.edu/pulsar/k1jt/FST4_Quick_Start.pdf
WSJT-X 2.3.0-rc1 is a beta-quality release candidate for a program
upgrade that provides a number of new features and capabilities.
These include:
- New modes FST4 and FST4W
- The *On Dx Echo* Doppler compensation method has been modified in
response to feedback from Users. Basic functionality is unchanged.
See the User Guide (Section 8.1) for more information.
Release: WSJT-X 2.2.2 Release: WSJT-X 2.2.2
June 22, 2020 June 22, 2020
--------------------- ---------------------

View File

@ -36,6 +36,7 @@ public:
impl (QString const& id, QString const& version, QString const& revision, impl (QString const& id, QString const& version, QString const& revision,
port_type server_port, MessageClient * self) port_type server_port, MessageClient * self)
: self_ {self} : self_ {self}
, dns_lookup_id_ {0}
, enabled_ {false} , enabled_ {false}
, id_ {id} , id_ {id}
, version_ {version} , version_ {version}
@ -81,6 +82,7 @@ public:
Q_SLOT void host_info_results (QHostInfo); Q_SLOT void host_info_results (QHostInfo);
MessageClient * self_; MessageClient * self_;
int dns_lookup_id_;
bool enabled_; bool enabled_;
QString id_; QString id_;
QString version_; QString version_;
@ -101,6 +103,7 @@ public:
void MessageClient::impl::host_info_results (QHostInfo host_info) void MessageClient::impl::host_info_results (QHostInfo host_info)
{ {
if (host_info.lookupId () != dns_lookup_id_) return;
if (QHostInfo::NoError != host_info.error ()) if (QHostInfo::NoError != host_info.error ())
{ {
Q_EMIT self_->error ("UDP server lookup failed:\n" + host_info.errorString ()); Q_EMIT self_->error ("UDP server lookup failed:\n" + host_info.errorString ());
@ -187,7 +190,13 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
quint8 modifiers {0}; quint8 modifiers {0};
in >> time >> snr >> delta_time >> delta_frequency >> mode >> message in >> time >> snr >> delta_time >> delta_frequency >> mode >> message
>> low_confidence >> modifiers; >> low_confidence >> modifiers;
TRACE_UDP ("Reply: time:" << time << "snr:" << snr << "dt:" << delta_time << "df:" << delta_frequency << "mode:" << mode << "message:" << message << "low confidence:" << low_confidence << "modifiers: 0x" << hex << modifiers); TRACE_UDP ("Reply: time:" << time << "snr:" << snr << "dt:" << delta_time << "df:" << delta_frequency << "mode:" << mode << "message:" << message << "low confidence:" << low_confidence << "modifiers: 0x"
#if QT_VERSION >= QT_VERSION_CHECK (5, 15, 0)
<< Qt::hex
#else
<< hex
#endif
<< modifiers);
if (check_status (in) != Fail) if (check_status (in) != Fail)
{ {
Q_EMIT self_->reply (time, snr, delta_time, delta_frequency Q_EMIT self_->reply (time, snr, delta_time, delta_frequency
@ -423,30 +432,24 @@ MessageClient::MessageClient (QString const& id, QString const& version, QString
: QObject {self} : QObject {self}
, m_ {id, version, revision, server_port, this} , m_ {id, version, revision, server_port, this}
{ {
connect (&*m_
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
connect (&*m_, static_cast<void (impl::*) (impl::SocketError)> (&impl::error) , static_cast<void (impl::*) (impl::SocketError)> (&impl::error), [this] (impl::SocketError e)
, [this] (impl::SocketError e) #else
{ , &impl::errorOccurred, [this] (impl::SocketError e)
#endif
{
#if defined (Q_OS_WIN) #if defined (Q_OS_WIN)
if (e != impl::NetworkError // take this out when Qt 5.5 if (e != impl::NetworkError // take this out when Qt 5.5 stops doing this spuriously
// stops doing this && e != impl::ConnectionRefusedError) // not interested in this with UDP socket
// spuriously {
&& e != impl::ConnectionRefusedError) // not
// interested
// in this with
// UDP socket
#else #else
Q_UNUSED (e); {
Q_UNUSED (e);
#endif #endif
{
Q_EMIT error (m_->errorString ());
}
});
#else
connect (&*m_, &impl::errorOccurred, [this] (impl::SocketError) {
Q_EMIT error (m_->errorString ()); Q_EMIT error (m_->errorString ());
}); }
#endif });
set_server (server); set_server (server);
} }
@ -464,11 +467,15 @@ void MessageClient::set_server (QString const& server)
{ {
m_->server_.clear (); m_->server_.clear ();
m_->server_string_ = server; m_->server_string_ = server;
if (!server.isEmpty ()) if (server.size ())
{ {
// queue a host address lookup // queue a host address lookup
TRACE_UDP ("server host DNS lookup:" << server); TRACE_UDP ("server host DNS lookup:" << server);
QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo))); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, &MessageClient::impl::host_info_results);
#else
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo)));
#endif
} }
} }
@ -477,27 +484,6 @@ void MessageClient::set_server_port (port_type server_port)
m_->server_port_ = server_port; m_->server_port_ = server_port;
} }
qint64 MessageClient::send_raw_datagram (QByteArray const& message, QHostAddress const& dest_address
, port_type dest_port)
{
if (dest_port && !dest_address.isNull ())
{
return m_->writeDatagram (message, dest_address, dest_port);
}
return 0;
}
void MessageClient::add_blocked_destination (QHostAddress const& a)
{
m_->blocked_addresses_.push_back (a);
if (a == m_->server_)
{
m_->server_.clear ();
Q_EMIT error ("UDP server blocked, please try another");
m_->pending_messages_.clear (); // discard
}
}
void MessageClient::enable (bool flag) void MessageClient::enable (bool flag)
{ {
m_->enabled_ = flag; m_->enabled_ = flag;
@ -573,7 +559,7 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
, QString const& comments, QString const& name, QDateTime time_on , QString const& comments, QString const& name, QDateTime time_on
, QString const& operator_call, QString const& my_call , QString const& operator_call, QString const& my_call
, QString const& my_grid, QString const& exchange_sent , QString const& my_grid, QString const& exchange_sent
, QString const& exchange_rcvd) , QString const& exchange_rcvd, QString const& propmode)
{ {
if (m_->server_port_ && !m_->server_string_.isEmpty ()) if (m_->server_port_ && !m_->server_string_.isEmpty ())
{ {
@ -582,8 +568,8 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
out << time_off << dx_call.toUtf8 () << dx_grid.toUtf8 () << dial_frequency << mode.toUtf8 () out << time_off << dx_call.toUtf8 () << dx_grid.toUtf8 () << dial_frequency << mode.toUtf8 ()
<< report_sent.toUtf8 () << report_received.toUtf8 () << tx_power.toUtf8 () << comments.toUtf8 () << report_sent.toUtf8 () << report_received.toUtf8 () << tx_power.toUtf8 () << comments.toUtf8 ()
<< name.toUtf8 () << time_on << operator_call.toUtf8 () << my_call.toUtf8 () << my_grid.toUtf8 () << name.toUtf8 () << time_on << operator_call.toUtf8 () << my_call.toUtf8 () << my_grid.toUtf8 ()
<< exchange_sent.toUtf8 () << exchange_rcvd.toUtf8 (); << exchange_sent.toUtf8 () << exchange_rcvd.toUtf8 () << propmode.toUtf8 ();
TRACE_UDP ("time off:" << time_off << "DX:" << dx_call << "DX grid:" << dx_grid << "dial:" << dial_frequency << "mode:" << mode << "sent:" << report_sent << "rcvd:" << report_received << "pwr:" << tx_power << "comments:" << comments << "name:" << name << "time on:" << time_on << "op:" << operator_call << "DE:" << my_call << "DE grid:" << my_grid << "exch sent:" << exchange_sent << "exch rcvd:" << exchange_rcvd); TRACE_UDP ("time off:" << time_off << "DX:" << dx_call << "DX grid:" << dx_grid << "dial:" << dial_frequency << "mode:" << mode << "sent:" << report_sent << "rcvd:" << report_received << "pwr:" << tx_power << "comments:" << comments << "name:" << name << "time on:" << time_on << "op:" << operator_call << "DE:" << my_call << "DE grid:" << my_grid << "exch sent:" << exchange_sent << "exch rcvd:" << exchange_rcvd << "prop_mode:" << propmode);
m_->send_message (out, message); m_->send_message (out, message);
} }
} }

View File

@ -69,21 +69,13 @@ public:
, QString const& report_received, QString const& tx_power, QString const& comments , QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call , QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid , QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd); , QString const& exchange_sent, QString const& exchange_rcvd
, QString const& propmode);
// ADIF_record argument should be valid ADIF excluding any <EOR> end // ADIF_record argument should be valid ADIF excluding any <EOR> end
// of record marker // of record marker
Q_SLOT void logged_ADIF (QByteArray const& ADIF_record); Q_SLOT void logged_ADIF (QByteArray const& ADIF_record);
// this may be used to send arbitrary UDP datagrams to and
// destination allowing the underlying socket to be used for general
// UDP messaging if desired
qint64 send_raw_datagram (QByteArray const&, QHostAddress const& dest_address, port_type dest_port);
// disallowed message destination (does not block datagrams sent
// with send_raw_datagram() above)
Q_SLOT void add_blocked_destination (QHostAddress const&);
// this signal is emitted if the server has requested a decode // this signal is emitted if the server has requested a decode
// window clear action // window clear action
Q_SIGNAL void clear_decodes (quint8 window); Q_SIGNAL void clear_decodes (quint8 window);

View File

@ -0,0 +1,62 @@
#include "Network/NetworkAccessManager.hpp"
#include <QString>
#include <QNetworkReply>
#include "moc_NetworkAccessManager.cpp"
NetworkAccessManager::NetworkAccessManager (QWidget * parent)
: QNetworkAccessManager (parent)
, parent_widget_ {parent}
{
// handle SSL errors that have not been cached as allowed
// exceptions and offer them to the user to add to the ignored
// exception cache
connect (this, &QNetworkAccessManager::sslErrors, this, &NetworkAccessManager::filter_SSL_errors);
}
void NetworkAccessManager::filter_SSL_errors (QNetworkReply * reply, QList<QSslError> const& errors)
{
QString message;
QList<QSslError> new_errors;
for (auto const& error: errors)
{
if (!allowed_ssl_errors_.contains (error))
{
new_errors << error;
message += '\n' + reply->request ().url ().toDisplayString () + ": " + error.errorString ();
}
}
if (new_errors.size ())
{
QString certs;
for (auto const& cert : reply->sslConfiguration ().peerCertificateChain ())
{
certs += cert.toText () + '\n';
}
if (MessageBox::Ignore == MessageBox::query_message (parent_widget_
, tr ("Network SSL/TLS Errors")
, message, certs
, MessageBox::Abort | MessageBox::Ignore))
{
// accumulate new SSL error exceptions that have been allowed
allowed_ssl_errors_.append (new_errors);
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
else
{
// no new exceptions so silently ignore the ones already allowed
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
QNetworkReply * NetworkAccessManager::createRequest (Operation operation, QNetworkRequest const& request
, QIODevice * outgoing_data)
{
auto reply = QNetworkAccessManager::createRequest (operation, request, outgoing_data);
// errors are usually certificate specific so passing all cached
// exceptions here is ok
reply->ignoreSslErrors (allowed_ssl_errors_);
return reply;
}

View File

@ -4,8 +4,6 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QList> #include <QList>
#include <QSslError> #include <QSslError>
#include <QNetworkReply>
#include <QString>
#include "widgets/MessageBox.hpp" #include "widgets/MessageBox.hpp"
@ -18,58 +16,18 @@ class QWidget;
class NetworkAccessManager class NetworkAccessManager
: public QNetworkAccessManager : public QNetworkAccessManager
{ {
Q_OBJECT
public: public:
NetworkAccessManager (QWidget * parent) explicit NetworkAccessManager (QWidget * parent);
: QNetworkAccessManager (parent)
{
// handle SSL errors that have not been cached as allowed
// exceptions and offer them to the user to add to the ignored
// exception cache
connect (this, &QNetworkAccessManager::sslErrors, [this, &parent] (QNetworkReply * reply, QList<QSslError> const& errors) {
QString message;
QList<QSslError> new_errors;
for (auto const& error: errors)
{
if (!allowed_ssl_errors_.contains (error))
{
new_errors << error;
message += '\n' + reply->request ().url ().toDisplayString () + ": "
+ error.errorString ();
}
}
if (new_errors.size ())
{
QString certs;
for (auto const& cert : reply->sslConfiguration ().peerCertificateChain ())
{
certs += cert.toText () + '\n';
}
if (MessageBox::Ignore == MessageBox::query_message (parent, tr ("Network SSL Errors"), message, certs, MessageBox::Abort | MessageBox::Ignore))
{
// accumulate new SSL error exceptions that have been allowed
allowed_ssl_errors_.append (new_errors);
reply->ignoreSslErrors (allowed_ssl_errors_);
}
}
else
{
// no new exceptions so silently ignore the ones already allowed
reply->ignoreSslErrors (allowed_ssl_errors_);
}
});
}
protected: protected:
QNetworkReply * createRequest (Operation operation, QNetworkRequest const& request, QIODevice * outgoing_data = nullptr) override QNetworkReply * createRequest (Operation, QNetworkRequest const&, QIODevice * = nullptr) override;
{
auto reply = QNetworkAccessManager::createRequest (operation, request, outgoing_data);
// errors are usually certificate specific so passing all cached
// exceptions here is ok
reply->ignoreSslErrors (allowed_ssl_errors_);
return reply;
}
private: private:
void filter_SSL_errors (QNetworkReply * reply, QList<QSslError> const& errors);
QWidget * parent_widget_;
QList<QSslError> allowed_ssl_errors_; QList<QSslError> allowed_ssl_errors_;
}; };

View File

@ -308,6 +308,7 @@
* My grid utf8 * My grid utf8
* Exchange sent utf8 * Exchange sent utf8
* Exchange received utf8 * Exchange received utf8
* ADIF Propagation mode utf8
* *
* The QSO logged message is sent to the server(s) when the * The QSO logged message is sent to the server(s) when the
* WSJT-X user accepts the "Log QSO" dialog by clicking the "OK" * WSJT-X user accepts the "Log QSO" dialog by clicking the "OK"

521
Network/PSKReporter.cpp Normal file
View File

@ -0,0 +1,521 @@
#include "PSKReporter.hpp"
// Interface for posting spots to PSK Reporter web site
// Implemented by Edson Pereira PY2SDR
// Updated by Bill Somerville, G4WJS
//
// Reports will be sent in batch mode every 5 minutes.
#include <cmath>
#include <QObject>
#include <QString>
#include <QDateTime>
#include <QSharedPointer>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QHostInfo>
#include <QQueue>
#include <QByteArray>
#include <QDataStream>
#include <QTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
#endif
#include "Configuration.hpp"
#include "pimpl_impl.hpp"
#include "moc_PSKReporter.cpp"
namespace
{
QLatin1String HOST {"report.pskreporter.info"};
// QLatin1String HOST {"127.0.0.1"};
quint16 SERVICE_PORT {4739};
// quint16 SERVICE_PORT {14739};
int MIN_SEND_INTERVAL {15}; // in seconds
int FLUSH_INTERVAL {4 * 5}; // in send intervals
bool ALIGNMENT_PADDING {true};
int MIN_PAYLOAD_LENGTH {508};
int MAX_PAYLOAD_LENGTH {1400};
}
class PSKReporter::impl final
: public QObject
{
Q_OBJECT
public:
impl (PSKReporter * self, Configuration const * config, QString const& program_info)
: self_ {self}
, config_ {config}
, sequence_number_ {0u}
, send_descriptors_ {0}
, send_receiver_data_ {0}
, flush_counter_ {0u}
, prog_id_ {program_info}
{
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
observation_id_ = qrand();
#else
observation_id_ = QRandomGenerator::global ()->generate ();
#endif
// This timer sets the interval to check for spots to send.
connect (&report_timer_, &QTimer::timeout, [this] () {send_report ();});
// This timer repeats the sending of IPFIX templates and receiver
// information if we are using UDP, in case server has been
// restarted ans lost cached information.
connect (&descriptor_timer_, &QTimer::timeout, [this] () {
if (socket_
&& QAbstractSocket::UdpSocket == socket_->socketType ())
{
// send templates again
send_descriptors_ = 3; // three times
// send receiver data set again
send_receiver_data_ = 3; // three times
}
});
}
void check_connection ()
{
if (!socket_
|| QAbstractSocket::UnconnectedState == socket_->state ()
|| (socket_->socketType () != config_->psk_reporter_tcpip () ? QAbstractSocket::TcpSocket : QAbstractSocket::UdpSocket))
{
// we need to create the appropriate socket
if (socket_
&& QAbstractSocket::UnconnectedState != socket_->state ()
&& QAbstractSocket::ClosingState != socket_->state ())
{
// handle re-opening asynchronously
auto connection = QSharedPointer<QMetaObject::Connection>::create ();
*connection = connect (socket_.data (), &QAbstractSocket::disconnected, [this, connection] () {
disconnect (*connection);
check_connection ();
});
// close gracefully
send_report (true);
socket_->close ();
}
else
{
reconnect ();
}
}
}
void handle_socket_error (QAbstractSocket::SocketError e)
{
switch (e)
{
case QAbstractSocket::RemoteHostClosedError:
socket_->disconnectFromHost ();
break;
case QAbstractSocket::TemporaryError:
break;
default:
spots_.clear ();
Q_EMIT self_->errorOccurred (socket_->errorString ());
break;
}
}
void reconnect ()
{
// Using deleteLater for the deleter as we may eventually
// be called from the disconnected handler above.
if (config_->psk_reporter_tcpip ())
{
socket_.reset (new QTcpSocket, &QObject::deleteLater);
send_descriptors_ = 1;
send_receiver_data_ = 1;
}
else
{
socket_.reset (new QUdpSocket, &QObject::deleteLater);
send_descriptors_ = 3;
send_receiver_data_ = 3;
}
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
connect (socket_.get (), &QAbstractSocket::errorOccurred, this, &PSKReporter::impl::handle_socket_error);
#else
connect (socket_.data (), QOverload<QAbstractSocket::SocketError>::of (&QAbstractSocket::error), this, &PSKReporter::impl::handle_socket_error);
#endif
// use this for pseudo connection with UDP, allows us to use
// QIODevice::write() instead of QUDPSocket::writeDatagram()
socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly);
if (!report_timer_.isActive ())
{
report_timer_.start (MIN_SEND_INTERVAL * 1000);
}
if (!descriptor_timer_.isActive ())
{
descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly
}
}
void stop ()
{
if (socket_)
{
socket_->disconnectFromHost ();
}
descriptor_timer_.stop ();
report_timer_.stop ();
}
void send_report (bool send_residue = false);
void build_preamble (QDataStream&);
bool flushing ()
{
return FLUSH_INTERVAL && !(++flush_counter_ % FLUSH_INTERVAL);
}
PSKReporter * self_;
Configuration const * config_;
QSharedPointer<QAbstractSocket> socket_;
int dns_lookup_id_;
QByteArray payload_;
quint32 sequence_number_;
int send_descriptors_;
// Currently PSK Reporter requires that a receiver data set is sent
// in every data flow. This memeber variable can be used to only
// send that information at session start (3 times for UDP), when it
// changes (3 times for UDP), or once per hour (3 times) if using
// UDP. Uncomment the relevant code to enable that fuctionality.
int send_receiver_data_;
unsigned flush_counter_;
quint32 observation_id_;
QString rx_call_;
QString rx_grid_;
QString rx_ant_;
QString prog_id_;
QByteArray tx_data_;
QByteArray tx_residue_;
struct Spot
{
bool operator == (Spot const& rhs)
{
return
call_ == rhs.call_
&& grid_ == rhs.grid_
&& mode_ == rhs.mode_
&& std::abs (Radio::FrequencyDelta (freq_ - rhs.freq_)) < 50;
}
QString call_;
QString grid_;
int snr_;
Radio::Frequency freq_;
QString mode_;
QDateTime time_;
};
QQueue<Spot> spots_;
QTimer report_timer_;
QTimer descriptor_timer_;
};
#include "PSKReporter.moc"
namespace
{
void writeUtfString (QDataStream& out, QString const& s)
{
auto const& utf = s.toUtf8 ().left (254);
out << quint8 (utf.size ());
out.writeRawData (utf, utf.size ());
}
int num_pad_bytes (int len)
{
return ALIGNMENT_PADDING ? (4 - len % 4) % 4 : 0;
}
void set_length (QDataStream& out, QByteArray& b)
{
// pad with nulls modulo 4
auto pad_len = num_pad_bytes (b.size ());
out.writeRawData (QByteArray {pad_len, '\0'}.constData (), pad_len);
auto pos = out.device ()->pos ();
out.device ()->seek (sizeof (quint16));
// insert length
out << static_cast<quint16> (b.size ());
out.device ()->seek (pos);
}
}
void PSKReporter::impl::build_preamble (QDataStream& message)
{
// Message Header
message
<< quint16 (10u) // Version Number
<< quint16 (0u) // Length (place-holder filled in later)
<< quint32 (0u) // Export Time (place-holder filled in later)
<< ++sequence_number_ // Sequence Number
<< observation_id_; // Observation Domain ID
if (send_descriptors_)
{
--send_descriptors_;
{
// Sender Information descriptor
QByteArray descriptor;
QDataStream out {&descriptor, QIODevice::WriteOnly};
out
<< quint16 (2u) // Template Set ID
<< quint16 (0u) // Length (place-holder)
<< quint16 (0x50e3) // Link ID
<< quint16 (7u) // Field Count
<< quint16 (0x8000 + 1u) // Option 1 Information Element ID (senderCallsign)
<< quint16 (0xffff) // Option 1 Field Length (variable)
<< quint32 (30351u) // Option 1 Enterprise Number
<< quint16 (0x8000 + 5u) // Option 2 Information Element ID (frequency)
<< quint16 (4u) // Option 2 Field Length
<< quint32 (30351u) // Option 2 Enterprise Number
<< quint16 (0x8000 + 6u) // Option 3 Information Element ID (sNR)
<< quint16 (1u) // Option 3 Field Length
<< quint32 (30351u) // Option 3 Enterprise Number
<< quint16 (0x8000 + 10u) // Option 4 Information Element ID (mode)
<< quint16 (0xffff) // Option 4 Field Length (variable)
<< quint32 (30351u) // Option 4 Enterprise Number
<< quint16 (0x8000 + 3u) // Option 5 Information Element ID (senderLocator)
<< quint16 (0xffff) // Option 5 Field Length (variable)
<< quint32 (30351u) // Option 5 Enterprise Number
<< quint16 (0x8000 + 11u) // Option 6 Information Element ID (informationSource)
<< quint16 (1u) // Option 6 Field Length
<< quint32 (30351u) // Option 6 Enterprise Number
<< quint16 (150u) // Option 7 Information Element ID (dateTimeSeconds)
<< quint16 (4u); // Option 7 Field Length
// insert Length and move to payload
set_length (out, descriptor);
message.writeRawData (descriptor.constData (), descriptor.size ());
}
{
// Receiver Information descriptor
QByteArray descriptor;
QDataStream out {&descriptor, QIODevice::WriteOnly};
out
<< quint16 (3u) // Options Template Set ID
<< quint16 (0u) // Length (place-holder)
<< quint16 (0x50e2) // Link ID
<< quint16 (4u) // Field Count
<< quint16 (0u) // Scope Field Count
<< quint16 (0x8000 + 2u) // Option 1 Information Element ID (receiverCallsign)
<< quint16 (0xffff) // Option 1 Field Length (variable)
<< quint32 (30351u) // Option 1 Enterprise Number
<< quint16 (0x8000 + 4u) // Option 2 Information Element ID (receiverLocator)
<< quint16 (0xffff) // Option 2 Field Length (variable)
<< quint32 (30351u) // Option 2 Enterprise Number
<< quint16 (0x8000 + 8u) // Option 3 Information Element ID (decodingSoftware)
<< quint16 (0xffff) // Option 3 Field Length (variable)
<< quint32 (30351u) // Option 3 Enterprise Number
<< quint16 (0x8000 + 9u) // Option 4 Information Element ID (antennaInformation)
<< quint16 (0xffff) // Option 4 Field Length (variable)
<< quint32 (30351u); // Option 4 Enterprise Number
// insert Length
set_length (out, descriptor);
message.writeRawData (descriptor.constData (), descriptor.size ());
}
}
// if (send_receiver_data_)
{
// --send_receiver_data_;
// Receiver information
QByteArray data;
QDataStream out {&data, QIODevice::WriteOnly};
// Set Header
out
<< quint16 (0x50e2) // Template ID
<< quint16 (0u); // Length (place-holder)
// Set data
writeUtfString (out, rx_call_);
writeUtfString (out, rx_grid_);
writeUtfString (out, prog_id_);
writeUtfString (out, rx_ant_);
// insert Length and move to payload
set_length (out, data);
message.writeRawData (data.constData (), data.size ());
}
}
void PSKReporter::impl::send_report (bool send_residue)
{
if (QAbstractSocket::ConnectedState != socket_->state ()) return;
QDataStream message {&payload_, QIODevice::WriteOnly | QIODevice::Append};
QDataStream tx_out {&tx_data_, QIODevice::WriteOnly | QIODevice::Append};
if (!payload_.size ())
{
// Build header, optional descriptors, and receiver information
build_preamble (message);
}
auto flush = flushing () || send_residue;
while (spots_.size () || flush)
{
if (!payload_.size ())
{
// Build header, optional descriptors, and receiver information
build_preamble (message);
}
if (!tx_data_.size () && (spots_.size () || tx_residue_.size ()))
{
// Set Header
tx_out
<< quint16 (0x50e3) // Template ID
<< quint16 (0u); // Length (place-holder)
}
// insert any residue
if (tx_residue_.size ())
{
tx_out.writeRawData (tx_residue_.constData (), tx_residue_.size ());
tx_residue_.clear ();
}
while (spots_.size () || flush)
{
auto tx_data_size = tx_data_.size ();
if (spots_.size ())
{
auto const& spot = spots_.dequeue ();
// Sender information
writeUtfString (tx_out, spot.call_);
tx_out
<< static_cast<quint32> (spot.freq_)
<< static_cast<qint8> (spot.snr_);
writeUtfString (tx_out, spot.mode_);
writeUtfString (tx_out, spot.grid_);
tx_out
<< quint8 (1u) // REPORTER_SOURCE_AUTOMATIC
<< static_cast<quint32> (
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
spot.time_.toSecsSinceEpoch ()
#else
spot.time_.toMSecsSinceEpoch () / 1000
#endif
);
}
auto len = payload_.size () + tx_data_.size ();
len += num_pad_bytes (tx_data_.size ());
len += num_pad_bytes (len);
if (len > MAX_PAYLOAD_LENGTH // our upper datagram size limit
|| (!spots_.size () && len > MIN_PAYLOAD_LENGTH) // spots drained and above lower datagram size limit
|| (flush && !spots_.size ())) // send what we have, possibly no spots
{
if (tx_data_.size ())
{
if (len <= MAX_PAYLOAD_LENGTH)
{
tx_data_size = tx_data_.size ();
}
QByteArray tx {tx_data_.left (tx_data_size)};
QDataStream out {&tx, QIODevice::WriteOnly | QIODevice::Append};
// insert Length
set_length (out, tx);
message.writeRawData (tx.constData (), tx.size ());
}
// insert Length and Export Time
set_length (message, payload_);
message.device ()->seek (2 * sizeof (quint16));
message << static_cast<quint32> (
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
QDateTime::currentDateTime ().toSecsSinceEpoch ()
#else
QDateTime::currentDateTime ().toMSecsSinceEpoch () / 1000
#endif
);
// Send data to PSK Reporter site
socket_->write (payload_); // TODO: handle errors
flush = false; // break loop
message.device ()->seek (0u);
payload_.clear (); // Fresh message
// Save unsent spots
tx_residue_ = tx_data_.right (tx_data_.size () - tx_data_size);
tx_out.device ()->seek (0u);
tx_data_.clear ();
break;
}
}
}
}
PSKReporter::PSKReporter (Configuration const * config, QString const& program_info)
: m_ {this, config, program_info}
{
}
PSKReporter::~PSKReporter ()
{
// m_->send_report (true); // send any pending spots
}
void PSKReporter::reconnect ()
{
m_->reconnect ();
}
void PSKReporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna)
{
m_->check_connection ();
if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_)
{
m_->send_receiver_data_ = m_->socket_
&& QAbstractSocket::UdpSocket == m_->socket_->socketType () ? 3 : 1;
m_->rx_call_ = call;
m_->rx_grid_ = gridSquare;
m_->rx_ant_ = antenna;
}
}
bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq
, QString const& mode, int snr)
{
m_->check_connection ();
if (m_->socket_ && m_->socket_->isValid ())
{
if (QAbstractSocket::UnconnectedState == m_->socket_->state ())
{
reconnect ();
}
m_->spots_.enqueue ({call, grid, snr, freq, mode, QDateTime::currentDateTimeUtc ()});
return true;
}
return false;
}
void PSKReporter::sendReport (bool last)
{
m_->check_connection ();
if (m_->socket_ && QAbstractSocket::ConnectedState == m_->socket_->state ())
{
m_->send_report (true);
}
if (last)
{
m_->stop ();
}
}

41
Network/PSKReporter.hpp Normal file
View File

@ -0,0 +1,41 @@
#ifndef PSK_REPORTER_HPP_
#define PSK_REPORTER_HPP_
#include <QObject>
#include "Radio.hpp"
#include "pimpl_h.hpp"
class QString;
class Configuration;
class PSKReporter final
: public QObject
{
Q_OBJECT
public:
explicit PSKReporter (Configuration const *, QString const& program_info);
~PSKReporter ();
void reconnect ();
void setLocalStation (QString const& call, QString const& grid, QString const& antenna);
//
// Returns false if PSK Reporter connection is not available
//
bool addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq, QString const& mode, int snr);
//
// Flush any pending spots to PSK Reporter
//
void sendReport (bool last = false);
Q_SIGNAL void errorOccurred (QString const& reason);
private:
class impl;
pimpl<impl> m_;
};
#endif

View File

@ -1,140 +0,0 @@
// KISS Interface for posting spots to PSK Reporter web site
// Implemented by Edson Pereira PY2SDR
//
// Reports will be sent in batch mode every 5 minutes.
#include "psk_reporter.h"
#include <QHostInfo>
#include <QTimer>
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
#include <QRandomGenerator>
#endif
#include "Network/MessageClient.hpp"
#include "moc_psk_reporter.cpp"
namespace
{
int constexpr MAX_PAYLOAD_LENGTH {1400};
}
PSK_Reporter::PSK_Reporter(MessageClient * message_client, QObject *parent) :
QObject {parent},
m_messageClient {message_client},
reportTimer {new QTimer {this}},
m_sequenceNumber {0}
{
m_header_h = "000Allllttttttttssssssssiiiiiiii";
// We use 50E2 and 50E3 for link Id
m_rxInfoDescriptor_h = "0003002C50E200040000"
"8002FFFF0000768F" // 2. Rx Call
"8004FFFF0000768F" // 4. Rx Grid
"8008FFFF0000768F" // 8. Rx Soft
"8009FFFF0000768F" // 9. Rx Antenna
"0000";
m_txInfoDescriptor_h = "0002003C50E30007"
"8001FFFF0000768F" // 1. Tx Call
"800500040000768F" // 5. Tx Freq
"800600010000768F" // 6. Tx snr
"800AFFFF0000768F" // 10. Tx Mode
"8003FFFF0000768F" // 3. Tx Grid
"800B00010000768F" // 11. Tx info src
"00960004"; // Report time
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0'));
#else
m_randomId_h = QString("%1").arg(QRandomGenerator::global ()->generate (), 8, 16, QChar('0'));
#endif
QHostInfo::lookupHost("report.pskreporter.info", this, SLOT(dnsLookupResult(QHostInfo)));
connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport()));
reportTimer->start(5*60*1000); // 5 minutes;
}
void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo)
{
m_rxCall = call;
m_rxGrid = gridSquare;
m_rxAnt = antenna;
m_progId = programInfo;
}
void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time )
{
QHash<QString,QString> spot;
spot["call"] = call;
spot["grid"] = grid;
spot["snr"] = snr;
spot["freq"] = freq;
spot["mode"] = mode;
spot["time"] = time;
m_spotQueue.enqueue(spot);
}
void PSK_Reporter::sendReport()
{
while (!m_spotQueue.isEmpty()) {
QString report_h;
// Header
QString header_h = m_header_h;
header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0')));
header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0')));
header_h.replace("iiiiiiii", m_randomId_h);
// Receiver information
QString rxInfoData_h = "50E2llll";
rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex();
rxInfoData_h += "0000";
rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0')));
// Sender information
QString txInfoData_h = "50E3llll";
while (!m_spotQueue.isEmpty()
&& (header_h.size () + m_rxInfoDescriptor_h.size () + m_txInfoDescriptor_h.size () + rxInfoData_h.size () + txInfoData_h.size ()) / 2 < MAX_PAYLOAD_LENGTH) {
QHash<QString,QString> spot = m_spotQueue.dequeue();
txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0'));
txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2);
txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC
txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0'));
}
txInfoData_h += "0000";
txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0')));
report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h;
//qDebug() << "Sending Report TX: ";
report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0')));
QByteArray report = QByteArray::fromHex(report_h.toUtf8());
// Send data to PSK Reporter site
if (!m_pskReporterAddress.isNull()) {
m_messageClient->send_raw_datagram (report, m_pskReporterAddress, 4739);
}
}
}
void PSK_Reporter::dnsLookupResult(QHostInfo info)
{
if (!info.addresses().isEmpty()) {
m_pskReporterAddress = info.addresses().at(0);
// qDebug() << "PSK Reporter IP: " << m_pskReporterAddress;
// deal with miss-configured settings that attempt to set a
// Pskreporter Internet address for the WSJT-X UDP protocol
// server address
m_messageClient->add_blocked_destination (m_pskReporterAddress);
}
}

View File

@ -1,54 +0,0 @@
// -*- Mode: C++ -*-
#ifndef PSK_REPORTER_H
#define PSK_REPORTER_H
#include <QObject>
#include <QString>
#include <QHostAddress>
#include <QQueue>
#include <QHash>
class MessageClient;
class QTimer;
class QHostInfo;
class PSK_Reporter : public QObject
{
Q_OBJECT
public:
explicit PSK_Reporter(MessageClient *, QObject *parent = nullptr);
void setLocalStation(QString call, QString grid, QString antenna, QString programInfo);
void addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time);
signals:
public slots:
void sendReport();
private slots:
void dnsLookupResult(QHostInfo info);
private:
QString m_header_h;
QString m_rxInfoDescriptor_h;
QString m_txInfoDescriptor_h;
QString m_randomId_h;
QString m_linkId_h;
QString m_rxCall;
QString m_rxGrid;
QString m_rxAnt;
QString m_progId;
QHostAddress m_pskReporterAddress;
QQueue< QHash<QString,QString> > m_spotQueue;
MessageClient * m_messageClient;
QTimer *reportTimer;
int m_sequenceNumber;
};
#endif // PSK_REPORTER_H

View File

@ -0,0 +1,12 @@
senderCallsign(30351/1)<string>[65535]
receiverCallsign(30351/2)<string>[65535]
senderLocator(30351/3)<string>[65535]
receiverLocator(30351/4)<string>[65535]
frequency(30351/5)<unsigned32>[4]
sNR(30351/6)<signed8>[1]
iMD(30351/7)<signed8>[1]
decoderSoftware(30351/8)<string>[65535]
antennaInformation(30351/9)<string>[65535]
mode(30351/10)<string>[65535]
informationSource(30351/11)<signed8>[1]
persistentIdentifier(30351/12)<string>[65535]

View File

@ -0,0 +1,94 @@
from __future__ import unicode_literals
import sys
import argparse
import logging
import time
import threading
import ipfix.ie
import ipfix.reader
import ipfix.message
import socketserver
import socket
class IPFixDatagramHandler (socketserver.DatagramRequestHandler):
def handle (self):
logging.info (f'Connection from {self.client_address}')
try:
self.server.msg_buffer.from_bytes (self.packet)
for rec in self.server.msg_buffer.namedict_iterator ():
logging.info (f't: {self.server.msg_buffer.get_export_time()}: {rec}')
except:
logging.error ('Unexpected exception:', sys.exc_info ()[0])
class IPFixUdpServer (socketserver.UDPServer):
def __init__ (self, *args, **kwargs):
self.msg_buffer = ipfix.message.MessageBuffer ()
super ().__init__ (*args, **kwargs)
class IPFixStreamHandler (socketserver.StreamRequestHandler):
def handle (self):
logging.info (f'Connection from {self.client_address}')
try:
msg_reader = ipfix.reader.from_stream (self.rfile)
for rec in msg_reader.namedict_iterator ():
logging.info (f't: {msg_reader.msg.get_export_time()}: {rec}')
logging.info (f'{self.client_address} closed their connection')
except ConnectionResetError:
logging.info (f'{self.client_address} connection reset')
except TimeoutError:
logging.info (f'{self.client_address} connection timed out')
except:
logging.error ('Unexpected exception:', sys.exc_info ()[0])
if __name__ == "__main__":
INTERFACE, PORT = '', 4739
ap = argparse.ArgumentParser (description='Dump IPFIX data collectedover UDP')
ap.add_argument ('-l', '--log', metavar='loglevel', default='WARNING', help='logging level')
ap.add_argument ('-s', '--spec', metavar='specfile', help='iespec file to read')
args = ap.parse_args ()
log_level = getattr (logging, args.log.upper (), None)
if not isinstance (log_level, int):
raise ValueError (f'Invalif log level: {log_level}')
logging.basicConfig (
level=log_level
, format='[%(levelname)s] (%(threadName)-10s) %(message)s'
,
)
logging.info (f'Starting IPFix servers on service port: {PORT}')
ipfix.ie.use_iana_default ()
ipfix.ie.use_5103_default ()
if args.spec:
ipfix.ie.use_specfile (args.spec)
udp_server = IPFixUdpServer ((INTERFACE, PORT), IPFixDatagramHandler)
udp_thread = threading.Thread (name='UDP Server', target=udp_server.serve_forever)
tcp_server = socketserver.TCPServer ((INTERFACE, PORT), IPFixStreamHandler)
tcp_server.allow_reuse_address = True
tcp_server.socket.setsockopt (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
tcp_thread = threading.Thread (name='TCP/IP Server', target=tcp_server.serve_forever)
udp_thread.start ()
tcp_thread.start ()
try:
while True:
time.sleep (1000)
except KeyboardInterrupt:
logging.warning ('Closing down servers')
udp_server.shutdown ()
tcp_server.shutdown ()
udp_thread.join ()
tcp_thread.join ()
logging.info ('Servers closed')

View File

@ -0,0 +1,64 @@
The following changes since commit e487dfbead9965c1a251d110dc55039a7ba4afef:
sphinx doc update (2017-09-20 16:07:22 +0200)
are available in the Git repository at:
git@github.com:g4wjs/python-ipfix.git varlen-and-padding
for you to fetch changes up to 6dcc106f22d25ac21b27f8ccb9b82be12e7eb18e:
Parse and emit data elements correctly when variable length items included (2020-06-19 19:53:33 +0100)
----------------------------------------------------------------
Bill Somerville (2):
Allow for alignment padding when parsing sets
Parse and emit data elements correctly when variable length items included
ipfix/message.py | 3 ++-
ipfix/template.py | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/ipfix/message.py b/ipfix/message.py
index 0af2fad..71c1328 100644
--- a/ipfix/message.py
+++ b/ipfix/message.py
@@ -398,7 +398,7 @@ class MessageBuffer(object):
offset += _sethdr_st.size # skip set header in decode
if setid == template.TEMPLATE_SET_ID or\
setid == template.OPTIONS_SET_ID:
- while offset < setend:
+ while offset + 4 < setend: # allow for padding up to 4 byte alignment
(tmpl, offset) = template.decode_template_from(
self.mbuf, offset, setid)
# FIXME handle withdrawal
@@ -431,6 +431,7 @@ class MessageBuffer(object):
# KeyError on template lookup - unknown data set
self.unknown_data_set_hook(self,
self.mbuf[offset-_sethdr_st.size:setend])
+ offset = setend # real end may be greater that accumulated offset
def namedict_iterator(self):
"""
diff --git a/ipfix/template.py b/ipfix/template.py
index 1203e55..26cd8c1 100644
--- a/ipfix/template.py
+++ b/ipfix/template.py
@@ -187,7 +187,7 @@ class Template(object):
offset += packplan.st.size
# short circuit on no varlen
- if not self.varlenslice:
+ if self.varlenslice is None:
return (vals, offset)
# direct iteration over remaining IEs
@@ -239,7 +239,7 @@ class Template(object):
offset += packplan.st.size
# shortcircuit no varlen
- if not self.varlenslice:
+ if self.varlenslice is None:
return offset
# direct iteration over remaining IEs

View File

@ -8,6 +8,8 @@
#include <QTimer> #include <QTimer>
#include <QFile> #include <QFile>
#include <QRegExp>
#include <QRegularExpression>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkReply> #include <QNetworkReply>
@ -18,215 +20,314 @@
namespace namespace
{ {
char const * const wsprNetUrl = "http://wsprnet.org/post?"; char const * const wsprNetUrl = "http://wsprnet.org/post/";
// char const * const wsprNetUrl = "http://127.0.0.1/post?"; //char const * const wsprNetUrl = "http://127.0.0.1:5000/post/";
//
// tested with this python REST mock of WSPRNet.org
//
/*
# Mock WSPRNet.org RESTful API
from flask import Flask, request, url_for
from flask_restful import Resource, Api
app = Flask(__name__)
@app.route ('/post/', methods=['GET', 'POST'])
def spot ():
if request.method == 'POST':
print (request.form)
return "1 spot(s) added"
with app.test_request_context ():
print (url_for ('spot'))
*/
// regexp to parse FST4W decodes
QRegularExpression fst4_re {R"(
(?<time>\d{4})
\s+(?<db>[-+]?\d+)
\s+(?<dt>[-+]?\d+\.\d+)
\s+(?<freq>\d+)
\s+`
\s+<?(?<call>[A-Z0-9/]+)>?(?:\s(?<grid>[A-R]{2}[0-9]{2}(?:[A-X]{2})?))?(?:\s+(?<dBm>\d+))?
)", QRegularExpression::ExtendedPatternSyntaxOption};
// regexp to parse wspr_spots.txt from wsprd
//
// 130223 2256 7 -21 -0.3 14.097090 DU1MGA PK04 37 0 40 0
// Date Time Sync dBm DT Freq Msg
// 1 2 3 4 5 6 -------7------ 8 9 10
QRegularExpression wspr_re(R"(^(\d+)\s+(\d+)\s+(\d+)\s+([+-]?\d+)\s+([+-]?\d+\.\d+)\s+(\d+\.\d+)\s+([^ ].*[^ ])\s+([+-]?\d+)\s+([+-]?\d+)\s+([+-]?\d+))");
}; };
WSPRNet::WSPRNet(QNetworkAccessManager * manager, QObject *parent) WSPRNet::WSPRNet (QNetworkAccessManager * manager, QObject *parent)
: QObject{parent} : QObject {parent}
, networkManager {manager} , network_manager_ {manager}
, uploadTimer {new QTimer {this}} , spots_to_send_ {0}
, m_urlQueueSize {0}
{ {
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReply(QNetworkReply*))); connect (network_manager_, &QNetworkAccessManager::finished, this, &WSPRNet::networkReply);
connect( uploadTimer, SIGNAL(timeout()), this, SLOT(work())); connect (&upload_timer_, &QTimer::timeout, this, &WSPRNet::work);
} }
void WSPRNet::upload(QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq, void WSPRNet::upload (QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq,
QString const& mode, QString const& tpct, QString const& dbm, QString const& version, QString const& mode, float TR_period, QString const& tpct, QString const& dbm,
QString const& fileName) QString const& version, QString const& fileName)
{ {
m_call = call; m_call = call;
m_grid = grid; m_grid = grid;
m_rfreq = rfreq; m_rfreq = rfreq;
m_tfreq = tfreq; m_tfreq = tfreq;
m_mode = mode; m_mode = mode;
m_tpct = tpct; TR_period_ = TR_period;
m_dbm = dbm; m_tpct = tpct;
m_vers = version; m_dbm = dbm;
m_file = fileName; m_vers = version;
m_file = fileName;
// Open the wsprd.out file // Open the wsprd.out file
QFile wsprdOutFile(fileName); QFile wsprdOutFile (fileName);
if (!wsprdOutFile.open(QIODevice::ReadOnly | QIODevice::Text) || if (!wsprdOutFile.open (QIODevice::ReadOnly | QIODevice::Text) || !wsprdOutFile.size ())
wsprdOutFile.size() == 0) { {
urlQueue.enqueue( wsprNetUrl + urlEncodeNoSpot()); spot_queue_.enqueue (urlEncodeNoSpot ());
m_uploadType = 1; m_uploadType = 1;
uploadTimer->start(200);
return;
} }
else
// Read the contents {
while (!wsprdOutFile.atEnd()) { // Read the contents
QHash<QString,QString> query; while (!wsprdOutFile.atEnd())
if ( decodeLine(wsprdOutFile.readLine(), query) ) { {
// Prevent reporting data ouside of the current frequency band SpotQueue::value_type query;
float f = fabs(m_rfreq.toFloat() - query["tqrg"].toFloat()); if (decodeLine (wsprdOutFile.readLine(), query))
if (f < 0.0002) { {
urlQueue.enqueue( wsprNetUrl + urlEncodeSpot(query)); // Prevent reporting data ouside of the current frequency band
m_uploadType = 2; float f = fabs (m_rfreq.toFloat() - query.queryItemValue ("tqrg", QUrl::FullyDecoded).toFloat());
if (f < 0.0002)
{
spot_queue_.enqueue(urlEncodeSpot (query));
m_uploadType = 2;
}
}
} }
}
} }
m_urlQueueSize = urlQueue.size(); spots_to_send_ = spot_queue_.size ();
uploadTimer->start(200); upload_timer_.start (200);
} }
void WSPRNet::networkReply(QNetworkReply *reply) void WSPRNet::post (QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq,
QString const& mode, float TR_period, QString const& tpct, QString const& dbm,
QString const& version, QString const& decode_text)
{
m_call = call;
m_grid = grid;
m_rfreq = rfreq;
m_tfreq = tfreq;
m_mode = mode;
TR_period_ = TR_period;
m_tpct = tpct;
m_dbm = dbm;
m_vers = version;
if (!decode_text.size ())
{
if (!spot_queue_.size ())
{
spot_queue_.enqueue (urlEncodeNoSpot ());
m_uploadType = 1;
}
spots_to_send_ = spot_queue_.size ();
upload_timer_.start (200);
}
else
{
auto const& match = fst4_re.match (decode_text);
if (match.hasMatch ())
{
SpotQueue::value_type query;
// Prevent reporting data ouside of the current frequency band
auto tqrg = match.captured ("freq").toInt ();
if (tqrg >= 1400 && tqrg <= 1600)
{
query.addQueryItem ("function", "wspr");
// use time as at 3/4 of T/R period before current to
// ensure date is in Rx period
auto const& date = QDateTime::currentDateTimeUtc ().addSecs (-TR_period * 3. / 4.).date ();
query.addQueryItem ("date", date.toString ("yyMMdd"));
query.addQueryItem ("time", match.captured ("time"));
query.addQueryItem ("sig", match.captured ("db"));
query.addQueryItem ("dt", match.captured ("dt"));
query.addQueryItem ("tqrg", QString::number (rfreq.toDouble () + (tqrg - 1500) / 1e6, 'f', 6));
query.addQueryItem ("tcall", match.captured ("call"));
query.addQueryItem ("drift", "0");
query.addQueryItem ("tgrid", match.captured ("grid"));
query.addQueryItem ("dbm", match.captured ("dBm"));
spot_queue_.enqueue (urlEncodeSpot (query));
m_uploadType = 2;
}
}
}
}
void WSPRNet::networkReply (QNetworkReply * reply)
{ {
// check if request was ours // check if request was ours
if (m_outstandingRequests.removeOne (reply)) { if (m_outstandingRequests.removeOne (reply))
if (QNetworkReply::NoError != reply->error ()) { {
Q_EMIT uploadStatus (QString {"Error: %1"}.arg (reply->error ())); if (QNetworkReply::NoError != reply->error ())
// not clearing queue or halting queuing as it may be a transient {
// one off request error Q_EMIT uploadStatus (QString {"Error: %1"}.arg (reply->error ()));
} // not clearing queue or halting queuing as it may be a
else { // transient one off request error
QString serverResponse = reply->readAll();
if( m_uploadType == 2) {
if (!serverResponse.contains(QRegExp("spot\\(s\\) added"))) {
emit uploadStatus(QString {"Upload Failed: %1"}.arg (serverResponse));
urlQueue.clear();
uploadTimer->stop();
} }
} else
{
QString serverResponse = reply->readAll ();
if (m_uploadType == 2)
{
if (!serverResponse.contains(QRegExp("spot\\(s\\) added")))
{
Q_EMIT uploadStatus (QString {"Upload Failed: %1"}.arg (serverResponse));
spot_queue_.clear ();
upload_timer_.stop ();
}
}
if (urlQueue.isEmpty()) { if (!spot_queue_.size ())
emit uploadStatus("done"); {
QFile::remove(m_file); Q_EMIT uploadStatus("done");
uploadTimer->stop(); QFile f {m_file};
} if (f.exists ()) f.remove ();
upload_timer_.stop ();
}
}
qDebug () << QString {"WSPRnet.org %1 outstanding requests"}.arg (m_outstandingRequests.size ());
// delete request object instance on return to the event loop otherwise it is leaked
reply->deleteLater ();
}
}
bool WSPRNet::decodeLine (QString const& line, SpotQueue::value_type& query)
{
auto const& rx_match = wspr_re.match (line);
if (rx_match.hasMatch ()) {
int msgType = 0;
QString msg = rx_match.captured (7);
QString call, grid, dbm;
QRegularExpression msgRx;
// Check for Message Type 1
msgRx.setPattern(R"(^([A-Z0-9]{3,6})\s+([A-R]{2}\d{2})\s+(\d+))");
auto match = msgRx.match (msg);
if (match.hasMatch ()) {
msgType = 1;
call = match.captured (1);
grid = match.captured (2);
dbm = match.captured (3);
} }
qDebug () << QString {"WSPRnet.org %1 outstanding requests"}.arg (m_outstandingRequests.size ()); // Check for Message Type 2
msgRx.setPattern(R"(^([A-Z0-9/]+)\s+(\d+))");
match = msgRx.match (msg);
if (match.hasMatch ()) {
msgType = 2;
call = match.captured (1);
grid = "";
dbm = match.captured (2);
}
// delete request object instance on return to the event loop otherwise it is leaked // Check for Message Type 3
reply->deleteLater (); msgRx.setPattern(R"(^<([A-Z0-9/]+)>\s+([A-R]{2}\d{2}[A-X]{2})\s+(\d+))");
match = msgRx.match (msg);
if (match.hasMatch ()) {
msgType = 3;
call = match.captured (1);
grid = match.captured (2);
dbm = match.captured (3);
}
// Unknown message format
if (!msgType) {
return false;
}
query.addQueryItem ("function", "wspr");
query.addQueryItem ("date", rx_match.captured (1));
query.addQueryItem ("time", rx_match.captured (2));
query.addQueryItem ("sig", rx_match.captured (4));
query.addQueryItem ("dt", rx_match.captured(5));
query.addQueryItem ("drift", rx_match.captured(8));
query.addQueryItem ("tqrg", rx_match.captured(6));
query.addQueryItem ("tcall", call);
query.addQueryItem ("tgrid", grid);
query.addQueryItem ("dbm", dbm);
} else {
return false;
} }
return true;
} }
bool WSPRNet::decodeLine(QString const& line, QHash<QString,QString> &query) auto WSPRNet::urlEncodeNoSpot () -> SpotQueue::value_type
{ {
// 130223 2256 7 -21 -0.3 14.097090 DU1MGA PK04 37 0 40 0 SpotQueue::value_type query;
// Date Time Sync dBm DT Freq Msg query.addQueryItem ("function", "wsprstat");
// 1 2 3 4 5 6 -------7------ 8 9 10 query.addQueryItem ("rcall", m_call);
QRegExp rx("^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+\\.\\d+)\\s+(\\d+\\.\\d+)\\s+(.*)\\s+([+-]?\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+)"); query.addQueryItem ("rgrid", m_grid);
if (rx.indexIn(line) != -1) { query.addQueryItem ("rqrg", m_rfreq);
int msgType = 0; query.addQueryItem ("tpct", m_tpct);
QString msg = rx.cap(7); query.addQueryItem ("tqrg", m_tfreq);
msg.remove(QRegExp("\\s+$")); query.addQueryItem ("dbm", m_dbm);
msg.remove(QRegExp("^\\s+")); query.addQueryItem ("version", m_vers);
QString call, grid, dbm; if (m_mode == "WSPR") query.addQueryItem ("mode", "2");
QRegExp msgRx; if (m_mode == "WSPR-15") query.addQueryItem ("mode", "15");
if (m_mode == "FST4W")
// Check for Message Type 1 {
msgRx.setPattern("^([A-Z0-9]{3,6})\\s+([A-Z]{2}\\d{2})\\s+(\\d+)"); query.addQueryItem ("mode", QString::number (static_cast<int> ((TR_period_ / 60.)+.5)));
if (msgRx.indexIn(msg) != -1) {
msgType = 1;
call = msgRx.cap(1);
grid = msgRx.cap(2);
dbm = msgRx.cap(3);
}
// Check for Message Type 2
msgRx.setPattern("^([A-Z0-9/]+)\\s+(\\d+)");
if (msgRx.indexIn(msg) != -1) {
msgType = 2;
call = msgRx.cap(1);
grid = "";
dbm = msgRx.cap(2);
}
// Check for Message Type 3
msgRx.setPattern("^<([A-Z0-9/]+)>\\s+([A-Z]{2}\\d{2}[A-Z]{2})\\s+(\\d+)");
if (msgRx.indexIn(msg) != -1) {
msgType = 3;
call = msgRx.cap(1);
grid = msgRx.cap(2);
dbm = msgRx.cap(3);
}
// Unknown message format
if (!msgType) {
return false;
}
query["function"] = "wspr";
query["date"] = rx.cap(1);
query["time"] = rx.cap(2);
query["sig"] = rx.cap(4);
query["dt"] = rx.cap(5);
query["drift"] = rx.cap(8);
query["tqrg"] = rx.cap(6);
query["tcall"] = call;
query["tgrid"] = grid;
query["dbm"] = dbm;
} else {
return false;
} }
return true; return query;;
} }
QString WSPRNet::urlEncodeNoSpot() auto WSPRNet::urlEncodeSpot (SpotQueue::value_type& query) -> SpotQueue::value_type
{ {
QString queryString; query.addQueryItem ("version", m_vers);
queryString += "function=wsprstat&"; query.addQueryItem ("rcall", m_call);
queryString += "rcall=" + m_call + "&"; query.addQueryItem ("rgrid", m_grid);
queryString += "rgrid=" + m_grid + "&"; query.addQueryItem ("rqrg", m_rfreq);
queryString += "rqrg=" + m_rfreq + "&"; if (m_mode == "WSPR") query.addQueryItem ("mode", "2");
queryString += "tpct=" + m_tpct + "&"; if (m_mode == "WSPR-15") query.addQueryItem ("mode", "15");
queryString += "tqrg=" + m_tfreq + "&"; if (m_mode == "FST4W")
queryString += "dbm=" + m_dbm + "&"; {
queryString += "version=" + m_vers; query.addQueryItem ("mode", QString::number (static_cast<int> ((TR_period_ / 60.)+.5)));
if(m_mode=="WSPR") queryString += "&mode=2"; }
if(m_mode=="WSPR-15") queryString += "&mode=15"; return query;
return queryString;;
}
QString WSPRNet::urlEncodeSpot(QHash<QString,QString> const& query)
{
QString queryString;
queryString += "function=" + query["function"] + "&";
queryString += "rcall=" + m_call + "&";
queryString += "rgrid=" + m_grid + "&";
queryString += "rqrg=" + m_rfreq + "&";
queryString += "date=" + query["date"] + "&";
queryString += "time=" + query["time"] + "&";
queryString += "sig=" + query["sig"] + "&";
queryString += "dt=" + query["dt"] + "&";
queryString += "drift=" + query["drift"] + "&";
queryString += "tqrg=" + query["tqrg"] + "&";
queryString += "tcall=" + query["tcall"] + "&";
queryString += "tgrid=" + query["tgrid"] + "&";
queryString += "dbm=" + query["dbm"] + "&";
queryString += "version=" + m_vers;
if(m_mode=="WSPR") queryString += "&mode=2";
if(m_mode=="WSPR-15") queryString += "&mode=15";
return queryString;
} }
void WSPRNet::work() void WSPRNet::work()
{ {
if (!urlQueue.isEmpty()) { if (spots_to_send_ && spot_queue_.size ())
{
#if QT_VERSION < QT_VERSION_CHECK (5, 15, 0) #if QT_VERSION < QT_VERSION_CHECK (5, 15, 0)
if (QNetworkAccessManager::Accessible != networkManager->networkAccessible ()) { if (QNetworkAccessManager::Accessible != network_manager_->networkAccessible ()) {
// try and recover network access for QNAM // try and recover network access for QNAM
networkManager->setNetworkAccessible (QNetworkAccessManager::Accessible); network_manager_->setNetworkAccessible (QNetworkAccessManager::Accessible);
} }
#endif #endif
QUrl url(urlQueue.dequeue()); QNetworkRequest request (QUrl {wsprNetUrl});
QNetworkRequest request(url); request.setHeader (QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
m_outstandingRequests << networkManager->get(request); auto const& spot = spot_queue_.dequeue ();
emit uploadStatus(QString {"Uploading Spot %1/%2"}.arg (m_urlQueueSize - urlQueue.size()).arg (m_urlQueueSize)); m_outstandingRequests << network_manager_->post (request, spot.query (QUrl::FullyEncoded).toUtf8 ());
} else { Q_EMIT uploadStatus(QString {"Uploading Spot %1/%2"}.arg (spots_to_send_ - spot_queue_.size()).arg (spots_to_send_));
uploadTimer->stop(); }
} else
{
upload_timer_.stop ();
}
} }
void WSPRNet::abortOutstandingRequests () { void WSPRNet::abortOutstandingRequests () {
urlQueue.clear (); spot_queue_.clear ();
for (auto& request : m_outstandingRequests) { for (auto& request : m_outstandingRequests) {
request->abort (); request->abort ();
} }
m_urlQueueSize = 0;
} }

View File

@ -2,45 +2,58 @@
#define WSPRNET_H #define WSPRNET_H
#include <QObject> #include <QObject>
#include <QTimer>
#include <QString> #include <QString>
#include <QList> #include <QList>
#include <QHash> #include <QUrlQuery>
#include <QQueue> #include <QQueue>
class QNetworkAccessManager; class QNetworkAccessManager;
class QTimer;
class QNetworkReply; class QNetworkReply;
class WSPRNet : public QObject class WSPRNet : public QObject
{ {
Q_OBJECT; Q_OBJECT
using SpotQueue = QQueue<QUrlQuery>;
public: public:
explicit WSPRNet(QNetworkAccessManager *, QObject *parent = nullptr); explicit WSPRNet (QNetworkAccessManager *, QObject *parent = nullptr);
void upload(QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq, void upload (QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq,
QString const& mode, QString const& tpct, QString const& dbm, QString const& version, QString const& mode, float TR_peirod, QString const& tpct, QString const& dbm,
QString const& fileName); QString const& version, QString const& fileName);
static bool decodeLine(QString const& line, QHash<QString,QString> &query); void post (QString const& call, QString const& grid, QString const& rfreq, QString const& tfreq,
QString const& mode, float TR_period, QString const& tpct, QString const& dbm,
QString const& version, QString const& decode_text = QString {});
signals: signals:
void uploadStatus(QString); void uploadStatus (QString);
public slots: public slots:
void networkReply(QNetworkReply *); void networkReply (QNetworkReply *);
void work(); void work ();
void abortOutstandingRequests (); void abortOutstandingRequests ();
private: private:
QNetworkAccessManager *networkManager; bool decodeLine (QString const& line, SpotQueue::value_type& query);
QList<QNetworkReply *> m_outstandingRequests; SpotQueue::value_type urlEncodeNoSpot ();
QString m_call, m_grid, m_rfreq, m_tfreq, m_mode, m_tpct, m_dbm, m_vers, m_file; SpotQueue::value_type urlEncodeSpot (SpotQueue::value_type& spot);
QQueue<QString> urlQueue;
QTimer *uploadTimer;
int m_urlQueueSize;
int m_uploadType;
QString urlEncodeNoSpot(); QNetworkAccessManager * network_manager_;
QString urlEncodeSpot(QHash<QString,QString> const& spot); QList<QNetworkReply *> m_outstandingRequests;
QString m_call;
QString m_grid;;
QString m_rfreq;
QString m_tfreq;
QString m_mode;
QString m_tpct;
QString m_dbm;
QString m_vers;
QString m_file;
float TR_period_;
int spots_to_send_;
SpotQueue spot_queue_;
QTimer upload_timer_;
int m_uploadType;
}; };
#endif // WSPRNET_H #endif // WSPRNET_H

View File

@ -13,6 +13,32 @@
Copyright 2001 - 2020 by Joe Taylor, K1JT. Copyright 2001 - 2020 by Joe Taylor, K1JT.
Release: WSJT-X 2.3.0-rc1
Sept 28, 2020
-------------------------
WSJT-X 2.3.0 is a program upgrade offering two new modes designed
especially for use on the LF and MF bands. FST4 is for 2-way QSOs,
and FST4W is for WSPR-like transmissions. Both modes offer a range of
options for T/R sequence lengths and threshold decoding sensitivities
extending well into the -40 dB range. Early tests have shown these
modes frequently spanning intercontinental distances on the 2200 m and
630 m bands. Further details and operating hints can be found in the
"Quick-Start Guide to FST4 and FST4W", posted on the WSJT web site:
https://physics.princeton.edu/pulsar/k1jt/FST4_Quick_Start.pdf
WSJT-X 2.3.0-rc1 is a beta-quality release candidate for a program
upgrade that provides a number of new features and capabilities.
These include:
- New modes FST4 and FST4W
- The *On Dx Echo* Doppler compensation method has been modified in
response to feedback from Users. Basic functionality is unchanged.
See the User Guide (Section 8.1) for more information.
Release: WSJT-X 2.2.2 Release: WSJT-X 2.2.2
June 22, 2020 June 22, 2020
--------------------- ---------------------

View File

@ -88,7 +88,7 @@ SampleDownloader::impl::impl (QSettings * settings
, directory_ {configuration, network_manager} , directory_ {configuration, network_manager}
, button_box_ {QDialogButtonBox::Close, Qt::Vertical} , button_box_ {QDialogButtonBox::Close, Qt::Vertical}
{ {
setWindowTitle (windowTitle () + ' ' + tr (title)); setWindowTitle (windowTitle () + ' ' + tr ("Download Samples"));
resize (500, 600); resize (500, 600);
{ {
SettingsGroup g {settings_, title}; SettingsGroup g {settings_, title};
@ -111,7 +111,7 @@ SampleDownloader::impl::impl (QSettings * settings
details_layout_.setMargin (0); details_layout_.setMargin (0);
details_layout_.addRow (tr ("Base URL for samples:"), &url_line_edit_); details_layout_.addRow (tr ("Base URL for samples:"), &url_line_edit_);
details_layout_.addRow (tr ("Only use HTTP:"), &http_only_check_box_); details_layout_.addRow (tr ("Only use HTTP:"), &http_only_check_box_);
http_only_check_box_.setToolTip (tr ("Check this is you get SSL/TLS errors")); http_only_check_box_.setToolTip (tr ("Check this if you get SSL/TLS errors"));
details_widget_.setLayout (&details_layout_); details_widget_.setLayout (&details_layout_);
main_layout_.addLayout (&left_layout_, 0, 0); main_layout_.addLayout (&left_layout_, 0, 0);

View File

@ -409,7 +409,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
{ {
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ()); TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error { throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n") tr ("DX Lab Suite Commander send command failed \"%1\": %2\n")
.arg (cmd) .arg (cmd)
.arg (commander_->errorString ()) .arg (commander_->errorString ())
}; };

View File

@ -606,6 +606,13 @@ int HamlibTransceiver::do_start ()
} }
} }
#if HAVE_HAMLIB_CACHING
// we must disable Hamlib caching because it lies about frequency
// for less than 1 Hz resolution rigs
auto orig_cache_timeout = rig_get_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL);
rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, 0);
#endif
int resolution {0}; int resolution {0};
if (freq_query_works_) if (freq_query_works_)
{ {
@ -646,6 +653,11 @@ int HamlibTransceiver::do_start ()
resolution = -1; // best guess resolution = -1; // best guess
} }
#if HAVE_HAMLIB_CACHING
// revert Hamlib cache timeout
rig_set_cache_timeout_ms (rig_.data (), HAMLIB_CACHE_ALL, orig_cache_timeout);
#endif
do_poll (); do_poll ();
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution); TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
@ -672,7 +684,7 @@ void HamlibTransceiver::do_stop ()
TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_); TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_);
} }
auto HamlibTransceiver::get_vfos (bool for_split) const -> std::tuple<vfo_t, vfo_t> std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
{ {
if (get_vfo_works_ && rig_->caps->get_vfo) if (get_vfo_works_ && rig_->caps->get_vfo)
{ {

View File

@ -25,8 +25,9 @@ namespace
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Operator"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Operator"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Call"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Call"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Grid"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Grid"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Exchange Sent"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Exch Sent"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Exchange Rcvd"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Exch Rcvd"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Prop"),
QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Comments"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Comments"),
}; };
} }
@ -212,7 +213,8 @@ void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time
, QString const& tx_power, QString const& comments , QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call , QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid , QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd) , QString const& exchange_sent, QString const& exchange_rcvd
, QString const& prop_mode)
{ {
QList<QStandardItem *> row; QList<QStandardItem *> row;
row << new QStandardItem {time_on.toString ("dd-MMM-yyyy hh:mm:ss")} row << new QStandardItem {time_on.toString ("dd-MMM-yyyy hh:mm:ss")}
@ -230,6 +232,7 @@ void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time
<< new QStandardItem {my_grid} << new QStandardItem {my_grid}
<< new QStandardItem {exchange_sent} << new QStandardItem {exchange_sent}
<< new QStandardItem {exchange_rcvd} << new QStandardItem {exchange_rcvd}
<< new QStandardItem {prop_mode}
<< new QStandardItem {comments}; << new QStandardItem {comments};
log_->appendRow (row); log_->appendRow (row);
log_table_view_->resizeColumnsToContents (); log_table_view_->resizeColumnsToContents ();

View File

@ -33,7 +33,7 @@ public:
, QString const& report_received, QString const& tx_power, QString const& comments , QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call , QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid , QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd); , QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode);
private: private:
void add_client (QString const& id, QString const& version, QString const& revision); void add_client (QString const& id, QString const& version, QString const& revision);

View File

@ -345,9 +345,10 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
QByteArray my_grid; QByteArray my_grid;
QByteArray exchange_sent; QByteArray exchange_sent;
QByteArray exchange_rcvd; QByteArray exchange_rcvd;
QByteArray prop_mode;
in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received
>> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid >> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid
>> exchange_sent >> exchange_rcvd; >> exchange_sent >> exchange_rcvd >> prop_mode;
if (check_status (in) != Fail) if (check_status (in) != Fail)
{ {
Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid) Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
@ -356,7 +357,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
, QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on , QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
, QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call) , QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
, QString::fromUtf8 (my_grid), QString::fromUtf8 (exchange_sent) , QString::fromUtf8 (my_grid), QString::fromUtf8 (exchange_sent)
, QString::fromUtf8 (exchange_rcvd)); , QString::fromUtf8 (exchange_rcvd), QString::fromUtf8 (prop_mode));
} }
} }
break; break;

View File

@ -106,7 +106,7 @@ public:
, QString const& report_received, QString const& tx_power, QString const& comments , QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call , QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid , QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd); , QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode);
Q_SIGNAL void decodes_cleared (QString const& id); Q_SIGNAL void decodes_cleared (QString const& id);
Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF); Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF);

View File

@ -102,7 +102,7 @@ public:
, QString const& report_received, QString const& tx_power , QString const& report_received, QString const& tx_power
, QString const& comments, QString const& name, QDateTime time_on , QString const& comments, QString const& name, QDateTime time_on
, QString const& operator_call, QString const& my_call, QString const& my_grid , QString const& operator_call, QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd) , QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode)
{ {
if (client_id == id_) if (client_id == id_)
{ {
@ -111,12 +111,13 @@ public:
<< "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments << "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments
<< "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call << "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call
<< "my_grid:" << my_grid << "exchange_sent:" << exchange_sent << "my_grid:" << my_grid << "exchange_sent:" << exchange_sent
<< "exchange_rcvd:" << exchange_rcvd; << "exchange_rcvd:" << exchange_rcvd << "prop_mode:" << prop_mode;
std::cout << QByteArray {80, '-'}.data () << '\n'; 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") 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 exchange_sent: %12 exchange_rcvd: %13 comments: %14 prop_mode: %15")
.arg (id_).arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received) .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 (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call)
.arg (my_call).arg (my_grid).toStdString () .arg (my_call).arg (my_grid).arg (exchange_sent).arg (exchange_rcvd)
.arg (comments).arg (prop_mode).toStdString ()
<< std::endl; << std::endl;
} }
} }

View File

@ -1,6 +1,6 @@
# Version number components # Version number components
set (WSJTX_VERSION_MAJOR 2) set (WSJTX_VERSION_MAJOR 2)
set (WSJTX_VERSION_MINOR 2) set (WSJTX_VERSION_MINOR 3)
set (WSJTX_VERSION_PATCH 2) set (WSJTX_VERSION_PATCH 0)
#set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions
set (WSJTX_VERSION_IS_RELEASE 1) # set to 1 for final release build set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build

View File

@ -220,7 +220,7 @@ public:
, carry_ {false} , carry_ {false}
, seed_ {{rand (), rand (), rand (), rand (), rand (), rand (), rand (), rand ()}} , seed_ {{rand (), rand (), rand (), rand (), rand (), rand (), rand (), rand ()}}
, gen_ {seed_} , gen_ {seed_}
, dist_ {1, 100} , dist_ {0, 99}
{ {
auto num_bands = configuration_->bands ()->rowCount (); auto num_bands = configuration_->bands ()->rowCount ();
for (auto& flags : bands_) for (auto& flags : bands_)

View File

@ -2,7 +2,7 @@
#define COMMONS_H #define COMMONS_H
#define NSMAX 6827 #define NSMAX 6827
#define NTMAX 300 #define NTMAX 30*60
#define RX_SAMPLE_RATE 12000 #define RX_SAMPLE_RATE 12000
#ifdef __cplusplus #ifdef __cplusplus
@ -85,7 +85,7 @@ extern struct {
} echocom_; } echocom_;
extern struct { extern struct {
float wave[606720]; float wave[(160+2)*134400*4]; /* (nsym+2)*nsps scaled up to 48kHz */
int nslots; int nslots;
int nfreq; int nfreq;
int i3bit[5]; int i3bit[5];

2046
cty.dat

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,27 @@
Here are the "displayWidgets()" strings for WSJT-X modes Here are the "displayWidgets()" strings for WSJT-X modes
1 2 3 1 2 3
012345678901234567890123456789012 012345678901234567890123456789012345
---------------------------------------------- ------------------------------------------------
JT4 111010000000110000110000000000000 JT4 111010000000110000110000000000000000
JT4/VHF 111110010010110110111100000000000 JT4/VHF 111110010010110110111100000000000000
JT9 111010000000111000010000000000001 JT9 111010000000111000010000000000001000
JT9/VHF 111110101000111110010000000000000 JT9/VHF 111110101000111110010000000000000000
JT9+JT65 111010000001111000010000000000001 JT9+JT65 111010000001111000010000000000001000
JT65 111010000000111000010000000000001 JT65 111010000000111000010000000000001000
JT65/VHF 111110010000110110101100010000000 JT65/VHF 111110010000110110101100010000000000
QRA64 111110010110110110000000001000000 QRA64 111110010110110110000000001000000000
ISCAT 100111000000000110000000000000000 ISCAT 100111000000000110000000000000000000
MSK144 101111110100000000010001000000000 MSK144 101111110100000000010001000000000000
WSPR 000000000000000001010000000000000 WSPR 000000000000000001010000000000000000
Echo 000000000000000000000010000000000 FST4 111111000100111000010000000100000011
FCal 001101000000000000000000000001000 FST4W 000000000000000001010000000000000100
FT8 111010000100111000010000100110001 Echo 000000000000000000000010000000000000
FT8/VHF 111010000100111000010000100110001 FCal 001101000000000000000000000001000000
FT8/Fox 111010000100111000010000000000100 FT8 111010000100111000010000100110001000
FT8/Hound 111010000100111000010000000000110 FT8/VHF 111010000100111000010000100110001000
FT8/Fox 111010000100111000010000000000100000
FT8/Hound 111010000100111000010000000000110000
---------------------------------------------- ----------------------------------------------
1 2 3 1 2 3
012345678901234567890123456789012 012345678901234567890123456789012
@ -60,3 +62,6 @@ Mapping of column numbers to widgets
30. labDXped 30. labDXped
31. cbRxAll 31. cbRxAll
32. cbCQonly 32. cbCQonly
33. sbTR_FST4W
34. sbF_Low
35. sbF_High

View File

@ -30,8 +30,7 @@ set (UG_SRCS
install-mac.adoc install-mac.adoc
install-windows.adoc install-windows.adoc
introduction.adoc introduction.adoc
measurement_tools.adoc intro_subsections.adoc
protocols.adoc
logging.adoc logging.adoc
make-qso.adoc make-qso.adoc
measurement_tools.adoc measurement_tools.adoc
@ -53,6 +52,8 @@ set (UG_SRCS
tutorial-example2.adoc tutorial-example2.adoc
tutorial-example3.adoc tutorial-example3.adoc
tutorial-example4.adoc tutorial-example4.adoc
tutorial-example5.adoc
tutorial-example6.adoc
tutorial-main-window.adoc tutorial-main-window.adoc
tutorial-wide-graph-settings.adoc tutorial-wide-graph-settings.adoc
utilities.adoc utilities.adoc
@ -82,6 +83,9 @@ set (UG_IMGS
images/FreqCal_Graph.png images/FreqCal_Graph.png
images/FreqCal_Results.png images/FreqCal_Results.png
images/freemsg.png images/freemsg.png
images/FST4_center.png
images/FST4_Decoding_Limits.png
images/FST4W_RoundRobin.png
images/ft4_decodes.png images/ft4_decodes.png
images/ft4_waterfall.png images/ft4_waterfall.png
images/ft8_decodes.png images/ft8_decodes.png

View File

@ -5,10 +5,10 @@ Usage example: include::../common/links.adoc[]
Syntax: [link-id] [link] [displayed text] Syntax: [link-id] [link] [displayed text]
Example: Example:
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter] :pskreporter: https://pskreporter.info/pskmap.html[PSK Reporter]
[link-id] = :pskreporter: [link-id] = :pskreporter:
[link] http://pskreporter.info/pskmap.html [link] https://pskreporter.info/pskmap.html
[displayed text] PSK Reporter [displayed text] PSK Reporter
Perform searches from the doc root directory: doc Perform searches from the doc root directory: doc
@ -42,53 +42,53 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
// General URL's // General URL's
//:launchpadac6sl: https://launchpad.net/~jnogatch/+archive/wsjtx[WSJT-X Linux Packages] //:launchpadac6sl: https://launchpad.net/~jnogatch/+archive/wsjtx[WSJT-X Linux Packages]
:alarmejt: http://f5jmh.free.fr/index.php?page=english[AlarmeJT] :alarmejt: http://f5jmh.free.fr/index.php?page=english[AlarmeJT]
:asciidoc_cheatsheet: http://powerman.name/doc/asciidoc[AsciiDoc Cheatsheet] :asciidoc_cheatsheet: https://powerman.name/doc/asciidoc[AsciiDoc Cheatsheet]
:asciidoc_help: http://www.methods.co.nz/asciidoc/userguide.html[AsciiDoc User Guide] :asciidoc_help: https://www.methods.co.nz/asciidoc/userguide.html[AsciiDoc User Guide]
:asciidoc_questions: http://www.methods.co.nz/asciidoc/faq.html[AsciiDoc FAQ] :asciidoc_questions: https://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] :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_style: https://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] :asciidoctor_syntax: https://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] :cc_by_sa: https://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] :debian32: https://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] :debian64: https://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] :raspbian: https://physics.princeton.edu/pulsar/K1JT/wsjtx_{VERSION}_armhf.deb[wsjtx_{VERSION}_armhf.deb]
:debian: http://www.debian.org/[Debian] :debian: https://www.debian.org/[Debian]
:dev_guide: http://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide] :dev_guide: https://www.physics.princeton.edu/pulsar/K1JT/wsjtx-doc/wsjt-dev-guide.html[Dev-Guide]
:devsvn: http://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN] :devsvn: https://sourceforge.net/p/wsjt/wsjt/HEAD/tree/[Devel-SVN]
:devrepo: https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/[SourceForge] :devrepo: https://sourceforge.net/p/wsjt/wsjtx/ci/master/tree/[SourceForge]
:dimension4: http://www.thinkman.com/dimension4/[Thinking Man Software] :dimension4: http://www.thinkman.com/dimension4/[Thinking Man Software]
:download: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[Download Page] :download: https://physics.princeton.edu/pulsar/K1JT/wsjtx.html[Download Page]
:dxatlas: http://www.dxatlas.com/[Afreet Software, Inc.] :dxatlas: http://www.dxatlas.com/[Afreet Software, Inc.]
:dxlcommander: http://www.dxlabsuite.com/commander/[Commander] :dxlcommander: https://www.dxlabsuite.com/commander/[Commander]
:dxlsuite: http://www.dxlabsuite.com/[DX Lab Suite] :dxlsuite: https://www.dxlabsuite.com/[DX Lab Suite]
:fedora32: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-i686.rpm[wsjtx-{VERSION}-i686.rpm] :fedora32: https://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] :fedora64: https://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_arrl: https://www.arrl.org/frequency-measuring-test[ARRL FMT Info]
:fmt_group: https://groups.yahoo.com/neo/groups/FMT-nuts/info[FMT Group] :fmt_group: https://groups.yahoo.com/neo/groups/FMT-nuts/info[FMT Group]
:fmt_k5cm: http://www.k5cm.com/[FMT Event Info] :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] :fmt_wspr: https://www.physics.princeton.edu/pulsar/K1JT/FMT_User.pdf[Accurate Frequency Measurements with your WSPR Setup]
:ft4_protocol: http://physics.princeton.edu/pulsar/k1jt/FT4_Protocol.pdf[The FT4 Protocol for Digital Contesting] :ft4_protocol: https://physics.princeton.edu/pulsar/k1jt/FT4_Protocol.pdf[The FT4 Protocol for Digital Contesting]
:ft4_ft8_protocols: http://physics.princeton.edu/pulsar/k1jt/FT4_FT8_QEX.pdf[The FT4 and FT8 Communication Protocols] :ft4_ft8_protocols: https://physics.princeton.edu/pulsar/k1jt/FT4_FT8_QEX.pdf[The FT4 and FT8 Communication Protocols]
:ft8_tips: http://www.g4ifb.com/FT8_Hinson_tips_for_HF_DXers.pdf[FT8 Operating Guide] :ft8_tips: https://www.g4ifb.com/FT8_Hinson_tips_for_HF_DXers.pdf[FT8 Operating Guide]
:ft8_DXped: http://physics.princeton.edu/pulsar/k1jt/FT8_DXpedition_Mode.pdf[FT8 DXpedition Mode] :ft8_DXped: https://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] :gnu_gpl: https://www.gnu.org/licenses/gpl-3.0.txt[GNU General Public License]
:homepage: http://physics.princeton.edu/pulsar/K1JT/[WSJT Home Page] :homepage: https://physics.princeton.edu/pulsar/K1JT/[WSJT Home Page]
:hrd: http://www.hrdsoftwarellc.com/[Ham Radio Deluxe] :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] :jt4eme: https://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] :jt65protocol: https://physics.princeton.edu/pulsar/K1JT/JT65.pdf[QEX]
:jtalert: http://hamapps.com/[JTAlert] :jtalert: https://hamapps.com/[JTAlert]
:launchpadki7mt: https://launchpad.net/~ki7mt[KI7MT PPA's] :launchpadki7mt: https://launchpad.net/~ki7mt[KI7MT PPA's]
:log4om: http://www.log4om.com[Log4OM] :log4om: https://www.log4om.com[Log4OM]
:lunarEchoes: http://physics.princeton.edu/pulsar/K1JT/LunarEchoes_QEX.pdf[QEX] :lunarEchoes: https://physics.princeton.edu/pulsar/K1JT/LunarEchoes_QEX.pdf[QEX]
:msk144: http://physics.princeton.edu/pulsar/k1jt/MSK144_Protocol_QEX.pdf[QEX] :msk144: https://physics.princeton.edu/pulsar/k1jt/MSK144_Protocol_QEX.pdf[QEX]
:msvcpp_redist: https://www.microsoft.com/en-ph/download/details.aspx?id=40784[Microsoft VC++ 2013 Redistributable] :msvcpp_redist: https://www.microsoft.com/en-ph/download/details.aspx?id=40784[Microsoft VC++ 2013 Redistributable]
:msys_url: http://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/[MSYS Download] :msys_url: https://sourceforge.net/projects/mingwbuilds/files/external-binary-packages/[MSYS Download]
:n1mm_logger: https://n1mm.hamdocs.com/tiki-index.php[N1MM Logger+] :n1mm_logger: https://n1mm.hamdocs.com/tiki-index.php[N1MM Logger+]
:ntpsetup: http://www.satsignal.eu/ntp/setup.html[Network Time Protocol Setup] :ntpsetup: https://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] :osx_instructions: https://physics.princeton.edu/pulsar/K1JT/OSX_Readme[Mac OS X Install Instructions]
:ppa: http://en.wikipedia.org/wiki/Personal_Package_Archive[PPA] :ppa: https://en.wikipedia.org/wiki/Personal_Package_Archive[PPA]
:projsummary: http://sourceforge.net/projects/wsjt/[Project Summary] :projsummary: https://sourceforge.net/projects/wsjt/[Project Summary]
:pskreporter: http://pskreporter.info/pskmap.html[PSK Reporter] :pskreporter: https://pskreporter.info/pskmap.html[PSK Reporter]
:sourceforge: https://sourceforge.net/user/registration[SourceForge] :sourceforge: https://sourceforge.net/user/registration[SourceForge]
:sourceforge-jtsdk: https://sourceforge.net/projects/jtsdk[SourceForge JTSDK] :sourceforge-jtsdk: https://sourceforge.net/projects/jtsdk[SourceForge JTSDK]
:ubuntu_sdk: https://launchpad.net/~ubuntu-sdk-team/+archive/ppa[Ubuntu SDK Notice] :ubuntu_sdk: https://launchpad.net/~ubuntu-sdk-team/+archive/ppa[Ubuntu SDK Notice]
@ -97,37 +97,37 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
:win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_1_1g.msi[Win64 OpenSSL Light Package] :win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_1_1g.msi[Win64 OpenSSL Light Package]
:writelog: https://writelog.com/[Writelog] :writelog: https://writelog.com/[Writelog]
:wsjtx_group: https://groups.io/g/WSJTX[WSJTX Group] :wsjtx_group: https://groups.io/g/WSJTX[WSJTX Group]
:wsjtx: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X] :wsjtx: https://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X]
:wspr0_guide: http://www.physics.princeton.edu/pulsar/K1JT/WSPR0_Instructions.TXT[WSPR0 Guide] :wspr0_guide: https://www.physics.princeton.edu/pulsar/K1JT/WSPR0_Instructions.TXT[WSPR0 Guide]
:wspr: http://physics.princeton.edu/pulsar/K1JT/wspr.html[WSPR Home Page] :wspr: https://physics.princeton.edu/pulsar/K1JT/wspr.html[WSPR Home Page]
:wsprnet: http://wsprnet.org/drupal/[WSPRnet] :wsprnet: https://wsprnet.org/drupal/[WSPRnet]
:wsprnet_activity: http://wsprnet.org/drupal/wsprnet/activity[WSPRnet Activity page] :wsprnet_activity: https://wsprnet.org/drupal/wsprnet/activity[WSPRnet Activity page]
// Download Links // Download Links
:cty_dat: http://www.country-files.com/cty/[Amateur Radio Country Files] :cty_dat: https://www.country-files.com/cty/[Amateur Radio Country Files]
:jtbridge: http://jt-bridge.eller.nu/[JT-Bridge] :jtbridge: https://jt-bridge.eller.nu/[JT-Bridge]
:jtsdk_doc: http://physics.princeton.edu/pulsar/K1JT/JTSDK-DOC.exe[Download] :jtsdk_doc: https://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_installer: https://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_omnirig: https://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_py: https://physics.princeton.edu/pulsar/K1JT/JTSDK-PY.exe[Download]
:jtsdk_qt: http://physics.princeton.edu/pulsar/K1JT/JTSDK-QT.exe[Download] :jtsdk_qt: https://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] :jtsdk_vcredist: https://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] :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] :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] :osx: https://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] :QRA64_EME: https://physics.princeton.edu/pulsar/K1JT/QRA64_EME.pdf[QRA64 for microwave EME]
:svn: http://subversion.apache.org/packages.html#windows[Subversion] :svn: https://subversion.apache.org/packages.html#windows[Subversion]
:win32: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win32.exe[wsjtx-{VERSION}-win32.exe] :win32: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win32.exe[wsjtx-{VERSION}-win32.exe]
:win64: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win64.exe[wsjtx-{VERSION}-win64.exe] :win64: https://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win64.exe[wsjtx-{VERSION}-win64.exe]
:wsjt-devel: https://lists.sourceforge.net/lists/listinfo/wsjt-devel[here] :wsjt-devel: https://lists.sourceforge.net/lists/listinfo/wsjt-devel[here]
:wsjt_repo: https://sourceforge.net/p/wsjt/wsjt_orig/ci/master/tree/[WSJT Source Repository] :wsjt_repo: https://sourceforge.net/p/wsjt/wsjt_orig/ci/master/tree/[WSJT Source Repository]
:wspr_code: http://physics.princeton.edu/pulsar/K1JT/WSPRcode.exe[WSPRcode.exe] :wspr_code: https://physics.princeton.edu/pulsar/K1JT/WSPRcode.exe[WSPRcode.exe]
:wspr_svn: https://sourceforge.net/p/wsjt/wspr/ci/master/tree/[WSPR Source Repository] :wspr_svn: https://sourceforge.net/p/wsjt/wspr/ci/master/tree/[WSPR Source Repository]
// MAIL-TO links // MAIL-TO links
:alex_efros: mailto:powerman@powerman.name[Alex Efros] :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] :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_list: https://sourceforge.net/mailarchive/forum.php?forum_name=wsjt-devel[WSJT Developers Email List]
:dev_mail_svn: https://sourceforge.net/auth/subscriptions/[WSJT SVN Archives] :dev_mail_svn: https://sourceforge.net/auth/subscriptions/[WSJT SVN Archives]
:devmail: mailto:wsjt-devel@lists.sourceforge.net[wsjt-devel@lists.sourceforge.net] :devmail: mailto:wsjt-devel@lists.sourceforge.net[wsjt-devel@lists.sourceforge.net]
:devmail1: mailto:wsjt-devel@lists.sourceforge.net[Post Message] :devmail1: mailto:wsjt-devel@lists.sourceforge.net[Post Message]

View File

@ -1,4 +1,4 @@
// Status=review // Status=edited
A *Status Bar* at the bottom edge of the main window provides useful A *Status Bar* at the bottom edge of the main window provides useful
information about operating conditions. information about operating conditions.
@ -9,15 +9,15 @@ image::status-bar-a.png[align="left",alt="Status Bar"]
Labels on the *Status Bar* display such information as the program's Labels on the *Status Bar* display such information as the program's
current operating state, configuration name, operating mode, and the current operating state, configuration name, operating mode, and the
content of your most recent transmitted message. The first label content of your most recent transmitted message. The first label
(operating state) can be Receiving, Tx (for Transmitting), Tune, or (operating state) can be Receiving, Tx (for Transmitting), Tx: Tune, or
the name of file opened from the *File* menu; this label is the name of the file opened from the *File* menu. This label is
highlighted in green for Receiving, yellow for Tx, red for Tune, and highlighted in green for Receiving, yellow for Tx, red for Tune, and
light blue for a file name. When transmitting, the Tx message is light blue for a file name. When transmitting, the Tx message is
displayed exactly as it will be decoded by receiving stations. The displayed exactly as it will be decoded by receiving stations. The
second label (as shown above) will be absent if you are using the second label (as shown above) will be absent if you are using the
*Default* setting on the *Configurations* menu. A progress bar shows *Default* setting on the *Configurations* menu. A progress bar shows
the elapsed fraction of a Tx or Rx sequence. Finally, if the Watchdog 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 (WD) timer was enabled on the *Files | Settings | General* tab, a label in the
lower right-hand corner displays the number of minutes remaining lower right-hand corner displays the number of minutes remaining
before timeout. before timeout.

View File

@ -1,4 +1,4 @@
// Status=review // Status=edited
The following controls appear at the bottom of the Wide Graph window. The following controls appear at the bottom of the Wide Graph window.
Decoding occurs only in the displayed frequency range; otherwise, with Decoding occurs only in the displayed frequency range; otherwise, with
@ -29,7 +29,7 @@ slower, as desired.
- A dropdown list below the *Palette* label lets you select from a - A dropdown list below the *Palette* label lets you select from a
wide range of waterfall color palettes. wide range of waterfall color palettes.
- Click *Adjust* to activate a window that allows you to create a - Click *Adjust* to activate a window that allows you to import or export a
user-defined palette. user-defined palette.
- Check *Flatten* if you want _WSJT-X_ to compensate for a sloping or - Check *Flatten* if you want _WSJT-X_ to compensate for a sloping or
@ -50,10 +50,10 @@ about right, depending on the input signal level, the chosen palette,
and your own preferences. Hover the mouse over a control to display a and your own preferences. Hover the mouse over a control to display a
tip reminding you of its function. tip reminding you of its function.
- The *Spec nn%* control may be used to set the fractional height of - The *Spec nn%* control is used to set the fractional height of
the spectrum plotted below the waterfall. the spectrum plotted below the waterfall.
- *Smooth* is active only when *Linear Average* has been selected. - *Smooth* is active only when *Linear Average* is selected.
Smoothing the displayed spectrum over more than one bin can enhance Smoothing the displayed spectrum over more than one bin can enhance
your ability to detect weak EME signals with Doppler spread more than your ability to detect weak EME signals with Doppler spread more than
a few Hz. a few Hz.
@ -66,7 +66,7 @@ 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 Graph window can be used to optimize gain and zero-offset for the
displayed information. Hover the mouse over a control to display a displayed information. Hover the mouse over a control to display a
tip reminding you of its function. Clicking the *Auto Level* button tip reminding you of its function. Clicking the *Auto Level* button
will produce reasonable settings as a starting point. produces reasonable settings as a starting point.
image::fast-graph-controls.png[align="center",alt="Fast Graph Controls"] image::fast-graph-controls.png[align="center",alt="Fast Graph Controls"]
@ -89,7 +89,7 @@ spectra, thereby smoothing the curves over multiple bins.
- Label *N* shows the number of echo pulses averaged. - Label *N* shows the number of echo pulses averaged.
- Click the *Colors* button to cycle through 6 possible choices of - Click the *Colors* button to cycle through six possible choices of
color and line width for the plots. color and line width for the plots.
[[CONTROLS_MISCELLANEOUS]] [[CONTROLS_MISCELLANEOUS]]

View File

@ -1,3 +1,5 @@
// Status: edited
_WSJT-X_ is programmed to cooperate closely with several other useful _WSJT-X_ is programmed to cooperate closely with several other useful
programs. programs.
@ -38,10 +40,10 @@ logging applications Aether, MacLoggerDX, RUMlog or RUMlogNG. It
checks QSO and QSL status of the call and DXCC entity, as well as many checks QSO and QSL status of the call and DXCC entity, as well as many
other features. other features.
* {n1mm_logger} is a free full feature contest logging application. It * {n1mm_logger} is a free, full-feature contest logging application. It
is only available for Windows. _WSJT-X_ can send logged QSO is only available for Windows. _WSJT-X_ can send logged QSO
information to it via a network connection. information to it via a network connection.
* {writelog} is a non-free full feature contest logging * {writelog} is a non-free, full-feature contest logging
application. It is only available for Windows. _WSJT-X_ can send application. It is only available for Windows. _WSJT-X_ can send
logged QSO information to it via a network connection. logged QSO information to it via a network connection.

View File

@ -1,19 +1,21 @@
[[AP_Decoding]] // Status: edited
=== AP Decoding === AP Decoding
The _WSJT-X_ decoders for FT4, FT8, JT65, and QRA64 include optional The _WSJT-X_ decoders for FT4, FT8, JT65, QRA64, include
procedures that take advantage of naturally accumulating information procedures that use naturally accumulating information during a
during a minimal QSO. This _a priori_ (AP) information increases minimal QSO. This _a priori_ (AP) information increases sensitivity
sensitivity of the decoder by up to 4 dB, at the cost of a slightly of the decoder by up to 4 dB, at the cost of a slightly higher rate of
higher rate of false decodes. false decodes. AP is optional in FT8, JT65, and QRA64, but is always
enabled for FT4.
For example: when you decide to answer a CQ, you already know your own For example: when you decide to answer a CQ, you already know your own
callsign and that of your potential QSO partner. The software callsign and that of your potential QSO partner. The software
therefore "`knows`" what might be expected for at least 57 message therefore "`knows`" what might be expected for at least 57 message
bits (28 for each of two callsigns, 1 or more for message type) in the bits (28 for each of two callsigns, one or more for message type) in the
next received message. The decoder's task can thus be reduced to next received message. The decoder's task is thus reduced to
determining the remaining 15 bits of the message and ensuring that the determining the remaining 15 bits of the message and ensuring that the
resulting solution is consistent with the message's parity symbols. resulting solution is reliable.
AP decoding starts by setting AP bits to the hypothesized values, as AP decoding starts by setting AP bits to the hypothesized values, as
if they had been received correctly. We then determine whether the if they had been received correctly. We then determine whether the
@ -21,12 +23,12 @@ remaining message and parity bits are consistent with the hypothesized
AP bits, with a specified level of confidence. Successful AP decodes 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` 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 is one of the single-digit AP decoding types listed in Table 1. For
example, `a2` indicates that the successful decode used *MyCall* as example, `a2` indicates that the successful decode used MyCall as
hypothetically known information. hypothetically known information.
[[FT8_AP_INFO_TABLE]] [[FT8_AP_INFO_TABLE]]
.FT4 and FT8 AP information types .FT4 and FT8 AP information types
[width="50%",cols="h10,<m20",frame=topbot,options="header"] [width="35%",cols="h10,<m20",frame=topbot,options="header"]
|=============================================== |===============================================
|aP | Message components |aP | Message components
|a1 | CQ &#160; &#160; ? &#160; &#160; ? |a1 | CQ &#160; &#160; ? &#160; &#160; ?
@ -49,7 +51,7 @@ be attempted in each state.
[[FT8_AP_DECODING_TYPES_TABLE]] [[FT8_AP_DECODING_TYPES_TABLE]]
.FT4 and FT8 AP decoding types for each QSO state .FT4 and FT8 AP decoding types for each QSO state
[width="50%",cols="h10,<m20",frame=topbot,options="header"] [width="35%",cols="h10,<m20",frame=topbot,options="header"]
|=========================================== |===========================================
|State |AP type |State |AP type
|CALLING STN | 2, 3 |CALLING STN | 2, 3
@ -60,14 +62,12 @@ be attempted in each state.
|CALLING CQ | 1, 2 |CALLING CQ | 1, 2
|=========================================== |===========================================
Decoding with _a priori_ information behaves slightly differently in Decoding with _a priori_ information behaves slightly differently
JT65. Some details are provided in Tables 3 and 4. Notations such as in JT65. Some details are provided in Tables 3 and 4.
`a63`, use a second digit to indicate the number of Rx intervals
averaged to obtain the decode.
[[JT65_AP_INFO_TABLE]] [[JT65_AP_INFO_TABLE]]
.JT65 AP information types .JT65 AP information types
[width="50%",cols="h10,<m20",frame=topbot,options="header"] [width="35%",cols="h10,<m20",frame=topbot,options="header"]
|=============================================== |===============================================
|aP | Message components |aP | Message components
|a1 | CQ &#160; &#160; ? &#160; &#160; ? |a1 | CQ &#160; &#160; ? &#160; &#160; ?
@ -81,7 +81,7 @@ averaged to obtain the decode.
[[JT65_AP_DECODING_TYPES_TABLE]] [[JT65_AP_DECODING_TYPES_TABLE]]
.JT65 AP decoding types for each QSO state .JT65 AP decoding types for each QSO state
[width="50%",cols="h10,<m20",frame=topbot,options="header"] [width="35%",cols="h10,<m20",frame=topbot,options="header"]
|=========================================== |===========================================
|State |AP type |State |AP type
|CALLING STN | 2, 3, 6, 7 |CALLING STN | 2, 3, 6, 7
@ -93,7 +93,6 @@ averaged to obtain the decode.
|=========================================== |===========================================
[[Decoded_Lines]]
=== Decoded Lines === Decoded Lines
Displayed information accompanying decoded messages generally includes UTC, Displayed information accompanying decoded messages generally includes UTC,
@ -110,7 +109,7 @@ summarized in the following Table:
[width="50%",cols="h,3*^",frame=topbot,options="header"] [width="50%",cols="h,3*^",frame=topbot,options="header"]
|=========================================== |===========================================
|Mode |Mode character|Sync character|End of line information |Mode |Mode character|Sync character|End of line information
|FT4 | + | | ? &#160; aP |FT4 | ~ | | ? &#160; aP
|FT8 | ~ | | ? &#160; aP |FT8 | ~ | | ? &#160; aP
|JT4 | $ | *, # | f, fN, dCN |JT4 | $ | *, # | f, fN, dCN
|JT9 | @ | | |JT9 | @ | |
@ -126,7 +125,7 @@ Sync character::
End of line information:: End of line information::
`?` - Decoded with lower confidence + `?` - Decoded with lower confidence +
`a` - Decoded with aid of some a priori (AP) information + `a` - Decoded with aid of some _a priori_ (AP) information +
`C` - Confidence indicator [ISCAT and Deep Search; (0-9,*)] + `C` - Confidence indicator [ISCAT and Deep Search; (0-9,*)] +
`d` - Deep Search algorithm + `d` - Deep Search algorithm +
`f` - Franke-Taylor or Fano algorithm + `f` - Franke-Taylor or Fano algorithm +
@ -140,7 +139,7 @@ Table 6 below shows the meaning of the return codes R in QRA64 mode.
[[QRA64_AP_INFO_TABLE]] [[QRA64_AP_INFO_TABLE]]
.QRA64 AP return codes .QRA64 AP return codes
[width="50%",cols="h10,<m20",frame=topbot,options="header"] [width="35%",cols="h10,<m20",frame=topbot,options="header"]
|=============================================== |===============================================
|rc | Message components |rc | Message components
|0 | ? &#160; &#160; ? &#160; &#160; ? |0 | ? &#160; &#160; ? &#160; &#160; ?

View File

@ -1,10 +1,12 @@
// Status: edited
//// ////
Questions: Questions:
Should be short one liners (in the .adoc file) ending with ?:: 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 If your question is too long for one line, consider multiple questions or rephrase.
Answers: Answers:
Can be bullet or paragraphs. Bullets make for easier reading. Can be bullets or paragraphs. Bullets make for easier reading.
Bullet Usage: Bullet Usage:
* = a circle bullet single intent * = a circle bullet single intent
@ -39,12 +41,12 @@ passband, if such control is available.
How should I configure _WSJT-X_ to run multiple instances?:: How should I configure _WSJT-X_ to run multiple instances?::
Start _WSJT-X_ from a command-prompt window, assigning each instance a Start _WSJT-X_ from a command-prompt window, assigning each a unique
unique identifier as in the following two-instance example. This identifier as in the following two-instance example. This procedure
procedure will isolate the *Settings* file and the writable file will isolate the *Settings* file and the writable file location for
location for each instance of _WSJT-X_. each instance of _WSJT-X_.
wsjtx --rig-name=TS2000 wsjtx --rig-name=TS590
wsjtx --rig-name=FT847 wsjtx --rig-name=FT847
I am getting a "Network Error - SSL/TLS support not installed" message. What should I do?:: I am getting a "Network Error - SSL/TLS support not installed" message. What should I do?::
@ -53,19 +55,18 @@ You need to install suitable _OpenSSL_ libraries - see <<OPENSSL,Instructions to
I occasionally get Rig Control Errors if I adjust my Icom rig's VFO. What's wrong?:: I occasionally get Rig Control Errors if I adjust my Icom rig's VFO. What's wrong?::
By default, most Icom transceivers have *CI-V Tranceive Mode" enabled, By default, most Icom transceivers have *CI-V Transceive Mode" enabled. This will cause unsolicited CAT traffic from the rig that disrupts CAT
this will cause unsolicited CAT traffic from the rig that disrupts CAT
control by a PC. Disable this option in the rig's menu. control by a PC. Disable this option in the rig's menu.
I want to control my transceiver with another application as well as _WSJT-X_, is that possible?:: I want to control my transceiver with another application as well as _WSJT-X_, is that possible?::
This only possible to do reliably via some kind of rig control server, This only possible to do reliably via some kind of rig control server.
that server must be able to accept both _WSJT-X_ and the other That server must be able to accept both _WSJT-X_ and the other
application(s) as clients. Using a dumb serial port splitter like the application(s) as clients. Using a dumb serial port splitter like the
VSPE tool is not supported, it may work but it is not reliable due to VSPE tool is not supported; it may work but it is not reliable due to
unmanaged CAT control collisions. Applications like the _Hamlib Rig unmanaged CAT control collisions. Applications like the _Hamlib Rig
Control Server (rigctld)_, _{omnirig}_, and _{dxlsuite} Commander_ are Control Server (rigctld)_, _{omnirig}_, and _{dxlsuite} Commander_ are
potentially suitable and _WSJT-X_ can act as a client to them all. potentially suitable; _WSJT-X_ can act as a client to them all.
Rig control through _OmniRig_ seems to fail when I click *Test CAT*. What can I do about it?:: Rig control through _OmniRig_ seems to fail when I click *Test CAT*. What can I do about it?::
@ -79,7 +80,7 @@ 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 other radio commands, due to a bug in HRD. HRD folks are aware of the
problem, and are working to resolve it. 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.:: I am running _WSJT-X_ under Ubuntu. The program starts, but the 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 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 window at the top of the primary display screen. You can restore menu
@ -100,10 +101,10 @@ I am running _WSJT-X_ on Linux using a KDE desktop. Why does *Menu->Configuratio
The KDE development team have added code to Qt that tries to The KDE development team have added code to Qt that tries to
automatically add shortcut accelerator keys to all buttons including automatically add shortcut accelerator keys to all buttons including
pop up menu buttons, this interferes with operation of the application pop up menu buttons. This interferes with operation of the application
(many other Qt applications have similar issues with KDE). Until this (many other Qt applications have similar issues with KDE). Until this
is fixed by the KDE team you must disable this misfeature. Edit the is fixed by the KDE team you must disable this feature. Edit the
file ~/.config/kdeglobals and add a section containing the following: file `~/.config/kdeglobals` and add a section containing the following:
[Development] [Development]
AutoCheckAccelerators=false AutoCheckAccelerators=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,7 +1,7 @@
// Status=review // Status=edited
Source code for _WSJT-X_ is available from a public repository at Source code for _WSJT-X_ is available from a public repository at
{devrepo}. To compile the program you will need to install at least the {devrepo}. To compile the program, at a minimum you must install the
following packages: following packages:
- Git - Git
@ -19,7 +19,7 @@ cd wsjtx
git checkout wsjtx-{VERSION} git checkout wsjtx-{VERSION}
===== =====
and for the current development branch, and for the current development branch:
===== =====
git clone git://git.code.sf.net/p/wsjt/wsjtx git clone git://git.code.sf.net/p/wsjt/wsjtx

View File

@ -1,16 +1,14 @@
// Status=review // Status=edited
Debian, Ubuntu, and other Debian-based systems including Raspbian: Debian, Ubuntu, and other Debian-based systems including Raspbian:
NOTE: The project team release binary installer packages for Linux NOTE: The project team release binary installer packages targeted for
when a new _WSJT-X_ release is announced. These are built to one contemporary version of a Linux distribution. Although these may
target one contemporary version of a Linux distribution. Although work on newer Linux versions or even different distributions, it is
these may work on newer Linux versions or even different unlikely that they work on older versions. Check the notes provided
distributions, it is unlikely that they will work on older with the release for details of the targeted Linux distributions and
versions. Check the notes provided with the release for details of the versions. If the binary package is not compatible with your Linux
targeted Linux distributions and versions. If the binary package is distribution or version, you must build the application from sources.
not compatible with your Linux distribution or version you must build
the application from sources.
* 32-bit: {debian32} * 32-bit: {debian32}
- To install: - To install:
@ -42,8 +40,11 @@ sudo dpkg -P wsjtx
You may also need to execute the following command in a terminal: You may also need to execute the following command in a terminal:
[example] ....
sudo apt install libqt5multimedia5-plugins libqt5serialport5 libqt5sql5-sqlite libfftw3-single3 sudo apt install libgfortran5 libqt5widgets5 libqt5network5 \
libqt5printsupport5 libqt5multimedia5-plugins libqt5serialport5 \
libqt5sql5-sqlite libfftw3-single3 libgomp1 libusb-1.0-0
....
Fedora, CentOS, Red Hat, and other rpm-based systems: Fedora, CentOS, Red Hat, and other rpm-based systems:
@ -71,5 +72,8 @@ sudo rpm -e wsjtx
You may also need to execute the following command in a terminal: You may also need to execute the following command in a terminal:
[example] ....
sudo dnf install fftw-libs-single qt5-qtmultimedia qt5-qtserialport sudo dnf install libgfortran fftw-libs-single qt5-qtbase \
qt5-qtmultimedia qt5-qtserialport qt5-qtsvg \
qt5-qtserialport libgomp libusbx
....

View File

@ -1,12 +1,12 @@
// These instructions are up-to-date for WSJT-X v2.2 // These instructions are up-to-date for WSJT-X v2.2
*OS X 10.12* and later: Download the file {osx} to your desktop, *macOS10.13* and later: Download the file {osx} to your desktop,
double-click on it and consult its `ReadMe` file for important double-click it and consult its `ReadMe` file for important
installation notes. installation notes.
If you have already installed a previous version, you can retain it by If you have already installed a previous version, you can retain it by
changing its name in the *Applications* folder (say, from _WSJT-X_ to changing its name in the *Applications* folder (such as from _WSJT-X_ to
_WSJT-X_2.1_). You can then proceed to the installation phase. _WSJT-X_2.2_). You can then proceed to the installation phase.
Take note also of the following: Take note also of the following:

View File

@ -1,7 +1,7 @@
// Status=edited // Status=edited
Download and execute the package file {win32} (WinXP, Vista, Win 7, Download and execute the package file {win32} (Win 7,
Win 8, Win10, 32-bit) or {win64} (Vista, Win 7, Win 8, Win10, 64-bit) Win 8, Win10, 32-bit) or {win64} (Win 7, Win 8, Win10, 64-bit)
following these instructions: following these instructions:
* Install _WSJT-X_ into its own directory, for example `C:\WSJTX` or `C:\WSJT\WSJTX`, rather than the conventional location `C:\Program * Install _WSJT-X_ into its own directory, for example `C:\WSJTX` or `C:\WSJT\WSJTX`, rather than the conventional location `C:\Program
@ -21,18 +21,32 @@ TIP: Your computer may be configured so that this directory is
`"%LocalAppData%\WSJT-X\"`. `"%LocalAppData%\WSJT-X\"`.
* The built-in Windows facility for time synchronization is usually * The built-in Windows facility for time synchronization is usually
not adequate. We recommend the program _Meinberg NTP_ (see not adequate. We recommend the program _Meinberg NTP Client_: see
{ntpsetup} for downloading and installation instructions) or {ntpsetup} for downloading and installation instructions. Recent
_Dimension 4_ from {dimension4}. Recent versions of Windows 10 are versions of Windows 10 are now shipped with a more capable Internet
now shipped with a more capable Internet time synchronization time synchronization service that is suitable if configured
service that is suitable if configured appropriately. appropriately. We do not recommend SNTP time setting tools or others
that make periodic correction steps, _WSJT-X_ requires that the PC
clock be monotonically increasing and smoothly continuous.
NOTE: Having a PC clock that appears to be synchronized to UTC is not
sufficient. "`Monotonically increasing`" means that the clock
must not be stepped backwards. "`Smoothly continuous`" means
that time must increase at a nearly constant rate, without
steps. Any necessary clock corrections must be applied by
adjusting the rate of increase, thereby correcting
synchronization errors gradually.
[[OPENSSL]] [[OPENSSL]]
image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error, image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error,
align="center"] align="center"]
* _WSJT-X_ requires installation of the _OpenSSL_ libraries. Suitable libraries may already be installed on your system. If they are not, you will see this error shortly after requesting a fetch of the latest LoTW users database. To fix this, install the _OpenSSL_ libraries. * _WSJT-X_ requires installation of the _OpenSSL_ libraries. Suitable
libraries may already be installed on your system. If they are not,
you will see this error shortly after requesting a fetch of the
latest LoTW users database. To fix this, install the _OpenSSL_
libraries.
** You can download a suitable _OpenSSL_ package for Windows from ** You can download a suitable _OpenSSL_ package for Windows from
{win_openssl_packages}; you need the latest *Windows Light* {win_openssl_packages}; you need the latest *Windows Light*

View File

@ -0,0 +1,40 @@
=== Documentation Conventions
In this manual the following icons call attention to particular types
of information:
NOTE: *Notes* containing information that may be of interest to
particular classes of users.
TIP: *Tips* on program features or capabilities that might otherwise be
overlooked.
IMPORTANT: *Warnings* about usage that could lead to undesired
consequences.
=== User Interface in Other Languages
The _WSJT-X_ user interface is now available in many languages. When
a translated user interface is available for the computer's default
System Language, it will appear automatically on program startup.
=== How You Can Contribute
_WSJT-X_ is part of an open-source project released under the
{gnu_gpl} (GPLv3). 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. We especially encourage
those with translation skills to volunteer their help, either for
this _User Guide_ or for the program's user interface.
The project's source-code repository can be found at {devrepo}, and
communication among the developers takes place on the email reflector
{devmail}. Bug reports and suggestions for new features, improvements
to the _WSJT-X_ User Guide, etc., may be sent there as well. You must
join the group before posting to the email list.
=== License
Before using _WSJT-X_, please read our licensing terms
<<LICENSE,here>>.

View File

@ -3,63 +3,73 @@
_WSJT-X_ is a computer program designed to facilitate basic amateur _WSJT-X_ is a computer program designed to facilitate basic amateur
radio communication using very weak signals. The first four letters in radio communication using very weak signals. The first four letters in
the program name stand for "`**W**eak **S**ignal communication by the program name stand for "`**W**eak **S**ignal communication by
K1**JT**,`" while the suffix "`-X`" indicates that _WSJT-X_ started as K1**JT**,`" while the suffix "`*-X*`" indicates that _WSJT-X_ started
an e**Xt**ended and e**X**perimental branch of the program _WSJT_, as an extended branch of an earlier program, _WSJT_, first released in
first released in 2001. Bill Somerville, G4WJS, and Steve Franke, 2001. Bill Somerville, G4WJS, and Steve Franke, K9AN, have been major
K9AN, have been major contributors to program development since 2013 contributors to development of _WSJT-X_ since 2013 and 2015, respectively.
and 2015, respectively.
_WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers ten different _WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers twelve
protocols or modes: *FT4*, *FT8*, *JT4*, *JT9*, *JT65*, *QRA64*, different protocols or modes: *FST4*, *FT4*, *FT8*, *JT4*, *JT9*,
*ISCAT*, *MSK144*, *WSPR*, and *Echo*. The first six are designed for *JT65*, *QRA64*, *ISCAT*, *MSK144*, *WSPR*, *FST4W*, and *Echo*. The
making reliable QSOs under weak-signal conditions. They use nearly first seven are designed for making reliable QSOs under weak-signal
identical message structure and source encoding. JT65 and QRA64 were conditions. They use nearly identical message structure and source
designed for EME ("`moonbounce`") on the VHF/UHF bands and have also encoding. JT65 and QRA64 were designed for EME ("`moonbounce`") on
proven very effective for worldwide QRP communication on the HF bands. the VHF/UHF bands and have also proven very effective for worldwide
QRA64 has a some advantages over JT65, including better performance QRP communication on the HF bands. QRA64 has some advantages over
for EME on the higher microwave bands. JT9 was originally designed JT65, including better performance for EME on the higher microwave
for the LF, MF, and lower HF bands. Its submode JT9A is 2 dB more bands. JT9 was originally designed for the HF and lower bands. Its
sensitive than JT65 while using less than 10% of the bandwidth. JT4 submode JT9A is 1 dB more sensitive than JT65 while using less than
offers a wide variety of tone spacings and has proven highly effective 10% of the bandwidth. JT4 offers a wide variety of tone spacings and
for EME on microwave bands up to 24 GHz. These four "`slow`" modes has proven highly effective for EME on microwave bands up to 24 GHz.
use one-minute timed sequences of alternating transmission and These four "`slow`" modes use one-minute timed sequences of
reception, so a minimal QSO takes four to six minutes — two or three alternating transmission and reception, so a minimal QSO takes four to
transmissions by each station, one sending in odd UTC minutes and the six minutes — two or three transmissions by each station, one sending
other even. FT8 is operationally similar but four times faster in odd UTC minutes and the other even. FT8 is operationally similar
(15-second T/R sequences) and less sensitive by a few dB. FT4 is but four times faster (15-second T/R sequences) and less sensitive by
faster still (7.5 s T/R sequences) and especially well suited for a few dB. FT4 is faster still (7.5 s T/R sequences) and especially
radio contesting. On the HF bands, world-wide QSOs are possible with well-suited for radio contesting. FST4 was added to _WSJT-X_ in
any of these modes using power levels of a few watts (or even version 2.3.0. It is intended especially for use on the LF and MF
milliwatts) and compromise antennas. On VHF bands and higher, QSOs bands, and already during its first few months of testing
are possible (by EME and other propagation types) at signal levels 10 intercontinental paths have been spanned many times on the 2200 and
to 15 dB below those required for CW. 630 m bands. Further details can be found in the following section,
<<NEW_FEATURES,New Features in Version 2.3.0>>. On the HF bands,
Note that even though their T/R sequences are short, FT4 and FT8 are world-wide QSOs are possible with any of these modes using power
classified as slow modes because their message frames are sent only levels of a few watts (or even milliwatts) and compromise antennas.
once per transmission. All fast modes in _WSJT-X_ send their message On VHF bands and higher, QSOs are possible (by EME and other
frames repeatedly, as many times as will fit into the Tx sequence propagation types) at signal levels 10 to 15 dB below those required
length. for CW.
*ISCAT*, *MSK144*, and optionally submodes *JT9E-H* are "`fast`" *ISCAT*, *MSK144*, and optionally submodes *JT9E-H* are "`fast`"
protocols designed to take advantage of brief signal enhancements from protocols designed to take advantage of brief signal enhancements from
ionized meteor trails, aircraft scatter, and other types of scatter ionized meteor trails, aircraft scatter, and other types of scatter
propagation. These modes use timed sequences of 5, 10, 15, or 30 s propagation. These modes use timed sequences of 5, 10, 15, or 30 s
duration. User messages are transmitted repeatedly at high rate (up duration. User messages are transmitted repeatedly at high rate (up
to 250 characters per second, for MSK144) to make good use of the to 250 characters per second for MSK144) to make good use of the
shortest meteor-trail reflections or "`pings`". ISCAT uses free-form shortest meteor-trail reflections or "`pings`". ISCAT uses free-form
messages up to 28 characters long, while MSK144 uses the same messages up to 28 characters long, while MSK144 uses the same
structured messages as the slow modes and optionally an abbreviated structured messages as the slow modes and optionally an abbreviated
format with hashed callsigns. format with hashed callsigns.
Note that some of the modes classified as slow can have T/R sequence
lengths as short the fast modes. "`Slow`" in this sense implies
message frames being sent only once per transmission. The fast modes
in _WSJT-X_ send their message frames repeatedly, as many times as
will fit into the Tx sequence length.
*WSPR* (pronounced "`whisper`") stands for **W**eak **S**ignal *WSPR* (pronounced "`whisper`") stands for **W**eak **S**ignal
**P**ropagation **R**eporter. The WSPR protocol was designed for probing **P**ropagation **R**eporter. The WSPR protocol was designed for
potential propagation paths using low-power transmissions. WSPR probing potential propagation paths using low-power transmissions.
messages normally carry the transmitting stations callsign, grid WSPR messages normally carry the transmitting stations callsign,
locator, and transmitter power in dBm, and they can be decoded at grid locator, and transmitter power in dBm, and with two-minute
signal-to-noise ratios as low as -31 dB in a 2500 Hz bandwidth. WSPR sequences they can be decoded at signal-to-noise ratios as low
users with internet access can automatically upload reception as -31 dB in a 2500 Hz bandwidth. *FST4W* is designed for
reports to a central database called {wsprnet} that provides a mapping similar purposes, but especially for use on LF and MF bands.
facility, archival storage, and many other features. It includes optional sequence lengths as long as 30 minutes and
reaches sensitivity tresholds as low as -45 dB. Users
with internet access can automatically upload WSPR and FST4W
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 *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. from the moon, even if they are far below the audible threshold.
@ -80,4 +90,4 @@ be beta releases leading up to the final release of v2.1.0.
Release candidates should be used _only_ during a short testing Release candidates should be used _only_ during a short testing
period. They carry an implied obligation to provide feedback to the period. They carry an implied obligation to provide feedback to the
program development group. Candidate releases should not be used on program development group. Candidate releases should not be used on
the air after a full release with the same number has been made. the air after a full release with the same number is made.

View File

@ -1,7 +1,9 @@
//status: edited
A basic logging facility in _WSJT-X_ saves QSO information to files A basic logging facility in _WSJT-X_ saves QSO information to files
named `wsjtx.log` (in comma-separated text format) and `wsjtx_log.adi` named `wsjtx.log` (in comma-separated text format) and `wsjtx_log.adi`
(in standard ADIF format). These files can be imported directly into (in standard ADIF format). These files can be imported directly into
other programs, for example spreadsheets and popular logging programs. other programs (such as spreadsheets and popular logging programs).
As described in the <<INSTALL,Installation>> and <<PLATFORM,Platform As described in the <<INSTALL,Installation>> and <<PLATFORM,Platform
Dependencies>> sections, different operating systems may place your Dependencies>> sections, different operating systems may place your
local log files in different locations. You can always navigate to local log files in different locations. You can always navigate to
@ -12,30 +14,32 @@ applications like {jtalert}, which can log QSOs automatically to other
applications including {hrd}, {dxlsuite}, and {log4om}. applications including {hrd}, {dxlsuite}, and {log4om}.
The program option *Show DXCC entity and worked before status* The program option *Show DXCC entity and worked before status*
(selectable on the *Settings | General* tab) is intended mostly for (selectable on the *File | Settings | General* tab) is intended mostly for
use on non-Windows platforms, where {jtalert} is not available. When use on non-Windows platforms, where {jtalert} is not available. When
this option is checked _WSJT-X_ appends some additional information to this option is checked, _WSJT-X_ appends some additional information to
all CQ messages displayed in the _Band Activity_ window. The name of all CQ messages displayed in the _Band Activity_ window. The name of
the DXCC entity is shown, abbreviated if necessary. Your "`worked the DXCC entity is shown, abbreviated if necessary. Your "`worked
before`" status for this callsign (according to log file before`" status for this callsign (according to log file
`wsjtx_log.adi`) is indicated by highlighting colors, if that option `wsjtx_log.adi`) is indicated by highlighting colors, if that option
has been selected. is selected.
_WSJT-X_ includes a built-in `cty.dat` file containing DXCC prefix _WSJT-X_ includes a built-in `cty.dat` file containing DXCC prefix
information. Updated files can be downloaded from the {cty_dat} web information. Updated files can be downloaded from the {cty_dat} web
site when required. If an updated `cty.dat` is present in the logs site when required. If an updated and readable `cty.dat` file is
folder and readable, it will be used in preference to the built-in present in the logs folder, it is used in preference to the
one. built-in file.
The log file `wsjtx_log.adi` is updated whenever you log a QSO from The log file `wsjtx_log.adi` is updated whenever you log a QSO from
_WSJT-X_. (Keep in mind that if you erase this file you will lose all _WSJT-X_. (Keep in mind that if you erase this file, you lose all
"`worked before`" information.) You can append or overwrite the "`worked before`" information.) You can append or overwrite the
`wsjtx_log.adi` file by exporting your QSO history as an ADIF file `wsjtx_log.adi` file by exporting your QSO history as an ADIF file
from another logging program. Turning *Show DXCC entity and worked from another logging program. Turning *Show DXCC entity and worked
before status* off and then on again will cause _WSJT-X_ to re-read before status* off and then on again causes _WSJT-X_ to re-read
the log file. Very large log files may cause _WSJT-X_ to slow down the log file. Very large log files may cause _WSJT-X_ to slow down
when searching for calls. If the ADIF log file has been changed when searching for calls. If the ADIF log file has been changed
outside of _WSJT-X_ you can force _WSJT-X_ to reload the file from the outside of _WSJT-X_ you can force _WSJT-X_ to reload the file from the
*Settings | Colors* tab using the *Rescan ADIF Log* button, see *Settings | Colors* tab using the *Rescan ADIF Log* button, see
<<COLORS,Decode Highlighting>>. <<COLORS,Decode Highlighting>>.
Additional features are provided for *Contest* and *Fox* logging.
(more to come, here ...)

View File

@ -37,7 +37,7 @@ assigns more reliable numbers to relatively strong signals.
NOTE: Signals become visible on the waterfall around S/N = 26 dB and NOTE: Signals become visible on the waterfall around S/N = 26 dB and
audible (to someone with very good hearing) around 15 dB. Thresholds audible (to someone with very good hearing) around 15 dB. Thresholds
for decodability are around -20 dB for FT8, -23 dB for JT4, 25 dB for for decodability are around -20 dB for FT8, -23 dB for JT4, 25 dB for
JT65, 27 dB for JT9. JT65, and 27 dB for JT9.
NOTE: Several options are available for circumstances where fast QSOs NOTE: Several options are available for circumstances where fast QSOs
are desirable. Double-click the *Tx1* control under _Now_ or _Next_ are desirable. Double-click the *Tx1* control under _Now_ or _Next_
@ -75,7 +75,7 @@ When calling CQ you may also choose to check the box *Call 1st*.
_WSJT-X_ will then respond automatically to the first decoded _WSJT-X_ will then respond automatically to the first decoded
responder to your CQ. responder to your CQ.
NOTE: When *Auto-Seq* is enabled the program de-activates *Enable Tx* NOTE: When *Auto-Seq* is enabled, the program de-activates *Enable Tx*
at the end of each QSO. It is not intended that _WSJT-X_ should make at the end of each QSO. It is not intended that _WSJT-X_ should make
fully automated QSOs. fully automated QSOs.
@ -259,7 +259,7 @@ that a second callsign is never permissible in these messages.
NOTE: During a transmission your outgoing message is displayed in the NOTE: During a transmission your outgoing message is displayed in the
first label on the *Status Bar* and shown exactly as another station first label on the *Status Bar* and shown exactly as another station
will receive it. You can check to see that you are actually receives it. You can check to see that you are actually
transmitting the message you wish to send. transmitting the message you wish to send.
QSOs involving *Type 2* compound callsigns might look like either QSOs involving *Type 2* compound callsigns might look like either
@ -287,7 +287,7 @@ standard structured messages without callsign prefix or suffix.
TIP: If you are using a compound callsign, you may want to TIP: If you are using a compound callsign, you may want to
experiment with the option *Message generation for type 2 compound experiment with the option *Message generation for type 2 compound
callsign holders* on the *Settings | General* tab, so that messages callsign holders* on the *File | Settings | General* tab, so that messages
will be generated that best suit your needs. will be generated that best suit your needs.
=== Pre-QSO Checklist === Pre-QSO Checklist

View File

@ -1,6 +1,8 @@
//Status: edited
=== Frequency Calibration === Frequency Calibration
Many _WSJT-X_ capabilities depend on signal-detection bandwidths no Many _WSJT-X_ capabilities depend on signal-detection bandwidths of no
more than a few Hz. Frequency accuracy and stability are therefore more than a few Hz. Frequency accuracy and stability are therefore
unusually important. We provide tools to enable accurate frequency unusually important. We provide tools to enable accurate frequency
calibration of your radio, as well as precise frequency measurement of calibration of your radio, as well as precise frequency measurement of
@ -11,11 +13,11 @@ measuring the error in dial frequency for each signal.
You will probably find it convenient to define and use a special You will probably find it convenient to define and use a special
<<CONFIG-MENU,Configuration>> dedicated to frequency calibration. <<CONFIG-MENU,Configuration>> dedicated to frequency calibration.
Then complete the following steps, as appropriate for your system. Then complete the following steps, as appropriate, for your system.
- Switch to FreqCal mode - Switch to FreqCal mode
- In the _Working Frequencies_ box on the *Settings -> Frequencies* - In the _Working Frequencies_ box on the *File | Settings | Frequencies*
tab, delete any default frequencies for *FreqCal* mode that are not tab, delete any default frequencies for *FreqCal* mode that are not
relevant for your location. You may want to replace some of them with relevant for your location. You may want to replace some of them with
reliably known frequencies receivable at your location. reliably known frequencies receivable at your location.
@ -29,14 +31,14 @@ of WWV at 2.500, 5.000, 10.000, 15.000, and 20.000 MHz, and CHU at
3.330, 7.850, and 14.670 MHz. Similar shortwave signals are available 3.330, 7.850, and 14.670 MHz. Similar shortwave signals are available
in other parts of the world. in other parts of the world.
- In most cases you will want to start by deleting any existing file - In most cases, start by deleting any existing file `fmt.all` in the
`fmt.all` in the directory where your log files are kept. directory where your log files are kept.
- To cycle automatically through your chosen list of calibration - To cycle automatically through your chosen list of calibration
frequencies, check *Execute frequency calibration cycle* on the frequencies, check *Execute frequency calibration cycle* on the
*Tools* menu. _WSJT-X_ will spend 30 seconds at each *Tools* menu. _WSJT-X_ will spend 30 seconds at each
frequency. Initially no measurement data is saved to the `fmt.all` frequency. Initially no measurement data is saved to the `fmt.all`
file although it is displayed on screen, this allows you to check your file although it is displayed on screen; this allows you to check your
current calibration parameters. current calibration parameters.
- During the calibration procedure, the radio's USB dial frequency is - During the calibration procedure, the radio's USB dial frequency is
@ -61,7 +63,7 @@ the nominal frequency itself (in MHz). For example, the 20 MHz
measurement for WWV shown above produced a measured tone offset of measurement for WWV shown above produced a measured tone offset of
24.6 Hz, displayed in the _WSJT-X_ decoded text window. The resulting 24.6 Hz, displayed in the _WSJT-X_ decoded text window. The resulting
calibration constant is 24.6/20=1.23 parts per million. This number calibration constant is 24.6/20=1.23 parts per million. This number
may be entered as *Slope* on the *settings -> Frequencies* tab. may be entered as *Slope* on the *File | Settings | Frequencies* tab.
A more precise calibration can be effected by fitting the intercept A more precise calibration can be effected by fitting the intercept
and slope of a straight line to the whole sequence of calibration and slope of a straight line to the whole sequence of calibration
@ -81,19 +83,19 @@ After running *Execute frequency calibration cycle* at least once with
good results, check and edit the file `fmt.all` in the log directory good results, check and edit the file `fmt.all` in the log directory
and delete any spurious or outlier measurements. The line-fitting and delete any spurious or outlier measurements. The line-fitting
procedure can then be carried out automatically by clicking *Solve for procedure can then be carried out automatically by clicking *Solve for
calibration parameters* on the *Tools* menu. The results will be calibration parameters* on the *Tools* menu. The results are
displayed as in the following screen shot. Estimated uncertainties displayed as in the following screen shot. Estimated uncertainties
are included for slope and intercept; `N` is the number of averaged are included for slope and intercept; `N` is the number of averaged
frequency measurements included in the fit, and `StdDev` is the root frequency measurements included in the fit, and `StdDev` is the root
mean square deviation of averaged measurements from the fitted mean square deviation of averaged measurements from the fitted
straight line. If the solution seems valid you will be offered an straight line. If the solution seems valid, you are offered an
*Apply* button to push that will automatically set the calibration *Apply* button to push that automatically sets the calibration
parameters in *Settings -> Frequencies -> Frequency Calibration*. parameters in *File | Settings | Frequencies | Frequency Calibration*.
image::FreqCal_Results.png[align="center",alt="FreqCal_Results"] image::FreqCal_Results.png[align="center",alt="FreqCal_Results"]
For a quick visual check of the resulting calibration, stay in For a quick visual check of the resulting calibration, stay in
*FreqCal* mode with the *Measure* option cleared. _WSJT-X_ will show *FreqCal* mode with the *Measure* option cleared. _WSJT-X_ shows
the adjusted results directly on the waterfall and the displayed the adjusted results directly on the waterfall and the displayed
records. records.
@ -103,8 +105,8 @@ _WSJT-X_ provides a tool that can be used to determine the detailed
shape of your receiver's passband. Disconnect your antenna or tune to shape of your receiver's passband. Disconnect your antenna or tune to
a quiet frequency with no signals. With _WSJT-X_ running in one of a quiet frequency with no signals. With _WSJT-X_ running in one of
the slow modes, select *Measure reference spectrum* from the *Tools* the slow modes, select *Measure reference spectrum* from the *Tools*
menu. Wait for about a minute and then hit the *Stop* button. A file menu. Wait for about a minute and then click *Stop*. A file
named `refspec.dat` will appear in your log directory. When you check named `refspec.dat` appears in your log directory. When you check
*Ref Spec* on the *Wide Graph*, the recorded reference spectrum will *Ref Spec* on the *Wide Graph*, the recorded reference spectrum will
then be used to flatten your overall effective passband. then be used to flatten your overall effective passband.
@ -122,39 +124,39 @@ response* generates an undistorted audio waveform equal to the one
generated by the transmitting station. Its Fourier transform is then generated by the transmitting station. Its Fourier transform is then
used as a frequency-dependent phase reference to compare with the used as a frequency-dependent phase reference to compare with the
phase of the received frame's Fourier coefficients. Phase differences phase of the received frame's Fourier coefficients. Phase differences
between the reference spectrum and received spectrum will include between the reference spectrum and received spectrum include
contributions from the originating station's transmit filter, the contributions from the originating station's transmit filter, the
propagation channel, and filters in the receiver. If the received propagation channel, and filters in the receiver. If the received
frame originates from a station known to transmit signals having frame originates from a station known to transmit signals having
little phase distortion (say, a station known to use a properly little phase distortion (such as a station known to use a properly
adjusted software-defined-transceiver) and if the received signal is adjusted software-defined transceiver), and if the received signal is
relatively free from multipath distortion so that the channel phase is relatively free from multipath distortion so that the channel phase is
close to linear, the measured phase differences will be representative close to linear, the measured phase differences will be representative
of the local receiver's phase response. of the local receiver's phase response.
Complete the following steps to generate a phase equalization curve: Complete the following steps to generate a phase equalization curve:
- Record a number of wav files that contain decodable signals from - Record a number of `wav` files that contain decodable signals from
your chosen reference station. Best results will be obtained when the your chosen reference station. Best results are obtained when the
signal-to-noise ratio of the reference signals is 10 dB or greater. signal-to-noise ratio of the reference signals is 10 dB or greater.
- Enter the callsign of the reference station in the DX Call box. - Enter the callsign of the reference station in the DX Call box.
- Select *Measure phase response* from the *Tools* menu, and open each - Select *Measure phase response* from the *Tools* menu, and open each
of the wav files in turn. The mode character on decoded text lines of the `wav` files in turn. The mode character on decoded text lines
will change from `&` to `^` while _WSJT-X_ is measuring the phase changes from `&` to `^` while _WSJT-X_ is measuring the phase
response, and it will change back to `&` after the measurement is response, and it changes back to `&` after the measurement is
completed. The program needs to average a number of high-SNR frames to completed. The program needs to average a number of high-SNR frames to
accurately estimate the phase, so it may be necessary to process accurately estimate the phase, so it may be necessary to process
several wav files. The measurement can be aborted at any time by several `wav` files. The measurement can be aborted at any time by
selecting *Measure phase response* again to toggle the phase selecting *Measure phase response* again to toggle the phase
measurement off. measurement off.
+ +
When the measurement is complete _WSJT-X_ will save the measured When the measurement is complete, _WSJT-X_ saves the measured
phase response in the *Log directory*, in a file with suffix phase response in the *Log directory*, in a file with suffix
".pcoeff". The filename will contain the callsign of the reference ".pcoeff". The filename contains the callsign of the reference
station and a timestamp, for example `K0TPP_170923_112027.pcoeff`. station and a timestamp, for example `K0TPP_170923_112027.pcoeff`.
- Select *Equalization tools ...* under the *Tools* menu and click the - Select *Equalization tools ...* under the *Tools* menu and click the
@ -165,23 +167,23 @@ the proposed phase equalization curve. It's a good idea to repeat the
phase measurement several times, using different wav files for each phase measurement several times, using different wav files for each
measurement, to ensure that your measurements are repeatable. measurement, to ensure that your measurements are repeatable.
- Once you are satisfied with a fitted curve, push the *Apply* button - Once you are satisfied with a fitted curve, click the *Apply* button
to save the proposed response. The red curve will be replaced with a to save the proposed response. The red curve is replaced with a
light green curve labeled "Current" to indicate that the phase light green curve labeled "Current" to indicate that the phase
equalization curve is now being applied to the received data. Another equalization curve is now being applied to the received data. Another
curve labeled "Group Delay" will appear. The "Group Delay" curve shows curve labeled "Group Delay" appears. The "Group Delay" curve shows
the group delay variation across the passband, in ms. Click the the group delay variation across the passband, in ms. Click the
*Discard Measured* button to remove the captured data from the plot, *Discard Measured* button to remove the captured data from the plot,
leaving only the applied phase equalization curve and corresponding leaving only the applied phase equalization curve and corresponding
group delay curve. group delay curve.
- To revert to no phase equalization, push the *Restore Defaults* - To revert to no phase equalization, click the *Restore Defaults*
button followed by the *Apply* button. button followed by the *Apply* button.
The three numbers printed at the end of each MSK144 decode line can be The three numbers printed at the end of each MSK144 decode line can be
used to assess the improvement provided by equalization. These numbers used to assess the improvement provided by equalization. These numbers
are: `N` = Number of frames averaged, `H` = Number of hard bit errors are: `N` = Number of frames averaged, `H` = Number of hard bit errors
corrected, `E` = Size of MSK eye diagram opening. corrected, and `E` = Size of MSK eye diagram opening.
Here is a decode of K0TPP obtained while *Measure phase response* was measuring Here is a decode of K0TPP obtained while *Measure phase response* was measuring
the phase response: the phase response:
@ -196,7 +198,7 @@ scale. Here's how the same decode looks after phase equalization:
103900 17 6.5 1493 & WA8CLT K0TPP +07 1 0 1.6 103900 17 6.5 1493 & WA8CLT K0TPP +07 1 0 1.6
In this case, equalization has increased the eye opening from 1.2 to In this case, equalization has increased the eye-opening from 1.2 to
1.6. Larger positive eye openings are associated with reduced 1.6. Larger positive eye openings are associated with reduced
likelihood of bit errors and higher likelihood that a frame will be likelihood of bit errors and higher likelihood that a frame will be
successfully decoded. In this case, the larger eye-opening tells us successfully decoded. In this case, the larger eye-opening tells us
@ -206,7 +208,7 @@ equalization curve is going to improve decoding of signals other than
those from the reference station, K0TPP. those from the reference station, K0TPP.
It's a good idea to carry out before and after comparisons using a It's a good idea to carry out before and after comparisons using a
large number of saved wav files with signals from many different large number of saved `wav` files with signals from many different
stations, to help decide whether your equalization curve improves stations, to help decide whether your equalization curve improves
decoding for most signals. When doing such comparisons, keep in mind decoding for most signals. When doing such comparisons, keep in mind
that equalization may cause _WSJT-X_ to successfully decode a frame that equalization may cause _WSJT-X_ to successfully decode a frame

View File

@ -1,107 +1,34 @@
[[NEW_FEATURES]]
=== New in Version {VERSION} === New in Version {VERSION}
*Improvements to decoders* _WSJT-X 2.3.0_ introduces *FST4* and *FST4W*, new digital protocols
designed particularly for the LF and MF bands. Decoders for these
modes can take advantage of the very small Doppler spreads present at
these frequencies, even over intercontinental distances. As a
consequence, fundamental sensitivities of FST4 and FST4W are better
than other _WSJT-X_ modes with the same sequence lengths, approaching
the theoretical limits for their rates of information throughput. The
FST4 protocol is optimized for two-way QSOs, while FST4W is for
quasi-beacon transmissions of WSPR-style messages. FST4 and FST4W do
not require the strict, independent phase locking and time
synchronization of modes like EbNaut.
*FT4:* Corrected bugs that prevented AP (_a priori_) decoding and/or The new modes use 4-GFSK modulation and share common software for
multi-pass decoding in some circumstances. Improved and extended the encoding and decoding messages. FST4 offers T/R sequence lengths of
algorithm for AP decoding. 15, 30, 60, 120, 300, 900, and 1800 seconds, while FST4W omits the
lengths shorter than 120 s. Submodes are given names like FST4-60,
FST4W-300, etc., the appended numbers indicating sequence length in
seconds. Message payloads contain either 77 bits, as in FT4, FT8, and
MSK144, or 50 bits for the WSPR-like messages of FST4W. Message
formats displayed to the user are like those in the other 77-bit and
50-bit modes in _WSJT-X_. Forward error correction uses a low density
parity check (LDPC) code with 240 information and parity bits.
Transmissions consist of 160 symbols: 120 information-carrying symbols
of two bits each, interspersed with five groups of eight predefined
synchronization symbols.
*FT8:* Decoding is now spread over three intervals. The first starts *We recommend that on the 2200 and 630 m bands FST4 should replace JT9
11.8 s into an Rx sequence and typically yields around 85% of the for making 2-way QSOs, and FST4W should replace WSPR for propagation
possible decodes, so you see most decodes much earlier than before. A tests*. Operating conventions on these LF and MF bands will
second processing step starts at 13.5 s, and the final one at 14.7 s. eventually determine the most useful T/R sequence lengths for each
Overall decoding yield on crowded bands is improved by 10% or more. type of operation.
Systems with receive latency greater than 0.2 s will see smaller
improvements, but will still see many decodes earlier than before.
SNR estimates no longer saturate at +20 dB, and large signals in the
passband no longer cause the SNR of weaker signals to be biased low.
Times written to cumulative journal file ALL.TXT are now correct even
when the decode occurs after the T/R sequence boundary. In FT8
DXpedition Mode, AP decoding is now implemented for Hounds when the
Fox has a compound callsign.
*JT4:* Formatting and display of averaged and Deep Search decodes has
been cleaned up and made consistent with other modes used for EME and
extreme weak-signal work on microwave bands.
*JT65:* Many improvements have been made for averaged and Deep Search
decodes, and their display to the user. For details see <<VHF_JT65,JT65>>
in the <<VHF_AND_UP,VHF+ Features>> section of this guide.
*WSPR:* Significant improvements have been made to the WSPR decoder's
sensitivity, its ability to cope with many signals in a crowded
sub-band, and its rate of undetected false decodes. We now use up to
three decoding passes. Passes 1 and 2 use noncoherent demodulation of
single symbols and allow for frequency drifts up to ±4 Hz in a
transmission. Pass 3 assumes no drift and does coherent block
detection of up to three symbols. It also applies bit-by-bit
normalization of the single-symbol bit metrics, a technique that has
proven helpful for signals corrupted by artifacts of the subtraction
of stronger signals and also for LF/MF signals heavily contaminated by
lightning transients. With these improvements the number of decodes
in a crowded WSPR sub-band typically increases by 10 to 15%.
*New message format:* When *EU VHF Contest* is selected, the Tx2 and
Tx3 messages -- those conveying signal report, serial number, and
6-character locator -- now use hashcodes for both callsigns. This
change is *not* backward compatible with earlier versions of _WSJT-X_, so
all users of *EU VHF Contest* messages should be sure to upgrade to
version 2.2.0. See <<CONTEST_MSGS,Contest Messages>> for details.
*Minor enhancements and bug fixes*
- *Save None* now writes no .wav files to disk, even temporarily.
- An explicit entry for *WW Digi Contest* has been added to *Special
operating activities* on the *Settings | Advanced* tab.
- The contest mode FT4 now always uses RR73 for the Tx4 message.
- *Keyboard shortcuts* have been added as an aid to accessibility:
*Alt+R* sets Tx4 message to RR73, *Ctrl+R* sets it to RRR.
- The *Status bar* now displays the number of decodes found in the
most recent Rx sequence.
- As an aid for partial color-blindness, the "`inverted goal posts`"
marking Rx frequency on the Wide Graph's frequency scale are now in a
darker shade of green.
=== Documentation Conventions
In this manual the following icons call attention to particular types
of information:
NOTE: *Notes* containing information that may be of interest to
particular classes of users.
TIP: *Tips* on program features or capabilities that might otherwise be
overlooked.
IMPORTANT: *Warnings* about usage that could lead to undesired
consequences.
=== User Interface in Other Languages
Thanks to Xavi Perez, EA3W, in cooperation with G4WJS, the _WSJT-X_
user interface is now available the Catalan language. Spanish will
follow soon, and other languages when translations are made. When a
translated user interface is available for the computer's default
System Language, it will appear automatically on program startup.
=== How You Can Contribute
_WSJT-X_ is part of an open-source project released under the
{gnu_gpl} (GPLv3). 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. We especially encourage
those with translation skills to volunteer their help, either for
this _User Guide_ or for the program's user interface.
The project's source-code repository can be found at {devrepo}, and
communication among the developers takes place on the email reflector
{devmail}. Bug reports and suggestions for new features, improvements
to the _WSJT-X_ User Guide, etc., may be sent there as well. You must
join the group before posting to the email list.

View File

@ -1,3 +1,5 @@
//status: edited
[[PROTOCOL_OVERVIEW]] [[PROTOCOL_OVERVIEW]]
=== Overview === Overview
@ -12,11 +14,11 @@ Special cases allow other information such as add-on callsign prefixes
aim is to compress the most common messages used for minimally valid aim is to compress the most common messages used for minimally valid
QSOs into a fixed 72-bit length. QSOs into a fixed 72-bit length.
The information payload for FT4, FT8, and MSK144 contains 77 bits. Information payloads for FST4, FT4, FT8, and MSK144 contain 77 bits.
The 5 new bits added to the original 72 are used to flag special The 5 additional bits are used to flag special message types used for
message types signifying special message types used for FT8 DXpedition nonstandard callsigns, contest exchanges, FT8 DXpedition Mode, and a
Mode, contesting, nonstandard callsigns, and a few other few other possibilities. Full details have been published in QEX, see
possibilities. {ft4_ft8_protocols}.
A standard amateur callsign consists of a one- or two-character A standard amateur callsign consists of a one- or two-character
prefix, at least one of which must be a letter, followed by a digit prefix, at least one of which must be a letter, followed by a digit
@ -30,17 +32,17 @@ of 4-digit Maidenhead grid locators on earth is 180×180 = 32,400,
which is less than 2^15^ = 32,768; so a grid locator requires 15 bits. which is less than 2^15^ = 32,768; so a grid locator requires 15 bits.
Some 6 million of the possible 28-bit values are not needed for Some 6 million of the possible 28-bit values are not needed for
callsigns. A few of these slots have been assigned to special message callsigns. A few of these slots are assigned to special message
components such as `CQ`, `DE`, and `QRZ`. `CQ` may be followed by three components such as `CQ`, `DE`, and `QRZ`. `CQ` may be followed by three
digits to indicate a desired callback frequency. (If K1ABC transmits digits to indicate a desired callback frequency. (If K1ABC transmits
on a standard calling frequency, say 50.280, and sends `CQ 290 K1ABC on a standard calling frequency such as 50.280, and sends `CQ 290 K1ABC
FN42`, it means that s/he will listen on 50.290 and respond there to FN42`, it means that s/he will listen on 50.290 and respond there to
any replies.) A numerical signal report of the form `nn` or any replies.) A numerical signal report of the form `nn` or
`Rnn` can be sent in place of a grid locator. (As originally `Rnn` can be sent in place of a grid locator. (As originally
defined, numerical signal reports `nn` were required to fall between -01 defined, numerical signal reports `nn` were required to fall between -01
and -30 dB. Recent program versions accommodate reports between and -30 dB. Recent program versions 2.3 and later accommodate reports between
-50 and +49 dB.) A country prefix or portable suffix may be -50 and +49 dB.) A country prefix or portable suffix may be
attached to one of the callsigns. When this feature is used the attached to one of the callsigns. When this feature is used, the
additional information is sent in place of the grid locator or by additional information is sent in place of the grid locator or by
encoding additional information into some of the 6 million available encoding additional information into some of the 6 million available
slots mentioned above. slots mentioned above.
@ -52,11 +54,6 @@ were the callsigns `E9AA` through `E9ZZ`. Upon reception they are
converted back to the form `CQ AA` through `CQ ZZ`, for display to the converted back to the form `CQ AA` through `CQ ZZ`, for display to the
user. user.
The FT4, FT8, and MSK144 protocols use different lossless compression
algorithms with features that generate and recognize special messages
used for contesting and other special purposes. Full details have
been published in QEX, see {ft4_ft8_protocols}.
To be useful on channels with low signal-to-noise ratio, this kind of To be useful on channels with low signal-to-noise ratio, this kind of
lossless message compression requires use of a strong forward error lossless message compression requires use of a strong forward error
correcting (FEC) code. Different codes are used for each mode. correcting (FEC) code. Different codes are used for each mode.
@ -69,6 +66,20 @@ _WSJT-X_ modes have continuous phase and constant envelope.
[[SLOW_MODES]] [[SLOW_MODES]]
=== Slow Modes === Slow Modes
[[FST4PRO]]
==== FST4
FST4 offers T/R sequence lengths of 15, 30, 60, 120, 300, 900, and
1800 seconds. Submodes are given names like FST4-60, FST4-120, etc.,
the appended numbers indicating sequence length in seconds. A 24-bit
cyclic redundancy check (CRC) is appended to the 77-bit message
payload to create a 101-bit message-plus-CRC word. Forward error
correction is accomplished using a (240,101) LDPC code. Transmissions
consist of 160 symbols: 120 information-carrying symbols of two bits
each, interspersed with five groups of eight predefined
synchronization symbols. Modulation uses 4-tone frequency-shift
keying (4-GFSK) with Gaussian smoothing of frequency transitions.
[[FT4PRO]] [[FT4PRO]]
==== FT4 ==== FT4
@ -147,7 +158,8 @@ following pseudo-random sequence:
The synchronizing tone is normally sent in each interval having a The synchronizing tone is normally sent in each interval having a
"`1`" in the sequence. Modulation is 65-FSK at 11025/4096 = 2.692 "`1`" in the sequence. Modulation is 65-FSK at 11025/4096 = 2.692
baud. Frequency spacing between tones is equal to the keying rate for baud. Frequency spacing between tones is equal to the keying rate for
JT65A, and 2 and 4 times larger for JT65B and JT65C. For EME QSOs the JT65A, and 2 and 4 times larger for JT65B and JT65C, respectively.
For EME QSOs the
signal report OOO is sometimes used instead of numerical signal signal report OOO is sometimes used instead of numerical signal
reports. It is conveyed by reversing sync and data positions in the reports. It is conveyed by reversing sync and data positions in the
transmitted sequence. Shorthand messages for RO, RRR, and 73 dispense transmitted sequence. Shorthand messages for RO, RRR, and 73 dispense
@ -155,7 +167,7 @@ with the sync vector entirely and use time intervals of 16384/11025 =
1.486 s for pairs of alternating tones. The lower frequency is the 1.486 s for pairs of alternating tones. The lower frequency is the
same as that of the sync tone used in long messages, and the frequency same as that of the sync tone used in long messages, and the frequency
separation is 110250/4096 = 26.92 Hz multiplied by n for JT65A, with n separation is 110250/4096 = 26.92 Hz multiplied by n for JT65A, with n
= 2, 3, 4 used to convey the messages RO, RRR, and 73. = 2, 3, 4 used to convey the messages RO, RRR, and 73, respectively.
[[QRA64_PROTOCOL]] [[QRA64_PROTOCOL]]
==== QRA64 ==== QRA64
@ -222,10 +234,24 @@ information the least significant. Thus, on a 0 3 scale, the tone
for a given symbol is twice the value (0 or 1) of the data bit, plus for a given symbol is twice the value (0 or 1) of the data bit, plus
the sync bit. the sync bit.
[[FST4WPRO]]
==== FST4W
FST4W offers T/R sequence lengths of 120, 300, 900, and 1800 seconds.
Submodes are given names like FST4W-120, FST4W-300, etc., the appended
numbers indicating sequence length in seconds. Message payloads
contain 50 bits, and a 24-bit cyclic redundancy check (CRC) appended
to create a 74-bit message-plus-CRC word. Forward error correction
is accomplished using a (240,74) LDPC code. Transmissions consist of
160 symbols: 120 information-carrying symbols of two bits each,
interspersed with five groups of eight predefined synchronization
symbols. Modulation uses 4-tone frequency-shift keying (4-GFSK) with
Gaussian smoothing of frequency transitions.
[[SLOW_SUMMARY]] [[SLOW_SUMMARY]]
==== Summary ==== Summary
Table 7 provides a brief summary parameters for the slow modes in Table 7 provides a brief summary of parameters for the slow modes in
_WSJT-X_. Parameters K and r specify the constraint length and rate _WSJT-X_. Parameters K and r specify the constraint length and rate
of the convolutional codes; n and k specify the sizes of the of the convolutional codes; n and k specify the sizes of the
(equivalent) block codes; Q is the alphabet size for the (equivalent) block codes; Q is the alphabet size for the
@ -236,17 +262,28 @@ which the probability of decoding is 50% or higher.
[[SLOW_TAB]] [[SLOW_TAB]]
.Parameters of Slow Modes .Parameters of Slow Modes
[width="90%",cols="3h,^3,^2,^1,^2,^2,^2,^2,^2,^2",frame=topbot,options="header"] [width="100%",cols="3h,^3,^2,^1,^2,^2,^2,^2,^2,^2",frame=topbot,options="header"]
|=============================================================================== |===============================================================================
|Mode |FEC Type |(n,k) | Q|Modulation type|Keying rate (Baud)|Bandwidth (Hz) |Mode |FEC Type |(n,k) | Q|Modulation type|Keying rate (Baud)|Bandwidth (Hz)
|Sync Energy|Tx Duration (s)|S/N Threshold (dB) |Sync Energy|Tx Duration (s)|S/N Threshold (dB)
|FT4 |LDPC, r=1/2|(174,91)| 4| 4-GFSK| 20.8333 | 83.3 | 0.15| 5.04 | -17.5 |FST4-15 |LDPC | (240,101)| 4| 4-GFSK| 16.67 | 67.7 | 0.25| 9.6 | -20.7
|FT8 |LDPC, r=1/2|(174,91)| 8| 8-GFSK| 6.25 | 50.0 | 0.27| 12.6 | -21 |FST4-30 |LDPC | (240,101)| 4| 4-GFSK| 7.14 | 28.6 | 0.25| 22.4 | -24.2
|FST4-60 |LDPC | (240,101)| 4| 4-GFSK| 3.09 | 12.4 | 0.25| 51.8 | -28.1
|FST4-120 |LDPC | (240,101)| 4| 4-GFSK| 1.46 | 5.9 | 0.25| 109.3 | -31.3
|FST4-300 |LDPC | (240,101)| 4| 4-GFSK| 0.558 | 2.2 | 0.25| 286.7 | -35.3
|FST4-900 |LDPC | (240,101)| 4| 4-GFSK| 0.180 | 0.72 | 0.25| 887.5 | -40.2
|FST4-1800 |LDPC | (240,101)| 4| 4-GFSK| 0.089 | 0.36 | 0.25| 1792.0| -43.2
|FT4 |LDPC |(174,91)| 4| 4-GFSK| 20.83 | 83.3 | 0.15| 5.04 | -17.5
|FT8 |LDPC |(174,91)| 8| 8-GFSK| 6.25 | 50.0 | 0.27| 12.6 | -21
|JT4A |K=32, r=1/2|(206,72)| 2| 4-FSK| 4.375| 17.5 | 0.50| 47.1 | -23 |JT4A |K=32, r=1/2|(206,72)| 2| 4-FSK| 4.375| 17.5 | 0.50| 47.1 | -23
|JT9A |K=32, r=1/2|(206,72)| 8| 9-FSK| 1.736| 15.6 | 0.19| 49.0 | -27 |JT9A |K=32, r=1/2|(206,72)| 8| 9-FSK| 1.736| 15.6 | 0.19| 49.0 | -26
|JT65A |Reed Solomon|(63,12) |64|65-FSK| 2.692| 177.6 | 0.50| 46.8 | -25 |JT65A |Reed Solomon|(63,12) |64|65-FSK| 2.692| 177.6 | 0.50| 46.8 | -25
|QRA64A|Q-ary Repeat Accumulate|(63,12) |64|64-FSK|1.736|111.1|0.25|48.4| -26 |QRA64A|Q-ary Repeat Accumulate|(63,12) |64|64-FSK|1.736|111.1|0.25|48.4| -26
| WSPR |K=32, r=1/2|(162,50)| 2| 4-FSK| 1.465| 5.9 | 0.50|110.6 | -31 | WSPR |K=32, r=1/2|(162,50)| 2| 4-FSK| 1.465| 5.9 | 0.50|110.6 | -31
|FST4W-120 |LDPC | (240,74)| 4| 4-GFSK| 1.46 | 5.9 | 0.25| 109.3 | -32.8
|FST4W-300 |LDPC | (240,74)| 4| 4-GFSK| 0.558 | 2.2 | 0.25| 286.7 | -36.8
|FST4W-900 |LDPC | (240,74)| 4| 4-GFSK| 0.180 | 0.72 | 0.25| 887.5 | -41.7
|FST4W-1800 |LDPC | (240,74)| 4| 4-GFSK| 0.089 | 0.36 | 0.25| 1792.0| -44.8
|=============================================================================== |===============================================================================
Submodes of JT4, JT9, JT65, and QRA64 offer wider tone spacings for Submodes of JT4, JT9, JT65, and QRA64 offer wider tone spacings for
@ -256,12 +293,10 @@ threshold sensitivities of the various submodes when spreading is
comparable to tone spacing. comparable to tone spacing.
[[SLOW_SUBMODES]] [[SLOW_SUBMODES]]
.Parameters of Slow Submodes .Parameters of Slow Submodes with Selectable Tone Spacings
[width="50%",cols="h,3*^",frame=topbot,options="header"] [width="50%",cols="h,3*^",frame=topbot,options="header"]
|===================================== |=====================================
|Mode |Tone Spacing |BW (Hz)|S/N (dB) |Mode |Tone Spacing |BW (Hz)|S/N (dB)
|FT4 |20.8333 | 83.3 |-17.5
|FT8 |6.25 | 50.0 |-21
|JT4A |4.375| 17.5 |-23 |JT4A |4.375| 17.5 |-23
|JT4B |8.75 | 30.6 |-22 |JT4B |8.75 | 30.6 |-22
|JT4C |17.5 | 56.9 |-21 |JT4C |17.5 | 56.9 |-21
@ -269,7 +304,7 @@ comparable to tone spacing.
|JT4E |78.75| 240.6 |-19 |JT4E |78.75| 240.6 |-19
|JT4F |157.5| 476.9 |-18 |JT4F |157.5| 476.9 |-18
|JT4G |315.0| 949.4 |-17 |JT4G |315.0| 949.4 |-17
|JT9A |1.736| 15.6 |-27 |JT9A |1.736| 15.6 |-26
|JT9B |3.472| 29.5 |-26 |JT9B |3.472| 29.5 |-26
|JT9C |6.944| 57.3 |-25 |JT9C |6.944| 57.3 |-25
|JT9D |13.889| 112.8 |-24 |JT9D |13.889| 112.8 |-24
@ -305,7 +340,7 @@ available character set is:
Transmissions consist of sequences of 24 symbols: a synchronizing Transmissions consist of sequences of 24 symbols: a synchronizing
pattern of four symbols at tone numbers 0, 1, 3, and 2, followed by pattern of four symbols at tone numbers 0, 1, 3, and 2, followed by
two symbols with tone number corresponding to (message length) and two symbols with tone number corresponding to (message length) and
(message length + 5), and finally 18 symbols conveying the user's (message length + 5), and, finally, 18 symbols conveying the user's
message, sent repeatedly character by character. The message always message, sent repeatedly character by character. The message always
starts with `@`, the beginning-of-message symbol, which is not starts with `@`, the beginning-of-message symbol, which is not
displayed to the user. The sync pattern and message-length indicator displayed to the user. The sync pattern and message-length indicator

View File

@ -1,7 +1,7 @@
// Status=review // Status=review
- SSB transceiver and antenna - SSB transceiver and antenna
- Computer running Windows 7 or later, Linux, or OS X - Computer running Windows 7 or later, macOS 10.13 or later, or Linux
- 1.5 GHz or faster CPU and 200 MB of available memory; faster - 1.5 GHz or faster CPU and 200 MB of available memory; faster
machines are better machines are better
- Monitor with at least 1024 x 780 resolution - Monitor with at least 1024 x 780 resolution

View File

@ -0,0 +1,23 @@
Do not confuse FST4 with FT4, which has a very different purpose!
FST4 is is designed for making 2-way QSOs on the LF and MF bands.
Operation with FST4 is similar to that with other _WSJT-X_ modes: most
on-screen controls, auto-sequencing, and other features behave in
familiar ways. However, operating conventions on the 2200 and 630 m
bands have made some additional user controls desirable. Spin boxes
labeled *F Low* and *F High* set lower and upper frequency limits used
by the FST4 decoder, and these limits are marked by dark green
angle-bracket symbols *< >* on the Wide Graph frequency scale:
image::FST4_Decoding_Limits.png[align="center"]
{empty} +
image::FST4_center.png[align="center"]
It's best to keep the decoding range fairly small, since QRM and
transmissions in other modes or sequence lengths will slow down the
decoding process (and of course will be undecodable). By checking
*Single decode* on the the *File | Settings | General* tab, you can
further limit the decoding range to the setting of *F Tol* on
either side of *Rx Freq*.

View File

@ -0,0 +1,18 @@
FST4W is used in the same way as WSPR, but FST4W has significant
advantages for use on the 2200 and 630 m bands. By default the
central *Rx Freq* is 1500 Hz and *F Tol* is 100 Hz, so the active
decoding range is 1400 to 1600 Hz. However, for added flexibility you
can select different center frequencies and *F Tol* values. We expect
that usage conventions will soon be established for FST4W activity on
2200 and 630 m.
A new drop-down control below *F Tol* offers a round-robin mode for
scheduling FST4W transmissions:
image::FST4W_RoundRobin.png[align="center"]
If three operators agree in advance to select the options *1/3*,
*2/3*, and *3/3*, for example, their FST4W transmissions will occur in
a fixed sequence with no two stations transmitting simultaneously.
Sequence 1 is the first sequence after 00:00 UTC. For WSPR-like
scheduling behavior, you should select *Random* with this control.

View File

@ -92,16 +92,14 @@ thing, both stations will have the required Doppler compensation.
Moreover, anyone else using this option will hear both of you Moreover, anyone else using this option will hear both of you
without the need for manual frequency changes. without the need for manual frequency changes.
- Select *On Dx Echo* when your QSO partner is not using automated - Select *On Dx Echo* when your QSO partner announces his/her transmit
Doppler tracking, and announces his/her transmit frequency and listening frequency and that they are listening on their own echo
on their own echo frequency. When clicked, this Doppler method will frequency. When clicked, this Doppler method will set your rig
set your rig frequency on receive to correct for the mutual Doppler frequency on receive to correct for the mutual Doppler shift. On
shift. On transmit, your rig frequency will be set so that your transmit, your rig frequency will be set so that your QSO partner will
QSO partner will receive you on the same frequency as their own echo receive you on the same frequency as they receive their own echo.
at the start of the QSO. As the QSO proceeds, your QSO partner will Sked frequency in this case is set to that announced by your QSO
receive you on this starting frequency so that they do not have to partner.
retune their receiver as the Doppler changes. Sked frequency in this
case is set to that announced by your QSO partner.
- Select *Call DX* after tuning the radio manually to find a station, - Select *Call DX* after tuning the radio manually to find a station,
with the Doppler mode initially set to *None*. You may be tuning the band with the Doppler mode initially set to *None*. You may be tuning the band

View File

@ -32,6 +32,9 @@ include::introduction.adoc[]
[[NEW_FEATURES]] [[NEW_FEATURES]]
include::new_features.adoc[] include::new_features.adoc[]
[[INTRO_SUBSECTIONS]]
include::intro_subsections.adoc[]
[[SYSREQ]] [[SYSREQ]]
== System Requirements == System Requirements
include::system-requirements.adoc[] include::system-requirements.adoc[]
@ -162,6 +165,14 @@ include::tutorial-example3.adoc[]
=== FT4 === FT4
include::tutorial-example4.adoc[] include::tutorial-example4.adoc[]
[[TUT_EX5]]
=== FST4
include::tutorial-example5.adoc[]
[[TUT_EX6]]
=== FST4W
include::tutorial-example6.adoc[]
[[MAKE_QSOS]] [[MAKE_QSOS]]
== Making QSOs == Making QSOs
include::make-qso.adoc[] include::make-qso.adoc[]

View File

@ -65,12 +65,18 @@ starts. This feature can be used to activate an automatic antenna
tuner (ATU) to tune a multi-band antenna to the newly selected band. tuner (ATU) to tune a multi-band antenna to the newly selected band.
- Depending on your station and antenna setup, band changes might - Depending on your station and antenna setup, band changes might
require other switching besides retuning your radio. To make this require other switching besides retuning your radio. To make this
possible in an automated way, whenever _WSJT-X_ executes a successful possible in an automated way, whenever _WSJT-X_ executes a successful
band-change command to a CAT-controlled radio, it looks for a file band-change command to a CAT-controlled radio, it looks for an
named `user_hardware.bat`, `user_hardware.cmd`, `user_hardware.exe`, executable file or script named `user_hardware`. This is done using
or `user_hardware` in the working directory. If one of these is found, `CMD /C user_hardware <band>` on Windows, or `/bin/sh -c user_hardware
_WSJT-X_ tries to execute the command <band>` on other platforms, where band is described below. On Windows
the first file with any extension listed on the PATHEXT environment
variable added to the file name root `user_hardware`, and found in the
directories listed on the PATH environment variable will be executed.
On other platforms, the first executable script, or program, named
`user_hardware` found in a directory listed on the PATH environment
variable will be executed.
user_hardware nnn user_hardware nnn
@ -78,6 +84,11 @@ _WSJT-X_ tries to execute the command
meters. You must write your own program, script, or batch file to do meters. You must write your own program, script, or batch file to do
the necessary switching at your station. the necessary switching at your station.
IMPORTANT: The use of the PATH (and PATHEXT on Windows) environment
variables is a new feature. To emulate previous behavior make sure
that the location of your user_hardware script or program is on the
PATH environment variable used by _WSJT-X_.
The following screen shot is an example of WSPR operation with The following screen shot is an example of WSPR operation with
band hopping enabled: band hopping enabled:

21
lib/77bit/call_to_c28.f90 Normal file
View File

@ -0,0 +1,21 @@
program call_to_c28
parameter (NTOKENS=2063592,MAX22=4194304)
character*6 call_std
character a1*37,a2*36,a3*10,a4*27
data a1/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
data a3/'0123456789'/
data a4/' ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
! call_std must be right adjusted, length 6
call_std=' K1ABC' !Redefine as needed
i1=index(a1,call_std(1:1))-1
i2=index(a2,call_std(2:2))-1
i3=index(a3,call_std(3:3))-1
i4=index(a4,call_std(4:4))-1
i5=index(a4,call_std(5:5))-1
i6=index(a4,call_std(6:6))-1
n28=NTOKENS + MAX22 + 36*10*27*27*27*i1 + 10*27*27*27*i2 + &
27*27*27*i3 + 27*27*i4 + 27*i5 + i6
write(*,1000) call_std,n28
1000 format('Callsign: ',a6,2x,'c28 as decimal integer:',i10)
end program call_to_c28

58
lib/77bit/free_text.f90 Normal file
View File

@ -0,0 +1,58 @@
program free_text
character*13 c13,w
character*71 f71
character*42 c
character*1 qa(10),qb(10)
data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?'/
c13='TNX BOB 73 GL' !Redefine as needed
call mp_short_init
qa=char(0)
w=adjustr(c13)
do i=1,13
j=index(c,w(i:i))-1
if(j.lt.0) j=0
call mp_short_mult(qb,qa(2:10),9,42) !qb(1:9)=42*qa(2:9)
call mp_short_add(qa,qb(2:10),9,j) !qa(1:9)=qb(2:9)+j
enddo
write(f71,1000) qa(2:10)
1000 format(b7.7,8b8.8)
write(*,1010) c13,f71
1010 format('Free text: ',a13/'f71: ',a71)
end program free_text
subroutine mp_short_ops(w,u)
! Multi-precision arithmetic with storage in character arrays.
character*1 w(*),u(*)
integer i,ireg,j,n,ir,iv,ii1,ii2
character*1 creg(4)
save ii1,ii2
equivalence (ireg,creg)
entry mp_short_init
ireg=256*ichar('2')+ichar('1')
do j=1,4
if (creg(j).eq.'1') ii1=j
if (creg(j).eq.'2') ii2=j
enddo
return
entry mp_short_add(w,u,n,iv)
ireg=256*iv
do j=n,1,-1
ireg=ichar(u(j))+ichar(creg(ii2))
w(j+1)=creg(ii1)
enddo
w(1)=creg(ii2)
return
entry mp_short_mult(w,u,n,iv)
ireg=0
do j=n,1,-1
ireg=ichar(u(j))*iv+ichar(creg(ii2))
w(j+1)=creg(ii1)
enddo
w(1)=creg(ii2)
return
return
end subroutine mp_short_ops

View File

@ -89,3 +89,8 @@ K1ABC/VE3 37
KA1ABC/VEX 37 KA1ABC/VEX 37
<PJ4/K1ABC> FK52UD <PJ4/K1ABC> FK52UD
<K1ABC/W4> FK52UD <K1ABC/W4> FK52UD
<W3CCX> <K1JT> 590001 FN20QI
<W3CCX> <K1JT/P> 590001 FN20QI
<W3CCX/P> <K1JT> 590001 FN20QI
<W3CCX/P> <K1JT/P> 590001 FN20QI
<W3CCX/QRP> <K1JT/QRO> 590001 FN20QI

View File

@ -0,0 +1,13 @@
program nonstd_to_c58
integer*8 n58
character*11 call_nonstd
character*38 c
data c/' 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ/'/
call_nonstd='PJ4/K1ABC' !Redifine as needed
n58=0
do i=1,11
n58=n58*38 + index(c,call_nonstd(i:i)) - 1
enddo
write(*,1000) call_nonstd,n58
1000 format('Callsign: ',a11,2x,'c58 as decimal integer:',i20)
end program nonstd_to_c58

View File

@ -2,8 +2,8 @@ module packjt77
! These variables are accessible from outside via "use packjt77": ! These variables are accessible from outside via "use packjt77":
parameter (MAXHASH=1000,MAXRECENT=10) parameter (MAXHASH=1000,MAXRECENT=10)
character (len=13), dimension(1:1024) :: calls10='' character (len=13), dimension(0:1023) :: calls10=''
character (len=13), dimension(1:4096) :: calls12='' character (len=13), dimension(0:4095) :: calls12=''
character (len=13), dimension(1:MAXHASH) :: calls22='' character (len=13), dimension(1:MAXHASH) :: calls22=''
character (len=13), dimension(1:MAXRECENT) :: recent_calls='' character (len=13), dimension(1:MAXRECENT) :: recent_calls=''
character (len=13) :: mycall13='' character (len=13) :: mycall13=''
@ -19,7 +19,7 @@ subroutine hash10(n10,c13)
character*13 c13 character*13 c13
c13='<...>' c13='<...>'
if(n10.lt.1 .or. n10.gt.1024) return if(n10.lt.0 .or. n10.gt.1023) return
if(len(trim(calls10(n10))).gt.0) then if(len(trim(calls10(n10))).gt.0) then
c13=calls10(n10) c13=calls10(n10)
c13='<'//trim(c13)//'>' c13='<'//trim(c13)//'>'
@ -33,7 +33,7 @@ subroutine hash12(n12,c13)
character*13 c13 character*13 c13
c13='<...>' c13='<...>'
if(n12.lt.1 .or. n12.gt.4096) return if(n12.lt.0 .or. n12.gt.4095) return
if(len(trim(calls12(n12))).gt.0) then if(len(trim(calls12(n12))).gt.0) then
c13=calls12(n12) c13=calls12(n12)
c13='<'//trim(c13)//'>' c13='<'//trim(c13)//'>'
@ -90,10 +90,10 @@ subroutine save_hash_call(c13,n10,n12,n22)
if(len(trim(cw)) .lt. 3) return if(len(trim(cw)) .lt. 3) return
n10=ihashcall(cw,10) n10=ihashcall(cw,10)
if(n10.ge.1 .and. n10 .le. 1024 .and. cw.ne.mycall13) calls10(n10)=cw if(n10.ge.0 .and. n10 .le. 1023 .and. cw.ne.mycall13) calls10(n10)=cw
n12=ihashcall(cw,12) n12=ihashcall(cw,12)
if(n12.ge.1 .and. n12 .le. 4096 .and. cw.ne.mycall13) calls12(n12)=cw if(n12.ge.0 .and. n12 .le. 4095 .and. cw.ne.mycall13) calls12(n12)=cw
n22=ihashcall(cw,22) n22=ihashcall(cw,22)
if(any(ihash22.eq.n22)) then ! If entry exists, make sure callsign is the most recently received one if(any(ihash22.eq.n22)) then ! If entry exists, make sure callsign is the most recently received one
@ -124,12 +124,14 @@ subroutine pack77(msg0,i3,n3,c77)
integer ntel(3) integer ntel(3)
msg=msg0 msg=msg0
if(i3.eq.0 .and. n3.eq.5) go to 5 i3_hint=i3
n3_hint=n3
i3=-1
n3=-1
if(i3_hint.eq.0 .and. n3_hint.eq.5) go to 5
! Convert msg to upper case; collapse multiple blanks; parse into words. ! Convert msg to upper case; collapse multiple blanks; parse into words.
call split77(msg,nwords,nw,w) call split77(msg,nwords,nw,w)
i3=-1
n3=-1
if(msg(1:3).eq.'CQ ' .or. msg(1:3).eq.'DE ' .or. msg(1:4).eq.'QRZ ') go to 100 if(msg(1:3).eq.'CQ ' .or. msg(1:3).eq.'DE ' .or. msg(1:4).eq.'QRZ ') go to 100
! Check 0.1 (DXpedition mode) ! Check 0.1 (DXpedition mode)
@ -160,7 +162,7 @@ subroutine pack77(msg0,i3,n3,c77)
go to 900 go to 900
endif endif
100 call pack77_06(nwords,w,i3,n3,c77) 100 call pack77_06(nwords,w,i3,n3,c77,i3_hint,n3_hint)
if(i3.ge.0) go to 900 if(i3.ge.0) go to 900
! Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P" ! Check Type 1 (Standard 77-bit message) or Type 2, with optional "/P"
@ -203,7 +205,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
integer ntel(3) integer ntel(3)
character*77 c77 character*77 c77
character*37 msg character*37 msg
character*13 call_1,call_2,call_3 character*13 call_1,call_2,call_3,call_1a
character*13 mycall13_0,dxcall13_0 character*13 mycall13_0,dxcall13_0
character*11 c11 character*11 c11
character*3 crpt,cntx,cpfx character*3 crpt,cntx,cpfx
@ -214,7 +216,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
character*38 c character*38 c
character*36 a2 character*36 a2
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22 integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
logical unpk28_success,unpk77_success logical unpk28_success,unpk77_success,unpkg4_success
logical dxcall13_set,mycall13_set logical dxcall13_set,mycall13_set
data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/,nzzz/46656/ data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/,nzzz/46656/
@ -276,11 +278,16 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
read(c77(72:77),'(2b3)') n3,i3 read(c77(72:77),'(2b3)') n3,i3
msg=repeat(' ',37) msg=repeat(' ',37)
if(i3.eq.0 .and. n3.eq.0) then if(i3.eq.0 .and. n3.eq.0) then
! 0.0 Free text ! 0.0 Free text
call unpacktext77(c77(1:71),msg(1:13)) call unpacktext77(c77(1:71),msg(1:13))
msg(14:)=' ' msg(14:)=' '
msg=adjustl(msg) msg=adjustl(msg)
if(msg(1:1).eq.' ') then
unpk77_success=.false.
return
endif
else if(i3.eq.0 .and. n3.eq.1) then else if(i3.eq.0 .and. n3.eq.1) then
! 0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -11 28 28 10 5 71 DXpedition Mode ! 0.1 K1ABC RR73; W9XYZ <KH1/KH7Z> -11 28 28 10 5 71 DXpedition Mode
@ -346,38 +353,32 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
msg=adjustl(msg) msg=adjustl(msg)
else if(i3.eq.0 .and. n3.eq.6) then else if(i3.eq.0 .and. n3.eq.6) then
read(c77(50:50),'(b1)') j2a read(c77(49:50),'(2b1)') j2a,j2b
j2b=0 itype=2
if(j2a.eq.0) read(c77(49:49),'(b1)') j2b if(j2b.eq.0 .and. j2a.eq.0) itype=1
j2=2*j2a+j2b if(j2b.eq.0 .and. j2a.eq.1) itype=3
if(j2.eq.0) then if(itype.eq.1) then
! WSPR Type 1 ! WSPR Type 1
read(c77,2010) n28,igrid4,idbm read(c77,2010) n28,igrid4,idbm
2010 format(b28.28,b15.15,b5.5) 2010 format(b28.28,b15.15,b5.5)
idbm=nint(idbm*10.0/3.0) idbm=nint(idbm*10.0/3.0)
call unpack28(n28,call_1,unpk28_success) call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false. if(.not.unpk28_success) unpk77_success=.false.
call to_grid4(igrid4,grid4) call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
write(crpt,'(i3)') idbm write(crpt,'(i3)') idbm
msg=trim(call_1)//' '//grid4//' '//trim(adjustl(crpt)) msg=trim(call_1)//' '//grid4//' '//trim(adjustl(crpt))
if (unpk77_success) call save_hash_call(call_1,n10,n12,n22) !### Is this OK here? ###
else if(j2.eq.1) then else if(itype.eq.2) then
! WSPR Type 2 ! WSPR Type 2
read(c77,2030) n28,igrid6
2030 format(b22.22,b25.25)
call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false.
call to_grid6(igrid6,grid6)
msg=trim(call_1)//' '//grid6
else if(j2.eq.2) then
! WSPR Type 3
read(c77,2020) n28,npfx,idbm read(c77,2020) n28,npfx,idbm
2020 format(b28.28,b16.16,b5.5) 2020 format(b28.28,b16.16,b5.5)
idbm=nint(idbm*10.0/3.0) idbm=nint(idbm*10.0/3.0)
call unpack28(n28,call_1,unpk28_success) call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false. if(.not.unpk28_success) unpk77_success=.false.
write(crpt,'(i3)') idbm write(crpt,'(i3)') idbm
cpfx=' '
if(npfx.lt.nzzz) then if(npfx.lt.nzzz) then
! Prefix ! Prefix
do i=3,1,-1 do i=3,1,-1
@ -387,10 +388,11 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
if(npfx.eq.0) exit if(npfx.eq.0) exit
enddo enddo
msg=trim(adjustl(cpfx))//'/'//trim(call_1)//' '//trim(adjustl(crpt)) msg=trim(adjustl(cpfx))//'/'//trim(call_1)//' '//trim(adjustl(crpt))
call_1a=trim(adjustl(cpfx))//'/'//trim(call_1)
call save_hash_call(call_1a,n10,n12,n22) !### Is this OK here? ###
else else
! Suffix ! Suffix
npfx=npfx-nzzz npfx=npfx-nzzz
cpfx=' '
if(npfx.le.35) then if(npfx.le.35) then
cpfx(1:1)=a2(npfx+1:npfx+1) cpfx(1:1)=a2(npfx+1:npfx+1)
else if(npfx.gt.35 .and. npfx.le.1295) then else if(npfx.gt.35 .and. npfx.le.1295) then
@ -405,10 +407,25 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
return return
endif endif
msg=trim(call_1)//'/'//trim(adjustl(cpfx))//' '//trim(adjustl(crpt)) msg=trim(call_1)//'/'//trim(adjustl(cpfx))//' '//trim(adjustl(crpt))
call_1a=trim(call_1)//'/'//trim(adjustl(cpfx))
call save_hash_call(call_1a,n10,n12,n22) !### Is this OK here? ###
endif endif
else if(itype.eq.3) then
! WSPR Type 3
read(c77,2030) n22,igrid6
2030 format(b22.22,b25.25)
n28=n22+2063592
call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false.
call to_grid(igrid6,grid6,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
msg=trim(call_1)//' '//grid6
endif endif
else if(i3.eq.0 .and. n3.gt.6) then
unpk77_success=.false.
else if(i3.eq.1 .or. i3.eq.2) then else if(i3.eq.1 .or. i3.eq.2) then
! Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest) ! Type 1 (standard message) or Type 2 ("/P" form for EU VHF contest)
read(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3 read(c77,1000) n28a,ipa,n28b,ipb,ir,igrid4,i3
@ -435,7 +452,8 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
if(i.ge.4) call add_call_to_recent_calls(call_2) if(i.ge.4) call add_call_to_recent_calls(call_2)
endif endif
if(igrid4.le.MAXGRID4) then if(igrid4.le.MAXGRID4) then
call to_grid4(igrid4,grid4) call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//grid4 if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//grid4
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//grid4 if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//grid4
if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false. if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false.
@ -446,7 +464,9 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
if(irpt.eq.3) msg=trim(call_1)//' '//trim(call_2)//' RR73' if(irpt.eq.3) msg=trim(call_1)//' '//trim(call_2)//' RR73'
if(irpt.eq.4) msg=trim(call_1)//' '//trim(call_2)//' 73' if(irpt.eq.4) msg=trim(call_1)//' '//trim(call_2)//' 73'
if(irpt.ge.5) then if(irpt.ge.5) then
write(crpt,'(i3.2)') irpt-35 isnr=irpt-35
if(isnr.gt.50) isnr=isnr-101
write(crpt,'(i3.2)') isnr
if(crpt(1:1).eq.' ') crpt(1:1)='+' if(crpt(1:1).eq.' ') crpt(1:1)='+'
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//crpt if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//crpt
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R'//crpt if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R'//crpt
@ -550,7 +570,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
nrs=52+irpt nrs=52+irpt
write(cexch,1022) nrs,iserial write(cexch,1022) nrs,iserial
1022 format(i2,i4.4) 1022 format(i2,i4.4)
call to_grid6(igrid6,grid6) call to_grid6(igrid6,grid6,unpk77_success)
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//cexch//' '//grid6 if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//cexch//' '//grid6
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//cexch//' '//grid6 if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//cexch//' '//grid6
@ -897,7 +917,7 @@ subroutine pack77_03(nwords,w,i3,n3,c77)
ntx=-1 ntx=-1
j=len(trim(w(nwords-1)))-1 j=len(trim(w(nwords-1)))-1
read(w(nwords-1)(1:j),*,err=1) ntx !Number of transmitters read(w(nwords-1)(1:j),*,err=1,end=1) ntx !Number of transmitters
1 if(ntx.lt.1 .or. ntx.gt.32) return 1 if(ntx.lt.1 .or. ntx.gt.32) return
nclass=ichar(w(nwords-1)(j+1:j+1))-ichar('A') nclass=ichar(w(nwords-1)(j+1:j+1))-ichar('A')
@ -925,7 +945,7 @@ subroutine pack77_03(nwords,w,i3,n3,c77)
end subroutine pack77_03 end subroutine pack77_03
subroutine pack77_06(nwords,w,i3,n3,c77) subroutine pack77_06(nwords,w,i3,n3,c77,i3_hint,n3_hint)
character*13 w(19) character*13 w(19)
character*77 c77 character*77 c77
@ -942,13 +962,14 @@ subroutine pack77_06(nwords,w,i3,n3,c77)
grid4(3:3).ge.'0' .and. grid4(3:3).le.'9' .and. & grid4(3:3).ge.'0' .and. grid4(3:3).le.'9' .and. &
grid4(4:4).ge.'0' .and. grid4(4:4).le.'9' grid4(4:4).ge.'0' .and. grid4(4:4).le.'9'
is_grid6(grid6)=len(trim(grid6)).eq.6 .and. & is_grid6(grid6)=(len(trim(grid6)).eq.6.or.len(trim(grid6)).eq.4).and. &
grid6(1:1).ge.'A' .and. grid6(1:1).le.'R' .and. & grid6(1:1).ge.'A' .and. grid6(1:1).le.'R' .and. &
grid6(2:2).ge.'A' .and. grid6(2:2).le.'R' .and. & grid6(2:2).ge.'A' .and. grid6(2:2).le.'R' .and. &
grid6(3:3).ge.'0' .and. grid6(3:3).le.'9' .and. & grid6(3:3).ge.'0' .and. grid6(3:3).le.'9' .and. &
grid6(4:4).ge.'0' .and. grid6(4:4).le.'9' .and. & grid6(4:4).ge.'0' .and. grid6(4:4).le.'9' .and. &
grid6(5:5).ge.'A' .and. grid6(5:5).le.'X' .and. & (len(trim(grid6)).eq.4.or. &
grid6(6:6).ge.'A' .and. grid6(6:6).le.'X' (grid6(5:5).ge.'A' .and. grid6(5:5).le.'X' .and. &
grid6(6:6).ge.'A' .and. grid6(6:6).le.'X'))
is_digit(c)=c.ge.'0' .and. c.le.'9' is_digit(c)=c.ge.'0' .and. c.le.'9'
@ -1020,22 +1041,32 @@ subroutine pack77_06(nwords,w,i3,n3,c77)
go to 900 go to 900
endif endif
if(nwords.eq.2 .and. m1.ge.5 .and. m1.le.12 .and. m2.le.6) then if(i3_hint.eq.0.and.n3_hint.eq.6.and.nwords.eq.2 .and. m1.ge.5 &
.and. m1.le.12 .and. m2.le.6) then
! WSPR Type 3 ! WSPR Type 3
!n3_hint=6 and i3_hint=0 is a hint that the caller wanted a
!50-bit encoding rather than the possible alternative n3=4 77-bit
!encoding
if(index(w(1),'<').lt.1 .or. index(w(1),'>').lt.1) go to 900 if(index(w(1),'<').lt.1 .or. index(w(1),'>').lt.1) go to 900
grid6=w(2)(1:6) grid6=w(2)(1:6)
if(.not.is_grid6(grid6)) go to 900 if(.not.is_grid6(grid6)) go to 900
i3=0 i3=0
n3=6 n3=6
call pack28(w(1),n28) call pack28(w(1),n28)
k1=(ichar(grid6(1:1))-ichar('A'))*18*10*10*24*24 n22=n28-2063592
k2=(ichar(grid6(2:2))-ichar('A'))*10*10*24*24 k1=(ichar(grid6(1:1))-ichar('A'))*18*10*10*25*25
k3=(ichar(grid6(3:3))-ichar('0'))*10*24*24 k2=(ichar(grid6(2:2))-ichar('A'))*10*10*25*25
k4=(ichar(grid6(4:4))-ichar('0'))*24*24 k3=(ichar(grid6(3:3))-ichar('0'))*10*25*25
k5=(ichar(grid6(5:5))-ichar('A'))*24 k4=(ichar(grid6(4:4))-ichar('0'))*25*25
k6=(ichar(grid6(6:6))-ichar('A')) if (grid6(5:6).eq.' ') then
igrid6=k1+k2+k3+k4+k5+k6 igrid6=k1+k2+k3+k4+24*25+24
write(c77,1030) n28,igrid6,2,0,n3,i3 else
k5=(ichar(grid6(5:5))-ichar('A'))*25
k6=(ichar(grid6(6:6))-ichar('A'))
igrid6=k1+k2+k3+k4+k5+k6
endif
write(c77,1030) n22,igrid6,2,0,n3,i3
1030 format(b22.22,b25.25,b3.3,b21.21,2b3.3) 1030 format(b22.22,b25.25,b3.3,b21.21,2b3.3)
endif endif
@ -1083,10 +1114,12 @@ subroutine pack77_1(nwords,w,i3,n3,c77)
if(c1.eq.'+' .or. c1.eq.'-') then if(c1.eq.'+' .or. c1.eq.'-') then
ir=0 ir=0
read(w(nwords),*,err=900) irpt read(w(nwords),*,err=900) irpt
if(irpt.ge.-50 .and. irpt.le.-31) irpt=irpt+101
irpt=irpt+35 irpt=irpt+35
else if(c2.eq.'R+' .or. c2.eq.'R-') then else if(c2.eq.'R+' .or. c2.eq.'R-') then
ir=1 ir=1
read(w(nwords)(2:),*) irpt read(w(nwords)(2:),*) irpt
if(irpt.ge.-50 .and. irpt.le.-31) irpt=irpt+101
irpt=irpt+35 irpt=irpt+35
else if(trim(w(nwords)).eq.'RRR') then else if(trim(w(nwords)).eq.'RRR') then
ir=0 ir=0
@ -1467,44 +1500,96 @@ subroutine add_call_to_recent_calls(callsign)
return return
end subroutine add_call_to_recent_calls end subroutine add_call_to_recent_calls
subroutine to_grid4(n,grid4) subroutine to_grid4(n,grid4,ok)
character*4 grid4 character*4 grid4
logical ok
ok=.false.
j1=n/(18*10*10) j1=n/(18*10*10)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10 n=n-j1*18*10*10
j2=n/(10*10) j2=n/(10*10)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10 n=n-j2*10*10
j3=n/10 j3=n/10
if (j3.lt.0.or.j3.gt.9) goto 900
j4=n-j3*10 j4=n-j3*10
if (j4.lt.0.or.j4.gt.9) goto 900
grid4(1:1)=char(j1+ichar('A')) grid4(1:1)=char(j1+ichar('A'))
grid4(2:2)=char(j2+ichar('A')) grid4(2:2)=char(j2+ichar('A'))
grid4(3:3)=char(j3+ichar('0')) grid4(3:3)=char(j3+ichar('0'))
grid4(4:4)=char(j4+ichar('0')) grid4(4:4)=char(j4+ichar('0'))
ok=.true.
return
900 return
end subroutine to_grid4 end subroutine to_grid4
subroutine to_grid6(n,grid6) subroutine to_grid6(n,grid6,ok)
character*6 grid6 character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*24*24) j1=n/(18*10*10*24*24)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10*24*24 n=n-j1*18*10*10*24*24
j2=n/(10*10*24*24) j2=n/(10*10*24*24)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10*24*24 n=n-j2*10*10*24*24
j3=n/(10*24*24) j3=n/(10*24*24)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*24*24 n=n-j3*10*24*24
j4=n/(24*24) j4=n/(24*24)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*24*24 n=n-j4*24*24
j5=n/24 j5=n/24
if (j5.lt.0.or.j5.gt.23) goto 900
j6=n-j5*24 j6=n-j5*24
if (j6.lt.0.or.j6.gt.23) goto 900
grid6(1:1)=char(j1+ichar('A')) grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A')) grid6(2:2)=char(j2+ichar('A'))
grid6(3:3)=char(j3+ichar('0')) grid6(3:3)=char(j3+ichar('0'))
grid6(4:4)=char(j4+ichar('0')) grid6(4:4)=char(j4+ichar('0'))
grid6(5:5)=char(j5+ichar('A')) grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A')) grid6(6:6)=char(j6+ichar('A'))
ok=.true.
return 900 return
end subroutine to_grid6 end subroutine to_grid6
subroutine to_grid(n,grid6,ok)
! 4-, or 6-character grid
character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*25*25)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10*25*25
j2=n/(10*10*25*25)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10*25*25
j3=n/(10*25*25)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*25*25
j4=n/(25*25)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*25*25
j5=n/25
if (j5.lt.0.or.j5.gt.24) goto 900
j6=n-j5*25
if (j6.lt.0.or.j6.gt.24) goto 900
grid6=''
grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A'))
grid6(3:3)=char(j3+ichar('0'))
grid6(4:4)=char(j4+ichar('0'))
if (j5.ne.24.or.j6.ne.24) then
grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A'))
endif
ok=.true.
900 return
end subroutine to_grid
end module packjt77 end module packjt77

441
lib/C_interface_module.f90 Normal file
View File

@ -0,0 +1,441 @@
! FILE: c_interface_module.f90
! PURPOSE: Supplement ISO-C-Binding to provide type aliases and interfaces
! to common ISO-C string functions to aid working with strings.
! AUTHOR: Joseph M. Krahn
! STATUS: Still in development. Reasonably complete, but somewhat limited testing.
!
! The idea is to provide type aliases for all ISO-C types, so that the
! Fortran interface code more explicitly defines the actual C interface.
! This should be updated to support F2008 variable-length allocatable
! strings.
!
! Entity names all have the "C_" prefix, as with ISO-C-Binding, with a
! few exceptions.
!
! Sourced from: http://fortranwiki.org/fortran/show/c_interface_module
!
! One FORALL statement reverted to a DO loop to avoid a gfortran 4.9.2 ICE
!
module C_interface_module
use, intrinsic :: ISO_C_Binding, &
! C type aliases for pointer derived types:
C_ptr => C_ptr , &
C_char_ptr => C_ptr, &
C_const_char_ptr => C_ptr, &
C_void_ptr => C_ptr, &
C_const_void_ptr => C_ptr
implicit none
public
!----------------------------------------------------------------------------
! C type aliases for intrinsic type KIND parameters:
! NOTE: a C enum may not always be a standard C int
integer, parameter :: C_enum = C_int
! Defining off_t is difficult, because it may depend on "LARGEFILE" selection.
! integer, parameter :: C_off_t = ??
! C string terminator alais using the 3-letter ASCII name.
! The C_ prefix is not used because it is just an ASCII character.
character(len=1,kind=C_char), parameter :: NUL = C_NULL_char
! NOTE: In C, "char" is distinct from "signed char", unlike integers.
! The plain "char" type is specific for text/string values, whereas
! "signed char" should indicate 1-byte integer data.
!
! Most ISO-C systems have wide chars "wchar_t", but Fortran compilers
! have limited support for different character kinds. UTF encoding
! adds more complexity. This should be updated as Fortran compilers
! include support for more character types.
!
! Fortran does not (yet) support unsigned types.
integer, parameter :: &
C_unsigned = C_int, &
C_unsigned_short = C_short, &
C_unsigned_long = C_long, &
C_unsigned_long_long = C_long_long, &
C_unsigned_char = C_signed_char, &
C_ssize_t = C_size_t, &
C_uint8_t = C_int8_t, &
C_uint16_t = C_int16_t, &
C_uint32_t = C_int32_t, &
C_uint64_t = C_int64_t, &
C_uint_least8_t = C_int_least8_t, &
C_uint_least16_t = C_int_least16_t, &
C_uint_least32_t = C_int_least32_t, &
C_uint_least64_t = C_int_least64_t, &
C_uint_fast8_t = C_int_fast8_t, &
C_uint_fast16_t = C_int_fast16_t, &
C_uint_fast32_t = C_int_fast32_t, &
C_uint_fast64_t = C_int_fast64_t, &
C_uintmax_t = C_intmax_t
! Note: ptrdiff_t cannot be reliably defined from other types.
! When practical, it is larger than a pointer because it benefits
! from the full unsigned range in both positive and negative directions.
! Integer versions including 'int', where the 'int' is optional:
integer, parameter :: &
C_short_int = C_short, &
C_long_int = C_long, &
C_long_long_int = C_long_long, &
C_unsigned_int = C_unsigned, &
C_unsigned_short_int = C_short, &
C_unsigned_long_int = C_long, &
C_unsigned_long_long_int = C_long_long
interface C_F_string
module procedure C_F_string_ptr
module procedure C_F_string_chars
end interface C_F_string
interface F_C_string
module procedure F_C_string_ptr
module procedure F_C_string_chars
end interface F_C_string
!=======================================================================
! Some useful ISO C library string functions from <string.h>
! These are based on GCC header sections marked as NAMESPACE_STD
interface
! Copy N bytes of SRC to DEST, no aliasing or overlapping allowed.
! extern void *memcpy (void *dest, const void *src, size_t n);
function C_memcpy(dest, src, n) result(result) bind(C,name="memcpy")
import C_void_ptr, C_size_t
type(C_void_ptr) :: result
type(C_void_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_void_ptr), value, intent(in) :: src ! target=intent(in)
integer(C_size_t), value, intent(in) :: n
end function C_memcpy
! Copy N bytes of SRC to DEST, guaranteeing correct behavior for overlapping strings.
!extern void *memmove (void *dest, const void *src, size_t n)
function C_memmove(dest, src, n) result(result) bind(C,name="memmove")
import C_void_ptr, C_size_t
type(C_void_ptr) :: result
type(C_void_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_void_ptr), value, intent(in) :: src
integer(C_size_t), value, intent(in) :: n
end function C_memmove
! Set N bytes of S to C.
!extern void *memset (void *s, int c, size_t n)
function C_memset(s, c, n) result(result) bind(C,name="memset")
import C_void_ptr, C_int, C_size_t
type(C_void_ptr) :: result
type(C_void_ptr), value, intent(in) :: s ! target=intent(out)
integer(C_int), value, intent(in) :: c
integer(C_size_t), value, intent(in) :: n
end function C_memset
! Compare N bytes of S1 and S2.
!extern int memcmp (const void *s1, const void *s2, size_t n)
pure function C_memcmp(s1, s2, n) result(result) bind(C,name="memcmp")
import C_int, C_void_ptr, C_size_t
integer(C_int) :: result
type(C_void_ptr), value, intent(in) :: s1
type(C_void_ptr), value, intent(in) :: s2
integer(C_size_t), value, intent(in) :: n
end function C_memcmp
! Search N bytes of S for C.
!extern void *memchr (const void *s, int c, size_t n)
pure function C_memchr(s, c, n) result(result) bind(C,name="memchr")
import C_void_ptr, C_int, C_size_t
type(C_void_ptr) :: result
type(C_void_ptr), value, intent(in) :: s
integer(C_int), value, intent(in) :: c
integer(C_size_t), value, intent(in) :: n
end function C_memchr
! Copy SRC to DEST.
!extern char *strcpy (char *dest, const char *src)
function C_strcpy(dest, src) result(result) bind(C,name="strcpy")
import C_char_ptr, C_size_t
type(C_char_ptr) :: result
type(C_char_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_char_ptr), value, intent(in) :: src
end function C_strcpy
! Copy no more than N characters of SRC to DEST.
!extern char *strncpy (char *dest, const char *src, size_t n)
function C_strncpy(dest, src, n) result(result) bind(C,name="strncpy")
import C_char_ptr, C_size_t
type(C_char_ptr) :: result
type(C_char_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_char_ptr), value, intent(in) :: src
integer(C_size_t), value, intent(in) :: n
end function C_strncpy
! Append SRC onto DEST.
!extern char *strcat (char *dest, const char *src)
function C_strcat(dest, src) result(result) bind(C,name="strcat")
import C_char_ptr, C_size_t
type(C_char_ptr) :: result
type(C_char_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_char_ptr), value, intent(in) :: src
end function C_strcat
! Append no more than N characters from SRC onto DEST.
!extern char *strncat (char *dest, const char *src, size_t n)
function C_strncat(dest, src, n) result(result) bind(C,name="strncat")
import C_char_ptr, C_size_t
type(C_char_ptr) :: result
type(C_char_ptr), value, intent(in) :: dest ! target=intent(out)
type(C_char_ptr), value, intent(in) :: src
integer(C_size_t), value, intent(in) :: n
end function C_strncat
! Compare S1 and S2.
!extern int strcmp (const char *s1, const char *s2)
pure function C_strcmp(s1, s2) result(result) bind(C,name="strcmp")
import C_int, C_char_ptr, C_size_t
integer(C_int) :: result
type(C_char_ptr), value, intent(in) :: s1
type(C_char_ptr), value, intent(in) :: s2
end function C_strcmp
! Compare N characters of S1 and S2.
!extern int strncmp (const char *s1, const char *s2, size_t n)
pure function C_strncmp(s1, s2, n) result(result) bind(C,name="strncmp")
import C_int, C_char_ptr, C_size_t
integer(C_int) :: result
type(C_char_ptr), value, intent(in) :: s1
type(C_char_ptr), value, intent(in) :: s2
integer(C_size_t), value, intent(in) :: n
end function C_strncmp
! Return the length of S.
!extern size_t strlen (const char *s)
pure function C_strlen(s) result(result) bind(C,name="strlen")
import C_char_ptr, C_size_t
integer(C_size_t) :: result
type(C_char_ptr), value, intent(in) :: s !character(len=*), intent(in)
end function C_strlen
end interface
! End of <string.h>
!=========================================================================
! Standard ISO-C malloc routines:
interface
! void *calloc(size_t nmemb, size_t size);
type(C_void_ptr) function C_calloc(nmemb, size) bind(C,name="calloc")
import C_void_ptr, C_size_t
integer(C_size_t), value, intent(in) :: nmemb, size
end function C_calloc
! void *malloc(size_t size);
type(C_void_ptr) function C_malloc(size) bind(C,name="malloc")
import C_void_ptr, C_size_t
integer(C_size_t), value, intent(in) :: size
end function C_malloc
! void free(void *ptr);
subroutine C_free(ptr) bind(C,name="free")
import C_void_ptr
type(C_void_ptr), value, intent(in) :: ptr
end subroutine C_free
! void *realloc(void *ptr, size_t size);
type(C_void_ptr) function C_realloc(ptr,size) bind(C,name="realloc")
import C_void_ptr, C_size_t
type(C_void_ptr), value, intent(in) :: ptr
integer(C_size_t), value, intent(in) :: size
end function C_realloc
end interface
interface assignment(=)
module procedure F_string_assign_C_string
end interface assignment(=)
!==========================================================================
contains
! HACK: For some reason, C_associated was not defined as pure.
pure logical function C_associated_pure(ptr) result(associated)
type(C_ptr), intent(in) :: ptr
integer(C_intptr_t) :: iptr
iptr = transfer(ptr,iptr)
associated = (iptr /= 0)
end function C_associated_pure
! Set a fixed-length Fortran string to the value of a C string.
subroutine F_string_assign_C_string(F_string, C_string)
character(len=*), intent(out) :: F_string
type(C_ptr), intent(in) :: C_string
character(len=1,kind=C_char), pointer :: p_chars(:)
integer :: i
if (.not. C_associated(C_string) ) then
F_string = ' '
else
call C_F_pointer(C_string,p_chars,[huge(0)])
i=1
do while(p_chars(i)/=NUL .and. i<=len(F_string))
F_string(i:i) = p_chars(i)
i=i+1
end do
if (i<len(F_string)) F_string(i:) = ' '
end if
end subroutine F_string_assign_C_string
! Copy a C string, passed by pointer, to a Fortran string.
! If the C pointer is NULL, the Fortran string is blanked.
! C_string must be NUL terminated, or at least as long as F_string.
! If C_string is longer, it is truncated. Otherwise, F_string is
! blank-padded at the end.
subroutine C_F_string_ptr(C_string, F_string)
type(C_ptr), intent(in) :: C_string
character(len=*), intent(out) :: F_string
character(len=1,kind=C_char), dimension(:), pointer :: p_chars
integer :: i
if (.not. C_associated(C_string)) then
F_string = ' '
else
call C_F_pointer(C_string,p_chars,[huge(0)])
i=1
do while(p_chars(i)/=NUL .and. i<=len(F_string))
F_string(i:i) = p_chars(i)
i=i+1
end do
if (i<len(F_string)) F_string(i:) = ' '
end if
end subroutine C_F_string_ptr
! Copy a C string, passed as a char-array reference, to a Fortran string.
subroutine C_F_string_chars(C_string, F_string)
character(len=1,kind=C_char), intent(in) :: C_string(*)
character(len=*), intent(out) :: F_string
integer :: i
i=1
do while(C_string(i)/=NUL .and. i<=len(F_string))
F_string(i:i) = C_string(i)
i=i+1
end do
if (i<len(F_string)) F_string(i:) = ' '
end subroutine C_F_string_chars
! Copy a Fortran string to an allocated C string pointer.
! If the C pointer is NULL, no action is taken. (Maybe auto allocate via libc call?)
! If the length is not passed, the C string must be at least: len(F_string)+1
! If the length is passed and F_string is too long, it is truncated.
subroutine F_C_string_ptr(F_string, C_string, C_string_len)
character(len=*), intent(in) :: F_string
type(C_ptr), intent(in) :: C_string ! target = intent(out)
integer, intent(in), optional :: C_string_len ! Max string length,
! INCLUDING THE TERMINAL NUL
character(len=1,kind=C_char), dimension(:), pointer :: p_chars
integer :: i, strlen
strlen = len(F_string)
if (present(C_string_len)) then
if (C_string_len <= 0) return
strlen = min(strlen,C_string_len)
end if
if (.not. C_associated(C_string)) then
return
end if
call C_F_pointer(C_string,p_chars,[strlen+1])
forall (i=1:strlen)
p_chars(i) = F_string(i:i)
end forall
p_chars(strlen+1) = NUL
end subroutine F_C_string_ptr
pure function C_strlen_safe(s) result(length)
integer(C_size_t) :: length
type(C_char_ptr), value, intent(in) :: s
if (.not. C_associated_pure(s)) then
length = 0
else
length = C_strlen(s)
end if
end function C_strlen_safe
function C_string_value(C_string) result(F_string)
type(C_ptr), intent(in) :: C_string
character(len=C_strlen_safe(C_string)) :: F_string
character(len=1,kind=C_char), dimension(:), pointer :: p_chars
integer :: i, length
length = len(F_string)
if (length/=0) then
call C_F_pointer(C_string,p_chars,[length])
forall (i=1:length)
F_string(i:i) = p_chars(i)
end forall
end if
end function C_string_value
! Copy a Fortran string to a C string passed by char-array reference.
! If the length is not passed, the C string must be at least: len(F_string)+1
! If the length is passed and F_string is too long, it is truncated.
subroutine F_C_string_chars(F_string, C_string, C_string_len)
character(len=*), intent(in) :: F_string
character(len=1,kind=C_char), dimension(*), intent(out) :: C_string
integer, intent(in), optional :: C_string_len ! Max string length,
! INCLUDING THE TERMINAL NUL
integer :: i, strlen
strlen = len(F_string)
if (present(C_string_len)) then
if (C_string_len <= 0) return
strlen = min(strlen,C_string_len)
end if
forall (i=1:strlen)
C_string(i) = F_string(i:i)
end forall
C_string(strlen+1) = NUL
end subroutine F_C_string_chars
! NOTE: Strings allocated here must be freed by the
! C library, such as via C_free() or C_string_free(),
type(C_ptr) function F_C_string_dup(F_string,length) result(C_string)
character(len=*), intent(in) :: F_string
integer, intent(in), optional :: length
character(len=1,kind=C_char), pointer :: C_string_ptr(:)
integer :: i
integer(C_size_t) :: strlen
if (present(length)) then
strlen = length
else
strlen = len(F_string)
end if
if (strlen <= 0) then
C_string = C_NULL_ptr
else
C_string = C_malloc(strlen+1)
if (C_associated(C_string)) then
call C_F_pointer(C_string,C_string_ptr,[strlen+1])
forall (i=1:strlen)
C_string_ptr(i) = F_string(i:i)
end forall
C_string_ptr(strlen+1) = NUL
end if
end if
end function F_C_string_dup
! NOTE: Strings allocated here must be freed by the
! C library, such as via C_free() or C_string_free(),
type(C_ptr) function C_string_alloc(length) result(C_string)
integer(C_size_t), intent(in) :: length
character(len=1,kind=C_char), pointer :: C_charptr
C_string = C_malloc(length+1)
if (C_associated(C_string)) then
call C_F_pointer(C_string,C_charptr)
C_charptr = NUL
end if
end function C_string_alloc
subroutine C_string_free(string)
type(C_ptr), intent(inout) :: string
if (C_associated(string)) then
call C_free(string)
string = C_NULL_ptr
end if
end subroutine C_string_free
end module C_interface_module

View File

@ -1,5 +1,4 @@
module astro_module module astro_module
use, intrinsic :: iso_c_binding, only : c_int, c_double, c_bool, c_char, c_ptr, c_size_t, c_f_pointer
implicit none implicit none
private private
@ -7,50 +6,37 @@ module astro_module
contains contains
subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid_cp,mygrid_len, & subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid_cp, &
hisgrid_cp,hisgrid_len,AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8, & hisgrid_cp,AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8, &
ntsky,ndop,ndop00,RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1, & ntsky,ndop,ndop00,RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1, &
width2,bTx,AzElFileName_cp,AzElFileName_len,jpleph_cp,jpleph_len) & width2,bTx,AzElFileName_cp,jpleph_file_name_cp) &
bind (C, name="astrosub") bind (C, name="astrosub")
integer, parameter :: dp = selected_real_kind(15, 50) use :: types, only: dp
use :: C_interface_module, only: C_int, C_double, C_bool, C_ptr, C_string_value, assignment(=)
integer(c_int), intent(in), value :: nyear, month, nday integer(C_int), intent(in), value :: nyear, month, nday
real(c_double), intent(in), value :: uth8, freq8 real(C_double), intent(in), value :: uth8, freq8
real(c_double), intent(out) :: AzSun8, ElSun8, AzMoon8, ElMoon8, AzMoonB8, & real(C_double), intent(out) :: AzSun8, ElSun8, AzMoon8, ElMoon8, AzMoonB8, &
ElMoonB8, Ramoon8, DecMoon8, Dgrd8, poloffset8, xnr8, techo8, width1, & ElMoonB8, Ramoon8, DecMoon8, Dgrd8, poloffset8, xnr8, techo8, width1, &
width2 width2
integer(c_int), intent(out) :: ntsky, ndop, ndop00 integer(C_int), intent(out) :: ntsky, ndop, ndop00
logical(c_bool), intent(in), value :: bTx logical(C_bool), intent(in), value :: bTx
type(c_ptr), intent(in), value :: mygrid_cp, hisgrid_cp, AzElFileName_cp, jpleph_cp type(C_ptr), value, intent(in) :: mygrid_cp, hisgrid_cp, AzElFileName_cp, &
integer(c_size_t), intent(in), value :: mygrid_len, hisgrid_len, AzElFileName_len, jpleph_len jpleph_file_name_cp
character(len=6) :: mygrid, hisgrid character(len=6) :: mygrid, hisgrid
character(kind=c_char, len=:), allocatable :: AzElFileName character(len=:), allocatable :: AzElFileName
character(len=1) :: c1 character(len=1) :: c1
integer :: ih, im, imin, is, isec, nfreq, nRx integer :: ih, im, imin, is, isec, nfreq, nRx
real(dp) :: AzAux, ElAux, dbMoon8, dfdt, dfdt0, doppler, doppler00, HA8, sd8, xlst8 real(dp) :: AzAux, ElAux, dbMoon8, dfdt, dfdt0, doppler, doppler00, HA8, sd8, xlst8
character*256 jpleph_file_name character*256 jpleph_file_name
common/jplcom/jpleph_file_name common/jplcom/jpleph_file_name
block mygrid = mygrid_cp
character(kind=c_char, len=mygrid_len), pointer :: mygrid_fp hisgrid = hisgrid_cp
character(kind=c_char, len=hisgrid_len), pointer :: hisgrid_fp AzElFileName = C_string_value (AzElFileName_cp)
character(kind=c_char, len=AzElFileName_len), pointer :: AzElFileName_fp jpleph_file_name = jpleph_file_name_cp
character(kind=c_char, len=jpleph_len), pointer :: jpleph_fp
call c_f_pointer(cptr=mygrid_cp, fptr=mygrid_fp)
mygrid = mygrid_fp
mygrid_fp => null()
call c_f_pointer(cptr=hisgrid_cp, fptr=hisgrid_fp)
hisgrid = hisgrid_fp
hisgrid_fp => null()
call c_f_pointer(cptr=AzElFileName_cp, fptr=AzElFileName_fp)
AzElFileName = AzElFileName_fp
AzElFileName_fp => null()
call c_f_pointer(cptr=jpleph_cp, fptr=jpleph_fp)
jpleph_file_name = jpleph_fp
jpleph_fp => null()
end block
call astro0(nyear,month,nday,uth8,freq8,mygrid,hisgrid, & call astro0(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, & AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &

57
lib/blanker.f90 Normal file
View File

@ -0,0 +1,57 @@
subroutine blanker(iwave,nz,ndropmax,npct,c_bigfft)
integer*2 iwave(nz)
complex c_bigfft(0:nz/2)
integer hist(0:32768)
real fblank !Fraction of points to be blanked
fblank=0.01*npct
hist=0
do i=1,nz
! ### NB: if iwave(i)=-32768, abs(iwave(i))=-32768 ###
if(iwave(i).eq.-32768) iwave(i)=-32767
n=abs(iwave(i))
hist(n)=hist(n)+1
enddo
n=0
do i=32768,0,-1
n=n+hist(i)
if(n.ge.nint(nz*fblank/ndropmax)) exit
enddo
nthresh=i
ndrop=0
ndropped=0
xx=0.
do i=1,nz
i0=iwave(i)
if(ndrop.gt.0) then
i0=0
ndropped=ndropped+1
ndrop=ndrop-1
endif
! Start to apply blanking
if(abs(i0).gt.nthresh) then
i0=0
ndropped=ndropped+1
ndrop=ndropmax
endif
! Now copy the data into c_bigfft
if(iand(i,1).eq.1) then
xx=i0
else
yy=i0
j=i/2 - 1
c_bigfft(j)=cmplx(xx,yy)
endif
enddo
fblanked=fblanked + 0.1*(float(ndropped)/nz - fblanked)
fblanked=float(ndropped)/nz
! write(*,3001) npct,nthresh,fblanked
!3001 format(2i5,f7.3)
return
end subroutine blanker

View File

@ -1,4 +1,4 @@
integer, parameter :: NTMAX=300 integer, parameter :: NTMAX=30*60
integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute) integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute)
integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate
integer, parameter :: NSMAX=6827 !Max length of saved spectra integer, parameter :: NSMAX=6827 !Max length of saved spectra

View File

@ -8,6 +8,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
use jt9_decode use jt9_decode
use ft8_decode use ft8_decode
use ft4_decode use ft4_decode
use fst4_decode
include 'jt9com.f90' include 'jt9com.f90'
include 'timer_common.inc' include 'timer_common.inc'
@ -32,6 +33,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
integer :: decoded integer :: decoded
end type counting_ft4_decoder end type counting_ft4_decoder
type, extends(fst4_decoder) :: counting_fst4_decoder
integer :: decoded
end type counting_fst4_decoder
real ss(184,NSMAX) real ss(184,NSMAX)
logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,ex logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,ex
integer*2 id2(NTMAX*12000) integer*2 id2(NTMAX*12000)
@ -48,6 +53,11 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
type(counting_jt9_decoder) :: my_jt9 type(counting_jt9_decoder) :: my_jt9
type(counting_ft8_decoder) :: my_ft8 type(counting_ft8_decoder) :: my_ft8
type(counting_ft4_decoder) :: my_ft4 type(counting_ft4_decoder) :: my_ft4
type(counting_fst4_decoder) :: my_fst4
rms=sqrt(dot_product(float(id2(1:180000)), &
float(id2(1:180000)))/180000.0)
if(rms.lt.3.0) go to 800
!cast C character arrays to Fortran character strings !cast C character arrays to Fortran character strings
datetime=transfer(params%datetime, datetime) datetime=transfer(params%datetime, datetime)
@ -62,6 +72,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
my_jt9%decoded = 0 my_jt9%decoded = 0
my_ft8%decoded = 0 my_ft8%decoded = 0
my_ft4%decoded = 0 my_ft4%decoded = 0
my_fst4%decoded = 0
! For testing only: return Rx messages stored in a file as decodes ! For testing only: return Rx messages stored in a file as decodes
inquire(file='rx_messages.txt',exist=ex) inquire(file='rx_messages.txt',exist=ex)
@ -180,9 +191,34 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
go to 800 go to 800
endif endif
rms=sqrt(dot_product(float(id2(300000:310000)), & if(params%nmode.eq.240) then
float(id2(300000:310000)))/10000.0) ! We're in FST4 mode
if(rms.lt.2.0) go to 800 ndepth=iand(params%ndepth,3)
iwspr=0
params%nsubmode=0
call timer('dec240 ',0)
call my_fst4%decode(fst4_decoded,id2,params%nutc, &
params%nQSOProgress,params%nfa,params%nfb, &
params%nfqso,ndepth,params%ntr,params%nexp_decode, &
params%ntol,params%emedelay,logical(params%nagain), &
logical(params%lapcqonly),mycall,hiscall,iwspr)
call timer('dec240 ',1)
go to 800
endif
if(params%nmode.eq.241) then
! We're in FST4W mode
ndepth=iand(params%ndepth,3)
iwspr=1
call timer('dec240 ',0)
call my_fst4%decode(fst4_decoded,id2,params%nutc, &
params%nQSOProgress,params%nfa,params%nfb, &
params%nfqso,ndepth,params%ntr,params%nexp_decode, &
params%ntol,params%emedelay,logical(params%nagain), &
logical(params%lapcqonly),mycall,hiscall,iwspr)
call timer('dec240 ',1)
go to 800
endif
! Zap data at start that might come from T/R switching transient? ! Zap data at start that might come from T/R switching transient?
nadd=100 nadd=100
@ -299,7 +335,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
! JT65 is not yet producing info for nsynced, ndecoded. ! JT65 is not yet producing info for nsynced, ndecoded.
800 ndecoded = my_jt4%decoded + my_jt65%decoded + my_jt9%decoded + & 800 ndecoded = my_jt4%decoded + my_jt65%decoded + my_jt9%decoded + &
my_ft8%decoded + my_ft4%decoded my_ft8%decoded + my_ft4%decoded + my_fst4%decoded
if(params%nmode.eq.8 .and. params%nzhsym.eq.41) ndec41=ndecoded if(params%nmode.eq.8 .and. params%nzhsym.eq.41) ndec41=ndecoded
if(params%nmode.eq.8 .and. params%nzhsym.eq.47) ndec47=ndecoded if(params%nmode.eq.8 .and. params%nzhsym.eq.47) ndec47=ndecoded
if(params%nmode.eq.8 .and. params%nzhsym.eq.50) then if(params%nmode.eq.8 .and. params%nzhsym.eq.50) then
@ -660,4 +696,67 @@ contains
return return
end subroutine ft4_decoded end subroutine ft4_decoded
subroutine fst4_decoded (this,nutc,sync,nsnr,dt,freq,decoded,nap, &
qual,ntrperiod,lwspr,fmid,w50)
use fst4_decode
implicit none
class(fst4_decoder), intent(inout) :: this
integer, intent(in) :: nutc
real, intent(in) :: sync
integer, intent(in) :: nsnr
real, intent(in) :: dt
real, intent(in) :: freq
character(len=37), intent(in) :: decoded
integer, intent(in) :: nap
real, intent(in) :: qual
integer, intent(in) :: ntrperiod
logical, intent(in) :: lwspr
real, intent(in) :: fmid
real, intent(in) :: w50
character*2 annot
character*37 decoded0
character*70 line
decoded0=decoded
annot=' '
if(nap.ne.0) then
write(annot,'(a1,i1)') 'a',nap
if(qual.lt.0.17) decoded0(37:37)='?'
endif
if(ntrperiod.lt.60) then
write(line,1001) nutc,nsnr,dt,nint(freq),decoded0,annot
1001 format(i6.6,i4,f5.1,i5,' ` ',1x,a37,1x,a2)
write(13,1002) nutc,nint(sync),nsnr,dt,freq,0,decoded0
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4')
else
write(line,1003) nutc,nsnr,dt,nint(freq),decoded0,annot
1003 format(i4.4,i4,f5.1,i5,' ` ',1x,a37,1x,a2,2f7.3)
write(13,1004) nutc,nint(sync),nsnr,dt,freq,0,decoded0
1004 format(i4.4,i4,i5,f6.1,f8.0,i4,3x,a37,' FST4')
endif
if(fmid.ne.-999.0) then
if(w50.lt.0.95) write(line(65:70),'(f6.3)') w50
if(w50.ge.0.95) write(line(65:70),'(f6.2)') w50
endif
write(*,1005) line
1005 format(a70)
call flush(6)
call flush(13)
select type(this)
type is (counting_fst4_decoder)
this%decoded = this%decoded + 1
end select
return
end subroutine fst4_decoded
end subroutine multimode_decoder end subroutine multimode_decoder

BIN
lib/fsk4hf/.DS_Store vendored Normal file

Binary file not shown.

562
lib/fst280_decode.f90 Normal file
View File

@ -0,0 +1,562 @@
module fst280_decode
type :: fst280_decoder
procedure(fst280_decode_callback), pointer :: callback
contains
procedure :: decode
end type fst280_decoder
abstract interface
subroutine fst280_decode_callback (this,nutc,sync,nsnr,dt,freq, &
decoded,nap,qual,ntrperiod)
import fst280_decoder
implicit none
class(fst280_decoder), intent(inout) :: this
integer, intent(in) :: nutc
real, intent(in) :: sync
integer, intent(in) :: nsnr
real, intent(in) :: dt
real, intent(in) :: freq
character(len=37), intent(in) :: decoded
integer, intent(in) :: nap
real, intent(in) :: qual
integer, intent(in) :: ntrperiod
end subroutine fst280_decode_callback
end interface
contains
subroutine decode(this,callback,iwave,nutc,nQSOProgress,nfqso, &
nfa,nfb,nsubmode,ndeep,ntrperiod,nexp_decode,ntol)
use timer_module, only: timer
use packjt77
include 'fst280/fst280_params.f90'
parameter (MAXCAND=100)
class(fst280_decoder), intent(inout) :: this
procedure(fst280_decode_callback) :: callback
character*37 decodes(100)
character*37 msg
character*77 c77
complex, allocatable :: c2(:)
complex, allocatable :: cframe(:)
complex, allocatable :: c_bigfft(:) !Complex waveform
real, allocatable :: r_data(:)
real llr(280),llra(280),llrb(280),llrc(280),llrd(280)
real candidates(100,4)
real bitmetrics(328,4)
real s4(0:3,NN)
integer itone(NN)
integer hmod
integer*1 apmask(280),cw(280)
integer*1 hbits(328)
integer*1 message101(101),message74(74)
logical badsync,unpk77_success,single_decode
integer*2 iwave(300*12000)
this%callback => callback
hmod=2**nsubmode
if(nfqso+nqsoprogress.eq.-999) return
Keff=91
iwspr=0
nmax=15*12000
single_decode=iand(nexp_decode,32).eq.32
if(ntrperiod.eq.15) then
nsps=800
nmax=15*12000
ndown=20/hmod
if(hmod.eq.8) ndown=2
else if(ntrperiod.eq.30) then
nsps=1680
nmax=30*12000
ndown=42/hmod
if(hmod.eq.4) ndown=10
if(hmod.eq.8) ndown=5
else if(ntrperiod.eq.60) then
nsps=3888
nmax=60*12000
ndown=96/hmod
if(hmod.eq.1) ndown=108
else if(ntrperiod.eq.120) then
nsps=8200
nmax=120*12000
if(hmod.eq.1) ndown=205
ndown=100/hmod
else if(ntrperiod.eq.300) then
nsps=21168
nmax=300*12000
ndown=504/hmod
end if
nss=nsps/ndown
fs=12000.0 !Sample rate
fs2=fs/ndown
nspsec=nint(fs2)
dt=1.0/fs !Sample interval (s)
dt2=1.0/fs2
tt=nsps*dt !Duration of "itone" symbols (s)
baud=1.0/tt
nfft1=2*int(nmax/2)
nh1=nfft1/2
allocate( r_data(1:nfft1+2) )
allocate( c_bigfft(0:nfft1/2) )
nfft2=nfft1/ndown
allocate( c2(0:nfft2-1) )
allocate( cframe(0:164*nss-1) )
npts=nmax
if(single_decode) then
fa=max(100,nint(nfqso+1.5*hmod*baud-ntol))
fb=min(4800,nint(nfqso+1.5*hmod*baud+ntol))
else
fa=max(100,nfa)
fb=min(4800,nfb)
endif
if(ndeep.eq.3) then
ntmax=4 ! number of block sizes to try
jittermax=2
norder=3
elseif(ndeep.eq.2) then
ntmax=3
jittermax=2
norder=3
elseif(ndeep.eq.1) then
ntmax=1
jittermax=2
norder=2
endif
! The big fft is done once and is used for calculating the smoothed spectrum
! and also for downconverting/downsampling each candidate.
r_data(1:nfft1)=iwave(1:nfft1)
r_data(nfft1+1:nfft1+2)=0.0
call four2a(r_data,nfft1,1,-1,0)
c_bigfft=cmplx(r_data(1:nfft1+2:2),r_data(2:nfft1+2:2))
! Get first approximation of candidate frequencies
call get_candidates_fst280(c_bigfft,nfft1,nsps,hmod,fs,fa,fb, &
ncand,candidates,base)
ndecodes=0
decodes=' '
isbest1=0
isbest8=0
fc21=0.
fc28=0.
do icand=1,ncand
fc0=candidates(icand,1)
detmet=candidates(icand,2)
! Downconvert and downsample a slice of the spectrum centered on the
! rough estimate of the candidates frequency.
! Output array c2 is complex baseband sampled at 12000/ndown Sa/sec.
! The size of the downsampled c2 array is nfft2=nfft1/ndown
call fst280_downsample(c_bigfft,nfft1,ndown,fc0,c2)
call timer('sync280 ',0)
do isync=0,1
if(isync.eq.0) then
fc1=0.0
is0=1.5*nint(fs2)
ishw=1.5*is0
isst=4*hmod
ifhw=12
df=.1*baud
else if(isync.eq.1) then
fc1=fc21
if(hmod.eq.1) fc1=fc28
is0=isbest1
if(hmod.eq.1) is0=isbest8
ishw=4*hmod
isst=1*hmod
ifhw=7
df=.02*baud
endif
smax1=0.0
smax8=0.0
do if=-ifhw,ifhw
fc=fc1+df*if
do istart=max(1,is0-ishw),is0+ishw,isst
call sync_fst280(c2,istart,fc,hmod,1,nfft2,nss,fs2,sync1)
call sync_fst280(c2,istart,fc,hmod,8,nfft2,nss,fs2,sync8)
if(sync8.gt.smax8) then
fc28=fc
isbest8=istart
smax8=sync8
endif
if(sync1.gt.smax1) then
fc21=fc
isbest1=istart
smax1=sync1
endif
enddo
enddo
enddo
call timer('sync280 ',1)
if(smax8/smax1 .lt. 0.65 ) then
fc2=fc21
isbest=isbest1
if(hmod.gt.1) ntmax=1
njitter=2
else
fc2=fc28
isbest=isbest8
if(hmod.gt.1) ntmax=1
njitter=2
endif
fc_synced = fc0 + fc2
dt_synced = (isbest-fs2)*dt2 !nominal dt is 1 second so frame starts at sample fs2
candidates(icand,3)=fc_synced
candidates(icand,4)=isbest
enddo
! remove duplicate candidates
do icand=1,ncand
fc=candidates(icand,3)
isbest=nint(candidates(icand,4))
do ic2=1,ncand
fc2=candidates(ic2,3)
isbest2=nint(candidates(ic2,4))
if(ic2.ne.icand .and. fc2.gt.0.0) then
if(abs(fc2-fc).lt.0.05*baud) then ! same frequency
if(abs(isbest2-isbest).le.2) then
candidates(ic2,3)=-1
endif
endif
endif
enddo
enddo
ic=0
do icand=1,ncand
if(candidates(icand,3).gt.0) then
ic=ic+1
candidates(ic,:)=candidates(icand,:)
endif
enddo
ncand=ic
do icand=1,ncand
sync=candidates(icand,2)
fc_synced=candidates(icand,3)
isbest=nint(candidates(icand,4))
xdt=(isbest-nspsec)/fs2
call fst280_downsample(c_bigfft,nfft1,ndown,fc_synced,c2)
do ijitter=0,jittermax
if(ijitter.eq.0) ioffset=0
if(ijitter.eq.1) ioffset=1
if(ijitter.eq.2) ioffset=-1
is0=isbest+ioffset
if(is0.lt.0) cycle
cframe=c2(is0:is0+164*nss-1)
bitmetrics=0
call get_fst280_bitmetrics(cframe,nss,hmod,ntmax,bitmetrics,s4,badsync)
if(badsync) cycle
hbits=0
where(bitmetrics(:,1).ge.0) hbits=1
ns1=count(hbits( 71: 78).eq.(/0,0,0,1,1,0,1,1/))
ns2=count(hbits( 79: 86).eq.(/0,1,0,0,1,1,1,0/))
ns3=count(hbits(157:164).eq.(/0,0,0,1,1,0,1,1/))
ns4=count(hbits(165:172).eq.(/0,1,0,0,1,1,1,0/))
ns5=count(hbits(243:250).eq.(/0,0,0,1,1,0,1,1/))
ns6=count(hbits(251:258).eq.(/0,1,0,0,1,1,1,0/))
nsync_qual=ns1+ns2+ns3+ns4+ns5+ns6
if(nsync_qual.lt. 26) cycle !### Value ?? ###
scalefac=2.83
llra( 1: 14)=bitmetrics( 1: 14, 1)
llra( 15: 28)=bitmetrics(315:328, 1)
llra( 29: 42)=bitmetrics( 15: 28, 1)
llra( 43: 56)=bitmetrics(301:314, 1)
llra( 57: 98)=bitmetrics( 29: 70, 1)
llra( 99:168)=bitmetrics( 87:156, 1)
llra(169:238)=bitmetrics(173:242, 1)
llra(239:280)=bitmetrics(259:300, 1)
llra=scalefac*llra
llrb( 1: 14)=bitmetrics( 1: 14, 2)
llrb( 15: 28)=bitmetrics(315:328, 2)
llrb( 29: 42)=bitmetrics( 15: 28, 2)
llrb( 43: 56)=bitmetrics(301:314, 2)
llrb( 57: 98)=bitmetrics( 29: 70, 2)
llrb( 99:168)=bitmetrics( 87:156, 2)
llrb(169:238)=bitmetrics(173:242, 2)
llrb(239:280)=bitmetrics(259:300, 2)
llrb=scalefac*llrb
llrc( 1: 14)=bitmetrics( 1: 14, 3)
llrc( 15: 28)=bitmetrics(315:328, 3)
llrc( 29: 42)=bitmetrics( 15: 28, 3)
llrc( 43: 56)=bitmetrics(301:314, 3)
llrc( 57: 98)=bitmetrics( 29: 70, 3)
llrc( 99:168)=bitmetrics( 87:156, 3)
llrc(169:238)=bitmetrics(173:242, 3)
llrc(239:280)=bitmetrics(259:300, 3)
llrc=scalefac*llrc
llrd( 1: 14)=bitmetrics( 1: 14, 4)
llrd( 15: 28)=bitmetrics(315:328, 4)
llrd( 29: 42)=bitmetrics( 15: 28, 4)
llrd( 43: 56)=bitmetrics(301:314, 4)
llrd( 57: 98)=bitmetrics( 29: 70, 4)
llrd( 99:168)=bitmetrics( 87:156, 4)
llrd(169:238)=bitmetrics(173:242, 4)
llrd(239:280)=bitmetrics(259:300, 4)
llrd=scalefac*llrd
apmask=0
do itry=1,ntmax
if(itry.eq.1) llr=llra
if(itry.eq.2) llr=llrb
if(itry.eq.3) llr=llrc
if(itry.eq.4) llr=llrd
dmin=0.0
nharderrors=-1
unpk77_success=.false.
if(iwspr.eq.0) then
maxosd=2
call timer('d280_101',0)
call decode280_101(llr,Keff,maxosd,norder,apmask,message101, &
cw,ntype,nharderrors,dmin)
call timer('d280_101',1)
else
maxosd=2
call timer('d280_74 ',0)
call decode280_74(llr,Keff,maxosd,norder,apmask,message74,cw, &
ntype,nharderrors,dmin)
call timer('d280_74 ',1)
endif
if(nharderrors .ge.0) then
if(iwspr.eq.0) then
write(c77,'(77i1)') message101(1:77)
call unpack77(c77,0,msg,unpk77_success)
else
write(c77,'(50i1)') message74(1:50)
c77(51:77)='000000000000000000000110000'
call unpack77(c77,0,msg,unpk77_success)
endif
if(unpk77_success) then
idupe=0
do i=1,ndecodes
if(decodes(i).eq.msg) idupe=1
enddo
if(idupe.eq.1) exit
ndecodes=ndecodes+1
decodes(ndecodes)=msg
if(iwspr.eq.0) then
call get_fst280_tones_from_bits(message101,itone,iwspr)
xsig=0
do i=1,NN
xsig=xsig+s4(itone(i),i)**2
enddo
arg=400.0*(xsig/base)-1.0
if(arg.gt.0.0) then
xsnr=10*log10(arg)-21.0-11.7*log10(nsps/800.0)
else
xsnr=-99.9
endif
endif
nsnr=nint(xsnr)
iaptype=0
qual=0.
fsig=fc_synced - 1.5*hmod*baud
!write(21,'(8i4,f7.1,f7.2,3f7.1,1x,a37)') &
! nutc,icand,itry,iaptype,ijitter,ntype,nsync_qual,nharderrors,dmin,sync,xsnr,xdt,fsig,msg
call this%callback(nutc,smax1,nsnr,xdt,fsig,msg, &
iaptype,qual,ntrperiod)
goto 2002
else
cycle
endif
endif
enddo ! metrics
enddo ! istart jitter
2002 continue
enddo !candidate list!ws
return
end subroutine decode
subroutine sync_fst280(cd0,i0,f0,hmod,ncoh,np,nss,fs,sync)
! Compute sync power for a complex, downsampled FST280 signal.
include 'fst280/fst280_params.f90'
complex cd0(0:np-1)
complex, allocatable, save :: csync(:)
complex, allocatable, save :: csynct(:)
complex ctwk(8*nss)
complex z1,z2,z3
logical first
integer hmod,isyncword(0:7)
real f0save
data isyncword/0,1,3,2,1,0,2,3/
data first/.true./,f0save/0.0/,nss0/-1/
save first,twopi,dt,fac,f0save,nss0
p(z1)=(real(z1*fac)**2 + aimag(z1*fac)**2)**0.5 !Compute power
if(nss.ne.nss0 .and. allocated(csync)) deallocate(csync,csynct)
if(first .or. nss.ne.nss0) then
allocate( csync(8*nss) )
allocate( csynct(8*nss) )
twopi=8.0*atan(1.0)
dt=1/fs
k=1
phi=0.0
do i=0,7
dphi=twopi*hmod*(isyncword(i)-1.5)/real(nss)
do j=1,nss
csync(k)=cmplx(cos(phi),sin(phi))
phi=mod(phi+dphi,twopi)
k=k+1
enddo
enddo
first=.false.
nss0=nss
fac=1.0/(8.0*nss)
endif
if(f0.ne.f0save) then
dphi=twopi*f0*dt
phi=0.0
do i=1,8*nss
ctwk(i)=cmplx(cos(phi),sin(phi))
phi=mod(phi+dphi,twopi)
enddo
csynct=ctwk*csync
f0save=f0
endif
i1=i0+35*nss !Costas arrays
i2=i0+78*nss
i3=i0+121*nss
s1=0.0
s2=0.0
s3=0.0
nsec=8/ncoh
do i=1,nsec
is=(i-1)*ncoh*nss
z1=0
if(i1+is.ge.1) then
z1=sum(cd0(i1+is:i1+is+ncoh*nss-1)*conjg(csynct(is+1:is+ncoh*nss)))
endif
z2=sum(cd0(i2+is:i2+is+ncoh*nss-1)*conjg(csynct(is+1:is+ncoh*nss)))
z3=0
if(i3+is+ncoh*nss-1.le.np) then
z3=sum(cd0(i3+is:i3+is+ncoh*nss-1)*conjg(csynct(is+1:is+ncoh*nss)))
endif
s1=s1+abs(z1)/(8*nss)
s2=s2+abs(z2)/(8*nss)
s3=s3+abs(z3)/(8*nss)
enddo
sync = s1+s2+s3
return
end subroutine sync_fst280
subroutine fst280_downsample(c_bigfft,nfft1,ndown,f0,c1)
! Output: Complex data in c(), sampled at 12000/ndown Hz
complex c_bigfft(0:nfft1/2)
complex c1(0:nfft1/ndown-1)
df=12000.0/nfft1
i0=nint(f0/df)
c1(0)=c_bigfft(i0)
nfft2=nfft1/ndown
do i=1,nfft2/2
if(i0+i.le.nfft1/2) c1(i)=c_bigfft(i0+i)
if(i0-i.ge.0) c1(nfft2-i)=c_bigfft(i0-i)
enddo
c1=c1/nfft2
call four2a(c1,nfft2,1,1,1) !c2c FFT back to time domain
return
end subroutine fst280_downsample
subroutine get_candidates_fst280(c_bigfft,nfft1,nsps,hmod,fs,fa,fb, &
ncand,candidates,base)
complex c_bigfft(0:nfft1/2)
integer hmod
integer indx(100)
real candidates(100,4)
real candidates0(100,4)
real snr_cand(100)
real s(18000)
real s2(18000)
data nfft1z/-1/
save nfft1z
nh1=nfft1/2
df1=fs/nfft1
baud=fs/nsps
df2=baud/2.0
nd=df2/df1
ndh=nd/2
ia=nint(max(100.0,fa)/df2)
ib=nint(min(4800.0,fb)/df2)
signal_bw=4*(12000.0/nsps)*hmod
analysis_bw=min(4800.0,fb)-max(100.0,fa)
noise_bw=10.0*signal_bw
if(analysis_bw.gt.noise_bw) then
ina=ia
inb=ib
else
fcenter=(fa+fb)/2.0
fl = max(100.0,fcenter-noise_bw/2.)/df2
fh = min(4800.0,fcenter+noise_bw/2.)/df2
ina=nint(fl)
inb=nint(fh)
endif
s=0.
do i=ina,inb ! noise analysis window includes signal analysis window
j0=nint(i*df2/df1)
do j=j0-ndh,j0+ndh
s(i)=s(i) + real(c_bigfft(j))**2 + aimag(c_bigfft(j))**2
enddo
enddo
ina=max(ina,1+3*hmod)
inb=min(inb,18000-3*hmod)
s2=0.
do i=ina,inb
s2(i)=s(i-hmod*3) + s(i-hmod) +s(i+hmod) +s(i+hmod*3)
enddo
call pctile(s2(ina+hmod*3:inb-hmod*3),inb-ina+1-hmod*6,30,base)
s2=s2/base
thresh=1.25
ncand=0
candidates=0
if(ia.lt.3) ia=3
if(ib.gt.18000-2) ib=18000-2
do i=ia,ib
if((s2(i).gt.s2(i-2)).and. &
(s2(i).gt.s2(i+2)).and. &
(s2(i).gt.thresh).and.ncand.lt.100) then
ncand=ncand+1
candidates(ncand,1)=df2*i
candidates(ncand,2)=s2(i)
endif
enddo
snr_cand=0.
snr_cand(1:ncand)=candidates(1:ncand,2)
call indexx(snr_cand,ncand,indx)
nmax=min(ncand,20)
do i=1,nmax
j=indx(ncand+1-i)
candidates0(i,1:4)=candidates(j,1:4)
enddo
ncand=nmax
candidates(1:ncand,1:4)=candidates0(1:ncand,1:4)
candidates(ncand+1:,1:4)=0.
return
end subroutine get_candidates_fst280
end module fst280_decode

View File

@ -0,0 +1,111 @@
subroutine bpdecode240_101(llr,apmask,maxiterations,message101,cw,nharderror,iter,ncheck)
!
! A log-domain belief propagation decoder for the (240,101) code.
!
integer, parameter:: N=240, K=101, M=N-K
integer*1 cw(N),apmask(N)
integer*1 decoded(K)
integer*1 message101(101)
integer nrw(M),ncw
integer Nm(6,M)
integer Mn(3,N) ! 3 checks per bit
integer synd(M)
real tov(3,N)
real toc(6,M)
real tanhtoc(6,M)
real zn(N)
real llr(N)
real Tmn
include "ldpc_240_101_parity.f90"
decoded=0
toc=0
tov=0
tanhtoc=0
! initialize messages to checks
do j=1,M
do i=1,nrw(j)
toc(i,j)=llr((Nm(i,j)))
enddo
enddo
ncnt=0
nclast=0
do iter=0,maxiterations
! Update bit log likelihood ratios (tov=0 in iteration 0).
do i=1,N
if( apmask(i) .ne. 1 ) then
zn(i)=llr(i)+sum(tov(1:ncw,i))
else
zn(i)=llr(i)
endif
enddo
! Check to see if we have a codeword (check before we do any iteration).
cw=0
where( zn .gt. 0. ) cw=1
ncheck=0
do i=1,M
synd(i)=sum(cw(Nm(1:nrw(i),i)))
if( mod(synd(i),2) .ne. 0 ) ncheck=ncheck+1
! if( mod(synd(i),2) .ne. 0 ) write(*,*) 'check ',i,' unsatisfied'
enddo
if( ncheck .eq. 0 ) then ! we have a codeword - if crc is good, return it
decoded=cw(1:101)
call get_crc24(decoded,101,nbadcrc)
nharderror=count( (2*cw-1)*llr .lt. 0.0 )
if(nbadcrc.eq.0) then
message101=decoded(1:101)
return
endif
endif
if( iter.gt.0 ) then ! this code block implements an early stopping criterion
! if( iter.gt.10000 ) then ! this code block implements an early stopping criterion
nd=ncheck-nclast
if( nd .lt. 0 ) then ! # of unsatisfied parity checks decreased
ncnt=0 ! reset counter
else
ncnt=ncnt+1
endif
! write(*,*) iter,ncheck,nd,ncnt
if( ncnt .ge. 5 .and. iter .ge. 10 .and. ncheck .gt. 15) then
nharderror=-1
return
endif
endif
nclast=ncheck
! Send messages from bits to check nodes
do j=1,M
do i=1,nrw(j)
ibj=Nm(i,j)
toc(i,j)=zn(ibj)
do kk=1,ncw ! subtract off what the bit had received from the check
if( Mn(kk,ibj) .eq. j ) then
toc(i,j)=toc(i,j)-tov(kk,ibj)
endif
enddo
enddo
enddo
! send messages from check nodes to variable nodes
do i=1,M
tanhtoc(1:6,i)=tanh(-toc(1:6,i)/2)
enddo
do j=1,N
do i=1,ncw
ichk=Mn(i,j) ! Mn(:,j) are the checks that include bit j
Tmn=product(tanhtoc(1:nrw(ichk),ichk),mask=Nm(1:nrw(ichk),ichk).ne.j)
call platanh(-Tmn,y)
! y=atanh(-Tmn)
tov(i,j)=2*y
enddo
enddo
enddo
nharderror=-1
return
end subroutine bpdecode240_101

155
lib/fst4/decode240_101.f90 Normal file
View File

@ -0,0 +1,155 @@
subroutine decode240_101(llr,Keff,maxosd,norder,apmask,message101,cw,ntype,nharderror,dmin)
!
! A hybrid bp/osd decoder for the (240,101) code.
!
! maxosd<0: do bp only
! maxosd=0: do bp and then call osd once with channel llrs
! maxosd>1: do bp and then call osd maxosd times with saved bp outputs
! norder : osd decoding depth
!
integer, parameter:: N=240, K=101, M=N-K
integer*1 cw(N),apmask(N)
integer*1 nxor(N),hdec(N)
integer*1 message101(101),m101(101)
integer nrw(M),ncw
integer Nm(6,M)
integer Mn(3,N) ! 3 checks per bit
integer synd(M)
real tov(3,N)
real toc(6,M)
real tanhtoc(6,M)
real zn(N),zsum(N),zsave(N,3)
real llr(N)
real Tmn
include "ldpc_240_101_parity.f90"
maxiterations=30
nosd=0
if(maxosd.gt.3) maxosd=3
if(maxosd.eq.0) then ! osd with channel llrs
nosd=1
zsave(:,1)=llr
elseif(maxosd.gt.0) then !
nosd=maxosd
elseif(maxosd.lt.0) then ! just bp
nosd=0
endif
toc=0
tov=0
tanhtoc=0
! initialize messages to checks
do j=1,M
do i=1,nrw(j)
toc(i,j)=llr((Nm(i,j)))
enddo
enddo
ncnt=0
nclast=0
zsum=0.0
do iter=0,maxiterations
! Update bit log likelihood ratios (tov=0 in iteration 0).
do i=1,N
if( apmask(i) .ne. 1 ) then
zn(i)=llr(i)+sum(tov(1:ncw,i))
else
zn(i)=llr(i)
endif
enddo
zsum=zsum+zn
if(iter.gt.0 .and. iter.le.maxosd) then
zsave(:,iter)=zsum
endif
! Check to see if we have a codeword (check before we do any iteration).
cw=0
where( zn .gt. 0. ) cw=1
ncheck=0
do i=1,M
synd(i)=sum(cw(Nm(1:nrw(i),i)))
if( mod(synd(i),2) .ne. 0 ) ncheck=ncheck+1
enddo
if( ncheck .eq. 0 ) then ! we have a codeword - if crc is good, return it
m101=0
m101(1:101)=cw(1:101)
call get_crc24(m101,101,nbadcrc)
if(nbadcrc.eq.0) then
message101=cw(1:101)
hdec=0
where(llr .ge. 0) hdec=1
nxor=ieor(hdec,cw)
nharderror=sum(nxor)
dmin=sum(nxor*abs(llr))
ntype=1
return
endif
endif
if( iter.gt.0 ) then ! this code block implements an early stopping criterion
! if( iter.gt.10000 ) then ! this code block implements an early stopping criterion
nd=ncheck-nclast
if( nd .lt. 0 ) then ! # of unsatisfied parity checks decreased
ncnt=0 ! reset counter
else
ncnt=ncnt+1
endif
! write(*,*) iter,ncheck,nd,ncnt
if( ncnt .ge. 5 .and. iter .ge. 10 .and. ncheck .gt. 15) then
nharderror=-1
exit
endif
endif
nclast=ncheck
! Send messages from bits to check nodes
do j=1,M
do i=1,nrw(j)
ibj=Nm(i,j)
toc(i,j)=zn(ibj)
do kk=1,ncw ! subtract off what the bit had received from the check
if( Mn(kk,ibj) .eq. j ) then
toc(i,j)=toc(i,j)-tov(kk,ibj)
endif
enddo
enddo
enddo
! send messages from check nodes to variable nodes
do i=1,M
tanhtoc(1:6,i)=tanh(-toc(1:6,i)/2)
enddo
do j=1,N
do i=1,ncw
ichk=Mn(i,j) ! Mn(:,j) are the checks that include bit j
Tmn=product(tanhtoc(1:nrw(ichk),ichk),mask=Nm(1:nrw(ichk),ichk).ne.j)
call platanh(-Tmn,y)
! y=atanh(-Tmn)
tov(i,j)=2*y
enddo
enddo
enddo ! bp iterations
do i=1,nosd
zn=zsave(:,i)
call osd240_101(zn,Keff,apmask,norder,message101,cw,nharderror,dminosd)
if(nharderror.gt.0) then
hdec=0
where(llr .ge. 0) hdec=1
nxor=ieor(hdec,cw)
nharderror=sum(nxor) ! re-calculate nharderror based on input llrs
dmin=sum(nxor*abs(llr))
ntype=1+i
return
endif
enddo
ntype=0
nharderror=-1
dminosd=0.0
return
end subroutine decode240_101

155
lib/fst4/decode240_74.f90 Normal file
View File

@ -0,0 +1,155 @@
subroutine decode240_74(llr,Keff,maxosd,norder,apmask,message74,cw,ntype,nharderror,dmin)
!
! A hybrid bp/osd decoder for the (240,74) code.
!
! maxosd<0: do bp only
! maxosd=0: do bp and then call osd once with channel llrs
! maxosd>1: do bp and then call osd maxosd times with saved bp outputs
! norder : osd decoding depth
!
integer, parameter:: N=240, K=74, M=N-K
integer*1 cw(N),apmask(N)
integer*1 nxor(N),hdec(N)
integer*1 message74(74),m74(74)
integer nrw(M),ncw
integer Nm(5,M)
integer Mn(3,N) ! 3 checks per bit
integer synd(M)
real tov(3,N)
real toc(5,M)
real tanhtoc(5,M)
real zn(N),zsum(N),zsave(N,3)
real llr(N)
real Tmn
include "ldpc_240_74_parity.f90"
maxiterations=30
nosd=0
if(maxosd.gt.3) maxosd=3
if(maxosd.eq.0) then ! osd with channel llrs
nosd=1
zsave(:,1)=llr
elseif(maxosd.gt.0) then !
nosd=maxosd
elseif(maxosd.lt.0) then ! just bp
nosd=0
endif
toc=0
tov=0
tanhtoc=0
! initialize messages to checks
do j=1,M
do i=1,nrw(j)
toc(i,j)=llr((Nm(i,j)))
enddo
enddo
ncnt=0
nclast=0
zsum=0.0
do iter=0,maxiterations
! Update bit log likelihood ratios (tov=0 in iteration 0).
do i=1,N
if( apmask(i) .ne. 1 ) then
zn(i)=llr(i)+sum(tov(1:ncw,i))
else
zn(i)=llr(i)
endif
enddo
zsum=zsum+zn
if(iter.gt.0 .and. iter.le.maxosd) then
zsave(:,iter)=zsum
endif
! Check to see if we have a codeword (check before we do any iteration).
cw=0
where( zn .gt. 0. ) cw=1
ncheck=0
do i=1,M
synd(i)=sum(cw(Nm(1:nrw(i),i)))
if( mod(synd(i),2) .ne. 0 ) ncheck=ncheck+1
enddo
if( ncheck .eq. 0 ) then ! we have a codeword - if crc is good, return it
m74=0
m74(1:74)=cw(1:74)
call get_crc24(m74,74,nbadcrc)
if(nbadcrc.eq.0) then
message74=cw(1:74)
hdec=0
where(llr .ge. 0) hdec=1
nxor=ieor(hdec,cw)
nharderror=sum(nxor)
dmin=sum(nxor*abs(llr))
ntype=1
return
endif
endif
if( iter.gt.0 ) then ! this code block implements an early stopping criterion
! if( iter.gt.10000 ) then ! this code block implements an early stopping criterion
nd=ncheck-nclast
if( nd .lt. 0 ) then ! # of unsatisfied parity checks decreased
ncnt=0 ! reset counter
else
ncnt=ncnt+1
endif
! write(*,*) iter,ncheck,nd,ncnt
if( ncnt .ge. 5 .and. iter .ge. 10 .and. ncheck .gt. 15) then
nharderror=-1
exit
endif
endif
nclast=ncheck
! Send messages from bits to check nodes
do j=1,M
do i=1,nrw(j)
ibj=Nm(i,j)
toc(i,j)=zn(ibj)
do kk=1,ncw ! subtract off what the bit had received from the check
if( Mn(kk,ibj) .eq. j ) then
toc(i,j)=toc(i,j)-tov(kk,ibj)
endif
enddo
enddo
enddo
! send messages from check nodes to variable nodes
do i=1,M
tanhtoc(1:5,i)=tanh(-toc(1:5,i)/2)
enddo
do j=1,N
do i=1,ncw
ichk=Mn(i,j) ! Mn(:,j) are the checks that include bit j
Tmn=product(tanhtoc(1:nrw(ichk),ichk),mask=Nm(1:nrw(ichk),ichk).ne.j)
call platanh(-Tmn,y)
! y=atanh(-Tmn)
tov(i,j)=2*y
enddo
enddo
enddo ! bp iterations
do i=1,nosd
zn=zsave(:,i)
call osd240_74(zn,Keff,apmask,norder,message74,cw,nharderror,dminosd)
if(nharderror.gt.0) then
hdec=0
where(llr .ge. 0) hdec=1
nxor=ieor(hdec,cw)
nharderror=sum(nxor) ! nharderror based on input llrs
dmin=sum(nxor*abs(llr))
ntype=1+i
return
endif
enddo
ntype=0
nharderror=-1
dminosd=0.0
return
end subroutine decode240_74

View File

@ -0,0 +1,46 @@
subroutine encode240_101(message,codeword)
use, intrinsic :: iso_c_binding
use iso_c_binding, only: c_loc,c_size_t
use crc
integer, parameter:: N=240, K=101, M=N-K
character*24 c24
integer*1 codeword(N)
integer*1 gen(M,K)
integer*1 message(K)
integer*1 pchecks(M)
integer*4 ncrc24
include "ldpc_240_101_generator.f90"
logical first
data first/.true./
save first,gen
if( first ) then ! fill the generator matrix
gen=0
do i=1,M
do j=1,26
read(g(i)(j:j),"(Z1)") istr
ibmax=4
if(j.eq.26) ibmax=1
do jj=1, ibmax
icol=(j-1)*4+jj
if( btest(istr,4-jj) ) gen(i,icol)=1
enddo
enddo
enddo
first=.false.
endif
do i=1,M
nsum=0
do j=1,K
nsum=nsum+message(j)*gen(i,j)
enddo
pchecks(i)=mod(nsum,2)
enddo
codeword(1:K)=message
codeword(K+1:N)=pchecks
return
end subroutine encode240_101

46
lib/fst4/encode240_74.f90 Normal file
View File

@ -0,0 +1,46 @@
subroutine encode240_74(message,codeword)
use, intrinsic :: iso_c_binding
use iso_c_binding, only: c_loc,c_size_t
use crc
integer, parameter:: N=240, K=74, M=N-K
character*24 c24
integer*1 codeword(N)
integer*1 gen(M,K)
integer*1 message(K)
integer*1 pchecks(M)
integer*4 ncrc24
include "ldpc_240_74_generator.f90"
logical first
data first/.true./
save first,gen
if( first ) then ! fill the generator matrix
gen=0
do i=1,M
do j=1,19
read(g(i)(j:j),"(Z1)") istr
ibmax=4
if(j.eq.19) ibmax=2
do jj=1, ibmax
icol=(j-1)*4+jj
if( btest(istr,4-jj) ) gen(i,icol)=1
enddo
enddo
enddo
first=.false.
endif
do i=1,M
nsum=0
do j=1,K
nsum=nsum+message(j)*gen(i,j)
enddo
pchecks(i)=mod(nsum,2)
enddo
codeword(1:K)=message
codeword(K+1:N)=pchecks
return
end subroutine encode240_74

View File

@ -0,0 +1,48 @@
subroutine fst4_baseline(s,np,ia,ib,npct,sbase)
! Fit baseline to spectrum (for FST4)
! Input: s(npts) Linear scale in power
! Output: sbase(npts) Baseline
implicit real*8 (a-h,o-z)
real*4 s(np),sw(np)
real*4 sbase(np)
real*4 base
real*8 x(1000),y(1000),a(5)
data nseg/8/
do i=ia,ib
sw(i)=10.0*log10(s(i)) !Convert to dB scale
enddo
nterms=3
nlen=(ib-ia+1)/nseg !Length of test segment
i0=(ib-ia+1)/2 !Midpoint
k=0
do n=1,nseg !Loop over all segments
ja=ia + (n-1)*nlen
jb=ja+nlen-1
call pctile(sw(ja),nlen,npct,base) !Find lowest npct of points
do i=ja,jb
if(sw(i).le.base) then
if (k.lt.1000) k=k+1 !Save all "lower envelope" points
x(k)=i-i0
y(k)=sw(i)
endif
enddo
enddo
kz=k
a=0.
call polyfit(x,y,y,kz,nterms,0,a,chisqr) !Fit a low-order polynomial
sbase=0.0
do i=ia,ib
t=i-i0
sbase(i)=a(1)+t*(a(2)+t*(a(3))) + 0.2
! write(51,3051) i,sw(i),sbase(i)
!3051 format(i8,2f12.3)
enddo
sbase=10**(sbase/10.0)
return
end subroutine fst4_baseline

7
lib/fst4/fst4_params.f90 Normal file
View File

@ -0,0 +1,7 @@
! FST4
! LDPC(240,101)/CRC24 code, five 8x4 sync
parameter (KK=77) !Information bits (77 + CRC24)
parameter (ND=120) !Data symbols
parameter (NS=40) !Sync symbols
parameter (NN=NS+ND) !Sync and data symbols (160)

155
lib/fst4/fst4sim.f90 Normal file
View File

@ -0,0 +1,155 @@
program fst4sim
! Generate simulated signals for experimental slow FT4 mode
use wavhdr
use packjt77
include 'fst4_params.f90' !Set various constants
type(hdr) h !Header for .wav file
logical*1 wspr_hint
character arg*12,fname*17
character msg37*37,msgsent37*37,c77*77
complex, allocatable :: c0(:)
complex, allocatable :: c(:)
real, allocatable :: wave(:)
integer hmod
integer itone(NN)
integer*1 msgbits(101)
integer*2, allocatable :: iwave(:) !Generated full-length waveform
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.10) then
print*,'Need 10 arguments, got ',nargs
print*,'Usage: fst4sim "message" TRsec f0 DT h fdop del nfiles snr W'
print*,'Examples: fst4sim "K1JT K9AN EN50" 60 1500 0.0 1 0.1 1.0 10 -15 F'
print*,'W (T or F) argument is hint to encoder to use WSPR message when there is abiguity'
go to 999
endif
call getarg(1,msg37) !Message to be transmitted
call getarg(2,arg)
read(arg,*) nsec !TR sequence length, seconds
call getarg(3,arg)
read(arg,*) f00 !Frequency (only used for single-signal)
call getarg(4,arg)
read(arg,*) xdt !Time offset from nominal (s)
call getarg(5,arg)
read(arg,*) hmod !Modulation index, h
call getarg(6,arg)
read(arg,*) fspread !Watterson frequency spread (Hz)
call getarg(7,arg)
read(arg,*) delay !Watterson delay (ms)
call getarg(8,arg)
read(arg,*) nfiles !Number of files
call getarg(9,arg)
read(arg,*) snrdb !SNR_2500
call getarg(10,arg)
read(arg,*) wspr_hint !0:break ties as 77-bit 1:break ties as 50-bit
nfiles=abs(nfiles)
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
nsps=0
if(nsec.eq.15) nsps=720
if(nsec.eq.30) nsps=1680
if(nsec.eq.60) nsps=3888
if(nsec.eq.120) nsps=8200
if(nsec.eq.300) nsps=21504
if(nsec.eq.900) nsps=66560
if(nsec.eq.1800) nsps=134400
if(nsps.eq.0) then
print*,'Invalid TR sequence length.'
go to 999
endif
baud=12000.0/nsps !Keying rate (baud)
nmax=nsec*12000
nz=nsps*NN
txt=nz*dt !Transmission length (s)
tt=nsps*dt !Duration of symbols (s)
nwave=max(nmax,(NN+2)*nsps)
allocate( c0(0:nwave-1) )
allocate( c(0:nwave-1) )
allocate( wave(nwave) )
allocate( iwave(nmax) )
bandwidth_ratio=2500.0/(fs/2.0)
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
if(wspr_hint) then
i3=0
n3=6
else
i3=-1
n3=-1
endif
call pack77(msg37,i3,n3,c77)
if(i3.eq.0.and.n3.eq.6) iwspr=1
call genfst4(msg37,0,msgsent37,msgbits,itone,iwspr)
write(*,*)
write(*,'(a9,a37,a3,L2,a7,i2)') 'Message: ',msgsent37,'W:',wspr_hint,' iwspr:',iwspr
write(*,1000) f00,xdt,hmod,txt,snrdb
1000 format('f0:',f9.3,' DT:',f6.2,' hmod:',i6,' TxT:',f6.1,' SNR:',f6.1)
write(*,*)
if(i3.eq.1) then
write(*,*) ' mycall hiscall hisgrid'
write(*,'(28i1,1x,i1,1x,28i1,1x,i1,1x,i1,1x,15i1,1x,3i1)') msgbits(1:77)
else
write(*,'(a14)') 'Message bits: '
write(*,'(77i1,1x,24i1)') msgbits
endif
write(*,*)
write(*,'(a17)') 'Channel symbols: '
write(*,'(10i1)') itone
write(*,*)
! call sgran()
fsample=12000.0
icmplx=1
f0=f00+1.5*hmod*baud
call gen_fst4wave(itone,NN,nsps,nwave,fsample,hmod,f0,icmplx,c0,wave)
k=nint((xdt+1.0)/dt)
if(nsec.eq.15) k=nint((xdt+0.5)/dt)
c0=cshift(c0,-k)
if(k.gt.0) c0(0:k-1)=0.0
if(k.lt.0) c0(nmax+k:nmax-1)=0.0
do ifile=1,nfiles
c=c0
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,nwave,NZ,fs,delay,fspread)
c=sig*c
wave=real(c)
if(snrdb.lt.90) then
do i=1,nmax !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
endif
gain=100.0
if(snrdb.lt.90.0) then
wave=gain*wave
else
datpk=maxval(abs(wave))
fac=32766.9/datpk
wave=fac*wave
endif
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave=nint(wave(:size(iwave)))
h=default_header(12000,nmax)
if(nmax/12000.le.30) then
write(fname,1102) ifile
1102 format('000000_',i6.6,'.wav')
else
write(fname,1104) ifile
1104 format('000000_',i4.4,'.wav')
endif
open(10,file=trim(fname),status='unknown',access='stream')
write(10) h,iwave !Save to *.wav file
close(10)
write(*,1110) ifile,xdt,f00,snrdb,fname
1110 format(i4,f7.2,f8.2,f7.1,2x,a17)
enddo
999 end program fst4sim

91
lib/fst4/gen_fst4wave.f90 Normal file
View File

@ -0,0 +1,91 @@
subroutine gen_fst4wave(itone,nsym,nsps,nwave,fsample,hmod,f0, &
icmplx,cwave,wave)
parameter(NTAB=65536)
real wave(nwave)
complex cwave(nwave),ctab(0:NTAB-1)
real, allocatable, save :: pulse(:)
real, allocatable :: dphi(:)
integer hmod
integer itone(nsym)
logical first
data first/.true./
data nsps0/-99/
save first,twopi,dt,tsym,nsps0,ctab
if(first) then
twopi=8.0*atan(1.0)
do i=0,NTAB-1
phi=i*twopi/NTAB
ctab(i)=cmplx(cos(phi),sin(phi))
enddo
endif
if(first.or.nsps.ne.nsps0) then
if(allocated(pulse)) deallocate(pulse)
allocate(pulse(1:3*nsps))
dt=1.0/fsample
tsym=nsps/fsample
! Compute the smoothed frequency-deviation pulse
do i=1,3*nsps
tt=(i-1.5*nsps)/real(nsps)
pulse(i)=gfsk_pulse(2.0,tt)
enddo
first=.false.
nsps0=nsps
endif
! Compute the smoothed frequency waveform.
! Length = (nsym+2)*nsps samples, zero-padded
allocate( dphi(0:(nsym+2)*nsps-1) )
dphi_peak=twopi*hmod/real(nsps)
dphi=0.0
do j=1,nsym
ib=(j-1)*nsps
ie=ib+3*nsps-1
dphi(ib:ie) = dphi(ib:ie) + dphi_peak*pulse(1:3*nsps)*itone(j)
enddo
! Calculate and insert the audio waveform
phi=0.0
dphi = dphi + twopi*(f0-1.5*hmod/tsym)*dt !Shift frequency up by f0
if(icmplx.eq.0) wave=0.
if(icmplx.eq.1) cwave=0.
k=0
do j=0,(nsym+2)*nsps-1
k=k+1
i=phi*float(NTAB)/twopi
i=iand(i,NTAB-1)
if(icmplx.eq.0) then
wave(k)=real(ctab(i))
else
cwave(k)=ctab(i)
endif
phi=phi+dphi(j)
if(phi.gt.twopi) phi=phi-twopi
enddo
! Compute the ramp-up and ramp-down symbols
kshift=nsps
if(icmplx.eq.0) then
wave(1:nsps)=0.0
wave(nsps+1:nsps+nsps/4)=wave(nsps+1:nsps+nsps/4) * &
(1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0
k1=nsym*nsps+3*nsps/4+1
wave((nsym+1)*nsps+1:)=0.0
wave(k1:k1+nsps/4)=wave(k1:k1+nsps/4) * &
(1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0
wave=cshift(wave,kshift)
else
cwave(1:nsps)=0.0
cwave(nsps+1:nsps+nsps/4)=cwave(nsps+1:nsps+nsps/4) * &
(1.0-cos(twopi*(/(i,i=0,nsps/4-1)/)/real(nsps/2)))/2.0
k1=nsym*nsps+3*nsps/4+1
cwave((nsym+1)*nsps+1:)=0.0
cwave(k1:k1+nsps/4)=cwave(k1:k1+nsps/4) * &
(1.0+cos(twopi*(/(i,i=0,nsps/4)/)/real(nsps/2)))/2.0
cwave=cshift(cwave,kshift)
endif
return
end subroutine gen_fst4wave

111
lib/fst4/genfst4.f90 Normal file
View File

@ -0,0 +1,111 @@
subroutine genfst4(msg0,ichk,msgsent,msgbits,i4tone,iwspr)
! Input:
! - msg0 requested message to be transmitted
! - ichk if ichk=1, return only msgsent
! - msgsent message as it will be decoded
! - i4tone array of audio tone values, {0,1,2,3}
! - iwspr in: 0: FST4 1: FST4W
! out 0: (240,101)/crc24, 1: (240,74)/crc24
!
! Frame structure:
! s8 d30 s8 d30 s8 d30 s8 d30 s8
use packjt77
include 'fst4_params.f90'
character*37 msg0
character*37 message !Message to be generated
character*37 msgsent !Message as it will be received
character*77 c77
character*24 c24
integer*4 i4tone(NN),itmp(ND)
integer*1 codeword(2*ND)
integer*1 msgbits(101),rvec(77)
integer isyncword1(8),isyncword2(8)
integer ncrc24
logical unpk77_success
data isyncword1/0,1,3,2,1,0,2,3/
data isyncword2/2,3,1,0,3,2,0,1/
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
message=msg0
do i=1, 37
if(ichar(message(i:i)).eq.0) then
message(i:37)=' '
exit
endif
enddo
do i=1,37 !Strip leading blanks
if(message(1:1).ne.' ') exit
message=message(i+1:)
enddo
i3=-1
n3=-1
if(iwspr.eq.1) then
i3=0
n3=6
endif
call pack77(message,i3,n3,c77)
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
msgbits=0
iwspr=0
if(i3.eq.0.and.n3.eq.6) then
iwspr=1
read(c77,'(50i1)') msgbits(1:50)
call get_crc24(msgbits,74,ncrc24)
write(c24,'(b24.24)') ncrc24
read(c24,'(24i1)') msgbits(51:74)
else
read(c77,'(77i1)') msgbits(1:77)
msgbits(1:77)=mod(msgbits(1:77)+rvec,2)
call get_crc24(msgbits,101,ncrc24)
write(c24,'(b24.24)') ncrc24
read(c24,'(24i1)') msgbits(78:101)
endif
if(ichk.eq.1) go to 999
if(unpk77_success) go to 2
1 msgbits=0
itone=0
msgsent='*** bad message *** '
go to 999
entry get_fst4_tones_from_bits(msgbits,i4tone,iwspr)
2 continue
if(iwspr.eq.0) then
call encode240_101(msgbits,codeword)
else
call encode240_74(msgbits(1:74),codeword)
endif
! Grayscale mapping:
! bits tone
! 00 0
! 01 1
! 11 2
! 10 3
do i=1,ND
is=codeword(2*i)+2*codeword(2*i-1)
if(is.le.1) itmp(i)=is
if(is.eq.2) itmp(i)=3
if(is.eq.3) itmp(i)=2
enddo
i4tone( 1: 8)=isyncword1
i4tone( 9: 38)=itmp( 1: 30)
i4tone( 39: 46)=isyncword2
i4tone( 47: 76)=itmp( 31: 60)
i4tone( 77: 84)=isyncword1
i4tone( 85:114)=itmp( 61: 90)
i4tone(115:122)=isyncword2
i4tone(123:152)=itmp( 91:120)
i4tone(153:160)=isyncword1
999 return
end subroutine genfst4

Some files were not shown because too many files have changed in this diff Show More