mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2025-05-24 10:22:26 -04:00
Merge branch 'release-2.1.0'
This commit is contained in:
commit
a6eecf3b23
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1,3 +1,5 @@
|
|||||||
.gitattributes export-ignore
|
.gitattributes export-ignore
|
||||||
/samples export-ignore
|
/samples export-ignore
|
||||||
/lib/fsk4hf export-ignore
|
/lib/fsk4hf export-ignore
|
||||||
|
/lib/fsk4hf export-ignore
|
||||||
|
/robots export-ignore
|
||||||
|
397
Audio/tools/record_time_signal.cpp
Normal file
397
Audio/tools/record_time_signal.cpp
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QCommandLineOption>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QAudioFormat>
|
||||||
|
#include <QAudioDeviceInfo>
|
||||||
|
#include <QAudioInput>
|
||||||
|
#include <QAudioOutput>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include "revision_utils.hpp"
|
||||||
|
#include "Audio/BWFFile.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
QTextStream qtout {stdout};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Record final
|
||||||
|
: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Record (int start, int duration, QAudioDeviceInfo const& source_device, BWFFile * output, int notify_interval, int buffer_size)
|
||||||
|
: source_ {source_device, output->format ()}
|
||||||
|
, notify_interval_ {notify_interval}
|
||||||
|
, output_ {output}
|
||||||
|
, duration_ {duration}
|
||||||
|
{
|
||||||
|
if (buffer_size) source_.setBufferSize (output_->format ().bytesForFrames (buffer_size));
|
||||||
|
if (notify_interval_)
|
||||||
|
{
|
||||||
|
source_.setNotifyInterval (notify_interval);
|
||||||
|
connect (&source_, &QAudioInput::notify, this, &Record::notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start == -1)
|
||||||
|
{
|
||||||
|
start_recording ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto now = QDateTime::currentDateTimeUtc ();
|
||||||
|
auto time = now.time ();
|
||||||
|
auto then = now;
|
||||||
|
then.setTime (QTime {time.hour (), time.minute (), start});
|
||||||
|
auto delta_ms = (now.msecsTo (then) + (60 * 1000)) % (60 * 1000);
|
||||||
|
QTimer::singleShot (int (delta_ms), Qt::PreciseTimer, this, &Record::start_recording);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SIGNAL void done ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SLOT void start_recording ()
|
||||||
|
{
|
||||||
|
qtout << "started recording at " << QDateTime::currentDateTimeUtc ().toString ("hh:mm:ss.zzz UTC") << endl;
|
||||||
|
source_.start (output_);
|
||||||
|
if (!notify_interval_) QTimer::singleShot (duration_ * 1000, Qt::PreciseTimer, this, &Record::stop_recording);
|
||||||
|
qtout << QString {"buffer size used is: %1"}.arg (source_.bufferSize ()) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void notify ()
|
||||||
|
{
|
||||||
|
auto length = source_.elapsedUSecs ();
|
||||||
|
qtout << QString {"%1 μs recorded\r"}.arg (length) << flush;
|
||||||
|
if (length >= duration_ * 1000 * 1000) stop_recording ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void stop_recording ()
|
||||||
|
{
|
||||||
|
auto length = source_.elapsedUSecs ();
|
||||||
|
source_.stop ();
|
||||||
|
qtout << QString {"%1 μs recorded "}.arg (length) << '(' << source_.format ().framesForBytes (output_->size ()) << " frames recorded)\n";
|
||||||
|
qtout << "stopped recording at " << QDateTime::currentDateTimeUtc ().toString ("hh:mm:ss.zzz UTC") << endl;
|
||||||
|
Q_EMIT done ();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAudioInput source_;
|
||||||
|
int notify_interval_;
|
||||||
|
BWFFile * output_;
|
||||||
|
int duration_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Playback final
|
||||||
|
: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Playback (int start, BWFFile * input, QAudioDeviceInfo const& sink_device, int notify_interval, int buffer_size, QString const& category)
|
||||||
|
: input_ {input}
|
||||||
|
, sink_ {sink_device, input->format ()}
|
||||||
|
, notify_interval_ {notify_interval}
|
||||||
|
{
|
||||||
|
if (buffer_size) sink_.setBufferSize (input_->format ().bytesForFrames (buffer_size));
|
||||||
|
if (category.size ()) sink_.setCategory (category);
|
||||||
|
if (notify_interval_)
|
||||||
|
{
|
||||||
|
sink_.setNotifyInterval (notify_interval);
|
||||||
|
connect (&sink_, &QAudioOutput::notify, this, &Playback::notify);
|
||||||
|
}
|
||||||
|
connect (&sink_, &QAudioOutput::stateChanged, this, &Playback::sink_state_changed);
|
||||||
|
if (start == -1)
|
||||||
|
{
|
||||||
|
start_playback ();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto now = QDateTime::currentDateTimeUtc ();
|
||||||
|
auto time = now.time ();
|
||||||
|
auto then = now;
|
||||||
|
then.setTime (QTime {time.hour (), time.minute (), start});
|
||||||
|
auto delta_ms = (now.msecsTo (then) + (60 * 1000)) % (60 * 1000);
|
||||||
|
QTimer::singleShot (int (delta_ms), Qt::PreciseTimer, this, &Playback::start_playback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SIGNAL void done ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_SLOT void start_playback ()
|
||||||
|
{
|
||||||
|
qtout << "started playback at " << QDateTime::currentDateTimeUtc ().toString ("hh:mm:ss.zzz UTC") << endl;
|
||||||
|
sink_.start (input_);
|
||||||
|
qtout << QString {"buffer size used is: %1 (%2 frames)"}.arg (sink_.bufferSize ()).arg (sink_.format ().framesForBytes (sink_.bufferSize ())) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void notify ()
|
||||||
|
{
|
||||||
|
auto length = sink_.elapsedUSecs ();
|
||||||
|
qtout << QString {"%1 μs rendered\r"}.arg (length) << flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void sink_state_changed (QAudio::State state)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case QAudio::ActiveState:
|
||||||
|
qtout << "\naudio output state changed to active\n";
|
||||||
|
break;
|
||||||
|
case QAudio::SuspendedState:
|
||||||
|
qtout << "\naudio output state changed to suspended\n";
|
||||||
|
break;
|
||||||
|
case QAudio::StoppedState:
|
||||||
|
qtout << "\naudio output state changed to stopped\n";
|
||||||
|
break;
|
||||||
|
case QAudio::IdleState:
|
||||||
|
stop_playback ();
|
||||||
|
qtout << "\naudio output state changed to idle\n";
|
||||||
|
break;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK (5, 10, 0)
|
||||||
|
case QAudio::InterruptedState:
|
||||||
|
qtout << "\naudio output state changed to interrupted\n";
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SLOT void stop_playback ()
|
||||||
|
{
|
||||||
|
auto length = sink_.elapsedUSecs ();
|
||||||
|
sink_.stop ();
|
||||||
|
qtout << QString {"%1 μs rendered "}.arg (length) << '(' << sink_.format ().framesForBytes (input_->size ()) << " frames rendered)\n";
|
||||||
|
qtout << "stopped playback at " << QDateTime::currentDateTimeUtc ().toString ("hh:mm:ss.zzz UTC") << endl;
|
||||||
|
Q_EMIT done ();
|
||||||
|
}
|
||||||
|
|
||||||
|
BWFFile * input_;
|
||||||
|
QAudioOutput sink_;
|
||||||
|
int notify_interval_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "record_time_signal.moc"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app {argc, argv};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
::setlocale (LC_NUMERIC, "C"); // ensure number forms are in
|
||||||
|
// consistent format, do this
|
||||||
|
// after instantiating
|
||||||
|
// QApplication so that Qt has
|
||||||
|
// correct l18n
|
||||||
|
|
||||||
|
// Override programs executable basename as application name.
|
||||||
|
app.setApplicationName ("WSJT-X Record Time Signal");
|
||||||
|
app.setApplicationVersion (version ());
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription (
|
||||||
|
"\nTool to determine and experiment with QAudioInput latencies\n\n"
|
||||||
|
"\tUse the -I option to list available recording device numbers\n"
|
||||||
|
);
|
||||||
|
auto help_option = parser.addHelpOption ();
|
||||||
|
auto version_option = parser.addVersionOption ();
|
||||||
|
|
||||||
|
parser.addOptions ({
|
||||||
|
{{"I", "list-audio-inputs"},
|
||||||
|
app.translate ("main", "List the available audio input devices")},
|
||||||
|
{{"O", "list-audio-outputs"},
|
||||||
|
app.translate ("main", "List the available audio output devices")},
|
||||||
|
{{"s", "start-time"},
|
||||||
|
app.translate ("main", "Record from <start-time> seconds, default start immediately"),
|
||||||
|
app.translate ("main", "start-time")},
|
||||||
|
{{"d", "duration"},
|
||||||
|
app.translate ("main", "Recording <duration> seconds"),
|
||||||
|
app.translate ("main", "duration")},
|
||||||
|
{{"o", "output"},
|
||||||
|
app.translate ("main", "Save output as <output-file>"),
|
||||||
|
app.translate ("main", "output-file")},
|
||||||
|
{{"i", "input"},
|
||||||
|
app.translate ("main", "Playback <input-file>"),
|
||||||
|
app.translate ("main", "input-file")},
|
||||||
|
{{"f", "force"},
|
||||||
|
app.translate ("main", "Overwrite existing file")},
|
||||||
|
{{"r", "sample-rate"},
|
||||||
|
app.translate ("main", "Record at <sample-rate>, default 48000 Hz"),
|
||||||
|
app.translate ("main", "sample-rate")},
|
||||||
|
{{"c", "num-channels"},
|
||||||
|
app.translate ("main", "Record <num> channels, default 2"),
|
||||||
|
app.translate ("main", "num")},
|
||||||
|
{{"R", "recording-device-number"},
|
||||||
|
app.translate ("main", "Record from <device-number>"),
|
||||||
|
app.translate ("main", "device-number")},
|
||||||
|
{{"P", "playback-device-number"},
|
||||||
|
app.translate ("main", "Playback to <device-number>"),
|
||||||
|
app.translate ("main", "device-number")},
|
||||||
|
{{"C", "category"},
|
||||||
|
app.translate ("main", "Playback <category-name>"),
|
||||||
|
app.translate ("main", "category-name")},
|
||||||
|
{{"n", "notify-interval"},
|
||||||
|
app.translate ("main", "use notify signals every <interval> milliseconds, zero to use a timer"),
|
||||||
|
app.translate ("main", "interval")},
|
||||||
|
{{"b", "buffer-size"},
|
||||||
|
app.translate ("main", "audio buffer size <frames>"),
|
||||||
|
app.translate ("main", "frames")},
|
||||||
|
});
|
||||||
|
parser.process (app);
|
||||||
|
|
||||||
|
auto input_devices = QAudioDeviceInfo::availableDevices (QAudio::AudioInput);
|
||||||
|
if (parser.isSet ("I"))
|
||||||
|
{
|
||||||
|
int n {0};
|
||||||
|
for (auto const& device : input_devices)
|
||||||
|
{
|
||||||
|
qtout << ++n << " - [" << device.deviceName () << ']' << endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto output_devices = QAudioDeviceInfo::availableDevices (QAudio::AudioOutput);
|
||||||
|
if (parser.isSet ("O"))
|
||||||
|
{
|
||||||
|
int n {0};
|
||||||
|
for (auto const& device : output_devices)
|
||||||
|
{
|
||||||
|
qtout << ++n << " - [" << device.deviceName () << ']' << endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int start {-1};
|
||||||
|
if (parser.isSet ("s"))
|
||||||
|
{
|
||||||
|
start = parser.value ("s").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"start time not a number"};
|
||||||
|
if (0 > start || start > 59) throw std::invalid_argument {"0 > start > 59"};
|
||||||
|
}
|
||||||
|
int sample_rate {48000};
|
||||||
|
if (parser.isSet ("r"))
|
||||||
|
{
|
||||||
|
sample_rate = parser.value ("r").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"sample rate not a number"};
|
||||||
|
}
|
||||||
|
int num_channels {2};
|
||||||
|
if (parser.isSet ("c"))
|
||||||
|
{
|
||||||
|
num_channels = parser.value ("c").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"channel count not a number"};
|
||||||
|
}
|
||||||
|
int notify_interval {0};
|
||||||
|
if (parser.isSet ("n"))
|
||||||
|
{
|
||||||
|
notify_interval = parser.value ("n").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"notify interval not a number"};
|
||||||
|
}
|
||||||
|
int buffer_size {0};
|
||||||
|
if (parser.isSet ("b"))
|
||||||
|
{
|
||||||
|
buffer_size = parser.value ("b").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"buffer size not a number"};
|
||||||
|
}
|
||||||
|
int input_device {0};
|
||||||
|
if (parser.isSet ("R"))
|
||||||
|
{
|
||||||
|
input_device = parser.value ("R").toInt (&ok);
|
||||||
|
if (!ok || 0 >= input_device || input_device > input_devices.size ())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument {"invalid recording device"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int output_device {0};
|
||||||
|
if (parser.isSet ("P"))
|
||||||
|
{
|
||||||
|
output_device = parser.value ("P").toInt (&ok);
|
||||||
|
if (!ok || 0 >= output_device || output_device > output_devices.size ())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument {"invalid playback device"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(parser.isSet ("o") || parser.isSet ("i"))) throw std::invalid_argument {"file required"};
|
||||||
|
if (parser.isSet ("o") && parser.isSet ("i")) throw std::invalid_argument {"specify either input or output"};
|
||||||
|
|
||||||
|
QAudioFormat audio_format;
|
||||||
|
if (parser.isSet ("o")) // Record
|
||||||
|
{
|
||||||
|
int duration = parser.value ("d").toInt (&ok);
|
||||||
|
if (!ok) throw std::invalid_argument {"duration not a number"};
|
||||||
|
|
||||||
|
QFileInfo ofi {parser.value ("o")};
|
||||||
|
if (!ofi.suffix ().size () && ofi.fileName ()[ofi.fileName ().size () - 1] != QChar {'.'})
|
||||||
|
{
|
||||||
|
ofi.setFile (ofi.filePath () + ".wav");
|
||||||
|
}
|
||||||
|
if (!parser.isSet ("f") && ofi.isFile ())
|
||||||
|
{
|
||||||
|
throw std::invalid_argument {"set the `-force' option to overwrite an existing output file"};
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_format.setSampleRate (sample_rate);
|
||||||
|
audio_format.setChannelCount (num_channels);
|
||||||
|
audio_format.setSampleSize (16);
|
||||||
|
audio_format.setSampleType (QAudioFormat::SignedInt);
|
||||||
|
audio_format.setCodec ("audio/pcm");
|
||||||
|
|
||||||
|
auto source = input_device ? input_devices[input_device - 1] : QAudioDeviceInfo::defaultInputDevice ();
|
||||||
|
if (!source.isFormatSupported (audio_format))
|
||||||
|
{
|
||||||
|
qtout << "warning, requested format not supported, using nearest" << endl;
|
||||||
|
audio_format = source.nearestFormat (audio_format);
|
||||||
|
}
|
||||||
|
BWFFile output_file {audio_format, ofi.filePath ()};
|
||||||
|
if (!output_file.open (BWFFile::WriteOnly)) throw std::invalid_argument {QString {"cannot open output file \"%1\""}.arg (ofi.filePath ()).toStdString ()};
|
||||||
|
|
||||||
|
// run the application
|
||||||
|
Record record {start, duration, source, &output_file, notify_interval, buffer_size};
|
||||||
|
QObject::connect (&record, &Record::done, &app, &QCoreApplication::quit);
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
else // Playback
|
||||||
|
{
|
||||||
|
QFileInfo ifi {parser.value ("i")};
|
||||||
|
if (!ifi.isFile () && !ifi.suffix ().size () && ifi.fileName ()[ifi.fileName ().size () - 1] != QChar {'.'})
|
||||||
|
{
|
||||||
|
ifi.setFile (ifi.filePath () + ".wav");
|
||||||
|
}
|
||||||
|
BWFFile input_file {audio_format, ifi.filePath ()};
|
||||||
|
if (!input_file.open (BWFFile::ReadOnly)) throw std::invalid_argument {QString {"cannot open input file \"%1\""}.arg (ifi.filePath ()).toStdString ()};
|
||||||
|
auto sink = output_device ? output_devices[output_device - 1] : QAudioDeviceInfo::defaultOutputDevice ();
|
||||||
|
if (!sink.isFormatSupported (input_file.format ()))
|
||||||
|
{
|
||||||
|
throw std::invalid_argument {"audio output device does not support input file audio format"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the application
|
||||||
|
Playback play {start, &input_file, sink, notify_interval, buffer_size, parser.value ("category")};
|
||||||
|
QObject::connect (&play, &Playback::done, &app, &QCoreApplication::quit);
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << e.what () << '\n';
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
std::cerr << "Unexpected fatal error\n";
|
||||||
|
throw; // hoping the runtime might tell us more about the exception
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
@ -19,9 +19,12 @@ find_path (__hamlib_pc_path NAMES hamlib.pc
|
|||||||
PATH_SUFFIXES lib/pkgconfig lib64/pkgconfig
|
PATH_SUFFIXES lib/pkgconfig lib64/pkgconfig
|
||||||
)
|
)
|
||||||
if (__hamlib_pc_path)
|
if (__hamlib_pc_path)
|
||||||
set (ENV{PKG_CONFIG_PATH} "${__hamlib_pc_path}" "$ENV{PKG_CONFIG_PATH}")
|
set (__pc_path $ENV{PKG_CONFIG_PATH})
|
||||||
unset (__hamlib_pc_path CACHE)
|
list (APPEND __pc_path "${__hamlib_pc_path}")
|
||||||
|
set (ENV{PKG_CONFIG_PATH} "${__pc_path}")
|
||||||
|
unset (__pc_path CACHE)
|
||||||
endif ()
|
endif ()
|
||||||
|
unset (__hamlib_pc_path CACHE)
|
||||||
|
|
||||||
# Use pkg-config to get hints about paths, libs and, flags
|
# Use pkg-config to get hints about paths, libs and, flags
|
||||||
unset (__pkg_config_checked_hamlib CACHE)
|
unset (__pkg_config_checked_hamlib CACHE)
|
||||||
|
@ -39,6 +39,10 @@ if (POLICY CMP0063)
|
|||||||
cmake_policy (SET CMP0063 NEW) # honour visibility properties for all library types
|
cmake_policy (SET CMP0063 NEW) # honour visibility properties for all library types
|
||||||
endif (POLICY CMP0063)
|
endif (POLICY CMP0063)
|
||||||
|
|
||||||
|
if (POLICY CMP0071)
|
||||||
|
cmake_policy (SET CMP0071 NEW) # run automoc and autouic on generated sources
|
||||||
|
endif (POLICY CMP0071)
|
||||||
|
|
||||||
include (${PROJECT_SOURCE_DIR}/CMake/VersionCompute.cmake)
|
include (${PROJECT_SOURCE_DIR}/CMake/VersionCompute.cmake)
|
||||||
message (STATUS "Building ${CMAKE_PROJECT_NAME}-${wsjtx_VERSION}")
|
message (STATUS "Building ${CMAKE_PROJECT_NAME}-${wsjtx_VERSION}")
|
||||||
|
|
||||||
@ -181,7 +185,11 @@ attach a debugger which will then receive the console output inside its console.
|
|||||||
set (PROJECT_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
set (PROJECT_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
if (NOT PROJECT_ARCHITECTURE)
|
if (NOT PROJECT_ARCHITECTURE)
|
||||||
# This is supposed to happen already on Windows
|
# This is supposed to happen already on Windows
|
||||||
|
if (CMAKE_SIZEOF_VOID_P MATCHES 8)
|
||||||
|
set (PROJECT_ARCHITECTURE "x64")
|
||||||
|
else ()
|
||||||
set (PROJECT_ARCHITECTURE "$ENV{PROCESSOR_ARCHITECTURE}")
|
set (PROJECT_ARCHITECTURE "$ENV{PROCESSOR_ARCHITECTURE}")
|
||||||
|
endif ()
|
||||||
endif (NOT PROJECT_ARCHITECTURE)
|
endif (NOT PROJECT_ARCHITECTURE)
|
||||||
message (STATUS "******************************************************")
|
message (STATUS "******************************************************")
|
||||||
message (STATUS "Building for for: ${CMAKE_SYSTEM_NAME}-${PROJECT_ARCHITECTURE}")
|
message (STATUS "Building for for: ${CMAKE_SYSTEM_NAME}-${PROJECT_ARCHITECTURE}")
|
||||||
@ -229,6 +237,7 @@ set (wsjt_qt_CXXSRCS
|
|||||||
models/FrequencyList.cpp
|
models/FrequencyList.cpp
|
||||||
models/StationList.cpp
|
models/StationList.cpp
|
||||||
widgets/FrequencyLineEdit.cpp
|
widgets/FrequencyLineEdit.cpp
|
||||||
|
widgets/FrequencyDeltaLineEdit.cpp
|
||||||
item_delegates/CandidateKeyFilter.cpp
|
item_delegates/CandidateKeyFilter.cpp
|
||||||
item_delegates/ForeignKeyDelegate.cpp
|
item_delegates/ForeignKeyDelegate.cpp
|
||||||
validators/LiveFrequencyValidator.cpp
|
validators/LiveFrequencyValidator.cpp
|
||||||
@ -271,9 +280,12 @@ set (wsjt_qt_CXXSRCS
|
|||||||
widgets/CabrilloLogWindow.cpp
|
widgets/CabrilloLogWindow.cpp
|
||||||
item_delegates/CallsignDelegate.cpp
|
item_delegates/CallsignDelegate.cpp
|
||||||
item_delegates/MaidenheadLocatorDelegate.cpp
|
item_delegates/MaidenheadLocatorDelegate.cpp
|
||||||
|
item_delegates/FrequencyDelegate.cpp
|
||||||
|
item_delegates/FrequencyDeltaDelegate.cpp
|
||||||
models/CabrilloLog.cpp
|
models/CabrilloLog.cpp
|
||||||
logbook/AD1CCty.cpp
|
logbook/AD1CCty.cpp
|
||||||
logbook/WorkedBefore.cpp
|
logbook/WorkedBefore.cpp
|
||||||
|
logbook/Multiplier.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set (wsjt_qtmm_CXXSRCS
|
set (wsjt_qtmm_CXXSRCS
|
||||||
@ -356,6 +368,7 @@ set (wsjt_FSRCS
|
|||||||
lib/jt65_decode.f90
|
lib/jt65_decode.f90
|
||||||
lib/jt65_mod.f90
|
lib/jt65_mod.f90
|
||||||
lib/ft8_decode.f90
|
lib/ft8_decode.f90
|
||||||
|
lib/ft4_decode.f90
|
||||||
lib/jt9_decode.f90
|
lib/jt9_decode.f90
|
||||||
lib/options.f90
|
lib/options.f90
|
||||||
lib/packjt.f90
|
lib/packjt.f90
|
||||||
@ -381,6 +394,7 @@ set (wsjt_FSRCS
|
|||||||
lib/azdist.f90
|
lib/azdist.f90
|
||||||
lib/badmsg.f90
|
lib/badmsg.f90
|
||||||
lib/ft8/baseline.f90
|
lib/ft8/baseline.f90
|
||||||
|
lib/ft4/ft4_baseline.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
|
||||||
@ -394,6 +408,7 @@ set (wsjt_FSRCS
|
|||||||
lib/chkhist.f90
|
lib/chkhist.f90
|
||||||
lib/chkmsg.f90
|
lib/chkmsg.f90
|
||||||
lib/chkss2.f90
|
lib/chkss2.f90
|
||||||
|
lib/ft4/clockit.f90
|
||||||
lib/ft8/compress.f90
|
lib/ft8/compress.f90
|
||||||
lib/coord.f90
|
lib/coord.f90
|
||||||
lib/db.f90
|
lib/db.f90
|
||||||
@ -455,6 +470,7 @@ set (wsjt_FSRCS
|
|||||||
lib/ft8.f90
|
lib/ft8.f90
|
||||||
lib/ft8dec.f90
|
lib/ft8dec.f90
|
||||||
lib/ft8/ft8sim.f90
|
lib/ft8/ft8sim.f90
|
||||||
|
lib/ft8/ft8sim_gfsk.f90
|
||||||
lib/gen4.f90
|
lib/gen4.f90
|
||||||
lib/gen65.f90
|
lib/gen65.f90
|
||||||
lib/gen9.f90
|
lib/gen9.f90
|
||||||
@ -462,12 +478,16 @@ set (wsjt_FSRCS
|
|||||||
lib/ft8/genft8.f90
|
lib/ft8/genft8.f90
|
||||||
lib/genmsk_128_90.f90
|
lib/genmsk_128_90.f90
|
||||||
lib/genmsk40.f90
|
lib/genmsk40.f90
|
||||||
|
lib/ft4/genft4.f90
|
||||||
|
lib/ft4/gen_ft4wave.f90
|
||||||
|
lib/ft8/gen_ft8wave.f90
|
||||||
lib/genqra64.f90
|
lib/genqra64.f90
|
||||||
lib/ft8/genft8refsig.f90
|
lib/ft8/genft8refsig.f90
|
||||||
lib/genwspr.f90
|
lib/genwspr.f90
|
||||||
lib/geodist.f90
|
lib/geodist.f90
|
||||||
lib/getlags.f90
|
lib/getlags.f90
|
||||||
lib/getmet4.f90
|
lib/getmet4.f90
|
||||||
|
lib/ft2/gfsk_pulse.f90
|
||||||
lib/graycode.f90
|
lib/graycode.f90
|
||||||
lib/graycode65.f90
|
lib/graycode65.f90
|
||||||
lib/grayline.f90
|
lib/grayline.f90
|
||||||
@ -506,6 +526,10 @@ set (wsjt_FSRCS
|
|||||||
lib/msk144signalquality.f90
|
lib/msk144signalquality.f90
|
||||||
lib/msk144sim.f90
|
lib/msk144sim.f90
|
||||||
lib/mskrtd.f90
|
lib/mskrtd.f90
|
||||||
|
lib/nuttal_window.f90
|
||||||
|
lib/ft4/ft4sim.f90
|
||||||
|
lib/ft4/ft4sim_mult.f90
|
||||||
|
lib/ft4/ft4_downsample.f90
|
||||||
lib/77bit/my_hash.f90
|
lib/77bit/my_hash.f90
|
||||||
lib/wsprd/osdwspr.f90
|
lib/wsprd/osdwspr.f90
|
||||||
lib/ft8/osd174_91.f90
|
lib/ft8/osd174_91.f90
|
||||||
@ -539,6 +563,7 @@ set (wsjt_FSRCS
|
|||||||
lib/stdmsg.f90
|
lib/stdmsg.f90
|
||||||
lib/subtract65.f90
|
lib/subtract65.f90
|
||||||
lib/ft8/subtractft8.f90
|
lib/ft8/subtractft8.f90
|
||||||
|
lib/ft4/subtractft4.f90
|
||||||
lib/sun.f90
|
lib/sun.f90
|
||||||
lib/symspec.f90
|
lib/symspec.f90
|
||||||
lib/symspec2.f90
|
lib/symspec2.f90
|
||||||
@ -546,8 +571,11 @@ set (wsjt_FSRCS
|
|||||||
lib/sync4.f90
|
lib/sync4.f90
|
||||||
lib/sync64.f90
|
lib/sync64.f90
|
||||||
lib/sync65.f90
|
lib/sync65.f90
|
||||||
|
lib/ft4/getcandidates4.f90
|
||||||
|
lib/ft4/get_ft4_bitmetrics.f90
|
||||||
lib/ft8/sync8.f90
|
lib/ft8/sync8.f90
|
||||||
lib/ft8/sync8d.f90
|
lib/ft8/sync8d.f90
|
||||||
|
lib/ft4/sync4d.f90
|
||||||
lib/sync9.f90
|
lib/sync9.f90
|
||||||
lib/sync9f.f90
|
lib/sync9f.f90
|
||||||
lib/sync9w.f90
|
lib/sync9w.f90
|
||||||
@ -794,7 +822,7 @@ if (WIN32)
|
|||||||
endif (NOT AXSERVER)
|
endif (NOT AXSERVER)
|
||||||
string (REPLACE "\"" "" AXSERVER ${AXSERVER})
|
string (REPLACE "\"" "" AXSERVER ${AXSERVER})
|
||||||
file (TO_CMAKE_PATH ${AXSERVER} AXSERVERSRCS)
|
file (TO_CMAKE_PATH ${AXSERVER} AXSERVERSRCS)
|
||||||
endif (WIN32)
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -827,9 +855,6 @@ if (Boost_NO_SYSTEM_PATHS)
|
|||||||
set (BOOST_ROOT ${PROJECT_SOURCE_DIR}/boost)
|
set (BOOST_ROOT ${PROJECT_SOURCE_DIR}/boost)
|
||||||
endif ()
|
endif ()
|
||||||
find_package (Boost 1.63 REQUIRED)
|
find_package (Boost 1.63 REQUIRED)
|
||||||
if (Boost_FOUND)
|
|
||||||
include_directories (${Boost_INCLUDE_DIRS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# OpenMP
|
# OpenMP
|
||||||
@ -860,10 +885,7 @@ message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}")
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Widgets finds its own dependencies.
|
# Widgets finds its own dependencies.
|
||||||
find_package (Qt5Widgets 5 REQUIRED)
|
find_package (Qt5 COMPONENTS Widgets Multimedia PrintSupport Sql LinguistTools REQUIRED)
|
||||||
find_package (Qt5Multimedia 5 REQUIRED)
|
|
||||||
find_package (Qt5PrintSupport 5 REQUIRED)
|
|
||||||
find_package (Qt5Sql 5 REQUIRED)
|
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_definitions (-DQT_NEEDS_QTMAIN)
|
add_definitions (-DQT_NEEDS_QTMAIN)
|
||||||
@ -1069,11 +1091,41 @@ add_custom_target (ctags COMMAND ${CTAGS} -o ${CMAKE_SOURCE_DIR}/tags -R ${sourc
|
|||||||
add_custom_target (etags COMMAND ${ETAGS} -o ${CMAKE_SOURCE_DIR}/TAGS -R ${sources})
|
add_custom_target (etags COMMAND ${ETAGS} -o ${CMAKE_SOURCE_DIR}/TAGS -R ${sources})
|
||||||
|
|
||||||
|
|
||||||
|
# Qt i18n
|
||||||
|
set (LANGUAGES
|
||||||
|
en_GB
|
||||||
|
pt_PT
|
||||||
|
)
|
||||||
|
foreach (lang_ ${LANGUAGES})
|
||||||
|
file (TO_NATIVE_PATH translations/wsjtx_${lang_}.ts ts_)
|
||||||
|
list (APPEND TS_FILES ${ts_})
|
||||||
|
endforeach ()
|
||||||
|
if (UPDATE_TRANSLATIONS)
|
||||||
|
message (STATUS "UPDATE_TRANSLATIONS option is set.")
|
||||||
|
qt5_create_translation (
|
||||||
|
QM_FILES ${wsjt_qt_UISRCS} ${wsjtx_UISRCS} ${wsjt_qt_CXXSRCS} ${wsjtx_CXXSRCS}
|
||||||
|
${TS_FILES}
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
qt5_add_translation (QM_FILES ${TS_FILES})
|
||||||
|
endif ()
|
||||||
|
add_custom_target (translations DEPENDS ${QM_FILES})
|
||||||
|
set_property (DIRECTORY PROPERTY CLEAN_NO_CUSTOM TRUE)
|
||||||
|
# do this after i18n to stop lupdate walking the boost tree which it
|
||||||
|
# chokes on
|
||||||
|
if (Boost_FOUND)
|
||||||
|
include_directories (${Boost_INCLUDE_DIRS})
|
||||||
|
endif ()
|
||||||
|
|
||||||
# embedded resources
|
# embedded resources
|
||||||
function (add_resources resources path)
|
function (add_resources resources path)
|
||||||
foreach (resource_file_ ${ARGN})
|
foreach (resource_file_ ${ARGN})
|
||||||
get_filename_component (name_ ${resource_file_} NAME)
|
get_filename_component (name_ ${resource_file_} NAME)
|
||||||
|
if (IS_ABSOLUTE "${resource_file_}")
|
||||||
|
file (TO_NATIVE_PATH ${resource_file_} source_)
|
||||||
|
else ()
|
||||||
file (TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${resource_file_} source_)
|
file (TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${resource_file_} source_)
|
||||||
|
endif ()
|
||||||
file (TO_NATIVE_PATH ${path}/${name_} dest_)
|
file (TO_NATIVE_PATH ${path}/${name_} dest_)
|
||||||
set (resources_ "${resources_}\n <file alias=\"${dest_}\">${source_}</file>")
|
set (resources_ "${resources_}\n <file alias=\"${dest_}\">${source_}</file>")
|
||||||
set (${resources} ${${resources}}${resources_} PARENT_SCOPE)
|
set (${resources} ${${resources}}${resources_} PARENT_SCOPE)
|
||||||
@ -1082,6 +1134,7 @@ endfunction (add_resources resources path)
|
|||||||
|
|
||||||
add_resources (wsjtx_RESOURCES "" ${TOP_LEVEL_RESOURCES})
|
add_resources (wsjtx_RESOURCES "" ${TOP_LEVEL_RESOURCES})
|
||||||
add_resources (wsjtx_RESOURCES /Palettes ${PALETTE_FILES})
|
add_resources (wsjtx_RESOURCES /Palettes ${PALETTE_FILES})
|
||||||
|
add_resources (wsjtx_RESOURCES /Translations ${QM_FILES})
|
||||||
|
|
||||||
configure_file (wsjtx.qrc.in wsjtx.qrc @ONLY)
|
configure_file (wsjtx.qrc.in wsjtx.qrc @ONLY)
|
||||||
|
|
||||||
@ -1102,7 +1155,6 @@ if (WIN32)
|
|||||||
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# targets
|
# targets
|
||||||
#
|
#
|
||||||
@ -1221,6 +1273,9 @@ target_link_libraries (jt49sim wsjt_fort wsjt_cxx)
|
|||||||
add_executable (allsim lib/allsim.f90 wsjtx.rc)
|
add_executable (allsim lib/allsim.f90 wsjtx.rc)
|
||||||
target_link_libraries (allsim wsjt_fort wsjt_cxx)
|
target_link_libraries (allsim wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (rtty_spec lib/rtty_spec.f90 wsjtx.rc)
|
||||||
|
target_link_libraries (rtty_spec wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
add_executable (jt65code lib/jt65code.f90 wsjtx.rc)
|
add_executable (jt65code lib/jt65code.f90 wsjtx.rc)
|
||||||
target_link_libraries (jt65code wsjt_fort wsjt_cxx)
|
target_link_libraries (jt65code wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
@ -1254,9 +1309,21 @@ target_link_libraries (ft8 wsjt_fort wsjt_cxx)
|
|||||||
add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc)
|
add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc)
|
||||||
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
|
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (ft8sim_gfsk lib/ft8/ft8sim_gfsk.f90 wsjtx.rc)
|
||||||
|
target_link_libraries (ft8sim_gfsk wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc)
|
add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc)
|
||||||
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
|
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (ft4sim lib/ft4/ft4sim.f90 wsjtx.rc)
|
||||||
|
target_link_libraries (ft4sim wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (ft4sim_mult lib/ft4/ft4sim_mult.f90 wsjtx.rc)
|
||||||
|
target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
|
||||||
|
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
|
||||||
|
|
||||||
endif(WSJT_BUILD_UTILS)
|
endif(WSJT_BUILD_UTILS)
|
||||||
|
|
||||||
# build the main application
|
# build the main application
|
||||||
@ -1606,6 +1673,7 @@ if (NOT is_debug_build)
|
|||||||
install (
|
install (
|
||||||
DIRECTORY
|
DIRECTORY
|
||||||
${QT_PLUGINS_DIR}/platforms
|
${QT_PLUGINS_DIR}/platforms
|
||||||
|
${QT_PLUGINS_DIR}/styles
|
||||||
${QT_PLUGINS_DIR}/accessible
|
${QT_PLUGINS_DIR}/accessible
|
||||||
${QT_PLUGINS_DIR}/audio
|
${QT_PLUGINS_DIR}/audio
|
||||||
${QT_PLUGINS_DIR}/imageformats
|
${QT_PLUGINS_DIR}/imageformats
|
||||||
|
@ -167,8 +167,11 @@
|
|||||||
#include "MetaDataRegistry.hpp"
|
#include "MetaDataRegistry.hpp"
|
||||||
#include "SettingsGroup.hpp"
|
#include "SettingsGroup.hpp"
|
||||||
#include "widgets/FrequencyLineEdit.hpp"
|
#include "widgets/FrequencyLineEdit.hpp"
|
||||||
|
#include "widgets/FrequencyDeltaLineEdit.hpp"
|
||||||
#include "item_delegates/CandidateKeyFilter.hpp"
|
#include "item_delegates/CandidateKeyFilter.hpp"
|
||||||
#include "item_delegates/ForeignKeyDelegate.hpp"
|
#include "item_delegates/ForeignKeyDelegate.hpp"
|
||||||
|
#include "item_delegates/FrequencyDelegate.hpp"
|
||||||
|
#include "item_delegates/FrequencyDeltaDelegate.hpp"
|
||||||
#include "TransceiverFactory.hpp"
|
#include "TransceiverFactory.hpp"
|
||||||
#include "Transceiver.hpp"
|
#include "Transceiver.hpp"
|
||||||
#include "models/Bands.hpp"
|
#include "models/Bands.hpp"
|
||||||
@ -209,6 +212,7 @@ namespace
|
|||||||
|LB|NU|YT|PEI
|
|LB|NU|YT|PEI
|
||||||
|DC # District of Columbia
|
|DC # District of Columbia
|
||||||
|DX # anyone else
|
|DX # anyone else
|
||||||
|
|SCC # Slovenia Contest Club contest
|
||||||
)
|
)
|
||||||
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
|
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
|
||||||
|
|
||||||
@ -247,6 +251,8 @@ namespace
|
|||||||
class FrequencyDialog final
|
class FrequencyDialog final
|
||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Item = FrequencyList_v2::Item;
|
using Item = FrequencyList_v2::Item;
|
||||||
|
|
||||||
@ -293,6 +299,8 @@ private:
|
|||||||
class StationDialog final
|
class StationDialog final
|
||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr)
|
explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr)
|
||||||
: QDialog {parent}
|
: QDialog {parent}
|
||||||
@ -563,6 +571,7 @@ private:
|
|||||||
DecodeHighlightingModel decode_highlighing_model_;
|
DecodeHighlightingModel decode_highlighing_model_;
|
||||||
DecodeHighlightingModel next_decode_highlighing_model_;
|
DecodeHighlightingModel next_decode_highlighing_model_;
|
||||||
bool highlight_by_mode_;
|
bool highlight_by_mode_;
|
||||||
|
bool include_WAE_entities_;
|
||||||
int LotW_days_since_upload_;
|
int LotW_days_since_upload_;
|
||||||
|
|
||||||
TransceiverFactory::ParameterPack rig_params_;
|
TransceiverFactory::ParameterPack rig_params_;
|
||||||
@ -608,6 +617,7 @@ private:
|
|||||||
bool miles_;
|
bool miles_;
|
||||||
bool quick_call_;
|
bool quick_call_;
|
||||||
bool disable_TX_on_73_;
|
bool disable_TX_on_73_;
|
||||||
|
bool force_call_1st_;
|
||||||
bool alternate_bindings_;
|
bool alternate_bindings_;
|
||||||
int watchdog_;
|
int watchdog_;
|
||||||
bool TX_messages_;
|
bool TX_messages_;
|
||||||
@ -630,6 +640,7 @@ private:
|
|||||||
bool udpWindowToFront_;
|
bool udpWindowToFront_;
|
||||||
bool udpWindowRestore_;
|
bool udpWindowRestore_;
|
||||||
DataMode data_mode_;
|
DataMode data_mode_;
|
||||||
|
bool bLowSidelobes_;
|
||||||
bool pwrBandTxMemory_;
|
bool pwrBandTxMemory_;
|
||||||
bool pwrBandTuneMemory_;
|
bool pwrBandTuneMemory_;
|
||||||
|
|
||||||
@ -703,6 +714,7 @@ bool Configuration::clear_DX () const {return m_->clear_DX_;}
|
|||||||
bool Configuration::miles () const {return m_->miles_;}
|
bool Configuration::miles () const {return m_->miles_;}
|
||||||
bool Configuration::quick_call () const {return m_->quick_call_;}
|
bool Configuration::quick_call () const {return m_->quick_call_;}
|
||||||
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
|
bool Configuration::disable_TX_on_73 () const {return m_->disable_TX_on_73_;}
|
||||||
|
bool Configuration::force_call_1st() const {return m_->force_call_1st_;}
|
||||||
bool Configuration::alternate_bindings() const {return m_->alternate_bindings_;}
|
bool Configuration::alternate_bindings() const {return m_->alternate_bindings_;}
|
||||||
int Configuration::watchdog () const {return m_->watchdog_;}
|
int Configuration::watchdog () const {return m_->watchdog_;}
|
||||||
bool Configuration::TX_messages () const {return m_->TX_messages_;}
|
bool Configuration::TX_messages () const {return m_->TX_messages_;}
|
||||||
@ -714,12 +726,14 @@ bool Configuration::x2ToneSpacing() const {return m_->x2ToneSpacing_;}
|
|||||||
bool Configuration::x4ToneSpacing() const {return m_->x4ToneSpacing_;}
|
bool Configuration::x4ToneSpacing() const {return m_->x4ToneSpacing_;}
|
||||||
bool Configuration::split_mode () const {return m_->split_mode ();}
|
bool Configuration::split_mode () const {return m_->split_mode ();}
|
||||||
QString Configuration::opCall() const {return m_->opCall_;}
|
QString Configuration::opCall() const {return m_->opCall_;}
|
||||||
|
void Configuration::opCall (QString const& call) {m_->opCall_ = call;}
|
||||||
QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
|
QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
|
||||||
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
|
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
|
||||||
bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;}
|
bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;}
|
||||||
QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
|
QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
|
||||||
auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
|
auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
|
||||||
bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;}
|
bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;}
|
||||||
|
bool Configuration::lowSidelobes() const {return m_->bLowSidelobes_;}
|
||||||
bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
|
bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
|
||||||
bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
|
bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
|
||||||
Bands * Configuration::bands () {return &m_->bands_;}
|
Bands * Configuration::bands () {return &m_->bands_;}
|
||||||
@ -739,6 +753,7 @@ bool Configuration::pwrBandTuneMemory () const {return m_->pwrBandTuneMemory_;}
|
|||||||
LotWUsers const& Configuration::lotw_users () const {return m_->lotw_users_;}
|
LotWUsers const& Configuration::lotw_users () const {return m_->lotw_users_;}
|
||||||
DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;}
|
DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;}
|
||||||
bool Configuration::highlight_by_mode () const {return m_->highlight_by_mode_;}
|
bool Configuration::highlight_by_mode () const {return m_->highlight_by_mode_;}
|
||||||
|
bool Configuration::include_WAE_entities () const {return m_->include_WAE_entities_;}
|
||||||
|
|
||||||
void Configuration::set_calibration (CalibrationParams params)
|
void Configuration::set_calibration (CalibrationParams params)
|
||||||
{
|
{
|
||||||
@ -944,6 +959,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||||||
, station_insert_action_ {tr ("&Insert ..."), nullptr}
|
, station_insert_action_ {tr ("&Insert ..."), nullptr}
|
||||||
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
|
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
|
||||||
, highlight_by_mode_ {false}
|
, highlight_by_mode_ {false}
|
||||||
|
, include_WAE_entities_ {false}
|
||||||
, LotW_days_since_upload_ {0}
|
, LotW_days_since_upload_ {0}
|
||||||
, last_port_type_ {TransceiverFactory::Capabilities::none}
|
, last_port_type_ {TransceiverFactory::Capabilities::none}
|
||||||
, rig_is_dummy_ {false}
|
, rig_is_dummy_ {false}
|
||||||
@ -1012,6 +1028,9 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||||||
});
|
});
|
||||||
lotw_users_.set_local_file_path (writeable_data_dir_.absoluteFilePath ("lotw-user-activity.csv"));
|
lotw_users_.set_local_file_path (writeable_data_dir_.absoluteFilePath ("lotw-user-activity.csv"));
|
||||||
|
|
||||||
|
// load the dictionary if it exists
|
||||||
|
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), false);
|
||||||
|
|
||||||
//
|
//
|
||||||
// validation
|
// validation
|
||||||
ui_->callsign_line_edit->setValidator (new CallsignValidator {this});
|
ui_->callsign_line_edit->setValidator (new CallsignValidator {this});
|
||||||
@ -1070,6 +1089,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||||||
//
|
//
|
||||||
fill_port_combo_box (ui_->PTT_port_combo_box);
|
fill_port_combo_box (ui_->PTT_port_combo_box);
|
||||||
ui_->PTT_port_combo_box->addItem ("CAT");
|
ui_->PTT_port_combo_box->addItem ("CAT");
|
||||||
|
ui_->PTT_port_combo_box->setItemData (ui_->PTT_port_combo_box->count () - 1, "Delegate to proxy CAT service", Qt::ToolTipRole);
|
||||||
|
|
||||||
//
|
//
|
||||||
// setup hooks to keep audio channels aligned with devices
|
// setup hooks to keep audio channels aligned with devices
|
||||||
@ -1109,9 +1129,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||||||
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
|
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
|
||||||
|
|
||||||
// delegates
|
// delegates
|
||||||
auto frequencies_item_delegate = new QStyledItemDelegate {this};
|
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {this});
|
||||||
frequencies_item_delegate->setItemEditorFactory (item_editor_factory ());
|
|
||||||
ui_->frequencies_table_view->setItemDelegate (frequencies_item_delegate);
|
|
||||||
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {®ions_, 0, this});
|
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {®ions_, 0, this});
|
||||||
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this});
|
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this});
|
||||||
|
|
||||||
@ -1150,9 +1168,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||||||
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
|
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
|
||||||
|
|
||||||
// stations delegates
|
// stations delegates
|
||||||
auto stations_item_delegate = new QStyledItemDelegate {this};
|
ui_->stations_table_view->setItemDelegateForColumn (StationList::offset_column, new FrequencyDeltaDelegate {this});
|
||||||
stations_item_delegate->setItemEditorFactory (item_editor_factory ());
|
|
||||||
ui_->stations_table_view->setItemDelegate (stations_item_delegate);
|
|
||||||
ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this});
|
ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this});
|
||||||
|
|
||||||
// stations actions
|
// stations actions
|
||||||
@ -1235,6 +1251,7 @@ void Configuration::impl::initialize_models ()
|
|||||||
ui_->miles_check_box->setChecked (miles_);
|
ui_->miles_check_box->setChecked (miles_);
|
||||||
ui_->quick_call_check_box->setChecked (quick_call_);
|
ui_->quick_call_check_box->setChecked (quick_call_);
|
||||||
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
||||||
|
ui_->force_call_1st_check_box->setChecked (force_call_1st_);
|
||||||
ui_->alternate_bindings_check_box->setChecked (alternate_bindings_);
|
ui_->alternate_bindings_check_box->setChecked (alternate_bindings_);
|
||||||
ui_->tx_watchdog_spin_box->setValue (watchdog_);
|
ui_->tx_watchdog_spin_box->setValue (watchdog_);
|
||||||
ui_->TX_messages_check_box->setChecked (TX_messages_);
|
ui_->TX_messages_check_box->setChecked (TX_messages_);
|
||||||
@ -1285,6 +1302,8 @@ void Configuration::impl::initialize_models ()
|
|||||||
ui_->udpWindowRestore->setChecked(udpWindowRestore_);
|
ui_->udpWindowRestore->setChecked(udpWindowRestore_);
|
||||||
ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
|
ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
|
||||||
ui_->calibration_slope_ppm_spin_box->setValue (calibration_.slope_ppm);
|
ui_->calibration_slope_ppm_spin_box->setValue (calibration_.slope_ppm);
|
||||||
|
ui_->rbLowSidelobes->setChecked(bLowSidelobes_);
|
||||||
|
if(!bLowSidelobes_) ui_->rbMaxSensitivity->setChecked(true);
|
||||||
|
|
||||||
if (rig_params_.ptt_port.isEmpty ())
|
if (rig_params_.ptt_port.isEmpty ())
|
||||||
{
|
{
|
||||||
@ -1306,6 +1325,7 @@ void Configuration::impl::initialize_models ()
|
|||||||
|
|
||||||
next_decode_highlighing_model_.items (decode_highlighing_model_.items ());
|
next_decode_highlighing_model_.items (decode_highlighing_model_.items ());
|
||||||
ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_);
|
ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_);
|
||||||
|
ui_->include_WAE_check_box->setChecked (include_WAE_entities_);
|
||||||
ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_);
|
ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_);
|
||||||
|
|
||||||
set_rig_invariants ();
|
set_rig_invariants ();
|
||||||
@ -1454,6 +1474,7 @@ void Configuration::impl::read_settings ()
|
|||||||
if (!highlight_items.size ()) highlight_items = DecodeHighlightingModel::default_items ();
|
if (!highlight_items.size ()) highlight_items = DecodeHighlightingModel::default_items ();
|
||||||
decode_highlighing_model_.items (highlight_items);
|
decode_highlighing_model_.items (highlight_items);
|
||||||
highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool ();
|
highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool ();
|
||||||
|
include_WAE_entities_ = settings_->value("IncludeWAEEntities", false).toBool ();
|
||||||
LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt ();
|
LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt ();
|
||||||
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
||||||
|
|
||||||
@ -1476,6 +1497,7 @@ void Configuration::impl::read_settings ()
|
|||||||
rig_params_.audio_source = settings_->value ("TXAudioSource", QVariant::fromValue (TransceiverFactory::TX_audio_source_front)).value<TransceiverFactory::TXAudioSource> ();
|
rig_params_.audio_source = settings_->value ("TXAudioSource", QVariant::fromValue (TransceiverFactory::TX_audio_source_front)).value<TransceiverFactory::TXAudioSource> ();
|
||||||
rig_params_.ptt_port = settings_->value ("PTTport").toString ();
|
rig_params_.ptt_port = settings_->value ("PTTport").toString ();
|
||||||
data_mode_ = settings_->value ("DataMode", QVariant::fromValue (data_mode_none)).value<Configuration::DataMode> ();
|
data_mode_ = settings_->value ("DataMode", QVariant::fromValue (data_mode_none)).value<Configuration::DataMode> ();
|
||||||
|
bLowSidelobes_ = settings_->value("LowSidelobes",true).toBool();
|
||||||
prompt_to_log_ = settings_->value ("PromptToLog", false).toBool ();
|
prompt_to_log_ = settings_->value ("PromptToLog", false).toBool ();
|
||||||
autoLog_ = settings_->value ("AutoLog", false).toBool ();
|
autoLog_ = settings_->value ("AutoLog", false).toBool ();
|
||||||
decodes_from_top_ = settings_->value ("DecodesFromTop", false).toBool ();
|
decodes_from_top_ = settings_->value ("DecodesFromTop", false).toBool ();
|
||||||
@ -1486,6 +1508,7 @@ void Configuration::impl::read_settings ()
|
|||||||
miles_ = settings_->value ("Miles", false).toBool ();
|
miles_ = settings_->value ("Miles", false).toBool ();
|
||||||
quick_call_ = settings_->value ("QuickCall", false).toBool ();
|
quick_call_ = settings_->value ("QuickCall", false).toBool ();
|
||||||
disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
|
disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
|
||||||
|
force_call_1st_ = settings_->value ("ForceCallFirst", false).toBool ();
|
||||||
alternate_bindings_ = settings_->value ("AlternateBindings", false).toBool ();
|
alternate_bindings_ = settings_->value ("AlternateBindings", false).toBool ();
|
||||||
watchdog_ = settings_->value ("TxWatchdog", 6).toInt ();
|
watchdog_ = settings_->value ("TxWatchdog", 6).toInt ();
|
||||||
TX_messages_ = settings_->value ("Tx2QSO", true).toBool ();
|
TX_messages_ = settings_->value ("Tx2QSO", true).toBool ();
|
||||||
@ -1565,6 +1588,7 @@ void Configuration::impl::write_settings ()
|
|||||||
settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ()));
|
settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ()));
|
||||||
settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
|
settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
|
||||||
settings_->setValue ("HighlightByMode", highlight_by_mode_);
|
settings_->setValue ("HighlightByMode", highlight_by_mode_);
|
||||||
|
settings_->setValue ("IncludeWAEEntities", include_WAE_entities_);
|
||||||
settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_);
|
settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_);
|
||||||
settings_->setValue ("toRTTY", log_as_RTTY_);
|
settings_->setValue ("toRTTY", log_as_RTTY_);
|
||||||
settings_->setValue ("dBtoComments", report_in_comments_);
|
settings_->setValue ("dBtoComments", report_in_comments_);
|
||||||
@ -1577,6 +1601,7 @@ void Configuration::impl::write_settings ()
|
|||||||
settings_->setValue ("CATStopBits", QVariant::fromValue (rig_params_.stop_bits));
|
settings_->setValue ("CATStopBits", QVariant::fromValue (rig_params_.stop_bits));
|
||||||
settings_->setValue ("CATHandshake", QVariant::fromValue (rig_params_.handshake));
|
settings_->setValue ("CATHandshake", QVariant::fromValue (rig_params_.handshake));
|
||||||
settings_->setValue ("DataMode", QVariant::fromValue (data_mode_));
|
settings_->setValue ("DataMode", QVariant::fromValue (data_mode_));
|
||||||
|
settings_->setValue ("LowSidelobes",bLowSidelobes_);
|
||||||
settings_->setValue ("PromptToLog", prompt_to_log_);
|
settings_->setValue ("PromptToLog", prompt_to_log_);
|
||||||
settings_->setValue ("AutoLog", autoLog_);
|
settings_->setValue ("AutoLog", autoLog_);
|
||||||
settings_->setValue ("DecodesFromTop", decodes_from_top_);
|
settings_->setValue ("DecodesFromTop", decodes_from_top_);
|
||||||
@ -1587,6 +1612,7 @@ void Configuration::impl::write_settings ()
|
|||||||
settings_->setValue ("Miles", miles_);
|
settings_->setValue ("Miles", miles_);
|
||||||
settings_->setValue ("QuickCall", quick_call_);
|
settings_->setValue ("QuickCall", quick_call_);
|
||||||
settings_->setValue ("73TxDisable", disable_TX_on_73_);
|
settings_->setValue ("73TxDisable", disable_TX_on_73_);
|
||||||
|
settings_->setValue ("ForceCallFirst", force_call_1st_);
|
||||||
settings_->setValue ("AlternateBindings", alternate_bindings_);
|
settings_->setValue ("AlternateBindings", alternate_bindings_);
|
||||||
settings_->setValue ("TxWatchdog", watchdog_);
|
settings_->setValue ("TxWatchdog", watchdog_);
|
||||||
settings_->setValue ("Tx2QSO", TX_messages_);
|
settings_->setValue ("Tx2QSO", TX_messages_);
|
||||||
@ -2031,10 +2057,12 @@ void Configuration::impl::accept ()
|
|||||||
miles_ = ui_->miles_check_box->isChecked ();
|
miles_ = ui_->miles_check_box->isChecked ();
|
||||||
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
||||||
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
||||||
|
force_call_1st_ = ui_->force_call_1st_check_box->isChecked ();
|
||||||
alternate_bindings_ = ui_->alternate_bindings_check_box->isChecked ();
|
alternate_bindings_ = ui_->alternate_bindings_check_box->isChecked ();
|
||||||
watchdog_ = ui_->tx_watchdog_spin_box->value ();
|
watchdog_ = ui_->tx_watchdog_spin_box->value ();
|
||||||
TX_messages_ = ui_->TX_messages_check_box->isChecked ();
|
TX_messages_ = ui_->TX_messages_check_box->isChecked ();
|
||||||
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
|
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
|
||||||
|
bLowSidelobes_ = ui_->rbLowSidelobes->isChecked();
|
||||||
save_directory_ = ui_->save_path_display_label->text ();
|
save_directory_ = ui_->save_path_display_label->text ();
|
||||||
azel_directory_ = ui_->azel_path_display_label->text ();
|
azel_directory_ = ui_->azel_path_display_label->text ();
|
||||||
enable_VHF_features_ = ui_->enable_VHF_features_check_box->isChecked ();
|
enable_VHF_features_ = ui_->enable_VHF_features_check_box->isChecked ();
|
||||||
@ -2064,7 +2092,12 @@ void Configuration::impl::accept ()
|
|||||||
Q_EMIT self_->udp_server_port_changed (new_port);
|
Q_EMIT self_->udp_server_port_changed (new_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui_->accept_udp_requests_check_box->isChecked () != accept_udp_requests_)
|
||||||
|
{
|
||||||
accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked ();
|
accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked ();
|
||||||
|
Q_EMIT self_->accept_udp_requests_changed (accept_udp_requests_);
|
||||||
|
}
|
||||||
|
|
||||||
n1mm_server_name_ = ui_->n1mm_server_name_line_edit->text ();
|
n1mm_server_name_ = ui_->n1mm_server_name_line_edit->text ();
|
||||||
n1mm_server_port_ = ui_->n1mm_server_port_spin_box->value ();
|
n1mm_server_port_ = ui_->n1mm_server_port_spin_box->value ();
|
||||||
broadcast_to_n1mm_ = ui_->enable_n1mm_broadcast_check_box->isChecked ();
|
broadcast_to_n1mm_ = ui_->enable_n1mm_broadcast_check_box->isChecked ();
|
||||||
@ -2097,6 +2130,7 @@ void Configuration::impl::accept ()
|
|||||||
Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_);
|
Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_);
|
||||||
}
|
}
|
||||||
highlight_by_mode_ = ui_->highlight_by_mode_check_box->isChecked ();
|
highlight_by_mode_ = ui_->highlight_by_mode_check_box->isChecked ();
|
||||||
|
include_WAE_entities_ = ui_->include_WAE_check_box->isChecked ();
|
||||||
LotW_days_since_upload_ = ui_->LotW_days_since_upload_spin_box->value ();
|
LotW_days_since_upload_ = ui_->LotW_days_since_upload_spin_box->value ();
|
||||||
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
||||||
|
|
||||||
@ -2155,7 +2189,7 @@ void Configuration::impl::on_rescan_log_push_button_clicked (bool /*clicked*/)
|
|||||||
|
|
||||||
void Configuration::impl::on_LotW_CSV_fetch_push_button_clicked (bool /*checked*/)
|
void Configuration::impl::on_LotW_CSV_fetch_push_button_clicked (bool /*checked*/)
|
||||||
{
|
{
|
||||||
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), true);
|
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), true, true);
|
||||||
ui_->LotW_CSV_fetch_push_button->setEnabled (false);
|
ui_->LotW_CSV_fetch_push_button->setEnabled (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2919,9 +2953,15 @@ void Configuration::impl::fill_port_combo_box (QComboBox * cb)
|
|||||||
// remove possibly confusing Windows device path (OK because
|
// remove possibly confusing Windows device path (OK because
|
||||||
// it gets added back by Hamlib)
|
// it gets added back by Hamlib)
|
||||||
cb->addItem (p.systemLocation ().remove (QRegularExpression {R"(^\\\\\.\\)"}));
|
cb->addItem (p.systemLocation ().remove (QRegularExpression {R"(^\\\\\.\\)"}));
|
||||||
|
auto tip = QString {"%1 %2 %3"}.arg (p.manufacturer ()).arg (p.serialNumber ()).arg (p.description ()).trimmed ();
|
||||||
|
if (tip.size ())
|
||||||
|
{
|
||||||
|
cb->setItemData (cb->count () - 1, tip, Qt::ToolTipRole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cb->addItem ("USB");
|
cb->addItem ("USB");
|
||||||
|
cb->setItemData (cb->count () - 1, "Custom USB device", Qt::ToolTipRole);
|
||||||
cb->setEditText (current_text);
|
cb->setEditText (current_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ public:
|
|||||||
bool miles () const;
|
bool miles () const;
|
||||||
bool quick_call () const;
|
bool quick_call () const;
|
||||||
bool disable_TX_on_73 () const;
|
bool disable_TX_on_73 () const;
|
||||||
|
bool force_call_1st() const;
|
||||||
bool alternate_bindings() const;
|
bool alternate_bindings() const;
|
||||||
int watchdog () const;
|
int watchdog () const;
|
||||||
bool TX_messages () const;
|
bool TX_messages () const;
|
||||||
@ -137,6 +138,7 @@ public:
|
|||||||
bool twoPass() const;
|
bool twoPass() const;
|
||||||
bool bFox() const;
|
bool bFox() const;
|
||||||
bool bHound() const;
|
bool bHound() const;
|
||||||
|
bool bLowSidelobes() const;
|
||||||
bool x2ToneSpacing() const;
|
bool x2ToneSpacing() const;
|
||||||
bool x4ToneSpacing() const;
|
bool x4ToneSpacing() const;
|
||||||
bool MyDx() const;
|
bool MyDx() const;
|
||||||
@ -146,12 +148,14 @@ public:
|
|||||||
bool EMEonly() const;
|
bool EMEonly() const;
|
||||||
bool post_decodes () const;
|
bool post_decodes () const;
|
||||||
QString opCall() const;
|
QString opCall() const;
|
||||||
|
void opCall (QString const&);
|
||||||
QString udp_server_name () const;
|
QString udp_server_name () const;
|
||||||
port_type udp_server_port () const;
|
port_type udp_server_port () const;
|
||||||
QString n1mm_server_name () const;
|
QString n1mm_server_name () const;
|
||||||
port_type n1mm_server_port () const;
|
port_type n1mm_server_port () const;
|
||||||
bool valid_n1mm_info () const;
|
bool valid_n1mm_info () const;
|
||||||
bool broadcast_to_n1mm() const;
|
bool broadcast_to_n1mm() const;
|
||||||
|
bool lowSidelobes() const;
|
||||||
bool accept_udp_requests () const;
|
bool accept_udp_requests () const;
|
||||||
bool udpWindowToFront () const;
|
bool udpWindowToFront () const;
|
||||||
bool udpWindowRestore () const;
|
bool udpWindowRestore () const;
|
||||||
@ -173,6 +177,7 @@ public:
|
|||||||
LotWUsers const& lotw_users () const;
|
LotWUsers const& lotw_users () const;
|
||||||
DecodeHighlightingModel const& decode_highlighting () const;
|
DecodeHighlightingModel const& decode_highlighting () const;
|
||||||
bool highlight_by_mode () const;
|
bool highlight_by_mode () const;
|
||||||
|
bool include_WAE_entities () const;
|
||||||
|
|
||||||
enum class SpecialOperatingActivity {NONE, NA_VHF, EU_VHF, FIELD_DAY, RTTY, FOX, HOUND};
|
enum class SpecialOperatingActivity {NONE, NA_VHF, EU_VHF, FIELD_DAY, RTTY, FOX, HOUND};
|
||||||
SpecialOperatingActivity special_op_id () const;
|
SpecialOperatingActivity special_op_id () const;
|
||||||
@ -266,6 +271,7 @@ public:
|
|||||||
//
|
//
|
||||||
Q_SIGNAL void udp_server_changed (QString const& udp_server) const;
|
Q_SIGNAL void udp_server_changed (QString const& udp_server) const;
|
||||||
Q_SIGNAL void udp_server_port_changed (port_type server_port) const;
|
Q_SIGNAL void udp_server_port_changed (port_type server_port) const;
|
||||||
|
Q_SIGNAL void accept_udp_requests_changed (bool checked) const;
|
||||||
|
|
||||||
// signal updates to decode highlighting
|
// signal updates to decode highlighting
|
||||||
Q_SIGNAL void decode_highlighting_changed (DecodeHighlightingModel const&) const;
|
Q_SIGNAL void decode_highlighting_changed (DecodeHighlightingModel const&) const;
|
||||||
|
246
Configuration.ui
246
Configuration.ui
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>546</width>
|
<width>546</width>
|
||||||
<height>536</height>
|
<height>553</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -301,7 +301,14 @@
|
|||||||
<string>Behavior</string>
|
<string>Behavior</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_8">
|
<layout class="QGridLayout" name="gridLayout_8">
|
||||||
<item row="4" column="1">
|
<item row="3" column="1">
|
||||||
|
<widget class="QCheckBox" name="decode_at_52s_check_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>Decode after EME delay</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_7">
|
<spacer name="horizontalSpacer_7">
|
||||||
@ -347,10 +354,27 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QCheckBox" name="decode_at_52s_check_box">
|
<widget class="QCheckBox" name="enable_VHF_features_check_box">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Decode after EME delay</string>
|
<string>Enable VHF/UHF/Microwave features</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QCheckBox" name="single_decode_check_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>Single decode</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="tx_QSY_check_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Some rigs are not able to process CAT commands while transmitting. This means that if you are operating in split mode you may have to uncheck this option.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Allow Tx frequency changes while transmitting</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -367,31 +391,35 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="single_decode_check_box">
|
<widget class="QCheckBox" name="monitor_last_used_check_box">
|
||||||
<property name="text">
|
|
||||||
<string>Single decode</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="enable_VHF_features_check_box">
|
|
||||||
<property name="text">
|
|
||||||
<string>Enable VHF/UHF/Microwave features</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QCheckBox" name="tx_QSY_check_box">
|
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>Some rigs are not able to process CAT commands while transmitting. This means that if you are operating in split mode you may have to uncheck this option.</p></body></html></string>
|
<string><html><head/><body><p>Check this if you wish to automatically return to the last monitored frequency when monitor is enabled, leave it unchecked if you wish to have the current rig frequency maintained.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Allow Tx frequency changes while transmitting</string>
|
<string>Monitor returns to last used frequency</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0" colspan="2">
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="alternate_bindings_check_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>Alternate F1-F6 bindings</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QCheckBox" name="disable_TX_on_73_check_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Turns off automatic transmissions after sending a 73 or any other free
|
||||||
|
text message.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Di&sable Tx after sending 73</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="CW_id_after_73_check_box">
|
<widget class="QCheckBox" name="CW_id_after_73_check_box">
|
||||||
@ -444,34 +472,6 @@ quiet period when decoding is done.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="monitor_last_used_check_box">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Check this if you wish to automatically return to the last monitored frequency when monitor is enabled, leave it unchecked if you wish to have the current rig frequency maintained.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Monitor returns to last used frequency</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QCheckBox" name="alternate_bindings_check_box">
|
|
||||||
<property name="text">
|
|
||||||
<string>Alternate F1-F5 bindings</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QCheckBox" name="disable_TX_on_73_check_box">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string>Turns off automatic transmissions after sending a 73 or any other free
|
|
||||||
text message.</string>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Di&sable Tx after sending 73</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="quick_call_check_box">
|
<widget class="QCheckBox" name="quick_call_check_box">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
@ -482,6 +482,13 @@ text message.</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QCheckBox" name="force_call_1st_check_box">
|
||||||
|
<property name="text">
|
||||||
|
<string>Calling CQ forces Call 1st</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -1912,7 +1919,7 @@ for assessing propagation and system performance.</string>
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="n1mm_group_box">
|
<widget class="QGroupBox" name="n1mm_group_box">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>N1MM Logger+ Broadcasts</string>
|
<string>Secondary UDP Server (deprecated)</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout_15">
|
<layout class="QFormLayout" name="formLayout_15">
|
||||||
<item row="0" column="0" colspan="2">
|
<item row="0" column="0" colspan="2">
|
||||||
@ -1928,7 +1935,7 @@ for assessing propagation and system performance.</string>
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="n1mm_server_name_label">
|
<widget class="QLabel" name="n1mm_server_name_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>N1MM Server name or IP address:</p></body></html></string>
|
<string>Server name or IP address:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>n1mm_server_name_line_edit</cstring>
|
<cstring>n1mm_server_name_line_edit</cstring>
|
||||||
@ -1945,7 +1952,7 @@ for assessing propagation and system performance.</string>
|
|||||||
<item row="2" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="n1mm_server_port_label">
|
<widget class="QLabel" name="n1mm_server_port_label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string><html><head/><body><p>N1MM Server port number:</p></body></html></string>
|
<string>Server port number:</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>n1mm_server_port_spin_box</cstring>
|
<cstring>n1mm_server_port_spin_box</cstring>
|
||||||
@ -2287,6 +2294,23 @@ Right click for insert and delete options.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout_20">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QCheckBox" name="include_WAE_check_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="includeExtraWAEEntitiesLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Include extra WAE entities</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>include_WAE_check_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -2299,35 +2323,6 @@ Right click for insert and delete options.</string>
|
|||||||
<string>Logbook of the World User Validation</string>
|
<string>Logbook of the World User Validation</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QFormLayout" name="formLayout_18">
|
<layout class="QFormLayout" name="formLayout_18">
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_14">
|
|
||||||
<property name="text">
|
|
||||||
<string>Age of last upload less than:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>LotW_days_since_upload_spin_box</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QSpinBox" name="LotW_days_since_upload_spin_box">
|
|
||||||
<property name="toolTip">
|
|
||||||
<string><html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
<property name="suffix">
|
|
||||||
<string> days</string>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>9999</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>365</number>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_15">
|
<widget class="QLabel" name="label_15">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -2362,6 +2357,35 @@ Right click for insert and delete options.</string>
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="text">
|
||||||
|
<string>Age of last upload less than:</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>LotW_days_since_upload_spin_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="LotW_days_since_upload_spin_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><html><head/><body><p>Adjust this spin box to set the age threshold of LotW user's last upload date that is accepted as a current LotW user.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="suffix">
|
||||||
|
<string> days</string>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>9999</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>365</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -2469,7 +2493,7 @@ Right click for insert and delete options.</string>
|
|||||||
<item row="1" column="0" colspan="2">
|
<item row="1" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="gbSpecialOpActivity">
|
<widget class="QGroupBox" name="gbSpecialOpActivity">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Special operating activity: Generation of FT8 and MSK144 messages</string>
|
<string>Special operating activity: Generation of FT4, FT8, and MSK144 messages</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checkable">
|
<property name="checkable">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -2556,7 +2580,7 @@ Right click for insert and delete options.</string>
|
|||||||
<string><html><head/><body><p>ARRL RTTY Roundup and similar contests. Exchange is US state, Canadian province, or &quot;DX&quot;.</p></body></html></string>
|
<string><html><head/><body><p>ARRL RTTY Roundup and similar contests. Exchange is US state, Canadian province, or &quot;DX&quot;.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>ARRL RTTY Roundup</string>
|
<string>RTTY Roundup messages</string>
|
||||||
</property>
|
</property>
|
||||||
<attribute name="buttonGroup">
|
<attribute name="buttonGroup">
|
||||||
<string notr="true">special_op_activity_button_group</string>
|
<string notr="true">special_op_activity_button_group</string>
|
||||||
@ -2821,6 +2845,48 @@ Right click for insert and delete options.</string>
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="4" column="0" colspan="2">
|
||||||
|
<widget class="QGroupBox" name="groupBox_7">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>0</width>
|
||||||
|
<height>50</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Waterfall spectra</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QRadioButton" name="rbLowSidelobes">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>10</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>91</width>
|
||||||
|
<height>17</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Low sidelobes</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QRadioButton" name="rbMaxSensitivity">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>120</x>
|
||||||
|
<y>20</y>
|
||||||
|
<width>92</width>
|
||||||
|
<height>17</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Most sensitive</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -3036,13 +3102,13 @@ Right click for insert and delete options.</string>
|
|||||||
</connection>
|
</connection>
|
||||||
</connections>
|
</connections>
|
||||||
<buttongroups>
|
<buttongroups>
|
||||||
<buttongroup name="CAT_data_bits_button_group"/>
|
|
||||||
<buttongroup name="special_op_activity_button_group"/>
|
<buttongroup name="special_op_activity_button_group"/>
|
||||||
<buttongroup name="PTT_method_button_group"/>
|
<buttongroup name="CAT_data_bits_button_group"/>
|
||||||
<buttongroup name="TX_audio_source_button_group"/>
|
|
||||||
<buttongroup name="TX_mode_button_group"/>
|
|
||||||
<buttongroup name="split_mode_button_group"/>
|
<buttongroup name="split_mode_button_group"/>
|
||||||
|
<buttongroup name="TX_mode_button_group"/>
|
||||||
|
<buttongroup name="PTT_method_button_group"/>
|
||||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
<buttongroup name="CAT_stop_bits_button_group"/>
|
||||||
<buttongroup name="CAT_handshake_button_group"/>
|
<buttongroup name="CAT_handshake_button_group"/>
|
||||||
|
<buttongroup name="TX_audio_source_button_group"/>
|
||||||
</buttongroups>
|
</buttongroups>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -127,7 +127,7 @@ int DXLabSuiteCommanderTransceiver::do_start ()
|
|||||||
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
|
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
|
||||||
}
|
}
|
||||||
|
|
||||||
poll ();
|
do_poll ();
|
||||||
return resolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
|
|||||||
update_mode (m);
|
update_mode (m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DXLabSuiteCommanderTransceiver::poll ()
|
void DXLabSuiteCommanderTransceiver::do_poll ()
|
||||||
{
|
{
|
||||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||||
bool quiet {false};
|
bool quiet {false};
|
||||||
|
@ -39,7 +39,7 @@ protected:
|
|||||||
void do_mode (MODE) override;
|
void do_mode (MODE) override;
|
||||||
void do_ptt (bool on) override;
|
void do_ptt (bool on) override;
|
||||||
|
|
||||||
void poll () override;
|
void do_poll () override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MODE get_mode (bool no_debug = false);
|
MODE get_mode (bool no_debug = false);
|
||||||
|
@ -38,5 +38,7 @@
|
|||||||
<string>True</string>
|
<string>True</string>
|
||||||
<key>NSRequiresAquaSystemAppearance</key>
|
<key>NSRequiresAquaSystemAppearance</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>This app requires microphone access to receive signals.</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
23
Detector.cpp
23
Detector.cpp
@ -2,6 +2,7 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QtAlgorithms>
|
#include <QtAlgorithms>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <math.h>
|
||||||
#include "commons.h"
|
#include "commons.h"
|
||||||
|
|
||||||
#include "moc_Detector.cpp"
|
#include "moc_Detector.cpp"
|
||||||
@ -10,7 +11,7 @@ extern "C" {
|
|||||||
void fil4_(qint16*, qint32*, qint16*, qint32*);
|
void fil4_(qint16*, qint32*, qint16*, qint32*);
|
||||||
}
|
}
|
||||||
|
|
||||||
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds,
|
Detector::Detector (unsigned frameRate, double periodLengthInSeconds,
|
||||||
unsigned downSampleFactor, QObject * parent)
|
unsigned downSampleFactor, QObject * parent)
|
||||||
: AudioDevice (parent)
|
: AudioDevice (parent)
|
||||||
, m_frameRate (frameRate)
|
, m_frameRate (frameRate)
|
||||||
@ -54,12 +55,14 @@ void Detector::clear ()
|
|||||||
|
|
||||||
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
||||||
{
|
{
|
||||||
int ns=secondInPeriod();
|
static unsigned mstr0=999999;
|
||||||
if(ns < m_ns) { // When ns has wrapped around to zero, restart the buffers
|
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||||
|
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
|
||||||
|
if(mstr < mstr0) { //When mstr has wrapped around to 0, restart the buffer
|
||||||
dec_data.params.kin = 0;
|
dec_data.params.kin = 0;
|
||||||
m_bufferPos = 0;
|
m_bufferPos = 0;
|
||||||
}
|
}
|
||||||
m_ns=ns;
|
mstr0=mstr;
|
||||||
|
|
||||||
// no torn frames
|
// no torn frames
|
||||||
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ())));
|
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ())));
|
||||||
@ -72,7 +75,7 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
|
|||||||
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) {
|
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) {
|
||||||
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
|
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
|
||||||
<< " frames of data on the floor!"
|
<< " frames of data on the floor!"
|
||||||
<< dec_data.params.kin << ns;
|
<< dec_data.params.kin << mstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned remaining = framesAccepted; remaining; ) {
|
for (unsigned remaining = framesAccepted; remaining; ) {
|
||||||
@ -120,13 +123,3 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
|
|||||||
return maxSize; // we drop any data past the end of the buffer on
|
return maxSize; // we drop any data past the end of the buffer on
|
||||||
// the floor until the next period starts
|
// the floor until the next period starts
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Detector::secondInPeriod () const
|
|
||||||
{
|
|
||||||
// we take the time of the data as the following assuming no latency
|
|
||||||
// delivering it to us (not true but close enough for us)
|
|
||||||
qint64 now (QDateTime::currentMSecsSinceEpoch ());
|
|
||||||
|
|
||||||
unsigned secondInToday ((now % 86400000LL) / 1000);
|
|
||||||
return secondInToday % m_period;
|
|
||||||
}
|
|
||||||
|
@ -22,9 +22,10 @@ public:
|
|||||||
//
|
//
|
||||||
// the samplesPerFFT argument is the number after down sampling
|
// the samplesPerFFT argument is the number after down sampling
|
||||||
//
|
//
|
||||||
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned downSampleFactor = 4u, QObject * parent = 0);
|
Detector (unsigned frameRate, double periodLengthInSeconds, unsigned downSampleFactor = 4u,
|
||||||
|
QObject * parent = 0);
|
||||||
|
|
||||||
void setTRPeriod(unsigned p) {m_period=p;}
|
void setTRPeriod(double p) {m_period=p;}
|
||||||
bool reset () override;
|
bool reset () override;
|
||||||
|
|
||||||
Q_SIGNAL void framesWritten (qint64) const;
|
Q_SIGNAL void framesWritten (qint64) const;
|
||||||
@ -40,10 +41,9 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void clear (); // discard buffer contents
|
void clear (); // discard buffer contents
|
||||||
unsigned secondInPeriod () const;
|
|
||||||
|
|
||||||
unsigned m_frameRate;
|
unsigned m_frameRate;
|
||||||
unsigned m_period;
|
double m_period;
|
||||||
unsigned m_downSampleFactor;
|
unsigned m_downSampleFactor;
|
||||||
qint32 m_samplesPerFFT; // after any down sampling
|
qint32 m_samplesPerFFT; // after any down sampling
|
||||||
qint32 m_ns;
|
qint32 m_ns;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "EmulateSplitTransceiver.hpp"
|
#include "EmulateSplitTransceiver.hpp"
|
||||||
|
|
||||||
|
#include "moc_EmulateSplitTransceiver.cpp"
|
||||||
|
|
||||||
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
|
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
|
||||||
: Transceiver {parent}
|
: Transceiver {parent}
|
||||||
, wrapped_ {std::move (wrapped)}
|
, wrapped_ {std::move (wrapped)}
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
class EmulateSplitTransceiver final
|
class EmulateSplitTransceiver final
|
||||||
: public Transceiver
|
: public Transceiver
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// takes ownership of wrapped Transceiver
|
// takes ownership of wrapped Transceiver
|
||||||
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped,
|
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped,
|
||||||
|
@ -19,6 +19,8 @@ namespace
|
|||||||
int constexpr yaesu_delay {250};
|
int constexpr yaesu_delay {250};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "moc_HRDTransceiver.cpp"
|
||||||
|
|
||||||
void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
|
void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
|
||||||
{
|
{
|
||||||
(*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, true, true /* maybe */);
|
(*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, true, true /* maybe */);
|
||||||
@ -885,7 +887,7 @@ bool HRDTransceiver::is_button_checked (int button_index, bool no_debug)
|
|||||||
return "1" == reply;
|
return "1" == reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HRDTransceiver::poll ()
|
void HRDTransceiver::do_poll ()
|
||||||
{
|
{
|
||||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||||
bool quiet {false};
|
bool quiet {false};
|
||||||
|
@ -27,6 +27,8 @@ class QByteArray;
|
|||||||
class HRDTransceiver final
|
class HRDTransceiver final
|
||||||
: public PollingTransceiver
|
: public PollingTransceiver
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
|
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ protected:
|
|||||||
void do_ptt (bool on) override;
|
void do_ptt (bool on) override;
|
||||||
|
|
||||||
// Implement the PollingTransceiver interface.
|
// Implement the PollingTransceiver interface.
|
||||||
void poll () override;
|
void do_poll () override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false);
|
QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false);
|
||||||
|
@ -632,7 +632,7 @@ int HamlibTransceiver::do_start ()
|
|||||||
resolution = -1; // best guess
|
resolution = -1; // best guess
|
||||||
}
|
}
|
||||||
|
|
||||||
poll ();
|
do_poll ();
|
||||||
|
|
||||||
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
|
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
|
||||||
return resolution;
|
return resolution;
|
||||||
@ -898,7 +898,7 @@ void HamlibTransceiver::do_mode (MODE mode)
|
|||||||
update_mode (mode);
|
update_mode (mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HamlibTransceiver::poll ()
|
void HamlibTransceiver::do_poll ()
|
||||||
{
|
{
|
||||||
#if !WSJT_TRACE_CAT_POLLS
|
#if !WSJT_TRACE_CAT_POLLS
|
||||||
#if defined (NDEBUG)
|
#if defined (NDEBUG)
|
||||||
|
@ -21,7 +21,7 @@ extern "C"
|
|||||||
class HamlibTransceiver final
|
class HamlibTransceiver final
|
||||||
: public PollingTransceiver
|
: public PollingTransceiver
|
||||||
{
|
{
|
||||||
Q_OBJECT; // for translation context
|
Q_OBJECT // for translation context
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void register_transceivers (TransceiverFactory::Transceivers *);
|
static void register_transceivers (TransceiverFactory::Transceivers *);
|
||||||
@ -40,7 +40,7 @@ class HamlibTransceiver final
|
|||||||
void do_mode (MODE) override;
|
void do_mode (MODE) override;
|
||||||
void do_ptt (bool) override;
|
void do_ptt (bool) override;
|
||||||
|
|
||||||
void poll () override;
|
void do_poll () override;
|
||||||
|
|
||||||
void error_check (int ret_code, QString const& doing) const;
|
void error_check (int ret_code, QString const& doing) const;
|
||||||
void set_conf (char const * item, char const * value);
|
void set_conf (char const * item, char const * value);
|
||||||
|
62
INSTALL
62
INSTALL
@ -30,13 +30,13 @@ Mac".
|
|||||||
|
|
||||||
Qt v5, preferably v5.5 or later is required to build WSJT-X.
|
Qt v5, preferably v5.5 or later is required to build WSJT-X.
|
||||||
|
|
||||||
Qt v5 multimedia support and serial port is necessary as well as the
|
Qt v5 multimedia support, serial port, and Linguist is necessary as
|
||||||
core Qt v5 components, normally installing the Qt multimedia
|
well as the core Qt v5 components, normally installing the Qt
|
||||||
development package and Qt serialport development package are
|
multimedia development, Qt serialport development packages, and the Qt
|
||||||
sufficient to pull in all the required Qt components and dependants as
|
Linguist packages are sufficient to pull in all the required Qt
|
||||||
a single transaction. On some systems the Qt multimedia plugin
|
components and dependants as a single transaction. On some systems
|
||||||
component is separate in the distribution repository an it may also
|
the Qt multimedia plugin component is separate in the distribution
|
||||||
need installing.
|
repository an it may also need installing.
|
||||||
|
|
||||||
The single precision FFTW v3 library libfftw3f is required along with
|
The single precision FFTW v3 library libfftw3f is required along with
|
||||||
the libfftw library development package. Normally installing the
|
the libfftw library development package. Normally installing the
|
||||||
@ -256,47 +256,6 @@ The above commands will build hamlib and install it into
|
|||||||
~/hamlib-prefix. If `make install-strip` fails, try `make install`.
|
~/hamlib-prefix. If `make install-strip` fails, try `make install`.
|
||||||
|
|
||||||
|
|
||||||
Qt
|
|
||||||
--
|
|
||||||
|
|
||||||
NOTE: As of Qt v5.4 building Qt from source on Mac OS X is no longer
|
|
||||||
necessary since the Qt team have switched to using the modern libc++
|
|
||||||
Standard C++ Library for all distributable run time
|
|
||||||
components. Instead you may simply download a binary installer for OS
|
|
||||||
X 64-bit. The binary installer is here:
|
|
||||||
|
|
||||||
http://www.qt.io/download
|
|
||||||
|
|
||||||
The binary Qt distributions prior to Qt v5.4 from
|
|
||||||
http://www.qt.io/download unfortunately are built to use the libstdc++
|
|
||||||
C++ support library, WSJT-X uses a less geriatric C++ dialect which
|
|
||||||
uses the libc++ C++ support library. This means that you need to
|
|
||||||
build Qt from sources. This is not difficult but does take some time.
|
|
||||||
|
|
||||||
Download the Qt source tarball from
|
|
||||||
http://www.qt.io/download-open-source/, the link is about half way
|
|
||||||
down the page, you want the full sources tar ball shown as a 'tar.gz'
|
|
||||||
link.
|
|
||||||
|
|
||||||
Unpack the sources and cd into the top level directory then type:
|
|
||||||
|
|
||||||
$ ./configure -prefix ~/local/qt-macx-clang -opensource \
|
|
||||||
-confirm-license -platform macx-clang -silent -nomake tests \
|
|
||||||
-nomake examples -sdk macosx10.10 -skip qtwebkit \
|
|
||||||
-skip qtwebkit-examples -skip qtquick1 -skip qtconnectivity \
|
|
||||||
-skip qtlocation -skip qtsensors -skip qtscript \
|
|
||||||
-skip qtwebsockets -skip qtwebengine -skip qtwebchannel \
|
|
||||||
-skip qtwayland -skip qtquickcontrols -skip qtdeclarative \
|
|
||||||
-skip qtxmlpatterns -skip qtenginio
|
|
||||||
$ make -j4
|
|
||||||
$ make install
|
|
||||||
|
|
||||||
If you are building on 10.8 or don't have the 10.10 Mac SDK (Xcode 6)
|
|
||||||
available, you can substitute '-sdk macosx10.9' above.
|
|
||||||
|
|
||||||
The build above will take a few hours to complete.
|
|
||||||
|
|
||||||
|
|
||||||
CMake
|
CMake
|
||||||
-----
|
-----
|
||||||
Although CMake is available via MacPorts I prefer to use the binary
|
Although CMake is available via MacPorts I prefer to use the binary
|
||||||
@ -328,6 +287,13 @@ $ sudo chgrp wheel /usr/local/bin
|
|||||||
and then retry the install command.
|
and then retry the install command.
|
||||||
|
|
||||||
|
|
||||||
|
Qt
|
||||||
|
--
|
||||||
|
|
||||||
|
Download the latest on-line installer package from the Qt web site and
|
||||||
|
isntall the latest Qt stable version development package.
|
||||||
|
|
||||||
|
|
||||||
WSJT-X
|
WSJT-X
|
||||||
------
|
------
|
||||||
First fetch the source from the repository:
|
First fetch the source from the repository:
|
||||||
|
@ -42,11 +42,12 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void load (QString const& url, bool forced_fetch)
|
void load (QString const& url, bool fetch, bool forced_fetch)
|
||||||
{
|
{
|
||||||
auto csv_file_name = csv_file_.fileName ();
|
|
||||||
abort (); // abort any active download
|
abort (); // abort any active download
|
||||||
if (!QFileInfo::exists (csv_file_name) || forced_fetch)
|
auto csv_file_name = csv_file_.fileName ();
|
||||||
|
auto exists = QFileInfo::exists (csv_file_name);
|
||||||
|
if (fetch && (!exists || forced_fetch))
|
||||||
{
|
{
|
||||||
current_url_.setUrl (url);
|
current_url_.setUrl (url);
|
||||||
if (current_url_.isValid () && !QSslSocket::supportsSsl ())
|
if (current_url_.isValid () && !QSslSocket::supportsSsl ())
|
||||||
@ -57,11 +58,14 @@ public:
|
|||||||
download (current_url_);
|
download (current_url_);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
if (exists)
|
||||||
{
|
{
|
||||||
// load the database asynchronously
|
// load the database asynchronously
|
||||||
future_load_ = std::async (std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_name);
|
future_load_ = std::async (std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void download (QUrl url)
|
void download (QUrl url)
|
||||||
{
|
{
|
||||||
@ -254,9 +258,9 @@ void LotWUsers::set_local_file_path (QString const& path)
|
|||||||
m_->csv_file_.setFileName (path);
|
m_->csv_file_.setFileName (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LotWUsers::load (QString const& url, bool force_download)
|
void LotWUsers::load (QString const& url, bool fetch, bool force_download)
|
||||||
{
|
{
|
||||||
m_->load (url, force_download);
|
m_->load (url, fetch, force_download);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LotWUsers::set_age_constraint (qint64 uploaded_since_days)
|
void LotWUsers::set_age_constraint (qint64 uploaded_since_days)
|
||||||
|
@ -23,7 +23,7 @@ public:
|
|||||||
|
|
||||||
void set_local_file_path (QString const&);
|
void set_local_file_path (QString const&);
|
||||||
|
|
||||||
Q_SLOT void load (QString const& url, bool force_download = false);
|
Q_SLOT void load (QString const& url, bool fetch = true, bool force_download = false);
|
||||||
Q_SLOT void set_age_constraint (qint64 uploaded_since_days);
|
Q_SLOT void set_age_constraint (qint64 uploaded_since_days);
|
||||||
|
|
||||||
// returns true if the specified call sign 'call' has uploaded their
|
// returns true if the specified call sign 'call' has uploaded their
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
@ -35,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}
|
||||||
|
, enabled_ {false}
|
||||||
, id_ {id}
|
, id_ {id}
|
||||||
, version_ {version}
|
, version_ {version}
|
||||||
, revision_ {revision}
|
, revision_ {revision}
|
||||||
@ -79,6 +81,7 @@ public:
|
|||||||
Q_SLOT void host_info_results (QHostInfo);
|
Q_SLOT void host_info_results (QHostInfo);
|
||||||
|
|
||||||
MessageClient * self_;
|
MessageClient * self_;
|
||||||
|
bool enabled_;
|
||||||
QString id_;
|
QString id_;
|
||||||
QString version_;
|
QString version_;
|
||||||
QString revision_;
|
QString revision_;
|
||||||
@ -152,7 +155,7 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||||||
// message format is described in NetworkMessage.hpp
|
// message format is described in NetworkMessage.hpp
|
||||||
//
|
//
|
||||||
NetworkMessage::Reader in {msg};
|
NetworkMessage::Reader in {msg};
|
||||||
if (OK == check_status (in) && id_ == in.id ()) // OK and for us
|
if (OK == check_status (in))
|
||||||
{
|
{
|
||||||
if (schema_ < in.schema ()) // one time record of server's
|
if (schema_ < in.schema ()) // one time record of server's
|
||||||
// negotiated schema
|
// negotiated schema
|
||||||
@ -160,6 +163,12 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||||||
schema_ = in.schema ();
|
schema_ = in.schema ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!enabled_)
|
||||||
|
{
|
||||||
|
TRACE_UDP ("message processing disabled for id:" << in.id ());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// message format is described in NetworkMessage.hpp
|
// message format is described in NetworkMessage.hpp
|
||||||
//
|
//
|
||||||
@ -200,6 +209,15 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NetworkMessage::Close:
|
||||||
|
TRACE_UDP ("Close");
|
||||||
|
if (check_status (in) != Fail)
|
||||||
|
{
|
||||||
|
last_message_.clear ();
|
||||||
|
Q_EMIT self_->close ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NetworkMessage::Replay:
|
case NetworkMessage::Replay:
|
||||||
TRACE_UDP ("Replay");
|
TRACE_UDP ("Replay");
|
||||||
if (check_status (in) != Fail)
|
if (check_status (in) != Fail)
|
||||||
@ -261,6 +279,42 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NetworkMessage::SwitchConfiguration:
|
||||||
|
{
|
||||||
|
QByteArray configuration_name;
|
||||||
|
in >> configuration_name;
|
||||||
|
TRACE_UDP ("Switch Configuration name:" << configuration_name);
|
||||||
|
if (check_status (in) != Fail)
|
||||||
|
{
|
||||||
|
Q_EMIT self_->switch_configuration (QString::fromUtf8 (configuration_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NetworkMessage::Configure:
|
||||||
|
{
|
||||||
|
QByteArray mode;
|
||||||
|
quint32 frequency_tolerance;
|
||||||
|
QByteArray submode;
|
||||||
|
bool fast_mode {false};
|
||||||
|
quint32 tr_period {std::numeric_limits<quint32>::max ()};
|
||||||
|
quint32 rx_df {std::numeric_limits<quint32>::max ()};
|
||||||
|
QByteArray dx_call;
|
||||||
|
QByteArray dx_grid;
|
||||||
|
bool generate_messages {false};
|
||||||
|
in >> mode >> frequency_tolerance >> submode >> fast_mode >> tr_period >> rx_df
|
||||||
|
>> dx_call >> dx_grid >> generate_messages;
|
||||||
|
TRACE_UDP ("Configure mode:" << mode << "frequency tolerance:" << frequency_tolerance << "submode:" << submode << "fast mode:" << fast_mode << "T/R period:" << tr_period << "rx df:" << rx_df << "dx call:" << dx_call << "dx grid:" << dx_grid << "generate messages:" << generate_messages);
|
||||||
|
if (check_status (in) != Fail)
|
||||||
|
{
|
||||||
|
Q_EMIT self_->configure (QString::fromUtf8 (mode), frequency_tolerance
|
||||||
|
, QString::fromUtf8 (submode), fast_mode, tr_period, rx_df
|
||||||
|
, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
|
||||||
|
, generate_messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Ignore
|
// Ignore
|
||||||
//
|
//
|
||||||
@ -438,13 +492,20 @@ void MessageClient::add_blocked_destination (QHostAddress const& a)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageClient::enable (bool flag)
|
||||||
|
{
|
||||||
|
m_->enabled_ = flag;
|
||||||
|
}
|
||||||
|
|
||||||
void MessageClient::status_update (Frequency f, QString const& mode, QString const& dx_call
|
void MessageClient::status_update (Frequency f, QString const& mode, QString const& dx_call
|
||||||
, QString const& report, QString const& tx_mode
|
, QString const& report, QString const& tx_mode
|
||||||
, bool tx_enabled, bool transmitting, bool decoding
|
, bool tx_enabled, bool transmitting, bool decoding
|
||||||
, qint32 rx_df, qint32 tx_df, QString const& de_call
|
, quint32 rx_df, quint32 tx_df, QString const& de_call
|
||||||
, QString const& de_grid, QString const& dx_grid
|
, QString const& de_grid, QString const& dx_grid
|
||||||
, bool watchdog_timeout, QString const& sub_mode
|
, bool watchdog_timeout, QString const& sub_mode
|
||||||
, bool fast_mode, quint8 special_op_mode)
|
, bool fast_mode, quint8 special_op_mode
|
||||||
|
, quint32 frequency_tolerance, quint32 tr_period
|
||||||
|
, QString const& configuration_name)
|
||||||
{
|
{
|
||||||
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
if (m_->server_port_ && !m_->server_string_.isEmpty ())
|
||||||
{
|
{
|
||||||
@ -453,8 +514,8 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con
|
|||||||
out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 ()
|
out << f << mode.toUtf8 () << dx_call.toUtf8 () << report.toUtf8 () << tx_mode.toUtf8 ()
|
||||||
<< tx_enabled << transmitting << decoding << rx_df << tx_df << de_call.toUtf8 ()
|
<< tx_enabled << transmitting << decoding << rx_df << tx_df << de_call.toUtf8 ()
|
||||||
<< de_grid.toUtf8 () << dx_grid.toUtf8 () << watchdog_timeout << sub_mode.toUtf8 ()
|
<< de_grid.toUtf8 () << dx_grid.toUtf8 () << watchdog_timeout << sub_mode.toUtf8 ()
|
||||||
<< fast_mode << special_op_mode;
|
<< fast_mode << special_op_mode << frequency_tolerance << tr_period << configuration_name.toUtf8 ();
|
||||||
TRACE_UDP ("frequency:" << f << "mode:" << mode << "DX:" << dx_call << "report:" << report << "Tx mode:" << tx_mode << "tx_enabled:" << tx_enabled << "Tx:" << transmitting << "decoding:" << decoding << "Rx df:" << rx_df << "Tx df:" << tx_df << "DE:" << de_call << "DE grid:" << de_grid << "DX grid:" << dx_grid << "w/d t/o:" << watchdog_timeout << "sub_mode:" << sub_mode << "fast mode:" << fast_mode << "spec op mode:" << special_op_mode);
|
TRACE_UDP ("frequency:" << f << "mode:" << mode << "DX:" << dx_call << "report:" << report << "Tx mode:" << tx_mode << "tx_enabled:" << tx_enabled << "Tx:" << transmitting << "decoding:" << decoding << "Rx df:" << rx_df << "Tx df:" << tx_df << "DE:" << de_call << "DE grid:" << de_grid << "DX grid:" << dx_grid << "w/d t/o:" << watchdog_timeout << "sub_mode:" << sub_mode << "fast mode:" << fast_mode << "spec op mode:" << special_op_mode << "frequency tolerance:" << frequency_tolerance << "T/R period:" << tr_period << "configuration name:" << configuration_name);
|
||||||
m_->send_message (out, message);
|
m_->send_message (out, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,7 +588,7 @@ void MessageClient::logged_ADIF (QByteArray const& ADIF_record)
|
|||||||
{
|
{
|
||||||
QByteArray message;
|
QByteArray message;
|
||||||
NetworkMessage::Builder out {&message, NetworkMessage::LoggedADIF, m_->id_, m_->schema_};
|
NetworkMessage::Builder out {&message, NetworkMessage::LoggedADIF, m_->id_, m_->schema_};
|
||||||
QByteArray ADIF {"\n<adif_ver:5>3.0.7\n<programid:6>WSJT-X\n<EOH>\n" + ADIF_record + " <EOR>"};
|
QByteArray ADIF {"\n<adif_ver:5>3.1.0\n<programid:6>WSJT-X\n<EOH>\n" + ADIF_record + " <EOR>"};
|
||||||
out << ADIF;
|
out << ADIF;
|
||||||
TRACE_UDP ("ADIF:" << ADIF);
|
TRACE_UDP ("ADIF:" << ADIF);
|
||||||
m_->send_message (out, message);
|
m_->send_message (out, message);
|
||||||
|
@ -47,12 +47,16 @@ public:
|
|||||||
// change the server port messages are sent to
|
// change the server port messages are sent to
|
||||||
Q_SLOT void set_server_port (port_type server_port = 0u);
|
Q_SLOT void set_server_port (port_type server_port = 0u);
|
||||||
|
|
||||||
|
// enable incoming messages
|
||||||
|
Q_SLOT void enable (bool);
|
||||||
|
|
||||||
// outgoing messages
|
// outgoing messages
|
||||||
Q_SLOT void status_update (Frequency, QString const& mode, QString const& dx_call, QString const& report
|
Q_SLOT void status_update (Frequency, QString const& mode, QString const& dx_call, QString const& report
|
||||||
, QString const& tx_mode, bool tx_enabled, bool transmitting, bool decoding
|
, QString const& tx_mode, bool tx_enabled, bool transmitting, bool decoding
|
||||||
, qint32 rx_df, qint32 tx_df, QString const& de_call, QString const& de_grid
|
, quint32 rx_df, quint32 tx_df, QString const& de_call, QString const& de_grid
|
||||||
, QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode
|
, QString const& dx_grid, bool watchdog_timeout, QString const& sub_mode
|
||||||
, bool fast_mode, quint8 special_op_mode);
|
, bool fast_mode, quint8 special_op_mode, quint32 frequency_tolerance
|
||||||
|
, quint32 tr_period, QString const& configuration_name);
|
||||||
Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
|
Q_SLOT void decode (bool is_new, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
|
||||||
, QString const& mode, QString const& message, bool low_confidence
|
, QString const& mode, QString const& message, bool low_confidence
|
||||||
, bool off_air);
|
, bool off_air);
|
||||||
@ -89,6 +93,10 @@ public:
|
|||||||
Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode
|
Q_SIGNAL void reply (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode
|
||||||
, QString const& message_text, bool low_confidence, quint8 modifiers);
|
, QString const& message_text, bool low_confidence, quint8 modifiers);
|
||||||
|
|
||||||
|
// this signal is emitted if the server has requested this client to
|
||||||
|
// close down gracefully
|
||||||
|
Q_SIGNAL void close ();
|
||||||
|
|
||||||
// this signal is emitted if the server has requested a replay of
|
// this signal is emitted if the server has requested a replay of
|
||||||
// all decodes
|
// all decodes
|
||||||
Q_SIGNAL void replay ();
|
Q_SIGNAL void replay ();
|
||||||
@ -105,6 +113,16 @@ public:
|
|||||||
// callsign request for the specified call
|
// callsign request for the specified call
|
||||||
Q_SIGNAL void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only);
|
Q_SIGNAL void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only);
|
||||||
|
|
||||||
|
// this signal is emitted if the server has requested a
|
||||||
|
// configuration switch
|
||||||
|
Q_SIGNAL void switch_configuration (QString const& configuration_name);
|
||||||
|
|
||||||
|
// this signal is emitted if the server has requested a
|
||||||
|
// configuration change
|
||||||
|
Q_SIGNAL void configure (QString const& mode, quint32 frequency_tolerance, QString const& submode
|
||||||
|
, bool fast_mode, quint32 tr_period, quint32 rx_df, QString const& dx_call
|
||||||
|
, QString const& dx_grid, bool generate_messages);
|
||||||
|
|
||||||
// this signal is emitted when network errors occur or if a host
|
// this signal is emitted when network errors occur or if a host
|
||||||
// lookup fails
|
// lookup fails
|
||||||
Q_SIGNAL void error (QString const&) const;
|
Q_SIGNAL void error (QString const&) const;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "MessageServer.hpp"
|
#include "MessageServer.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
@ -16,6 +17,11 @@
|
|||||||
|
|
||||||
#include "moc_MessageServer.cpp"
|
#include "moc_MessageServer.cpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
auto quint32_max = std::numeric_limits<quint32>::max ();
|
||||||
|
}
|
||||||
|
|
||||||
class MessageServer::impl
|
class MessageServer::impl
|
||||||
: public QUdpSocket
|
: public QUdpSocket
|
||||||
{
|
{
|
||||||
@ -238,8 +244,8 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
|
|||||||
bool tx_enabled {false};
|
bool tx_enabled {false};
|
||||||
bool transmitting {false};
|
bool transmitting {false};
|
||||||
bool decoding {false};
|
bool decoding {false};
|
||||||
qint32 rx_df {-1};
|
quint32 rx_df {quint32_max};
|
||||||
qint32 tx_df {-1};
|
quint32 tx_df {quint32_max};
|
||||||
QByteArray de_call;
|
QByteArray de_call;
|
||||||
QByteArray de_grid;
|
QByteArray de_grid;
|
||||||
QByteArray dx_grid;
|
QByteArray dx_grid;
|
||||||
@ -247,9 +253,12 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
|
|||||||
QByteArray sub_mode;
|
QByteArray sub_mode;
|
||||||
bool fast_mode {false};
|
bool fast_mode {false};
|
||||||
quint8 special_op_mode {0};
|
quint8 special_op_mode {0};
|
||||||
|
quint32 frequency_tolerance {quint32_max};
|
||||||
|
quint32 tr_period {quint32_max};
|
||||||
|
QByteArray configuration_name;
|
||||||
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
|
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
|
||||||
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
|
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
|
||||||
>> fast_mode >> special_op_mode;
|
>> fast_mode >> special_op_mode >> frequency_tolerance >> tr_period >> configuration_name;
|
||||||
if (check_status (in) != Fail)
|
if (check_status (in) != Fail)
|
||||||
{
|
{
|
||||||
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
|
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
|
||||||
@ -258,7 +267,8 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
|
|||||||
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
|
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
|
||||||
, QString::fromUtf8 (dx_grid), watchdog_timeout
|
, QString::fromUtf8 (dx_grid), watchdog_timeout
|
||||||
, QString::fromUtf8 (sub_mode), fast_mode
|
, QString::fromUtf8 (sub_mode), fast_mode
|
||||||
, special_op_mode);
|
, special_op_mode, frequency_tolerance, tr_period
|
||||||
|
, QString::fromUtf8 (configuration_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -493,6 +503,17 @@ void MessageServer::replay (QString const& id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageServer::close (QString const& id)
|
||||||
|
{
|
||||||
|
auto iter = m_->clients_.find (id);
|
||||||
|
if (iter != std::end (m_->clients_))
|
||||||
|
{
|
||||||
|
QByteArray message;
|
||||||
|
NetworkMessage::Builder out {&message, NetworkMessage::Close, id, (*iter).negotiated_schema_number_};
|
||||||
|
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MessageServer::halt_tx (QString const& id, bool auto_only)
|
void MessageServer::halt_tx (QString const& id, bool auto_only)
|
||||||
{
|
{
|
||||||
auto iter = m_->clients_.find (id);
|
auto iter = m_->clients_.find (id);
|
||||||
@ -541,3 +562,30 @@ void MessageServer::highlight_callsign (QString const& id, QString const& callsi
|
|||||||
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageServer::switch_configuration (QString const& id, QString const& configuration_name)
|
||||||
|
{
|
||||||
|
auto iter = m_->clients_.find (id);
|
||||||
|
if (iter != std::end (m_->clients_))
|
||||||
|
{
|
||||||
|
QByteArray message;
|
||||||
|
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, id, (*iter).negotiated_schema_number_};
|
||||||
|
out << configuration_name.toUtf8 ();
|
||||||
|
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageServer::configure (QString const& id, QString const& mode, quint32 frequency_tolerance
|
||||||
|
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
|
||||||
|
, QString const& dx_call, QString const& dx_grid, bool generate_messages)
|
||||||
|
{
|
||||||
|
auto iter = m_->clients_.find (id);
|
||||||
|
if (iter != std::end (m_->clients_))
|
||||||
|
{
|
||||||
|
QByteArray message;
|
||||||
|
NetworkMessage::Builder out {&message, NetworkMessage::Configure, id, (*iter).negotiated_schema_number_};
|
||||||
|
out << mode.toUtf8 () << frequency_tolerance << submode.toUtf8 () << fast_mode << tr_period << rx_df
|
||||||
|
<< dx_call.toUtf8 () << dx_grid.toUtf8 () << generate_messages;
|
||||||
|
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -52,6 +52,9 @@ public:
|
|||||||
Q_SLOT void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
|
Q_SLOT void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
|
||||||
, QString const& mode, QString const& message, bool low_confidence, quint8 modifiers);
|
, QString const& mode, QString const& message, bool low_confidence, quint8 modifiers);
|
||||||
|
|
||||||
|
// ask the client with identification 'id' to close down gracefully
|
||||||
|
Q_SLOT void close (QString const& id);
|
||||||
|
|
||||||
// ask the client with identification 'id' to replay all decodes
|
// ask the client with identification 'id' to replay all decodes
|
||||||
Q_SLOT void replay (QString const& id);
|
Q_SLOT void replay (QString const& id);
|
||||||
|
|
||||||
@ -72,15 +75,25 @@ public:
|
|||||||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||||
, bool last_only = false);
|
, bool last_only = false);
|
||||||
|
|
||||||
|
// ask the client with identification 'id' to switch to
|
||||||
|
// configuration 'configuration_name'
|
||||||
|
Q_SLOT void switch_configuration (QString const& id, QString const& configuration_name);
|
||||||
|
|
||||||
|
// ask the client with identification 'id' to change configuration
|
||||||
|
Q_SLOT void configure (QString const& id, QString const& mode, quint32 frequency_tolerance
|
||||||
|
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
|
||||||
|
, QString const& dx_call, QString const& dx_grid, bool generate_messages);
|
||||||
|
|
||||||
// the following signals are emitted when a client broadcasts the
|
// the following signals are emitted when a client broadcasts the
|
||||||
// matching message
|
// matching message
|
||||||
Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision);
|
Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision);
|
||||||
Q_SIGNAL void status_update (QString const& id, Frequency, QString const& mode, QString const& dx_call
|
Q_SIGNAL void status_update (QString const& id, Frequency, QString const& mode, QString const& dx_call
|
||||||
, QString const& report, QString const& tx_mode, bool tx_enabled
|
, QString const& report, QString const& tx_mode, bool tx_enabled
|
||||||
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
|
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
|
||||||
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
||||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
||||||
, quint8 special_op_mode);
|
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
|
||||||
|
, QString const& configuration_name);
|
||||||
Q_SIGNAL void client_closed (QString const& id);
|
Q_SIGNAL void client_closed (QString const& id);
|
||||||
Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time
|
Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time
|
||||||
, quint32 delta_frequency, QString const& mode, QString const& message
|
, quint32 delta_frequency, QString const& mode, QString const& message
|
||||||
|
@ -14,16 +14,35 @@
|
|||||||
#include "WFPalette.hpp"
|
#include "WFPalette.hpp"
|
||||||
#include "models/IARURegions.hpp"
|
#include "models/IARURegions.hpp"
|
||||||
#include "models/DecodeHighlightingModel.hpp"
|
#include "models/DecodeHighlightingModel.hpp"
|
||||||
#include "widgets/FrequencyLineEdit.hpp"
|
#include "widgets/DateTimeEdit.hpp"
|
||||||
|
|
||||||
QItemEditorFactory * item_editor_factory ()
|
namespace
|
||||||
{
|
{
|
||||||
static QItemEditorFactory * our_item_editor_factory = new QItemEditorFactory;
|
class ItemEditorFactory final
|
||||||
return our_item_editor_factory;
|
: public QItemEditorFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ItemEditorFactory ()
|
||||||
|
: default_factory_ {QItemEditorFactory::defaultFactory ()}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget * createEditor (int user_type, QWidget * parent) const override
|
||||||
|
{
|
||||||
|
auto editor = QItemEditorFactory::createEditor (user_type, parent);
|
||||||
|
return editor ? editor : default_factory_->createEditor (user_type, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QItemEditorFactory const * default_factory_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_types ()
|
void register_types ()
|
||||||
{
|
{
|
||||||
|
auto item_editor_factory = new ItemEditorFactory;
|
||||||
|
QItemEditorFactory::setDefaultFactory (item_editor_factory);
|
||||||
|
|
||||||
// types in Radio.hpp are registered in their own translation unit
|
// types in Radio.hpp are registered in their own translation unit
|
||||||
// as they are needed in the wsjtx_udp shared library too
|
// as they are needed in the wsjtx_udp shared library too
|
||||||
|
|
||||||
@ -31,9 +50,7 @@ void register_types ()
|
|||||||
// used as signal/slot connection arguments since the new Qt 5.5
|
// used as signal/slot connection arguments since the new Qt 5.5
|
||||||
// Q_ENUM macro only seems to register the unqualified name
|
// Q_ENUM macro only seems to register the unqualified name
|
||||||
|
|
||||||
item_editor_factory ()->registerEditor (qMetaTypeId<Radio::Frequency> (), new QStandardItemEditorCreator<FrequencyLineEdit> ());
|
item_editor_factory->registerEditor (qMetaTypeId<QDateTime> (), new QStandardItemEditorCreator<DateTimeEdit> ());
|
||||||
//auto frequency_delta_type_id = qRegisterMetaType<Radio::FrequencyDelta> ("FrequencyDelta");
|
|
||||||
item_editor_factory ()->registerEditor (qMetaTypeId<Radio::FrequencyDelta> (), new QStandardItemEditorCreator<FrequencyDeltaLineEdit> ());
|
|
||||||
|
|
||||||
// Frequency list model
|
// Frequency list model
|
||||||
qRegisterMetaTypeStreamOperators<FrequencyList_v2::Item> ("Item_v2");
|
qRegisterMetaTypeStreamOperators<FrequencyList_v2::Item> ("Item_v2");
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
#ifndef META_DATA_REGISTRY_HPP__
|
#ifndef META_DATA_REGISTRY_HPP__
|
||||||
#define META_DATA_REGISTRY_HPP__
|
#define META_DATA_REGISTRY_HPP__
|
||||||
|
|
||||||
class QItemEditorFactory;
|
|
||||||
|
|
||||||
QItemEditorFactory * item_editor_factory ();
|
|
||||||
void register_types ();
|
void register_types ();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -25,15 +25,15 @@ double constexpr Modulator::m_twoPi;
|
|||||||
// unsigned m_nspd=1.2*48000.0/wpm;
|
// unsigned m_nspd=1.2*48000.0/wpm;
|
||||||
// m_nspd=3072; //18.75 WPM
|
// m_nspd=3072; //18.75 WPM
|
||||||
|
|
||||||
Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
|
Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds,
|
||||||
QObject * parent)
|
QObject * parent)
|
||||||
: AudioDevice {parent}
|
: AudioDevice {parent}
|
||||||
, m_quickClose {false}
|
, m_quickClose {false}
|
||||||
, m_phi {0.0}
|
, m_phi {0.0}
|
||||||
, m_toneSpacing {0.0}
|
, m_toneSpacing {0.0}
|
||||||
, m_fSpread {0.0}
|
, m_fSpread {0.0}
|
||||||
, m_frameRate {frameRate}
|
|
||||||
, m_period {periodLengthInSeconds}
|
, m_period {periodLengthInSeconds}
|
||||||
|
, m_frameRate {frameRate}
|
||||||
, m_state {Idle}
|
, m_state {Idle}
|
||||||
, m_tuning {false}
|
, m_tuning {false}
|
||||||
, m_cwLevel {false}
|
, m_cwLevel {false}
|
||||||
@ -45,19 +45,15 @@ Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
|
|||||||
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
void Modulator::start (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, int TRperiod)
|
bool synchronize, bool fastMode, double dBSNR, double 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;
|
||||||
|
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
|
||||||
|
|
||||||
if (m_state != Idle)
|
if(m_state != Idle) stop();
|
||||||
{
|
|
||||||
stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_quickClose = false;
|
m_quickClose = false;
|
||||||
|
|
||||||
m_symbolsLength = symbolsLength;
|
m_symbolsLength = symbolsLength;
|
||||||
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
|
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
|
||||||
m_frequency0 = 0.;
|
m_frequency0 = 0.;
|
||||||
@ -69,7 +65,9 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||||||
m_toneSpacing = toneSpacing;
|
m_toneSpacing = toneSpacing;
|
||||||
m_bFastMode=fastMode;
|
m_bFastMode=fastMode;
|
||||||
m_TRperiod=TRperiod;
|
m_TRperiod=TRperiod;
|
||||||
unsigned delay_ms = 1920 == m_nsps && 15 == m_period ? 500 : 1000;
|
unsigned delay_ms=1000;
|
||||||
|
if(m_nsps==1920) delay_ms=500; //FT8
|
||||||
|
if(m_nsps==576) delay_ms=300; //FT4
|
||||||
|
|
||||||
// noise generator parameters
|
// noise generator parameters
|
||||||
if (m_addNoise) {
|
if (m_addNoise) {
|
||||||
@ -78,10 +76,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned mstr = ms0 % (1000 * m_period); // ms in period
|
// round up to an exact portion of a second that allows for startup delays
|
||||||
|
|
||||||
// round up to an exact portion of a second that allows for startup
|
|
||||||
// delays
|
|
||||||
m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
|
m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
|
||||||
|
|
||||||
if(m_bFastMode) m_ic=0;
|
if(m_bFastMode) m_ic=0;
|
||||||
@ -93,6 +88,11 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||||||
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
|
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
|
||||||
|
// << m_ic << m_silentFrames << m_silentFrames/48000.0
|
||||||
|
// << mstr << fmod(double(ms0),1000.0*m_period);
|
||||||
|
|
||||||
|
|
||||||
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));
|
||||||
@ -149,6 +149,8 @@ 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));
|
||||||
|
|
||||||
switch (m_state)
|
switch (m_state)
|
||||||
{
|
{
|
||||||
case Synchronizing:
|
case Synchronizing:
|
||||||
@ -170,17 +172,19 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
case Active:
|
case Active:
|
||||||
{
|
{
|
||||||
unsigned int isym=0;
|
unsigned int isym=0;
|
||||||
// qDebug() << "Mod A" << m_toneSpacing << m_ic;
|
|
||||||
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
|
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
|
||||||
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
|
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
|
||||||
if(m_TRperiod==3) slowCwId=false;
|
if(m_TRperiod==3.0) slowCwId=false;
|
||||||
bool fastCwId=false;
|
bool fastCwId=false;
|
||||||
static bool bCwId=false;
|
static bool bCwId=false;
|
||||||
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
||||||
float tsec=0.001*(ms % (1000*m_TRperiod));
|
float tsec=0.001*(ms % int(1000*m_TRperiod));
|
||||||
if(m_bFastMode and (icw[0]>0) and (tsec > (m_TRperiod-5.0))) fastCwId=true;
|
if(m_bFastMode and (icw[0]>0) and (tsec > (m_TRperiod-5.0))) fastCwId=true;
|
||||||
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
|
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
|
||||||
|
|
||||||
|
// qDebug() << "Mod A" << m_ic << isym << tsec;
|
||||||
|
|
||||||
if(slowCwId or fastCwId) { // Transmit CW ID?
|
if(slowCwId or fastCwId) { // Transmit CW ID?
|
||||||
m_dphi = m_twoPi*m_frequency/m_frameRate;
|
m_dphi = m_twoPi*m_frequency/m_frameRate;
|
||||||
if(m_bFastMode and !bCwId) {
|
if(m_bFastMode and !bCwId) {
|
||||||
@ -247,15 +251,15 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
i1= m_symbolsLength * 4.0 * m_nsps;
|
i1= m_symbolsLength * 4.0 * m_nsps;
|
||||||
}
|
}
|
||||||
if(m_bFastMode and !m_tuning) {
|
if(m_bFastMode and !m_tuning) {
|
||||||
i1=m_TRperiod*48000 - 24000;
|
i1=m_TRperiod*48000.0 - 24000.0;
|
||||||
i0=i1-816;
|
i0=i1-816;
|
||||||
}
|
}
|
||||||
|
|
||||||
qint16 sample;
|
qint16 sample;
|
||||||
|
|
||||||
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
|
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
|
||||||
isym=0;
|
isym=0;
|
||||||
if(!m_tuning and m_TRperiod!=3) isym=m_ic / (4.0 * m_nsps); //Actual
|
if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000
|
||||||
//fsample=48000
|
|
||||||
if(m_bFastMode) isym=isym%m_symbolsLength;
|
if(m_bFastMode) isym=isym%m_symbolsLength;
|
||||||
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
||||||
if(itone[0]>=100) {
|
if(itone[0]>=100) {
|
||||||
@ -267,8 +271,6 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
|
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// qDebug() << "Mod B" << m_bFastMode << m_ic << numFrames << isym << itone[isym]
|
|
||||||
// << m_toneFrequency0 << m_nsps;
|
|
||||||
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
|
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
|
||||||
m_isym0 = isym;
|
m_isym0 = isym;
|
||||||
m_frequency0 = m_frequency; //???
|
m_frequency0 = m_frequency; //???
|
||||||
@ -289,9 +291,12 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
if (m_ic > i1) m_amp = 0.0;
|
if (m_ic > i1) m_amp = 0.0;
|
||||||
|
|
||||||
sample=qRound(m_amp*qSin(m_phi));
|
sample=qRound(m_amp*qSin(m_phi));
|
||||||
if(m_toneSpacing < 0) sample=qRound(m_amp*foxcom_.wave[m_ic]);
|
|
||||||
|
|
||||||
// if(m_ic < 100) qDebug() << "Mod C" << m_ic << m_amp << foxcom_.wave[m_ic] << sample;
|
//Here's where we transmit from a precomputed wave[] array:
|
||||||
|
if(!m_tuning and (m_toneSpacing < 0)) {
|
||||||
|
m_amp=32767.0;
|
||||||
|
sample=qRound(m_amp*foxcom_.wave[m_ic]);
|
||||||
|
}
|
||||||
|
|
||||||
samples = load(postProcessSample(sample), samples);
|
samples = load(postProcessSample(sample), samples);
|
||||||
++framesGenerated;
|
++framesGenerated;
|
||||||
@ -309,6 +314,14 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
|
|
||||||
m_frequency0 = m_frequency;
|
m_frequency0 = m_frequency;
|
||||||
// done for this chunk - continue on next call
|
// done for this chunk - continue on next call
|
||||||
|
|
||||||
|
// qDebug() << "Mod B" << m_ic << i1 << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod));
|
||||||
|
|
||||||
|
while (samples != end) // pad block with silence
|
||||||
|
{
|
||||||
|
samples = load (0, samples);
|
||||||
|
++framesGenerated;
|
||||||
|
}
|
||||||
return framesGenerated * bytesPerFrame ();
|
return framesGenerated * bytesPerFrame ();
|
||||||
}
|
}
|
||||||
// fall through
|
// fall through
|
||||||
|
@ -23,7 +23,7 @@ class Modulator
|
|||||||
public:
|
public:
|
||||||
enum ModulatorState {Synchronizing, Active, Idle};
|
enum ModulatorState {Synchronizing, Active, Idle};
|
||||||
|
|
||||||
Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent = nullptr);
|
Modulator (unsigned frameRate, double periodLengthInSeconds, QObject * parent = nullptr);
|
||||||
|
|
||||||
void close () override;
|
void close () override;
|
||||||
|
|
||||||
@ -31,13 +31,14 @@ public:
|
|||||||
double frequency () const {return m_frequency;}
|
double frequency () const {return m_frequency;}
|
||||||
bool isActive () const {return m_state != Idle;}
|
bool isActive () const {return m_state != Idle;}
|
||||||
void setSpread(double s) {m_fSpread=s;}
|
void setSpread(double s) {m_fSpread=s;}
|
||||||
void setTRPeriod(unsigned p) {m_period=p;}
|
void setTRPeriod(double p) {m_period=p;}
|
||||||
void set_nsym(int n) {m_symbolsLength=n;}
|
void set_nsym(int n) {m_symbolsLength=n;}
|
||||||
|
void set_ms0(qint64 ms) {m_ms0=ms;}
|
||||||
|
|
||||||
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency,
|
Q_SLOT void start (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., int TRperiod=60);
|
double dBSNR = 99., double TRperiod=60.0);
|
||||||
Q_SLOT void stop (bool quick = false);
|
Q_SLOT void stop (bool quick = false);
|
||||||
Q_SLOT void tune (bool newState = true);
|
Q_SLOT void tune (bool newState = true);
|
||||||
Q_SLOT void setFrequency (double newFrequency) {m_frequency = newFrequency;}
|
Q_SLOT void setFrequency (double newFrequency) {m_frequency = newFrequency;}
|
||||||
@ -71,13 +72,14 @@ private:
|
|||||||
double m_fac;
|
double m_fac;
|
||||||
double m_toneSpacing;
|
double m_toneSpacing;
|
||||||
double m_fSpread;
|
double m_fSpread;
|
||||||
|
double m_TRperiod;
|
||||||
|
double m_period;
|
||||||
|
|
||||||
qint64 m_silentFrames;
|
qint64 m_silentFrames;
|
||||||
qint32 m_TRperiod;
|
qint64 m_ms0;
|
||||||
qint16 m_ramp;
|
qint16 m_ramp;
|
||||||
|
|
||||||
unsigned m_frameRate;
|
unsigned m_frameRate;
|
||||||
unsigned m_period;
|
|
||||||
ModulatorState volatile m_state;
|
ModulatorState volatile m_state;
|
||||||
|
|
||||||
bool volatile m_tuning;
|
bool volatile m_tuning;
|
||||||
|
@ -65,6 +65,8 @@ namespace
|
|||||||
class NameDialog final
|
class NameDialog final
|
||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NameDialog (QString const& current_name,
|
explicit NameDialog (QString const& current_name,
|
||||||
QStringList const& current_names,
|
QStringList const& current_names,
|
||||||
@ -112,6 +114,8 @@ namespace
|
|||||||
class ExistingNameDialog final
|
class ExistingNameDialog final
|
||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ExistingNameDialog (QStringList const& current_names, QWidget * parent = nullptr)
|
explicit ExistingNameDialog (QStringList const& current_names, QWidget * parent = nullptr)
|
||||||
: QDialog {parent}
|
: QDialog {parent}
|
||||||
@ -155,13 +159,16 @@ public:
|
|||||||
bool exit ();
|
bool exit ();
|
||||||
|
|
||||||
QSettings settings_;
|
QSettings settings_;
|
||||||
|
QString current_;
|
||||||
|
|
||||||
|
// switch to this configuration
|
||||||
|
void select_configuration (QString const& target_name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using Dictionary = QMap<QString, QVariant>;
|
using Dictionary = QMap<QString, QVariant>;
|
||||||
|
|
||||||
// create a configuration maintenance sub menu
|
// create a configuration maintenance sub menu
|
||||||
QMenu * create_sub_menu (QMainWindow * main_window,
|
QMenu * create_sub_menu (QMenu * parent,
|
||||||
QMenu * parent,
|
|
||||||
QString const& menu_title,
|
QString const& menu_title,
|
||||||
QActionGroup * = nullptr);
|
QActionGroup * = nullptr);
|
||||||
|
|
||||||
@ -171,32 +178,32 @@ private:
|
|||||||
// write the settings values from the dictionary to the current group
|
// write the settings values from the dictionary to the current group
|
||||||
void load_from (Dictionary const&, bool add_placeholder = true);
|
void load_from (Dictionary const&, bool add_placeholder = true);
|
||||||
|
|
||||||
// switch to this configuration
|
|
||||||
void select_configuration (QMainWindow *, QMenu const *);
|
|
||||||
|
|
||||||
// clone this configuration
|
// clone this configuration
|
||||||
void clone_configuration (QMainWindow * main_window, QMenu *, QMenu const *);
|
void clone_configuration (QMenu *, QMenu const *);
|
||||||
|
|
||||||
// update this configuration from another
|
// update this configuration from another
|
||||||
void clone_into_configuration (QMainWindow *, QMenu const *);
|
void clone_into_configuration (QMenu const *);
|
||||||
|
|
||||||
// reset configuration to default values
|
// reset configuration to default values
|
||||||
void reset_configuration (QMainWindow *, QMenu const *);
|
void reset_configuration (QMenu const *);
|
||||||
|
|
||||||
// change configuration name
|
// change configuration name
|
||||||
void rename_configuration (QMainWindow *, QMenu *);
|
void rename_configuration (QMenu *);
|
||||||
|
|
||||||
// remove a configuration
|
// remove a configuration
|
||||||
void delete_configuration (QMainWindow *, QMenu *);
|
void delete_configuration (QMenu *);
|
||||||
|
|
||||||
|
// action to take on restart
|
||||||
|
enum class RepositionType {unchanged, replace, save_and_replace};
|
||||||
|
void restart (RepositionType);
|
||||||
|
|
||||||
MultiSettings const * parent_; // required for emitting signals
|
MultiSettings const * parent_; // required for emitting signals
|
||||||
|
QMainWindow * main_window_;
|
||||||
bool name_change_emit_pending_; // delayed until menu built
|
bool name_change_emit_pending_; // delayed until menu built
|
||||||
|
|
||||||
QFont original_font_;
|
QFont original_font_;
|
||||||
QString current_;
|
|
||||||
|
|
||||||
// action to take on restart
|
RepositionType reposition_type_;
|
||||||
enum class RepositionType {unchanged, replace, save_and_replace} reposition_type_;
|
|
||||||
Dictionary new_settings_;
|
Dictionary new_settings_;
|
||||||
bool exit_flag_; // false means loop around with new
|
bool exit_flag_; // false means loop around with new
|
||||||
// configuration
|
// configuration
|
||||||
@ -263,6 +270,16 @@ void MultiSettings::create_menu_actions (QMainWindow * main_window, QMenu * menu
|
|||||||
m_->create_menu_actions (main_window, menu);
|
m_->create_menu_actions (main_window, menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiSettings::select_configuration (QString const& name)
|
||||||
|
{
|
||||||
|
m_->select_configuration (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MultiSettings::configuration_name () const
|
||||||
|
{
|
||||||
|
return m_->current_;
|
||||||
|
}
|
||||||
|
|
||||||
bool MultiSettings::exit ()
|
bool MultiSettings::exit ()
|
||||||
{
|
{
|
||||||
return m_->exit ();
|
return m_->exit ();
|
||||||
@ -271,6 +288,7 @@ bool MultiSettings::exit ()
|
|||||||
MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name)
|
MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name)
|
||||||
: settings_ {settings_path (), QSettings::IniFormat}
|
: settings_ {settings_path (), QSettings::IniFormat}
|
||||||
, parent_ {parent}
|
, parent_ {parent}
|
||||||
|
, main_window_ {nullptr}
|
||||||
, name_change_emit_pending_ {true}
|
, name_change_emit_pending_ {true}
|
||||||
, reposition_type_ {RepositionType::unchanged}
|
, reposition_type_ {RepositionType::unchanged}
|
||||||
, exit_flag_ {true}
|
, exit_flag_ {true}
|
||||||
@ -407,13 +425,14 @@ bool MultiSettings::impl::reposition ()
|
|||||||
// and, reset
|
// and, reset
|
||||||
void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu)
|
void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu)
|
||||||
{
|
{
|
||||||
|
main_window_ = main_window;
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
if (current_group.size ()) settings_.endGroup ();
|
if (current_group.size ()) settings_.endGroup ();
|
||||||
SettingsGroup alternatives {&settings_, multi_settings_root_group};
|
SettingsGroup alternatives {&settings_, multi_settings_root_group};
|
||||||
// get the current configuration name
|
// get the current configuration name
|
||||||
auto const& current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
|
auto const& current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
|
||||||
// add the default configuration sub menu
|
// add the default configuration sub menu
|
||||||
QMenu * default_menu = create_sub_menu (main_window, menu, current_configuration_name, configurations_group_);
|
QMenu * default_menu = create_sub_menu (menu, current_configuration_name, configurations_group_);
|
||||||
// and set as the current configuration
|
// and set as the current configuration
|
||||||
default_menu->menuAction ()->setChecked (true);
|
default_menu->menuAction ()->setChecked (true);
|
||||||
|
|
||||||
@ -423,7 +442,7 @@ void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu
|
|||||||
// add all the other configurations
|
// add all the other configurations
|
||||||
for (auto const& configuration_name: available_configurations)
|
for (auto const& configuration_name: available_configurations)
|
||||||
{
|
{
|
||||||
create_sub_menu (main_window, menu, configuration_name, configurations_group_);
|
create_sub_menu (menu, configuration_name, configurations_group_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||||
@ -446,8 +465,7 @@ bool MultiSettings::impl::exit ()
|
|||||||
return reposition ();
|
return reposition ();
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
QMenu * MultiSettings::impl::create_sub_menu (QMenu * parent_menu,
|
||||||
QMenu * parent_menu,
|
|
||||||
QString const& menu_title,
|
QString const& menu_title,
|
||||||
QActionGroup * action_group)
|
QActionGroup * action_group)
|
||||||
{
|
{
|
||||||
@ -456,7 +474,7 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
|||||||
sub_menu->menuAction ()->setCheckable (true);
|
sub_menu->menuAction ()->setCheckable (true);
|
||||||
|
|
||||||
// populate sub-menu actions before showing
|
// populate sub-menu actions before showing
|
||||||
connect (sub_menu, &QMenu::aboutToShow, [this, main_window, parent_menu, sub_menu] () {
|
connect (sub_menu, &QMenu::aboutToShow, [this, parent_menu, sub_menu] () {
|
||||||
// depopulate before populating and showing because on Mac OS X
|
// depopulate before populating and showing because on Mac OS X
|
||||||
// there is an issue with depopulating in QMenu::aboutToHide()
|
// there is an issue with depopulating in QMenu::aboutToHide()
|
||||||
// with connections being disconnected before they are actioned
|
// with connections being disconnected before they are actioned
|
||||||
@ -470,16 +488,16 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
|||||||
{
|
{
|
||||||
auto select_action = new QAction {tr ("&Switch To"), this};
|
auto select_action = new QAction {tr ("&Switch To"), this};
|
||||||
sub_menu->addAction (select_action);
|
sub_menu->addAction (select_action);
|
||||||
connect (select_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
connect (select_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||||
select_configuration (main_window, sub_menu);
|
select_configuration (sub_menu->title ());
|
||||||
});
|
});
|
||||||
sub_menu->addSeparator ();
|
sub_menu->addSeparator ();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto clone_action = new QAction {tr ("&Clone"), this};
|
auto clone_action = new QAction {tr ("&Clone"), this};
|
||||||
sub_menu->addAction (clone_action);
|
sub_menu->addAction (clone_action);
|
||||||
connect (clone_action, &QAction::triggered, [this, main_window, parent_menu, sub_menu] (bool) {
|
connect (clone_action, &QAction::triggered, [this, parent_menu, sub_menu] (bool) {
|
||||||
clone_configuration (main_window, parent_menu, sub_menu);
|
clone_configuration (parent_menu, sub_menu);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
@ -489,29 +507,29 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
|||||||
{
|
{
|
||||||
auto clone_into_action = new QAction {tr ("Clone &Into ..."), this};
|
auto clone_into_action = new QAction {tr ("Clone &Into ..."), this};
|
||||||
sub_menu->addAction (clone_into_action);
|
sub_menu->addAction (clone_into_action);
|
||||||
connect (clone_into_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
connect (clone_into_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||||
clone_into_configuration (main_window, sub_menu);
|
clone_into_configuration (sub_menu);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||||
auto reset_action = new QAction {tr ("R&eset"), this};
|
auto reset_action = new QAction {tr ("R&eset"), this};
|
||||||
sub_menu->addAction (reset_action);
|
sub_menu->addAction (reset_action);
|
||||||
connect (reset_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
connect (reset_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||||
reset_configuration (main_window, sub_menu);
|
reset_configuration (sub_menu);
|
||||||
});
|
});
|
||||||
|
|
||||||
auto rename_action = new QAction {tr ("&Rename ..."), this};
|
auto rename_action = new QAction {tr ("&Rename ..."), this};
|
||||||
sub_menu->addAction (rename_action);
|
sub_menu->addAction (rename_action);
|
||||||
connect (rename_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
connect (rename_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||||
rename_configuration (main_window, sub_menu);
|
rename_configuration (sub_menu);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!is_current)
|
if (!is_current)
|
||||||
{
|
{
|
||||||
auto delete_action = new QAction {tr ("&Delete"), this};
|
auto delete_action = new QAction {tr ("&Delete"), this};
|
||||||
sub_menu->addAction (delete_action);
|
sub_menu->addAction (delete_action);
|
||||||
connect (delete_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
connect (delete_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||||
delete_configuration (main_window, sub_menu);
|
delete_configuration (sub_menu);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -554,11 +572,9 @@ void MultiSettings::impl::load_from (Dictionary const& dictionary, bool add_plac
|
|||||||
settings_.sync ();
|
settings_.sync ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::select_configuration (QMainWindow * main_window, QMenu const * menu)
|
void MultiSettings::impl::select_configuration (QString const& target_name)
|
||||||
{
|
{
|
||||||
auto const& target_name = menu->title ();
|
if (main_window_ && target_name != current_)
|
||||||
|
|
||||||
if (target_name != current_)
|
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
@ -573,13 +589,11 @@ void MultiSettings::impl::select_configuration (QMainWindow * main_window, QMenu
|
|||||||
// and set up the restart
|
// and set up the restart
|
||||||
current_ = target_name;
|
current_ = target_name;
|
||||||
Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
|
Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
|
||||||
reposition_type_ = RepositionType::save_and_replace;
|
restart (RepositionType::save_and_replace);
|
||||||
exit_flag_ = false;
|
|
||||||
main_window->close ();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::clone_configuration (QMainWindow * main_window, QMenu * parent_menu, QMenu const * menu)
|
void MultiSettings::impl::clone_configuration (QMenu * parent_menu, QMenu const * menu)
|
||||||
{
|
{
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
if (current_group.size ()) settings_.endGroup ();
|
if (current_group.size ()) settings_.endGroup ();
|
||||||
@ -612,12 +626,15 @@ void MultiSettings::impl::clone_configuration (QMainWindow * main_window, QMenu
|
|||||||
load_from (source_settings);
|
load_from (source_settings);
|
||||||
|
|
||||||
// insert the new configuration sub menu in the parent menu
|
// insert the new configuration sub menu in the parent menu
|
||||||
create_sub_menu (main_window, parent_menu, new_name, configurations_group_);
|
create_sub_menu (parent_menu, new_name, configurations_group_);
|
||||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, QMenu const * menu)
|
void MultiSettings::impl::clone_into_configuration (QMenu const * menu)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT (main_window_);
|
||||||
|
if (!main_window_) return;
|
||||||
|
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
if (current_group.size ()) settings_.endGroup ();
|
if (current_group.size ()) settings_.endGroup ();
|
||||||
auto const& target_name = menu->title ();
|
auto const& target_name = menu->title ();
|
||||||
@ -638,11 +655,12 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pick a source configuration
|
// pick a source configuration
|
||||||
ExistingNameDialog dialog {sources, main_window};
|
ExistingNameDialog dialog {sources, main_window_};
|
||||||
if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ()))
|
if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ()))
|
||||||
{
|
{
|
||||||
QString source_name {1 == sources.size () ? sources.at (0) : dialog.name ()};
|
QString source_name {1 == sources.size () ? sources.at (0) : dialog.name ()};
|
||||||
if (MessageBox::Yes == MessageBox::query_message (main_window,
|
if (main_window_
|
||||||
|
&& MessageBox::Yes == MessageBox::query_message (main_window_,
|
||||||
tr ("Clone Into Configuration"),
|
tr ("Clone Into Configuration"),
|
||||||
tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
|
tr ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
|
||||||
.arg (unescape_ampersands (target_name))
|
.arg (unescape_ampersands (target_name))
|
||||||
@ -665,9 +683,7 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||||||
if (target_name == current_)
|
if (target_name == current_)
|
||||||
{
|
{
|
||||||
// restart with new settings
|
// restart with new settings
|
||||||
reposition_type_ = RepositionType::replace;
|
restart (RepositionType::replace);
|
||||||
exit_flag_ = false;
|
|
||||||
main_window->close ();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -682,11 +698,15 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu const * menu)
|
void MultiSettings::impl::reset_configuration (QMenu const * menu)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT (main_window_);
|
||||||
|
if (!main_window_) return;
|
||||||
|
|
||||||
auto const& target_name = menu->title ();
|
auto const& target_name = menu->title ();
|
||||||
|
|
||||||
if (MessageBox::Yes != MessageBox::query_message (main_window,
|
if (!main_window_
|
||||||
|
|| MessageBox::Yes != MessageBox::query_message (main_window_,
|
||||||
tr ("Reset Configuration"),
|
tr ("Reset Configuration"),
|
||||||
tr ("Confirm reset to default values for configuration \"%1\"?")
|
tr ("Confirm reset to default values for configuration \"%1\"?")
|
||||||
.arg (unescape_ampersands (target_name))))
|
.arg (unescape_ampersands (target_name))))
|
||||||
@ -697,10 +717,8 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu
|
|||||||
if (target_name == current_)
|
if (target_name == current_)
|
||||||
{
|
{
|
||||||
// restart with default settings
|
// restart with default settings
|
||||||
reposition_type_ = RepositionType::replace;
|
|
||||||
new_settings_.clear ();
|
new_settings_.clear ();
|
||||||
exit_flag_ = false;
|
restart (RepositionType::replace);
|
||||||
main_window->close ();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -717,8 +735,11 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu * menu)
|
void MultiSettings::impl::rename_configuration (QMenu * menu)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT (main_window_);
|
||||||
|
if (!main_window_) return;
|
||||||
|
|
||||||
auto const& current_group = settings_.group ();
|
auto const& current_group = settings_.group ();
|
||||||
if (current_group.size ()) settings_.endGroup ();
|
if (current_group.size ()) settings_.endGroup ();
|
||||||
auto const& target_name = menu->title ();
|
auto const& target_name = menu->title ();
|
||||||
@ -729,7 +750,7 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu
|
|||||||
invalid_names << settings_.value (multi_settings_current_name_key).toString ();
|
invalid_names << settings_.value (multi_settings_current_name_key).toString ();
|
||||||
|
|
||||||
// get the new name
|
// get the new name
|
||||||
NameDialog dialog {target_name, invalid_names, main_window};
|
NameDialog dialog {target_name, invalid_names, main_window_};
|
||||||
if (QDialog::Accepted == dialog.exec ())
|
if (QDialog::Accepted == dialog.exec ())
|
||||||
{
|
{
|
||||||
if (target_name == current_)
|
if (target_name == current_)
|
||||||
@ -760,8 +781,9 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu
|
|||||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu * menu)
|
void MultiSettings::impl::delete_configuration (QMenu * menu)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT (main_window_);
|
||||||
auto const& target_name = menu->title ();
|
auto const& target_name = menu->title ();
|
||||||
|
|
||||||
if (target_name == current_)
|
if (target_name == current_)
|
||||||
@ -770,7 +792,8 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (MessageBox::Yes != MessageBox::query_message (main_window,
|
if (!main_window_
|
||||||
|
|| MessageBox::Yes != MessageBox::query_message (main_window_,
|
||||||
tr ("Delete Configuration"),
|
tr ("Delete Configuration"),
|
||||||
tr ("Confirm deletion of configuration \"%1\"?")
|
tr ("Confirm deletion of configuration \"%1\"?")
|
||||||
.arg (unescape_ampersands (target_name))))
|
.arg (unescape_ampersands (target_name))))
|
||||||
@ -789,3 +812,12 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
|
|||||||
// update the menu
|
// update the menu
|
||||||
menu->deleteLater ();
|
menu->deleteLater ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiSettings::impl::restart (RepositionType type)
|
||||||
|
{
|
||||||
|
Q_ASSERT (main_window_);
|
||||||
|
reposition_type_ = type;
|
||||||
|
exit_flag_ = false;
|
||||||
|
main_window_->close ();
|
||||||
|
main_window_ = nullptr;
|
||||||
|
}
|
||||||
|
@ -80,6 +80,10 @@ public:
|
|||||||
// action is triggered.
|
// action is triggered.
|
||||||
void create_menu_actions (QMainWindow *, QMenu *);
|
void create_menu_actions (QMainWindow *, QMenu *);
|
||||||
|
|
||||||
|
// switch to this configuration if it exists
|
||||||
|
Q_SLOT void select_configuration (QString const& name);
|
||||||
|
QString configuration_name () const;
|
||||||
|
|
||||||
// Access to the QSettings object instance.
|
// Access to the QSettings object instance.
|
||||||
QSettings * settings ();
|
QSettings * settings ();
|
||||||
|
|
||||||
|
22
NEWS
22
NEWS
@ -13,6 +13,28 @@
|
|||||||
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1
|
||||||
|
July 15, 2019
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1 is a major update that introduces FT4, a new protocol
|
||||||
|
targeted at HF contesting. Other improvements have been made in the
|
||||||
|
following areas:
|
||||||
|
|
||||||
|
- FT8 waveform generated with GMSK, fully backward compatible
|
||||||
|
- user options for waterfall and spectrum display
|
||||||
|
- contest logging
|
||||||
|
- rig control
|
||||||
|
- user interface
|
||||||
|
- UDP messaging for inter-program communication
|
||||||
|
- accessibility
|
||||||
|
|
||||||
|
There are numerous minor enhancements and bug fixes.
|
||||||
|
|
||||||
|
We now provide a separate installation package for 64-bit Windows 7
|
||||||
|
and later, with significant improvements in decoding speed.
|
||||||
|
|
||||||
|
|
||||||
Release: WSJT-X 2.0.1
|
Release: WSJT-X 2.0.1
|
||||||
February 25, 2019
|
February 25, 2019
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -116,15 +116,18 @@
|
|||||||
* Tx Enabled bool
|
* Tx Enabled bool
|
||||||
* Transmitting bool
|
* Transmitting bool
|
||||||
* Decoding bool
|
* Decoding bool
|
||||||
* Rx DF qint32
|
* Rx DF quint32
|
||||||
* Tx DF qint32
|
* Tx DF quint32
|
||||||
* DE call utf8
|
* DE call utf8
|
||||||
* DE grid utf8
|
* DE grid utf8
|
||||||
* DX grid utf8
|
* DX grid utf8
|
||||||
* Tx Watchdog bool
|
* Tx Watchdog bool
|
||||||
* Sub-mode utf8
|
* Sub-mode utf8
|
||||||
* Fast mode bool
|
* Fast mode bool
|
||||||
* Special operation mode quint8
|
* Special Operation Mode quint8
|
||||||
|
* Frequency Tolerance quint32
|
||||||
|
* T/R Period quint32
|
||||||
|
* Configuration Name utf8
|
||||||
*
|
*
|
||||||
* WSJT-X sends this status message when various internal state
|
* WSJT-X sends this status message when various internal state
|
||||||
* changes to allow the server to track the relevant state of each
|
* changes to allow the server to track the relevant state of each
|
||||||
@ -133,19 +136,22 @@
|
|||||||
*
|
*
|
||||||
* Application start up,
|
* Application start up,
|
||||||
* "Enable Tx" button status changes,
|
* "Enable Tx" button status changes,
|
||||||
* Dial frequency changes,
|
* dial frequency changes,
|
||||||
* Changes to the "DX Call" field,
|
* changes to the "DX Call" field,
|
||||||
* Operating mode, sub-mode or fast mode changes,
|
* operating mode, sub-mode or fast mode changes,
|
||||||
* Transmit mode changed (in dual JT9+JT65 mode),
|
* transmit mode changed (in dual JT9+JT65 mode),
|
||||||
* Changes to the "Rpt" spinner,
|
* changes to the "Rpt" spinner,
|
||||||
* After an old decodes replay sequence (see Replay below),
|
* after an old decodes replay sequence (see Replay below),
|
||||||
* When switching between Tx and Rx mode,
|
* when switching between Tx and Rx mode,
|
||||||
* At the start and end of decoding,
|
* at the start and end of decoding,
|
||||||
* When the Rx DF changes,
|
* when the Rx DF changes,
|
||||||
* When the Tx DF changes,
|
* when the Tx DF changes,
|
||||||
* When settings are exited,
|
* when settings are exited,
|
||||||
* When the DX call or grid changes,
|
* when the DX call or grid changes,
|
||||||
* When the Tx watchdog is set or reset.
|
* when the Tx watchdog is set or reset,
|
||||||
|
* when the frequency tolerance is changed,
|
||||||
|
* when the T/R period is changed,
|
||||||
|
* when the configuration name changes.
|
||||||
*
|
*
|
||||||
* The Special operation mode is an enumeration that indicates the
|
* The Special operation mode is an enumeration that indicates the
|
||||||
* setting selected in the WSJT-X "Settings->Advanced->Special
|
* setting selected in the WSJT-X "Settings->Advanced->Special
|
||||||
@ -159,6 +165,10 @@
|
|||||||
* 5 -> FOX
|
* 5 -> FOX
|
||||||
* 6 -> HOUND
|
* 6 -> HOUND
|
||||||
*
|
*
|
||||||
|
* The Frequency Tolerance and T/R period fields may have a value
|
||||||
|
* of the maximum quint32 value which implies the field is not
|
||||||
|
* applicable.
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* Decode Out 2 quint32
|
* Decode Out 2 quint32
|
||||||
* Id (unique key) utf8
|
* Id (unique key) utf8
|
||||||
@ -271,11 +281,12 @@
|
|||||||
* button.
|
* button.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Close Out 6 quint32
|
* Close Out/In 6 quint32
|
||||||
* Id (unique key) utf8
|
* Id (unique key) utf8
|
||||||
*
|
*
|
||||||
* Close is sent by a client immediately prior to it shutting
|
* Close is sent by a client immediately prior to it shutting
|
||||||
* down gracefully.
|
* down gracefully. When sent by a server it requests the target
|
||||||
|
* client to close down gracefully.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Replay In 7 quint32
|
* Replay In 7 quint32
|
||||||
@ -414,6 +425,35 @@
|
|||||||
* the last instance only instead of all instances of the
|
* the last instance only instead of all instances of the
|
||||||
* specified call be highlighted or have it's highlighting
|
* specified call be highlighted or have it's highlighting
|
||||||
* cleared.
|
* cleared.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* SwitchConfiguration In 14 quint32
|
||||||
|
* Id (unique key) utf8
|
||||||
|
* Configuration Name utf8
|
||||||
|
*
|
||||||
|
* The server may send this message at any time. The message
|
||||||
|
* specifies the name of the configuration to switch to. The new
|
||||||
|
* configuration must exist.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Configure In 15 quint32
|
||||||
|
* Id (unique key) utf8
|
||||||
|
* Mode utf8
|
||||||
|
* Frequency Tolerance quint32
|
||||||
|
* Submode utf8
|
||||||
|
* Fast Mode bool
|
||||||
|
* T/R Period quint32
|
||||||
|
* Rx DF quint32
|
||||||
|
* DX Call utf8
|
||||||
|
* DX Grid utf8
|
||||||
|
* Generate Messages bool
|
||||||
|
*
|
||||||
|
* The server may send this message at any time. The message
|
||||||
|
* specifies various configuration options. For utf8 string
|
||||||
|
* fields an empty value implies no change, for the quint32 Rx DF
|
||||||
|
* and Frequency Tolerance fields the maximum quint32 value
|
||||||
|
* implies no change. Invalid or unrecognized values will be
|
||||||
|
* silently ignored.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
@ -443,6 +483,8 @@ namespace NetworkMessage
|
|||||||
Location,
|
Location,
|
||||||
LoggedADIF,
|
LoggedADIF,
|
||||||
HighlightCallsign,
|
HighlightCallsign,
|
||||||
|
SwitchConfiguration,
|
||||||
|
Configure,
|
||||||
maximum_message_type_ // ONLY add new message types
|
maximum_message_type_ // ONLY add new message types
|
||||||
// immediately before here
|
// immediately before here
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QEventLoop>
|
||||||
|
|
||||||
#include "qt_helpers.hpP"
|
#include "qt_helpers.hpP"
|
||||||
|
|
||||||
@ -109,6 +110,15 @@ OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// returns false on time out
|
||||||
|
bool OmniRigTransceiver::await_notification_with_timeout (int timeout)
|
||||||
|
{
|
||||||
|
QEventLoop el;
|
||||||
|
connect (this, &OmniRigTransceiver::notified, &el, [&el] () {el.exit (1);});
|
||||||
|
QTimer::singleShot (timeout, Qt::CoarseTimer, &el, [&el] () {el.exit (0);});
|
||||||
|
return 1 == el.exec (); // wait for notify or timer
|
||||||
|
}
|
||||||
|
|
||||||
int OmniRigTransceiver::do_start ()
|
int OmniRigTransceiver::do_start ()
|
||||||
{
|
{
|
||||||
TRACE_CAT ("OmniRigTransceiver", "starting");
|
TRACE_CAT ("OmniRigTransceiver", "starting");
|
||||||
@ -182,19 +192,26 @@ int OmniRigTransceiver::do_start ()
|
|||||||
.arg (readable_params_, 8, 16, QChar ('0'))
|
.arg (readable_params_, 8, 16, QChar ('0'))
|
||||||
.arg (writable_params_, 8, 16, QChar ('0'))
|
.arg (writable_params_, 8, 16, QChar ('0'))
|
||||||
.arg (rig_number_).toLocal8Bit ());
|
.arg (rig_number_).toLocal8Bit ());
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
offline_timer_.reset (new QTimer);
|
|
||||||
offline_timer_->setSingleShot (true);
|
|
||||||
offline_timer_->setInterval (5 * 1000);
|
|
||||||
connect (&*offline_timer_, &QTimer::timeout, this, &OmniRigTransceiver::timeout_check);
|
|
||||||
|
|
||||||
for (unsigned tries {0}; tries < 10; ++tries)
|
|
||||||
{
|
{
|
||||||
QThread::msleep (100); // wait until OmniRig polls the rig
|
if (OmniRig::ST_ONLINE == rig_->Status ())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await_notification_with_timeout (1000);
|
||||||
|
}
|
||||||
|
if (OmniRig::ST_ONLINE != rig_->Status ())
|
||||||
|
{
|
||||||
|
throw_qstring ("OmniRig: " + rig_->StatusStr ());
|
||||||
|
}
|
||||||
auto f = rig_->GetRxFrequency ();
|
auto f = rig_->GetRxFrequency ();
|
||||||
int resolution {0};
|
for (int i = 0; (f == 0) && (i < 5); ++i)
|
||||||
if (f)
|
|
||||||
{
|
{
|
||||||
|
await_notification_with_timeout (1000);
|
||||||
|
f = rig_->GetRxFrequency ();
|
||||||
|
}
|
||||||
|
update_rx_frequency (f);
|
||||||
|
int resolution {0};
|
||||||
if (OmniRig::PM_UNKNOWN == rig_->Vfo ()
|
if (OmniRig::PM_UNKNOWN == rig_->Vfo ()
|
||||||
&& (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
|
&& (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
|
||||||
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
|
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
|
||||||
@ -203,6 +220,7 @@ int OmniRigTransceiver::do_start ()
|
|||||||
// can't query VFO but can set explicitly
|
// can't query VFO but can set explicitly
|
||||||
rig_->SetVfo (OmniRig::PM_VFOA);
|
rig_->SetVfo (OmniRig::PM_VFOA);
|
||||||
}
|
}
|
||||||
|
f = state ().frequency ();
|
||||||
if (f % 10) return resolution; // 1Hz resolution
|
if (f % 10) return resolution; // 1Hz resolution
|
||||||
auto test_frequency = f - f % 100 + 55;
|
auto test_frequency = f - f % 100 + 55;
|
||||||
if (OmniRig::PM_FREQ & writable_params_)
|
if (OmniRig::PM_FREQ & writable_params_)
|
||||||
@ -221,6 +239,11 @@ int OmniRigTransceiver::do_start ()
|
|||||||
{
|
{
|
||||||
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
|
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
|
||||||
}
|
}
|
||||||
|
if (!await_notification_with_timeout (1000))
|
||||||
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "do_start 1: wait timed out");
|
||||||
|
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
|
||||||
|
}
|
||||||
switch (rig_->GetRxFrequency () - test_frequency)
|
switch (rig_->GetRxFrequency () - test_frequency)
|
||||||
{
|
{
|
||||||
case -5: resolution = -1; break; // 10Hz truncated
|
case -5: resolution = -1; break; // 10Hz truncated
|
||||||
@ -244,6 +267,11 @@ int OmniRigTransceiver::do_start ()
|
|||||||
{
|
{
|
||||||
rig_->SetFreqA (test_frequency);
|
rig_->SetFreqA (test_frequency);
|
||||||
}
|
}
|
||||||
|
if (!await_notification_with_timeout (2000))
|
||||||
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "do_start 2: wait timed out");
|
||||||
|
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
|
||||||
|
}
|
||||||
if (9 == rig_->GetRxFrequency () - test_frequency)
|
if (9 == rig_->GetRxFrequency () - test_frequency)
|
||||||
{
|
{
|
||||||
resolution = 2; // 20Hz rounded
|
resolution = 2; // 20Hz rounded
|
||||||
@ -264,19 +292,9 @@ int OmniRigTransceiver::do_start ()
|
|||||||
update_rx_frequency (f);
|
update_rx_frequency (f);
|
||||||
return resolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
throw_qstring (tr ("OmniRig: Initialization timed out"));
|
|
||||||
return 0; // keep compiler happy
|
|
||||||
}
|
|
||||||
|
|
||||||
void OmniRigTransceiver::do_stop ()
|
void OmniRigTransceiver::do_stop ()
|
||||||
{
|
{
|
||||||
if (offline_timer_)
|
|
||||||
{
|
|
||||||
offline_timer_->stop ();
|
|
||||||
offline_timer_.reset ();
|
|
||||||
}
|
|
||||||
|
|
||||||
QThread::msleep (200); // leave some time for pending
|
QThread::msleep (200); // leave some time for pending
|
||||||
// commands at the server end
|
// commands at the server end
|
||||||
if (port_)
|
if (port_)
|
||||||
@ -300,17 +318,6 @@ void OmniRigTransceiver::do_stop ()
|
|||||||
TRACE_CAT ("OmniRigTransceiver", "stopped");
|
TRACE_CAT ("OmniRigTransceiver", "stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OmniRigTransceiver::do_sync (bool force_signal, bool /*no_poll*/)
|
|
||||||
{
|
|
||||||
// nothing much we can do here, we just have to let OmniRig do its
|
|
||||||
// stuff and its first poll should send us and update that will
|
|
||||||
// trigger a update signal from us. Any attempt to query OmniRig
|
|
||||||
// leads to a whole mess of trouble since its internal state is
|
|
||||||
// garbage until it has done its first rig poll.
|
|
||||||
send_update_signal_ = force_signal;
|
|
||||||
update_complete ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
|
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
|
||||||
{
|
{
|
||||||
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
|
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
|
||||||
@ -319,16 +326,20 @@ void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString
|
|||||||
|
|
||||||
void OmniRigTransceiver::handle_visible_change ()
|
void OmniRigTransceiver::handle_visible_change ()
|
||||||
{
|
{
|
||||||
|
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||||
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
|
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
|
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
|
||||||
{
|
{
|
||||||
|
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "rig type change: rig =" << rig_number);
|
||||||
if (rig_number_ == rig_number)
|
if (rig_number_ == rig_number)
|
||||||
{
|
{
|
||||||
|
if (!rig_ || rig_->isNull ()) return;
|
||||||
readable_params_ = rig_->ReadableParams ();
|
readable_params_ = rig_->ReadableParams ();
|
||||||
writable_params_ = rig_->WriteableParams ();
|
writable_params_ = rig_->WriteableParams ();
|
||||||
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
|
TRACE_CAT ("OmniRigTransceiver", QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
|
||||||
.arg (rig_->RigType ())
|
.arg (rig_->RigType ())
|
||||||
.arg (readable_params_, 8, 16, QChar ('0'))
|
.arg (readable_params_, 8, 16, QChar ('0'))
|
||||||
.arg (writable_params_, 8, 16, QChar ('0'))
|
.arg (writable_params_, 8, 16, QChar ('0'))
|
||||||
@ -338,48 +349,47 @@ void OmniRigTransceiver::handle_rig_type_change (int rig_number)
|
|||||||
|
|
||||||
void OmniRigTransceiver::handle_status_change (int rig_number)
|
void OmniRigTransceiver::handle_status_change (int rig_number)
|
||||||
{
|
{
|
||||||
|
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", QString {"status change for rig %1"}.arg (rig_number).toLocal8Bit ());
|
||||||
if (rig_number_ == rig_number)
|
if (rig_number_ == rig_number)
|
||||||
{
|
{
|
||||||
|
if (!rig_ || rig_->isNull ()) return;
|
||||||
auto const& status = rig_->StatusStr ().toLocal8Bit ();
|
auto const& status = rig_->StatusStr ().toLocal8Bit ();
|
||||||
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig status change: new status for rig %1 = "}.arg (rig_number).toLocal8Bit () << status);
|
TRACE_CAT ("OmniRigTransceiver", "OmniRig status change: new status = " << status);
|
||||||
if (OmniRig::ST_ONLINE != rig_->Status ())
|
if (OmniRig::ST_ONLINE != rig_->Status ())
|
||||||
{
|
{
|
||||||
if (!offline_timer_->isActive ())
|
offline ("Rig went offline");
|
||||||
{
|
|
||||||
offline_timer_->start (); // give OmniRig time to recover
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
offline_timer_->stop ();
|
Q_EMIT notified ();
|
||||||
update_rx_frequency (rig_->GetRxFrequency ());
|
|
||||||
update_complete ();
|
|
||||||
TRACE_CAT ("OmniRigTransceiver", "OmniRig frequency:" << state ().frequency ());
|
|
||||||
}
|
}
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// update_rx_frequency (rig_->GetRxFrequency ());
|
||||||
|
// update_complete ();
|
||||||
|
// TRACE_CAT ("OmniRigTransceiver", "frequency:" << state ().frequency ());
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OmniRigTransceiver::timeout_check ()
|
|
||||||
{
|
|
||||||
offline ("Rig went offline");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
||||||
{
|
{
|
||||||
if (rig_number_ == rig_number)
|
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||||
{
|
TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"}
|
||||||
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig params change: params = 0x%1 for rig %2"}
|
|
||||||
.arg (params, 8, 16, QChar ('0'))
|
.arg (params, 8, 16, QChar ('0'))
|
||||||
.arg (rig_number).toLocal8Bit ()
|
.arg (rig_number).toLocal8Bit ()
|
||||||
<< "state before:" << state ());
|
<< "state before:" << state ());
|
||||||
|
if (rig_number_ == rig_number)
|
||||||
|
{
|
||||||
|
if (!rig_ || rig_->isNull ()) return;
|
||||||
// starting_ = false;
|
// starting_ = false;
|
||||||
TransceiverState old_state {state ()};
|
TransceiverState old_state {state ()};
|
||||||
auto need_frequency = false;
|
auto need_frequency = false;
|
||||||
// state_.online = true; // sometimes we don't get an initial
|
|
||||||
// // OmniRig::ST_ONLINE status change
|
|
||||||
// // event
|
|
||||||
if (params & OmniRig::PM_VFOAA)
|
if (params & OmniRig::PM_VFOAA)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOAA");
|
||||||
update_split (false);
|
update_split (false);
|
||||||
reversed_ = false;
|
reversed_ = false;
|
||||||
update_rx_frequency (rig_->FreqA ());
|
update_rx_frequency (rig_->FreqA ());
|
||||||
@ -387,6 +397,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOAB)
|
if (params & OmniRig::PM_VFOAB)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOAB");
|
||||||
update_split (true);
|
update_split (true);
|
||||||
reversed_ = false;
|
reversed_ = false;
|
||||||
update_rx_frequency (rig_->FreqA ());
|
update_rx_frequency (rig_->FreqA ());
|
||||||
@ -394,6 +405,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOBA)
|
if (params & OmniRig::PM_VFOBA)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOBA");
|
||||||
update_split (true);
|
update_split (true);
|
||||||
reversed_ = true;
|
reversed_ = true;
|
||||||
update_other_frequency (rig_->FreqA ());
|
update_other_frequency (rig_->FreqA ());
|
||||||
@ -401,6 +413,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOBB)
|
if (params & OmniRig::PM_VFOBB)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOBB");
|
||||||
update_split (false);
|
update_split (false);
|
||||||
reversed_ = true;
|
reversed_ = true;
|
||||||
update_other_frequency (rig_->FreqA ());
|
update_other_frequency (rig_->FreqA ());
|
||||||
@ -408,154 +421,195 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOA)
|
if (params & OmniRig::PM_VFOA)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOA");
|
||||||
reversed_ = false;
|
reversed_ = false;
|
||||||
need_frequency = true;
|
need_frequency = true;
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOB)
|
if (params & OmniRig::PM_VFOB)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "VFOB");
|
||||||
reversed_ = true;
|
reversed_ = true;
|
||||||
need_frequency = true;
|
need_frequency = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params & OmniRig::PM_FREQ)
|
if (params & OmniRig::PM_FREQ)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQ");
|
||||||
need_frequency = true;
|
need_frequency = true;
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_FREQA)
|
if (params & OmniRig::PM_FREQA)
|
||||||
{
|
{
|
||||||
|
auto f = rig_->FreqA ();
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
|
||||||
if (reversed_)
|
if (reversed_)
|
||||||
{
|
{
|
||||||
update_other_frequency (rig_->FreqA ());
|
update_other_frequency (f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_rx_frequency (rig_->FreqA ());
|
update_rx_frequency (f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_FREQB)
|
if (params & OmniRig::PM_FREQB)
|
||||||
{
|
{
|
||||||
|
auto f = rig_->FreqB ();
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
|
||||||
if (reversed_)
|
if (reversed_)
|
||||||
{
|
{
|
||||||
update_rx_frequency (rig_->FreqB ());
|
update_rx_frequency (f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_other_frequency (rig_->FreqB ());
|
update_other_frequency (f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (need_frequency)
|
if (need_frequency)
|
||||||
{
|
{
|
||||||
if (readable_params_ & OmniRig::PM_FREQA)
|
if (readable_params_ & OmniRig::PM_FREQA)
|
||||||
{
|
{
|
||||||
|
auto f = rig_->FreqA ();
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
|
||||||
if (reversed_)
|
if (reversed_)
|
||||||
{
|
{
|
||||||
update_other_frequency (rig_->FreqA ());
|
update_other_frequency (f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_rx_frequency (rig_->FreqA ());
|
update_rx_frequency (f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
need_frequency = false;
|
|
||||||
}
|
}
|
||||||
if (readable_params_ & OmniRig::PM_FREQB)
|
if (readable_params_ & OmniRig::PM_FREQB)
|
||||||
{
|
{
|
||||||
|
auto f = rig_->FreqB ();
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
|
||||||
if (reversed_)
|
if (reversed_)
|
||||||
{
|
{
|
||||||
update_rx_frequency (rig_->FreqB ());
|
update_rx_frequency (f);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
update_other_frequency (rig_->FreqB ());
|
update_other_frequency (f);
|
||||||
}
|
|
||||||
need_frequency = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (need_frequency && (readable_params_ & OmniRig::PM_FREQ)
|
}
|
||||||
&& !state ().ptt ())
|
if (readable_params_ & OmniRig::PM_FREQ && !state ().ptt ())
|
||||||
{
|
{
|
||||||
update_rx_frequency (rig_->Freq ());
|
auto f = rig_->Freq ();
|
||||||
|
if (f)
|
||||||
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FREQ = " << f);
|
||||||
|
update_rx_frequency (f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_PITCH)
|
if (params & OmniRig::PM_PITCH)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "PITCH");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_RITOFFSET)
|
if (params & OmniRig::PM_RITOFFSET)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "RITOFFSET");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_RIT0)
|
if (params & OmniRig::PM_RIT0)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "RIT0");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOEQUAL)
|
if (params & OmniRig::PM_VFOEQUAL)
|
||||||
{
|
{
|
||||||
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
|
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
|
||||||
|
auto m = map_mode (rig_->Mode ());
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m));
|
||||||
update_rx_frequency (f);
|
update_rx_frequency (f);
|
||||||
update_other_frequency (f);
|
update_other_frequency (f);
|
||||||
update_mode (map_mode (rig_->Mode ()));
|
update_mode (m);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_VFOSWAP)
|
if (params & OmniRig::PM_VFOSWAP)
|
||||||
{
|
{
|
||||||
auto temp = state ().tx_frequency ();
|
TRACE_CAT ("OmniRigTransceiver", "VFOSWAP");
|
||||||
|
auto f = state ().tx_frequency ();
|
||||||
update_other_frequency (state ().frequency ());
|
update_other_frequency (state ().frequency ());
|
||||||
update_rx_frequency (temp);
|
update_rx_frequency (f);
|
||||||
update_mode (map_mode (rig_->Mode ()));
|
update_mode (map_mode (rig_->Mode ()));
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_SPLITON)
|
if (params & OmniRig::PM_SPLITON)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "SPLITON");
|
||||||
update_split (true);
|
update_split (true);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_SPLITOFF)
|
if (params & OmniRig::PM_SPLITOFF)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "SPLITOFF");
|
||||||
update_split (false);
|
update_split (false);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_RITON)
|
if (params & OmniRig::PM_RITON)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "RITON");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_RITOFF)
|
if (params & OmniRig::PM_RITOFF)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "RITOFF");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_XITON)
|
if (params & OmniRig::PM_XITON)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "XITON");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_XITOFF)
|
if (params & OmniRig::PM_XITOFF)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "XITOFF");
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_RX)
|
if (params & OmniRig::PM_RX)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "RX");
|
||||||
update_PTT (false);
|
update_PTT (false);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_TX)
|
if (params & OmniRig::PM_TX)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "TX");
|
||||||
update_PTT ();
|
update_PTT ();
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_CW_U)
|
if (params & OmniRig::PM_CW_U)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "CW-R");
|
||||||
update_mode (CW_R);
|
update_mode (CW_R);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_CW_L)
|
if (params & OmniRig::PM_CW_L)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "CW");
|
||||||
update_mode (CW);
|
update_mode (CW);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_SSB_U)
|
if (params & OmniRig::PM_SSB_U)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "USB");
|
||||||
update_mode (USB);
|
update_mode (USB);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_SSB_L)
|
if (params & OmniRig::PM_SSB_L)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "LSB");
|
||||||
update_mode (LSB);
|
update_mode (LSB);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_DIG_U)
|
if (params & OmniRig::PM_DIG_U)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "DATA-U");
|
||||||
update_mode (DIG_U);
|
update_mode (DIG_U);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_DIG_L)
|
if (params & OmniRig::PM_DIG_L)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "DATA-L");
|
||||||
update_mode (DIG_L);
|
update_mode (DIG_L);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_AM)
|
if (params & OmniRig::PM_AM)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "AM");
|
||||||
update_mode (AM);
|
update_mode (AM);
|
||||||
}
|
}
|
||||||
if (params & OmniRig::PM_FM)
|
if (params & OmniRig::PM_FM)
|
||||||
{
|
{
|
||||||
|
TRACE_CAT ("OmniRigTransceiver", "FM");
|
||||||
update_mode (FM);
|
update_mode (FM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,6 +620,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||||||
}
|
}
|
||||||
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
|
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
|
||||||
}
|
}
|
||||||
|
Q_EMIT notified ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply)
|
void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply)
|
||||||
@ -573,8 +628,10 @@ void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& co
|
|||||||
(void)command;
|
(void)command;
|
||||||
(void)reply;
|
(void)reply;
|
||||||
|
|
||||||
|
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||||
if (rig_number_ == rig_number)
|
if (rig_number_ == rig_number)
|
||||||
{
|
{
|
||||||
|
if (!rig_ || rig_->isNull ()) return;
|
||||||
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
|
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
|
||||||
<< "with reply" << reply.toString ().toLocal8Bit ()
|
<< "with reply" << reply.toString ().toLocal8Bit ()
|
||||||
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
|
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
|
||||||
|
@ -38,10 +38,11 @@ public:
|
|||||||
void do_tx_frequency (Frequency, MODE, bool no_ignore) override;
|
void do_tx_frequency (Frequency, MODE, bool no_ignore) override;
|
||||||
void do_mode (MODE) override;
|
void do_mode (MODE) override;
|
||||||
void do_ptt (bool on) override;
|
void do_ptt (bool on) override;
|
||||||
void do_sync (bool force_signal, bool no_poll) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_SLOT void timeout_check ();
|
bool await_notification_with_timeout (int timeout);
|
||||||
|
Q_SIGNAL void notified () const;
|
||||||
|
// Q_SLOT void timeout_check ();
|
||||||
Q_SLOT void handle_COM_exception (int, QString, QString, QString);
|
Q_SLOT void handle_COM_exception (int, QString, QString, QString);
|
||||||
Q_SLOT void handle_visible_change ();
|
Q_SLOT void handle_visible_change ();
|
||||||
Q_SLOT void handle_rig_type_change (int rig_number);
|
Q_SLOT void handle_rig_type_change (int rig_number);
|
||||||
@ -62,7 +63,7 @@ private:
|
|||||||
QString rig_type_;
|
QString rig_type_;
|
||||||
int readable_params_;
|
int readable_params_;
|
||||||
int writable_params_;
|
int writable_params_;
|
||||||
QScopedPointer<QTimer> offline_timer_;
|
// QScopedPointer<QTimer> offline_timer_;
|
||||||
bool send_update_signal_;
|
bool send_update_signal_;
|
||||||
bool reversed_; // some rigs can reverse VFOs
|
bool reversed_; // some rigs can reverse VFOs
|
||||||
};
|
};
|
||||||
|
@ -129,29 +129,34 @@ bool PollingTransceiver::do_pre_update ()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PollingTransceiver::do_sync (bool force_signal, bool no_poll)
|
void PollingTransceiver::handle_timeout ()
|
||||||
{
|
{
|
||||||
if (!no_poll) poll (); // tell sub-classes to update our state
|
QString message;
|
||||||
|
bool force_signal {false};
|
||||||
|
|
||||||
// Signal new state if it is directly requested or, what we expected
|
// we must catch all exceptions here since we are called by Qt and
|
||||||
// or, hasn't become what we expected after polls_to_stabilize
|
// inform our parent of the failure via the offline() message
|
||||||
// polls. Unsolicited changes will be signalled immediately unless
|
try
|
||||||
// they intervene in a expected sequence where they will be delayed.
|
{
|
||||||
|
do_poll (); // tell sub-classes to update our state
|
||||||
|
|
||||||
|
// Signal new state if it what we expected or, hasn't become
|
||||||
|
// what we expected after polls_to_stabilize polls. Unsolicited
|
||||||
|
// changes will be signalled immediately unless they intervene
|
||||||
|
// in a expected sequence where they will be delayed.
|
||||||
if (retries_)
|
if (retries_)
|
||||||
{
|
{
|
||||||
--retries_;
|
--retries_;
|
||||||
if (force_signal || state () == next_state_ || !retries_)
|
if (state () == next_state_ || !retries_)
|
||||||
{
|
{
|
||||||
// our client wants a signal regardless
|
// the expected state has arrived or there are no more
|
||||||
// or the expected state has arrived
|
// retries
|
||||||
// or there are no more retries
|
|
||||||
force_signal = true;
|
force_signal = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (force_signal || state () != last_signalled_state_)
|
else if (state () != last_signalled_state_)
|
||||||
{
|
{
|
||||||
// here is the normal passive polling path either our client has
|
// here is the normal passive polling path where state has
|
||||||
// requested a state update regardless of change or state has
|
|
||||||
// changed asynchronously
|
// changed asynchronously
|
||||||
force_signal = true;
|
force_signal = true;
|
||||||
}
|
}
|
||||||
@ -165,17 +170,6 @@ void PollingTransceiver::do_sync (bool force_signal, bool no_poll)
|
|||||||
update_complete (true);
|
update_complete (true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PollingTransceiver::handle_timeout ()
|
|
||||||
{
|
|
||||||
QString message;
|
|
||||||
|
|
||||||
// we must catch all exceptions here since we are called by Qt and
|
|
||||||
// inform our parent of the failure via the offline() message
|
|
||||||
try
|
|
||||||
{
|
|
||||||
do_sync ();
|
|
||||||
}
|
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
message = e.what ();
|
message = e.what ();
|
||||||
|
@ -39,11 +39,9 @@ protected:
|
|||||||
QObject * parent);
|
QObject * parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void do_sync (bool force_signal = false, bool no_poll = false) override final;
|
|
||||||
|
|
||||||
// Sub-classes implement this and fetch what they can from the rig
|
// Sub-classes implement this and fetch what they can from the rig
|
||||||
// in a non-intrusive manner.
|
// in a non-intrusive manner.
|
||||||
virtual void poll () = 0;
|
virtual void do_poll () = 0;
|
||||||
|
|
||||||
void do_post_start () override final;
|
void do_post_start () override final;
|
||||||
void do_post_stop () override final;
|
void do_post_stop () override final;
|
||||||
|
@ -74,14 +74,14 @@ namespace Radio
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QString frequency_MHz_string (Frequency f, QLocale const& locale)
|
QString frequency_MHz_string (Frequency f, int precision, QLocale const& locale)
|
||||||
{
|
{
|
||||||
return locale.toString (f / MHz_factor, 'f', frequency_precsion);
|
return locale.toString (f / MHz_factor, 'f', precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString frequency_MHz_string (FrequencyDelta d, QLocale const& locale)
|
QString frequency_MHz_string (FrequencyDelta d, int precision, QLocale const& locale)
|
||||||
{
|
{
|
||||||
return locale.toString (d / MHz_factor, 'f', frequency_precsion);
|
return locale.toString (d / MHz_factor, 'f', precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale)
|
QString pretty_frequency_MHz_string (Frequency f, QLocale const& locale)
|
||||||
|
@ -42,8 +42,8 @@ namespace Radio
|
|||||||
//
|
//
|
||||||
// Frequency type formatting
|
// Frequency type formatting
|
||||||
//
|
//
|
||||||
QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
QString UDP_EXPORT frequency_MHz_string (Frequency, int precision = 6, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, int precision = 6, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
QString UDP_EXPORT pretty_frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
QString UDP_EXPORT pretty_frequency_MHz_string (double, int scale, QLocale const& = QLocale ());
|
||||||
QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
QString UDP_EXPORT pretty_frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||||
|
@ -12,6 +12,178 @@
|
|||||||
|
|
||||||
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1
|
||||||
|
July 15, 2019
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1 is a major update that introduces FT4, a new protocol
|
||||||
|
targeted at HF contesting. Other improvements have been made in the
|
||||||
|
following areas:
|
||||||
|
|
||||||
|
- FT8 waveform generated with GMSK, fully backward compatible
|
||||||
|
- user options for waterfall and spectrum display
|
||||||
|
- contest logging
|
||||||
|
- rig control
|
||||||
|
- user interface
|
||||||
|
- UDP messaging for inter-program communication
|
||||||
|
- accessibility
|
||||||
|
|
||||||
|
There are numerous minor enhancements and bug fixes.
|
||||||
|
|
||||||
|
We now provide a separate installation package for 64-bit Windows 7
|
||||||
|
and later, with significant improvements in decoding speed.
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc7
|
||||||
|
June 3, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
This release is a bug fix only release addressing regressions in the
|
||||||
|
prior RC6 release. There are no functional changes other than an
|
||||||
|
updated AD1C CTY.DAT database.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc6
|
||||||
|
June 2, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Changes and bug fixes since WSJT-X 2.1.0-rc5:
|
||||||
|
|
||||||
|
IMPORTANT CHANGES TO THE FT4 PROTOCOL *** NOT BACKWARD COMPATIBLE ***
|
||||||
|
- T/R sequence length increased from 6.0 to 7.5 seconds
|
||||||
|
- Symbol rate decreased from 23.4375 to 20.8333 baud
|
||||||
|
- Signal bandwidth decreased from 90 Hz to 80 Hz
|
||||||
|
|
||||||
|
OTHER FT4 IMPROVEMENTS
|
||||||
|
- Allowable time offsets -1.0 < DT < +1.0 s
|
||||||
|
- Tx4 message with RRR now allowed, except in contest messages
|
||||||
|
- Audio frequency is now sent to PSK Reporter
|
||||||
|
- Add a third decoding pass
|
||||||
|
- Add ordered statistics decoding
|
||||||
|
- Improved sensitivity: threshold S/N is now -17.5 dB
|
||||||
|
- Improved S/N calculation
|
||||||
|
- In FT4 mode, Shift+F11/F12 moves Tx freq by +/- 100 Hz
|
||||||
|
|
||||||
|
OTHER IMPROVEMENTS
|
||||||
|
- Improvements to accessibility
|
||||||
|
- Updates to the User Guide (not yet complete, however)
|
||||||
|
- New user option: "Calling CQ forces Call 1st"
|
||||||
|
- N1MM Logger+ now uses the standard WSJT-X UDP messages
|
||||||
|
- OK/Cancel buttons on Log QSO window maintain fixed positions
|
||||||
|
- Put EU VHF contest serial numbers into the ADIF SRX and STX fields
|
||||||
|
- Enhancements to the Omni-Rig CAT interface
|
||||||
|
- New setting option to include or exclude WAE entities
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
- Fix generation of Tx5 message when one callsign is nonstandard
|
||||||
|
- Fix a bug that prevented use on macOS
|
||||||
|
- Fix a bug that caused mode switch from FT4 to FT8
|
||||||
|
- Fix a bug that caused FT4 to do WSPR-style band hopping
|
||||||
|
- Fix a bug that caused a Fortran bounds error
|
||||||
|
- Repaired field editing in the Contest Log window
|
||||||
|
|
||||||
|
Release candidate WSJT-X 2.1.0-rc6 will be available for beta-testing
|
||||||
|
through July 21, 2019. It will be inoperable during the ARRL June VHF
|
||||||
|
QSO Party (June 8-10) or ARRL Field Day (June 22-23). It will
|
||||||
|
permanently cease to function after July 21, 2019. If all goes
|
||||||
|
according to plan, by that time there will be a General
|
||||||
|
Availability (GA) release of WSJT-X 2.1.0.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc5
|
||||||
|
April 29, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1.0 fifth release candidate is a minor release including the
|
||||||
|
following.
|
||||||
|
|
||||||
|
- Repairs a defect that stopped messages from UDP servers being accepted.
|
||||||
|
- Improved message sequencing a QSO end for CQing FT4 stations.
|
||||||
|
- "Best S+P" action times out after two minutes waiting for a candidate.
|
||||||
|
- Updated macOS Info.plist to comply with latest mic. privacy controls.
|
||||||
|
- Multi-pass decoding for FT4 inc. prior decode subtraction.
|
||||||
|
- Fast/Normal/Deep options for the FT4 decoder.
|
||||||
|
- Proposed suggested working frequencies for the new FT4 mode.
|
||||||
|
- Repair a defect in RTTY RU where sequencer fails to advance to Tx3.
|
||||||
|
- Fix a defect where the contest serial # spin box was incorrectly hidden.
|
||||||
|
- Fix defects in ALL.TXT formatting for JT65 and JT9.
|
||||||
|
- Reduce timeout for AP decoding with own call from 10 mins to 5 mins.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc4
|
||||||
|
April 10, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1.0 fourth release candidate is a minor release including the
|
||||||
|
following.
|
||||||
|
|
||||||
|
- New "Call Best" button for FT4 mode to select the best reply to a
|
||||||
|
CQ call based on neediness.
|
||||||
|
- Fixed UTC display on FT4 waterfall.
|
||||||
|
|
||||||
|
This release is made by invitation only to selected testers to trial
|
||||||
|
the FT4 mode in semi-realistic contest simulations and to elicit
|
||||||
|
feedback to guide future development.
|
||||||
|
|
||||||
|
*Note* this release is not for general public release and we request
|
||||||
|
that it is not distributed.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc3
|
||||||
|
April 5, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1.0 third release candidate is an enhancement release to
|
||||||
|
change the implementation of the new FT4 mode to a synchronous T/R
|
||||||
|
period of 6 seconds.
|
||||||
|
|
||||||
|
This release is made by invitation only to selected testers to trial
|
||||||
|
the FT4 mode in semi-realistic contest simulations and to elicit
|
||||||
|
feedback to guide future development.
|
||||||
|
|
||||||
|
*Note* this release is not for general public release and we request
|
||||||
|
that it is not distributed.
|
||||||
|
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc2
|
||||||
|
March 29, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1.0 second release candidate is a bug fix release to repair
|
||||||
|
some usability issues with FT4 contest mode. The following new
|
||||||
|
features are also included.
|
||||||
|
|
||||||
|
- Better options for QSO flow by clicking Tx# buttons to transmit
|
||||||
|
- A 64-bit package for Windows 64-bit systems
|
||||||
|
- Improved FT4 sync detection speed
|
||||||
|
|
||||||
|
This release is made by invitation only to selected testers to trial
|
||||||
|
the FT4 mode in semi-realistic contest simulations and to elicit
|
||||||
|
feedback to guide future development.
|
||||||
|
|
||||||
|
*Note* this release is not for general public release and we request
|
||||||
|
that it is not distributed.
|
||||||
|
|
||||||
|
Release: WSJT-X 2.1.0-rc1
|
||||||
|
March 25, 2019
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
WSJT-X 2.1.0 first release candidate is a preview alpha quality
|
||||||
|
release containing the following new features.
|
||||||
|
|
||||||
|
- FT4 mode, a new mode targeted at HF digital contesting
|
||||||
|
- GMSK modulation for FT4 and FT8
|
||||||
|
- New waterfall option to select between raw sensitivity or a
|
||||||
|
filtered signal representation for best visualization of signal
|
||||||
|
quality
|
||||||
|
|
||||||
|
This release is made by invitation only to selected testers to trial
|
||||||
|
the FT4 mode in semi-realistic contest simulations and to elicit
|
||||||
|
feedback to guide future development.
|
||||||
|
|
||||||
|
*Note* this release is not for general public release and we request
|
||||||
|
that it is not distributed.
|
||||||
|
|
||||||
|
|
||||||
Release: WSJT-X 2.0.1
|
Release: WSJT-X 2.0.1
|
||||||
February 25, 2019
|
February 25, 2019
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QMutexLocker>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
@ -28,10 +30,11 @@ private:
|
|||||||
QTextStream * original_stream_;
|
QTextStream * original_stream_;
|
||||||
QtMessageHandler original_handler_;
|
QtMessageHandler original_handler_;
|
||||||
static QTextStream * current_stream_;
|
static QTextStream * current_stream_;
|
||||||
|
static QMutex mutex_;
|
||||||
};
|
};
|
||||||
|
|
||||||
QTextStream * TraceFile::impl::current_stream_;
|
QTextStream * TraceFile::impl::current_stream_;
|
||||||
|
QMutex TraceFile::impl::mutex_;
|
||||||
|
|
||||||
// delegate to implementation class
|
// delegate to implementation class
|
||||||
TraceFile::TraceFile (QString const& trace_file_path)
|
TraceFile::TraceFile (QString const& trace_file_path)
|
||||||
@ -73,7 +76,10 @@ TraceFile::impl::~impl ()
|
|||||||
void TraceFile::impl::message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
|
void TraceFile::impl::message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
|
||||||
{
|
{
|
||||||
Q_ASSERT_X (current_stream_, "TraceFile:message_handler", "no stream to write to");
|
Q_ASSERT_X (current_stream_, "TraceFile:message_handler", "no stream to write to");
|
||||||
|
{
|
||||||
|
QMutexLocker lock {&mutex_}; // thread safety - serialize writes to the trace file
|
||||||
*current_stream_ << qFormatLogMessage (type, context, msg) << endl;
|
*current_stream_ << qFormatLogMessage (type, context, msg) << endl;
|
||||||
|
}
|
||||||
|
|
||||||
if (QtFatalMsg == type)
|
if (QtFatalMsg == type)
|
||||||
{
|
{
|
||||||
|
@ -19,10 +19,10 @@ void TransceiverBase::start (unsigned sequence_number) noexcept
|
|||||||
QString message;
|
QString message;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
last_sequence_number_ = sequence_number;
|
||||||
may_update u {this, true};
|
may_update u {this, true};
|
||||||
shutdown ();
|
shutdown ();
|
||||||
startup ();
|
startup ();
|
||||||
last_sequence_number_ = sequence_number;
|
|
||||||
}
|
}
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
@ -46,6 +46,7 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||||||
QString message;
|
QString message;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
last_sequence_number_ = sequence_number;
|
||||||
may_update u {this, true};
|
may_update u {this, true};
|
||||||
bool was_online {requested_.online ()};
|
bool was_online {requested_.online ()};
|
||||||
if (!s.online () && was_online)
|
if (!s.online () && was_online)
|
||||||
@ -115,7 +116,6 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||||||
// record what actually changed
|
// record what actually changed
|
||||||
requested_.ptt (actual_.ptt ());
|
requested_.ptt (actual_.ptt ());
|
||||||
}
|
}
|
||||||
last_sequence_number_ = sequence_number;
|
|
||||||
}
|
}
|
||||||
catch (std::exception const& e)
|
catch (std::exception const& e)
|
||||||
{
|
{
|
||||||
@ -133,10 +133,27 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||||||
|
|
||||||
void TransceiverBase::startup ()
|
void TransceiverBase::startup ()
|
||||||
{
|
{
|
||||||
Q_EMIT resolution (do_start ());
|
QString message;
|
||||||
do_post_start ();
|
try
|
||||||
|
{
|
||||||
actual_.online (true);
|
actual_.online (true);
|
||||||
requested_.online (true);
|
requested_.online (true);
|
||||||
|
auto res = do_start ();
|
||||||
|
do_post_start ();
|
||||||
|
Q_EMIT resolution (res);
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
message = e.what ();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
message = unexpected;
|
||||||
|
}
|
||||||
|
if (!message.isEmpty ())
|
||||||
|
{
|
||||||
|
offline (message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransceiverBase::shutdown ()
|
void TransceiverBase::shutdown ()
|
||||||
@ -163,8 +180,8 @@ void TransceiverBase::shutdown ()
|
|||||||
}
|
}
|
||||||
do_stop ();
|
do_stop ();
|
||||||
do_post_stop ();
|
do_post_stop ();
|
||||||
actual_.online (false);
|
actual_ = TransceiverState {};
|
||||||
requested_.online (false);
|
requested_ = TransceiverState {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransceiverBase::stop () noexcept
|
void TransceiverBase::stop () noexcept
|
||||||
@ -193,10 +210,13 @@ void TransceiverBase::stop () noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TransceiverBase::update_rx_frequency (Frequency rx)
|
void TransceiverBase::update_rx_frequency (Frequency rx)
|
||||||
|
{
|
||||||
|
if (rx)
|
||||||
{
|
{
|
||||||
actual_.frequency (rx);
|
actual_.frequency (rx);
|
||||||
requested_.frequency (rx); // track rig changes
|
requested_.frequency (rx); // track rig changes
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TransceiverBase::update_other_frequency (Frequency tx)
|
void TransceiverBase::update_other_frequency (Frequency tx)
|
||||||
{
|
{
|
||||||
|
@ -112,8 +112,6 @@ protected:
|
|||||||
virtual void do_ptt (bool = true) = 0;
|
virtual void do_ptt (bool = true) = 0;
|
||||||
virtual void do_post_ptt (bool = true) {}
|
virtual void do_post_ptt (bool = true) {}
|
||||||
|
|
||||||
virtual void do_sync (bool force_signal = false, bool no_poll = false) = 0;
|
|
||||||
|
|
||||||
virtual bool do_pre_update () {return true;}
|
virtual bool do_pre_update () {return true;}
|
||||||
|
|
||||||
// sub classes report rig state changes with these methods
|
// sub classes report rig state changes with these methods
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
#include "ClientWidget.hpp"
|
#include "ClientWidget.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <QRegExp>
|
#include <QRegExp>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QtWidgets>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include "validators/MaidenheadLocatorValidator.hpp"
|
#include "validators/MaidenheadLocatorValidator.hpp"
|
||||||
|
|
||||||
@ -11,6 +14,9 @@ namespace
|
|||||||
//QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
|
//QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
|
||||||
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>]*"};
|
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>]*"};
|
||||||
QRegularExpression cq_re {"(CQ|CQDX|QRZ)[^A-Z0-9/]+"};
|
QRegularExpression cq_re {"(CQ|CQDX|QRZ)[^A-Z0-9/]+"};
|
||||||
|
QRegExpValidator message_validator {message_alphabet};
|
||||||
|
MaidenheadLocatorValidator locator_validator;
|
||||||
|
quint32 quint32_max {std::numeric_limits<quint32>::max ()};
|
||||||
|
|
||||||
void update_dynamic_property (QWidget * widget, char const * property, QVariant const& value)
|
void update_dynamic_property (QWidget * widget, char const * property, QVariant const& value)
|
||||||
{
|
{
|
||||||
@ -21,9 +27,10 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id)
|
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id, QObject * parent)
|
||||||
: client_id_ {client_id}
|
: QSortFilterProxyModel {parent}
|
||||||
, rx_df_ (-1)
|
, client_id_ {client_id}
|
||||||
|
, rx_df_ (quint32_max)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +56,7 @@ QVariant ClientWidget::IdFilterModel::data (QModelIndex const& proxy_index, int
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: // DF
|
case 4: // DF
|
||||||
if (qAbs (QSortFilterProxyModel::data (proxy_index).toInt () - rx_df_) <= 10)
|
if (qAbs (QSortFilterProxyModel::data (proxy_index).toUInt () - rx_df_) <= 10)
|
||||||
{
|
{
|
||||||
return QColor {255, 200, 200};
|
return QColor {255, 200, 200};
|
||||||
}
|
}
|
||||||
@ -87,7 +94,7 @@ void ClientWidget::IdFilterModel::de_call (QString const& call)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWidget::IdFilterModel::rx_df (int df)
|
void ClientWidget::IdFilterModel::rx_df (quint32 df)
|
||||||
{
|
{
|
||||||
if (df != rx_df_)
|
if (df != rx_df_)
|
||||||
{
|
{
|
||||||
@ -119,26 +126,48 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
|
|||||||
, QListWidget const * calls_of_interest, QWidget * parent)
|
, QListWidget const * calls_of_interest, QWidget * parent)
|
||||||
: QDockWidget {make_title (id, version, revision), parent}
|
: QDockWidget {make_title (id, version, revision), parent}
|
||||||
, id_ {id}
|
, id_ {id}
|
||||||
|
, done_ {false}
|
||||||
, calls_of_interest_ {calls_of_interest}
|
, calls_of_interest_ {calls_of_interest}
|
||||||
, decodes_proxy_model_ {id_}
|
, decodes_proxy_model_ {id}
|
||||||
|
, beacons_proxy_model_ {id}
|
||||||
, erase_action_ {new QAction {tr ("&Erase Band Activity"), this}}
|
, erase_action_ {new QAction {tr ("&Erase Band Activity"), this}}
|
||||||
, erase_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}}
|
, erase_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}}
|
||||||
, erase_both_action_ {new QAction {tr ("Erase &Both"), this}}
|
, erase_both_action_ {new QAction {tr ("Erase &Both"), this}}
|
||||||
, decodes_table_view_ {new QTableView}
|
, decodes_table_view_ {new QTableView {this}}
|
||||||
, beacons_table_view_ {new QTableView}
|
, beacons_table_view_ {new QTableView {this}}
|
||||||
, message_line_edit_ {new QLineEdit}
|
, message_line_edit_ {new QLineEdit {this}}
|
||||||
, grid_line_edit_ {new QLineEdit}
|
, grid_line_edit_ {new QLineEdit {this}}
|
||||||
|
, generate_messages_push_button_ {new QPushButton {tr ("&Gen Msgs"), this}}
|
||||||
|
, auto_off_button_ {nullptr}
|
||||||
|
, halt_tx_button_ {nullptr}
|
||||||
|
, de_label_ {new QLabel {this}}
|
||||||
|
, frequency_label_ {new QLabel {this}}
|
||||||
|
, tx_df_label_ {new QLabel {this}}
|
||||||
|
, report_label_ {new QLabel {this}}
|
||||||
|
, configuration_line_edit_ {new QLineEdit {this}}
|
||||||
|
, mode_line_edit_ {new QLineEdit {this}}
|
||||||
|
, frequency_tolerance_spin_box_ {new QSpinBox {this}}
|
||||||
|
, tx_mode_label_ {new QLabel {this}}
|
||||||
|
, submode_line_edit_ {new QLineEdit {this}}
|
||||||
|
, fast_mode_check_box_ {new QCheckBox {this}}
|
||||||
|
, tr_period_spin_box_ {new QSpinBox {this}}
|
||||||
|
, rx_df_spin_box_ {new QSpinBox {this}}
|
||||||
|
, dx_call_line_edit_ {new QLineEdit {this}}
|
||||||
|
, dx_grid_line_edit_ {new QLineEdit {this}}
|
||||||
|
, decodes_page_ {new QWidget {this}}
|
||||||
|
, beacons_page_ {new QWidget {this}}
|
||||||
|
, content_widget_ {new QFrame {this}}
|
||||||
|
, status_bar_ {new QStatusBar {this}}
|
||||||
|
, control_button_box_ {new QDialogButtonBox {this}}
|
||||||
|
, form_layout_ {new QFormLayout}
|
||||||
|
, horizontal_layout_ {new QHBoxLayout}
|
||||||
|
, subform1_layout_ {new QFormLayout}
|
||||||
|
, subform2_layout_ {new QFormLayout}
|
||||||
|
, subform3_layout_ {new QFormLayout}
|
||||||
|
, decodes_layout_ {new QVBoxLayout {decodes_page_}}
|
||||||
|
, beacons_layout_ {new QVBoxLayout {beacons_page_}}
|
||||||
|
, content_layout_ {new QVBoxLayout {content_widget_}}
|
||||||
, decodes_stack_ {new QStackedLayout}
|
, decodes_stack_ {new QStackedLayout}
|
||||||
, auto_off_button_ {new QPushButton {tr ("&Auto Off")}}
|
|
||||||
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
|
|
||||||
, de_label_ {new QLabel}
|
|
||||||
, mode_label_ {new QLabel}
|
|
||||||
, fast_mode_ {false}
|
|
||||||
, frequency_label_ {new QLabel}
|
|
||||||
, dx_label_ {new QLabel}
|
|
||||||
, rx_df_label_ {new QLabel}
|
|
||||||
, tx_df_label_ {new QLabel}
|
|
||||||
, report_label_ {new QLabel}
|
|
||||||
, columns_resized_ {false}
|
, columns_resized_ {false}
|
||||||
{
|
{
|
||||||
// set up widgets
|
// set up widgets
|
||||||
@ -152,11 +181,33 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
|
|||||||
decodes_table_view_->insertAction (nullptr, erase_rx_frequency_action_);
|
decodes_table_view_->insertAction (nullptr, erase_rx_frequency_action_);
|
||||||
decodes_table_view_->insertAction (nullptr, erase_both_action_);
|
decodes_table_view_->insertAction (nullptr, erase_both_action_);
|
||||||
|
|
||||||
auto form_layout = new QFormLayout;
|
message_line_edit_->setValidator (&message_validator);
|
||||||
form_layout->addRow (tr ("Free text:"), message_line_edit_);
|
grid_line_edit_->setValidator (&locator_validator);
|
||||||
form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_);
|
dx_grid_line_edit_->setValidator (&locator_validator);
|
||||||
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
|
tr_period_spin_box_->setRange (5, 30);
|
||||||
grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this});
|
tr_period_spin_box_->setSuffix (" s");
|
||||||
|
rx_df_spin_box_->setRange (200, 5000);
|
||||||
|
frequency_tolerance_spin_box_->setRange (10, 1000);
|
||||||
|
frequency_tolerance_spin_box_->setPrefix ("\u00b1");
|
||||||
|
frequency_tolerance_spin_box_->setSuffix (" Hz");
|
||||||
|
|
||||||
|
form_layout_->addRow (tr ("Free text:"), message_line_edit_);
|
||||||
|
form_layout_->addRow (tr ("Temporary grid:"), grid_line_edit_);
|
||||||
|
form_layout_->addRow (tr ("Configuration name:"), configuration_line_edit_);
|
||||||
|
form_layout_->addRow (horizontal_layout_);
|
||||||
|
subform1_layout_->addRow (tr ("Mode:"), mode_line_edit_);
|
||||||
|
subform2_layout_->addRow (tr ("Submode:"), submode_line_edit_);
|
||||||
|
subform3_layout_->addRow (tr ("Fast mode:"), fast_mode_check_box_);
|
||||||
|
subform1_layout_->addRow (tr ("T/R period:"), tr_period_spin_box_);
|
||||||
|
subform2_layout_->addRow (tr ("Rx DF:"), rx_df_spin_box_);
|
||||||
|
subform3_layout_->addRow (tr ("Freq. Tol:"), frequency_tolerance_spin_box_);
|
||||||
|
subform1_layout_->addRow (tr ("DX call:"), dx_call_line_edit_);
|
||||||
|
subform2_layout_->addRow (tr ("DX grid:"), dx_grid_line_edit_);
|
||||||
|
subform3_layout_->addRow (generate_messages_push_button_);
|
||||||
|
horizontal_layout_->addLayout (subform1_layout_);
|
||||||
|
horizontal_layout_->addLayout (subform2_layout_);
|
||||||
|
horizontal_layout_->addLayout (subform3_layout_);
|
||||||
|
|
||||||
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
|
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
|
||||||
Q_EMIT do_free_text (id_, text, false);
|
Q_EMIT do_free_text (id_, text, false);
|
||||||
});
|
});
|
||||||
@ -166,66 +217,102 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
|
|||||||
connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () {
|
connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
Q_EMIT location (id_, grid_line_edit_->text ());
|
Q_EMIT location (id_, grid_line_edit_->text ());
|
||||||
});
|
});
|
||||||
|
connect (configuration_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
|
Q_EMIT switch_configuration (id_, configuration_line_edit_->text ());
|
||||||
|
});
|
||||||
|
connect (mode_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, mode_line_edit_->text (), quint32_max, empty, fast_mode ()
|
||||||
|
, quint32_max, quint32_max, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (frequency_tolerance_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
|
||||||
|
QString empty;
|
||||||
|
auto f = frequency_tolerance_spin_box_->specialValueText ().size () ? quint32_max : i;
|
||||||
|
Q_EMIT configure (id_, empty, f, empty, fast_mode ()
|
||||||
|
, quint32_max, quint32_max, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (submode_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, submode_line_edit_->text (), fast_mode ()
|
||||||
|
, quint32_max, quint32_max, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (fast_mode_check_box_, &QCheckBox::stateChanged, [this] (int state) {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, Qt::Checked == state
|
||||||
|
, quint32_max, quint32_max, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (tr_period_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
|
||||||
|
, i, quint32_max, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (rx_df_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
|
||||||
|
, quint32_max, i, empty, empty, false);
|
||||||
|
});
|
||||||
|
connect (dx_call_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
|
||||||
|
, quint32_max, quint32_max, dx_call_line_edit_->text (), empty, false);
|
||||||
|
});
|
||||||
|
connect (dx_grid_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
|
||||||
|
, quint32_max, quint32_max, empty, dx_grid_line_edit_->text (), false);
|
||||||
|
});
|
||||||
|
|
||||||
auto decodes_page = new QWidget;
|
decodes_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||||
auto decodes_layout = new QVBoxLayout {decodes_page};
|
decodes_layout_->addWidget (decodes_table_view_);
|
||||||
decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
decodes_layout_->addLayout (form_layout_);
|
||||||
decodes_layout->addWidget (decodes_table_view_);
|
|
||||||
decodes_layout->addLayout (form_layout);
|
|
||||||
|
|
||||||
auto beacons_proxy_model = new IdFilterModel {id_};
|
beacons_proxy_model_.setSourceModel (beacons_model);
|
||||||
beacons_proxy_model->setSourceModel (beacons_model);
|
beacons_table_view_->setModel (&beacons_proxy_model_);
|
||||||
beacons_table_view_->setModel (beacons_proxy_model);
|
|
||||||
beacons_table_view_->verticalHeader ()->hide ();
|
beacons_table_view_->verticalHeader ()->hide ();
|
||||||
beacons_table_view_->hideColumn (0);
|
beacons_table_view_->hideColumn (0);
|
||||||
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||||
beacons_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu);
|
beacons_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu);
|
||||||
beacons_table_view_->insertAction (nullptr, erase_action_);
|
beacons_table_view_->insertAction (nullptr, erase_action_);
|
||||||
|
|
||||||
auto beacons_page = new QWidget;
|
beacons_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||||
auto beacons_layout = new QVBoxLayout {beacons_page};
|
beacons_layout_->addWidget (beacons_table_view_);
|
||||||
beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
|
||||||
beacons_layout->addWidget (beacons_table_view_);
|
|
||||||
|
|
||||||
decodes_stack_->addWidget (decodes_page);
|
decodes_stack_->addWidget (decodes_page_);
|
||||||
decodes_stack_->addWidget (beacons_page);
|
decodes_stack_->addWidget (beacons_page_);
|
||||||
|
|
||||||
// stack alternative views
|
// stack alternative views
|
||||||
auto content_layout = new QVBoxLayout;
|
content_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||||
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
content_layout_->addLayout (decodes_stack_);
|
||||||
content_layout->addLayout (decodes_stack_);
|
|
||||||
|
|
||||||
// set up controls
|
// set up controls
|
||||||
auto control_button_box = new QDialogButtonBox;
|
auto_off_button_ = control_button_box_->addButton (tr ("&Auto Off"), QDialogButtonBox::ActionRole);
|
||||||
control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole);
|
halt_tx_button_ = control_button_box_->addButton (tr ("&Halt Tx"), QDialogButtonBox::ActionRole);
|
||||||
control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole);
|
connect (generate_messages_push_button_, &QAbstractButton::clicked, [this] (bool /*checked*/) {
|
||||||
|
QString empty;
|
||||||
|
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
|
||||||
|
, quint32_max, quint32_max, empty, empty, true);
|
||||||
|
});
|
||||||
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||||
Q_EMIT do_halt_tx (id_, true);
|
Q_EMIT do_halt_tx (id_, true);
|
||||||
});
|
});
|
||||||
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||||
Q_EMIT do_halt_tx (id_, false);
|
Q_EMIT do_halt_tx (id_, false);
|
||||||
});
|
});
|
||||||
content_layout->addWidget (control_button_box);
|
content_layout_->addWidget (control_button_box_);
|
||||||
|
|
||||||
// set up status area
|
// set up status area
|
||||||
auto status_bar = new QStatusBar;
|
status_bar_->addPermanentWidget (de_label_);
|
||||||
status_bar->addPermanentWidget (de_label_);
|
status_bar_->addPermanentWidget (tx_mode_label_);
|
||||||
status_bar->addPermanentWidget (mode_label_);
|
status_bar_->addPermanentWidget (frequency_label_);
|
||||||
status_bar->addPermanentWidget (frequency_label_);
|
status_bar_->addPermanentWidget (tx_df_label_);
|
||||||
status_bar->addPermanentWidget (dx_label_);
|
status_bar_->addPermanentWidget (report_label_);
|
||||||
status_bar->addPermanentWidget (rx_df_label_);
|
content_layout_->addWidget (status_bar_);
|
||||||
status_bar->addPermanentWidget (tx_df_label_);
|
connect (this, &ClientWidget::topLevelChanged, status_bar_, &QStatusBar::setSizeGripEnabled);
|
||||||
status_bar->addPermanentWidget (report_label_);
|
|
||||||
content_layout->addWidget (status_bar);
|
|
||||||
connect (this, &ClientWidget::topLevelChanged, status_bar, &QStatusBar::setSizeGripEnabled);
|
|
||||||
|
|
||||||
// set up central widget
|
// set up central widget
|
||||||
auto content_widget = new QFrame;
|
content_widget_->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
||||||
content_widget->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
setWidget (content_widget_);
|
||||||
content_widget->setLayout (content_layout);
|
|
||||||
setWidget (content_widget);
|
|
||||||
// setMinimumSize (QSize {550, 0});
|
// setMinimumSize (QSize {550, 0});
|
||||||
setFeatures (DockWidgetMovable | DockWidgetFloatable);
|
|
||||||
setAllowedAreas (Qt::BottomDockWidgetArea);
|
setAllowedAreas (Qt::BottomDockWidgetArea);
|
||||||
setFloating (true);
|
setFloating (true);
|
||||||
|
|
||||||
@ -252,6 +339,25 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientWidget::dispose ()
|
||||||
|
{
|
||||||
|
done_ = true;
|
||||||
|
close ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientWidget::closeEvent (QCloseEvent *e)
|
||||||
|
{
|
||||||
|
if (!done_)
|
||||||
|
{
|
||||||
|
Q_EMIT do_close (id_);
|
||||||
|
e->ignore (); // defer closure until client actually closes
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QDockWidget::closeEvent (e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ClientWidget::~ClientWidget ()
|
ClientWidget::~ClientWidget ()
|
||||||
{
|
{
|
||||||
for (int row = 0; row < calls_of_interest_->count (); ++row)
|
for (int row = 0; row < calls_of_interest_->count (); ++row)
|
||||||
@ -261,16 +367,45 @@ ClientWidget::~ClientWidget ()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClientWidget::fast_mode () const
|
||||||
|
{
|
||||||
|
return fast_mode_check_box_->isChecked ();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void update_line_edit (QLineEdit * le, QString const& value, bool allow_empty = true)
|
||||||
|
{
|
||||||
|
le->setEnabled (value.size () || allow_empty);
|
||||||
|
if (!(le->hasFocus () && le->isModified ()))
|
||||||
|
{
|
||||||
|
le->setText (value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_spin_box (QSpinBox * sb, int value, QString const& special_value = QString {})
|
||||||
|
{
|
||||||
|
sb->setSpecialValueText (special_value);
|
||||||
|
bool enable {0 == special_value.size ()};
|
||||||
|
sb->setEnabled (enable);
|
||||||
|
if (!sb->hasFocus () && enable)
|
||||||
|
{
|
||||||
|
sb->setValue (value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
||||||
, QString const& report, QString const& tx_mode, bool tx_enabled
|
, QString const& report, QString const& tx_mode, bool tx_enabled
|
||||||
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
|
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
|
||||||
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
||||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
, bool watchdog_timeout, QString const& submode, bool fast_mode
|
||||||
, quint8 special_op_mode)
|
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
|
||||||
|
, QString const& configuration_name)
|
||||||
{
|
{
|
||||||
if (id == id_)
|
if (id == id_)
|
||||||
{
|
{
|
||||||
fast_mode_ = fast_mode;
|
fast_mode_check_box_->setChecked (fast_mode);
|
||||||
decodes_proxy_model_.de_call (de_call);
|
decodes_proxy_model_.de_call (de_call);
|
||||||
decodes_proxy_model_.rx_df (rx_df);
|
decodes_proxy_model_.rx_df (rx_df);
|
||||||
QString special;
|
QString special;
|
||||||
@ -288,21 +423,25 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const&
|
|||||||
.arg (de_grid.size () ? '(' + de_grid + ')' : QString {})
|
.arg (de_grid.size () ? '(' + de_grid + ')' : QString {})
|
||||||
.arg (special)
|
.arg (special)
|
||||||
: QString {});
|
: QString {});
|
||||||
mode_label_->setText (QString {"Mode: %1%2%3%4"}
|
update_line_edit (mode_line_edit_, mode);
|
||||||
.arg (mode)
|
update_spin_box (frequency_tolerance_spin_box_, frequency_tolerance
|
||||||
.arg (sub_mode)
|
, quint32_max == frequency_tolerance ? QString {"n/a"} : QString {});
|
||||||
.arg (fast_mode && !mode.contains (QRegularExpression {R"(ISCAT|MSK144)"}) ? "fast" : "")
|
update_line_edit (submode_line_edit_, submode, false);
|
||||||
|
tx_mode_label_->setText (QString {"Tx Mode: %1"}
|
||||||
.arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')'));
|
.arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')'));
|
||||||
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
||||||
dx_label_->setText (dx_call.size () >= 0 ? QString {"DX: %1%2"}.arg (dx_call)
|
update_line_edit (dx_call_line_edit_, dx_call);
|
||||||
.arg (dx_grid.size () ? '(' + dx_grid + ')' : QString {}) : QString {});
|
update_line_edit (dx_grid_line_edit_, dx_grid);
|
||||||
rx_df_label_->setText (rx_df >= 0 ? QString {"Rx: %1"}.arg (rx_df) : "");
|
if (rx_df != quint32_max) update_spin_box (rx_df_spin_box_, rx_df);
|
||||||
tx_df_label_->setText (tx_df >= 0 ? QString {"Tx: %1"}.arg (tx_df) : "");
|
update_spin_box (tr_period_spin_box_, tr_period
|
||||||
|
, quint32_max == tr_period ? QString {"n/a"} : QString {});
|
||||||
|
tx_df_label_->setText (QString {"Tx: %1"}.arg (tx_df));
|
||||||
report_label_->setText ("SNR: " + report);
|
report_label_->setText ("SNR: " + report);
|
||||||
update_dynamic_property (frequency_label_, "transmitting", transmitting);
|
update_dynamic_property (frequency_label_, "transmitting", transmitting);
|
||||||
auto_off_button_->setEnabled (tx_enabled);
|
auto_off_button_->setEnabled (tx_enabled);
|
||||||
halt_tx_button_->setEnabled (transmitting);
|
halt_tx_button_->setEnabled (transmitting);
|
||||||
update_dynamic_property (mode_label_, "decoding", decoding);
|
update_line_edit (configuration_line_edit_, configuration_name);
|
||||||
|
update_dynamic_property (mode_line_edit_, "decoding", decoding);
|
||||||
update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout);
|
update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#ifndef WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
#ifndef WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||||
#define WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
#define WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||||
|
|
||||||
|
#include <QDockWidget>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QtWidgets>
|
|
||||||
|
|
||||||
#include "MessageServer.hpp"
|
#include "MessageServer.hpp"
|
||||||
|
|
||||||
@ -13,6 +13,20 @@ class QAbstractItemModel;
|
|||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
class QColor;
|
class QColor;
|
||||||
class QAction;
|
class QAction;
|
||||||
|
class QListWidget;
|
||||||
|
class QFormLayout;
|
||||||
|
class QVBoxLayout;
|
||||||
|
class QHBoxLayout;
|
||||||
|
class QStackedLayout;
|
||||||
|
class QTableView;
|
||||||
|
class QLineEdit;
|
||||||
|
class QAbstractButton;
|
||||||
|
class QLabel;
|
||||||
|
class QCheckBox;
|
||||||
|
class QSpinBox;
|
||||||
|
class QFrame;
|
||||||
|
class QStatusBar;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
|
||||||
using Frequency = MessageServer::Frequency;
|
using Frequency = MessageServer::Frequency;
|
||||||
|
|
||||||
@ -25,16 +39,18 @@ public:
|
|||||||
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
||||||
, QString const& id, QString const& version, QString const& revision
|
, QString const& id, QString const& version, QString const& revision
|
||||||
, QListWidget const * calls_of_interest, QWidget * parent = nullptr);
|
, QListWidget const * calls_of_interest, QWidget * parent = nullptr);
|
||||||
|
void dispose ();
|
||||||
~ClientWidget ();
|
~ClientWidget ();
|
||||||
|
|
||||||
bool fast_mode () const {return fast_mode_;}
|
bool fast_mode () const;
|
||||||
|
|
||||||
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
|
||||||
, QString const& report, QString const& tx_mode, bool tx_enabled
|
, QString const& report, QString const& tx_mode, bool tx_enabled
|
||||||
, bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df
|
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
|
||||||
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
, QString const& de_call, QString const& de_grid, QString const& dx_grid
|
||||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
||||||
, quint8 special_op_mode);
|
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
|
||||||
|
, QString const& configuration_name);
|
||||||
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr
|
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr
|
||||||
, float delta_time, quint32 delta_frequency, QString const& mode
|
, float delta_time, quint32 delta_frequency, QString const& mode
|
||||||
, QString const& message, bool low_confidence, bool off_air);
|
, QString const& message, bool low_confidence, bool off_air);
|
||||||
@ -45,6 +61,7 @@ public:
|
|||||||
Q_SLOT void decodes_cleared (QString const& client_id);
|
Q_SLOT void decodes_cleared (QString const& client_id);
|
||||||
|
|
||||||
Q_SIGNAL void do_clear_decodes (QString const& id, quint8 window = 0);
|
Q_SIGNAL void do_clear_decodes (QString const& id, quint8 window = 0);
|
||||||
|
Q_SIGNAL void do_close (QString const& id);
|
||||||
Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier);
|
Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier);
|
||||||
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only);
|
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only);
|
||||||
Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
|
Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
|
||||||
@ -52,30 +69,39 @@ public:
|
|||||||
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
|
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
|
||||||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||||
, bool last_only = false);
|
, bool last_only = false);
|
||||||
|
Q_SIGNAL void switch_configuration (QString const& id, QString const& configuration_name);
|
||||||
|
Q_SIGNAL void configure (QString const& id, QString const& mode, quint32 frequency_tolerance
|
||||||
|
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
|
||||||
|
, QString const& dx_call, QString const& dx_grid, bool generate_messages);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString id_;
|
|
||||||
QListWidget const * calls_of_interest_;
|
|
||||||
class IdFilterModel final
|
class IdFilterModel final
|
||||||
: public QSortFilterProxyModel
|
: public QSortFilterProxyModel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IdFilterModel (QString const& client_id);
|
IdFilterModel (QString const& client_id, QObject * = nullptr);
|
||||||
|
|
||||||
void de_call (QString const&);
|
void de_call (QString const&);
|
||||||
void rx_df (int);
|
void rx_df (quint32);
|
||||||
|
|
||||||
QVariant data (QModelIndex const& proxy_index, int role = Qt::DisplayRole) const override;
|
QVariant data (QModelIndex const& proxy_index, int role = Qt::DisplayRole) const override;
|
||||||
|
private:
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override;
|
bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override;
|
||||||
|
|
||||||
private:
|
|
||||||
QString client_id_;
|
QString client_id_;
|
||||||
QString call_;
|
QString call_;
|
||||||
QRegularExpression base_call_re_;
|
QRegularExpression base_call_re_;
|
||||||
int rx_df_;
|
quint32 rx_df_;
|
||||||
} decodes_proxy_model_;
|
};
|
||||||
|
|
||||||
|
void closeEvent (QCloseEvent *) override;
|
||||||
|
|
||||||
|
QString id_;
|
||||||
|
bool done_;
|
||||||
|
QListWidget const * calls_of_interest_;
|
||||||
|
IdFilterModel decodes_proxy_model_;
|
||||||
|
IdFilterModel beacons_proxy_model_;
|
||||||
|
|
||||||
QAction * erase_action_;
|
QAction * erase_action_;
|
||||||
QAction * erase_rx_frequency_action_;
|
QAction * erase_rx_frequency_action_;
|
||||||
QAction * erase_both_action_;
|
QAction * erase_both_action_;
|
||||||
@ -83,17 +109,39 @@ private:
|
|||||||
QTableView * beacons_table_view_;
|
QTableView * beacons_table_view_;
|
||||||
QLineEdit * message_line_edit_;
|
QLineEdit * message_line_edit_;
|
||||||
QLineEdit * grid_line_edit_;
|
QLineEdit * grid_line_edit_;
|
||||||
QStackedLayout * decodes_stack_;
|
QAbstractButton * generate_messages_push_button_;
|
||||||
QAbstractButton * auto_off_button_;
|
QAbstractButton * auto_off_button_;
|
||||||
QAbstractButton * halt_tx_button_;
|
QAbstractButton * halt_tx_button_;
|
||||||
QLabel * de_label_;
|
QLabel * de_label_;
|
||||||
QLabel * mode_label_;
|
|
||||||
bool fast_mode_;
|
|
||||||
QLabel * frequency_label_;
|
QLabel * frequency_label_;
|
||||||
QLabel * dx_label_;
|
|
||||||
QLabel * rx_df_label_;
|
|
||||||
QLabel * tx_df_label_;
|
QLabel * tx_df_label_;
|
||||||
QLabel * report_label_;
|
QLabel * report_label_;
|
||||||
|
QLineEdit * configuration_line_edit_;
|
||||||
|
QLineEdit * mode_line_edit_;
|
||||||
|
QSpinBox * frequency_tolerance_spin_box_;
|
||||||
|
QLabel * tx_mode_label_;
|
||||||
|
QLineEdit * submode_line_edit_;
|
||||||
|
QCheckBox * fast_mode_check_box_;
|
||||||
|
QSpinBox * tr_period_spin_box_;
|
||||||
|
QSpinBox * rx_df_spin_box_;
|
||||||
|
QLineEdit * dx_call_line_edit_;
|
||||||
|
QLineEdit * dx_grid_line_edit_;
|
||||||
|
QWidget * decodes_page_;
|
||||||
|
QWidget * beacons_page_;
|
||||||
|
QFrame * content_widget_;
|
||||||
|
QStatusBar * status_bar_;
|
||||||
|
QDialogButtonBox * control_button_box_;
|
||||||
|
|
||||||
|
QFormLayout * form_layout_;
|
||||||
|
QHBoxLayout * horizontal_layout_;
|
||||||
|
QFormLayout * subform1_layout_;
|
||||||
|
QFormLayout * subform2_layout_;
|
||||||
|
QFormLayout * subform3_layout_;
|
||||||
|
QVBoxLayout * decodes_layout_;
|
||||||
|
QVBoxLayout * beacons_layout_;
|
||||||
|
QVBoxLayout * content_layout_;
|
||||||
|
QStackedLayout * decodes_stack_;
|
||||||
|
|
||||||
bool columns_resized_;
|
bool columns_resized_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -250,12 +250,15 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const&
|
|||||||
connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added);
|
connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added);
|
||||||
connect (server_, &MessageServer::decodes_cleared, dock, &ClientWidget::decodes_cleared);
|
connect (server_, &MessageServer::decodes_cleared, dock, &ClientWidget::decodes_cleared);
|
||||||
connect (dock, &ClientWidget::do_clear_decodes, server_, &MessageServer::clear_decodes);
|
connect (dock, &ClientWidget::do_clear_decodes, server_, &MessageServer::clear_decodes);
|
||||||
|
connect (dock, &ClientWidget::do_close, server_, &MessageServer::close);
|
||||||
connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply);
|
connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply);
|
||||||
connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx);
|
connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx);
|
||||||
connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text);
|
connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text);
|
||||||
connect (dock, &ClientWidget::location, server_, &MessageServer::location);
|
connect (dock, &ClientWidget::location, server_, &MessageServer::location);
|
||||||
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
|
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
|
||||||
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
|
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
|
||||||
|
connect (dock, &ClientWidget::switch_configuration, server_, &MessageServer::switch_configuration);
|
||||||
|
connect (dock, &ClientWidget::configure, server_, &MessageServer::configure);
|
||||||
dock_widgets_[id] = dock;
|
dock_widgets_[id] = dock;
|
||||||
server_->replay (id); // request decodes and status
|
server_->replay (id); // request decodes and status
|
||||||
}
|
}
|
||||||
@ -265,7 +268,7 @@ void MessageAggregatorMainWindow::remove_client (QString const& id)
|
|||||||
auto iter = dock_widgets_.find (id);
|
auto iter = dock_widgets_.find (id);
|
||||||
if (iter != std::end (dock_widgets_))
|
if (iter != std::end (dock_widgets_))
|
||||||
{
|
{
|
||||||
(*iter)->close ();
|
(*iter)->dispose ();
|
||||||
dock_widgets_.erase (iter);
|
dock_widgets_.erase (iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ public:
|
|||||||
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
|
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
|
||||||
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
|
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
|
||||||
, bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/
|
, bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/
|
||||||
, quint8 /*special_op_mode*/)
|
, quint8 /*special_op_mode*/, quint32 /*frequency_tolerance*/, quint32 /*tr_period*/
|
||||||
|
, QString const& /*configuration_name*/)
|
||||||
{
|
{
|
||||||
if (id == id_)
|
if (id == id_)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Version number components
|
# Version number components
|
||||||
set (WSJTX_VERSION_MAJOR 2)
|
set (WSJTX_VERSION_MAJOR 2)
|
||||||
set (WSJTX_VERSION_MINOR 0)
|
set (WSJTX_VERSION_MINOR 1)
|
||||||
set (WSJTX_VERSION_PATCH 1)
|
set (WSJTX_VERSION_PATCH 0)
|
||||||
set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions
|
set (WSJTX_RC 8) # 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 1) # set to 1 for final release build
|
||||||
|
@ -44,6 +44,8 @@ namespace
|
|||||||
class Dialog
|
class Dialog
|
||||||
: public QDialog
|
: public QDialog
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using BandList = QList<QString>;
|
using BandList = QList<QString>;
|
||||||
|
|
||||||
@ -69,6 +71,8 @@ private:
|
|||||||
static int constexpr band_index_role {Qt::UserRole};
|
static int constexpr band_index_role {Qt::UserRole};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "WSPRBandHopping.moc"
|
||||||
|
|
||||||
Dialog::Dialog (QSettings * settings, Configuration const * configuration, BandList const * WSPR_bands
|
Dialog::Dialog (QSettings * settings, Configuration const * configuration, BandList const * WSPR_bands
|
||||||
, QBitArray * bands, int * gray_line_duration, QWidget * parent)
|
, QBitArray * bands, int * gray_line_duration, QWidget * parent)
|
||||||
: QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint}
|
: QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint}
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <cstdbool>
|
#include <cstdbool>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#else
|
#endif
|
||||||
|
#ifndef __cplusplus
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -51,10 +51,11 @@ DecodedText::DecodedText (QString const& the_string)
|
|||||||
|
|
||||||
QStringList DecodedText::messageWords () const
|
QStringList DecodedText::messageWords () const
|
||||||
{
|
{
|
||||||
if (is_standard_)
|
if(is_standard_) {
|
||||||
{
|
|
||||||
// extract up to the first four message words
|
// extract up to the first four message words
|
||||||
return words_re.match (message_).capturedTexts ();
|
QString t=message_;
|
||||||
|
if(t.left(4)=="TU; ") t=message_.mid(4,-1);
|
||||||
|
return words_re.match(t).capturedTexts();
|
||||||
}
|
}
|
||||||
// simple word split for free text messages
|
// simple word split for free text messages
|
||||||
auto words = message_.split (' ', QString::SkipEmptyParts);
|
auto words = message_.split (' ', QString::SkipEmptyParts);
|
||||||
|
@ -52,6 +52,7 @@ set (UG_SRCS
|
|||||||
tutorial-example1.adoc
|
tutorial-example1.adoc
|
||||||
tutorial-example2.adoc
|
tutorial-example2.adoc
|
||||||
tutorial-example3.adoc
|
tutorial-example3.adoc
|
||||||
|
tutorial-example4.adoc
|
||||||
tutorial-main-window.adoc
|
tutorial-main-window.adoc
|
||||||
tutorial-wide-graph-settings.adoc
|
tutorial-wide-graph-settings.adoc
|
||||||
utilities.adoc
|
utilities.adoc
|
||||||
@ -77,6 +78,8 @@ 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/ft4_decodes.png
|
||||||
|
images/ft4_waterfall.png
|
||||||
images/ft8_decodes.png
|
images/ft8_decodes.png
|
||||||
images/FT8_waterfall.png
|
images/FT8_waterfall.png
|
||||||
images/help-menu.png
|
images/help-menu.png
|
||||||
|
@ -91,7 +91,8 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
|
|||||||
: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]
|
||||||
:win_openssl_packages: https://slproweb.com/products/Win32OpenSSL.html[Windows OpenSSL Packages]
|
:win_openssl_packages: https://slproweb.com/products/Win32OpenSSL.html[Windows OpenSSL Packages]
|
||||||
:win32_openssl: https://slproweb.com/download/Win32OpenSSL_Light-1_0_2q.exe[Win32 OpenSSL Lite Package]
|
:win32_openssl: https://slproweb.com/download/Win32OpenSSL_Light-1_0_2r.exe[Win32 OpenSSL Lite Package]
|
||||||
|
:win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2r.exe[Win64 OpenSSL Lite Package]
|
||||||
:writelog: https://writelog.com/[Writelog]
|
:writelog: https://writelog.com/[Writelog]
|
||||||
:wsjt_yahoo_group: https://groups.yahoo.com/neo/groups/wsjtgroup/info[WSJT Group]
|
:wsjt_yahoo_group: https://groups.yahoo.com/neo/groups/wsjtgroup/info[WSJT Group]
|
||||||
:wsjtx: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X]
|
:wsjtx: http://physics.princeton.edu/pulsar/K1JT/wsjtx.html[WSJT-X]
|
||||||
@ -115,6 +116,7 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
|
|||||||
:QRA64_EME: http://physics.princeton.edu/pulsar/K1JT/QRA64_EME.pdf[QRA64 for microwave EME]
|
:QRA64_EME: http://physics.princeton.edu/pulsar/K1JT/QRA64_EME.pdf[QRA64 for microwave EME]
|
||||||
:svn: http://subversion.apache.org/packages.html#windows[Subversion]
|
:svn: http://subversion.apache.org/packages.html#windows[Subversion]
|
||||||
:win32: http://physics.princeton.edu/pulsar/K1JT/wsjtx-{VERSION}-win32.exe[wsjtx-{VERSION}-win32.exe]
|
:win32: http://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]
|
||||||
: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: http://physics.princeton.edu/pulsar/K1JT/WSPRcode.exe[WSPRcode.exe]
|
||||||
|
@ -95,3 +95,19 @@ QT_QPA_PLATFORMTHEME set to empty (the space after the '=' character
|
|||||||
is necessary):
|
is necessary):
|
||||||
|
|
||||||
QT_QPA_PLATFORMTHEME= wsjtx
|
QT_QPA_PLATFORMTHEME= wsjtx
|
||||||
|
|
||||||
|
I am running _WSJT-X_ on Linux using a KDE desktop. Why does *Menu->Configurations* misbehave?::
|
||||||
|
|
||||||
|
The KDE development team have added code to Qt that tries to
|
||||||
|
automatically add shortcut accelerator keys to all buttons including
|
||||||
|
pop up menu buttons, this interferes with operation of the application
|
||||||
|
(many other Qt applications have similar issues with KDE). Until this
|
||||||
|
is fixed by the KDE team you must disable this misfeature. Edit the
|
||||||
|
file ~/.config/kdeglobals and add a section containing the following:
|
||||||
|
|
||||||
|
[Development]
|
||||||
|
AutoCheckAccelerators=false
|
||||||
|
|
||||||
|
+
|
||||||
|
See https://stackoverflow.com/a/32711483 and
|
||||||
|
https://bugs.kde.org/show_bug.cgi?id=337491 for more details.
|
||||||
|
BIN
doc/user_guide/en/images/ft4_decodes.png
Normal file
BIN
doc/user_guide/en/images/ft4_decodes.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
BIN
doc/user_guide/en/images/ft4_waterfall.png
Normal file
BIN
doc/user_guide/en/images/ft4_waterfall.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
@ -1,11 +1,12 @@
|
|||||||
// Status=review
|
// Status=review
|
||||||
|
|
||||||
Download and execute the package file {win32}, following these
|
Download and execute the package file {win32} (WinXP, Vista, Win 7,
|
||||||
instructions:
|
Win 8, Win10, 32-bit) or {win64} (Vista, Win 7, Win 8, Win10, 64-bit)
|
||||||
|
following these instructions:
|
||||||
|
|
||||||
* Install _WSJT-X_ into its own directory, for example `C:\WSJTX` or `
|
* Install _WSJT-X_ into its own directory, for example `C:\WSJTX` or `
|
||||||
C:\WSJT\WSJTX`, rather than the conventional location `C:\Program
|
C:\WSJT\WSJTX`, rather than the conventional location `C:\Program
|
||||||
Files (x86)\WSJTX`.
|
Files ...\WSJTX`.
|
||||||
|
|
||||||
* All program files relating to _WSJT-X_ will be stored in the chosen
|
* All program files relating to _WSJT-X_ will be stored in the chosen
|
||||||
installation directory and its subdirectories.
|
installation directory and its subdirectories.
|
||||||
@ -29,26 +30,31 @@ TIP: Your computer may be configured so that this directory is
|
|||||||
|
|
||||||
[[OPENSSL]]
|
[[OPENSSL]]
|
||||||
|
|
||||||
* image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error, role="right"]
|
* image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error,
|
||||||
From this version onward _WSJT-X_ requires the _OpenSSL_ libraries
|
role="right"] _WSJT-X_ requires the _OpenSSL_ libraries to be
|
||||||
to be installed. Suitable libraries may already be installed on your
|
installed. Suitable libraries may already be installed on your
|
||||||
system, if they are not you will see this error shortly after
|
system, if they are not you will see this error shortly after
|
||||||
startup. To fix this you need to install the _OpenSSL_ libraries.
|
startup. To fix this you need to install the _OpenSSL_ libraries.
|
||||||
|
|
||||||
** You can download a suitable _OpenSSL_ package for from
|
** You can download a suitable _OpenSSL_ package for from
|
||||||
{win_openssl_packages}, you need the latest *Win32 v1.0.2 Lite*
|
{win_openssl_packages}, you need the latest *Windows v1.0.2 Lite*
|
||||||
version (Note it is the Win32 package even if you are using a
|
version. For the 32-bit _WSJT-X_ build use the Win32 version of the
|
||||||
64-bit Windows operating system) which at the time of writing was
|
_OpenSSL_ libraries, for the 64-bit _WSJT-X_ use the Win64 version
|
||||||
{win32_openssl}.
|
of the _OpenSSL_ libraries (Note it is OK to install both versions
|
||||||
|
on a 64-bit system) which at the time of writing were
|
||||||
|
{win32_openssl} and {win64_openssl} respectively.
|
||||||
|
|
||||||
** Install the package and accept the default options, including the
|
** Install the package and accept the default options, including the
|
||||||
option to copy the _OpenSSL_ DLLs to the Windows system directory
|
option to copy the _OpenSSL_ DLLs to the Windows system
|
||||||
(this is important). +
|
directory. There is no obligation to donate to the _OpenSSL_
|
||||||
|
project, un-check all the donation options if desired. +
|
||||||
|
|
||||||
NOTE: If you still get the same network error after installing the
|
NOTE: If you still get the same network error after installing the
|
||||||
_OpenSSL_ libraries then you also need to install the
|
_OpenSSL_ libraries then you also need to install the
|
||||||
{msvcpp_redist} component. From the download page select
|
{msvcpp_redist} component. From the download page select
|
||||||
`vcredist_x86.exe` and run it to install.
|
`vcredist_x86.exe` for use with the 32-bit _WSJT-X_ build or
|
||||||
|
`vcredist_x64.exe` with the 64-bit build, then run it to
|
||||||
|
install.
|
||||||
|
|
||||||
TIP: If you cannot install the _OpenSSL_ libraries or do not have an
|
TIP: If you cannot install the _OpenSSL_ libraries or do not have an
|
||||||
Internet connection on the computer used to run
|
Internet connection on the computer used to run
|
||||||
|
@ -7,10 +7,10 @@ K1**JT**,`" while the suffix "`-X`" indicates that _WSJT-X_ started as
|
|||||||
an extended and experimental branch of the program
|
an extended and experimental branch of the program
|
||||||
_WSJT_.
|
_WSJT_.
|
||||||
|
|
||||||
_WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers nine different
|
_WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers ten different
|
||||||
protocols or modes: *FT8*, *JT4*, *JT9*, *JT65*, *QRA64*, *ISCAT*,
|
protocols or modes: *FT4*, *FT8*, *JT4*, *JT9*, *JT65*, *QRA64*,
|
||||||
*MSK144*, *WSPR*, and *Echo*. The first five are designed for making
|
*ISCAT*, *MSK144*, *WSPR*, and *Echo*. The first six are designed for
|
||||||
reliable QSOs under extreme weak-signal conditions. They use nearly
|
making reliable QSOs under weak-signal conditions. They use nearly
|
||||||
identical message structure and source encoding. JT65 and QRA64 were
|
identical message structure and source encoding. JT65 and QRA64 were
|
||||||
designed for EME ("`moonbounce`") on the VHF/UHF bands and have also
|
designed for EME ("`moonbounce`") on the VHF/UHF bands and have also
|
||||||
proven very effective for worldwide QRP communication on the HF bands.
|
proven very effective for worldwide QRP communication on the HF bands.
|
||||||
@ -25,12 +25,19 @@ one-minute timed sequences of alternating transmission and reception,
|
|||||||
so a minimal QSO takes four to six minutes — two or three
|
so a minimal QSO takes four to six minutes — two or three
|
||||||
transmissions by each station, one sending in odd UTC minutes and the
|
transmissions by each station, one sending in odd UTC minutes and the
|
||||||
other even. FT8 is operationally similar but four times faster
|
other even. FT8 is operationally similar but four times faster
|
||||||
(15-second T/R sequences) and less sensitive by a few dB. On the HF
|
(15-second T/R sequences) and less sensitive by a few dB. FT4 is
|
||||||
bands, world-wide QSOs are possible with any of these modes using
|
faster still (7.5 s T/R sequences) and especially well suited for
|
||||||
power levels of a few watts (or even milliwatts) and compromise
|
radio contesting. On the HF bands, world-wide QSOs are possible with
|
||||||
antennas. On VHF bands and higher, QSOs are possible (by EME and
|
any of these modes using power levels of a few watts (or even
|
||||||
other propagation types) at signal levels 10 to 15 dB below those
|
milliwatts) and compromise antennas. On VHF bands and higher, QSOs
|
||||||
required for CW.
|
are possible (by EME and other propagation types) at signal levels 10
|
||||||
|
to 15 dB below those required for CW.
|
||||||
|
|
||||||
|
Note that even though their T/R sequences are short, FT4 and FT8 are
|
||||||
|
classified as slow modes because their message frames are sent only
|
||||||
|
once per transmission. All fast modes in _WSJT-X_ send their message
|
||||||
|
frames repeatedly, as many times as will fit into the Tx sequence
|
||||||
|
length.
|
||||||
|
|
||||||
*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
|
||||||
@ -65,10 +72,10 @@ are available for all three platforms.
|
|||||||
|
|
||||||
*Version Numbers:* _WSJT-X_ release numbers have major, minor, and
|
*Version Numbers:* _WSJT-X_ release numbers have major, minor, and
|
||||||
patch numbers separated by periods: for example, _WSJT-X_ Version
|
patch numbers separated by periods: for example, _WSJT-X_ Version
|
||||||
1.9.0. Temporary "`beta`" release candidates are sometimes made in
|
2.1.0. Temporary _beta release_ candidates are sometimes made in
|
||||||
advance of a new general-availability release, in order to obtain user
|
advance of a new general-availability release, in order to obtain user
|
||||||
feedback. For example, version 1.9.0-rc1, 1.9.0-rc2, etc., would
|
feedback. For example, version 2.1.0-rc1, 2.1.0-rc2, etc., would
|
||||||
be beta releases leading up to the final release of v1.9.0.
|
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
|
||||||
|
@ -1,40 +1,15 @@
|
|||||||
=== New in Version {VERSION}
|
=== New in Version {VERSION}
|
||||||
|
|
||||||
For quick reference, here's a short list of features and capabilities
|
The most important feature added to _WSJT-X_ since Version 2.0.1 is
|
||||||
added to _WSJT-X_ since Version 1.9.1:
|
the new *FT4 protocol*, designed especially for radio contesting. It
|
||||||
|
has T/R sequence length 7.5 s, bandwidth 80 Hz, and threshold
|
||||||
- New FT8 and MSK144 protocols with 77-bit payloads permit these enhancements:
|
sensitivity -17.5 dB. Version 2.1.0 also has improvements to FT8
|
||||||
|
waveform generation, waterfall and spectrum display, contest logging,
|
||||||
* Optimized contest messages for NA VHF, EU VHF, Field Day, RTTY Roundup
|
rig control, the user interface, keyboard shortcuts, UDP messaging for
|
||||||
|
inter-program communication, and accessibility, as well as a number of
|
||||||
* Full support for "/R" and "/P" calls in relevant contests
|
more minor enhancements and bug fixes. We now provide a separate
|
||||||
|
installation package for 64-bit Windows Vista and later, offering
|
||||||
* New logging features for contesting
|
significant improvements in decoding speed.
|
||||||
|
|
||||||
* Integration with {n1mm_logger} and {writelog} for contesting
|
|
||||||
|
|
||||||
* Improved support for compound and nonstandard callsigns
|
|
||||||
|
|
||||||
* Nearly equal (or better) sensitivity compared to old protocols
|
|
||||||
|
|
||||||
* Lower false decode rates
|
|
||||||
|
|
||||||
- Improved color highlighting of received messages
|
|
||||||
|
|
||||||
- Improved WSPR sensitivity
|
|
||||||
|
|
||||||
- Expanded and improved UDP messages sent to companion programs
|
|
||||||
|
|
||||||
- Bug fixes and other minor tweaks to user interface
|
|
||||||
|
|
||||||
IMPORTANT: Note that for FT8 and MSK144 there is no backward
|
|
||||||
compatibility with WSJT-X 1.9.1 and earlier. Everyone using these
|
|
||||||
modes should upgrade to WSJT-X 2.0 by January 1, 2019.
|
|
||||||
|
|
||||||
IMPORTANT: _WSJT-X_ Version 2.0 drops support for Apple Mac OS X 10.9
|
|
||||||
(Mavericks). It is possible to build from source for this operating
|
|
||||||
system version but the DMG installer package requires 10.10 or later.
|
|
||||||
|
|
||||||
|
|
||||||
=== Documentation Conventions
|
=== Documentation Conventions
|
||||||
|
|
||||||
|
@ -12,10 +12,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 FT8 and MSK144 contains 77 bits. The 5
|
The information payload for FT4, FT8, and MSK144 contains 77 bits.
|
||||||
additional bits are used to flag special message types used for FT8
|
The 5 new bits added to the original 72 are used to flag special
|
||||||
DXpedition Mode, contesting, nonstandard callsigns, and a few other
|
message types signifying special message types used for FT8 DXpedition
|
||||||
special types.
|
Mode, contesting, nonstandard callsigns, and a few other
|
||||||
|
possibilities.
|
||||||
|
|
||||||
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
|
||||||
@ -67,18 +68,29 @@ _WSJT-X_ modes have continuous phase and constant envelope.
|
|||||||
[[SLOW_MODES]]
|
[[SLOW_MODES]]
|
||||||
=== Slow Modes
|
=== Slow Modes
|
||||||
|
|
||||||
|
[[FT4PRO]]
|
||||||
|
==== FT4
|
||||||
|
|
||||||
|
Forward error correction (FEC) in FT4 uses a low-density parity check
|
||||||
|
(LDPC) code with 77 information bits, a 14-bit cyclic redundancy check
|
||||||
|
(CRC), and 83 parity bits making a 174-bit codeword. It is thus
|
||||||
|
called an LDPC (174,91) code. Synchronization uses four 4×4 Costas
|
||||||
|
arrays, and ramp-up and ramp-down symbols are inserted at the start
|
||||||
|
and end of each transmission. Modulation is 4-tone frequency-shift
|
||||||
|
keying (4-GFSK) with Gaussian smoothing of frequency transitions. The
|
||||||
|
keying rate is 12000/576 = 20.8333 baud. Each transmitted symbol
|
||||||
|
conveys two bits, so the total number of channel symbols is 174/2 + 16
|
||||||
|
+ 2 = 105. The total bandwidth is 4 × 20.8333 = 83.3 Hz.
|
||||||
|
|
||||||
[[FT8PRO]]
|
[[FT8PRO]]
|
||||||
==== FT8
|
==== FT8
|
||||||
|
|
||||||
Forward error correction (FEC) in FT8 uses a low-density parity check
|
FT8 uses the same LDPC (174,91) code as FT4. Modulation is 8-tone
|
||||||
(LDPC) code with 77 information bits, a 14-bit cyclic redundancy check
|
frequency-shift keying (8-GFSK) at 12000/1920 = 6.25 baud.
|
||||||
(CRC), and 83 parity bits making a 174-bit codeword. It is thus
|
Synchronization uses 7×7 Costas arrays at the beginning, middle, and
|
||||||
called an LDPC (174,91) code. Synchronization uses 7×7 Costas arrays
|
end of each transmission. Transmitted symbols carry three bits, so
|
||||||
at the beginning, middle, and end of each transmission. Modulation is
|
the total number of channel symbols is 174/3 + 21 = 79. The total
|
||||||
8-tone frequency-shift keying (8-FSK) at 12000/1920 = 6.25 baud. Each
|
occupied bandwidth is 8 × 6.25 = 50 Hz.
|
||||||
transmitted symbol carries three bits, so the total number of channel
|
|
||||||
symbols is 174/3 + 21 = 79. The total occupied bandwidth is 8 × 6.25
|
|
||||||
= 50 Hz.
|
|
||||||
|
|
||||||
[[JT4PRO]]
|
[[JT4PRO]]
|
||||||
==== JT4
|
==== JT4
|
||||||
@ -227,7 +239,8 @@ which the probability of decoding is 50% or higher.
|
|||||||
|===============================================================================
|
|===============================================================================
|
||||||
|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)
|
||||||
|FT8 |LDPC, r=1/2|(174,91)| 8| 8-FSK| 6.25 | 50.0 | 0.27| 12.6 | -21
|
|FT4 |LDPC, r=1/2|(174,91)| 4| 4-GFSK| 20.8333 | 83.3 | 0.15| 5.04 | -17.5
|
||||||
|
|FT8 |LDPC, r=1/2|(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 | -27
|
||||||
|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
|
||||||
@ -246,6 +259,7 @@ comparable to tone spacing.
|
|||||||
[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
|
|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
|
||||||
|
52
doc/user_guide/en/tutorial-example4.adoc
Normal file
52
doc/user_guide/en/tutorial-example4.adoc
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Status=review
|
||||||
|
.Main Window:
|
||||||
|
- Select *FT4* on the *Mode* menu.
|
||||||
|
- Double-click on *Erase* to clear both text windows.
|
||||||
|
|
||||||
|
.Wide Graph Settings:
|
||||||
|
|
||||||
|
- *Bins/Pixel* = 7, *Start* = 100 Hz, *N Avg* = 1
|
||||||
|
- Adjust the width of the Wide Graph window so that the upper
|
||||||
|
frequency limit is approximately 4000 Hz.
|
||||||
|
|
||||||
|
.Open a Wave File:
|
||||||
|
|
||||||
|
- Select *File | Open* and navigate to
|
||||||
|
+...\save\samples\FT4\000000_000002.wav+. The waterfall and Band
|
||||||
|
Activity window should look something like the following screen shots.
|
||||||
|
This sample file was recorded during a practice contest test session, so
|
||||||
|
most of the decoded messages use the *RTTY Roundup* message formats.
|
||||||
|
|
||||||
|
[[X15]]
|
||||||
|
image::ft4_waterfall.png[align="left",alt="Wide Graph Decode FT4"]
|
||||||
|
|
||||||
|
image::ft4_decodes.png[align="left"]
|
||||||
|
|
||||||
|
- Click with the mouse anywhere on the waterfall display. The green Rx
|
||||||
|
frequency marker will jump to your selected frequency, and the Rx
|
||||||
|
frequency control on the main window will be updated accordingly.
|
||||||
|
|
||||||
|
- Do the same thing with the *Shift* key held down. Now the red Tx
|
||||||
|
frequency marker and its associated control on the main window will
|
||||||
|
follow your frequency selections.
|
||||||
|
|
||||||
|
- Do the same thing with the *Ctrl* key held down. Now the both colored
|
||||||
|
markers and both spinner controls will follow your selections.
|
||||||
|
|
||||||
|
- Now double-click on any of the the lines of decoded text in the Band
|
||||||
|
Activity window. Any line will show similar behavior, setting
|
||||||
|
Rx frequency to that of the selected message and leaving Tx frequency
|
||||||
|
unchanged. To change both Rx and Tx frequencies, hold *Ctrl* down
|
||||||
|
when double-clicking.
|
||||||
|
|
||||||
|
TIP: To avoid QRM from competing callers, it is frequently desirable
|
||||||
|
to answer a CQ on a different frequency from that of the CQing
|
||||||
|
station. The same is true when you tail-end another QSO. Choose a Tx
|
||||||
|
frequency that appears to be not in use. You might want to check the
|
||||||
|
box *Hold Tx Freq*.
|
||||||
|
|
||||||
|
TIP: Keyboard shortcuts *Shift+F11* and *Shift+F12* provide an easy
|
||||||
|
way to move your FT4 Tx frequency down or up in 90 Hz steps.
|
||||||
|
|
||||||
|
IMPORTANT: When finished with this Tutorial, don't forget to re-enter
|
||||||
|
your own callsign as *My Call* on the *Settings | General* tab.
|
@ -140,6 +140,10 @@ include::tutorial-example2.adoc[]
|
|||||||
=== FT8
|
=== FT8
|
||||||
include::tutorial-example3.adoc[]
|
include::tutorial-example3.adoc[]
|
||||||
|
|
||||||
|
[[TUT_EX4]]
|
||||||
|
=== FT4
|
||||||
|
include::tutorial-example4.adoc[]
|
||||||
|
|
||||||
[[MAKE_QSOS]]
|
[[MAKE_QSOS]]
|
||||||
== Making QSOs
|
== Making QSOs
|
||||||
include::make-qso.adoc[]
|
include::make-qso.adoc[]
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
#ifndef DATE_TIME_AS_SECS_SINCE_EPOCH_DELEGATE_HPP_
|
|
||||||
#define DATE_TIME_AS_SECS_SINCE_EPOCH_DELEGATE_HPP_
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QLocale>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <QDateTimeEdit>
|
|
||||||
|
|
||||||
class DateTimeAsSecsSinceEpochDelegate final
|
|
||||||
: public QStyledItemDelegate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DateTimeAsSecsSinceEpochDelegate (QObject * parent = nullptr)
|
|
||||||
: QStyledItemDelegate {parent}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static QVariant to_secs_since_epoch (QDateTime const& date_time)
|
|
||||||
{
|
|
||||||
return date_time.toMSecsSinceEpoch () / 1000ull;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QDateTime to_date_time (QModelIndex const& index, int role = Qt::DisplayRole)
|
|
||||||
{
|
|
||||||
return to_date_time (index.model ()->data (index, role));
|
|
||||||
}
|
|
||||||
|
|
||||||
static QDateTime to_date_time (QVariant const& value)
|
|
||||||
{
|
|
||||||
return QDateTime::fromMSecsSinceEpoch (value.toULongLong () * 1000ull, Qt::UTC);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString displayText (QVariant const& value, QLocale const& locale) const override
|
|
||||||
{
|
|
||||||
return locale.toString (to_date_time (value), locale.dateFormat (QLocale::ShortFormat) + " hh:mm:ss");
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const& /*option*/, QModelIndex const& /*index*/) const override
|
|
||||||
{
|
|
||||||
std::unique_ptr<QDateTimeEdit> editor {new QDateTimeEdit {parent}};
|
|
||||||
editor->setDisplayFormat (parent->locale ().dateFormat (QLocale::ShortFormat) + " hh:mm:ss");
|
|
||||||
editor->setTimeSpec (Qt::UTC); // needed because it ignores time
|
|
||||||
// spec of the QDateTime that it is
|
|
||||||
// set from
|
|
||||||
return editor.release ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEditorData (QWidget * editor, QModelIndex const& index) const override
|
|
||||||
{
|
|
||||||
static_cast<QDateTimeEdit *> (editor)->setDateTime (to_date_time (index, Qt::EditRole));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setModelData (QWidget * editor, QAbstractItemModel * model, QModelIndex const& index) const override
|
|
||||||
{
|
|
||||||
model->setData (index, to_secs_since_epoch (static_cast<QDateTimeEdit *> (editor)->dateTime ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateEditorGeometry (QWidget * editor, QStyleOptionViewItem const& option, QModelIndex const& /*index*/) const override
|
|
||||||
{
|
|
||||||
editor->setGeometry (option.rect);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
27
item_delegates/FrequencyDelegate.cpp
Normal file
27
item_delegates/FrequencyDelegate.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include "FrequencyDelegate.hpp"
|
||||||
|
|
||||||
|
#include "widgets/FrequencyLineEdit.hpp"
|
||||||
|
|
||||||
|
FrequencyDelegate::FrequencyDelegate (QObject * parent)
|
||||||
|
: QStyledItemDelegate {parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget * FrequencyDelegate::createEditor (QWidget * parent, QStyleOptionViewItem const&
|
||||||
|
, QModelIndex const&) const
|
||||||
|
{
|
||||||
|
auto * editor = new FrequencyLineEdit {parent};
|
||||||
|
editor->setFrame (false);
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrequencyDelegate::setEditorData (QWidget * editor, QModelIndex const& index) const
|
||||||
|
{
|
||||||
|
static_cast<FrequencyLineEdit *> (editor)->frequency (index.model ()->data (index, Qt::EditRole).value<Radio::Frequency> ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrequencyDelegate::setModelData (QWidget * editor, QAbstractItemModel * model, QModelIndex const& index) const
|
||||||
|
{
|
||||||
|
model->setData (index, static_cast<FrequencyLineEdit *> (editor)->frequency (), Qt::EditRole);
|
||||||
|
}
|
||||||
|
|
21
item_delegates/FrequencyDelegate.hpp
Normal file
21
item_delegates/FrequencyDelegate.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef FREQUENCY_DELEGATE_HPP_
|
||||||
|
#define FREQUENCY_DELEGATE_HPP_
|
||||||
|
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Class FrequencyDelegate
|
||||||
|
//
|
||||||
|
// Item delegate for editing a frequency in Hertz but displayed in MHz
|
||||||
|
//
|
||||||
|
class FrequencyDelegate final
|
||||||
|
: public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FrequencyDelegate (QObject * parent = nullptr);
|
||||||
|
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||||
|
void setEditorData (QWidget * editor, QModelIndex const&) const override;
|
||||||
|
void setModelData (QWidget * editor, QAbstractItemModel *, QModelIndex const&) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
26
item_delegates/FrequencyDeltaDelegate.cpp
Normal file
26
item_delegates/FrequencyDeltaDelegate.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "FrequencyDeltaDelegate.hpp"
|
||||||
|
|
||||||
|
#include "widgets/FrequencyDeltaLineEdit.hpp"
|
||||||
|
|
||||||
|
FrequencyDeltaDelegate::FrequencyDeltaDelegate (QObject * parent)
|
||||||
|
: QStyledItemDelegate {parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget * FrequencyDeltaDelegate::createEditor (QWidget * parent, QStyleOptionViewItem const&
|
||||||
|
, QModelIndex const&) const
|
||||||
|
{
|
||||||
|
auto * editor = new FrequencyDeltaLineEdit {parent};
|
||||||
|
editor->setFrame (false);
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrequencyDeltaDelegate::setEditorData (QWidget * editor, QModelIndex const& index) const
|
||||||
|
{
|
||||||
|
static_cast<FrequencyDeltaLineEdit *> (editor)->frequency_delta (index.model ()->data (index, Qt::EditRole).value<Radio::FrequencyDelta> ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrequencyDeltaDelegate::setModelData (QWidget * editor, QAbstractItemModel * model, QModelIndex const& index) const
|
||||||
|
{
|
||||||
|
model->setData (index, static_cast<FrequencyDeltaLineEdit *> (editor)->frequency_delta (), Qt::EditRole);
|
||||||
|
}
|
21
item_delegates/FrequencyDeltaDelegate.hpp
Normal file
21
item_delegates/FrequencyDeltaDelegate.hpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef FREQUENCY_DELTA_DELEGATE_HPP_
|
||||||
|
#define FREQUENCY_DELTA_DELEGATE_HPP_
|
||||||
|
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Class FrequencyDeltaDelegate
|
||||||
|
//
|
||||||
|
// Item delegate for editing a frequency delta in Hertz but displayed in MHz
|
||||||
|
//
|
||||||
|
class FrequencyDeltaDelegate final
|
||||||
|
: public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit FrequencyDeltaDelegate (QObject * parent = nullptr);
|
||||||
|
QWidget * createEditor (QWidget * parent, QStyleOptionViewItem const&, QModelIndex const&) const override;
|
||||||
|
void setEditorData (QWidget * editor, QModelIndex const&) const override;
|
||||||
|
void setModelData (QWidget * editor, QAbstractItemModel *, QModelIndex const&) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -1,12 +1,13 @@
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
item_delegates/ForeignKeyDelegate.cpp \
|
item_delegates/ForeignKeyDelegate.cpp \
|
||||||
item_delegates/FrequencyItemDelegate.cpp \
|
item_delegates/FrequencyDelegate.cpp \
|
||||||
|
item_delegates/FrequencyDeltaDelegate.cpp \
|
||||||
item_delegates/CallsignDelegate.cpp \
|
item_delegates/CallsignDelegate.cpp \
|
||||||
item_delegates/MaidenheadLocatorItemDelegate.cpp
|
item_delegates/MaidenheadLocatorItemDelegate.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
item_delegates/ForeignKeyDelegate.hpp \
|
item_delegates/ForeignKeyDelegate.hpp \
|
||||||
item_delegates/FrequencyItemDelegate.hpp \
|
item_delegates/FrequencyDelegate.hpp \
|
||||||
|
item_delegates/FrequencyDeltaDelegate.hpp \
|
||||||
item_delegates/CallsignDelegate.hpp \
|
item_delegates/CallsignDelegate.hpp \
|
||||||
item_delegates/MaidenheadLocatorDelegate.hpp \
|
item_delegates/MaidenheadLocatorDelegate.hpp
|
||||||
item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp
|
|
||||||
|
@ -22,15 +22,10 @@ i3.n3 Example message Bits Total Purpose
|
|||||||
2 PA3XYZ/P GM4ABC/P R JO22 28 1 28 1 1 15 74 EU VHF contest
|
2 PA3XYZ/P GM4ABC/P R JO22 28 1 28 1 1 15 74 EU VHF contest
|
||||||
3 TU; W9XYZ K1ABC R 579 MA 1 28 28 1 3 13 74 ARRL RTTY Roundup
|
3 TU; W9XYZ K1ABC R 579 MA 1 28 28 1 3 13 74 ARRL RTTY Roundup
|
||||||
4 <WA9XYZ> PJ4/KA1ABC RR73 12 58 1 2 1 74 Nonstandard calls
|
4 <WA9XYZ> PJ4/KA1ABC RR73 12 58 1 2 1 74 Nonstandard calls
|
||||||
5 ... tbd
|
5 TU; W9XYZ K1ABC R-07 FN 1 28 28 1 7 9 74 WWROF contest ?
|
||||||
6 ... tbd
|
6 ... tbd
|
||||||
7 ... tbd
|
7 ... tbd
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
In case we need them, later:
|
|
||||||
|
|
||||||
5 TU; W9XYZ K1ABC R 579 8 MA 1 28 28 1 3 6 7 74 CQ WW RTTY
|
|
||||||
6 TU; W9XYZ K1ABC R 579 MA 1 28 28 1 3 13 74 CQ WPX RTTY
|
|
||||||
----------------------------------------------------------------------------------
|
|
||||||
NB: three 74-bit message types and two 71-bit message subtypes are still TBD.
|
NB: three 74-bit message types and two 71-bit message subtypes are still TBD.
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -72,7 +72,18 @@ CQ W9XYZ EN37
|
|||||||
W9XYZ <YW18FIFA> R-09
|
W9XYZ <YW18FIFA> R-09
|
||||||
YW18FIFA <W9XYZ> RRR
|
YW18FIFA <W9XYZ> RRR
|
||||||
<W9XYZ> YW18FIFA 73
|
<W9XYZ> YW18FIFA 73
|
||||||
10. Other stuff
|
|
||||||
|
10. WWROF FT8/FT4 contest
|
||||||
|
-----------------------------------------------------------
|
||||||
|
CQ TEST K1ABC FN42
|
||||||
|
K1ABC W9XYZ -16 EN
|
||||||
|
W9XYZ K1ABC R-07 FN
|
||||||
|
K1ABC W9XYZ RR73
|
||||||
|
K1ABC G3AAA -11 IO
|
||||||
|
TU; G3AAA K1ABC R-09 FN
|
||||||
|
K1ABC G3AAA RR73
|
||||||
|
|
||||||
|
11. Other stuff
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
TNX BOB 73 GL
|
TNX BOB 73 GL
|
||||||
CQ YW18FIFA
|
CQ YW18FIFA
|
||||||
|
@ -172,6 +172,10 @@ subroutine pack77(msg0,i3,n3,c77)
|
|||||||
call pack77_4(nwords,w,i3,n3,c77)
|
call pack77_4(nwords,w,i3,n3,c77)
|
||||||
if(i3.ge.0) go to 900
|
if(i3.ge.0) go to 900
|
||||||
|
|
||||||
|
! Check Type 5 (WWROF contest exchange)
|
||||||
|
call pack77_5(nwords,w,i3,n3,c77)
|
||||||
|
if(i3.ge.0) go to 900
|
||||||
|
|
||||||
! It defaults to free text
|
! It defaults to free text
|
||||||
800 i3=0
|
800 i3=0
|
||||||
n3=0
|
n3=0
|
||||||
@ -204,6 +208,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
|
|||||||
character*6 cexch,grid6
|
character*6 cexch,grid6
|
||||||
character*4 grid4,cserial
|
character*4 grid4,cserial
|
||||||
character*3 csec(NSEC)
|
character*3 csec(NSEC)
|
||||||
|
character*2 cfield
|
||||||
character*38 c
|
character*38 c
|
||||||
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
|
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
|
||||||
logical unpk28_success,unpk77_success
|
logical unpk28_success,unpk77_success
|
||||||
@ -491,8 +496,31 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
|
|||||||
else
|
else
|
||||||
msg='CQ '//trim(call_2)
|
msg='CQ '//trim(call_2)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
else if(i3.eq.5) then
|
||||||
|
! 5 TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
|
||||||
|
read(c77,1041) itu,n28a,n28b,ir,irpt,nexch,i3
|
||||||
|
1041 format(b1,2b28.28,b1,b7.7,b9.9,b3.3)
|
||||||
|
call unpack28(n28a,call_1,unpk28_success)
|
||||||
|
if(.not.unpk28_success) unpk77_success=.false.
|
||||||
|
call unpack28(n28b,call_2,unpk28_success)
|
||||||
|
if(.not.unpk28_success) unpk77_success=.false.
|
||||||
|
write(crpt,'(i3.2)') irpt-35
|
||||||
|
if(crpt(1:1).eq.' ') crpt(1:1)='+'
|
||||||
|
n1=nexch/18
|
||||||
|
n2=nexch - 18*n1
|
||||||
|
cfield(1:1)=char(ichar('A')+n1)
|
||||||
|
cfield(2:2)=char(ichar('A')+n2)
|
||||||
|
if(itu.eq.0 .and. ir.eq.0) msg=trim(call_1)//' '//trim(call_2)// &
|
||||||
|
' '//crpt//' '//cfield
|
||||||
|
if(itu.eq.1 .and. ir.eq.0) msg='TU; '//trim(call_1)//' '//trim(call_2)// &
|
||||||
|
' '//crpt//' '//cfield
|
||||||
|
if(itu.eq.0 .and. ir.eq.1) msg=trim(call_1)//' '//trim(call_2)// &
|
||||||
|
' R'//crpt//' '//cfield
|
||||||
|
if(itu.eq.1 .and. ir.eq.1) msg='TU; '//trim(call_1)//' '//trim(call_2)// &
|
||||||
|
' R'//crpt//' '//cfield
|
||||||
endif
|
endif
|
||||||
if(msg(1:4).eq.'CQ <') unpk77_success=.false.
|
! if(msg(1:4).eq.'CQ <') unpk77_success=.false.
|
||||||
|
|
||||||
return
|
return
|
||||||
end subroutine unpack77
|
end subroutine unpack77
|
||||||
@ -1040,12 +1068,11 @@ subroutine pack77_3(nwords,w,i3,n3,c77)
|
|||||||
call chkcall(w(i1+1),bcall_2,ok2)
|
call chkcall(w(i1+1),bcall_2,ok2)
|
||||||
if(.not.ok1 .or. .not.ok2) go to 900
|
if(.not.ok1 .or. .not.ok2) go to 900
|
||||||
crpt=w(nwords-1)(1:3)
|
crpt=w(nwords-1)(1:3)
|
||||||
|
if(index(crpt,'-').ge.1 .or. index(crpt,'+').ge.1) go to 900
|
||||||
if(crpt(1:1).eq.'5' .and. crpt(2:2).ge.'2' .and. crpt(2:2).le.'9' .and. &
|
if(crpt(1:1).eq.'5' .and. crpt(2:2).ge.'2' .and. crpt(2:2).le.'9' .and. &
|
||||||
crpt(3:3).eq.'9') then
|
crpt(3:3).eq.'9') then
|
||||||
nserial=0
|
nserial=0
|
||||||
read(w(nwords),*,err=1) nserial
|
read(w(nwords),*,err=1) nserial
|
||||||
!1 i3=3
|
|
||||||
! n3=0
|
|
||||||
endif
|
endif
|
||||||
1 mult=' '
|
1 mult=' '
|
||||||
imult=-1
|
imult=-1
|
||||||
@ -1150,6 +1177,60 @@ subroutine pack77_4(nwords,w,i3,n3,c77)
|
|||||||
900 return
|
900 return
|
||||||
end subroutine pack77_4
|
end subroutine pack77_4
|
||||||
|
|
||||||
|
subroutine pack77_5(nwords,w,i3,n3,c77)
|
||||||
|
! Check Type 5 (WWROF contest exchange)
|
||||||
|
|
||||||
|
character*13 w(19)
|
||||||
|
character*77 c77
|
||||||
|
character*6 bcall_1,bcall_2
|
||||||
|
character*3 mult
|
||||||
|
character crpt*4
|
||||||
|
character c1*1,c2*2
|
||||||
|
logical ok1,ok2
|
||||||
|
|
||||||
|
if(nwords.eq.4 .or. nwords.eq.5 .or. nwords.eq.6) then
|
||||||
|
i1=1
|
||||||
|
if(trim(w(1)).eq.'TU;') i1=2
|
||||||
|
call chkcall(w(i1),bcall_1,ok1)
|
||||||
|
call chkcall(w(i1+1),bcall_2,ok2)
|
||||||
|
if(.not.ok1 .or. .not.ok2) go to 900
|
||||||
|
crpt=w(nwords-1)(1:4)
|
||||||
|
if(index(crpt,'-').lt.1 .and. index(crpt,'+').lt.1) go to 900
|
||||||
|
|
||||||
|
c1=crpt(1:1)
|
||||||
|
c2=crpt(1:2)
|
||||||
|
irpt=-1
|
||||||
|
if(c1.eq.'+' .or. c1.eq.'-') then
|
||||||
|
ir=0
|
||||||
|
read(w(nwords-1),*,err=900) irpt
|
||||||
|
irpt=irpt+35
|
||||||
|
else if(c2.eq.'R+' .or. c2.eq.'R-') then
|
||||||
|
ir=1
|
||||||
|
read(w(nwords-1)(2:),*) irpt
|
||||||
|
irpt=irpt+35
|
||||||
|
endif
|
||||||
|
if(irpt.eq.-1 .or. len(trim(w(nwords))).ne.2) go to 900
|
||||||
|
c2=w(nwords)(1:2)
|
||||||
|
n1=ichar(c2(1:1)) - ichar('A')
|
||||||
|
n2=ichar(c2(2:2)) - ichar('A')
|
||||||
|
if(n1.lt.0 .or. n1.gt.17) go to 900
|
||||||
|
if(n2.lt.0 .or. n2.gt.17) go to 900
|
||||||
|
nexch=18*n1 + n2
|
||||||
|
i3=5
|
||||||
|
n3=0
|
||||||
|
itu=0
|
||||||
|
if(trim(w(1)).eq.'TU;') itu=1
|
||||||
|
call pack28(w(1+itu),n28a)
|
||||||
|
call pack28(w(2+itu),n28b)
|
||||||
|
! 5 TU; W9XYZ K1ABC R-09 FN 1 28 28 1 7 9 74 WWROF contest
|
||||||
|
write(c77,1010) itu,n28a,n28b,ir,irpt,nexch,i3
|
||||||
|
1010 format(b1,2b28.28,b1,b7.7,b9.9,b3.3)
|
||||||
|
|
||||||
|
end if
|
||||||
|
|
||||||
|
900 return
|
||||||
|
end subroutine pack77_5
|
||||||
|
|
||||||
subroutine packtext77(c13,c71)
|
subroutine packtext77(c13,c71)
|
||||||
|
|
||||||
character*13 c13,w
|
character*13 c13,w
|
||||||
|
@ -12,7 +12,9 @@ subroutine addit(itone,nfsample,nsym,nsps,ifreq,sig,dat)
|
|||||||
dphi=0.
|
dphi=0.
|
||||||
|
|
||||||
iters=1
|
iters=1
|
||||||
if(nsym.eq.79) iters=2
|
if(nsym.eq.79) iters=2 !FT8
|
||||||
|
if(nsym.eq.103) iters=5 !FT4
|
||||||
|
|
||||||
do iter=1,iters
|
do iter=1,iters
|
||||||
f=ifreq
|
f=ifreq
|
||||||
phi=0.
|
phi=0.
|
||||||
@ -20,10 +22,12 @@ subroutine addit(itone,nfsample,nsym,nsps,ifreq,sig,dat)
|
|||||||
k=12000 !Start audio at t = 1.0 s
|
k=12000 !Start audio at t = 1.0 s
|
||||||
t=0.
|
t=0.
|
||||||
if(nsym.eq.79) k=12000 + (iter-1)*12000*30 !Special case for FT8
|
if(nsym.eq.79) k=12000 + (iter-1)*12000*30 !Special case for FT8
|
||||||
|
if(nsym.eq.103) k=12000 + (iter-1)*12000*10 !Special case for FT4
|
||||||
isym0=-1
|
isym0=-1
|
||||||
do i=1,ntot
|
do i=1,ntot
|
||||||
t=t+dt
|
t=t+dt
|
||||||
isym=nint(t/tsym) + 1
|
isym=nint(t/tsym) + 1
|
||||||
|
if(isym.gt.nsym) exit
|
||||||
if(isym.ne.isym0) then
|
if(isym.ne.isym0) then
|
||||||
freq=f + itone(isym)*baud
|
freq=f + itone(isym)*baud
|
||||||
dphi=twopi*freq*dt
|
dphi=twopi*freq*dt
|
||||||
@ -59,7 +63,7 @@ subroutine addcw(icw,ncw,ifreq,sig,dat)
|
|||||||
phi=0.
|
phi=0.
|
||||||
k=12000 !Start audio at t = 1.0 s
|
k=12000 !Start audio at t = 1.0 s
|
||||||
t=0.
|
t=0.
|
||||||
npts=60*12000
|
npts=59*12000
|
||||||
x=0.
|
x=0.
|
||||||
do i=1,npts
|
do i=1,npts
|
||||||
t=t+dt
|
t=t+dt
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
program allsim
|
program allsim
|
||||||
|
|
||||||
! Generate simulated data for WSJT-X slow modes: JT4, JT9, JT65, QRA64,
|
! Generate simulated data for WSJT-X modes: JT4, JT9, JT65, FT8, FT4, QRA64,
|
||||||
! and WSPR. Also unmodulated carrier and 20 WPM CW.
|
! and WSPR. Also unmodulated carrier and 20 WPM CW.
|
||||||
|
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ program allsim
|
|||||||
logical*1 bcontest
|
logical*1 bcontest
|
||||||
real*4 dat(NMAX)
|
real*4 dat(NMAX)
|
||||||
character message*22,msgsent*22,arg*8,mygrid*6
|
character message*22,msgsent*22,arg*8,mygrid*6
|
||||||
|
character*37 msg37,msgsent37
|
||||||
|
|
||||||
nargs=iargc()
|
nargs=iargc()
|
||||||
if(nargs.ne.1) then
|
if(nargs.ne.1) then
|
||||||
@ -60,15 +61,20 @@ program allsim
|
|||||||
call gen4(message,0,msgsent,itone,itype)
|
call gen4(message,0,msgsent,itone,itype)
|
||||||
call addit(itone,11025,206,2520,1200,sig,dat) !JT4
|
call addit(itone,11025,206,2520,1200,sig,dat) !JT4
|
||||||
|
|
||||||
i3bit=0 ! ### TEMPORARY ??? ###
|
i3=-1
|
||||||
call genft8(message,mygrid,bcontest,i3bit,msgsent,msgbits,itone)
|
n3=-1
|
||||||
|
call genft8(message,i3,n3,msgsent,msgbits,itone)
|
||||||
call addit(itone,12000,79,1920,1400,sig,dat) !FT8
|
call addit(itone,12000,79,1920,1400,sig,dat) !FT8
|
||||||
|
|
||||||
|
msg37=message//' '
|
||||||
|
call genft4(msg37,0,msgsent37,itone)
|
||||||
|
call addit(itone,12000,103,512,1600,sig,dat) !FT4
|
||||||
|
|
||||||
call genqra64(message,0,msgsent,itone,itype)
|
call genqra64(message,0,msgsent,itone,itype)
|
||||||
call addit(itone,12000,84,6912,1600,sig,dat) !QRA64
|
call addit(itone,12000,84,6912,1800,sig,dat) !QRA64
|
||||||
|
|
||||||
call gen65(message,0,msgsent,itone,itype)
|
call gen65(message,0,msgsent,itone,itype)
|
||||||
call addit(itone,11025,126,4096,1800,sig,dat) !JT65
|
call addit(itone,11025,126,4096,2000,sig,dat) !JT65
|
||||||
|
|
||||||
iwave(1:npts)=nint(rms*dat(1:npts))
|
iwave(1:npts)=nint(rms*dat(1:npts))
|
||||||
|
|
||||||
|
@ -1,16 +1,56 @@
|
|||||||
subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
|
module astro_module
|
||||||
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &
|
use, intrinsic :: iso_c_binding, only : c_int, c_double, c_bool, c_char, c_ptr, c_size_t, c_f_pointer
|
||||||
RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1,width2,bTx, &
|
implicit none
|
||||||
AzElFileName,jpleph)
|
|
||||||
|
|
||||||
implicit real*8 (a-h,o-z)
|
private
|
||||||
character*6 mygrid,hisgrid,c1*1
|
public :: astrosub
|
||||||
character*6 AzElFileName*(*),jpleph*(*)
|
|
||||||
|
contains
|
||||||
|
|
||||||
|
subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid_cp,mygrid_len, &
|
||||||
|
hisgrid_cp,hisgrid_len,AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8, &
|
||||||
|
ntsky,ndop,ndop00,RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1, &
|
||||||
|
width2,bTx,AzElFileName_cp,AzElFileName_len,jpleph_cp,jpleph_len) &
|
||||||
|
bind (C, name="astrosub")
|
||||||
|
|
||||||
|
integer, parameter :: dp = selected_real_kind(15, 50)
|
||||||
|
|
||||||
|
integer(c_int), intent(in), value :: nyear, month, nday
|
||||||
|
real(c_double), intent(in), value :: uth8, freq8
|
||||||
|
real(c_double), intent(out) :: AzSun8, ElSun8, AzMoon8, ElMoon8, AzMoonB8, &
|
||||||
|
ElMoonB8, Ramoon8, DecMoon8, Dgrd8, poloffset8, xnr8, techo8, width1, &
|
||||||
|
width2
|
||||||
|
integer(c_int), intent(out) :: ntsky, ndop, ndop00
|
||||||
|
logical(c_bool), intent(in), value :: bTx
|
||||||
|
type(c_ptr), intent(in), value :: mygrid_cp, hisgrid_cp, AzElFileName_cp, jpleph_cp
|
||||||
|
integer(c_size_t), intent(in), value :: mygrid_len, hisgrid_len, AzElFileName_len, jpleph_len
|
||||||
|
|
||||||
|
character(len=6) :: mygrid, hisgrid
|
||||||
|
character(kind=c_char, len=:), allocatable :: AzElFileName
|
||||||
|
character(len=1) :: c1
|
||||||
|
integer :: ih, im, imin, is, isec, nfreq, nRx
|
||||||
|
real(dp) :: AzAux, ElAux, dbMoon8, dfdt, dfdt0, doppler, doppler00, HA8, sd8, xlst8
|
||||||
character*256 jpleph_file_name
|
character*256 jpleph_file_name
|
||||||
logical*1 bTx
|
|
||||||
common/jplcom/jpleph_file_name
|
common/jplcom/jpleph_file_name
|
||||||
|
|
||||||
jpleph_file_name=jpleph
|
block
|
||||||
|
character(kind=c_char, len=mygrid_len), pointer :: mygrid_fp
|
||||||
|
character(kind=c_char, len=hisgrid_len), pointer :: hisgrid_fp
|
||||||
|
character(kind=c_char, len=AzElFileName_len), pointer :: AzElFileName_fp
|
||||||
|
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, &
|
||||||
@ -53,3 +93,5 @@ subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
|
|||||||
|
|
||||||
999 return
|
999 return
|
||||||
end subroutine astrosub
|
end subroutine astrosub
|
||||||
|
|
||||||
|
end module astro_module
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
subroutine azdist(grid1,grid2,utch,nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter)
|
subroutine azdist(MyGrid,HisGrid,utch,nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter)
|
||||||
|
|
||||||
character*(*) grid1,grid2
|
character(len=*) :: MyGrid,HisGrid
|
||||||
character*6 MyGrid,HisGrid,mygrid0,hisgrid0
|
character*6 mygrid0,hisgrid0
|
||||||
real*8 utch,utch0
|
real*8 utch,utch0
|
||||||
logical HotABetter,IamEast
|
logical HotABetter,IamEast
|
||||||
real eltab(22),daztab(22)
|
real eltab(22),daztab(22)
|
||||||
@ -12,11 +12,6 @@ subroutine azdist(grid1,grid2,utch,nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter)
|
|||||||
data mygrid0/" "/,hisgrid0/" "/,utch0/-999.d0/
|
data mygrid0/" "/,hisgrid0/" "/,utch0/-999.d0/
|
||||||
save
|
save
|
||||||
|
|
||||||
MyGrid=grid1//' '
|
|
||||||
HisGrid=grid2//' '
|
|
||||||
if(ichar(MyGrid(5:5)).eq.0) MyGrid(5:6)=' '
|
|
||||||
if(ichar(HisGrid(5:5)).eq.0) HisGrid(5:6)=' '
|
|
||||||
|
|
||||||
if(MyGrid.eq.HisGrid) then
|
if(MyGrid.eq.HisGrid) then
|
||||||
naz=0
|
naz=0
|
||||||
nel=0
|
nel=0
|
||||||
|
@ -7,6 +7,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
use jt65_decode
|
use jt65_decode
|
||||||
use jt9_decode
|
use jt9_decode
|
||||||
use ft8_decode
|
use ft8_decode
|
||||||
|
use ft4_decode
|
||||||
|
|
||||||
include 'jt9com.f90'
|
include 'jt9com.f90'
|
||||||
include 'timer_common.inc'
|
include 'timer_common.inc'
|
||||||
@ -27,6 +28,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
integer :: decoded
|
integer :: decoded
|
||||||
end type counting_ft8_decoder
|
end type counting_ft8_decoder
|
||||||
|
|
||||||
|
type, extends(ft4_decoder) :: counting_ft4_decoder
|
||||||
|
integer :: decoded
|
||||||
|
end type counting_ft4_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)
|
||||||
@ -40,6 +45,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
type(counting_jt65_decoder) :: my_jt65
|
type(counting_jt65_decoder) :: my_jt65
|
||||||
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
|
||||||
|
|
||||||
!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)
|
||||||
@ -53,6 +59,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
my_jt65%decoded = 0
|
my_jt65%decoded = 0
|
||||||
my_jt9%decoded = 0
|
my_jt9%decoded = 0
|
||||||
my_ft8%decoded = 0
|
my_ft8%decoded = 0
|
||||||
|
my_ft4%decoded = 0
|
||||||
|
|
||||||
single_decode=iand(params%nexp_decode,32).ne.0
|
single_decode=iand(params%nexp_decode,32).ne.0
|
||||||
bVHF=iand(params%nexp_decode,64).ne.0
|
bVHF=iand(params%nexp_decode,64).ne.0
|
||||||
@ -126,8 +133,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
n30fox(j)=n
|
n30fox(j)=n
|
||||||
m=n30max-n
|
m=n30max-n
|
||||||
if(len(trim(g2fox(j))).eq.4) then
|
if(len(trim(g2fox(j))).eq.4) then
|
||||||
call azdist(mygrid,g2fox(j),0.d0,nAz,nEl,nDmiles,nDkm, &
|
call azdist(mygrid,g2fox(j)//' ',0.d0,nAz,nEl,nDmiles, &
|
||||||
nHotAz,nHotABetter)
|
nDkm,nHotAz,nHotABetter)
|
||||||
else
|
else
|
||||||
nDkm=9999
|
nDkm=9999
|
||||||
endif
|
endif
|
||||||
@ -142,6 +149,15 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
go to 800
|
go to 800
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if(params%nmode.eq.5) then
|
||||||
|
call timer('decft4 ',0)
|
||||||
|
call my_ft4%decode(ft4_decoded,id2,params%nQSOProgress,params%nfqso, &
|
||||||
|
params%nutc,params%nfa,params%nfb,params%ndepth, &
|
||||||
|
logical(params%lapcqonly),ncontest,mycall,hiscall)
|
||||||
|
call timer('decft4 ',1)
|
||||||
|
go to 800
|
||||||
|
endif
|
||||||
|
|
||||||
rms=sqrt(dot_product(float(id2(300000:310000)), &
|
rms=sqrt(dot_product(float(id2(300000:310000)), &
|
||||||
float(id2(300000:310000)))/10000.0)
|
float(id2(300000:310000)))/10000.0)
|
||||||
if(rms.lt.2.0) go to 800
|
if(rms.lt.2.0) go to 800
|
||||||
@ -258,7 +274,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
!$omp end parallel sections
|
!$omp end parallel sections
|
||||||
|
|
||||||
! 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 + my_ft8%decoded
|
800 ndecoded = my_jt4%decoded + my_jt65%decoded + my_jt9%decoded + &
|
||||||
|
my_ft8%decoded + my_ft4%decoded
|
||||||
write(*,1010) nsynced,ndecoded
|
write(*,1010) nsynced,ndecoded
|
||||||
1010 format('<DecodeFinished>',2i4)
|
1010 format('<DecodeFinished>',2i4)
|
||||||
call flush(6)
|
call flush(6)
|
||||||
@ -500,7 +517,7 @@ contains
|
|||||||
decoded0=decoded
|
decoded0=decoded
|
||||||
|
|
||||||
annot=' '
|
annot=' '
|
||||||
if(ncontest.eq.0 .and. nap.ne.0) then
|
if(nap.ne.0) then
|
||||||
write(annot,'(a1,i1)') 'a',nap
|
write(annot,'(a1,i1)') 'a',nap
|
||||||
if(qual.lt.0.17) decoded0(37:37)='?'
|
if(qual.lt.0.17) decoded0(37:37)='?'
|
||||||
endif
|
endif
|
||||||
@ -561,4 +578,44 @@ contains
|
|||||||
return
|
return
|
||||||
end subroutine ft8_decoded
|
end subroutine ft8_decoded
|
||||||
|
|
||||||
|
subroutine ft4_decoded (this,sync,snr,dt,freq,decoded,nap,qual)
|
||||||
|
use ft4_decode
|
||||||
|
implicit none
|
||||||
|
|
||||||
|
class(ft4_decoder), intent(inout) :: this
|
||||||
|
real, intent(in) :: sync
|
||||||
|
integer, intent(in) :: snr
|
||||||
|
real, intent(in) :: dt
|
||||||
|
real, intent(in) :: freq
|
||||||
|
character(len=37), intent(in) :: decoded
|
||||||
|
character c1*12,c2*12,g2*4,w*4
|
||||||
|
integer i0,i1,i2,i3,i4,i5,n30,nwrap
|
||||||
|
integer, intent(in) :: nap
|
||||||
|
real, intent(in) :: qual
|
||||||
|
character*2 annot
|
||||||
|
character*37 decoded0
|
||||||
|
|
||||||
|
decoded0=decoded
|
||||||
|
|
||||||
|
annot=' '
|
||||||
|
if(nap.ne.0) then
|
||||||
|
write(annot,'(a1,i1)') 'a',nap
|
||||||
|
if(qual.lt.0.17) decoded0(37:37)='?'
|
||||||
|
endif
|
||||||
|
|
||||||
|
write(*,1001) params%nutc,snr,dt,nint(freq),decoded0,annot
|
||||||
|
1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,a2)
|
||||||
|
write(13,1002) params%nutc,nint(sync),snr,dt,freq,0,decoded0
|
||||||
|
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FT4')
|
||||||
|
|
||||||
|
call flush(6)
|
||||||
|
call flush(13)
|
||||||
|
|
||||||
|
select type(this)
|
||||||
|
type is (counting_ft4_decoder)
|
||||||
|
this%decoded = this%decoded + 1
|
||||||
|
end select
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine ft4_decoded
|
||||||
end subroutine multimode_decoder
|
end subroutine multimode_decoder
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
subroutine fast_decode(id2,narg,trperiod,line,mycall_12, &
|
||||||
hiscall_12)
|
hiscall_12)
|
||||||
|
|
||||||
parameter (NMAX=30*12000)
|
parameter (NMAX=30*12000)
|
||||||
@ -6,6 +6,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||||||
integer*2 id2a(NMAX)
|
integer*2 id2a(NMAX)
|
||||||
integer*2 id2b(NMAX)
|
integer*2 id2b(NMAX)
|
||||||
integer narg(0:14)
|
integer narg(0:14)
|
||||||
|
double precision trperiod
|
||||||
real dat(30*12000)
|
real dat(30*12000)
|
||||||
complex cdat(262145),cdat2(262145)
|
complex cdat(262145),cdat2(262145)
|
||||||
real psavg(450)
|
real psavg(450)
|
||||||
@ -41,7 +42,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||||||
nhashcalls=narg(12)
|
nhashcalls=narg(12)
|
||||||
|
|
||||||
line(1:100)(1:1)=char(0)
|
line(1:100)(1:1)=char(0)
|
||||||
if(t0.gt.float(ntrperiod)) go to 900
|
if(t0.gt.trperiod) go to 900
|
||||||
if(t0.gt.t1) go to 900
|
if(t0.gt.t1) go to 900
|
||||||
|
|
||||||
if(nmode.eq.102) then
|
if(nmode.eq.102) then
|
||||||
@ -53,7 +54,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||||||
cdat2=cdat
|
cdat2=cdat
|
||||||
ndat=ndat0
|
ndat=ndat0
|
||||||
call wav11(id2,ndat,dat)
|
call wav11(id2,ndat,dat)
|
||||||
nzz=11025*ntrperiod
|
nzz=11025*int(trperiod) !beware if fractional T/R period ever used here
|
||||||
if(ndat.lt.nzz) dat(ndat+1:nzz)=0.0
|
if(ndat.lt.nzz) dat(ndat+1:nzz)=0.0
|
||||||
ndat=min(ndat,30*11025)
|
ndat=min(ndat,30*11025)
|
||||||
call ana932(dat,ndat,cdat,npts) !Make downsampled analytic signal
|
call ana932(dat,ndat,cdat,npts) !Make downsampled analytic signal
|
||||||
|
@ -19,6 +19,7 @@ subroutine four2a(a,nfft,ndim,isign,iform)
|
|||||||
! This version of four2a makes calls to the FFTW library to do the
|
! This version of four2a makes calls to the FFTW library to do the
|
||||||
! actual computations.
|
! actual computations.
|
||||||
|
|
||||||
|
use fftw3
|
||||||
parameter (NPMAX=2100) !Max numberf of stored plans
|
parameter (NPMAX=2100) !Max numberf of stored plans
|
||||||
parameter (NSMALL=16384) !Max size of "small" FFTs
|
parameter (NSMALL=16384) !Max size of "small" FFTs
|
||||||
complex a(nfft) !Array to be transformed
|
complex a(nfft) !Array to be transformed
|
||||||
@ -29,7 +30,6 @@ subroutine four2a(a,nfft,ndim,isign,iform)
|
|||||||
logical found_plan
|
logical found_plan
|
||||||
data nplan/0/ !Number of stored plans
|
data nplan/0/ !Number of stored plans
|
||||||
common/patience/npatience,nthreads !Patience and threads for FFTW plans
|
common/patience/npatience,nthreads !Patience and threads for FFTW plans
|
||||||
include 'fftw3.f90' !FFTW definitions
|
|
||||||
save plan,nplan,nn,ns,nf,nl
|
save plan,nplan,nn,ns,nf,nl
|
||||||
|
|
||||||
if(nfft.lt.0) go to 999
|
if(nfft.lt.0) go to 999
|
||||||
@ -107,7 +107,7 @@ subroutine four2a(a,nfft,ndim,isign,iform)
|
|||||||
!$omp end critical(fftw)
|
!$omp end critical(fftw)
|
||||||
end if
|
end if
|
||||||
enddo
|
enddo
|
||||||
|
call fftwf_cleanup()
|
||||||
nplan=0
|
nplan=0
|
||||||
!$omp end critical(four2a)
|
!$omp end critical(four2a)
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||||||
endif
|
endif
|
||||||
smax=0.
|
smax=0.
|
||||||
s=0.
|
s=0.
|
||||||
|
ipk=-99
|
||||||
do i=ia,ib
|
do i=ia,ib
|
||||||
s(i)=real(cx(i))**2 + aimag(cx(i))**2
|
s(i)=real(cx(i))**2 + aimag(cx(i))**2
|
||||||
if(s(i).gt.smax) then
|
if(s(i).gt.smax) then
|
||||||
@ -49,6 +50,7 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||||||
endif
|
endif
|
||||||
enddo
|
enddo
|
||||||
|
|
||||||
|
if(ipk.ge.1) then
|
||||||
call peakup(s(ipk-1),s(ipk),s(ipk+1),dx)
|
call peakup(s(ipk-1),s(ipk),s(ipk+1),dx)
|
||||||
fpeak=df * (ipk+dx)
|
fpeak=df * (ipk+dx)
|
||||||
ap=(fpeak/fs+1.0/(2.0*NFFT))
|
ap=(fpeak/fs+1.0/(2.0*NFFT))
|
||||||
@ -67,6 +69,12 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||||||
ave=xsum/nsum
|
ave=xsum/nsum
|
||||||
snr=db(smax/ave)
|
snr=db(smax/ave)
|
||||||
pave=db(ave) + 8.0
|
pave=db(ave) + 8.0
|
||||||
|
else
|
||||||
|
snr=-99.9
|
||||||
|
pave=-99.9
|
||||||
|
fpeak=-99.9
|
||||||
|
ferr=-99.9
|
||||||
|
endif
|
||||||
cflag=' '
|
cflag=' '
|
||||||
if(snr.lt.20.0) cflag='*'
|
if(snr.lt.20.0) cflag='*'
|
||||||
n=n+1
|
n=n+1
|
||||||
|
12
lib/fsk4hf/ft2_params.f90
Normal file
12
lib/fsk4hf/ft2_params.f90
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
! LDPC (128,90) code
|
||||||
|
parameter (KK=90) !Information bits (77 + CRC13)
|
||||||
|
parameter (ND=128) !Data symbols
|
||||||
|
parameter (NS=16) !Sync symbols (2x8)
|
||||||
|
parameter (NN=NS+ND) !Total channel symbols (144)
|
||||||
|
parameter (NSPS=160) !Samples per symbol at 12000 S/s
|
||||||
|
parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040)
|
||||||
|
parameter (NMAX=2.5*12000) !Samples in iwave (36,000)
|
||||||
|
parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
||||||
|
parameter (NSTEP=NSPS/4) !Rough time-sync step size
|
||||||
|
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
|
||||||
|
parameter (NDOWN=16) !Downsample factor
|
335
lib/fsk4hf/ft2d.f90
Normal file
335
lib/fsk4hf/ft2d.f90
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
program ft2d
|
||||||
|
|
||||||
|
use crc
|
||||||
|
use packjt77
|
||||||
|
include 'ft2_params.f90'
|
||||||
|
character arg*8,message*37,c77*77,infile*80,fname*16,datetime*11
|
||||||
|
character*37 decodes(100)
|
||||||
|
character*120 data_dir
|
||||||
|
character*90 dmsg
|
||||||
|
complex c2(0:NMAX/16-1) !Complex waveform
|
||||||
|
complex cb(0:NMAX/16-1)
|
||||||
|
complex cd(0:144*10-1) !Complex waveform
|
||||||
|
complex c1(0:9),c0(0:9)
|
||||||
|
complex ccor(0:1,144)
|
||||||
|
complex csum,cterm,cc0,cc1,csync1,csync2
|
||||||
|
complex csync(16),csl(0:159)
|
||||||
|
real*8 fMHz
|
||||||
|
|
||||||
|
real a(5)
|
||||||
|
real rxdata(128),llr(128) !Soft symbols
|
||||||
|
real llr2(128)
|
||||||
|
real sbits(144),sbits1(144),sbits3(144)
|
||||||
|
real ps(0:8191),psbest(0:8191)
|
||||||
|
real candidates(100,2)
|
||||||
|
real savg(NH1),sbase(NH1)
|
||||||
|
integer ihdr(11)
|
||||||
|
integer*2 iwave(NMAX) !Generated full-length waveform
|
||||||
|
integer*1 message77(77),apmask(128),cw(128)
|
||||||
|
integer*1 hbits(144),hbits1(144),hbits3(144)
|
||||||
|
integer*1 s16(16),s45(45)
|
||||||
|
logical unpk77_success
|
||||||
|
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
|
||||||
|
data s45/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,1,1,0,1,0,0,0,1,1,1,0,0/
|
||||||
|
|
||||||
|
fs=12000.0/NDOWN !Sample rate
|
||||||
|
dt=1/fs !Sample interval after downsample (s)
|
||||||
|
tt=NSPS*dt !Duration of "itone" symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate for "itone" symbols (baud)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
h=0.800 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
|
||||||
|
|
||||||
|
dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample
|
||||||
|
dphi0=-1*dphi
|
||||||
|
dphi1=+1*dphi
|
||||||
|
phi0=0.0
|
||||||
|
phi1=0.0
|
||||||
|
do i=0,9
|
||||||
|
c1(i)=cmplx(cos(phi1),sin(phi1))
|
||||||
|
c0(i)=cmplx(cos(phi0),sin(phi0))
|
||||||
|
phi1=mod(phi1+dphi1,twopi)
|
||||||
|
phi0=mod(phi0+dphi0,twopi)
|
||||||
|
enddo
|
||||||
|
the=twopi*h/2.0
|
||||||
|
cc1=cmplx(cos(the),-sin(the))
|
||||||
|
cc0=cmplx(cos(the),sin(the))
|
||||||
|
|
||||||
|
k=0
|
||||||
|
do j=1,16
|
||||||
|
dphi1=(2*s16(j)-1)*dphi
|
||||||
|
phi1=0.0
|
||||||
|
do i=0,9
|
||||||
|
csl(k)=cmplx(cos(phi1),sin(phi1))
|
||||||
|
phi1=mod(phi1+dphi1,twopi)
|
||||||
|
k=k+1
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
nargs=iargc()
|
||||||
|
if(nargs.lt.1) then
|
||||||
|
print*,'Usage: ft2d [-a <data_dir>] [-f fMHz] file1 [file2 ...]'
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
iarg=1
|
||||||
|
data_dir="."
|
||||||
|
call getarg(iarg,arg)
|
||||||
|
if(arg(1:2).eq.'-a') then
|
||||||
|
call getarg(iarg+1,data_dir)
|
||||||
|
iarg=iarg+2
|
||||||
|
endif
|
||||||
|
call getarg(iarg,arg)
|
||||||
|
if(arg(1:2).eq.'-f') then
|
||||||
|
call getarg(iarg+1,arg)
|
||||||
|
read(arg,*) fMHz
|
||||||
|
iarg=iarg+2
|
||||||
|
endif
|
||||||
|
ncoh=1
|
||||||
|
|
||||||
|
do ifile=iarg,nargs
|
||||||
|
call getarg(ifile,infile)
|
||||||
|
j2=index(infile,'.wav')
|
||||||
|
open(10,file=infile,status='old',access='stream')
|
||||||
|
read(10,end=999) ihdr,iwave
|
||||||
|
read(infile(j2-4:j2-1),*) nutc
|
||||||
|
datetime=infile(j2-11:j2-1)
|
||||||
|
close(10)
|
||||||
|
candidates=0.0
|
||||||
|
ncand=0
|
||||||
|
call getcandidates2(iwave,375.0,3000.0,0.2,2200.0,100,savg,candidates,ncand,sbase)
|
||||||
|
ndecodes=0
|
||||||
|
do icand=1,ncand
|
||||||
|
f0=candidates(icand,1)
|
||||||
|
xsnr=1.0
|
||||||
|
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
|
||||||
|
call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol
|
||||||
|
|
||||||
|
!c2=c2/sqrt(sum(abs(c2(0:NMAX/16-1))))
|
||||||
|
!ishift=-1
|
||||||
|
!rccbest=-99.
|
||||||
|
!do is=0,435
|
||||||
|
!rcc=0.0
|
||||||
|
! do id=10,10
|
||||||
|
! rcc=rcc+abs(sum(conjg(c2(is:is+159-id))*c2(is+id:is+159)*csl(0:159-id)*conjg(csl(id:159))))
|
||||||
|
! enddo
|
||||||
|
! if(rcc.gt.rccbest) then
|
||||||
|
! rccbest=rcc
|
||||||
|
! ishift=is
|
||||||
|
! endif
|
||||||
|
!write(21,*) is,rcc
|
||||||
|
!enddo
|
||||||
|
|
||||||
|
! 750 samples/second here
|
||||||
|
ibest=-1
|
||||||
|
sybest=-99.
|
||||||
|
dfbest=-1.
|
||||||
|
do if=-30,+30
|
||||||
|
df=if
|
||||||
|
a=0.
|
||||||
|
a(1)=-df
|
||||||
|
call twkfreq1(c2,NMAX/16,fs,a,cb)
|
||||||
|
do is=0,374
|
||||||
|
csync1=0.
|
||||||
|
cterm=1
|
||||||
|
do ib=1,16
|
||||||
|
! do ib=1,45
|
||||||
|
i1=(ib-1)*10+is
|
||||||
|
if(s16(ib).eq.1) then
|
||||||
|
! if(s45(ib).eq.1) then
|
||||||
|
csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm
|
||||||
|
cterm=cterm*cc1
|
||||||
|
else
|
||||||
|
csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm
|
||||||
|
cterm=cterm*cc0
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
if(abs(csync1).gt.sybest) then
|
||||||
|
ibest=is
|
||||||
|
sybest=abs(csync1)
|
||||||
|
dfbest=df
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
a=0.
|
||||||
|
!dfbest=1500.0-f0
|
||||||
|
a(1)=-dfbest
|
||||||
|
|
||||||
|
call twkfreq1(c2,NMAX/16,fs,a,cb)
|
||||||
|
|
||||||
|
!ibest=197
|
||||||
|
ib=ibest
|
||||||
|
|
||||||
|
cd=cb(ib:ib+144*10-1)
|
||||||
|
s2=sum(cd*conjg(cd))/(10*144)
|
||||||
|
cd=cd/sqrt(s2)
|
||||||
|
do nseq=1,4
|
||||||
|
if( nseq.eq.1 ) then ! noncoherent single-symbol detection
|
||||||
|
sbits1=0.0
|
||||||
|
do ibit=1,144
|
||||||
|
ib=(ibit-1)*10
|
||||||
|
ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9)))
|
||||||
|
ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9)))
|
||||||
|
sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit))
|
||||||
|
hbits1(ibit)=0
|
||||||
|
if(sbits1(ibit).gt.0) hbits1(ibit)=1
|
||||||
|
enddo
|
||||||
|
sbits=sbits1
|
||||||
|
hbits=hbits1
|
||||||
|
sbits3=sbits1
|
||||||
|
hbits3=hbits1
|
||||||
|
elseif( nseq.ge.2 ) then
|
||||||
|
nbit=2*nseq-1
|
||||||
|
numseq=2**(nbit)
|
||||||
|
ps=0
|
||||||
|
do ibit=nbit/2+1,144-nbit/2
|
||||||
|
ps=0.0
|
||||||
|
pmax=0.0
|
||||||
|
do iseq=0,numseq-1
|
||||||
|
csum=0.0
|
||||||
|
cterm=1.0
|
||||||
|
k=1
|
||||||
|
do i=nbit-1,0,-1
|
||||||
|
ibb=iand(iseq/(2**i),1)
|
||||||
|
csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm
|
||||||
|
if(ibb.eq.0) cterm=cterm*cc0
|
||||||
|
if(ibb.eq.1) cterm=cterm*cc1
|
||||||
|
k=k+1
|
||||||
|
enddo
|
||||||
|
ps(iseq)=abs(csum)
|
||||||
|
if( ps(iseq) .gt. pmax ) then
|
||||||
|
pmax=ps(iseq)
|
||||||
|
ibflag=1
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
if( ibflag .eq. 1 ) then
|
||||||
|
psbest=ps
|
||||||
|
ibflag=0
|
||||||
|
endif
|
||||||
|
call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit))
|
||||||
|
hbits3(ibit)=0
|
||||||
|
if(sbits3(ibit).gt.0) hbits3(ibit)=1
|
||||||
|
enddo
|
||||||
|
sbits=sbits3
|
||||||
|
hbits=hbits3
|
||||||
|
endif
|
||||||
|
nsync_qual=count(hbits(1:16).eq.s16)
|
||||||
|
! if(nsync_qual.lt.10) exit
|
||||||
|
rxdata=sbits(17:144)
|
||||||
|
rxav=sum(rxdata(1:128))/128.0
|
||||||
|
rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0
|
||||||
|
rxsig=sqrt(rx2av-rxav*rxav)
|
||||||
|
rxdata=rxdata/rxsig
|
||||||
|
sigma=0.80
|
||||||
|
llr(1:128)=2*rxdata/(sigma*sigma)
|
||||||
|
!xllrmax=maxval(abs(llr))
|
||||||
|
!write(*,*) ifile,icand,nseq,nsync_qual
|
||||||
|
apmask=0
|
||||||
|
!apmask(1:29)=1
|
||||||
|
!llr(1:29)=xllrmax*(2*s45(17:45)-1)
|
||||||
|
max_iterations=40
|
||||||
|
do ibias=0,0
|
||||||
|
llr2=llr
|
||||||
|
if(ibias.eq.1) llr2=llr+0.4
|
||||||
|
if(ibias.eq.2) llr2=llr-0.4
|
||||||
|
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
|
||||||
|
if(nharderror.ge.0) exit
|
||||||
|
enddo
|
||||||
|
if(sum(message77).eq.0) cycle
|
||||||
|
if( nharderror.ge.0 ) then
|
||||||
|
write(c77,'(77i1)') message77(1:77)
|
||||||
|
call unpack77(c77,1,message,unpk77_success)
|
||||||
|
idupe=0
|
||||||
|
do i=1,ndecodes
|
||||||
|
if(decodes(i).eq.message) idupe=1
|
||||||
|
enddo
|
||||||
|
if(idupe.eq.1) goto 888
|
||||||
|
ndecodes=ndecodes+1
|
||||||
|
decodes(ndecodes)=message
|
||||||
|
nsnr=nint(xsnr)
|
||||||
|
freq=f0+dfbest
|
||||||
|
1210 format(a11,2i4,f6.2,f12.7,2x,a22,i3)
|
||||||
|
write(*,1212) datetime(8:11),nsnr,ibest/750.0,freq,message,'*',nseq,nharderror,nsync_qual
|
||||||
|
1212 format(a4,i4,2x,f5.3,f11.1,2x,a22,a1,i5,i5,i5)
|
||||||
|
goto 888
|
||||||
|
endif
|
||||||
|
enddo ! nseq
|
||||||
|
888 continue
|
||||||
|
enddo !candidate list
|
||||||
|
enddo !files
|
||||||
|
|
||||||
|
write(*,1120)
|
||||||
|
1120 format("<DecodeFinished>")
|
||||||
|
|
||||||
|
999 end program ft2d
|
||||||
|
|
||||||
|
subroutine getbitmetric(ib,ps,ns,xmet)
|
||||||
|
real ps(0:ns-1)
|
||||||
|
xm1=0
|
||||||
|
xm0=0
|
||||||
|
do i=0,ns-1
|
||||||
|
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
|
||||||
|
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
|
||||||
|
enddo
|
||||||
|
xmet=xm1-xm0
|
||||||
|
return
|
||||||
|
end subroutine getbitmetric
|
||||||
|
|
||||||
|
subroutine downsample2(ci,f0,co)
|
||||||
|
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
|
||||||
|
complex ci(0:NI-1),ct(0:NI-1)
|
||||||
|
complex co(0:NO-1)
|
||||||
|
fs=12000.0
|
||||||
|
df=fs/NI
|
||||||
|
ct=ci
|
||||||
|
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
|
||||||
|
i0=nint(f0/df)
|
||||||
|
ct=cshift(ct,i0)
|
||||||
|
co=0.0
|
||||||
|
co(0)=ct(0)
|
||||||
|
b=8.0
|
||||||
|
do i=1,NO/2
|
||||||
|
arg=(i*df/b)**2
|
||||||
|
filt=exp(-arg)
|
||||||
|
co(i)=ct(i)*filt
|
||||||
|
co(NO-i)=ct(NI-i)*filt
|
||||||
|
enddo
|
||||||
|
co=co/NO
|
||||||
|
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
|
||||||
|
return
|
||||||
|
end subroutine downsample2
|
||||||
|
|
||||||
|
subroutine ft2_downsample(iwave,f0,c)
|
||||||
|
|
||||||
|
! Input: i*2 data in iwave() at sample rate 12000 Hz
|
||||||
|
! Output: Complex data in c(), sampled at 1200 Hz
|
||||||
|
|
||||||
|
include 'ft2_params.f90'
|
||||||
|
parameter (NFFT2=NMAX/16)
|
||||||
|
integer*2 iwave(NMAX)
|
||||||
|
complex c(0:NMAX/16-1)
|
||||||
|
complex c1(0:NFFT2-1)
|
||||||
|
complex cx(0:NMAX/2)
|
||||||
|
real x(NMAX)
|
||||||
|
equivalence (x,cx)
|
||||||
|
|
||||||
|
BW=4.0*75
|
||||||
|
df=12000.0/NMAX
|
||||||
|
x=iwave
|
||||||
|
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
||||||
|
ibw=nint(BW/df)
|
||||||
|
i0=nint(f0/df)
|
||||||
|
c1=0.
|
||||||
|
c1(0)=cx(i0)
|
||||||
|
do i=1,NFFT2/2
|
||||||
|
arg=(i-1)*df/bw
|
||||||
|
win=exp(-arg*arg)
|
||||||
|
c1(i)=cx(i0+i)*win
|
||||||
|
c1(NFFT2-i)=cx(i0-i)*win
|
||||||
|
enddo
|
||||||
|
c1=c1/NFFT2
|
||||||
|
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
||||||
|
c=c1(0:NMAX/16-1)
|
||||||
|
return
|
||||||
|
end subroutine ft2_downsample
|
||||||
|
|
154
lib/fsk4hf/ft2sim.f90
Normal file
154
lib/fsk4hf/ft2sim.f90
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
program ft2sim
|
||||||
|
|
||||||
|
! Generate simulated signals for experimental "FT2" mode
|
||||||
|
|
||||||
|
use wavhdr
|
||||||
|
use packjt77
|
||||||
|
include 'ft2_params.f90' !Set various constants
|
||||||
|
parameter (NWAVE=NN*NSPS)
|
||||||
|
type(hdr) h !Header for .wav file
|
||||||
|
character arg*12,fname*17
|
||||||
|
character msg37*37,msgsent37*37
|
||||||
|
character c77*77
|
||||||
|
complex c0(0:NMAX-1)
|
||||||
|
complex c(0:NMAX-1)
|
||||||
|
real wave(NMAX)
|
||||||
|
real dphi(0:NMAX-1)
|
||||||
|
real pulse(480)
|
||||||
|
integer itone(NN)
|
||||||
|
integer*1 msgbits(77)
|
||||||
|
integer*2 iwave(NMAX) !Generated full-length waveform
|
||||||
|
|
||||||
|
! Get command-line argument(s)
|
||||||
|
nargs=iargc()
|
||||||
|
if(nargs.ne.8) then
|
||||||
|
print*,'Usage: ft2sim "message" f0 DT fdop del width nfiles snr'
|
||||||
|
print*,'Examples: ft2sim "K1ABC W9XYZ EN37" 1500.0 0.0 0.1 1.0 0 10 -18'
|
||||||
|
print*,' ft2sim "WA9XYZ/R KA1ABC/R FN42" 1500.0 0.0 0.1 1.0 0 10 -18'
|
||||||
|
print*,' ft2sim "K1ABC RR73; W9XYZ <KH1/KH7Z> -11" 300 0 0 0 25 1 -10'
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
call getarg(1,msg37) !Message to be transmitted
|
||||||
|
call getarg(2,arg)
|
||||||
|
read(arg,*) f0 !Frequency (only used for single-signal)
|
||||||
|
call getarg(3,arg)
|
||||||
|
read(arg,*) xdt !Time offset from nominal (s)
|
||||||
|
call getarg(4,arg)
|
||||||
|
read(arg,*) fspread !Watterson frequency spread (Hz)
|
||||||
|
call getarg(5,arg)
|
||||||
|
read(arg,*) delay !Watterson delay (ms)
|
||||||
|
call getarg(6,arg)
|
||||||
|
read(arg,*) width !Filter transition width (Hz)
|
||||||
|
call getarg(7,arg)
|
||||||
|
read(arg,*) nfiles !Number of files
|
||||||
|
call getarg(8,arg)
|
||||||
|
read(arg,*) snrdb !SNR_2500
|
||||||
|
|
||||||
|
nfiles=abs(nfiles)
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
fs=12000.0 !Sample rate (Hz)
|
||||||
|
dt=1.0/fs !Sample interval (s)
|
||||||
|
hmod=0.800 !Modulation index (0.5 is MSK, 1.0 is FSK)
|
||||||
|
tt=NSPS*dt !Duration of symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate (baud)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
|
||||||
|
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
|
||||||
|
txt=NN*NSPS/12000.0
|
||||||
|
|
||||||
|
! Source-encode, then get itone()
|
||||||
|
i3=-1
|
||||||
|
n3=-1
|
||||||
|
call pack77(msg37,i3,n3,c77)
|
||||||
|
read(c77,'(77i1)') msgbits
|
||||||
|
call genft2(msg37,0,msgsent37,itone,itype)
|
||||||
|
write(*,*)
|
||||||
|
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
||||||
|
write(*,1000) f0,xdt,txt,snrdb
|
||||||
|
1000 format('f0:',f9.3,' DT:',f6.2,' 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)') msgbits
|
||||||
|
endif
|
||||||
|
write(*,*)
|
||||||
|
write(*,'(a17)') 'Channel symbols: '
|
||||||
|
write(*,'(79i1)') itone
|
||||||
|
write(*,*)
|
||||||
|
|
||||||
|
call sgran()
|
||||||
|
|
||||||
|
! The filtered frequency pulse
|
||||||
|
do i=1,480
|
||||||
|
tt=(i-240.5)/160.0
|
||||||
|
pulse(i)=gfsk_pulse(1.0,tt)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
! Define the instantaneous frequency waveform
|
||||||
|
dphi_peak=twopi*(hmod/2.0)/real(NSPS)
|
||||||
|
dphi=0.0
|
||||||
|
do j=1,NN
|
||||||
|
ib=(j-1)*160
|
||||||
|
ie=ib+480-1
|
||||||
|
dphi(ib:ie)=dphi(ib:ie)+dphi_peak*pulse*(2*itone(j)-1)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
phi=0.0
|
||||||
|
c0=0.0
|
||||||
|
dphi=dphi+twopi*f0*dt
|
||||||
|
do j=0,NMAX-1
|
||||||
|
c0(j)=cmplx(cos(phi),sin(phi))
|
||||||
|
phi=mod(phi+dphi(j),twopi)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
c0(0:159)=c0(0:159)*(1.0-cos(twopi*(/(i,i=0,159)/)/320.0) )/2.0
|
||||||
|
c0(145*160:145*160+159)=c0(145*160:145*160+159)*(1.0+cos(twopi*(/(i,i=0,159)/)/320.0 ))/2.0
|
||||||
|
c0(146*160:)=0.
|
||||||
|
|
||||||
|
k=nint((xdt+0.25)/dt)
|
||||||
|
c0=cshift(c0,-k)
|
||||||
|
ia=k
|
||||||
|
|
||||||
|
do ifile=1,nfiles
|
||||||
|
c=c0
|
||||||
|
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,NMAX,NWAVE,fs,delay,fspread)
|
||||||
|
c=sig*c
|
||||||
|
|
||||||
|
ib=k
|
||||||
|
wave=real(c)
|
||||||
|
peak=maxval(abs(wave(ia:ib)))
|
||||||
|
nslots=1
|
||||||
|
if(width.gt.0.0) call filt8(f0,nslots,width,wave)
|
||||||
|
|
||||||
|
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)
|
||||||
|
h=default_header(12000,NMAX)
|
||||||
|
write(fname,1102) ifile
|
||||||
|
1102 format('000000_',i6.6,'.wav')
|
||||||
|
open(10,file=fname,status='unknown',access='stream')
|
||||||
|
write(10) h,iwave !Save to *.wav file
|
||||||
|
close(10)
|
||||||
|
write(*,1110) ifile,xdt,f0,snrdb,fname
|
||||||
|
1110 format(i4,f7.2,f8.2,f7.1,2x,a17)
|
||||||
|
enddo
|
||||||
|
999 end program ft2sim
|
329
lib/fsk4hf/ft4d.f90
Normal file
329
lib/fsk4hf/ft4d.f90
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
program ft4d
|
||||||
|
|
||||||
|
use crc
|
||||||
|
use packjt77
|
||||||
|
include 'ft4_params.f90'
|
||||||
|
character arg*8,message*37,c77*77,infile*80,fname*16,datetime*11
|
||||||
|
character*37 decodes(100)
|
||||||
|
character*120 data_dir
|
||||||
|
character*90 dmsg
|
||||||
|
complex cd2(0:NMAX/16-1) !Complex waveform
|
||||||
|
complex cb(0:NMAX/16-1)
|
||||||
|
complex cd(0:76*20-1) !Complex waveform
|
||||||
|
complex csum,cterm
|
||||||
|
complex ctwk(80),ctwk2(80)
|
||||||
|
complex csymb(20)
|
||||||
|
complex cs(0:3,NN)
|
||||||
|
real s4(0:3,NN)
|
||||||
|
|
||||||
|
real*8 fMHz
|
||||||
|
real ps(0:8191),psbest(0:8191)
|
||||||
|
real bmeta(152),bmetb(152),bmetc(152)
|
||||||
|
real s(NH1,NHSYM)
|
||||||
|
real a(5)
|
||||||
|
real llr(128),llr2(128),llra(128),llrb(128),llrc(128)
|
||||||
|
real s2(0:255)
|
||||||
|
real candidate(3,100)
|
||||||
|
real savg(NH1),sbase(NH1)
|
||||||
|
integer ihdr(11)
|
||||||
|
integer icos4(0:3)
|
||||||
|
integer*2 iwave(NMAX) !Generated full-length waveform
|
||||||
|
integer*1 message77(77),apmask(128),cw(128)
|
||||||
|
integer*1 hbits(152),hbits1(152),hbits3(152)
|
||||||
|
integer*1 s12(12)
|
||||||
|
integer graymap(0:3)
|
||||||
|
integer ip(1)
|
||||||
|
logical unpk77_success
|
||||||
|
logical one(0:511,0:7) ! 256 4-symbol sequences, 8 bits
|
||||||
|
data s12/1,1,1,2,2,2,2,2,2,1,1,1/
|
||||||
|
data icos4/0,1,3,2/
|
||||||
|
data graymap/0,1,3,2/
|
||||||
|
save one
|
||||||
|
|
||||||
|
fs=12000.0/NDOWN !Sample rate
|
||||||
|
dt=1/fs !Sample interval after downsample (s)
|
||||||
|
tt=NSPS*dt !Duration of "itone" symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate for "itone" symbols (baud)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
h=1.0 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
|
||||||
|
|
||||||
|
one=.false.
|
||||||
|
do i=0,255
|
||||||
|
do j=0,7
|
||||||
|
if(iand(i,2**j).ne.0) one(i,j)=.true.
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
nargs=iargc()
|
||||||
|
if(nargs.lt.1) then
|
||||||
|
print*,'Usage: ft4d [-a <data_dir>] [-f fMHz] file1 [file2 ...]'
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
iarg=1
|
||||||
|
data_dir="."
|
||||||
|
call getarg(iarg,arg)
|
||||||
|
if(arg(1:2).eq.'-a') then
|
||||||
|
call getarg(iarg+1,data_dir)
|
||||||
|
iarg=iarg+2
|
||||||
|
endif
|
||||||
|
call getarg(iarg,arg)
|
||||||
|
if(arg(1:2).eq.'-f') then
|
||||||
|
call getarg(iarg+1,arg)
|
||||||
|
read(arg,*) fMHz
|
||||||
|
iarg=iarg+2
|
||||||
|
endif
|
||||||
|
ncoh=1
|
||||||
|
|
||||||
|
do ifile=iarg,nargs
|
||||||
|
call getarg(ifile,infile)
|
||||||
|
j2=index(infile,'.wav')
|
||||||
|
open(10,file=infile,status='old',access='stream')
|
||||||
|
read(10,end=999) ihdr,iwave
|
||||||
|
read(infile(j2-4:j2-1),*) nutc
|
||||||
|
datetime=infile(j2-11:j2-1)
|
||||||
|
close(10)
|
||||||
|
candidate=0.0
|
||||||
|
ncand=0
|
||||||
|
|
||||||
|
nfqso=1500
|
||||||
|
nfa=500
|
||||||
|
nfb=2700
|
||||||
|
syncmin=1.0
|
||||||
|
maxcand=100
|
||||||
|
! call syncft4(iwave,nfa,nfb,syncmin,nfqso,maxcand,s,candidate,ncand,sbase)
|
||||||
|
|
||||||
|
call getcandidates4(iwave,375.0,3000.0,0.2,2200.0,100,savg,candidate,ncand,sbase)
|
||||||
|
ndecodes=0
|
||||||
|
do icand=1,ncand
|
||||||
|
f0=candidate(1,icand)-1.5*37.5
|
||||||
|
xsnr=1.0
|
||||||
|
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
|
||||||
|
call ft4_downsample(iwave,f0,cd2) ! downsample from 320 Sa/Symbol to 20 Sa/Symbol
|
||||||
|
sum2=sum(cd2*conjg(cd2))/(20.0*76)
|
||||||
|
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
|
||||||
|
|
||||||
|
! 750 samples/second here
|
||||||
|
ibest=-1
|
||||||
|
smax=-99.
|
||||||
|
dfbest=-1.
|
||||||
|
do idf=-90,+90,5
|
||||||
|
df=idf
|
||||||
|
a=0.
|
||||||
|
a(1)=df
|
||||||
|
ctwk=1.
|
||||||
|
call twkfreq1(ctwk,80,fs,a,ctwk2)
|
||||||
|
do istart=0,315
|
||||||
|
call sync4d(cd2,istart,ctwk2,1,sync)
|
||||||
|
if(sync.gt.smax) then
|
||||||
|
smax=sync
|
||||||
|
ibest=istart
|
||||||
|
dfbest=df
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
f0=f0+dfbest
|
||||||
|
!f0=1443.75
|
||||||
|
call ft4_downsample(iwave,f0,cb) ! downsample from 320s/Symbol to 20s/Symbol
|
||||||
|
sum2=sum(abs(cb)**2)/(20.0*76)
|
||||||
|
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
|
||||||
|
!ibest=208
|
||||||
|
cd=cb(ibest:ibest+76*20-1)
|
||||||
|
do k=1,NN
|
||||||
|
i1=(k-1)*20
|
||||||
|
csymb=cd(i1:i1+19)
|
||||||
|
call four2a(csymb,20,1,-1,1)
|
||||||
|
cs(0:3,k)=csymb(1:4)/1e2
|
||||||
|
s4(0:3,k)=abs(csymb(1:4))
|
||||||
|
enddo
|
||||||
|
|
||||||
|
! sync quality check
|
||||||
|
is1=0
|
||||||
|
is2=0
|
||||||
|
is3=0
|
||||||
|
do k=1,4
|
||||||
|
ip=maxloc(s4(:,k))
|
||||||
|
if(icos4(k-1).eq.(ip(1)-1)) is1=is1+1
|
||||||
|
ip=maxloc(s4(:,k+36))
|
||||||
|
if(icos4(k-1).eq.(ip(1)-1)) is2=is2+1
|
||||||
|
ip=maxloc(s4(:,k+72))
|
||||||
|
if(icos4(k-1).eq.(ip(1)-1)) is3=is3+1
|
||||||
|
enddo
|
||||||
|
! hard sync sum - max is 12
|
||||||
|
nsync=is1+is2+is3
|
||||||
|
|
||||||
|
do nseq=1,3
|
||||||
|
if(nseq.eq.1) nsym=1
|
||||||
|
if(nseq.eq.2) nsym=2
|
||||||
|
if(nseq.eq.3) nsym=4
|
||||||
|
nt=2**(2*nsym)
|
||||||
|
do ks=1,76,nsym
|
||||||
|
amax=-1.0
|
||||||
|
do i=0,nt-1
|
||||||
|
i1=i/64
|
||||||
|
i2=iand(i,63)/16
|
||||||
|
i3=iand(i,15)/4
|
||||||
|
i4=iand(i,3)
|
||||||
|
if(nsym.eq.1) then
|
||||||
|
s2(i)=abs(cs(graymap(i4),ks))
|
||||||
|
elseif(nsym.eq.2) then
|
||||||
|
s2(i)=abs(cs(graymap(i3),ks)+cs(graymap(i4),ks+1))
|
||||||
|
elseif(nsym.eq.4) then
|
||||||
|
s2(i)=abs(cs(graymap(i1),ks ) + &
|
||||||
|
cs(graymap(i2),ks+1) + &
|
||||||
|
cs(graymap(i3),ks+2) + &
|
||||||
|
cs(graymap(i4),ks+3) &
|
||||||
|
)
|
||||||
|
else
|
||||||
|
print*,"Error - nsym must be 1, 2, or 4."
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
ipt=1+(ks-1)*2
|
||||||
|
if(nsym.eq.1) ibmax=1
|
||||||
|
if(nsym.eq.2) ibmax=3
|
||||||
|
if(nsym.eq.4) ibmax=7
|
||||||
|
do ib=0,ibmax
|
||||||
|
bm=maxval(s2(0:nt-1),one(0:nt-1,ibmax-ib)) - &
|
||||||
|
maxval(s2(0:nt-1),.not.one(0:nt-1,ibmax-ib))
|
||||||
|
if(ipt+ib .gt.152) cycle
|
||||||
|
if(nsym.eq.1) then
|
||||||
|
bmeta(ipt+ib)=bm
|
||||||
|
elseif(nsym.eq.2) then
|
||||||
|
bmetb(ipt+ib)=bm
|
||||||
|
elseif(nsym.eq.4) then
|
||||||
|
bmetc(ipt+ib)=bm
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
call normalizebmet(bmeta,152)
|
||||||
|
call normalizebmet(bmetb,152)
|
||||||
|
call normalizebmet(bmetc,152)
|
||||||
|
|
||||||
|
hbits=0
|
||||||
|
where(bmeta.ge.0) hbits=1
|
||||||
|
ns1=count(hbits( 1: 8).eq.(/0,0,0,1,1,0,1,1/))
|
||||||
|
ns2=count(hbits( 73: 80).eq.(/0,0,0,1,1,0,1,1/))
|
||||||
|
ns3=count(hbits(145:152).eq.(/0,0,0,1,1,0,1,1/))
|
||||||
|
nsync_qual=ns1+ns2+ns3
|
||||||
|
|
||||||
|
sigma=0.7
|
||||||
|
llra(1:64)=bmeta(9:72)
|
||||||
|
llra(65:128)=bmeta(81:144)
|
||||||
|
llra=2*llra/sigma**2
|
||||||
|
llrb(1:64)=bmetb(9:72)
|
||||||
|
llrb(65:128)=bmetb(81:144)
|
||||||
|
llrb=2*llrb/sigma**2
|
||||||
|
llrc(1:64)=bmetc(9:72)
|
||||||
|
llrc(65:128)=bmetc(81:144)
|
||||||
|
llrc=2*llrc/sigma**2
|
||||||
|
|
||||||
|
do isd=1,3
|
||||||
|
if(isd.eq.1) llr=llra
|
||||||
|
if(isd.eq.2) llr=llrb
|
||||||
|
if(isd.eq.3) llr=llrc
|
||||||
|
apmask=0
|
||||||
|
max_iterations=40
|
||||||
|
do ibias=0,0
|
||||||
|
llr2=llr
|
||||||
|
if(ibias.eq.1) llr2=llr+0.4
|
||||||
|
if(ibias.eq.2) llr2=llr-0.4
|
||||||
|
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
|
||||||
|
if(nharderror.ge.0) exit
|
||||||
|
enddo
|
||||||
|
if(sum(message77).eq.0) cycle
|
||||||
|
if( nharderror.ge.0 ) then
|
||||||
|
write(c77,'(77i1)') message77(1:77)
|
||||||
|
call unpack77(c77,1,message,unpk77_success)
|
||||||
|
idupe=0
|
||||||
|
do i=1,ndecodes
|
||||||
|
if(decodes(i).eq.message) idupe=1
|
||||||
|
enddo
|
||||||
|
if(idupe.eq.1) cycle
|
||||||
|
ndecodes=ndecodes+1
|
||||||
|
decodes(ndecodes)=message
|
||||||
|
nsnr=nint(xsnr)
|
||||||
|
write(*,1212) datetime(8:11),nsnr,ibest/750.0,f0,message,'*',nharderror,nsync_qual,isd,niterations
|
||||||
|
1212 format(a4,i4,2x,f5.3,f11.1,2x,a22,a1,i5,i5,i5,i5)
|
||||||
|
endif
|
||||||
|
enddo ! sequence estimation
|
||||||
|
enddo !candidate list
|
||||||
|
enddo !files
|
||||||
|
|
||||||
|
write(*,1120)
|
||||||
|
1120 format("<DecodeFinished>")
|
||||||
|
|
||||||
|
999 end program ft4d
|
||||||
|
|
||||||
|
subroutine getbitmetric(ib,ps,ns,xmet)
|
||||||
|
real ps(0:ns-1)
|
||||||
|
xm1=0
|
||||||
|
xm0=0
|
||||||
|
do i=0,ns-1
|
||||||
|
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
|
||||||
|
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
|
||||||
|
enddo
|
||||||
|
xmet=xm1-xm0
|
||||||
|
return
|
||||||
|
end subroutine getbitmetric
|
||||||
|
|
||||||
|
subroutine downsample4(ci,f0,co)
|
||||||
|
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
|
||||||
|
complex ci(0:NI-1),ct(0:NI-1)
|
||||||
|
complex co(0:NO-1)
|
||||||
|
fs=12000.0
|
||||||
|
df=fs/NI
|
||||||
|
ct=ci
|
||||||
|
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
|
||||||
|
i0=nint(f0/df)
|
||||||
|
ct=cshift(ct,i0)
|
||||||
|
co=0.0
|
||||||
|
co(0)=ct(0)
|
||||||
|
b=8.0
|
||||||
|
do i=1,NO/2
|
||||||
|
arg=(i*df/b)**2
|
||||||
|
filt=exp(-arg)
|
||||||
|
co(i)=ct(i)*filt
|
||||||
|
co(NO-i)=ct(NI-i)*filt
|
||||||
|
enddo
|
||||||
|
co=co/NO
|
||||||
|
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
|
||||||
|
return
|
||||||
|
end subroutine downsample4
|
||||||
|
|
||||||
|
subroutine ft4_downsample(iwave,f0,c)
|
||||||
|
|
||||||
|
! Input: i*2 data in iwave() at sample rate 12000 Hz
|
||||||
|
! Output: Complex data in c(), sampled at 1200 Hz
|
||||||
|
|
||||||
|
include 'ft4_params.f90'
|
||||||
|
parameter (NFFT2=NMAX/16)
|
||||||
|
integer*2 iwave(NMAX)
|
||||||
|
complex c(0:NMAX/16-1)
|
||||||
|
complex c1(0:NFFT2-1)
|
||||||
|
complex cx(0:NMAX/2)
|
||||||
|
real x(NMAX)
|
||||||
|
equivalence (x,cx)
|
||||||
|
|
||||||
|
BW=6.0*75
|
||||||
|
df=12000.0/NMAX
|
||||||
|
x=iwave
|
||||||
|
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
||||||
|
ibw=nint(BW/df)
|
||||||
|
i0=nint(f0/df)
|
||||||
|
c1=0.
|
||||||
|
c1(0)=cx(i0)
|
||||||
|
do i=1,NFFT2/2
|
||||||
|
arg=(i-1)*df/bw
|
||||||
|
win=exp(-arg*arg)
|
||||||
|
c1(i)=cx(i0+i)*win
|
||||||
|
c1(NFFT2-i)=cx(i0-i)*win
|
||||||
|
enddo
|
||||||
|
c1=c1/NFFT2
|
||||||
|
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
||||||
|
c=c1(0:NMAX/16-1)
|
||||||
|
return
|
||||||
|
end subroutine ft4_downsample
|
||||||
|
|
86
lib/fsk4hf/genft2.f90
Normal file
86
lib/fsk4hf/genft2.f90
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
subroutine genft2(msg0,ichk,msgsent,i4tone,itype)
|
||||||
|
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
|
||||||
|
!
|
||||||
|
! Encode an MSK144 message
|
||||||
|
! Input:
|
||||||
|
! - msg0 requested message to be transmitted
|
||||||
|
! - ichk if ichk=1, return only msgsent
|
||||||
|
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
|
||||||
|
! - msgsent message as it will be decoded
|
||||||
|
! - i4tone array of audio tone values, 0 or 1
|
||||||
|
! - itype message type
|
||||||
|
! 1 = 77 bit message
|
||||||
|
! 7 = 16 bit message "<Call_1 Call2> Rpt"
|
||||||
|
|
||||||
|
use iso_c_binding, only: c_loc,c_size_t
|
||||||
|
use packjt77
|
||||||
|
character*37 msg0
|
||||||
|
character*37 message !Message to be generated
|
||||||
|
character*37 msgsent !Message as it will be received
|
||||||
|
character*77 c77
|
||||||
|
integer*4 i4tone(144)
|
||||||
|
integer*1 codeword(128)
|
||||||
|
integer*1 msgbits(77)
|
||||||
|
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
|
||||||
|
integer*1 s16(16)
|
||||||
|
real*8 xi(864),xq(864),pi,twopi
|
||||||
|
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
|
||||||
|
equivalence (ihash,i1hash)
|
||||||
|
logical unpk77_success
|
||||||
|
|
||||||
|
nsym=128
|
||||||
|
pi=4.0*atan(1.0)
|
||||||
|
twopi=8.*atan(1.0)
|
||||||
|
|
||||||
|
message(1:37)=' '
|
||||||
|
itype=1
|
||||||
|
if(msg0(1:1).eq.'@') then !Generate a fixed tone
|
||||||
|
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
|
||||||
|
go to 2
|
||||||
|
1 nfreq=1000
|
||||||
|
2 i4tone(1)=nfreq
|
||||||
|
else
|
||||||
|
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
|
||||||
|
|
||||||
|
if(message(1:1).eq.'<') then
|
||||||
|
i2=index(message,'>')
|
||||||
|
i1=0
|
||||||
|
if(i2.gt.0) i1=index(message(1:i2),' ')
|
||||||
|
if(i1.gt.0) then
|
||||||
|
call genmsk40(message,msgsent,ichk,i4tone,itype)
|
||||||
|
if(itype.lt.0) go to 999
|
||||||
|
i4tone(41)=-40
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
i3=-1
|
||||||
|
n3=-1
|
||||||
|
call pack77(message,i3,n3,c77)
|
||||||
|
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
|
||||||
|
|
||||||
|
if(ichk.eq.1) go to 999
|
||||||
|
read(c77,"(77i1)") msgbits
|
||||||
|
call encode_128_90(msgbits,codeword)
|
||||||
|
|
||||||
|
!Create 144-bit channel vector:
|
||||||
|
bitseq=0
|
||||||
|
bitseq(1:16)=s16
|
||||||
|
bitseq(17:144)=codeword
|
||||||
|
|
||||||
|
i4tone=bitseq
|
||||||
|
endif
|
||||||
|
|
||||||
|
999 return
|
||||||
|
end subroutine genft2
|
63
lib/fsk4hf/getcandidates2.f90
Normal file
63
lib/fsk4hf/getcandidates2.f90
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
subroutine getcandidates2(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
||||||
|
ncand,sbase)
|
||||||
|
|
||||||
|
! For now, hardwired to find the largest peak in the average spectrum
|
||||||
|
|
||||||
|
include 'ft2_params.f90'
|
||||||
|
real s(NH1,NHSYM)
|
||||||
|
real savg(NH1),savsm(NH1)
|
||||||
|
real sbase(NH1)
|
||||||
|
real x(NFFT1)
|
||||||
|
complex cx(0:NH1)
|
||||||
|
real candidate(3,maxcand)
|
||||||
|
integer*2 id(NMAX)
|
||||||
|
integer*1 s8(8)
|
||||||
|
integer indx(NH1)
|
||||||
|
data s8/0,1,1,1,0,0,1,0/
|
||||||
|
equivalence (x,cx)
|
||||||
|
|
||||||
|
! Compute symbol spectra, stepping by NSTEP steps.
|
||||||
|
savg=0.
|
||||||
|
tstep=NSTEP/12000.0
|
||||||
|
df=12000.0/NFFT1 !3.125 Hz
|
||||||
|
fac=1.0/300.0
|
||||||
|
do j=1,NHSYM
|
||||||
|
ia=(j-1)*NSTEP + 1
|
||||||
|
ib=ia+NSPS-1
|
||||||
|
x(1:NSPS)=fac*id(ia:ib)
|
||||||
|
x(NSPS+1:)=0.
|
||||||
|
call four2a(x,NFFT1,1,-1,0) !r2c FFT
|
||||||
|
do i=1,NH1
|
||||||
|
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
|
||||||
|
enddo
|
||||||
|
savg=savg + s(1:NH1,j) !Average spectrum
|
||||||
|
enddo
|
||||||
|
savsm=0.
|
||||||
|
do i=2,NH1-1
|
||||||
|
savsm(i)=sum(savg(i-1:i+1))/3.
|
||||||
|
enddo
|
||||||
|
|
||||||
|
nfa=fa/df
|
||||||
|
nfb=fb/df
|
||||||
|
np=nfb-nfa+1
|
||||||
|
indx=0
|
||||||
|
call indexx(savsm(nfa:nfb),np,indx)
|
||||||
|
xn=savsm(nfa+indx(nint(0.3*np)))
|
||||||
|
savsm=savsm/xn
|
||||||
|
imax=-1
|
||||||
|
xmax=-99.
|
||||||
|
do i=2,NH1-1
|
||||||
|
if(savsm(i).gt.savsm(i-1).and. &
|
||||||
|
savsm(i).gt.savsm(i+1).and. &
|
||||||
|
savsm(i).gt.xmax) then
|
||||||
|
xmax=savsm(i)
|
||||||
|
imax=i
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
f0=imax*df
|
||||||
|
if(xmax.gt.1.2) then
|
||||||
|
ncand=ncand+1
|
||||||
|
candidate(1,ncand)=f0
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
end subroutine getcandidates2
|
@ -1,194 +0,0 @@
|
|||||||
program msksim
|
|
||||||
|
|
||||||
! Simulate characteristics of a potential "MSK10" mode using LDPC (168,84)
|
|
||||||
! code, OQPDK modulation, and 30 s T/R sequences.
|
|
||||||
|
|
||||||
! Reception and Demodulation algorithm:
|
|
||||||
! 1. Compute coarse spectrum; find fc1 = approx carrier freq
|
|
||||||
! 2. Mix from fc1 to 0; LPF at +/- 0.75*R
|
|
||||||
! 3. Square, FFT; find peaks near -R/2 and +R/2 to get fc2
|
|
||||||
! 4. Mix from fc2 to 0
|
|
||||||
! 5. Fit cb13 (central part of csync) to c -> lag, phase
|
|
||||||
! 6. Fit complex ploynomial for channel equalization
|
|
||||||
! 7. Get soft bits from equalized data
|
|
||||||
|
|
||||||
parameter (KK=84) !Information bits (72 + CRC12)
|
|
||||||
parameter (ND=168) !Data symbols: LDPC (168,84), r=1/2
|
|
||||||
parameter (NS=65) !Sync symbols (2 x 26 + Barker 13)
|
|
||||||
parameter (NR=3) !Ramp up/down
|
|
||||||
parameter (NN=NR+NS+ND) !Total symbols (236)
|
|
||||||
parameter (NSPS=1152/72) !Samples per MSK symbol (16)
|
|
||||||
parameter (N2=2*NSPS) !Samples per OQPSK symbol (32)
|
|
||||||
parameter (N13=13*N2) !Samples in central sync vector (416)
|
|
||||||
parameter (NZ=NSPS*NN) !Samples in baseband waveform (3776)
|
|
||||||
parameter (NFFT1=4*NSPS,NH1=NFFT1/2)
|
|
||||||
|
|
||||||
character*8 arg
|
|
||||||
complex cbb(0:NZ-1) !Complex baseband waveform
|
|
||||||
complex csync(0:NZ-1) !Sync symbols only, from cbb
|
|
||||||
complex cb13(0:N13-1) !Barker 13 waveform
|
|
||||||
complex c(0:NZ-1) !Complex waveform
|
|
||||||
complex c0(0:NZ-1) !Complex waveform
|
|
||||||
complex zz(NS+ND) !Complex symbol values (intermediate)
|
|
||||||
complex z
|
|
||||||
real xnoise(0:NZ-1) !Generated random noise
|
|
||||||
real ynoise(0:NZ-1) !Generated random noise
|
|
||||||
real rxdata(ND),llr(ND) !Soft symbols
|
|
||||||
real pp(2*NSPS) !Shaped pulse for OQPSK
|
|
||||||
real a(5) !For twkfreq1
|
|
||||||
real aa(20),bb(20) !Fitted polyco's
|
|
||||||
integer id(NS+ND) !NRZ values (+/-1) for Sync and Data
|
|
||||||
integer ierror(NS+ND)
|
|
||||||
integer icw(NN)
|
|
||||||
integer*1 msgbits(KK),decoded(KK),apmask(ND),cw(ND)
|
|
||||||
! integer*1 codeword(ND)
|
|
||||||
data msgbits/0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1, &
|
|
||||||
1,1,1,0,1,1,1,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,0,1,1,0,1,1, &
|
|
||||||
1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0/
|
|
||||||
|
|
||||||
nargs=iargc()
|
|
||||||
if(nargs.ne.6) then
|
|
||||||
print*,'Usage: mskhfsim f0(Hz) delay(ms) fspread(Hz) maxn iters snr(dB)'
|
|
||||||
print*,'Example: mskhfsim 0 0 0 5 10 -20'
|
|
||||||
print*,'Set snr=0 to cycle through a range'
|
|
||||||
go to 999
|
|
||||||
endif
|
|
||||||
call getarg(1,arg)
|
|
||||||
read(arg,*) f0 !Generated carrier frequency
|
|
||||||
call getarg(2,arg)
|
|
||||||
read(arg,*) delay !Delta_t (ms) for Watterson model
|
|
||||||
call getarg(3,arg)
|
|
||||||
read(arg,*) fspread !Fspread (Hz) for Watterson model
|
|
||||||
call getarg(4,arg)
|
|
||||||
read(arg,*) maxn !Max nterms for polyfit
|
|
||||||
call getarg(5,arg)
|
|
||||||
read(arg,*) iters !Iterations at each SNR
|
|
||||||
call getarg(6,arg)
|
|
||||||
read(arg,*) snrdb !Specified SNR_2500
|
|
||||||
|
|
||||||
twopi=8.0*atan(1.0)
|
|
||||||
fs=12000.0/72.0 !Sample rate = 166.6666667 Hz
|
|
||||||
dt=1.0/fs !Sample interval (s)
|
|
||||||
tt=NSPS*dt !Duration of "itone" symbols (s)
|
|
||||||
ts=2*NSPS*dt !Duration of OQPSK symbols (s)
|
|
||||||
baud=1.0/tt !Keying rate for "itone" symbols (baud)
|
|
||||||
txt=NZ*dt !Transmission length (s)
|
|
||||||
bandwidth_ratio=2500.0/(fs/2.0)
|
|
||||||
write(*,1000) f0,delay,fspread,maxn,iters,baud,3*baud,txt
|
|
||||||
1000 format('f0:',f5.1,' Delay:',f4.1,' fSpread:',f5.2,' maxn:',i3, &
|
|
||||||
' Iters:',i6/'Baud:',f7.3,' BW:',f5.1,' TxT:',f5.1,f5.2/)
|
|
||||||
write(*,1004)
|
|
||||||
1004 format(/' SNR err ber fer fsigma'/37('-'))
|
|
||||||
|
|
||||||
do i=1,N2 !Half-sine pulse shape
|
|
||||||
pp(i)=sin(0.5*(i-1)*twopi/(2*NSPS))
|
|
||||||
enddo
|
|
||||||
|
|
||||||
call genmskhf(msgbits,id,icw,cbb,csync)!Generate baseband waveform and csync
|
|
||||||
cb13=csync(1680:2095) !Copy the Barker 13 waveform
|
|
||||||
a=0.
|
|
||||||
a(1)=f0
|
|
||||||
call twkfreq1(cbb,NZ,fs,a,cbb) !Mix to specified frequency
|
|
||||||
|
|
||||||
isna=-10
|
|
||||||
isnb=-30
|
|
||||||
if(snrdb.ne.0.0) then
|
|
||||||
isna=nint(snrdb)
|
|
||||||
isnb=isna
|
|
||||||
endif
|
|
||||||
do isnr=isna,isnb,-1 !Loop over SNR range
|
|
||||||
snrdb=isnr
|
|
||||||
sig=sqrt(bandwidth_ratio) * 10.0**(0.05*snrdb)
|
|
||||||
if(snrdb.gt.90.0) sig=1.0
|
|
||||||
nhard=0
|
|
||||||
nhardsync=0
|
|
||||||
nfe=0
|
|
||||||
sqf=0.
|
|
||||||
do iter=1,iters !Loop over requested iterations
|
|
||||||
c=cbb
|
|
||||||
if(delay.ne.0.0 .or. fspread.ne.0.0) then
|
|
||||||
call watterson(c,NZ,fs,delay,fspread)
|
|
||||||
endif
|
|
||||||
c=sig*c !Scale to requested SNR
|
|
||||||
if(snrdb.lt.90) then
|
|
||||||
do i=0,NZ-1 !Generate gaussian noise
|
|
||||||
xnoise(i)=gran()
|
|
||||||
ynoise(i)=gran()
|
|
||||||
enddo
|
|
||||||
c=c + cmplx(xnoise,ynoise) !Add AWGN noise
|
|
||||||
endif
|
|
||||||
|
|
||||||
call getfc1(c,fc1) !First approx for freq
|
|
||||||
call getfc2(c,csync,fc1,fc2,fc3) !Refined freq
|
|
||||||
sqf=sqf + (fc1+fc2-f0)**2
|
|
||||||
|
|
||||||
!NB: Measured performance is about equally good using fc2 or fc3 here:
|
|
||||||
a(1)=-(fc1+fc2)
|
|
||||||
a(2:5)=0.
|
|
||||||
call twkfreq1(c,NZ,fs,a,c) !Mix c down by fc1+fc2
|
|
||||||
|
|
||||||
! The following may not be necessary?
|
|
||||||
! z=sum(c(1680:2095)*cb13)/208.0 !Get phase from Barker 13 vector
|
|
||||||
! z0=z/abs(z)
|
|
||||||
! c=c*conjg(z0)
|
|
||||||
|
|
||||||
!---------------------------------------------------------------- DT
|
|
||||||
! Not presently used:
|
|
||||||
amax=0.
|
|
||||||
jpk=0
|
|
||||||
do j=-20*NSPS,20*NSPS !Get jpk
|
|
||||||
z=sum(c(1680+j:2095+j)*cb13)/208.0
|
|
||||||
if(abs(z).gt.amax) then
|
|
||||||
amax=abs(z)
|
|
||||||
jpk=j
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
xdt=jpk/fs
|
|
||||||
|
|
||||||
nterms=maxn
|
|
||||||
c0=c
|
|
||||||
do itry=1,10
|
|
||||||
idf=itry/2
|
|
||||||
if(mod(itry,2).eq.0) idf=-idf
|
|
||||||
nhard0=0
|
|
||||||
nhardsync0=0
|
|
||||||
ifer=1
|
|
||||||
a(1)=idf*0.01
|
|
||||||
a(2:5)=0.
|
|
||||||
call twkfreq1(c0,NZ,fs,a,c) !Mix c0 into c
|
|
||||||
call cpolyfit(c,pp,id,maxn,aa,bb,zz,nhs)
|
|
||||||
call msksoftsym(zz,aa,bb,id,nterms,ierror,rxdata,nhard0,nhardsync0)
|
|
||||||
if(nhardsync0.gt.12) cycle
|
|
||||||
rxav=sum(rxdata)/ND
|
|
||||||
rx2av=sum(rxdata*rxdata)/ND
|
|
||||||
rxsig=sqrt(rx2av-rxav*rxav)
|
|
||||||
rxdata=rxdata/rxsig
|
|
||||||
ss=0.84
|
|
||||||
llr=2.0*rxdata/(ss*ss)
|
|
||||||
apmask=0
|
|
||||||
max_iterations=40
|
|
||||||
ifer=0
|
|
||||||
call bpdecode168(llr,apmask,max_iterations,decoded,niterations,cw)
|
|
||||||
nbadcrc=0
|
|
||||||
if(niterations.ge.0) call chkcrc12(decoded,nbadcrc)
|
|
||||||
if(niterations.lt.0 .or. count(msgbits.ne.decoded).gt.0 .or. &
|
|
||||||
nbadcrc.ne.0) ifer=1
|
|
||||||
! if(ifer.eq.0) write(67,1301) snrdb,itry,idf,niterations, &
|
|
||||||
! nhardsync0,nhard0
|
|
||||||
!1301 format(f6.1,5i6)
|
|
||||||
if(ifer.eq.0) exit
|
|
||||||
enddo !Freq dither loop
|
|
||||||
nhard=nhard+nhard0
|
|
||||||
nhardsync=nharsdync+nhardsync0
|
|
||||||
nfe=nfe+ifer
|
|
||||||
enddo
|
|
||||||
|
|
||||||
fsigma=sqrt(sqf/iters)
|
|
||||||
ber=float(nhard)/((NS+ND)*iters)
|
|
||||||
fer=float(nfe)/iters
|
|
||||||
write(*,1050) snrdb,nhard,ber,fer,fsigma
|
|
||||||
! write(60,1050) snrdb,nhard,ber,fer,fsigma
|
|
||||||
1050 format(f6.1,i7,f8.4,f7.3,f8.2)
|
|
||||||
enddo
|
|
||||||
|
|
||||||
999 end program msksim
|
|
@ -70,14 +70,14 @@ endfunction
|
|||||||
# M-ary PSK Block Coded Modulation," Igal Sason and Gil Weichman,
|
# M-ary PSK Block Coded Modulation," Igal Sason and Gil Weichman,
|
||||||
# doi: 10.1109/EEEI.2006.321097
|
# doi: 10.1109/EEEI.2006.321097
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
N=174
|
N=128
|
||||||
K=75
|
K=90
|
||||||
R=K/N
|
R=K/N
|
||||||
|
|
||||||
delta=0.01;
|
delta=0.01;
|
||||||
[ths,fval,info,output]=fzero(@f1,[delta,pi/2-delta], optimset ("jacobian", "off"));
|
[ths,fval,info,output]=fzero(@f1,[delta,pi/2-delta], optimset ("jacobian", "off"));
|
||||||
|
|
||||||
for ebnodb=-6:0.5:4
|
for ebnodb=-3:0.5:4
|
||||||
ebno=10^(ebnodb/10.0);
|
ebno=10^(ebnodb/10.0);
|
||||||
esno=ebno*R;
|
esno=ebno*R;
|
||||||
A=sqrt(2*esno);
|
A=sqrt(2*esno);
|
||||||
|
19
lib/fsk4hf/spb_128_90.dat
Normal file
19
lib/fsk4hf/spb_128_90.dat
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
N = 128
|
||||||
|
K = 90
|
||||||
|
R = 0.70312
|
||||||
|
-3.000000 0.000341
|
||||||
|
-2.500000 0.001513
|
||||||
|
-2.000000 0.006049
|
||||||
|
-1.500000 0.021280
|
||||||
|
-1.000000 0.064283
|
||||||
|
-0.500000 0.162755
|
||||||
|
0.000000 0.338430
|
||||||
|
0.500000 0.571867
|
||||||
|
1.000000 0.791634
|
||||||
|
1.500000 0.930284
|
||||||
|
2.000000 0.985385
|
||||||
|
2.500000 0.998258
|
||||||
|
3.000000 0.999893
|
||||||
|
3.500000 0.999997
|
||||||
|
4.000000 1.000000
|
||||||
|
|
6
lib/ft2/cdatetime.f90
Normal file
6
lib/ft2/cdatetime.f90
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
character*17 function cdatetime()
|
||||||
|
character cdate*8,ctime*10
|
||||||
|
call date_and_time(cdate,ctime)
|
||||||
|
cdatetime=cdate(3:8)//'_'//ctime
|
||||||
|
return
|
||||||
|
end function cdatetime
|
279
lib/ft2/ft2.f90
Normal file
279
lib/ft2/ft2.f90
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
program ft2
|
||||||
|
|
||||||
|
use packjt77
|
||||||
|
include 'gcom1.f90'
|
||||||
|
integer ft2audio,ptt
|
||||||
|
logical allok
|
||||||
|
character*20 pttport
|
||||||
|
character*8 arg
|
||||||
|
character*80 fname
|
||||||
|
integer*2 id2(30000)
|
||||||
|
|
||||||
|
open(12,file='all_ft2.txt',status='unknown',position='append')
|
||||||
|
nargs=iargc()
|
||||||
|
if(nargs.eq.1) then
|
||||||
|
call getarg(1,fname)
|
||||||
|
open(10,file=fname,status='old',access='stream')
|
||||||
|
read(10) id2(1:22) !Read (and ignore) the header
|
||||||
|
read(10) id2 !Read the Rx data
|
||||||
|
close(10)
|
||||||
|
call ft2_decode(fname(1:17),nfqso,id2,ndecodes,mycall,hiscall,nrx)
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
|
||||||
|
allok=.true.
|
||||||
|
! Get home-station details
|
||||||
|
open(10,file='ft2.ini',status='old',err=1)
|
||||||
|
go to 2
|
||||||
|
1 print*,'Cannot open ft2.ini'
|
||||||
|
allok=.false.
|
||||||
|
2 read(10,*,err=3) mycall,mygrid,ndevin,ndevout,pttport,exch
|
||||||
|
go to 4
|
||||||
|
3 print*,'Error reading ft2.ini'
|
||||||
|
allok=.false.
|
||||||
|
4 if(index(pttport,'/').lt.1) read(pttport,*) nport
|
||||||
|
hiscall=' '
|
||||||
|
hiscall_next=' '
|
||||||
|
idevin=ndevin
|
||||||
|
idevout=ndevout
|
||||||
|
call padevsub(idevin,idevout)
|
||||||
|
if(idevin.ne.ndevin .or. idevout.ne.ndevout) allok=.false.
|
||||||
|
i1=0
|
||||||
|
i1=ptt(nport,1,1,iptt)
|
||||||
|
i1=ptt(nport,1,0,iptt)
|
||||||
|
if(i1.lt.0 .and. nport.ne.0) allok=.false.
|
||||||
|
if(.not.allok) then
|
||||||
|
write(*,"('Please fix setup error(s) and restart.')")
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
|
||||||
|
nright=1
|
||||||
|
iwrite=0
|
||||||
|
iwave=0
|
||||||
|
nwave=NTZ
|
||||||
|
nfsample=12000
|
||||||
|
ngo=1
|
||||||
|
npabuf=1152
|
||||||
|
ntxok=0
|
||||||
|
ntransmitting=0
|
||||||
|
tx_once=.false.
|
||||||
|
snrdb=99.0
|
||||||
|
txmsg='CQ K1JT FN20'
|
||||||
|
ltx=.false.
|
||||||
|
lrx=.false.
|
||||||
|
autoseq=.false.
|
||||||
|
QSO_in_progress=.false.
|
||||||
|
ntxed=0
|
||||||
|
|
||||||
|
if(nargs.eq.3) then
|
||||||
|
call getarg(1,txmsg)
|
||||||
|
call getarg(2,arg)
|
||||||
|
read(arg,*) f0
|
||||||
|
call getarg(3,arg)
|
||||||
|
read(arg,*) snrdb
|
||||||
|
tx_once=.true.
|
||||||
|
ftx=1500.0
|
||||||
|
call transmit(-1,ftx,iptt)
|
||||||
|
snrdb=99.0
|
||||||
|
endif
|
||||||
|
|
||||||
|
! Start the audio streams
|
||||||
|
ierr=ft2audio(idevin,idevout,npabuf,nright,y1,y2,NRING,iwrite,itx, &
|
||||||
|
iwave,nwave+3*1152,nfsample,nTxOK,nTransmitting,ngo)
|
||||||
|
if(ierr.ne.0) then
|
||||||
|
print*,'Error',ierr,' starting audio input and/or output.'
|
||||||
|
endif
|
||||||
|
|
||||||
|
999 end program ft2
|
||||||
|
|
||||||
|
subroutine update(total_time,ic1,ic2)
|
||||||
|
|
||||||
|
use wavhdr
|
||||||
|
type(hdr) h
|
||||||
|
real*8 total_time
|
||||||
|
integer*8 count0,count1,clkfreq
|
||||||
|
integer ptt
|
||||||
|
integer*2 id(30000)
|
||||||
|
logical transmitted,level,ok
|
||||||
|
character*70 line
|
||||||
|
character cdatetime*17,fname*17,mode*8,band*6
|
||||||
|
include 'gcom1.f90'
|
||||||
|
data nt0/-1/,transmitted/.false./,snr/-99.0/
|
||||||
|
data level/.false./
|
||||||
|
save nt0,transmitted,level,snr,iptt
|
||||||
|
|
||||||
|
if(ic1.ne.0 .or. ic2.ne.0) then
|
||||||
|
if(ic1.eq.27 .and. ic2.eq.0) ngo=0 !ESC
|
||||||
|
if(nTxOK.eq.0 .and. ntransmitting.eq.0) then
|
||||||
|
nfunc=0
|
||||||
|
if(ic1.eq.0 .and. ic2.eq.59) nfunc=1 !F1
|
||||||
|
if(ic1.eq.0 .and. ic2.eq.60) nfunc=2 !F2
|
||||||
|
if(ic1.eq.0 .and. ic2.eq.61) nfunc=3 !F3
|
||||||
|
if(ic1.eq.0 .and. ic2.eq.62) nfunc=4 !F4
|
||||||
|
if(ic1.eq.0 .and. ic2.eq.63) nfunc=5 !F5
|
||||||
|
if(nfunc.eq.1 .or. (nfunc.ge.2 .and. hiscall.ne.' ')) then
|
||||||
|
ftx=1500.0
|
||||||
|
call transmit(nfunc,ftx,iptt)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if(ic1.eq.13 .and. ic2.eq.0) hiscall=hiscall_next
|
||||||
|
if((ic1.eq.97 .or. ic1.eq.65) .and. ic2.eq.0) autoseq=.not.autoseq
|
||||||
|
if((ic1.eq.108 .or. ic1.eq.76) .and. ic2.eq.0) level=.not.level
|
||||||
|
endif
|
||||||
|
|
||||||
|
if(ntransmitting.eq.1) transmitted=.true.
|
||||||
|
if(transmitted .and. ntransmitting.eq.0) then
|
||||||
|
i1=0
|
||||||
|
if(iptt.eq.1 .and. nport.gt.0) i1=ptt(nport,0,1,iptt)
|
||||||
|
if(tx_once .and. transmitted) stop
|
||||||
|
transmitted=.false.
|
||||||
|
endif
|
||||||
|
|
||||||
|
nt=2*total_time
|
||||||
|
if(nt.gt.nt0 .or. ic1.ne.0 .or. ic2.ne.0) then
|
||||||
|
if(level) then
|
||||||
|
! Measure and display the average level of signal plus noise in past 0.5 s
|
||||||
|
k=iwrite-6000
|
||||||
|
if(k.lt.1) k=k+NRING
|
||||||
|
sq=0.
|
||||||
|
do i=1,6000
|
||||||
|
k=k+1
|
||||||
|
if(k.gt.NRING) k=k-NRING
|
||||||
|
x=y1(k)
|
||||||
|
sq=sq + x*x
|
||||||
|
enddo
|
||||||
|
sigdb=0.
|
||||||
|
if(sq.gt.0.0) sigdb=db(sq/6000.0)
|
||||||
|
n=sigdb
|
||||||
|
if(n.lt.1) n=1
|
||||||
|
if(n.gt.70) n=70
|
||||||
|
line=' '
|
||||||
|
line(n:n)='*'
|
||||||
|
write(*,1030) sigdb,ntxed,autoseq,QSO_in_progress,(line(i:i),i=1,n)
|
||||||
|
1030 format(f4.1,i3,2L2,1x,70a1)
|
||||||
|
! write(*,1020) nt,total_time,iwrite,itx,ntxok,ntransmitting,ndecodes, &
|
||||||
|
! snr,sigdb,line
|
||||||
|
!1020 format(i6,f9.3,i10,i6,3i3,f6.0,f6.1,1x,a30)
|
||||||
|
endif
|
||||||
|
k=iwrite-30000
|
||||||
|
if(k.lt.1) k=k+NRING
|
||||||
|
do i=1,30000
|
||||||
|
k=k+1
|
||||||
|
if(k.gt.NRING) k=k-NRING
|
||||||
|
id(i)=y1(k)
|
||||||
|
enddo
|
||||||
|
nutc=0
|
||||||
|
nfqso=1500
|
||||||
|
ndecodes=0
|
||||||
|
if(maxval(abs(id)).gt.0) then
|
||||||
|
call system_clock(count0,clkfreq)
|
||||||
|
nrx=-1
|
||||||
|
call ft2_decode(cdatetime(),nfqso,id,ndecodes,mycall,hiscall,nrx)
|
||||||
|
call system_clock(count1,clkfreq)
|
||||||
|
! tdecode=float(count1-count0)/float(clkfreq)
|
||||||
|
|
||||||
|
if(ndecodes.ge.1) then
|
||||||
|
fMHz=7.074
|
||||||
|
mode='FT2'
|
||||||
|
nsubmode=1
|
||||||
|
ntrperiod=0
|
||||||
|
h=default_header(12000,30000)
|
||||||
|
k=0
|
||||||
|
do i=1,250
|
||||||
|
sq=0
|
||||||
|
do n=1,120
|
||||||
|
k=k+1
|
||||||
|
x=id(k)
|
||||||
|
sq=sq + x*x
|
||||||
|
enddo
|
||||||
|
write(43,3043) i,0.01*i,1.e-4*sq
|
||||||
|
3043 format(i7,f12.6,f12.3)
|
||||||
|
enddo
|
||||||
|
call set_wsjtx_wav_params(fMHz,mode,nsubmode,ntrperiod,id)
|
||||||
|
band=""
|
||||||
|
mode=""
|
||||||
|
nsubmode=-1
|
||||||
|
ntrperiod=-1
|
||||||
|
call get_wsjtx_wav_params(id,band,mode,nsubmode,ntrperiod,ok)
|
||||||
|
! write(*,1010) band,ntrperiod,mode,char(ichar('A')-1+id(3))
|
||||||
|
!1010 format('Band: ',a6,' T/R period:',i4,' Mode: ',a8,1x,a1)
|
||||||
|
|
||||||
|
fname=cdatetime()
|
||||||
|
fname(14:17)='.wav'
|
||||||
|
open(13,file=fname,status='unknown',access='stream')
|
||||||
|
write(13) h,id
|
||||||
|
close(13)
|
||||||
|
endif
|
||||||
|
if(autoseq .and.nrx.eq.2) QSO_in_progress=.true.
|
||||||
|
if(autoseq .and. QSO_in_progress .and. nrx.ge.1 .and. nrx.le.4) then
|
||||||
|
lrx(nrx)=.true.
|
||||||
|
ftx=1500.0
|
||||||
|
if(ntxed.eq.1) then
|
||||||
|
if(nrx.eq.2) then
|
||||||
|
call transmit(3,ftx,iptt)
|
||||||
|
else
|
||||||
|
call transmit(1,ftx,iptt)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if(ntxed.eq.2) then
|
||||||
|
if(nrx.eq.3) then
|
||||||
|
call transmit(4,ftx,iptt)
|
||||||
|
QSO_in_progress=.false.
|
||||||
|
write(*,1032)
|
||||||
|
1032 format('QSO complete: S+P side')
|
||||||
|
else
|
||||||
|
call transmit(2,ftx,iptt)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
if(ntxed.eq.3) then
|
||||||
|
if(nrx.eq.4) then
|
||||||
|
QSO_in_progress=.false.
|
||||||
|
write(*,1034)
|
||||||
|
1034 format('QSO complete: CQ side')
|
||||||
|
else
|
||||||
|
call transmit(3,ftx,iptt)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
nt0=nt
|
||||||
|
endif
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine update
|
||||||
|
|
||||||
|
character*17 function cdatetime()
|
||||||
|
character cdate*8,ctime*10
|
||||||
|
call date_and_time(cdate,ctime)
|
||||||
|
cdatetime=cdate(3:8)//'_'//ctime
|
||||||
|
return
|
||||||
|
end function cdatetime
|
||||||
|
|
||||||
|
subroutine transmit(nfunc,ftx,iptt)
|
||||||
|
include 'gcom1.f90'
|
||||||
|
character*17 cdatetime
|
||||||
|
integer ptt
|
||||||
|
|
||||||
|
if(nTxOK.eq.1) return
|
||||||
|
|
||||||
|
if(nfunc.eq.1) txmsg='CQ '//trim(mycall)//' '//mygrid
|
||||||
|
if(nfunc.eq.2) txmsg=trim(hiscall)//' '//trim(mycall)// &
|
||||||
|
' 559 '//trim(exch)
|
||||||
|
if(nfunc.eq.3) txmsg=trim(hiscall)//' '//trim(mycall)// &
|
||||||
|
' R 559 '//trim(exch)
|
||||||
|
if(nfunc.eq.4) txmsg=trim(hiscall)//' '//trim(mycall)//' RR73'
|
||||||
|
if(nfunc.eq.5) txmsg='TNX 73 GL'
|
||||||
|
call ft2_iwave(txmsg,ftx,snrdb,iwave)
|
||||||
|
iwave(23041:)=0
|
||||||
|
i1=ptt(nport,1,1,iptt)
|
||||||
|
ntxok=1
|
||||||
|
n=len(trim(txmsg))
|
||||||
|
write(*,1010) cdatetime(),0,0.0,nint(ftx),(txmsg(i:i),i=1,n)
|
||||||
|
write(12,1010) cdatetime(),0,0.0,nint(ftx),(txmsg(i:i),i=1,n)
|
||||||
|
1010 format(a17,i4,f6.2,i5,' Tx ',37a1)
|
||||||
|
if(nfunc.ge.1 .and. nfunc.le.4) ntxed=nfunc
|
||||||
|
if(nfunc.ge.1 .and. nfunc.le.5) ltx(nfunc)=.true.
|
||||||
|
if(nfunc.eq.2 .or. nfunc.eq.3) QSO_in_progress=.true.
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine transmit
|
2
lib/ft2/ft2.ini
Normal file
2
lib/ft2/ft2.ini
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
K1JT FN20 1 5 0 NJ
|
||||||
|
MyCall MyGrid AudioIn AudioOut PTTport Exch
|
298
lib/ft2/ft2_decode.f90
Normal file
298
lib/ft2/ft2_decode.f90
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
subroutine ft2_decode(cdatetime0,nfqso,iwave,ndecodes,mycall,hiscall,nrx,line)
|
||||||
|
|
||||||
|
use crc
|
||||||
|
use packjt77
|
||||||
|
include 'ft2_params.f90'
|
||||||
|
character message*37,c77*77
|
||||||
|
character*61 line
|
||||||
|
character*37 decodes(100)
|
||||||
|
character*120 data_dir
|
||||||
|
character*17 cdatetime0,cdatetime
|
||||||
|
character*6 mycall,hiscall,hhmmss
|
||||||
|
complex c2(0:NMAX/16-1) !Complex waveform
|
||||||
|
complex cb(0:NMAX/16-1)
|
||||||
|
complex cd(0:144*10-1) !Complex waveform
|
||||||
|
complex c1(0:9),c0(0:9)
|
||||||
|
complex ccor(0:1,144)
|
||||||
|
complex csum,cterm,cc0,cc1,csync1
|
||||||
|
real*8 fMHz
|
||||||
|
|
||||||
|
real a(5)
|
||||||
|
real rxdata(128),llr(128) !Soft symbols
|
||||||
|
real llr2(128)
|
||||||
|
real sbits(144),sbits1(144),sbits3(144)
|
||||||
|
real ps(0:8191),psbest(0:8191)
|
||||||
|
real candidate(3,100)
|
||||||
|
real savg(NH1)
|
||||||
|
integer*2 iwave(NMAX) !Generated full-length waveform
|
||||||
|
integer*1 message77(77),apmask(128),cw(128)
|
||||||
|
integer*1 hbits(144),hbits1(144),hbits3(144)
|
||||||
|
integer*1 s16(16)
|
||||||
|
logical unpk77_success
|
||||||
|
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
|
||||||
|
|
||||||
|
hhmmss=cdatetime0(8:13)
|
||||||
|
fs=12000.0/NDOWN !Sample rate
|
||||||
|
dt=1/fs !Sample interval after downsample (s)
|
||||||
|
tt=NSPS*dt !Duration of "itone" symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate for "itone" symbols (baud)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
h=0.8 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
|
||||||
|
|
||||||
|
dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample
|
||||||
|
dphi0=-1*dphi
|
||||||
|
dphi1=+1*dphi
|
||||||
|
phi0=0.0
|
||||||
|
phi1=0.0
|
||||||
|
do i=0,9
|
||||||
|
c1(i)=cmplx(cos(phi1),sin(phi1))
|
||||||
|
c0(i)=cmplx(cos(phi0),sin(phi0))
|
||||||
|
phi1=mod(phi1+dphi1,twopi)
|
||||||
|
phi0=mod(phi0+dphi0,twopi)
|
||||||
|
enddo
|
||||||
|
the=twopi*h/2.0
|
||||||
|
cc1=cmplx(cos(the),-sin(the))
|
||||||
|
cc0=cmplx(cos(the),sin(the))
|
||||||
|
|
||||||
|
data_dir="."
|
||||||
|
fMHz=7.074
|
||||||
|
ncoh=1
|
||||||
|
candidate=0.0
|
||||||
|
ncand=0
|
||||||
|
fa=375.0
|
||||||
|
fb=3000.0
|
||||||
|
syncmin=0.2
|
||||||
|
maxcand=100
|
||||||
|
nfqso=-1
|
||||||
|
call getcandidates2a(iwave,fa,fb,maxcand,savg,candidate,ncand)
|
||||||
|
ndecodes=0
|
||||||
|
do icand=1,ncand
|
||||||
|
f0=candidate(1,icand)
|
||||||
|
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
|
||||||
|
call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol
|
||||||
|
! 750 samples/second here
|
||||||
|
ibest=-1
|
||||||
|
sybest=-99.
|
||||||
|
dfbest=-1.
|
||||||
|
!### do if=-15,+15
|
||||||
|
do if=-30,30
|
||||||
|
df=if
|
||||||
|
a=0.
|
||||||
|
a(1)=-df
|
||||||
|
call twkfreq1(c2,NMAX/16,fs,a,cb)
|
||||||
|
do is=0,374 !DT search range is 0 - 0.5 s
|
||||||
|
csync1=0.
|
||||||
|
cterm=1
|
||||||
|
do ib=1,16
|
||||||
|
i1=(ib-1)*10+is
|
||||||
|
i2=i1+136*10
|
||||||
|
if(s16(ib).eq.1) then
|
||||||
|
csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm
|
||||||
|
cterm=cterm*cc1
|
||||||
|
else
|
||||||
|
csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm
|
||||||
|
cterm=cterm*cc0
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
if(abs(csync1).gt.sybest) then
|
||||||
|
ibest=is
|
||||||
|
sybest=abs(csync1)
|
||||||
|
dfbest=df
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
a=0.
|
||||||
|
a(1)=-dfbest
|
||||||
|
call twkfreq1(c2,NMAX/16,fs,a,cb)
|
||||||
|
ib=ibest
|
||||||
|
cd=cb(ib:ib+144*10-1)
|
||||||
|
s2=sum(real(cd*conjg(cd)))/(10*144)
|
||||||
|
cd=cd/sqrt(s2)
|
||||||
|
do nseq=1,5
|
||||||
|
if( nseq.eq.1 ) then ! noncoherent single-symbol detection
|
||||||
|
sbits1=0.0
|
||||||
|
do ibit=1,144
|
||||||
|
ib=(ibit-1)*10
|
||||||
|
ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9)))
|
||||||
|
ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9)))
|
||||||
|
sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit))
|
||||||
|
hbits1(ibit)=0
|
||||||
|
if(sbits1(ibit).gt.0) hbits1(ibit)=1
|
||||||
|
enddo
|
||||||
|
sbits=sbits1
|
||||||
|
hbits=hbits1
|
||||||
|
sbits3=sbits1
|
||||||
|
hbits3=hbits1
|
||||||
|
elseif( nseq.ge.2 ) then
|
||||||
|
nbit=2*nseq-1
|
||||||
|
numseq=2**(nbit)
|
||||||
|
ps=0
|
||||||
|
do ibit=nbit/2+1,144-nbit/2
|
||||||
|
ps=0.0
|
||||||
|
pmax=0.0
|
||||||
|
do iseq=0,numseq-1
|
||||||
|
csum=0.0
|
||||||
|
cterm=1.0
|
||||||
|
k=1
|
||||||
|
do i=nbit-1,0,-1
|
||||||
|
ibb=iand(iseq/(2**i),1)
|
||||||
|
csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm
|
||||||
|
if(ibb.eq.0) cterm=cterm*cc0
|
||||||
|
if(ibb.eq.1) cterm=cterm*cc1
|
||||||
|
k=k+1
|
||||||
|
enddo
|
||||||
|
ps(iseq)=abs(csum)
|
||||||
|
if( ps(iseq) .gt. pmax ) then
|
||||||
|
pmax=ps(iseq)
|
||||||
|
ibflag=1
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
if( ibflag .eq. 1 ) then
|
||||||
|
psbest=ps
|
||||||
|
ibflag=0
|
||||||
|
endif
|
||||||
|
call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit))
|
||||||
|
hbits3(ibit)=0
|
||||||
|
if(sbits3(ibit).gt.0) hbits3(ibit)=1
|
||||||
|
enddo
|
||||||
|
sbits=sbits3
|
||||||
|
hbits=hbits3
|
||||||
|
endif
|
||||||
|
nsync_qual=count(hbits(1:16).eq.s16)
|
||||||
|
if(nsync_qual.lt.10) exit
|
||||||
|
rxdata=sbits(17:144)
|
||||||
|
rxav=sum(rxdata(1:128))/128.0
|
||||||
|
rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0
|
||||||
|
rxsig=sqrt(rx2av-rxav*rxav)
|
||||||
|
rxdata=rxdata/rxsig
|
||||||
|
sigma=0.80
|
||||||
|
llr(1:128)=2*rxdata/(sigma*sigma)
|
||||||
|
apmask=0
|
||||||
|
max_iterations=40
|
||||||
|
do ibias=0,0
|
||||||
|
llr2=llr
|
||||||
|
if(ibias.eq.1) llr2=llr+0.4
|
||||||
|
if(ibias.eq.2) llr2=llr-0.4
|
||||||
|
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
|
||||||
|
if(nharderror.ge.0) exit
|
||||||
|
enddo
|
||||||
|
nhardmin=-1
|
||||||
|
if(sum(message77).eq.0) cycle
|
||||||
|
if( nharderror.ge.0 ) then
|
||||||
|
write(c77,'(77i1)') message77(1:77)
|
||||||
|
call unpack77(c77,nrx,message,unpk77_success)
|
||||||
|
idupe=0
|
||||||
|
do i=1,ndecodes
|
||||||
|
if(decodes(i).eq.message) idupe=1
|
||||||
|
enddo
|
||||||
|
if(idupe.eq.1) exit
|
||||||
|
ndecodes=ndecodes+1
|
||||||
|
decodes(ndecodes)=message
|
||||||
|
xsnr=db(sybest*sybest) - 115.0 !### Rough estimate of S/N ###
|
||||||
|
nsnr=nint(xsnr)
|
||||||
|
freq=f0+dfbest
|
||||||
|
write(line,1000) hhmmss,nsnr,ibest/750.0,nint(freq),message
|
||||||
|
1000 format(a6,i4,f5.2,i5,' + ',1x,a37)
|
||||||
|
open(24,file='all_ft2.txt',status='unknown',position='append')
|
||||||
|
write(24,1002) cdatetime0,nsnr,ibest/750.0,nint(freq),message, &
|
||||||
|
nseq,nharderror,nhardmin
|
||||||
|
if(hhmmss.eq.' ') write(*,1002) cdatetime0,nsnr, &
|
||||||
|
ibest/750.0,nint(freq),message,nseq,nharderror,nhardmin
|
||||||
|
1002 format(a17,i4,f6.2,i5,' Rx ',a37,3i5)
|
||||||
|
close(24)
|
||||||
|
|
||||||
|
!### Temporary: assume most recent decoded message conveys "hiscall".
|
||||||
|
i0=index(message,' ')
|
||||||
|
if(i0.ge.3 .and. i0.le.7) then
|
||||||
|
hiscall=message(i0+1:i0+6)
|
||||||
|
i1=index(hiscall,' ')
|
||||||
|
if(i1.gt.0) hiscall=hiscall(1:i1)
|
||||||
|
endif
|
||||||
|
nrx=-1
|
||||||
|
if(index(message,'CQ ').eq.1) nrx=1
|
||||||
|
if((index(message,trim(mycall)//' ').eq.1) .and. &
|
||||||
|
(index(message,' '//trim(hiscall)//' ').ge.4)) then
|
||||||
|
if(index(message,' 559 ').gt.8) nrx=2
|
||||||
|
if(index(message,' R 559 ').gt.8) nrx=3
|
||||||
|
if(index(message,' RR73 ').gt.8) nrx=4
|
||||||
|
endif
|
||||||
|
!###
|
||||||
|
exit
|
||||||
|
endif
|
||||||
|
enddo ! nseq
|
||||||
|
enddo !candidate list
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine ft2_decode
|
||||||
|
|
||||||
|
subroutine getbitmetric(ib,ps,ns,xmet)
|
||||||
|
real ps(0:ns-1)
|
||||||
|
xm1=0
|
||||||
|
xm0=0
|
||||||
|
do i=0,ns-1
|
||||||
|
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
|
||||||
|
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
|
||||||
|
enddo
|
||||||
|
xmet=xm1-xm0
|
||||||
|
return
|
||||||
|
end subroutine getbitmetric
|
||||||
|
|
||||||
|
subroutine downsample2(ci,f0,co)
|
||||||
|
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
|
||||||
|
complex ci(0:NI-1),ct(0:NI-1)
|
||||||
|
complex co(0:NO-1)
|
||||||
|
fs=12000.0
|
||||||
|
df=fs/NI
|
||||||
|
ct=ci
|
||||||
|
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
|
||||||
|
i0=nint(f0/df)
|
||||||
|
ct=cshift(ct,i0)
|
||||||
|
co=0.0
|
||||||
|
co(0)=ct(0)
|
||||||
|
b=8.0
|
||||||
|
do i=1,NO/2
|
||||||
|
arg=(i*df/b)**2
|
||||||
|
filt=exp(-arg)
|
||||||
|
co(i)=ct(i)*filt
|
||||||
|
co(NO-i)=ct(NI-i)*filt
|
||||||
|
enddo
|
||||||
|
co=co/NO
|
||||||
|
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
|
||||||
|
return
|
||||||
|
end subroutine downsample2
|
||||||
|
|
||||||
|
subroutine ft2_downsample(iwave,f0,c)
|
||||||
|
|
||||||
|
! Input: i*2 data in iwave() at sample rate 12000 Hz
|
||||||
|
! Output: Complex data in c(), sampled at 1200 Hz
|
||||||
|
|
||||||
|
include 'ft2_params.f90'
|
||||||
|
parameter (NFFT2=NMAX/16)
|
||||||
|
integer*2 iwave(NMAX)
|
||||||
|
complex c(0:NMAX/16-1)
|
||||||
|
complex c1(0:NFFT2-1)
|
||||||
|
complex cx(0:NMAX/2)
|
||||||
|
real x(NMAX)
|
||||||
|
equivalence (x,cx)
|
||||||
|
|
||||||
|
BW=4.0*75
|
||||||
|
df=12000.0/NMAX
|
||||||
|
x=iwave
|
||||||
|
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
||||||
|
ibw=nint(BW/df)
|
||||||
|
i0=nint(f0/df)
|
||||||
|
c1=0.
|
||||||
|
c1(0)=cx(i0)
|
||||||
|
do i=1,NFFT2/2
|
||||||
|
arg=(i-1)*df/bw
|
||||||
|
win=exp(-arg*arg)
|
||||||
|
c1(i)=cx(i0+i)*win
|
||||||
|
c1(NFFT2-i)=cx(i0-i)*win
|
||||||
|
enddo
|
||||||
|
c1=c1/NFFT2
|
||||||
|
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
||||||
|
c=c1(0:NMAX/16-1)
|
||||||
|
return
|
||||||
|
end subroutine ft2_downsample
|
88
lib/ft2/ft2_gfsk_iwave.f90
Normal file
88
lib/ft2/ft2_gfsk_iwave.f90
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
subroutine ft2_gfsk_iwave(msg37,f0,snrdb,iwave)
|
||||||
|
|
||||||
|
! Generate waveform for experimental "FT2" mode
|
||||||
|
|
||||||
|
use packjt77
|
||||||
|
include 'ft2_params.f90' !Set various constants
|
||||||
|
parameter (NWAVE=(NN+2)*NSPS)
|
||||||
|
character msg37*37,msgsent37*37
|
||||||
|
real wave(NWAVE),xnoise(NWAVE)
|
||||||
|
real dphi(NWAVE)
|
||||||
|
real pulse(480)
|
||||||
|
|
||||||
|
integer itone(NN)
|
||||||
|
integer*2 iwave(NWAVE) !Generated full-length waveform
|
||||||
|
logical first
|
||||||
|
data first/.true./
|
||||||
|
save pulse
|
||||||
|
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
fs=12000.0 !Sample rate (Hz)
|
||||||
|
dt=1.0/fs !Sample interval (s)
|
||||||
|
hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0)
|
||||||
|
tt=NSPS*dt !Duration of symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate (baud)
|
||||||
|
bw=1.5*baud !Occupied bandwidth (Hz)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
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
|
||||||
|
txt=NN*NSPS/12000.0
|
||||||
|
|
||||||
|
if(first) then
|
||||||
|
! The filtered frequency pulse
|
||||||
|
do i=1,480
|
||||||
|
tt=(i-240.5)/160.0
|
||||||
|
pulse(i)=gfsk_pulse(1.0,tt)
|
||||||
|
enddo
|
||||||
|
dphi_peak=twopi*(hmod/2.0)/real(NSPS)
|
||||||
|
first=.false.
|
||||||
|
endif
|
||||||
|
|
||||||
|
! Source-encode, then get itone():
|
||||||
|
itype=1
|
||||||
|
call genft2(msg37,0,msgsent37,itone,itype)
|
||||||
|
|
||||||
|
! Create the instantaneous frequency waveform
|
||||||
|
dphi=0.0
|
||||||
|
do j=1,NN
|
||||||
|
ib=(j-1)*160+1
|
||||||
|
ie=ib+480-1
|
||||||
|
dphi(ib:ie)=dphi(ib:ie)+dphi_peak*pulse*(2*itone(j)-1)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
phi=0.0
|
||||||
|
wave=0.0
|
||||||
|
sqrt2=sqrt(2.)
|
||||||
|
dphi=dphi+twopi*f0*dt
|
||||||
|
do j=1,NWAVE
|
||||||
|
wave(j)=sqrt2*sin(phi)
|
||||||
|
sqsig=sqsig + wave(j)**2
|
||||||
|
phi=mod(phi+dphi(j),twopi)
|
||||||
|
enddo
|
||||||
|
wave(1:160)=wave(1:160)*(1.0-cos(twopi*(/(i,i=0,159)/)/320.0) )/2.0
|
||||||
|
wave(145*160+1:146*160)=wave(145*160+1:146*160)*(1.0+cos(twopi*(/(i,i=0,159)/)/320.0 ))/2.0
|
||||||
|
wave(146*160+1:)=0.
|
||||||
|
|
||||||
|
if(snrdb.gt.90.0) then
|
||||||
|
iwave=nint((32767.0/sqrt(2.0))*wave)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
sqnoise=1.e-30
|
||||||
|
if(snrdb.lt.90) then
|
||||||
|
do i=1,NWAVE !Add gaussian noise at specified SNR
|
||||||
|
xnoise(i)=gran() !Noise has rms = 1.0
|
||||||
|
enddo
|
||||||
|
endif
|
||||||
|
xnoise=xnoise*sqrt(0.5*fs/2500.0)
|
||||||
|
fac=30.0
|
||||||
|
snr_amplitude=10.0**(0.05*snrdb)
|
||||||
|
wave=fac*(snr_amplitude*wave + xnoise)
|
||||||
|
datpk=maxval(abs(wave))
|
||||||
|
print*,'A',snr_amplitude,datpk
|
||||||
|
|
||||||
|
iwave=nint((30000.0/datpk)*wave)
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine ft2_gfsk_iwave
|
64
lib/ft2/ft2_iwave.f90
Normal file
64
lib/ft2/ft2_iwave.f90
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
subroutine ft2_iwave(msg37,f0,snrdb,iwave)
|
||||||
|
|
||||||
|
! Generate waveform for experimental "FT2" mode
|
||||||
|
|
||||||
|
use packjt77
|
||||||
|
include 'ft2_params.f90' !Set various constants
|
||||||
|
parameter (NWAVE=NN*NSPS)
|
||||||
|
character msg37*37,msgsent37*37
|
||||||
|
real wave(NWAVE),xnoise(NWAVE)
|
||||||
|
integer itone(NN)
|
||||||
|
integer*2 iwave(NWAVE) !Generated full-length waveform
|
||||||
|
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
fs=12000.0 !Sample rate (Hz)
|
||||||
|
dt=1.0/fs !Sample interval (s)
|
||||||
|
hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0)
|
||||||
|
tt=NSPS*dt !Duration of symbols (s)
|
||||||
|
baud=1.0/tt !Keying rate (baud)
|
||||||
|
bw=1.5*baud !Occupied bandwidth (Hz)
|
||||||
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
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
|
||||||
|
txt=NN*NSPS/12000.0
|
||||||
|
|
||||||
|
! Source-encode, then get itone():
|
||||||
|
itype=1
|
||||||
|
call genft2(msg37,0,msgsent37,itone,itype)
|
||||||
|
|
||||||
|
k=0
|
||||||
|
phi=0.0
|
||||||
|
sqsig=0.
|
||||||
|
do j=1,NN !Generate real waveform
|
||||||
|
dphi=twopi*(f0*dt+(hmod/2.0)*(2*itone(j)-1)/real(NSPS))
|
||||||
|
do i=1,NSPS
|
||||||
|
k=k+1
|
||||||
|
wave(k)=sqrt(2.0)*sin(phi) !Signal has rms = 1.0
|
||||||
|
sqsig=sqsig + wave(k)**2
|
||||||
|
phi=mod(phi+dphi,twopi)
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
if(snrdb.gt.90.0) then
|
||||||
|
iwave=nint((32767.0/sqrt(2.0))*wave)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
sqnoise=1.e-30
|
||||||
|
if(snrdb.lt.90) then
|
||||||
|
do i=1,NWAVE !Add gaussian noise at specified SNR
|
||||||
|
xnoise(i)=gran() !Noise has rms = 1.0
|
||||||
|
enddo
|
||||||
|
endif
|
||||||
|
xnoise=xnoise*sqrt(0.5*fs/2500.0)
|
||||||
|
fac=30.0
|
||||||
|
snr_amplitude=10.0**(0.05*snrdb)
|
||||||
|
wave=fac*(snr_amplitude*wave + xnoise)
|
||||||
|
datpk=maxval(abs(wave))
|
||||||
|
print*,'A',snr_amplitude,datpk
|
||||||
|
|
||||||
|
iwave=nint((30000.0/datpk)*wave)
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine ft2_iwave
|
12
lib/ft2/ft2_params.f90
Normal file
12
lib/ft2/ft2_params.f90
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
! LDPC (128,90) code
|
||||||
|
parameter (KK=90) !Information bits (77 + CRC13)
|
||||||
|
parameter (ND=128) !Data symbols
|
||||||
|
parameter (NS=16) !Sync symbols (2x8)
|
||||||
|
parameter (NN=NS+ND) !Total channel symbols (144)
|
||||||
|
parameter (NSPS=160) !Samples per symbol at 12000 S/s
|
||||||
|
parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040)
|
||||||
|
parameter (NMAX=30000) !Samples in iwave (2.5*12000)
|
||||||
|
parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
||||||
|
parameter (NSTEP=NSPS/4) !Rough time-sync step size
|
||||||
|
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
|
||||||
|
parameter (NDOWN=16) !Downsample factor
|
347
lib/ft2/ft2audio.c
Normal file
347
lib/ft2/ft2audio.c
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "portaudio.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
int iaa;
|
||||||
|
int icc;
|
||||||
|
double total_time=0.0;
|
||||||
|
|
||||||
|
// Definition of structure pointing to the audio data
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int *iwrite;
|
||||||
|
int *itx;
|
||||||
|
int *TxOK;
|
||||||
|
int *Transmitting;
|
||||||
|
int *nwave;
|
||||||
|
int *nright;
|
||||||
|
int nring;
|
||||||
|
int nfs;
|
||||||
|
short *y1;
|
||||||
|
short *y2;
|
||||||
|
short *iwave;
|
||||||
|
} paTestData;
|
||||||
|
|
||||||
|
// Input callback routine:
|
||||||
|
static int
|
||||||
|
SoundIn( void *inputBuffer, void *outputBuffer,
|
||||||
|
unsigned long framesPerBuffer,
|
||||||
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags,
|
||||||
|
void *userData )
|
||||||
|
{
|
||||||
|
paTestData *data = (paTestData*)userData;
|
||||||
|
short *in = (short*)inputBuffer;
|
||||||
|
unsigned int i;
|
||||||
|
static int ia=0;
|
||||||
|
|
||||||
|
if(*data->Transmitting) return 0;
|
||||||
|
|
||||||
|
if(statusFlags!=0) printf("Status flags %d\n",(int)statusFlags);
|
||||||
|
|
||||||
|
if((statusFlags&1) == 0) {
|
||||||
|
//increment buffer pointers only if data available
|
||||||
|
ia=*data->iwrite;
|
||||||
|
if(*data->nright==0) { //Use left channel for input
|
||||||
|
for(i=0; i<framesPerBuffer; i++) {
|
||||||
|
data->y1[ia] = (*in++);
|
||||||
|
data->y2[ia] = (*in++);
|
||||||
|
ia++;
|
||||||
|
}
|
||||||
|
} else { //Use right channel
|
||||||
|
for(i=0; i<framesPerBuffer; i++) {
|
||||||
|
data->y2[ia] = (*in++);
|
||||||
|
data->y1[ia] = (*in++);
|
||||||
|
ia++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ia >= data->nring) ia=0; //Wrap buffer pointer if necessary
|
||||||
|
*data->iwrite = ia; //Save buffer pointer
|
||||||
|
iaa=ia;
|
||||||
|
total_time += (double)framesPerBuffer/12000.0;
|
||||||
|
// printf("iwrite: %d\n",*data->iwrite);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output callback routine:
|
||||||
|
static int
|
||||||
|
SoundOut( void *inputBuffer, void *outputBuffer,
|
||||||
|
unsigned long framesPerBuffer,
|
||||||
|
const PaStreamCallbackTimeInfo* timeInfo,
|
||||||
|
PaStreamCallbackFlags statusFlags,
|
||||||
|
void *userData )
|
||||||
|
{
|
||||||
|
paTestData *data = (paTestData*)userData;
|
||||||
|
short *wptr = (short*)outputBuffer;
|
||||||
|
unsigned int i,n;
|
||||||
|
static short int n2;
|
||||||
|
static int ic=0;
|
||||||
|
static int TxOKz=0;
|
||||||
|
static clock_t tstart=-1;
|
||||||
|
static clock_t tend=-1;
|
||||||
|
static int nsent=0;
|
||||||
|
|
||||||
|
// printf("txOK: %d %d\n",TxOKz,*data->TxOK);
|
||||||
|
|
||||||
|
if(*data->TxOK && (!TxOKz)) ic=0; //Reset buffer pointer to start Tx
|
||||||
|
*data->Transmitting=*data->TxOK; //Set the "transmitting" flag
|
||||||
|
|
||||||
|
if(*data->TxOK) {
|
||||||
|
if(!TxOKz) {
|
||||||
|
// Start of a transmission
|
||||||
|
tstart=clock();
|
||||||
|
nsent=0;
|
||||||
|
// printf("Start Tx\n");
|
||||||
|
}
|
||||||
|
TxOKz=*data->TxOK;
|
||||||
|
for(i=0 ; i < framesPerBuffer; i++ ) {
|
||||||
|
n2=data->iwave[ic];
|
||||||
|
*wptr++ = n2; //left
|
||||||
|
*wptr++ = n2; //right
|
||||||
|
ic++;
|
||||||
|
|
||||||
|
if(ic > *data->nwave) {
|
||||||
|
*data->TxOK = 0;
|
||||||
|
*data->Transmitting = 0;
|
||||||
|
*data->iwrite = 0; //Reset Rx buffer pointer to 0
|
||||||
|
ic=0;
|
||||||
|
tend=clock();
|
||||||
|
double TxT=((double)(tend-tstart))/CLOCKS_PER_SEC;
|
||||||
|
// printf("End Tx, TxT = %f nSent = %d\n",TxT,nsent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nsent += framesPerBuffer;
|
||||||
|
} else {
|
||||||
|
memset((void*)outputBuffer, 0, 2*sizeof(short)*framesPerBuffer);
|
||||||
|
}
|
||||||
|
*data->itx = icc; //Save buffer pointer
|
||||||
|
icc=ic;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************/
|
||||||
|
int ft2audio_(int *ndevin, int *ndevout, int *npabuf, int *nright,
|
||||||
|
short y1[], short y2[], int *nring, int *iwrite,
|
||||||
|
int *itx, short iwave[], int *nwave, int *nfsample,
|
||||||
|
int *TxOK, int *Transmitting, int *ngo)
|
||||||
|
|
||||||
|
{
|
||||||
|
paTestData data;
|
||||||
|
PaStream *instream, *outstream;
|
||||||
|
PaStreamParameters inputParameters, outputParameters;
|
||||||
|
// PaStreamInfo *streamInfo;
|
||||||
|
|
||||||
|
int nfpb = *npabuf;
|
||||||
|
int nSampleRate = *nfsample;
|
||||||
|
int ndevice_in = *ndevin;
|
||||||
|
int ndevice_out = *ndevout;
|
||||||
|
double dSampleRate = (double) *nfsample;
|
||||||
|
PaError err_init, err_open_in, err_open_out, err_start_in, err_start_out;
|
||||||
|
PaError err = 0;
|
||||||
|
|
||||||
|
data.iwrite = iwrite;
|
||||||
|
data.itx = itx;
|
||||||
|
data.TxOK = TxOK;
|
||||||
|
data.Transmitting = Transmitting;
|
||||||
|
data.y1 = y1;
|
||||||
|
data.y2 = y2;
|
||||||
|
data.nring = *nring;
|
||||||
|
data.nright = nright;
|
||||||
|
data.nwave = nwave;
|
||||||
|
data.iwave = iwave;
|
||||||
|
data.nfs = nSampleRate;
|
||||||
|
|
||||||
|
err_init = Pa_Initialize(); // Initialize PortAudio
|
||||||
|
|
||||||
|
if(err_init) {
|
||||||
|
printf("Error initializing PortAudio.\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_init),
|
||||||
|
err_init);
|
||||||
|
Pa_Terminate(); // I don't think we need this but...
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("Opening device %d for input, %d for output...\n",
|
||||||
|
// ndevice_in,ndevice_out);
|
||||||
|
|
||||||
|
inputParameters.device = ndevice_in;
|
||||||
|
inputParameters.channelCount = 2;
|
||||||
|
inputParameters.sampleFormat = paInt16;
|
||||||
|
inputParameters.suggestedLatency = 0.2;
|
||||||
|
inputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
|
|
||||||
|
// Test if this configuration actually works, so we do not run into an
|
||||||
|
// ugly assertion
|
||||||
|
err_open_in = Pa_IsFormatSupported(&inputParameters, NULL, dSampleRate);
|
||||||
|
|
||||||
|
if (err_open_in == 0) {
|
||||||
|
err_open_in = Pa_OpenStream(
|
||||||
|
&instream, //address of stream
|
||||||
|
&inputParameters,
|
||||||
|
NULL,
|
||||||
|
dSampleRate, //Sample rate
|
||||||
|
nfpb, //Frames per buffer
|
||||||
|
paNoFlag,
|
||||||
|
(PaStreamCallback *)SoundIn, //Callback routine
|
||||||
|
(void *)&data); //address of data structure
|
||||||
|
|
||||||
|
if(err_open_in) { // We should have no error here usually
|
||||||
|
printf("Error opening input audio stream:\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in),
|
||||||
|
err_open_in);
|
||||||
|
err = 1;
|
||||||
|
} else {
|
||||||
|
// printf("Successfully opened audio input.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Error opening input audio stream.\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in),
|
||||||
|
err_open_in);
|
||||||
|
err = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputParameters.device = ndevice_out;
|
||||||
|
outputParameters.channelCount = 2;
|
||||||
|
outputParameters.sampleFormat = paInt16;
|
||||||
|
outputParameters.suggestedLatency = 0.2;
|
||||||
|
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||||
|
|
||||||
|
// Test if this configuration actually works, so we do not run into an
|
||||||
|
// ugly assertion.
|
||||||
|
err_open_out = Pa_IsFormatSupported(NULL, &outputParameters, dSampleRate);
|
||||||
|
|
||||||
|
if (err_open_out == 0) {
|
||||||
|
err_open_out = Pa_OpenStream(
|
||||||
|
&outstream, //address of stream
|
||||||
|
NULL,
|
||||||
|
&outputParameters,
|
||||||
|
dSampleRate, //Sample rate
|
||||||
|
nfpb, //Frames per buffer
|
||||||
|
paNoFlag,
|
||||||
|
(PaStreamCallback *)SoundOut, //Callback routine
|
||||||
|
(void *)&data); //address of data structure
|
||||||
|
|
||||||
|
if(err_open_out) { // We should have no error here usually
|
||||||
|
printf("Error opening output audio stream!\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out),
|
||||||
|
err_open_out);
|
||||||
|
err += 2;
|
||||||
|
} else {
|
||||||
|
// printf("Successfully opened audio output.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("Error opening output audio stream.\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out),
|
||||||
|
err_open_out);
|
||||||
|
err += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there was no error in opening both streams start them
|
||||||
|
if (err == 0) {
|
||||||
|
err_start_in = Pa_StartStream(instream); //Start input stream
|
||||||
|
if(err_start_in) {
|
||||||
|
printf("Error starting input audio stream!\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_in),
|
||||||
|
err_start_in);
|
||||||
|
err += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
err_start_out = Pa_StartStream(outstream); //Start output stream
|
||||||
|
if(err_start_out) {
|
||||||
|
printf("Error starting output audio stream!\n");
|
||||||
|
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_out),
|
||||||
|
err_start_out);
|
||||||
|
err += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == 0) printf("Audio streams running normally.\n******************************************************************\n");
|
||||||
|
|
||||||
|
while( Pa_IsStreamActive(instream) && (*ngo != 0) && (err == 0) ) {
|
||||||
|
int ic1=0;
|
||||||
|
int ic2=0;
|
||||||
|
if(_kbhit()) ic1 = _getch();
|
||||||
|
if(_kbhit()) ic2 = _getch();
|
||||||
|
// if(ic1!=0 || ic2!=0) printf("%d %d %d\n",iaa,ic1,ic2);
|
||||||
|
update_(&total_time,&ic1,&ic2);
|
||||||
|
Pa_Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
Pa_AbortStream(instream); // Abort stream
|
||||||
|
Pa_CloseStream(instream); // Close stream, we're done.
|
||||||
|
Pa_AbortStream(outstream); // Abort stream
|
||||||
|
Pa_CloseStream(outstream); // Close stream, we're done.
|
||||||
|
|
||||||
|
Pa_Terminate();
|
||||||
|
|
||||||
|
return(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int padevsub_(int *idevin, int *idevout)
|
||||||
|
{
|
||||||
|
int numdev,ndefin,ndefout;
|
||||||
|
int nchin[101], nchout[101];
|
||||||
|
int i, devIdx;
|
||||||
|
int numDevices;
|
||||||
|
const PaDeviceInfo *pdi;
|
||||||
|
PaError err;
|
||||||
|
|
||||||
|
Pa_Initialize();
|
||||||
|
numDevices = Pa_GetDeviceCount();
|
||||||
|
numdev = numDevices;
|
||||||
|
|
||||||
|
if( numDevices < 0 ) {
|
||||||
|
err = numDevices;
|
||||||
|
Pa_Terminate();
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((devIdx = Pa_GetDefaultInputDevice()) > 0) {
|
||||||
|
ndefin = devIdx;
|
||||||
|
} else {
|
||||||
|
ndefin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((devIdx = Pa_GetDefaultOutputDevice()) > 0) {
|
||||||
|
ndefout = devIdx;
|
||||||
|
} else {
|
||||||
|
ndefout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nAudio Input Output Device Name\n");
|
||||||
|
printf("Device Channels Channels\n");
|
||||||
|
printf("------------------------------------------------------------------\n");
|
||||||
|
|
||||||
|
for( i=0; i < numDevices; i++ ) {
|
||||||
|
pdi = Pa_GetDeviceInfo(i);
|
||||||
|
// if(i == Pa_GetDefaultInputDevice()) ndefin = i;
|
||||||
|
// if(i == Pa_GetDefaultOutputDevice()) ndefout = i;
|
||||||
|
nchin[i]=pdi->maxInputChannels;
|
||||||
|
nchout[i]=pdi->maxOutputChannels;
|
||||||
|
printf(" %2d %2d %2d %s\n",i,nchin[i],nchout[i],
|
||||||
|
pdi->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nUser requested devices: Input = %2d Output = %2d\n",
|
||||||
|
*idevin,*idevout);
|
||||||
|
printf("Default devices: Input = %2d Output = %2d\n",
|
||||||
|
ndefin,ndefout);
|
||||||
|
if((*idevin<0) || (*idevin>=numdev)) *idevin=ndefin;
|
||||||
|
if((*idevout<0) || (*idevout>=numdev)) *idevout=ndefout;
|
||||||
|
if((*idevin==0) && (*idevout==0)) {
|
||||||
|
*idevin=ndefin;
|
||||||
|
*idevout=ndefout;
|
||||||
|
}
|
||||||
|
printf("Will open devices: Input = %2d Output = %2d\n",
|
||||||
|
*idevin,*idevout);
|
||||||
|
|
||||||
|
Pa_Terminate();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
7
lib/ft2/g4.cmd
Normal file
7
lib/ft2/g4.cmd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
gcc -c ft2audio.c
|
||||||
|
gcc -c ptt.c
|
||||||
|
gfortran -c ../77bit/packjt77.f90
|
||||||
|
gfortran -c ../wavhdr.f90
|
||||||
|
gfortran -c ../crc.f90
|
||||||
|
gfortran -o ft2 -fbounds-check -fno-second-underscore -ffpe-trap=invalid,zero -Wall -Wno-conversion -Wno-character-truncation ft2.f90 ft2_iwave.f90 ft2_decode.f90 getcandidates2.f90 ft2audio.o ptt.o /JTSDK/wsjtx-output/qt55/2.1.0/Release/build/libwsjt_fort.a /JTSDK/wsjtx-output/qt55/2.1.0/Release/build/libwsjt_cxx.a libportaudio.a ../libfftw3f_win.a -lwinmm
|
||||||
|
rm *.o *.mod
|
34
lib/ft2/gcom1.f90
Normal file
34
lib/ft2/gcom1.f90
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
! Variable Purpose
|
||||||
|
!---------------------------------------------------------------------------
|
||||||
|
integer NRING !Length of Rx ring buffer
|
||||||
|
integer NTZ !Length of Tx waveform in samples
|
||||||
|
parameter(NRING=230400) !Ring buffer at 12000 samples/sec
|
||||||
|
parameter(NTZ=23040) !144*160
|
||||||
|
parameter(NMAX=30000) !2.5*12000
|
||||||
|
real snrdb
|
||||||
|
integer ndevin !Device# for audio input
|
||||||
|
integer ndevout !Device# for audio output
|
||||||
|
integer iwrite !Pointer to Rx ring buffer
|
||||||
|
integer itx !Pointer to Tx buffer
|
||||||
|
integer ngo !Set to 0 to terminate audio streams
|
||||||
|
integer nTransmitting !Actually transmitting?
|
||||||
|
integer nTxOK !OK to transmit?
|
||||||
|
integer nport !COM port for PTT
|
||||||
|
logical tx_once !Transmit one message, then exit
|
||||||
|
logical ltx !True if msg i has been transmitted
|
||||||
|
logical lrx !True if msg i has been received
|
||||||
|
logical autoseq
|
||||||
|
logical QSO_in_progress
|
||||||
|
integer*2 y1 !Ring buffer for audio channel 0
|
||||||
|
integer*2 y2 !Ring buffer for audio channel 1
|
||||||
|
integer*2 iwave !Data for Tx audio
|
||||||
|
character*6 mycall
|
||||||
|
character*6 hiscall
|
||||||
|
character*6 hiscall_next
|
||||||
|
character*4 mygrid
|
||||||
|
character*3 exch
|
||||||
|
character*37 txmsg
|
||||||
|
|
||||||
|
common/gcom1/snrdb,ndevin,ndevout,iwrite,itx,ngo,nTransmitting,nTxOK,nport, &
|
||||||
|
ntxed,tx_once,y1(NRING),y2(NRING),iwave(NTZ+3*1152),ltx(5),lrx(5), &
|
||||||
|
autoseq,QSO_in_progress,mycall,hiscall,hiscall_next,mygrid,exch,txmsg
|
86
lib/ft2/genft2.f90
Normal file
86
lib/ft2/genft2.f90
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
subroutine genft2(msg0,ichk,msgsent,i4tone,itype)
|
||||||
|
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
|
||||||
|
!
|
||||||
|
! Encode an MSK144 message
|
||||||
|
! Input:
|
||||||
|
! - msg0 requested message to be transmitted
|
||||||
|
! - ichk if ichk=1, return only msgsent
|
||||||
|
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
|
||||||
|
! - msgsent message as it will be decoded
|
||||||
|
! - i4tone array of audio tone values, 0 or 1
|
||||||
|
! - itype message type
|
||||||
|
! 1 = 77 bit message
|
||||||
|
! 7 = 16 bit message "<Call_1 Call2> Rpt"
|
||||||
|
|
||||||
|
use iso_c_binding, only: c_loc,c_size_t
|
||||||
|
use packjt77
|
||||||
|
character*37 msg0
|
||||||
|
character*37 message !Message to be generated
|
||||||
|
character*37 msgsent !Message as it will be received
|
||||||
|
character*77 c77
|
||||||
|
integer*4 i4tone(144)
|
||||||
|
integer*1 codeword(128)
|
||||||
|
integer*1 msgbits(77)
|
||||||
|
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
|
||||||
|
integer*1 s16(16)
|
||||||
|
real*8 xi(864),xq(864),pi,twopi
|
||||||
|
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
|
||||||
|
equivalence (ihash,i1hash)
|
||||||
|
logical unpk77_success
|
||||||
|
|
||||||
|
nsym=128
|
||||||
|
pi=4.0*atan(1.0)
|
||||||
|
twopi=8.*atan(1.0)
|
||||||
|
|
||||||
|
message(1:37)=' '
|
||||||
|
itype=1
|
||||||
|
if(msg0(1:1).eq.'@') then !Generate a fixed tone
|
||||||
|
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
|
||||||
|
go to 2
|
||||||
|
1 nfreq=1000
|
||||||
|
2 i4tone(1)=nfreq
|
||||||
|
else
|
||||||
|
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
|
||||||
|
|
||||||
|
if(message(1:1).eq.'<') then
|
||||||
|
i2=index(message,'>')
|
||||||
|
i1=0
|
||||||
|
if(i2.gt.0) i1=index(message(1:i2),' ')
|
||||||
|
if(i1.gt.0) then
|
||||||
|
call genmsk40(message,msgsent,ichk,i4tone,itype)
|
||||||
|
if(itype.lt.0) go to 999
|
||||||
|
i4tone(41)=-40
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
i3=-1
|
||||||
|
n3=-1
|
||||||
|
call pack77(message,i3,n3,c77)
|
||||||
|
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
|
||||||
|
|
||||||
|
if(ichk.eq.1) go to 999
|
||||||
|
read(c77,"(77i1)") msgbits
|
||||||
|
call encode_128_90(msgbits,codeword)
|
||||||
|
|
||||||
|
!Create 144-bit channel vector:
|
||||||
|
bitseq=0
|
||||||
|
bitseq(1:16)=s16
|
||||||
|
bitseq(17:144)=codeword
|
||||||
|
|
||||||
|
i4tone=bitseq
|
||||||
|
endif
|
||||||
|
|
||||||
|
999 return
|
||||||
|
end subroutine genft2
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user