mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-09-29 08:36:37 -04:00
Merge branch 'release-2.1.0' into develop
This commit is contained in:
commit
1968597783
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}")
|
||||||
|
|
||||||
@ -233,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
|
||||||
@ -275,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
|
||||||
@ -383,9 +391,11 @@ set (wsjt_FSRCS
|
|||||||
lib/astro0.f90
|
lib/astro0.f90
|
||||||
lib/avecho.f90
|
lib/avecho.f90
|
||||||
lib/averms.f90
|
lib/averms.f90
|
||||||
|
lib/ft4/averaged_mf.f90
|
||||||
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
|
||||||
@ -518,7 +528,6 @@ set (wsjt_FSRCS
|
|||||||
lib/msk144sim.f90
|
lib/msk144sim.f90
|
||||||
lib/mskrtd.f90
|
lib/mskrtd.f90
|
||||||
lib/nuttal_window.f90
|
lib/nuttal_window.f90
|
||||||
lib/ft4/ft4b.f90
|
|
||||||
lib/ft4/ft4sim.f90
|
lib/ft4/ft4sim.f90
|
||||||
lib/ft4/ft4sim_mult.f90
|
lib/ft4/ft4sim_mult.f90
|
||||||
lib/ft4/ft4_downsample.f90
|
lib/ft4/ft4_downsample.f90
|
||||||
@ -555,6 +564,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
|
||||||
@ -563,7 +573,7 @@ set (wsjt_FSRCS
|
|||||||
lib/sync64.f90
|
lib/sync64.f90
|
||||||
lib/sync65.f90
|
lib/sync65.f90
|
||||||
lib/ft4/getcandidates4.f90
|
lib/ft4/getcandidates4.f90
|
||||||
lib/ft4/syncft4.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/ft4/sync4d.f90
|
||||||
@ -846,9 +856,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
|
||||||
@ -879,10 +886,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 REQUIRED Widgets Multimedia PrintSupport Sql LinguistTools)
|
||||||
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)
|
||||||
@ -1088,11 +1092,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)
|
||||||
@ -1101,6 +1135,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)
|
||||||
|
|
||||||
@ -1121,7 +1156,6 @@ if (WIN32)
|
|||||||
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# targets
|
# targets
|
||||||
#
|
#
|
||||||
@ -1240,6 +1274,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)
|
||||||
|
|
||||||
@ -1282,11 +1319,14 @@ target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
|
|||||||
add_executable (ft4sim lib/ft4/ft4sim.f90 wsjtx.rc)
|
add_executable (ft4sim lib/ft4/ft4sim.f90 wsjtx.rc)
|
||||||
target_link_libraries (ft4sim wsjt_fort wsjt_cxx)
|
target_link_libraries (ft4sim wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
|
add_executable (averaged_mf lib/ft4/averaged_mf.f90 wsjtx.rc)
|
||||||
|
target_link_libraries (averaged_mf wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
add_executable (ft4sim_mult lib/ft4/ft4sim_mult.f90 wsjtx.rc)
|
add_executable (ft4sim_mult lib/ft4/ft4sim_mult.f90 wsjtx.rc)
|
||||||
target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
|
target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
|
||||||
|
|
||||||
add_executable (ft4d lib/ft4/ft4d.f90 wsjtx.rc)
|
add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
|
||||||
target_link_libraries (ft4d wsjt_fort wsjt_cxx)
|
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
|
||||||
|
|
||||||
endif(WSJT_BUILD_UTILS)
|
endif(WSJT_BUILD_UTILS)
|
||||||
|
|
||||||
|
@ -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"
|
||||||
@ -248,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;
|
||||||
|
|
||||||
@ -294,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}
|
||||||
@ -564,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_;
|
||||||
@ -609,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_;
|
||||||
@ -705,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_;}
|
||||||
@ -716,6 +726,7 @@ 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_;}
|
||||||
@ -742,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)
|
||||||
{
|
{
|
||||||
@ -947,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}
|
||||||
@ -1116,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});
|
||||||
|
|
||||||
@ -1157,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
|
||||||
@ -1242,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_);
|
||||||
@ -1315,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 ();
|
||||||
@ -1463,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_);
|
||||||
|
|
||||||
@ -1496,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 ();
|
||||||
@ -1575,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_);
|
||||||
@ -1598,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_);
|
||||||
@ -2042,6 +2057,7 @@ 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 ();
|
||||||
@ -2076,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 ();
|
||||||
@ -2109,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_);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
@ -147,6 +148,7 @@ 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;
|
||||||
@ -175,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;
|
||||||
@ -268,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;
|
||||||
|
204
Configuration.ui
204
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>
|
||||||
@ -3079,12 +3103,12 @@ Right click for insert and delete options.</string>
|
|||||||
</connections>
|
</connections>
|
||||||
<buttongroups>
|
<buttongroups>
|
||||||
<buttongroup name="special_op_activity_button_group"/>
|
<buttongroup name="special_op_activity_button_group"/>
|
||||||
<buttongroup name="PTT_method_button_group"/>
|
|
||||||
<buttongroup name="TX_audio_source_button_group"/>
|
|
||||||
<buttongroup name="split_mode_button_group"/>
|
|
||||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
|
||||||
<buttongroup name="CAT_data_bits_button_group"/>
|
<buttongroup name="CAT_data_bits_button_group"/>
|
||||||
|
<buttongroup name="split_mode_button_group"/>
|
||||||
<buttongroup name="TX_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_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:
|
||||||
|
@ -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_;
|
||||||
@ -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
|
||||||
// qDebug() << "ModStart" << symbolsLength << framesPerSymbol
|
|
||||||
// << frequency << toneSpacing;
|
|
||||||
|
|
||||||
if(m_state != Idle) stop();
|
if(m_state != Idle) 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;
|
||||||
@ -92,14 +87,11 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||||||
if (synchronize && !m_tuning && !m_bFastMode) {
|
if (synchronize && !m_tuning && !m_bFastMode) {
|
||||||
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));
|
||||||
}
|
}
|
||||||
if(symbolsLength==105 and framesPerSymbol==512
|
|
||||||
and (toneSpacing==12000.0/512.0 or toneSpacing==-2.0)) {
|
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
|
||||||
//### FT4 parameters
|
// << m_ic << m_silentFrames << m_silentFrames/48000.0
|
||||||
m_ic=0;
|
// << mstr << fmod(double(ms0),1000.0*m_period);
|
||||||
m_silentFrames=0;
|
|
||||||
}
|
|
||||||
// qDebug() << "Mod AA" << symbolsLength << framesPerSymbol << toneSpacing;
|
|
||||||
// qDebug() << "Mod AB" << delay_ms << mstr << m_ic << m_silentFrames;
|
|
||||||
|
|
||||||
initialize (QIODevice::ReadOnly, channel);
|
initialize (QIODevice::ReadOnly, channel);
|
||||||
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
|
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
|
||||||
@ -183,11 +175,11 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -259,7 +251,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +259,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||||||
|
|
||||||
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 fsample=48000
|
if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000
|
||||||
if(m_bFastMode) isym=isym%m_symbolsLength;
|
if(m_bFastMode) isym=isym%m_symbolsLength;
|
||||||
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
||||||
if(itone[0]>=100) {
|
if(itone[0]>=100) {
|
||||||
|
@ -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,14 +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;}
|
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;}
|
||||||
@ -72,14 +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;
|
||||||
qint64 m_ms0;
|
qint64 m_ms0;
|
||||||
qint32 m_TRperiod;
|
|
||||||
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 ();
|
||||||
|
|
||||||
|
@ -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,157 @@
|
|||||||
|
|
||||||
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
Copyright 2001 - 2019 by Joe Taylor, K1JT.
|
||||||
|
|
||||||
|
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
|
||||||
---------------------
|
---------------------
|
||||||
|
@ -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_)
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -92,6 +92,7 @@ d). Edit lines as needed. Keeping them in alphabetic order help see dupes.
|
|||||||
: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_2r.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]
|
||||||
|
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,11 @@
|
|||||||
=== 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, contest logging, rig control, the user interface,
|
||||||
* Optimized contest messages for NA VHF, EU VHF, Field Day, RTTY Roundup
|
and accessibility, as well as a number of bug fixes.
|
||||||
|
|
||||||
* Full support for "/R" and "/P" calls in relevant contests
|
|
||||||
|
|
||||||
* New logging features for contesting
|
|
||||||
|
|
||||||
* 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
|
||||||
|
@ -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
|
||||||
|
@ -133,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
|
||||||
@ -152,8 +152,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||||||
if(params%nmode.eq.5) then
|
if(params%nmode.eq.5) then
|
||||||
call timer('decft4 ',0)
|
call timer('decft4 ',0)
|
||||||
call my_ft4%decode(ft4_decoded,id2,params%nQSOProgress,params%nfqso, &
|
call my_ft4%decode(ft4_decoded,id2,params%nQSOProgress,params%nfqso, &
|
||||||
params%nutc,params%nfa,params%nfb,params%ndepth,ncontest, &
|
params%nutc,params%nfa,params%nfb,params%ndepth, &
|
||||||
mycall,hiscall)
|
logical(params%lapcqonly),ncontest,mycall,hiscall)
|
||||||
call timer('decft4 ',1)
|
call timer('decft4 ',1)
|
||||||
go to 800
|
go to 800
|
||||||
endif
|
endif
|
||||||
@ -517,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
|
||||||
@ -598,15 +598,15 @@ 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
|
||||||
|
|
||||||
write(*,1001) params%nutc,snr,dt,nint(freq),decoded0,annot
|
write(*,1001) params%nutc,snr,dt,nint(freq),decoded0,annot
|
||||||
1001 format(i6.6,i4,f5.1,i5,' ~ ',1x,a37,1x,a2)
|
1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,a2)
|
||||||
write(13,1002) params%nutc,nint(sync),snr,dt,freq,0,decoded0
|
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,' FT8')
|
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FT4')
|
||||||
|
|
||||||
call flush(6)
|
call flush(6)
|
||||||
call flush(13)
|
call flush(13)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
64
lib/ft4/averaged_mf.f90
Normal file
64
lib/ft4/averaged_mf.f90
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
program averaged_mf
|
||||||
|
|
||||||
|
parameter (nsps=32)
|
||||||
|
complex cgfsk(3*nsps,64)
|
||||||
|
complex clin(3*nsps,64)
|
||||||
|
complex cavg(3*nsps,4)
|
||||||
|
complex cavl(3*nsps,4)
|
||||||
|
real pulse(3*nsps)
|
||||||
|
real dphi(3*nsps)
|
||||||
|
|
||||||
|
do i=1,3*NSPS
|
||||||
|
t=(i-1.5*nsps)/real(nsps)
|
||||||
|
pulse(i)=gfsk_pulse(1.0,t)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
hmod=1.0
|
||||||
|
dphi_peak=twopi*hmod/real(nsps)
|
||||||
|
|
||||||
|
do iwf=1,64
|
||||||
|
i0=mod((iwf-1)/16,4)
|
||||||
|
i1=mod((iwf-1)/4,4)
|
||||||
|
i2=mod(iwf-1,4)
|
||||||
|
dphi=0.0
|
||||||
|
dphi(1:64)=dphi_peak*pulse(33:96)*i1
|
||||||
|
dphi(1:96)=dphi(1:96)+dphi_peak*pulse(1:96)*i0
|
||||||
|
dphi(33:96)=dphi(33:96)+dphi_peak*pulse(1:64)*i2
|
||||||
|
phi=0.0
|
||||||
|
do j=1,96
|
||||||
|
cgfsk(j,iwf)=cmplx(cos(phi),sin(phi))
|
||||||
|
phi=mod(phi+dphi(j),twopi)
|
||||||
|
enddo
|
||||||
|
cgfsk(:,iwf)=cgfsk(:,iwf)*conjg(cgfsk(48,iwf))
|
||||||
|
enddo
|
||||||
|
|
||||||
|
do iwf=1,64
|
||||||
|
i0=mod((iwf-1)/16,4)
|
||||||
|
i1=mod((iwf-1)/4,4)
|
||||||
|
i2=mod(iwf-1,4)
|
||||||
|
dphi=0.0
|
||||||
|
dphi(1:32)=dphi_peak*i1
|
||||||
|
dphi(33:64)=dphi_peak*i0
|
||||||
|
dphi(65:96)=dphi_peak*i2
|
||||||
|
phi=0.0
|
||||||
|
do j=1,96
|
||||||
|
clin(j,iwf)=cmplx(cos(phi),sin(phi))
|
||||||
|
phi=mod(phi+dphi(j),twopi)
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
|
||||||
|
do i=1,4
|
||||||
|
ib=(i-1)*16+1
|
||||||
|
ie=ib+15
|
||||||
|
cavg(:,i)=sum(cgfsk(:,ib:ie),2)/16.0
|
||||||
|
cavl(:,i)=sum(clin(:,ib:ie),2)/16.0
|
||||||
|
do j=1,96
|
||||||
|
write(*,*) j
|
||||||
|
write(21,*) i,j,real(cavg(j,i)),imag(cavg(j,i)),real(cavl(j,i)),imag(cavl(j,i))
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
end program averaged_mf
|
||||||
|
|
49
lib/ft4/ft4_baseline.f90
Normal file
49
lib/ft4/ft4_baseline.f90
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
subroutine ft4_baseline(s,nfa,nfb,sbase)
|
||||||
|
|
||||||
|
! Fit baseline to spectrum
|
||||||
|
! Input: s(npts) Linear scale in power
|
||||||
|
! Output: sbase(npts) Baseline
|
||||||
|
|
||||||
|
include 'ft4_params.f90'
|
||||||
|
implicit real*8 (a-h,o-z)
|
||||||
|
real*4 s(NH1)
|
||||||
|
real*4 sbase(NH1)
|
||||||
|
real*4 base
|
||||||
|
real*8 x(1000),y(1000),a(5)
|
||||||
|
data nseg/10/,npct/10/
|
||||||
|
|
||||||
|
df=12000.0/NFFT1 !5.21 Hz
|
||||||
|
ia=max(nint(200.0/df),nfa)
|
||||||
|
ib=min(NH1,nfb)
|
||||||
|
do i=ia,ib
|
||||||
|
s(i)=10.0*log10(s(i)) !Convert to dB scale
|
||||||
|
enddo
|
||||||
|
|
||||||
|
nterms=5
|
||||||
|
nlen=(ib-ia+1)/nseg !Length of test segment
|
||||||
|
i0=(ib-ia+1)/2 !Midpoint
|
||||||
|
k=0
|
||||||
|
do n=1,nseg !Loop over all segments
|
||||||
|
ja=ia + (n-1)*nlen
|
||||||
|
jb=ja+nlen-1
|
||||||
|
call pctile(s(ja),nlen,npct,base) !Find lowest npct of points
|
||||||
|
do i=ja,jb
|
||||||
|
if(s(i).le.base) then
|
||||||
|
if (k.lt.1000) k=k+1 !Save all "lower envelope" points
|
||||||
|
x(k)=i-i0
|
||||||
|
y(k)=s(i)
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
kz=k
|
||||||
|
a=0.
|
||||||
|
call polyfit(x,y,y,kz,nterms,0,a,chisqr) !Fit a low-order polynomial
|
||||||
|
do i=ia,ib
|
||||||
|
t=i-i0
|
||||||
|
sbase(i)=a(1)+t*(a(2)+t*(a(3)+t*(a(4)+t*(a(5))))) + 0.65
|
||||||
|
! write(51,3051) i*df,s(i),sbase(i)
|
||||||
|
!3051 format(3f12.3)
|
||||||
|
sbase(i)=10**(sbase(i)/10.0)
|
||||||
|
enddo
|
||||||
|
return
|
||||||
|
end subroutine ft4_baseline
|
@ -1,11 +1,8 @@
|
|||||||
subroutine ft4_downsample(iwave,newdata,f0,c)
|
subroutine ft4_downsample(dd,newdata,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'
|
include 'ft4_params.f90'
|
||||||
parameter (NFFT2=NMAX/16)
|
parameter (NFFT2=NMAX/NDOWN)
|
||||||
integer*2 iwave(NMAX)
|
real dd(NMAX)
|
||||||
complex c(0:NMAX/NDOWN-1)
|
complex c(0:NMAX/NDOWN-1)
|
||||||
complex c1(0:NFFT2-1)
|
complex c1(0:NFFT2-1)
|
||||||
complex cx(0:NMAX/2)
|
complex cx(0:NMAX/2)
|
||||||
@ -33,15 +30,15 @@ subroutine ft4_downsample(iwave,newdata,f0,c)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if(newdata) then
|
if(newdata) then
|
||||||
x=iwave
|
x=dd
|
||||||
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
||||||
endif
|
endif
|
||||||
i0=nint(f0/df)
|
i0=nint(f0/df)
|
||||||
c1=0.
|
c1=0.
|
||||||
c1(0)=cx(i0)
|
if(i0.ge.0 .and. i0.le.NMAX/2) c1(0)=cx(i0)
|
||||||
do i=1,NFFT2/2
|
do i=1,NFFT2/2
|
||||||
if(i0+i.le.NMAX/2) c1(i)=cx(i0+i)
|
if(i0+i.ge.0 .and. i0+i.le.NMAX/2) c1(i)=cx(i0+i)
|
||||||
if(i0-i.ge.0) c1(NFFT2-i)=cx(i0-i)
|
if(i0-i.ge.0 .and. i0-i.le.NMAX/2) c1(NFFT2-i)=cx(i0-i)
|
||||||
enddo
|
enddo
|
||||||
c1=c1*window/NFFT2
|
c1=c1*window/NFFT2
|
||||||
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
! FT4
|
! FT4
|
||||||
! LDPC(174,91) code, four 4x4 Costas arrays for Sync
|
! LDPC(174,91) code, four 4x4 Costas arrays for sync, ramp-up and ramp-down symbols
|
||||||
|
|
||||||
parameter (KK=91) !Information bits (77 + CRC14)
|
parameter (KK=91) !Information bits (77 + CRC14)
|
||||||
parameter (ND=87) !Data symbols
|
parameter (ND=87) !Data symbols
|
||||||
parameter (NS=16) !Sync symbols
|
parameter (NS=16) !Sync symbols
|
||||||
parameter (NN=NS+ND) !Sync and data symbols (103)
|
parameter (NN=NS+ND) !Sync and data symbols (103)
|
||||||
parameter (NN2=NS+ND+2) !Total channel symbols (105)
|
parameter (NN2=NS+ND+2) !Total channel symbols (105)
|
||||||
parameter (NSPS=512) !Samples per symbol at 12000 S/s
|
parameter (NSPS=576) !Samples per symbol at 12000 S/s
|
||||||
parameter (NZ=NSPS*NN) !Sync and Data samples (52736)
|
parameter (NZ=NSPS*NN) !Sync and Data samples (59328)
|
||||||
parameter (NZ2=NSPS*NN2) !Total samples in shaped waveform (53760)
|
parameter (NZ2=NSPS*NN2) !Total samples in shaped waveform (60480)
|
||||||
parameter (NMAX=5*12000) !Samples in iwave (60,000)
|
parameter (NMAX=21*3456) !Samples in iwave (72576)
|
||||||
parameter (NFFT1=2048, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
parameter (NFFT1=2304, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
||||||
parameter (NSTEP=NSPS) !Coarse time-sync step size
|
parameter (NSTEP=NSPS) !Coarse time-sync step size
|
||||||
parameter (NHSYM=(NMAX-NFFT1)/NSTEP) !Number of symbol spectra (1/4-sym steps)
|
parameter (NHSYM=(NMAX-NFFT1)/NSTEP) !Number of symbol spectra (1/4-sym steps)
|
||||||
parameter (NDOWN=16) !Downsample factor
|
parameter (NDOWN=18) !Downsample factor
|
||||||
|
489
lib/ft4/ft4b.f90
489
lib/ft4/ft4b.f90
@ -1,489 +0,0 @@
|
|||||||
subroutine ft4b(cdatetime0,tbuf,nfa,nfb,nQSOProgress,ncontest,nfqso, &
|
|
||||||
iwave,ndecodes,mycall,hiscall,cqstr,line,data_dir)
|
|
||||||
|
|
||||||
use packjt77
|
|
||||||
include 'ft4_params.f90'
|
|
||||||
parameter (NSS=NSPS/NDOWN)
|
|
||||||
|
|
||||||
character message*37,msgsent*37,msg0*37
|
|
||||||
character c77*77
|
|
||||||
character*61 line,linex(100)
|
|
||||||
character*37 decodes(100)
|
|
||||||
character*512 data_dir,fname
|
|
||||||
character*17 cdatetime0
|
|
||||||
character*12 mycall,hiscall
|
|
||||||
character*12 mycall0,hiscall0
|
|
||||||
character*6 hhmmss
|
|
||||||
character*4 cqstr,cqstr0
|
|
||||||
|
|
||||||
complex cd2(0:NMAX/NDOWN-1) !Complex waveform
|
|
||||||
complex cb(0:NMAX/NDOWN-1)
|
|
||||||
complex cd(0:NN*NSS-1) !Complex waveform
|
|
||||||
complex ctwk(2*NSS),ctwk2(2*NSS,-16:16)
|
|
||||||
complex csymb(NSS)
|
|
||||||
complex cs(0:3,NN)
|
|
||||||
real s4(0:3,NN)
|
|
||||||
|
|
||||||
real bmeta(2*NN),bmetb(2*NN),bmetc(2*NN)
|
|
||||||
real a(5)
|
|
||||||
real llr(2*ND),llra(2*ND),llrb(2*ND),llrc(2*ND),llrd(2*ND)
|
|
||||||
real s2(0:255)
|
|
||||||
real candidate(3,100)
|
|
||||||
real savg(NH1),sbase(NH1)
|
|
||||||
|
|
||||||
integer apbits(2*ND)
|
|
||||||
integer apmy_ru(28),aphis_fd(28)
|
|
||||||
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
|
||||||
integer*2 iwave(NMAX) !Raw received data
|
|
||||||
integer*1 message77(77),rvec(77),apmask(2*ND),cw(2*ND)
|
|
||||||
integer*1 hbits(2*NN)
|
|
||||||
integer graymap(0:3)
|
|
||||||
integer ip(1)
|
|
||||||
integer nappasses(0:5) ! # of decoding passes for QSO States 0-5
|
|
||||||
integer naptypes(0:5,4) ! nQSOProgress, decoding pass
|
|
||||||
integer mcq(29)
|
|
||||||
integer mrrr(19),m73(19),mrr73(19)
|
|
||||||
|
|
||||||
logical nohiscall,unpk77_success
|
|
||||||
logical one(0:255,0:7) ! 256 4-symbol sequences, 8 bits
|
|
||||||
logical first, dobigfft
|
|
||||||
|
|
||||||
data icos4a/0,1,3,2/
|
|
||||||
data icos4b/1,0,2,3/
|
|
||||||
data icos4c/2,3,1,0/
|
|
||||||
data icos4d/3,2,0,1/
|
|
||||||
data graymap/0,1,3,2/
|
|
||||||
data msg0/' '/
|
|
||||||
data first/.true./
|
|
||||||
data mcq/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0/
|
|
||||||
data mrrr/0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1/
|
|
||||||
data m73/0,1,1,1,1,1,1,0,1,0,0,1,0,1,0,0,0,0,1/
|
|
||||||
data mrr73/0,1,1,1,1,1,1,0,0,1,1,1,0,1,0,1,0,0,1/
|
|
||||||
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
|
|
||||||
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
|
|
||||||
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
|
|
||||||
save fs,dt,tt,txt,twopi,h,one,first,linex,apbits,nappasses,naptypes, &
|
|
||||||
mycall0,hiscall0,msg0,cqstr0,ctwk2
|
|
||||||
|
|
||||||
call clockit('ft4_deco',0)
|
|
||||||
hhmmss=cdatetime0(8:13)
|
|
||||||
|
|
||||||
if(first) then
|
|
||||||
fs=12000.0/NDOWN !Sample rate after downsampling
|
|
||||||
dt=1/fs !Sample interval after downsample (s)
|
|
||||||
tt=NSPS*dt !Duration of "itone" symbols (s)
|
|
||||||
txt=NZ*dt !Transmission length (s) without ramp up/down
|
|
||||||
twopi=8.0*atan(1.0)
|
|
||||||
h=1.0
|
|
||||||
one=.false.
|
|
||||||
do i=0,255
|
|
||||||
do j=0,7
|
|
||||||
if(iand(i,2**j).ne.0) one(i,j)=.true.
|
|
||||||
enddo
|
|
||||||
enddo
|
|
||||||
|
|
||||||
do idf=-16,16
|
|
||||||
a=0.
|
|
||||||
a(1)=real(idf)
|
|
||||||
ctwk=1.
|
|
||||||
call clockit('twkfreq1',0)
|
|
||||||
call twkfreq1(ctwk,2*NSS,fs/2.0,a,ctwk2(:,idf))
|
|
||||||
call clockit('twkfreq1',1)
|
|
||||||
enddo
|
|
||||||
|
|
||||||
mrrr=2*mod(mrrr+rvec(59:77),2)-1
|
|
||||||
m73=2*mod(m73+rvec(59:77),2)-1
|
|
||||||
mrr73=2*mod(mrr73+rvec(59:77),2)-1
|
|
||||||
nappasses(0)=2
|
|
||||||
nappasses(1)=2
|
|
||||||
nappasses(2)=2
|
|
||||||
nappasses(3)=2
|
|
||||||
nappasses(4)=2
|
|
||||||
nappasses(5)=3
|
|
||||||
|
|
||||||
! iaptype
|
|
||||||
!------------------------
|
|
||||||
! 1 CQ ??? ??? (29 ap bits)
|
|
||||||
! 2 MyCall ??? ??? (29 ap bits)
|
|
||||||
! 3 MyCall DxCall ??? (58 ap bits)
|
|
||||||
! 4 MyCall DxCall RRR (77 ap bits)
|
|
||||||
! 5 MyCall DxCall 73 (77 ap bits)
|
|
||||||
! 6 MyCall DxCall RR73 (77 ap bits)
|
|
||||||
!********
|
|
||||||
naptypes(0,1:4)=(/1,2,0,0/) ! Tx6 selected (CQ)
|
|
||||||
naptypes(1,1:4)=(/2,3,0,0/) ! Tx1
|
|
||||||
naptypes(2,1:4)=(/2,3,0,0/) ! Tx2
|
|
||||||
naptypes(3,1:4)=(/3,6,0,0/) ! Tx3
|
|
||||||
naptypes(4,1:4)=(/3,6,0,0/) ! Tx4
|
|
||||||
naptypes(5,1:4)=(/3,1,2,0/) ! Tx5
|
|
||||||
|
|
||||||
mycall0=''
|
|
||||||
hiscall0=''
|
|
||||||
cqstr0=''
|
|
||||||
first=.false.
|
|
||||||
endif
|
|
||||||
|
|
||||||
if(cqstr.ne.cqstr0) then
|
|
||||||
i0=index(cqstr,' ')
|
|
||||||
if(i0.le.1) then
|
|
||||||
message='CQ A1AA AA01'
|
|
||||||
else
|
|
||||||
message='CQ '//cqstr(1:i0-1)//' A1AA AA01'
|
|
||||||
endif
|
|
||||||
i3=-1
|
|
||||||
n3=-1
|
|
||||||
call pack77(message,i3,n3,c77)
|
|
||||||
call unpack77(c77,1,msgsent,unpk77_success)
|
|
||||||
read(c77,'(29i1)') mcq
|
|
||||||
mcq=2*mod(mcq+rvec(1:29),2)-1
|
|
||||||
cqstr0=cqstr
|
|
||||||
endif
|
|
||||||
|
|
||||||
l1=index(mycall,char(0))
|
|
||||||
if(l1.ne.0) mycall(l1:)=" "
|
|
||||||
l1=index(hiscall,char(0))
|
|
||||||
if(l1.ne.0) hiscall(l1:)=" "
|
|
||||||
if(mycall.ne.mycall0 .or. hiscall.ne.hiscall0) then
|
|
||||||
apbits=0
|
|
||||||
apbits(1)=99
|
|
||||||
apbits(30)=99
|
|
||||||
apmy_ru=0
|
|
||||||
aphis_fd=0
|
|
||||||
|
|
||||||
if(len(trim(mycall)) .lt. 3) go to 10
|
|
||||||
|
|
||||||
nohiscall=.false.
|
|
||||||
hiscall0=hiscall
|
|
||||||
if(len(trim(hiscall0)).lt.3) then
|
|
||||||
hiscall0=mycall ! use mycall for dummy hiscall - mycall won't be hashed.
|
|
||||||
nohiscall=.true.
|
|
||||||
endif
|
|
||||||
message=trim(mycall)//' '//trim(hiscall0)//' RR73'
|
|
||||||
i3=-1
|
|
||||||
n3=-1
|
|
||||||
call pack77(message,i3,n3,c77)
|
|
||||||
call unpack77(c77,1,msgsent,unpk77_success)
|
|
||||||
if(i3.ne.1 .or. (message.ne.msgsent) .or. .not.unpk77_success) go to 10
|
|
||||||
read(c77,'(77i1)') message77
|
|
||||||
apmy_ru=2*mod(message77(1:28)+rvec(2:29),2)-1
|
|
||||||
aphis_fd=2*mod(message77(30:57)+rvec(29:56),2)-1
|
|
||||||
message77=mod(message77+rvec,2)
|
|
||||||
call encode174_91(message77,cw)
|
|
||||||
apbits=2*cw-1
|
|
||||||
if(nohiscall) apbits(30)=99
|
|
||||||
|
|
||||||
10 continue
|
|
||||||
mycall0=mycall
|
|
||||||
hiscall0=hiscall
|
|
||||||
endif
|
|
||||||
candidate=0.0
|
|
||||||
ncand=0
|
|
||||||
syncmin=1.2
|
|
||||||
maxcand=100
|
|
||||||
|
|
||||||
fa=nfa
|
|
||||||
fb=nfb
|
|
||||||
call clockit('getcand4',0)
|
|
||||||
call getcandidates4(iwave,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|
||||||
ncand,sbase)
|
|
||||||
call clockit('getcand4',1)
|
|
||||||
|
|
||||||
ndecodes=0
|
|
||||||
dobigfft=.true.
|
|
||||||
do icand=1,ncand
|
|
||||||
f0=candidate(1,icand)
|
|
||||||
snr=candidate(3,icand)-1.0
|
|
||||||
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
|
|
||||||
call clockit('ft4_down',0)
|
|
||||||
call ft4_downsample(iwave,dobigfft,f0,cd2) !Downsample from 512 to 32 Sa/Symbol
|
|
||||||
if(dobigfft) dobigfft=.false.
|
|
||||||
call clockit('ft4_down',1)
|
|
||||||
|
|
||||||
sum2=sum(cd2*conjg(cd2))/(real(NMAX)/real(NDOWN))
|
|
||||||
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
|
|
||||||
! Sample rate is now 12000/16 = 750 samples/second
|
|
||||||
do isync=1,2
|
|
||||||
if(isync.eq.1) then
|
|
||||||
idfmin=-12
|
|
||||||
idfmax=12
|
|
||||||
idfstp=3
|
|
||||||
ibmin=0
|
|
||||||
ibmax=216 !Max DT = 216/750 = 0.288 s
|
|
||||||
ibstp=4
|
|
||||||
else
|
|
||||||
idfmin=idfbest-4
|
|
||||||
idfmax=idfbest+4
|
|
||||||
idfstp=1
|
|
||||||
ibmin=max(0,ibest-5)
|
|
||||||
ibmax=min(ibest+5,NMAX/NDOWN-1)
|
|
||||||
ibstp=1
|
|
||||||
endif
|
|
||||||
ibest=-1
|
|
||||||
smax=-99.
|
|
||||||
idfbest=0
|
|
||||||
do idf=idfmin,idfmax,idfstp
|
|
||||||
|
|
||||||
call clockit('sync4d ',0)
|
|
||||||
do istart=ibmin,ibmax,ibstp
|
|
||||||
call sync4d(cd2,istart,ctwk2(:,idf),1,sync) !Find sync power
|
|
||||||
if(sync.gt.smax) then
|
|
||||||
smax=sync
|
|
||||||
ibest=istart
|
|
||||||
idfbest=idf
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
call clockit('sync4d ',1)
|
|
||||||
|
|
||||||
enddo
|
|
||||||
enddo
|
|
||||||
f0=f0+real(idfbest)
|
|
||||||
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
|
|
||||||
|
|
||||||
call clockit('ft4down ',0)
|
|
||||||
call ft4_downsample(iwave,dobigfft,f0,cb) !Final downsample with corrected f0
|
|
||||||
call clockit('ft4down ',1)
|
|
||||||
sum2=sum(abs(cb)**2)/(real(NSS)*NN)
|
|
||||||
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
|
|
||||||
cd=cb(ibest:ibest+NN*NSS-1)
|
|
||||||
call clockit('four2a ',0)
|
|
||||||
do k=1,NN
|
|
||||||
i1=(k-1)*NSS
|
|
||||||
csymb=cd(i1:i1+NSS-1)
|
|
||||||
call four2a(csymb,NSS,1,-1,1)
|
|
||||||
cs(0:3,k)=csymb(1:4)
|
|
||||||
s4(0:3,k)=abs(csymb(1:4))
|
|
||||||
enddo
|
|
||||||
call clockit('four2a ',1)
|
|
||||||
|
|
||||||
! Sync quality check
|
|
||||||
is1=0
|
|
||||||
is2=0
|
|
||||||
is3=0
|
|
||||||
is4=0
|
|
||||||
do k=1,4
|
|
||||||
ip=maxloc(s4(:,k))
|
|
||||||
if(icos4a(k-1).eq.(ip(1)-1)) is1=is1+1
|
|
||||||
ip=maxloc(s4(:,k+33))
|
|
||||||
if(icos4b(k-1).eq.(ip(1)-1)) is2=is2+1
|
|
||||||
ip=maxloc(s4(:,k+66))
|
|
||||||
if(icos4c(k-1).eq.(ip(1)-1)) is3=is3+1
|
|
||||||
ip=maxloc(s4(:,k+99))
|
|
||||||
if(icos4d(k-1).eq.(ip(1)-1)) is4=is4+1
|
|
||||||
enddo
|
|
||||||
nsync=is1+is2+is3+is4 !Number of correct hard sync symbols, 0-16
|
|
||||||
if(smax .lt. 0.7 .or. nsync .lt. 8) cycle
|
|
||||||
|
|
||||||
do nseq=1,3 !Try coherent sequences of 1, 2, and 4 symbols
|
|
||||||
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,NN-nsym+1,nsym !87+16=103 symbols.
|
|
||||||
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.2*NN) 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
|
|
||||||
|
|
||||||
bmetb(205:206)=bmeta(205:206)
|
|
||||||
bmetc(201:204)=bmetb(201:204)
|
|
||||||
bmetc(205:206)=bmeta(205:206)
|
|
||||||
|
|
||||||
call clockit('normaliz',0)
|
|
||||||
call normalizebmet(bmeta,2*NN)
|
|
||||||
call normalizebmet(bmetb,2*NN)
|
|
||||||
call normalizebmet(bmetc,2*NN)
|
|
||||||
call clockit('normaliz',1)
|
|
||||||
|
|
||||||
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( 67: 74).eq.(/0,1,0,0,1,1,1,0/))
|
|
||||||
ns3=count(hbits(133:140).eq.(/1,1,1,0,0,1,0,0/))
|
|
||||||
ns4=count(hbits(199:206).eq.(/1,0,1,1,0,0,0,1/))
|
|
||||||
nsync_qual=ns1+ns2+ns3+ns4
|
|
||||||
if(nsync_qual.lt. 20) cycle
|
|
||||||
|
|
||||||
scalefac=2.83
|
|
||||||
llra( 1: 58)=bmeta( 9: 66)
|
|
||||||
llra( 59:116)=bmeta( 75:132)
|
|
||||||
llra(117:174)=bmeta(141:198)
|
|
||||||
llra=scalefac*llra
|
|
||||||
llrb( 1: 58)=bmetb( 9: 66)
|
|
||||||
llrb( 59:116)=bmetb( 75:132)
|
|
||||||
llrb(117:174)=bmetb(141:198)
|
|
||||||
llrb=scalefac*llrb
|
|
||||||
llrc( 1: 58)=bmetc( 9: 66)
|
|
||||||
llrc( 59:116)=bmetc( 75:132)
|
|
||||||
llrc(117:174)=bmetc(141:198)
|
|
||||||
llrc=scalefac*llrc
|
|
||||||
|
|
||||||
apmag=maxval(abs(llra))*1.1
|
|
||||||
npasses=3+nappasses(nQSOProgress)
|
|
||||||
if(ncontest.ge.5) npasses=3 ! Don't support Fox and Hound
|
|
||||||
do ipass=1,npasses
|
|
||||||
if(ipass.eq.1) llr=llra
|
|
||||||
if(ipass.eq.2) llr=llrb
|
|
||||||
if(ipass.eq.3) llr=llrc
|
|
||||||
if(ipass.le.3) then
|
|
||||||
apmask=0
|
|
||||||
iaptype=0
|
|
||||||
endif
|
|
||||||
|
|
||||||
if(ipass .gt. 3) then
|
|
||||||
llrd=llrc
|
|
||||||
iaptype=naptypes(nQSOProgress,ipass-3)
|
|
||||||
|
|
||||||
! ncontest=0 : NONE
|
|
||||||
! 1 : NA_VHF
|
|
||||||
! 2 : EU_VHF
|
|
||||||
! 3 : FIELD DAY
|
|
||||||
! 4 : RTTY
|
|
||||||
! 5 : FOX
|
|
||||||
! 6 : HOUND
|
|
||||||
!
|
|
||||||
! Conditions that cause us to bail out of AP decoding
|
|
||||||
napwid=50
|
|
||||||
if(ncontest.le.4 .and. iaptype.ge.3 .and. (abs(f0-nfqso).gt.napwid) ) cycle
|
|
||||||
if(iaptype.ge.2 .and. apbits(1).gt.1) cycle ! No, or nonstandard, mycall
|
|
||||||
if(iaptype.ge.3 .and. apbits(30).gt.1) cycle ! No, or nonstandard, dxcall
|
|
||||||
|
|
||||||
if(iaptype.eq.1) then ! CQ or CQ TEST or CQ FD or CQ RU or CQ SCC
|
|
||||||
apmask=0
|
|
||||||
apmask(1:29)=1
|
|
||||||
llrd(1:29)=apmag*mcq(1:29)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if(iaptype.eq.2) then ! MyCall,???,???
|
|
||||||
apmask=0
|
|
||||||
if(ncontest.eq.0.or.ncontest.eq.1) then
|
|
||||||
apmask(1:29)=1
|
|
||||||
llrd(1:29)=apmag*apbits(1:29)
|
|
||||||
else if(ncontest.eq.2) then
|
|
||||||
apmask(1:28)=1
|
|
||||||
llrd(1:28)=apmag*apbits(1:28)
|
|
||||||
else if(ncontest.eq.3) then
|
|
||||||
apmask(1:28)=1
|
|
||||||
llrd(1:28)=apmag*apbits(1:28)
|
|
||||||
else if(ncontest.eq.4) then
|
|
||||||
apmask(2:29)=1
|
|
||||||
llrd(2:29)=apmag*apmy_ru(1:28)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if(iaptype.eq.3) then ! MyCall,DxCall,???
|
|
||||||
apmask=0
|
|
||||||
if(ncontest.eq.0.or.ncontest.eq.1.or.ncontest.eq.2) then
|
|
||||||
apmask(1:58)=1
|
|
||||||
llrd(1:58)=apmag*apbits(1:58)
|
|
||||||
else if(ncontest.eq.3) then ! Field Day
|
|
||||||
apmask(1:56)=1
|
|
||||||
llrd(1:28)=apmag*apbits(1:28)
|
|
||||||
llrd(29:56)=apmag*aphis_fd(1:28)
|
|
||||||
else if(ncontest.eq.4) then ! RTTY RU
|
|
||||||
apmask(2:57)=1
|
|
||||||
llrd(2:29)=apmag*apmy_ru(1:28)
|
|
||||||
llrd(30:57)=apmag*apbits(30:57)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
|
|
||||||
apmask=0
|
|
||||||
if(ncontest.le.4) then
|
|
||||||
apmask(1:91)=1 ! mycall, hiscall, RRR|73|RR73
|
|
||||||
if(iaptype.eq.6) llrd(1:91)=apmag*apbits(1:91)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
llr=llrd
|
|
||||||
endif
|
|
||||||
max_iterations=40
|
|
||||||
message77=0
|
|
||||||
call clockit('bpdecode',0)
|
|
||||||
call bpdecode174_91(llr,apmask,max_iterations,message77, &
|
|
||||||
cw,nharderror,niterations)
|
|
||||||
call clockit('bpdecode',1)
|
|
||||||
if(sum(message77).eq.0) cycle
|
|
||||||
if( nharderror.ge.0 ) then
|
|
||||||
message77=mod(message77+rvec,2) ! remove rvec scrambling
|
|
||||||
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(ibest.le.10 .and. message.eq.msg0) idupe=1 !Already decoded
|
|
||||||
if(idupe.eq.1) exit
|
|
||||||
ndecodes=ndecodes+1
|
|
||||||
decodes(ndecodes)=message
|
|
||||||
if(snr.gt.0.0) then
|
|
||||||
xsnr=10*log10(snr)-14.0
|
|
||||||
else
|
|
||||||
xsnr=-20.0
|
|
||||||
endif
|
|
||||||
nsnr=nint(max(-20.0,xsnr))
|
|
||||||
freq=f0
|
|
||||||
tsig=mod(tbuf + ibest/750.0,100.0)
|
|
||||||
|
|
||||||
write(line,1000) hhmmss,nsnr,tsig,nint(freq),message
|
|
||||||
1000 format(a6,i4,f5.1,i5,' + ',1x,a37)
|
|
||||||
l1=index(data_dir,char(0))-1
|
|
||||||
if(l1.ge.1) data_dir(l1+1:l1+1)="/"
|
|
||||||
fname=data_dir(1:l1+1)//'all_ft4.txt'
|
|
||||||
open(24,file=trim(fname),status='unknown',position='append')
|
|
||||||
write(24,1002) cdatetime0,nsnr,tsig,nint(freq),message, &
|
|
||||||
nharderror,nsync_qual,ipass,niterations,iaptype,nsync
|
|
||||||
if(hhmmss.eq.' ') write(*,1002) cdatetime0,nsnr, &
|
|
||||||
tsig,nint(freq),message,nharderror,nsync_qual,ipass, &
|
|
||||||
niterations,iaptype
|
|
||||||
1002 format(a17,i4,f5.1,i5,' Rx ',a37,6i4)
|
|
||||||
close(24)
|
|
||||||
linex(ndecodes)=line
|
|
||||||
if(ibest.ge.ibmax-15) msg0=message !Possible dupe candidate
|
|
||||||
exit
|
|
||||||
endif
|
|
||||||
enddo !Sequence estimation
|
|
||||||
enddo !Candidate list
|
|
||||||
call clockit('ft4_deco',1)
|
|
||||||
call clockit2(data_dir)
|
|
||||||
call clockit('ft4_deco',101)
|
|
||||||
return
|
|
||||||
|
|
||||||
entry get_ft4msg(idecode,line)
|
|
||||||
line=linex(idecode)
|
|
||||||
return
|
|
||||||
|
|
||||||
end subroutine ft4b
|
|
@ -1,83 +0,0 @@
|
|||||||
program ft4d
|
|
||||||
|
|
||||||
include 'ft4_params.f90'
|
|
||||||
character*8 arg
|
|
||||||
character*17 cdatetime
|
|
||||||
character*512 data_dir
|
|
||||||
character*12 mycall
|
|
||||||
character*12 hiscall
|
|
||||||
character*80 infile
|
|
||||||
character*61 line
|
|
||||||
character*4 cqstr
|
|
||||||
real*8 fMHz
|
|
||||||
integer ihdr(11)
|
|
||||||
integer*2 iwave(240000) !20*12000
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
nargs=iargc()
|
|
||||||
if(nargs.lt.1) then
|
|
||||||
print*,'Usage: ft4d [-a <data_dir>] [-f fMHz] [-n nQSOProgress] 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
|
|
||||||
nQSOProgress=0
|
|
||||||
if(arg(1:2).eq.'-n') then
|
|
||||||
call getarg(iarg+1,arg)
|
|
||||||
read(arg,*) nQSOProgress
|
|
||||||
iarg=iarg+2
|
|
||||||
endif
|
|
||||||
nfa=10
|
|
||||||
nfb=4990
|
|
||||||
ndecodes=0
|
|
||||||
nfqso=1500
|
|
||||||
mycall="K9AN"
|
|
||||||
hiscall="K1JT"
|
|
||||||
ncontest=4
|
|
||||||
cqstr="RU "
|
|
||||||
|
|
||||||
do ifile=iarg,nargs
|
|
||||||
call getarg(ifile,infile)
|
|
||||||
open(10,file=infile,status='old',access='stream')
|
|
||||||
read(10) ihdr
|
|
||||||
npts=min(ihdr(11)/2,180000)
|
|
||||||
read(10) iwave(1:npts)
|
|
||||||
close(10)
|
|
||||||
cdatetime=infile
|
|
||||||
j2=index(infile,'.wav')
|
|
||||||
if(j2.ge.14) cdatetime=infile(j2-13:j2)//'000'
|
|
||||||
istep=3456
|
|
||||||
nsteps=(npts-52800)/istep + 1
|
|
||||||
do n=1,nsteps
|
|
||||||
i0=(n-1)*istep + 1
|
|
||||||
tbuf=(i0-1)/12000.0
|
|
||||||
call ft4b(cdatetime,tbuf,nfa,nfb,nQSOProgress,ncontest, &
|
|
||||||
nfqso,iwave(i0),ndecodes,mycall,hiscall,cqstr,line,data_dir)
|
|
||||||
do idecode=1,ndecodes
|
|
||||||
call get_ft4msg(idecode,line)
|
|
||||||
write(*,'(a61)') line
|
|
||||||
enddo
|
|
||||||
enddo !steps
|
|
||||||
enddo !files
|
|
||||||
|
|
||||||
call four2a(xx,-1,1,-1,1) !Destroy FFTW plans to free their memory
|
|
||||||
|
|
||||||
999 end program ft4d
|
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ program ft4sim
|
|||||||
use packjt77
|
use packjt77
|
||||||
include 'ft4_params.f90' !Set various constants
|
include 'ft4_params.f90' !Set various constants
|
||||||
parameter (NWAVE=NN*NSPS)
|
parameter (NWAVE=NN*NSPS)
|
||||||
parameter (NZZ=18*3456) !62208
|
parameter (NZZ=21*3456) !72576
|
||||||
type(hdr) h !Header for .wav file
|
type(hdr) h !Header for .wav file
|
||||||
character arg*12,fname*17
|
character arg*12,fname*17
|
||||||
character msg37*37,msgsent37*37
|
character msg37*37,msgsent37*37
|
||||||
@ -51,19 +51,18 @@ program ft4sim
|
|||||||
hmod=1.0 !Modulation index (0.5 is MSK, 1.0 is FSK)
|
hmod=1.0 !Modulation index (0.5 is MSK, 1.0 is FSK)
|
||||||
tt=NSPS*dt !Duration of symbols (s)
|
tt=NSPS*dt !Duration of symbols (s)
|
||||||
baud=1.0/tt !Keying rate (baud)
|
baud=1.0/tt !Keying rate (baud)
|
||||||
txt=NZ*dt !Transmission length (s)
|
txt=NZ2*dt !Transmission length (s)
|
||||||
|
|
||||||
bandwidth_ratio=2500.0/(fs/2.0)
|
bandwidth_ratio=2500.0/(fs/2.0)
|
||||||
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
||||||
if(snrdb.gt.90.0) sig=1.0
|
if(snrdb.gt.90.0) sig=1.0
|
||||||
txt=NN*NSPS/12000.0
|
|
||||||
|
|
||||||
! Source-encode, then get itone()
|
! Source-encode, then get itone()
|
||||||
i3=-1
|
i3=-1
|
||||||
n3=-1
|
n3=-1
|
||||||
call pack77(msg37,i3,n3,c77)
|
call pack77(msg37,i3,n3,c77)
|
||||||
read(c77,'(77i1)') msgbits
|
read(c77,'(77i1)') msgbits
|
||||||
call genft4(msg37,0,msgsent37,itone)
|
call genft4(msg37,0,msgsent37,msgbits,itone)
|
||||||
write(*,*)
|
write(*,*)
|
||||||
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
||||||
write(*,1000) f0,xdt,txt,snrdb
|
write(*,1000) f0,xdt,txt,snrdb
|
||||||
@ -111,8 +110,10 @@ program ft4sim
|
|||||||
c0((NN+1)*NSPS:(NN+2)*NSPS-1)=c0((NN+1)*NSPS:(NN+2)*NSPS-1)*(1.0+cos(twopi*(/(i,i=0,NSPS-1)/)/(2.0*NSPS) ))/2.0
|
c0((NN+1)*NSPS:(NN+2)*NSPS-1)=c0((NN+1)*NSPS:(NN+2)*NSPS-1)*(1.0+cos(twopi*(/(i,i=0,NSPS-1)/)/(2.0*NSPS) ))/2.0
|
||||||
c0((NN+2)*NSPS:)=0.
|
c0((NN+2)*NSPS:)=0.
|
||||||
|
|
||||||
k=nint((xdt+0.5)/dt)
|
k=nint((xdt+0.5)/dt)-NSPS
|
||||||
c0=cshift(c0,-k)
|
c0=cshift(c0,-k)
|
||||||
|
if(k.gt.0) c0(0:k-1)=0.0
|
||||||
|
if(k.lt.0) c0(NZZ+k:NZZ-1)=0.0
|
||||||
|
|
||||||
do ifile=1,nfiles
|
do ifile=1,nfiles
|
||||||
c=c0
|
c=c0
|
||||||
|
@ -6,17 +6,17 @@ program ft4sim_mult
|
|||||||
use packjt77
|
use packjt77
|
||||||
include 'ft4_params.f90' !FT4 protocol constants
|
include 'ft4_params.f90' !FT4 protocol constants
|
||||||
parameter (NWAVE=NN*NSPS)
|
parameter (NWAVE=NN*NSPS)
|
||||||
parameter (NZZ=65760) !Length of .wav file (4.48+1.0)*12000
|
parameter (NZZ=72576) !Length of .wav file (21*3456)
|
||||||
type(hdr) h !Header for .wav file
|
type(hdr) h !Header for .wav file
|
||||||
character arg*12,fname*17,cjunk*4
|
character arg*12,fname*17,cjunk*4
|
||||||
character msg37*37,msgsent37*37,c77*77
|
character msg37*37,msgsent37*37,c77*77
|
||||||
|
complex cwave0((NN+2)*NSPS)
|
||||||
real wave0((NN+2)*NSPS)
|
real wave0((NN+2)*NSPS)
|
||||||
real wave(NZZ)
|
real wave(NZZ)
|
||||||
real tmp(NZZ)
|
real tmp(NZZ)
|
||||||
integer itone(NN)
|
integer itone(NN)
|
||||||
|
integer*1 msgbits(77)
|
||||||
integer*2 iwave(NZZ) !Generated full-length waveform
|
integer*2 iwave(NZZ) !Generated full-length waveform
|
||||||
integer icos4(4)
|
|
||||||
data icos4/0,1,3,2/
|
|
||||||
|
|
||||||
! Get command-line argument(s)
|
! Get command-line argument(s)
|
||||||
nargs=iargc()
|
nargs=iargc()
|
||||||
@ -53,17 +53,18 @@ program ft4sim_mult
|
|||||||
read(10,1003,end=100) cjunk,isnr,xdt0,ifreq,msg37
|
read(10,1003,end=100) cjunk,isnr,xdt0,ifreq,msg37
|
||||||
1003 format(a4,30x,i3,f5.1,i5,1x,a37)
|
1003 format(a4,30x,i3,f5.1,i5,1x,a37)
|
||||||
if(cjunk.eq.'File') go to 100
|
if(cjunk.eq.'File') go to 100
|
||||||
if(isnr.lt.-16) isnr=-16
|
if(isnr.lt.-17) isnr=-17
|
||||||
f0=ifreq*93.75/50.0
|
f0=ifreq*960.0/576.0
|
||||||
call random_number(r)
|
call random_number(r)
|
||||||
xdt=r-0.5
|
xdt=r-0.5
|
||||||
! Source-encode, then get itone()
|
! Source-encode, then get itone()
|
||||||
i3=-1
|
i3=-1
|
||||||
n3=-1
|
n3=-1
|
||||||
call pack77(msg37,i3,n3,c77)
|
call pack77(msg37,i3,n3,c77)
|
||||||
call genft4(msg37,0,msgsent37,itone)
|
call genft4(msg37,0,msgsent37,msgbits,itone)
|
||||||
nwave0=(NN+2)*NSPS
|
nwave0=(NN+2)*NSPS
|
||||||
call gen_ft4wave(itone,NN,NSPS,12000.0,f0,wave0,nwave0)
|
icmplx=0
|
||||||
|
call gen_ft4wave(itone,NN,NSPS,12000.0,f0,cwave0,wave0,icmplx,nwave0)
|
||||||
|
|
||||||
k0=nint((xdt+0.5)/dt)
|
k0=nint((xdt+0.5)/dt)
|
||||||
if(k0.lt.1) k0=1
|
if(k0.lt.1) k0=1
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
|
subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
||||||
|
|
||||||
real wave(nwave)
|
real wave(nwave)
|
||||||
real pulse(6144) !512*4*3
|
complex cwave(nwave)
|
||||||
real dphi(0:240000-1)
|
real pulse(6912) !576*4*3
|
||||||
|
real dphi(0:250000-1)
|
||||||
integer itone(nsym)
|
integer itone(nsym)
|
||||||
logical first
|
logical first
|
||||||
data first/.true./
|
data first/.true./
|
||||||
@ -12,7 +13,7 @@ subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
|
|||||||
twopi=8.0*atan(1.0)
|
twopi=8.0*atan(1.0)
|
||||||
dt=1.0/fsample
|
dt=1.0/fsample
|
||||||
hmod=1.0
|
hmod=1.0
|
||||||
! Compute the frequency-smoothing pulse
|
! Compute the smoothed frequency-deviation pulse
|
||||||
do i=1,3*nsps
|
do i=1,3*nsps
|
||||||
tt=(i-1.5*nsps)/real(nsps)
|
tt=(i-1.5*nsps)/real(nsps)
|
||||||
pulse(i)=gfsk_pulse(1.0,tt)
|
pulse(i)=gfsk_pulse(1.0,tt)
|
||||||
@ -34,19 +35,32 @@ subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
|
|||||||
phi=0.0
|
phi=0.0
|
||||||
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
||||||
wave=0.
|
wave=0.
|
||||||
|
if(icmplx.eq.1) cwave=0.
|
||||||
k=0
|
k=0
|
||||||
do j=0,nwave-1
|
do j=0,nwave-1
|
||||||
k=k+1
|
k=k+1
|
||||||
|
if(icmplx.eq.0) then
|
||||||
wave(k)=sin(phi)
|
wave(k)=sin(phi)
|
||||||
|
else
|
||||||
|
cwave(k)=cmplx(cos(phi),sin(phi))
|
||||||
|
endif
|
||||||
phi=mod(phi+dphi(j),twopi)
|
phi=mod(phi+dphi(j),twopi)
|
||||||
enddo
|
enddo
|
||||||
|
|
||||||
! Compute the ramp-up and ramp-down symbols
|
! Compute the ramp-up and ramp-down symbols
|
||||||
|
if(icmplx.eq.0) then
|
||||||
wave(1:nsps)=wave(1:nsps) * &
|
wave(1:nsps)=wave(1:nsps) * &
|
||||||
(1.0-cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
(1.0-cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
||||||
k1=(nsym+1)*nsps+1
|
k1=(nsym+1)*nsps+1
|
||||||
wave(k1:k1+nsps-1)=wave(k1:k1+nsps-1) * &
|
wave(k1:k1+nsps-1)=wave(k1:k1+nsps-1) * &
|
||||||
(1.0+cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
(1.0+cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
||||||
|
else
|
||||||
|
cwave(1:nsps)=cwave(1:nsps) * &
|
||||||
|
(1.0-cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
||||||
|
k1=(nsym+1)*nsps+1
|
||||||
|
cwave(k1:k1+nsps-1)=cwave(k1:k1+nsps-1) * &
|
||||||
|
(1.0+cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
||||||
|
endif
|
||||||
|
|
||||||
return
|
return
|
||||||
end subroutine gen_ft4wave
|
end subroutine gen_ft4wave
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
subroutine genft4(msg0,ichk,msgsent,i4tone)
|
subroutine genft4(msg0,ichk,msgsent,msgbits,i4tone)
|
||||||
|
|
||||||
! Encode an FT4 message
|
! Encode an FT4 message
|
||||||
! Input:
|
! Input:
|
||||||
@ -11,7 +11,7 @@ subroutine genft4(msg0,ichk,msgsent,i4tone)
|
|||||||
! s16 + 87symbols + 2 ramp up/down = 105 total channel symbols
|
! s16 + 87symbols + 2 ramp up/down = 105 total channel symbols
|
||||||
! r1 + s4 + d29 + s4 + d29 + s4 + d29 + s4 + r1
|
! r1 + s4 + d29 + s4 + d29 + s4 + d29 + s4 + r1
|
||||||
|
|
||||||
! Message duration: TxT = 105*512/12000 = 4.48 s
|
! Message duration: TxT = 105*576/12000 = 5.04 s
|
||||||
|
|
||||||
! use iso_c_binding, only: c_loc,c_size_t
|
! use iso_c_binding, only: c_loc,c_size_t
|
||||||
|
|
||||||
@ -52,8 +52,16 @@ subroutine genft4(msg0,ichk,msgsent,i4tone)
|
|||||||
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
|
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
|
||||||
|
|
||||||
if(ichk.eq.1) go to 999
|
if(ichk.eq.1) go to 999
|
||||||
read(c77,"(77i1)") msgbits
|
read(c77,'(77i1)',err=1) msgbits
|
||||||
msgbits=mod(msgbits+rvec,2)
|
if(unpk77_success) go to 2
|
||||||
|
1 msgbits=0
|
||||||
|
itone=0
|
||||||
|
msgsent='*** bad message *** '
|
||||||
|
go to 999
|
||||||
|
|
||||||
|
entry get_ft4_tones_from_77bits(msgbits,i4tone)
|
||||||
|
|
||||||
|
2 msgbits=mod(msgbits+rvec,2)
|
||||||
call encode174_91(msgbits,codeword)
|
call encode174_91(msgbits,codeword)
|
||||||
|
|
||||||
! Grayscale mapping:
|
! Grayscale mapping:
|
||||||
|
114
lib/ft4/get_ft4_bitmetrics.f90
Normal file
114
lib/ft4/get_ft4_bitmetrics.f90
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
subroutine get_ft4_bitmetrics(cd,bitmetrics,badsync)
|
||||||
|
|
||||||
|
include 'ft4_params.f90'
|
||||||
|
parameter (NSS=NSPS/NDOWN,NDMAX=NMAX/NDOWN)
|
||||||
|
complex cd(0:NN*NSS-1)
|
||||||
|
complex cs(0:3,NN)
|
||||||
|
complex csymb(NSS)
|
||||||
|
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
||||||
|
integer graymap(0:3)
|
||||||
|
integer ip(1)
|
||||||
|
logical one(0:255,0:7) ! 256 4-symbol sequences, 8 bits
|
||||||
|
logical first
|
||||||
|
logical badsync
|
||||||
|
real bitmetrics(2*NN,3)
|
||||||
|
real s2(0:255)
|
||||||
|
real s4(0:3,NN)
|
||||||
|
|
||||||
|
data icos4a/0,1,3,2/
|
||||||
|
data icos4b/1,0,2,3/
|
||||||
|
data icos4c/2,3,1,0/
|
||||||
|
data icos4d/3,2,0,1/
|
||||||
|
data graymap/0,1,3,2/
|
||||||
|
data first/.true./
|
||||||
|
save first,one
|
||||||
|
|
||||||
|
if(first) then
|
||||||
|
one=.false.
|
||||||
|
do i=0,255
|
||||||
|
do j=0,7
|
||||||
|
if(iand(i,2**j).ne.0) one(i,j)=.true.
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
first=.false.
|
||||||
|
endif
|
||||||
|
|
||||||
|
do k=1,NN
|
||||||
|
i1=(k-1)*NSS
|
||||||
|
csymb=cd(i1:i1+NSS-1)
|
||||||
|
call four2a(csymb,NSS,1,-1,1)
|
||||||
|
cs(0:3,k)=csymb(1:4)
|
||||||
|
s4(0:3,k)=abs(csymb(1:4))
|
||||||
|
enddo
|
||||||
|
|
||||||
|
! Sync quality check
|
||||||
|
is1=0
|
||||||
|
is2=0
|
||||||
|
is3=0
|
||||||
|
is4=0
|
||||||
|
badsync=.false.
|
||||||
|
do k=1,4
|
||||||
|
ip=maxloc(s4(:,k))
|
||||||
|
if(icos4a(k-1).eq.(ip(1)-1)) is1=is1+1
|
||||||
|
ip=maxloc(s4(:,k+33))
|
||||||
|
if(icos4b(k-1).eq.(ip(1)-1)) is2=is2+1
|
||||||
|
ip=maxloc(s4(:,k+66))
|
||||||
|
if(icos4c(k-1).eq.(ip(1)-1)) is3=is3+1
|
||||||
|
ip=maxloc(s4(:,k+99))
|
||||||
|
if(icos4d(k-1).eq.(ip(1)-1)) is4=is4+1
|
||||||
|
enddo
|
||||||
|
nsync=is1+is2+is3+is4 !Number of correct hard sync symbols, 0-16
|
||||||
|
if(nsync .lt. 8) then
|
||||||
|
badsync=.true.
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
do nseq=1,3 !Try coherent sequences of 1, 2, and 4 symbols
|
||||||
|
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,NN-nsym+1,nsym !87+16=103 symbols.
|
||||||
|
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.2*NN) cycle
|
||||||
|
bitmetrics(ipt+ib,nseq)=bm
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
enddo
|
||||||
|
|
||||||
|
bitmetrics(205:206,2)=bitmetrics(205:206,1)
|
||||||
|
bitmetrics(201:204,3)=bitmetrics(201:204,2)
|
||||||
|
bitmetrics(205:206,3)=bitmetrics(205:206,1)
|
||||||
|
|
||||||
|
call normalizebmet(bitmetrics(:,1),2*NN)
|
||||||
|
call normalizebmet(bitmetrics(:,2),2*NN)
|
||||||
|
call normalizebmet(bitmetrics(:,3),2*NN)
|
||||||
|
return
|
||||||
|
|
||||||
|
end subroutine get_ft4_bitmetrics
|
@ -1,4 +1,4 @@
|
|||||||
subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
subroutine getcandidates4(dd,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
||||||
ncand,sbase)
|
ncand,sbase)
|
||||||
|
|
||||||
include 'ft4_params.f90'
|
include 'ft4_params.f90'
|
||||||
@ -8,9 +8,8 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||||||
real x(NFFT1)
|
real x(NFFT1)
|
||||||
real window(NFFT1)
|
real window(NFFT1)
|
||||||
complex cx(0:NH1)
|
complex cx(0:NH1)
|
||||||
real candidate(3,maxcand)
|
real candidate(2,maxcand),candidatet(2,maxcand)
|
||||||
integer*2 id(NMAX)
|
real dd(NMAX)
|
||||||
integer indx(NH1)
|
|
||||||
integer ipk(1)
|
integer ipk(1)
|
||||||
equivalence (x,cx)
|
equivalence (x,cx)
|
||||||
logical first
|
logical first
|
||||||
@ -26,42 +25,35 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||||||
|
|
||||||
! Compute symbol spectra, stepping by NSTEP steps.
|
! Compute symbol spectra, stepping by NSTEP steps.
|
||||||
savg=0.
|
savg=0.
|
||||||
tstep=NSTEP/12000.0
|
|
||||||
df=12000.0/NFFT1
|
df=12000.0/NFFT1
|
||||||
fac=1.0/300.0
|
fac=1.0/300.0
|
||||||
do j=1,NHSYM
|
do j=1,NHSYM
|
||||||
ia=(j-1)*NSTEP + 1
|
ia=(j-1)*NSTEP + 1
|
||||||
ib=ia+NFFT1-1
|
ib=ia+NFFT1-1
|
||||||
if(ib.gt.NMAX) exit
|
if(ib.gt.NMAX) exit
|
||||||
x=fac*id(ia:ib)*window
|
x=fac*dd(ia:ib)*window
|
||||||
call four2a(x,NFFT1,1,-1,0) !r2c FFT
|
call four2a(x,NFFT1,1,-1,0) !r2c FFT
|
||||||
do i=1,NH1
|
do i=1,NH1
|
||||||
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
|
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
|
||||||
enddo
|
enddo
|
||||||
savg=savg + s(1:NH1,j) !Average spectrum
|
savg=savg + s(1:NH1,j) !Average spectrum
|
||||||
enddo
|
enddo
|
||||||
|
savg=savg/NHSYM
|
||||||
savsm=0.
|
savsm=0.
|
||||||
do i=8,NH1-7
|
do i=8,NH1-7
|
||||||
savsm(i)=sum(savg(i-7:i+7))/15.
|
savsm(i)=sum(savg(i-7:i+7))/15.
|
||||||
enddo
|
enddo
|
||||||
nfa=fa/df
|
|
||||||
if(nfa.lt.1) nfa=1
|
|
||||||
nfb=fb/df
|
|
||||||
if(nfb.gt.nint(5000.0/df)) nfb=nint(5000.0/df)
|
|
||||||
n300=300/df
|
|
||||||
n2500=2500/df
|
|
||||||
! np=nfb-nfa+1
|
|
||||||
np=n2500-n300+1
|
|
||||||
indx=0
|
|
||||||
call indexx(savsm(n300:n2500),np,indx)
|
|
||||||
xn=savsm(n300+indx(nint(0.3*np)))
|
|
||||||
ncand=0
|
|
||||||
if(xn.le.1.e-8) return
|
|
||||||
savsm=savsm/xn
|
|
||||||
! call ft4_baseline(savg,nfa,nfb,sbase)
|
|
||||||
! savsm=savsm/sbase
|
|
||||||
|
|
||||||
f_offset = -1.5*12000/512
|
nfa=fa/df
|
||||||
|
if(nfa.lt.nint(200.0/df)) nfa=nint(200.0/df)
|
||||||
|
nfb=fb/df
|
||||||
|
if(nfb.gt.nint(4910.0/df)) nfb=nint(4910.0/df)
|
||||||
|
call ft4_baseline(savg,nfa,nfb,sbase)
|
||||||
|
if(any(sbase(nfa:nfb).le.0)) return
|
||||||
|
savsm(nfa:nfb)=savsm(nfa:nfb)/sbase(nfa:nfb)
|
||||||
|
f_offset = -1.5*12000.0/NSPS
|
||||||
|
ncand=0
|
||||||
|
candidatet=0
|
||||||
do i=nfa+1,nfb-1
|
do i=nfa+1,nfb-1
|
||||||
if(savsm(i).ge.savsm(i-1) .and. savsm(i).ge.savsm(i+1) .and. &
|
if(savsm(i).ge.savsm(i-1) .and. savsm(i).ge.savsm(i+1) .and. &
|
||||||
savsm(i).ge.syncmin) then
|
savsm(i).ge.syncmin) then
|
||||||
@ -69,18 +61,26 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||||||
del=0.
|
del=0.
|
||||||
if(den.ne.0.0) del=0.5*(savsm(i-1)-savsm(i+1))/den
|
if(den.ne.0.0) del=0.5*(savsm(i-1)-savsm(i+1))/den
|
||||||
fpeak=(i+del)*df+f_offset
|
fpeak=(i+del)*df+f_offset
|
||||||
|
if(fpeak.lt.200.0 .or. fpeak.gt.4910.0) cycle
|
||||||
speak=savsm(i) - 0.25*(savsm(i-1)-savsm(i+1))*del
|
speak=savsm(i) - 0.25*(savsm(i-1)-savsm(i+1))*del
|
||||||
ncand=ncand+1
|
ncand=ncand+1
|
||||||
if(ncand.gt.maxcand) then
|
candidatet(1,ncand)=fpeak
|
||||||
ncand=maxcand
|
candidatet(2,ncand)=speak
|
||||||
exit
|
|
||||||
endif
|
|
||||||
candidate(1,ncand)=fpeak
|
|
||||||
candidate(2,ncand)=-99.99
|
|
||||||
candidate(3,ncand)=speak
|
|
||||||
if(ncand.eq.maxcand) exit
|
if(ncand.eq.maxcand) exit
|
||||||
endif
|
endif
|
||||||
enddo
|
enddo
|
||||||
|
candidate=0
|
||||||
|
nq=count(abs(candidatet(1,1:ncand)-nfqso).le.20.0)
|
||||||
|
n1=1
|
||||||
|
n2=nq+1
|
||||||
|
do i=1,ncand
|
||||||
|
if(abs(candidatet(1,i)-nfqso).le.20.0) then
|
||||||
|
candidate(1:2,n1)=candidatet(1:2,i)
|
||||||
|
n1=n1+1
|
||||||
|
else
|
||||||
|
candidate(1:2,n2)=candidatet(1:2,i)
|
||||||
|
n2=n2+1
|
||||||
|
endif
|
||||||
|
enddo
|
||||||
return
|
return
|
||||||
end subroutine getcandidates4
|
end subroutine getcandidates4
|
||||||
|
66
lib/ft4/subtractft4.f90
Normal file
66
lib/ft4/subtractft4.f90
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
subroutine subtractft4(dd,itone,f0,dt)
|
||||||
|
|
||||||
|
! Subtract an ft4 signal
|
||||||
|
!
|
||||||
|
! Measured signal : dd(t) = a(t)cos(2*pi*f0*t+theta(t))
|
||||||
|
! Reference signal : cref(t) = exp( j*(2*pi*f0*t+phi(t)) )
|
||||||
|
! Complex amp : cfilt(t) = LPF[ dd(t)*CONJG(cref(t)) ]
|
||||||
|
! Subtract : dd(t) = dd(t) - 2*REAL{cref*cfilt}
|
||||||
|
|
||||||
|
use timer_module, only: timer
|
||||||
|
|
||||||
|
parameter (NMAX=21*3456,NSPS=576,NFFT=NMAX,NFILT=1400)
|
||||||
|
parameter (NFRAME=(103+2)*NSPS)
|
||||||
|
real*4 dd(NMAX), window(-NFILT/2:NFILT/2), xjunk
|
||||||
|
complex cref,camp,cfilt,cw
|
||||||
|
integer itone(103)
|
||||||
|
logical first
|
||||||
|
data first/.true./
|
||||||
|
common/heap8/cref(NFRAME),camp(NMAX),cfilt(NMAX),cw(NMAX),xjunk(NFRAME)
|
||||||
|
save first
|
||||||
|
|
||||||
|
nstart=dt*12000+1-NSPS
|
||||||
|
nsym=103
|
||||||
|
fs=12000.0
|
||||||
|
icmplx=1
|
||||||
|
bt=1.0
|
||||||
|
nss=NSPS
|
||||||
|
call gen_ft4wave(itone,nsym,nss,fs,f0,cref,xjunk,icmplx,NFRAME)
|
||||||
|
camp=0.
|
||||||
|
do i=1,nframe
|
||||||
|
id=nstart-1+i
|
||||||
|
if(id.ge.1.and.id.le.NMAX) camp(i)=dd(id)*conjg(cref(i))
|
||||||
|
enddo
|
||||||
|
|
||||||
|
if(first) then
|
||||||
|
! Create and normalize the filter
|
||||||
|
pi=4.0*atan(1.0)
|
||||||
|
fac=1.0/float(nfft)
|
||||||
|
sum=0.0
|
||||||
|
do j=-NFILT/2,NFILT/2
|
||||||
|
window(j)=cos(pi*j/NFILT)**2
|
||||||
|
sum=sum+window(j)
|
||||||
|
enddo
|
||||||
|
cw=0.
|
||||||
|
cw(1:NFILT+1)=window/sum
|
||||||
|
cw=cshift(cw,NFILT/2+1)
|
||||||
|
call four2a(cw,nfft,1,-1,1)
|
||||||
|
cw=cw*fac
|
||||||
|
first=.false.
|
||||||
|
endif
|
||||||
|
|
||||||
|
cfilt=0.0
|
||||||
|
cfilt(1:nframe)=camp(1:nframe)
|
||||||
|
call four2a(cfilt,nfft,1,-1,1)
|
||||||
|
cfilt(1:nfft)=cfilt(1:nfft)*cw(1:nfft)
|
||||||
|
call four2a(cfilt,nfft,1,1,1)
|
||||||
|
|
||||||
|
! Subtract the reconstructed signal
|
||||||
|
do i=1,nframe
|
||||||
|
j=nstart+i-1
|
||||||
|
if(j.ge.1 .and. j.le.NMAX) dd(j)=dd(j)-2*REAL(cfilt(i)*cref(i))
|
||||||
|
enddo
|
||||||
|
|
||||||
|
return
|
||||||
|
end subroutine subtractft4
|
||||||
|
|
@ -9,7 +9,6 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||||||
complex csync2(2*NSS)
|
complex csync2(2*NSS)
|
||||||
complex ctwk(2*NSS)
|
complex ctwk(2*NSS)
|
||||||
complex z1,z2,z3,z4
|
complex z1,z2,z3,z4
|
||||||
complex zz1,zz2,zz3,zz4
|
|
||||||
logical first
|
logical first
|
||||||
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
||||||
data icos4a/0,1,3,2/
|
data icos4a/0,1,3,2/
|
||||||
@ -19,7 +18,7 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||||||
data first/.true./
|
data first/.true./
|
||||||
save first,twopi,csynca,csyncb,csyncc,csyncd,fac
|
save first,twopi,csynca,csyncb,csyncc,csyncd,fac
|
||||||
|
|
||||||
p(z1)=real(z1*fac)**2 + aimag(z1*fac)**2 !Statement function for power
|
p(z1)=(real(z1*fac)**2 + aimag(z1*fac)**2)**0.5 !Statement function for power
|
||||||
|
|
||||||
if( first ) then
|
if( first ) then
|
||||||
twopi=8.0*atan(1.0)
|
twopi=8.0*atan(1.0)
|
||||||
@ -60,7 +59,17 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||||||
z4=0.
|
z4=0.
|
||||||
|
|
||||||
if(itwk.eq.1) csync2=ctwk*csynca !Tweak the frequency
|
if(itwk.eq.1) csync2=ctwk*csynca !Tweak the frequency
|
||||||
if(i1.ge.0 .and. i1+4*NSS-1.le.NP-1) z1=sum(cd0(i1:i1+4*NSS-1:2)*conjg(csync2))
|
z1=0.
|
||||||
|
if(i1.ge.0 .and. i1+4*NSS-1.le.NP-1) then
|
||||||
|
z1=sum(cd0(i1:i1+4*NSS-1:2)*conjg(csync2))
|
||||||
|
elseif( i1.lt.0 ) then
|
||||||
|
npts=(i1+4*NSS-1)/2
|
||||||
|
if(npts.le.16) then
|
||||||
|
z1=0.
|
||||||
|
else
|
||||||
|
z1=sum(cd0(0:i1+4*NSS-1:2)*conjg(csync2(2*NSS-npts:)))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
if(itwk.eq.1) csync2=ctwk*csyncb !Tweak the frequency
|
if(itwk.eq.1) csync2=ctwk*csyncb !Tweak the frequency
|
||||||
if(i2.ge.0 .and. i2+4*NSS-1.le.NP-1) z2=sum(cd0(i2:i2+4*NSS-1:2)*conjg(csync2))
|
if(i2.ge.0 .and. i2+4*NSS-1.le.NP-1) z2=sum(cd0(i2:i2+4*NSS-1:2)*conjg(csync2))
|
||||||
@ -69,7 +78,17 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||||||
if(i3.ge.0 .and. i3+4*NSS-1.le.NP-1) z3=sum(cd0(i3:i3+4*NSS-1:2)*conjg(csync2))
|
if(i3.ge.0 .and. i3+4*NSS-1.le.NP-1) z3=sum(cd0(i3:i3+4*NSS-1:2)*conjg(csync2))
|
||||||
|
|
||||||
if(itwk.eq.1) csync2=ctwk*csyncd !Tweak the frequency
|
if(itwk.eq.1) csync2=ctwk*csyncd !Tweak the frequency
|
||||||
if(i4.ge.0 .and. i4+4*NSS-1.le.NP-1) z4=sum(cd0(i4:i4+4*NSS-1:2)*conjg(csync2))
|
z4=0.
|
||||||
|
if(i4.ge.0 .and. i4+4*NSS-1.le.NP-1) then
|
||||||
|
z4=sum(cd0(i4:i4+4*NSS-1:2)*conjg(csync2))
|
||||||
|
elseif( i4+4*NSS-1.gt.NP-1 ) then
|
||||||
|
npts=(NP-1-i4+1)/2
|
||||||
|
if(npts.le.16) then
|
||||||
|
z4=0.
|
||||||
|
else
|
||||||
|
z4=sum(cd0(i4:i4+2*npts-1:2)*conjg(csync2(1:npts)))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
sync = p(z1) + p(z2) + p(z3) + p(z4)
|
sync = p(z1) + p(z2) + p(z3) + p(z4)
|
||||||
|
|
||||||
|
@ -1,145 +0,0 @@
|
|||||||
subroutine syncft4(iwave,nfa,nfb,syncmin,nfqso,maxcand,s,candidate, &
|
|
||||||
ncand,sbase)
|
|
||||||
|
|
||||||
include 'ft4_params.f90'
|
|
||||||
! Search over +/- 2.5s relative to 0.5s TX start time.
|
|
||||||
parameter (JZ=20)
|
|
||||||
complex cx(0:NH1)
|
|
||||||
real s(NH1,NHSYM)
|
|
||||||
real savg(NH1)
|
|
||||||
real sbase(NH1)
|
|
||||||
real x(NFFT1)
|
|
||||||
real sync2d(NH1,-JZ:JZ)
|
|
||||||
real red(NH1)
|
|
||||||
real candidate0(3,maxcand)
|
|
||||||
real candidate(3,maxcand)
|
|
||||||
real dd(NMAX)
|
|
||||||
integer jpeak(NH1)
|
|
||||||
integer indx(NH1)
|
|
||||||
integer ii(1)
|
|
||||||
integer*2 iwave(NMAX)
|
|
||||||
integer icos4(0:3)
|
|
||||||
data icos4/0,1,3,2/ !Costas 4x4 tone pattern
|
|
||||||
equivalence (x,cx)
|
|
||||||
|
|
||||||
dd=iwave/1e3
|
|
||||||
! Compute symbol spectra, stepping by NSTEP steps.
|
|
||||||
savg=0.
|
|
||||||
tstep=NSTEP/12000.0
|
|
||||||
df=12000.0/NFFT1
|
|
||||||
fac=1.0/300.0
|
|
||||||
do j=1,NHSYM
|
|
||||||
ia=(j-1)*NSTEP + 1
|
|
||||||
ib=ia+NSPS-1
|
|
||||||
x(1:NSPS)=fac*dd(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
|
|
||||||
|
|
||||||
call baseline(savg,nfa,nfb,sbase)
|
|
||||||
|
|
||||||
ia=max(1,nint(nfa/df))
|
|
||||||
ib=nint(nfb/df)
|
|
||||||
nssy=NSPS/NSTEP ! # steps per symbol
|
|
||||||
nfos=NFFT1/NSPS ! # frequency bin oversampling factor
|
|
||||||
jstrt=0.25/tstep
|
|
||||||
candidate0=0.
|
|
||||||
k=0
|
|
||||||
|
|
||||||
do i=ia,ib
|
|
||||||
do j=-JZ,+JZ
|
|
||||||
ta=0.
|
|
||||||
tb=0.
|
|
||||||
tc=0.
|
|
||||||
t0a=0.
|
|
||||||
t0b=0.
|
|
||||||
t0c=0.
|
|
||||||
do n=0,3
|
|
||||||
m=j+jstrt+nssy*n
|
|
||||||
if(m.ge.1.and.m.le.NHSYM) then
|
|
||||||
ta=ta + s(i+nfos*icos4(n),m)
|
|
||||||
t0a=t0a + sum(s(i:i+nfos*3:nfos,m))
|
|
||||||
endif
|
|
||||||
tb=tb + s(i+nfos*icos4(n),m+nssy*36)
|
|
||||||
t0b=t0b + sum(s(i:i+nfos*3:nfos,m+nssy*36))
|
|
||||||
if(m+nssy*72.le.NHSYM) then
|
|
||||||
tc=tc + s(i+nfos*icos4(n),m+nssy*72)
|
|
||||||
t0c=t0c + sum(s(i:i+nfos*3:nfos,m+nssy*72))
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
t=ta+tb+tc
|
|
||||||
t0=t0a+t0b+t0c
|
|
||||||
t0=(t0-t)/3.0
|
|
||||||
sync_abc=t/t0
|
|
||||||
t=tb+tc
|
|
||||||
t0=t0b+t0c
|
|
||||||
t0=(t0-t)/3.0
|
|
||||||
sync_bc=t/t0
|
|
||||||
sync2d(i,j)=max(sync_abc,sync_bc)
|
|
||||||
enddo
|
|
||||||
enddo
|
|
||||||
|
|
||||||
red=0.
|
|
||||||
do i=ia,ib
|
|
||||||
ii=maxloc(sync2d(i,-JZ:JZ)) - 1 - JZ
|
|
||||||
j0=ii(1)
|
|
||||||
jpeak(i)=j0
|
|
||||||
red(i)=sync2d(i,j0)
|
|
||||||
enddo
|
|
||||||
iz=ib-ia+1
|
|
||||||
call indexx(red(ia:ib),iz,indx)
|
|
||||||
ibase=indx(nint(0.40*iz)) - 1 + ia
|
|
||||||
if(ibase.lt.1) ibase=1
|
|
||||||
if(ibase.gt.nh1) ibase=nh1
|
|
||||||
base=red(ibase)
|
|
||||||
red=red/base
|
|
||||||
do i=1,min(maxcand,iz)
|
|
||||||
n=ia + indx(iz+1-i) - 1
|
|
||||||
if(red(n).lt.syncmin.or.isnan(red(n)).or.k.eq.maxcand) exit
|
|
||||||
k=k+1
|
|
||||||
! candidate0(1,k)=n*df+37.5*1.5
|
|
||||||
candidate0(1,k)=n*df
|
|
||||||
candidate0(2,k)=(jpeak(n)-1)*tstep
|
|
||||||
candidate0(3,k)=red(n)
|
|
||||||
enddo
|
|
||||||
ncand=k
|
|
||||||
|
|
||||||
! Put nfqso at top of list, and save only the best of near-dupe freqs.
|
|
||||||
do i=1,ncand
|
|
||||||
if(abs(candidate0(1,i)-nfqso).lt.10.0) candidate0(1,i)=-candidate0(1,i)
|
|
||||||
if(i.ge.2) then
|
|
||||||
do j=1,i-1
|
|
||||||
fdiff=abs(candidate0(1,i))-abs(candidate0(1,j))
|
|
||||||
if(abs(fdiff).lt.4.0) then
|
|
||||||
if(candidate0(3,i).ge.candidate0(3,j)) candidate0(3,j)=0.
|
|
||||||
if(candidate0(3,i).lt.candidate0(3,j)) candidate0(3,i)=0.
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
|
|
||||||
fac=20.0/maxval(s)
|
|
||||||
s=fac*s
|
|
||||||
|
|
||||||
! Sort by sync
|
|
||||||
! call indexx(candidate0(3,1:ncand),ncand,indx)
|
|
||||||
! Sort by frequency
|
|
||||||
call indexx(candidate0(1,1:ncand),ncand,indx)
|
|
||||||
k=1
|
|
||||||
! do i=ncand,1,-1
|
|
||||||
do i=1,ncand
|
|
||||||
j=indx(i)
|
|
||||||
! if( candidate0(3,j) .ge. syncmin .and. candidate0(2,j).ge.-1.5 ) then
|
|
||||||
if( candidate0(3,j) .ge. syncmin ) then
|
|
||||||
candidate(2:3,k)=candidate0(2:3,j)
|
|
||||||
candidate(1,k)=abs(candidate0(1,j))
|
|
||||||
k=k+1
|
|
||||||
endif
|
|
||||||
enddo
|
|
||||||
ncand=k-1
|
|
||||||
return
|
|
||||||
end subroutine syncft4
|
|
@ -24,15 +24,14 @@ module ft4_decode
|
|||||||
contains
|
contains
|
||||||
|
|
||||||
subroutine decode(this,callback,iwave,nQSOProgress,nfqso, &
|
subroutine decode(this,callback,iwave,nQSOProgress,nfqso, &
|
||||||
nutc,nfa,nfb,ndepth,ncontest,mycall,hiscall)
|
nutc,nfa,nfb,ndepth,lapcqonly,ncontest,mycall,hiscall)
|
||||||
use timer_module, only: timer
|
use timer_module, only: timer
|
||||||
use packjt77
|
use packjt77
|
||||||
include 'ft4/ft4_params.f90'
|
include 'ft4/ft4_params.f90'
|
||||||
class(ft4_decoder), intent(inout) :: this
|
class(ft4_decoder), intent(inout) :: this
|
||||||
procedure(ft4_decode_callback) :: callback
|
procedure(ft4_decode_callback) :: callback
|
||||||
parameter (NSS=NSPS/NDOWN)
|
parameter (NSS=NSPS/NDOWN,NDMAX=NMAX/NDOWN)
|
||||||
parameter (NZZ=18*3456)
|
character message*37,msgsent*37
|
||||||
character message*37,msgsent*37,msg0*37
|
|
||||||
character c77*77
|
character c77*77
|
||||||
character*37 decodes(100)
|
character*37 decodes(100)
|
||||||
character*512 data_dir,fname
|
character*512 data_dir,fname
|
||||||
@ -42,44 +41,35 @@ contains
|
|||||||
character*6 hhmmss
|
character*6 hhmmss
|
||||||
character*4 cqstr,cqstr0
|
character*4 cqstr,cqstr0
|
||||||
|
|
||||||
complex cd2(0:NZZ/NDOWN-1) !Complex waveform
|
complex cd2(0:NDMAX-1) !Complex waveform
|
||||||
complex cb(0:NZZ/NDOWN-1+NN*NSS)
|
complex cb(0:NDMAX-1)
|
||||||
complex cd(0:NN*NSS-1) !Complex waveform
|
complex cd(0:NN*NSS-1) !Complex waveform
|
||||||
complex ctwk(2*NSS),ctwk2(2*NSS,-16:16)
|
complex ctwk(2*NSS),ctwk2(2*NSS,-16:16)
|
||||||
complex csymb(NSS)
|
|
||||||
complex cs(0:3,NN)
|
|
||||||
real s4(0:3,NN)
|
|
||||||
|
|
||||||
real bmeta(2*NN),bmetb(2*NN),bmetc(2*NN)
|
|
||||||
real a(5)
|
real a(5)
|
||||||
|
real bitmetrics(2*NN,3)
|
||||||
|
real dd(NMAX)
|
||||||
real llr(2*ND),llra(2*ND),llrb(2*ND),llrc(2*ND),llrd(2*ND)
|
real llr(2*ND),llra(2*ND),llrb(2*ND),llrc(2*ND),llrd(2*ND)
|
||||||
real s2(0:255)
|
real candidate(2,100)
|
||||||
real candidate(3,100)
|
|
||||||
real savg(NH1),sbase(NH1)
|
real savg(NH1),sbase(NH1)
|
||||||
|
|
||||||
integer apbits(2*ND)
|
integer apbits(2*ND)
|
||||||
integer apmy_ru(28),aphis_fd(28)
|
integer apmy_ru(28),aphis_fd(28)
|
||||||
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
integer*2 iwave(NMAX) !Raw received data
|
||||||
integer*2 iwave(NZZ) !Raw received data
|
|
||||||
integer*1 message77(77),rvec(77),apmask(2*ND),cw(2*ND)
|
integer*1 message77(77),rvec(77),apmask(2*ND),cw(2*ND)
|
||||||
integer*1 hbits(2*NN)
|
integer*1 hbits(2*NN)
|
||||||
integer graymap(0:3)
|
integer i4tone(103)
|
||||||
integer ip(1)
|
|
||||||
integer nappasses(0:5) ! # of decoding passes for QSO States 0-5
|
integer nappasses(0:5) ! # of decoding passes for QSO States 0-5
|
||||||
integer naptypes(0:5,4) ! nQSOProgress, decoding pass
|
integer naptypes(0:5,4) ! nQSOProgress, decoding pass
|
||||||
integer mcq(29)
|
integer mcq(29)
|
||||||
integer mrrr(19),m73(19),mrr73(19)
|
integer mrrr(19),m73(19),mrr73(19)
|
||||||
|
|
||||||
logical nohiscall,unpk77_success
|
logical nohiscall,unpk77_success
|
||||||
logical one(0:255,0:7) ! 256 4-symbol sequences, 8 bits
|
|
||||||
logical first, dobigfft
|
logical first, dobigfft
|
||||||
|
logical dosubtract,doosd
|
||||||
|
logical badsync
|
||||||
|
logical, intent(in) :: lapcqonly
|
||||||
|
|
||||||
data icos4a/0,1,3,2/
|
|
||||||
data icos4b/1,0,2,3/
|
|
||||||
data icos4c/2,3,1,0/
|
|
||||||
data icos4d/3,2,0,1/
|
|
||||||
data graymap/0,1,3,2/
|
|
||||||
data msg0/' '/
|
|
||||||
data first/.true./
|
data first/.true./
|
||||||
data mcq/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0/
|
data mcq/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0/
|
||||||
data mrrr/0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1/
|
data mrrr/0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1/
|
||||||
@ -88,11 +78,14 @@ contains
|
|||||||
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
|
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
|
||||||
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
|
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
|
||||||
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
|
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
|
||||||
save fs,dt,tt,txt,twopi,h,one,first,apbits,nappasses,naptypes, &
|
save fs,dt,tt,txt,twopi,h,first,apbits,nappasses,naptypes, &
|
||||||
mycall0,hiscall0,msg0,cqstr0,ctwk2
|
mycall0,hiscall0,cqstr0,ctwk2
|
||||||
|
|
||||||
this%callback => callback
|
this%callback => callback
|
||||||
hhmmss=cdatetime0(8:13)
|
hhmmss=cdatetime0(8:13)
|
||||||
|
dxcall13=hiscall
|
||||||
|
mycall13=mycall
|
||||||
|
|
||||||
if(first) then
|
if(first) then
|
||||||
fs=12000.0/NDOWN !Sample rate after downsampling
|
fs=12000.0/NDOWN !Sample rate after downsampling
|
||||||
dt=1/fs !Sample interval after downsample (s)
|
dt=1/fs !Sample interval after downsample (s)
|
||||||
@ -100,12 +93,6 @@ contains
|
|||||||
txt=NZ*dt !Transmission length (s) without ramp up/down
|
txt=NZ*dt !Transmission length (s) without ramp up/down
|
||||||
twopi=8.0*atan(1.0)
|
twopi=8.0*atan(1.0)
|
||||||
h=1.0
|
h=1.0
|
||||||
one=.false.
|
|
||||||
do i=0,255
|
|
||||||
do j=0,7
|
|
||||||
if(iand(i,2**j).ne.0) one(i,j)=.true.
|
|
||||||
enddo
|
|
||||||
enddo
|
|
||||||
|
|
||||||
do idf=-16,16
|
do idf=-16,16
|
||||||
a=0.
|
a=0.
|
||||||
@ -199,49 +186,88 @@ contains
|
|||||||
mycall0=mycall
|
mycall0=mycall
|
||||||
hiscall0=hiscall
|
hiscall0=hiscall
|
||||||
endif
|
endif
|
||||||
candidate=0.0
|
|
||||||
ncand=0
|
|
||||||
syncmin=1.2
|
|
||||||
maxcand=100
|
maxcand=100
|
||||||
|
ndecodes=0
|
||||||
|
decodes=' '
|
||||||
fa=nfa
|
fa=nfa
|
||||||
fb=nfb
|
fb=nfb
|
||||||
|
dd=iwave
|
||||||
|
|
||||||
|
! ndepth=3: 3 passes, bp+osd
|
||||||
|
! ndepth=2: 3 passes, bp only
|
||||||
|
! ndepth=1: 1 pass, no subtraction
|
||||||
|
|
||||||
|
max_iterations=40
|
||||||
|
syncmin=1.2
|
||||||
|
dosubtract=.true.
|
||||||
|
doosd=.true.
|
||||||
|
nsp=3
|
||||||
|
if(ndepth.eq.2) then
|
||||||
|
doosd=.false.
|
||||||
|
endif
|
||||||
|
if(ndepth.eq.1) then
|
||||||
|
nsp=1
|
||||||
|
dosubtract=.false.
|
||||||
|
doosd=.false.
|
||||||
|
endif
|
||||||
|
|
||||||
|
do isp = 1,nsp
|
||||||
|
if(isp.eq.2) then
|
||||||
|
if(ndecodes.eq.0) exit
|
||||||
|
nd1=ndecodes
|
||||||
|
elseif(isp.eq.3) then
|
||||||
|
nd2=ndecodes-nd1
|
||||||
|
if(nd2.eq.0) exit
|
||||||
|
endif
|
||||||
|
|
||||||
|
candidate=0.0
|
||||||
|
ncand=0
|
||||||
call timer('getcand4',0)
|
call timer('getcand4',0)
|
||||||
call getcandidates4(iwave,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
call getcandidates4(dd,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
||||||
ncand,sbase)
|
ncand,sbase)
|
||||||
call timer('getcand4',1)
|
call timer('getcand4',1)
|
||||||
|
|
||||||
ndecodes=0
|
|
||||||
dobigfft=.true.
|
dobigfft=.true.
|
||||||
do icand=1,ncand
|
do icand=1,ncand
|
||||||
f0=candidate(1,icand)
|
f0=candidate(1,icand)
|
||||||
snr=candidate(3,icand)-1.0
|
snr=candidate(2,icand)-1.0
|
||||||
call timer('ft4_down',0)
|
call timer('ft4_down',0)
|
||||||
call ft4_downsample(iwave,dobigfft,f0,cd2) !Downsample to 32 Sam/Sym
|
call ft4_downsample(dd,dobigfft,f0,cd2) !Downsample to 32 Sam/Sym
|
||||||
call timer('ft4_down',1)
|
call timer('ft4_down',1)
|
||||||
if(dobigfft) dobigfft=.false.
|
if(dobigfft) dobigfft=.false.
|
||||||
sum2=sum(cd2*conjg(cd2))/(real(NZZ)/real(NDOWN))
|
sum2=sum(cd2*conjg(cd2))/(real(NMAX)/real(NDOWN))
|
||||||
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
|
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
|
||||||
! Sample rate is now 12000/16 = 750 samples/second
|
! Sample rate is now 12000/18 = 666.67 samples/second
|
||||||
|
do iseg=1,3 ! DT search is done over 3 segments
|
||||||
do isync=1,2
|
do isync=1,2
|
||||||
if(isync.eq.1) then
|
if(isync.eq.1) then
|
||||||
idfmin=-12
|
idfmin=-12
|
||||||
idfmax=12
|
idfmax=12
|
||||||
idfstp=3
|
idfstp=3
|
||||||
ibmin=0
|
ibmin=-344
|
||||||
ibmax=800
|
ibmax=1012
|
||||||
|
if(iseg.eq.1) then
|
||||||
|
ibmin=108
|
||||||
|
ibmax=560
|
||||||
|
elseif(iseg.eq.2) then
|
||||||
|
smax1=smax
|
||||||
|
ibmin=560
|
||||||
|
ibmax=1012
|
||||||
|
elseif(iseg.eq.3) then
|
||||||
|
ibmin=-344
|
||||||
|
ibmax=108
|
||||||
|
endif
|
||||||
ibstp=4
|
ibstp=4
|
||||||
else
|
else
|
||||||
idfmin=idfbest-4
|
idfmin=idfbest-4
|
||||||
idfmax=idfbest+4
|
idfmax=idfbest+4
|
||||||
idfstp=1
|
idfstp=1
|
||||||
ibmin=max(0,ibest-5)
|
ibmin=ibest-5
|
||||||
ibmax=min(ibest+5,NZZ/NDOWN-1)
|
ibmax=ibest+5
|
||||||
ibstp=1
|
ibstp=1
|
||||||
endif
|
endif
|
||||||
ibest=-1
|
ibest=-1
|
||||||
smax=-99.
|
|
||||||
idfbest=0
|
idfbest=0
|
||||||
|
smax=-99.
|
||||||
call timer('sync4d ',0)
|
call timer('sync4d ',0)
|
||||||
do idf=idfmin,idfmax,idfstp
|
do idf=idfmin,idfmax,idfstp
|
||||||
do istart=ibmin,ibmax,ibstp
|
do istart=ibmin,ibmax,ibstp
|
||||||
@ -255,99 +281,30 @@ contains
|
|||||||
enddo
|
enddo
|
||||||
call timer('sync4d ',1)
|
call timer('sync4d ',1)
|
||||||
enddo
|
enddo
|
||||||
f0=f0+real(idfbest)
|
if(iseg.eq.1) smax1=smax
|
||||||
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
|
if(smax.lt.1.2) cycle
|
||||||
! write(*,3002) smax,ibest/750.0,f0
|
if(iseg.gt.1 .and. smax.lt.smax1) cycle
|
||||||
!3002 format('b',3f8.2)
|
f1=f0+real(idfbest)
|
||||||
|
if( f1.le.10.0 .or. f1.ge.4990.0 ) cycle
|
||||||
call timer('ft4down ',0)
|
call timer('ft4down ',0)
|
||||||
call ft4_downsample(iwave,dobigfft,f0,cb) !Final downsample, corrected f0
|
call ft4_downsample(dd,dobigfft,f1,cb) !Final downsample, corrected f0
|
||||||
call timer('ft4down ',1)
|
call timer('ft4down ',1)
|
||||||
sum2=sum(abs(cb)**2)/(real(NSS)*NN)
|
sum2=sum(abs(cb)**2)/(real(NSS)*NN)
|
||||||
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
|
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
|
||||||
cd=cb(ibest:ibest+NN*NSS-1)
|
cd=0.
|
||||||
call timer('four2a ',0)
|
if(ibest.ge.0) then
|
||||||
do k=1,NN
|
it=min(NDMAX-1,ibest+NN*NSS-1)
|
||||||
i1=(k-1)*NSS
|
np=it-ibest+1
|
||||||
csymb=cd(i1:i1+NSS-1)
|
cd(0:np-1)=cb(ibest:it)
|
||||||
call four2a(csymb,NSS,1,-1,1)
|
|
||||||
cs(0:3,k)=csymb(1:4)
|
|
||||||
s4(0:3,k)=abs(csymb(1:4))
|
|
||||||
enddo
|
|
||||||
call timer('four2a ',1)
|
|
||||||
|
|
||||||
! Sync quality check
|
|
||||||
is1=0
|
|
||||||
is2=0
|
|
||||||
is3=0
|
|
||||||
is4=0
|
|
||||||
do k=1,4
|
|
||||||
ip=maxloc(s4(:,k))
|
|
||||||
if(icos4a(k-1).eq.(ip(1)-1)) is1=is1+1
|
|
||||||
ip=maxloc(s4(:,k+33))
|
|
||||||
if(icos4b(k-1).eq.(ip(1)-1)) is2=is2+1
|
|
||||||
ip=maxloc(s4(:,k+66))
|
|
||||||
if(icos4c(k-1).eq.(ip(1)-1)) is3=is3+1
|
|
||||||
ip=maxloc(s4(:,k+99))
|
|
||||||
if(icos4d(k-1).eq.(ip(1)-1)) is4=is4+1
|
|
||||||
enddo
|
|
||||||
nsync=is1+is2+is3+is4 !Number of correct hard sync symbols, 0-16
|
|
||||||
if(smax .lt. 0.7 .or. nsync .lt. 8) cycle
|
|
||||||
|
|
||||||
do nseq=1,3 !Try coherent sequences of 1, 2, and 4 symbols
|
|
||||||
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,NN-nsym+1,nsym !87+16=103 symbols.
|
|
||||||
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
|
else
|
||||||
print*,"Error - nsym must be 1, 2, or 4."
|
cd(-ibest:ibest+NN*NSS-1)=cb(0:NN*NSS+2*ibest-1)
|
||||||
endif
|
endif
|
||||||
enddo
|
call timer('bitmet ',0)
|
||||||
ipt=1+(ks-1)*2
|
call get_ft4_bitmetrics(cd,bitmetrics,badsync)
|
||||||
if(nsym.eq.1) ibmax=1
|
call timer('bitmet ',1)
|
||||||
if(nsym.eq.2) ibmax=3
|
if(badsync) cycle
|
||||||
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.2*NN) 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
|
|
||||||
|
|
||||||
bmetb(205:206)=bmeta(205:206)
|
|
||||||
bmetc(201:204)=bmetb(201:204)
|
|
||||||
bmetc(205:206)=bmeta(205:206)
|
|
||||||
|
|
||||||
call normalizebmet(bmeta,2*NN)
|
|
||||||
call normalizebmet(bmetb,2*NN)
|
|
||||||
call normalizebmet(bmetc,2*NN)
|
|
||||||
|
|
||||||
hbits=0
|
hbits=0
|
||||||
where(bmeta.ge.0) hbits=1
|
where(bitmetrics(:,1).ge.0) hbits=1
|
||||||
ns1=count(hbits( 1: 8).eq.(/0,0,0,1,1,0,1,1/))
|
ns1=count(hbits( 1: 8).eq.(/0,0,0,1,1,0,1,1/))
|
||||||
ns2=count(hbits( 67: 74).eq.(/0,1,0,0,1,1,1,0/))
|
ns2=count(hbits( 67: 74).eq.(/0,1,0,0,1,1,1,0/))
|
||||||
ns3=count(hbits(133:140).eq.(/1,1,1,0,0,1,0,0/))
|
ns3=count(hbits(133:140).eq.(/1,1,1,0,0,1,0,0/))
|
||||||
@ -356,21 +313,23 @@ contains
|
|||||||
if(nsync_qual.lt. 20) cycle
|
if(nsync_qual.lt. 20) cycle
|
||||||
|
|
||||||
scalefac=2.83
|
scalefac=2.83
|
||||||
llra( 1: 58)=bmeta( 9: 66)
|
llra( 1: 58)=bitmetrics( 9: 66, 1)
|
||||||
llra( 59:116)=bmeta( 75:132)
|
llra( 59:116)=bitmetrics( 75:132, 1)
|
||||||
llra(117:174)=bmeta(141:198)
|
llra(117:174)=bitmetrics(141:198, 1)
|
||||||
llra=scalefac*llra
|
llra=scalefac*llra
|
||||||
llrb( 1: 58)=bmetb( 9: 66)
|
llrb( 1: 58)=bitmetrics( 9: 66, 2)
|
||||||
llrb( 59:116)=bmetb( 75:132)
|
llrb( 59:116)=bitmetrics( 75:132, 2)
|
||||||
llrb(117:174)=bmetb(141:198)
|
llrb(117:174)=bitmetrics(141:198, 2)
|
||||||
llrb=scalefac*llrb
|
llrb=scalefac*llrb
|
||||||
llrc( 1: 58)=bmetc( 9: 66)
|
llrc( 1: 58)=bitmetrics( 9: 66, 3)
|
||||||
llrc( 59:116)=bmetc( 75:132)
|
llrc( 59:116)=bitmetrics( 75:132, 3)
|
||||||
llrc(117:174)=bmetc(141:198)
|
llrc(117:174)=bitmetrics(141:198, 3)
|
||||||
llrc=scalefac*llrc
|
llrc=scalefac*llrc
|
||||||
|
|
||||||
apmag=maxval(abs(llra))*1.1
|
apmag=maxval(abs(llra))*1.1
|
||||||
npasses=3+nappasses(nQSOProgress)
|
npasses=3+nappasses(nQSOProgress)
|
||||||
|
if(lapcqonly) npasses=4
|
||||||
|
if(ndepth.eq.1) npasses=3
|
||||||
if(ncontest.ge.5) npasses=3 ! Don't support Fox and Hound
|
if(ncontest.ge.5) npasses=3 ! Don't support Fox and Hound
|
||||||
do ipass=1,npasses
|
do ipass=1,npasses
|
||||||
if(ipass.eq.1) llr=llra
|
if(ipass.eq.1) llr=llra
|
||||||
@ -382,8 +341,9 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if(ipass .gt. 3) then
|
if(ipass .gt. 3) then
|
||||||
llrd=llrc
|
llrd=llra
|
||||||
iaptype=naptypes(nQSOProgress,ipass-3)
|
iaptype=naptypes(nQSOProgress,ipass-3)
|
||||||
|
if(lapcqonly) iaptype=1
|
||||||
|
|
||||||
! ncontest=0 : NONE
|
! ncontest=0 : NONE
|
||||||
! 1 : NA_VHF
|
! 1 : NA_VHF
|
||||||
@ -395,7 +355,7 @@ contains
|
|||||||
!
|
!
|
||||||
! Conditions that cause us to bail out of AP decoding
|
! Conditions that cause us to bail out of AP decoding
|
||||||
napwid=50
|
napwid=50
|
||||||
if(ncontest.le.4 .and. iaptype.ge.3 .and. (abs(f0-nfqso).gt.napwid) ) cycle
|
if(ncontest.le.4 .and. iaptype.ge.3 .and. (abs(f1-nfqso).gt.napwid) ) cycle
|
||||||
if(iaptype.ge.2 .and. apbits(1).gt.1) cycle ! No, or nonstandard, mycall
|
if(iaptype.ge.2 .and. apbits(1).gt.1) cycle ! No, or nonstandard, mycall
|
||||||
if(iaptype.ge.3 .and. apbits(30).gt.1) cycle ! No, or nonstandard, dxcall
|
if(iaptype.ge.3 .and. apbits(30).gt.1) cycle ! No, or nonstandard, dxcall
|
||||||
|
|
||||||
@ -441,46 +401,66 @@ contains
|
|||||||
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
|
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
|
||||||
apmask=0
|
apmask=0
|
||||||
if(ncontest.le.4) then
|
if(ncontest.le.4) then
|
||||||
apmask(1:91)=1 ! mycall, hiscall, RRR|73|RR73
|
apmask(1:77)=1 ! mycall, hiscall, RRR|73|RR73
|
||||||
if(iaptype.eq.6) llrd(1:91)=apmag*apbits(1:91)
|
if(iaptype.eq.6) llrd(1:77)=apmag*apbits(1:77)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
llr=llrd
|
llr=llrd
|
||||||
endif
|
endif
|
||||||
max_iterations=40
|
|
||||||
message77=0
|
message77=0
|
||||||
|
dmin=0.0
|
||||||
call timer('bpdec174',0)
|
call timer('bpdec174',0)
|
||||||
call bpdecode174_91(llr,apmask,max_iterations,message77, &
|
call bpdecode174_91(llr,apmask,max_iterations,message77, &
|
||||||
cw,nharderror,niterations)
|
cw,nharderror,niterations)
|
||||||
call timer('bpdec174',1)
|
call timer('bpdec174',1)
|
||||||
|
|
||||||
|
if(doosd .and. nharderror.lt.0) then
|
||||||
|
ndeep=3
|
||||||
|
! if(abs(nfqso-f1).le.napwid) then
|
||||||
|
! ndeep=4
|
||||||
|
! endif
|
||||||
|
call timer('osd174_91 ',0)
|
||||||
|
call osd174_91(llr,apmask,ndeep,message77,cw,nharderror,dmin)
|
||||||
|
call timer('osd174_91 ',1)
|
||||||
|
endif
|
||||||
|
|
||||||
if(sum(message77).eq.0) cycle
|
if(sum(message77).eq.0) cycle
|
||||||
if( nharderror.ge.0 ) then
|
if( nharderror.ge.0 ) then
|
||||||
message77=mod(message77+rvec,2) ! remove rvec scrambling
|
message77=mod(message77+rvec,2) ! remove rvec scrambling
|
||||||
write(c77,'(77i1)') message77(1:77)
|
write(c77,'(77i1)') message77(1:77)
|
||||||
call unpack77(c77,1,message,unpk77_success)
|
call unpack77(c77,1,message,unpk77_success)
|
||||||
|
if(unpk77_success.and.dosubtract) then
|
||||||
|
call get_ft4_tones_from_77bits(message77,i4tone)
|
||||||
|
dt=real(ibest)/666.67
|
||||||
|
call timer('subtract',0)
|
||||||
|
call subtractft4(dd,i4tone,f1,dt)
|
||||||
|
call timer('subtract',1)
|
||||||
|
endif
|
||||||
idupe=0
|
idupe=0
|
||||||
do i=1,ndecodes
|
do i=1,ndecodes
|
||||||
if(decodes(i).eq.message) idupe=1
|
if(decodes(i).eq.message) idupe=1
|
||||||
enddo
|
enddo
|
||||||
if(ibest.le.10 .and. message.eq.msg0) idupe=1 !Already decoded
|
|
||||||
if(idupe.eq.1) exit
|
if(idupe.eq.1) exit
|
||||||
ndecodes=ndecodes+1
|
ndecodes=ndecodes+1
|
||||||
decodes(ndecodes)=message
|
decodes(ndecodes)=message
|
||||||
if(snr.gt.0.0) then
|
if(snr.gt.0.0) then
|
||||||
xsnr=10*log10(snr)-14.0
|
xsnr=10*log10(snr)-14.8
|
||||||
else
|
else
|
||||||
xsnr=-20.0
|
xsnr=-21.0
|
||||||
endif
|
endif
|
||||||
nsnr=nint(max(-20.0,xsnr))
|
nsnr=nint(max(-21.0,xsnr))
|
||||||
xdt=ibest/750.0 - 0.5
|
xdt=ibest/666.67 - 0.5
|
||||||
call this%callback(sync,nsnr,xdt,f0,message,iaptype,qual)
|
!write(21,'(i6.6,i5,2x,f4.1,i6,2x,a37,2x,f4.1,3i3,f5.1,i4,i4,i4)') &
|
||||||
if(ibest.ge.ibmax-15) msg0=message !Possible dupe candidate
|
! nutc,nsnr,xdt,nint(f1),message,smax,iaptype,ipass,isp,dmin,nsync_qual,nharderror,iseg
|
||||||
|
call this%callback(smax,nsnr,xdt,f1,message,iaptype,qual)
|
||||||
exit
|
exit
|
||||||
endif
|
endif
|
||||||
enddo !Sequence estimation
|
enddo !Sequence estimation
|
||||||
|
if(nharderror.ge.0) exit
|
||||||
|
enddo !3 DT segments
|
||||||
enddo !Candidate list
|
enddo !Candidate list
|
||||||
|
enddo !Subtraction loop
|
||||||
return
|
return
|
||||||
end subroutine decode
|
end subroutine decode
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
|
|||||||
cycle
|
cycle
|
||||||
endif
|
endif
|
||||||
nbadcrc=0 ! If we get this far: valid codeword, valid (i3,n3), nonquirky message.
|
nbadcrc=0 ! If we get this far: valid codeword, valid (i3,n3), nonquirky message.
|
||||||
call get_tones_from_77bits(message77,itone)
|
call get_ft8_tones_from_77bits(message77,itone)
|
||||||
if(lsubtract) call subtractft8(dd0,itone,f1,xdt)
|
if(lsubtract) call subtractft8(dd0,itone,f1,xdt)
|
||||||
xsig=0.0
|
xsig=0.0
|
||||||
xnoi=0.0
|
xnoi=0.0
|
||||||
|
@ -91,7 +91,7 @@ program ft8sim
|
|||||||
msg0=msg
|
msg0=msg
|
||||||
do ifile=1,nfiles
|
do ifile=1,nfiles
|
||||||
k=nint((xdt+0.5)/dt)
|
k=nint((xdt+0.5)/dt)
|
||||||
ia=k
|
ia=max(1,k)
|
||||||
phi=0.0
|
phi=0.0
|
||||||
c0=0.0
|
c0=0.0
|
||||||
do j=1,NN !Generate complex waveform
|
do j=1,NN !Generate complex waveform
|
||||||
@ -105,7 +105,7 @@ program ft8sim
|
|||||||
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c0,NMAX,NWAVE,fs,delay,fspread)
|
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c0,NMAX,NWAVE,fs,delay,fspread)
|
||||||
c=sig*c0
|
c=sig*c0
|
||||||
|
|
||||||
ib=k
|
ib=min(k,NMAX)
|
||||||
wave=real(c)
|
wave=real(c)
|
||||||
peak=maxval(abs(wave(ia:ib)))
|
peak=maxval(abs(wave(ia:ib)))
|
||||||
nslots=1
|
nslots=1
|
||||||
|
@ -57,6 +57,7 @@ program ft8sim_gfsk
|
|||||||
baud=1.0/tt !Keying rate (baud)
|
baud=1.0/tt !Keying rate (baud)
|
||||||
bw=8*baud !Occupied bandwidth (Hz)
|
bw=8*baud !Occupied bandwidth (Hz)
|
||||||
txt=NZ*dt !Transmission length (s)
|
txt=NZ*dt !Transmission length (s)
|
||||||
|
bt=2.0
|
||||||
bandwidth_ratio=2500.0/(fs/2.0)
|
bandwidth_ratio=2500.0/(fs/2.0)
|
||||||
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
||||||
if(snrdb.gt.90.0) sig=1.0
|
if(snrdb.gt.90.0) sig=1.0
|
||||||
@ -67,7 +68,7 @@ program ft8sim_gfsk
|
|||||||
n3=-1
|
n3=-1
|
||||||
call pack77(msg37,i3,n3,c77)
|
call pack77(msg37,i3,n3,c77)
|
||||||
call genft8(msg37,i3,n3,msgsent37,msgbits,itone)
|
call genft8(msg37,i3,n3,msgsent37,msgbits,itone)
|
||||||
call gen_ft8wave(itone,NN,NSPS,fs,f0,cwave,xjunk,1,NWAVE) !Generate complex cwave
|
call gen_ft8wave(itone,NN,NSPS,bt,fs,f0,cwave,xjunk,1,NWAVE) !Generate complex cwave
|
||||||
|
|
||||||
write(*,*)
|
write(*,*)
|
||||||
write(*,'(a23,a37,3x,a7,i1,a1,i1)') 'New Style FT8 Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
write(*,'(a23,a37,3x,a7,i1,a1,i1)') 'New Style FT8 Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
subroutine gen_ft8wave(itone,nsym,nsps,bt,fsample,f0,cwave,wave,icmplx,nwave)
|
||||||
!
|
!
|
||||||
! generate ft8 waveform using Gaussian-filtered frequency pulses.
|
! generate ft8 waveform using Gaussian-filtered frequency pulses.
|
||||||
!
|
!
|
||||||
@ -9,21 +9,20 @@ subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
|||||||
real pulse(23040)
|
real pulse(23040)
|
||||||
real dphi(0:(nsym+2)*nsps-1)
|
real dphi(0:(nsym+2)*nsps-1)
|
||||||
integer itone(nsym)
|
integer itone(nsym)
|
||||||
logical first
|
data ibt0/0/
|
||||||
data first/.true./
|
save pulse,twopi,dt,hmod,ibt0
|
||||||
save pulse,first,twopi,dt,hmod
|
|
||||||
|
|
||||||
if(first) then
|
ibt=nint(10*bt)
|
||||||
|
if(ibt0.ne.ibt) then
|
||||||
twopi=8.0*atan(1.0)
|
twopi=8.0*atan(1.0)
|
||||||
dt=1.0/fsample
|
dt=1.0/fsample
|
||||||
hmod=1.0
|
hmod=1.0
|
||||||
bt=2.0
|
|
||||||
! Compute the frequency-smoothing pulse
|
! Compute the frequency-smoothing pulse
|
||||||
do i=1,3*nsps
|
do i=1,3*nsps
|
||||||
tt=(i-1.5*nsps)/real(nsps)
|
tt=(i-1.5*nsps)/real(nsps)
|
||||||
pulse(i)=gfsk_pulse(bt,tt)
|
pulse(i)=gfsk_pulse(bt,tt)
|
||||||
enddo
|
enddo
|
||||||
first=.false.
|
ibt0=nint(10*bt)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
! Compute the smoothed frequency waveform.
|
! Compute the smoothed frequency waveform.
|
||||||
@ -43,6 +42,7 @@ subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
|||||||
phi=0.0
|
phi=0.0
|
||||||
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
||||||
wave=0.
|
wave=0.
|
||||||
|
if (icmplx .ne. 0) cwave=0. ! avoid writing to memory we may not have access to
|
||||||
k=0
|
k=0
|
||||||
do j=nsps,nsps+nwave-1 !Don't include dummy symbols
|
do j=nsps,nsps+nwave-1 !Don't include dummy symbols
|
||||||
k=k+1
|
k=k+1
|
||||||
|
@ -25,7 +25,7 @@ subroutine genft8(msg,i3,n3,msgsent,msgbits,itone)
|
|||||||
msgsent='*** bad message *** '
|
msgsent='*** bad message *** '
|
||||||
go to 900
|
go to 900
|
||||||
|
|
||||||
entry get_tones_from_77bits(msgbits,itone)
|
entry get_ft8_tones_from_77bits(msgbits,itone)
|
||||||
|
|
||||||
2 call encode174_91(msgbits,codeword) !Encode the test message
|
2 call encode174_91(msgbits,codeword) !Encode the test message
|
||||||
|
|
||||||
|
@ -11,16 +11,22 @@ subroutine subtractft8(dd,itone,f0,dt)
|
|||||||
|
|
||||||
parameter (NMAX=15*12000,NFRAME=1920*79)
|
parameter (NMAX=15*12000,NFRAME=1920*79)
|
||||||
parameter (NFFT=NMAX,NFILT=1400)
|
parameter (NFFT=NMAX,NFILT=1400)
|
||||||
real*4 dd(NMAX), window(-NFILT/2:NFILT/2)
|
real*4 dd(NMAX), window(-NFILT/2:NFILT/2), xjunk
|
||||||
complex cref,camp,cfilt,cw
|
complex cref,camp,cfilt,cw
|
||||||
integer itone(79)
|
integer itone(79)
|
||||||
logical first
|
logical first
|
||||||
data first/.true./
|
data first/.true./
|
||||||
common/heap8/cref(NFRAME),camp(NMAX),cfilt(NMAX),cw(NMAX)
|
common/heap8/cref(NFRAME),camp(NMAX),cfilt(NMAX),cw(NMAX),xjunk(NFRAME)
|
||||||
save first
|
save first
|
||||||
|
|
||||||
nstart=dt*12000+1
|
nstart=dt*12000+1
|
||||||
call genft8refsig(itone,cref,f0)
|
! call genft8refsig(itone,cref,f0)
|
||||||
|
nsym=79
|
||||||
|
nsps=1920
|
||||||
|
fs=12000.0
|
||||||
|
icmplx=1
|
||||||
|
bt=4.0 ! Temporary compromise?
|
||||||
|
call gen_ft8wave(itone,nsym,nsps,bt,fs,f0,cref,xjunk,icmplx,NFRAME)
|
||||||
camp=0.
|
camp=0.
|
||||||
do i=1,nframe
|
do i=1,nframe
|
||||||
id=nstart-1+i
|
id=nstart-1+i
|
||||||
|
@ -89,7 +89,12 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,maxcand,s,candidate, &
|
|||||||
enddo
|
enddo
|
||||||
iz=ib-ia+1
|
iz=ib-ia+1
|
||||||
call indexx(red(ia:ib),iz,indx)
|
call indexx(red(ia:ib),iz,indx)
|
||||||
ibase=indx(nint(0.40*iz)) - 1 + ia
|
npctile=nint(0.40*iz)
|
||||||
|
if(npctile.lt.1) then ! something is wrong; bail out
|
||||||
|
ncand=0
|
||||||
|
return;
|
||||||
|
endif
|
||||||
|
ibase=indx(npctile) - 1 + ia
|
||||||
if(ibase.lt.1) ibase=1
|
if(ibase.lt.1) ibase=1
|
||||||
if(ibase.gt.nh1) ibase=nh1
|
if(ibase.gt.nh1) ibase=nh1
|
||||||
base=red(ibase)
|
base=red(ibase)
|
||||||
|
@ -156,7 +156,7 @@ contains
|
|||||||
nfreqz=nint(dfx)
|
nfreqz=nint(dfx)
|
||||||
call timer('sync4 ',1)
|
call timer('sync4 ',1)
|
||||||
|
|
||||||
nsnr=nint(snrx)
|
nsnr=-26
|
||||||
if(sync.lt.syncmin) then
|
if(sync.lt.syncmin) then
|
||||||
if (associated (this%decode_callback)) then
|
if (associated (this%decode_callback)) then
|
||||||
call this%decode_callback(nsnr,dtxz,nfreqz,.false.,csync, &
|
call this%decode_callback(nsnr,dtxz,nfreqz,.false.,csync, &
|
||||||
@ -166,6 +166,7 @@ contains
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
! We have achieved sync
|
! We have achieved sync
|
||||||
|
nsnr=nint(snrsync - 22.9)
|
||||||
decoded=blank
|
decoded=blank
|
||||||
deepmsg=blank
|
deepmsg=blank
|
||||||
special=' '
|
special=' '
|
||||||
|
@ -22,7 +22,8 @@ program jt9
|
|||||||
!### ndepth was defined as 60001. Why???
|
!### ndepth was defined as 60001. Why???
|
||||||
integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, &
|
integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, &
|
||||||
fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0
|
fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0
|
||||||
logical :: read_files = .true., tx9 = .false., display_help = .false.
|
logical :: read_files = .true., tx9 = .false., display_help = .false., &
|
||||||
|
bLowSidelobes = .false.
|
||||||
type (option) :: long_options(26) = [ &
|
type (option) :: long_options(26) = [ &
|
||||||
option ('help', .false., 'h', 'Display this help message', ''), &
|
option ('help', .false., 'h', 'Display this help message', ''), &
|
||||||
option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), &
|
option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), &
|
||||||
@ -224,7 +225,7 @@ program jt9
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
shared_data%id2=0 !??? Why is this necessary ???
|
shared_data%id2=0 !??? Why is this necessary ???
|
||||||
|
if(mode.eq.5) npts=21*3456
|
||||||
do iblk=1,npts/kstep
|
do iblk=1,npts/kstep
|
||||||
k=iblk*kstep
|
k=iblk*kstep
|
||||||
if(mode.eq.8 .and. k.gt.179712) exit
|
if(mode.eq.8 .and. k.gt.179712) exit
|
||||||
@ -232,7 +233,7 @@ program jt9
|
|||||||
read(unit=wav%lun,end=3) shared_data%id2(k-kstep+1:k)
|
read(unit=wav%lun,end=3) shared_data%id2(k-kstep+1:k)
|
||||||
go to 4
|
go to 4
|
||||||
3 call timer('read_wav',1)
|
3 call timer('read_wav',1)
|
||||||
print*,'EOF on input file ',infile
|
print*,'EOF on input file ',trim(infile)
|
||||||
exit
|
exit
|
||||||
4 call timer('read_wav',1)
|
4 call timer('read_wav',1)
|
||||||
nhsym=(k-2048)/kstep
|
nhsym=(k-2048)/kstep
|
||||||
@ -242,7 +243,7 @@ program jt9
|
|||||||
ingain=0
|
ingain=0
|
||||||
call timer('symspec ',0)
|
call timer('symspec ',0)
|
||||||
nminw=1
|
nminw=1
|
||||||
call symspec(shared_data,k,ntrperiod,nsps,ingain,nminw,pxdb, &
|
call symspec(shared_data,k,ntrperiod,nsps,ingain,bLowSidelobes,nminw,pxdb, &
|
||||||
s,df3,ihsym,npts8,pxdbmax)
|
s,df3,ihsym,npts8,pxdbmax)
|
||||||
call timer('symspec ',1)
|
call timer('symspec ',1)
|
||||||
endif
|
endif
|
||||||
|
@ -53,7 +53,8 @@ contains
|
|||||||
type(riff_descriptor) :: desc
|
type(riff_descriptor) :: desc
|
||||||
character(len=4) :: riff_type
|
character(len=4) :: riff_type
|
||||||
|
|
||||||
open (newunit=this%lun, file=filename, access='stream', form='unformatted', status='old')
|
this%lun=26
|
||||||
|
open (unit=this%lun, file=filename, access='stream',status='old')
|
||||||
read (unit=this%lun) desc,riff_type
|
read (unit=this%lun) desc,riff_type
|
||||||
inquire (unit=this%lun, pos=filepos)
|
inquire (unit=this%lun, pos=filepos)
|
||||||
do
|
do
|
||||||
@ -67,5 +68,6 @@ contains
|
|||||||
end if
|
end if
|
||||||
filepos = filepos + (desc%size + 1) / 2 * 2 ! pad to even alignment
|
filepos = filepos + (desc%size + 1) / 2 * 2 ! pad to even alignment
|
||||||
end do
|
end do
|
||||||
|
return
|
||||||
end subroutine read
|
end subroutine read
|
||||||
end module readwav
|
end module readwav
|
||||||
|
102
lib/rtty_spec.f90
Normal file
102
lib/rtty_spec.f90
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
program rtty_spec
|
||||||
|
|
||||||
|
! Generate simulated data for standard RTTY and WSJT-X modes FT8, FT4
|
||||||
|
|
||||||
|
use wavhdr
|
||||||
|
use packjt
|
||||||
|
parameter (NMAX=15*12000)
|
||||||
|
type(hdr) h
|
||||||
|
complex cwave(NMAX)
|
||||||
|
real wave(NMAX)
|
||||||
|
real*4 dat(NMAX) !Generated waveform
|
||||||
|
integer*2 iwave(NMAX) !Generated waveform
|
||||||
|
integer itone(680) !Channel symbols (values 0-1, 0-3, 0-7)
|
||||||
|
integer*1 msgbits(77)
|
||||||
|
character*37 msg37,msgsent37
|
||||||
|
character*8 arg
|
||||||
|
|
||||||
|
nargs=iargc()
|
||||||
|
if(nargs.ne.1) then
|
||||||
|
print*,'Usage: rtty_spec <snr>'
|
||||||
|
go to 999
|
||||||
|
endif
|
||||||
|
call getarg(1,arg)
|
||||||
|
read(arg,*) snrdb !S/N in dB (2500 hz reference BW)
|
||||||
|
|
||||||
|
rmsdb=25.
|
||||||
|
rms=10.0**(0.05*rmsdb)
|
||||||
|
sig=10.0**(0.05*snrdb)
|
||||||
|
npts=NMAX
|
||||||
|
|
||||||
|
do i=1,NMAX !Generate gaussian noise
|
||||||
|
dat(i)=gran()
|
||||||
|
enddo
|
||||||
|
|
||||||
|
! Add the RTTY signal
|
||||||
|
fsample=12000.0 !Sample rate (Hz)
|
||||||
|
dt=1.0/fsample !Sample interval (s)
|
||||||
|
twopi=8.0*atan(1.0)
|
||||||
|
phi=0.
|
||||||
|
dphi=0.
|
||||||
|
j0=-1
|
||||||
|
do i=6001,NMAX-6000
|
||||||
|
j=nint(i*dt/0.022)
|
||||||
|
if(j.ne.j0) then
|
||||||
|
f0=1415.0
|
||||||
|
call random_number(rr)
|
||||||
|
if(rr.gt.0.5) f0=1585.0
|
||||||
|
dphi=twopi*f0*dt
|
||||||
|
j0=j
|
||||||
|
endif
|
||||||
|
phi=phi+dphi
|
||||||
|
if(phi.gt.twopi) phi=phi-twopi
|
||||||
|
dat(i)=dat(i) + sig*sin(phi)
|
||||||
|
enddo
|
||||||
|
|
||||||
|
! FT8 signal (FSK)
|
||||||
|
i3=0
|
||||||
|
n3=0
|
||||||
|
msg37='WB9XYZ KA2ABC FN42'
|
||||||
|
call genft8(msg37,i3,n3,msgsent37,msgbits,itone)
|
||||||
|
nsym=79
|
||||||
|
nsps=1920
|
||||||
|
bt=99.0
|
||||||
|
f0=3500.0
|
||||||
|
icmplx=0
|
||||||
|
nwave=nsym*nsps
|
||||||
|
call gen_ft8wave(itone,nsym,nsps,bt,fsample,f0,cwave,wave,icmplx,nwave)
|
||||||
|
dat(6001:6000+nwave)=dat(6001:6000+nwave) + sig*wave(1:nwave)
|
||||||
|
|
||||||
|
! FT8 signal (GFSK)
|
||||||
|
i3=0
|
||||||
|
n3=0
|
||||||
|
msg37='WB9XYZ KA2ABC FN42'
|
||||||
|
call genft8(msg37,i3,n3,msgsent37,msgbits,itone)
|
||||||
|
nsym=79
|
||||||
|
nsps=1920
|
||||||
|
bt=2.0
|
||||||
|
f0=4000.0
|
||||||
|
icmplx=0
|
||||||
|
nwave=nsym*nsps
|
||||||
|
call gen_ft8wave(itone,nsym,nsps,bt,fsample,f0,cwave,wave,icmplx,nwave)
|
||||||
|
dat(6001:6000+nwave)=dat(6001:6000+nwave) + sig*wave(1:nwave)
|
||||||
|
|
||||||
|
! Add the FT4 signal
|
||||||
|
ichk=0
|
||||||
|
call genft4(msg37,ichk,msgsent37,msgbits,itone)
|
||||||
|
nsym=103
|
||||||
|
nsps=576
|
||||||
|
f0=4500.0
|
||||||
|
icmplx=0
|
||||||
|
nwave=(nsym+2)*nsps
|
||||||
|
call gen_ft4wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
||||||
|
dat(6001:6000+nwave)=dat(6001:6000+nwave) + sig*wave(1:nwave)
|
||||||
|
|
||||||
|
h=default_header(12000,NMAX)
|
||||||
|
datmax=maxval(abs(dat))
|
||||||
|
iwave=nint(32767.0*dat/datmax)
|
||||||
|
open(10,file='000000_000001.wav',access='stream',status='unknown')
|
||||||
|
write(10) h,iwave
|
||||||
|
close(10)
|
||||||
|
|
||||||
|
999 end program rtty_spec
|
@ -16,6 +16,7 @@
|
|||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDebugStateSaver>
|
#include <QDebugStateSaver>
|
||||||
|
#include "Configuration.hpp"
|
||||||
#include "Radio.hpp"
|
#include "Radio.hpp"
|
||||||
#include "pimpl_impl.hpp"
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
@ -155,14 +156,16 @@ typedef multi_index_container<
|
|||||||
class AD1CCty::impl final
|
class AD1CCty::impl final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit impl ()
|
using entity_by_id = entities_type::index<id>::type;
|
||||||
|
|
||||||
|
explicit impl (Configuration const * configuration)
|
||||||
|
: configuration_ {configuration}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Record fixup (QString call, prefix const& p) const
|
entity_by_id::iterator lookup_entity (QString call, prefix const& p) const
|
||||||
{
|
{
|
||||||
call = call.toUpper ();
|
call = call.toUpper ();
|
||||||
using entity_by_id = entities_type::index<id>::type;
|
|
||||||
entity_by_id::iterator e; // iterator into entity set
|
entity_by_id::iterator e; // iterator into entity set
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -171,23 +174,26 @@ public:
|
|||||||
if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3)
|
if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3)
|
||||||
{
|
{
|
||||||
// KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
|
// KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
|
||||||
e = entities_.project<id> (entities_.get<primary_prefix> ().find ("K"));
|
return entities_.project<id> (entities_.get<primary_prefix> ().find ("K"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
e = entities_.get<id> ().find (p.entity_id_);
|
return entities_.get<id> ().find (p.entity_id_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Record fixup (prefix const& p, entity const& e) const
|
||||||
|
{
|
||||||
Record result;
|
Record result;
|
||||||
result.continent = e->continent_;
|
result.continent = e.continent_;
|
||||||
result.CQ_zone = e->CQ_zone_;
|
result.CQ_zone = e.CQ_zone_;
|
||||||
result.ITU_zone = e->ITU_zone_;
|
result.ITU_zone = e.ITU_zone_;
|
||||||
result.entity_name = e->name_;
|
result.entity_name = e.name_;
|
||||||
result.WAE_only = e->WAE_only_;
|
result.WAE_only = e.WAE_only_;
|
||||||
result.latitude = e->lat_;
|
result.latitude = e.lat_;
|
||||||
result.longtitude = e->long_;
|
result.longtitude = e.long_;
|
||||||
result.UTC_offset = e->UTC_offset_;
|
result.UTC_offset = e.UTC_offset_;
|
||||||
result.primary_prefix = e->primary_prefix_;
|
result.primary_prefix = e.primary_prefix_;
|
||||||
|
|
||||||
// check for overrides
|
// check for overrides
|
||||||
bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true};
|
bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true};
|
||||||
@ -220,6 +226,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Configuration const * configuration_;
|
||||||
QString path_;
|
QString path_;
|
||||||
entities_type entities_;
|
entities_type entities_;
|
||||||
prefixes_type prefixes_;
|
prefixes_type prefixes_;
|
||||||
@ -307,8 +314,13 @@ char const * AD1CCty::continent (Continent c)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AD1CCty::AD1CCty ()
|
AD1CCty::AD1CCty (Configuration const * configuration)
|
||||||
|
: m_ {configuration}
|
||||||
{
|
{
|
||||||
|
Q_ASSERT (configuration);
|
||||||
|
// TODO: G4WJS - consider doing the following asynchronously to
|
||||||
|
// speed up startup. Not urgent as it takes less than 1s on a Core
|
||||||
|
// i7 reading BIG CTY.DAT.
|
||||||
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
|
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
|
||||||
m_->path_ = dataPath.exists (file_name)
|
m_->path_ = dataPath.exists (file_name)
|
||||||
? dataPath.absoluteFilePath (file_name) // user override
|
? dataPath.absoluteFilePath (file_name) // user override
|
||||||
@ -389,7 +401,7 @@ auto AD1CCty::lookup (QString const& call) const -> Record
|
|||||||
auto p = m_->prefixes_.find (exact_search);
|
auto p = m_->prefixes_.find (exact_search);
|
||||||
if (p != m_->prefixes_.end () && p->exact_)
|
if (p != m_->prefixes_.end () && p->exact_)
|
||||||
{
|
{
|
||||||
return m_->fixup (call, *p);
|
return m_->fixup (*p, *m_->lookup_entity (call, *p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (search_prefix.size ())
|
while (search_prefix.size ())
|
||||||
@ -397,9 +409,11 @@ auto AD1CCty::lookup (QString const& call) const -> Record
|
|||||||
auto p = m_->prefixes_.find (search_prefix);
|
auto p = m_->prefixes_.find (search_prefix);
|
||||||
if (p != m_->prefixes_.end ())
|
if (p != m_->prefixes_.end ())
|
||||||
{
|
{
|
||||||
if (!p->exact_ || call.size () == search_prefix.size ())
|
impl::entity_by_id::iterator e = m_->lookup_entity (call, *p);
|
||||||
|
if ((m_->configuration_->include_WAE_entities () || !e->WAE_only_)
|
||||||
|
&& (!p->exact_ || call.size () == search_prefix.size ()))
|
||||||
{
|
{
|
||||||
return m_->fixup (call, *p);
|
return m_->fixup (*p, *e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
search_prefix = search_prefix.left (search_prefix.size () - 1);
|
search_prefix = search_prefix.left (search_prefix.size () - 1);
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
#ifndef AD1C_CTY_HPP_
|
#ifndef AD1C_CTY_HPP_
|
||||||
#define AD1C_CTY_HPP_
|
#define AD1C_CTY_HPP_
|
||||||
|
|
||||||
#include <boost/core/noncopyable.hpp>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QDebug>
|
||||||
#include "pimpl_h.hpp"
|
#include "pimpl_h.hpp"
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
class Configuration;
|
||||||
|
|
||||||
//
|
//
|
||||||
// AD1CCty - Fast access database of Jim Reisert, AD1C's, cty.dat
|
// AD1CCty - Fast access database of Jim Reisert, AD1C's, cty.dat
|
||||||
// entity and entity override information file.
|
// entity and entity override information file.
|
||||||
//
|
//
|
||||||
class AD1CCty final
|
class AD1CCty final
|
||||||
: public QObject
|
: public QObject
|
||||||
, private boost::noncopyable
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ public:
|
|||||||
QString primary_prefix;
|
QString primary_prefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit AD1CCty ();
|
explicit AD1CCty (Configuration const *);
|
||||||
~AD1CCty ();
|
~AD1CCty ();
|
||||||
Record lookup (QString const& call) const;
|
Record lookup (QString const& call) const;
|
||||||
|
|
||||||
|
44
logbook/Multiplier.cpp
Normal file
44
logbook/Multiplier.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "Multiplier.hpp"
|
||||||
|
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include <QDebug>
|
||||||
|
#include "models/CabrilloLog.hpp"
|
||||||
|
#include "pimpl_impl.hpp"
|
||||||
|
|
||||||
|
class Multiplier::impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
impl (AD1CCty const * countries)
|
||||||
|
: countries_ {countries}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AD1CCty const * countries_;
|
||||||
|
worked_set entities_worked_;
|
||||||
|
worked_set grids_worked_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Multiplier::Multiplier (AD1CCty const * countries)
|
||||||
|
: m_ {countries}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Multiplier::~Multiplier ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Multiplier::reload (CabrilloLog const * log)
|
||||||
|
{
|
||||||
|
m_->entities_worked_ = log->unique_DXCC_entities (m_->countries_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Multiplier::entities_worked () const -> worked_set const&
|
||||||
|
{
|
||||||
|
return m_->entities_worked_;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Multiplier::grids_worked () const -> worked_set const&
|
||||||
|
{
|
||||||
|
return m_->grids_worked_;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user