mirror of https://github.com/saitohirga/WSJT-X.git
Merge branch 'release-2.1.0' into develop
This commit is contained in:
commit
1968597783
|
@ -1,3 +1,5 @@
|
|||
.gitattributes export-ignore
|
||||
/samples export-ignore
|
||||
/lib/fsk4hf export-ignore
|
||||
/lib/fsk4hf export-ignore
|
||||
/robots export-ignore
|
||||
|
|
|
@ -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
|
||||
)
|
||||
if (__hamlib_pc_path)
|
||||
set (ENV{PKG_CONFIG_PATH} "${__hamlib_pc_path}" "$ENV{PKG_CONFIG_PATH}")
|
||||
unset (__hamlib_pc_path CACHE)
|
||||
set (__pc_path $ENV{PKG_CONFIG_PATH})
|
||||
list (APPEND __pc_path "${__hamlib_pc_path}")
|
||||
set (ENV{PKG_CONFIG_PATH} "${__pc_path}")
|
||||
unset (__pc_path CACHE)
|
||||
endif ()
|
||||
unset (__hamlib_pc_path CACHE)
|
||||
|
||||
# Use pkg-config to get hints about paths, libs and, flags
|
||||
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
|
||||
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)
|
||||
message (STATUS "Building ${CMAKE_PROJECT_NAME}-${wsjtx_VERSION}")
|
||||
|
||||
|
@ -233,6 +237,7 @@ set (wsjt_qt_CXXSRCS
|
|||
models/FrequencyList.cpp
|
||||
models/StationList.cpp
|
||||
widgets/FrequencyLineEdit.cpp
|
||||
widgets/FrequencyDeltaLineEdit.cpp
|
||||
item_delegates/CandidateKeyFilter.cpp
|
||||
item_delegates/ForeignKeyDelegate.cpp
|
||||
validators/LiveFrequencyValidator.cpp
|
||||
|
@ -275,9 +280,12 @@ set (wsjt_qt_CXXSRCS
|
|||
widgets/CabrilloLogWindow.cpp
|
||||
item_delegates/CallsignDelegate.cpp
|
||||
item_delegates/MaidenheadLocatorDelegate.cpp
|
||||
item_delegates/FrequencyDelegate.cpp
|
||||
item_delegates/FrequencyDeltaDelegate.cpp
|
||||
models/CabrilloLog.cpp
|
||||
logbook/AD1CCty.cpp
|
||||
logbook/WorkedBefore.cpp
|
||||
logbook/Multiplier.cpp
|
||||
)
|
||||
|
||||
set (wsjt_qtmm_CXXSRCS
|
||||
|
@ -383,9 +391,11 @@ set (wsjt_FSRCS
|
|||
lib/astro0.f90
|
||||
lib/avecho.f90
|
||||
lib/averms.f90
|
||||
lib/ft4/averaged_mf.f90
|
||||
lib/azdist.f90
|
||||
lib/badmsg.f90
|
||||
lib/ft8/baseline.f90
|
||||
lib/ft4/ft4_baseline.f90
|
||||
lib/bpdecode40.f90
|
||||
lib/bpdecode128_90.f90
|
||||
lib/ft8/bpdecode174_91.f90
|
||||
|
@ -518,7 +528,6 @@ set (wsjt_FSRCS
|
|||
lib/msk144sim.f90
|
||||
lib/mskrtd.f90
|
||||
lib/nuttal_window.f90
|
||||
lib/ft4/ft4b.f90
|
||||
lib/ft4/ft4sim.f90
|
||||
lib/ft4/ft4sim_mult.f90
|
||||
lib/ft4/ft4_downsample.f90
|
||||
|
@ -555,6 +564,7 @@ set (wsjt_FSRCS
|
|||
lib/stdmsg.f90
|
||||
lib/subtract65.f90
|
||||
lib/ft8/subtractft8.f90
|
||||
lib/ft4/subtractft4.f90
|
||||
lib/sun.f90
|
||||
lib/symspec.f90
|
||||
lib/symspec2.f90
|
||||
|
@ -563,7 +573,7 @@ set (wsjt_FSRCS
|
|||
lib/sync64.f90
|
||||
lib/sync65.f90
|
||||
lib/ft4/getcandidates4.f90
|
||||
lib/ft4/syncft4.f90
|
||||
lib/ft4/get_ft4_bitmetrics.f90
|
||||
lib/ft8/sync8.f90
|
||||
lib/ft8/sync8d.f90
|
||||
lib/ft4/sync4d.f90
|
||||
|
@ -846,9 +856,6 @@ if (Boost_NO_SYSTEM_PATHS)
|
|||
set (BOOST_ROOT ${PROJECT_SOURCE_DIR}/boost)
|
||||
endif ()
|
||||
find_package (Boost 1.63 REQUIRED)
|
||||
if (Boost_FOUND)
|
||||
include_directories (${Boost_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
||||
#
|
||||
# OpenMP
|
||||
|
@ -879,10 +886,7 @@ message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}")
|
|||
#
|
||||
|
||||
# Widgets finds its own dependencies.
|
||||
find_package (Qt5Widgets 5 REQUIRED)
|
||||
find_package (Qt5Multimedia 5 REQUIRED)
|
||||
find_package (Qt5PrintSupport 5 REQUIRED)
|
||||
find_package (Qt5Sql 5 REQUIRED)
|
||||
find_package (Qt5 REQUIRED Widgets Multimedia PrintSupport Sql LinguistTools)
|
||||
|
||||
if (WIN32)
|
||||
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})
|
||||
|
||||
|
||||
# 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
|
||||
function (add_resources resources path)
|
||||
foreach (resource_file_ ${ARGN})
|
||||
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_)
|
||||
endif ()
|
||||
file (TO_NATIVE_PATH ${path}/${name_} dest_)
|
||||
set (resources_ "${resources_}\n <file alias=\"${dest_}\">${source_}</file>")
|
||||
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 /Palettes ${PALETTE_FILES})
|
||||
add_resources (wsjtx_RESOURCES /Translations ${QM_FILES})
|
||||
|
||||
configure_file (wsjtx.qrc.in wsjtx.qrc @ONLY)
|
||||
|
||||
|
@ -1121,7 +1156,6 @@ if (WIN32)
|
|||
wrap_ax_server (GENAXSRCS ${AXSERVERSRCS})
|
||||
endif (WIN32)
|
||||
|
||||
|
||||
#
|
||||
# targets
|
||||
#
|
||||
|
@ -1240,6 +1274,9 @@ target_link_libraries (jt49sim wsjt_fort wsjt_cxx)
|
|||
add_executable (allsim lib/allsim.f90 wsjtx.rc)
|
||||
target_link_libraries (allsim wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (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)
|
||||
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)
|
||||
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)
|
||||
target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
|
||||
|
||||
add_executable (ft4d lib/ft4/ft4d.f90 wsjtx.rc)
|
||||
target_link_libraries (ft4d wsjt_fort wsjt_cxx)
|
||||
add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
|
||||
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
|
||||
|
||||
endif(WSJT_BUILD_UTILS)
|
||||
|
||||
|
|
|
@ -167,8 +167,11 @@
|
|||
#include "MetaDataRegistry.hpp"
|
||||
#include "SettingsGroup.hpp"
|
||||
#include "widgets/FrequencyLineEdit.hpp"
|
||||
#include "widgets/FrequencyDeltaLineEdit.hpp"
|
||||
#include "item_delegates/CandidateKeyFilter.hpp"
|
||||
#include "item_delegates/ForeignKeyDelegate.hpp"
|
||||
#include "item_delegates/FrequencyDelegate.hpp"
|
||||
#include "item_delegates/FrequencyDeltaDelegate.hpp"
|
||||
#include "TransceiverFactory.hpp"
|
||||
#include "Transceiver.hpp"
|
||||
#include "models/Bands.hpp"
|
||||
|
@ -248,6 +251,8 @@ namespace
|
|||
class FrequencyDialog final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Item = FrequencyList_v2::Item;
|
||||
|
||||
|
@ -294,6 +299,8 @@ private:
|
|||
class StationDialog final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit StationDialog (StationList const * stations, Bands * bands, QWidget * parent = nullptr)
|
||||
: QDialog {parent}
|
||||
|
@ -564,6 +571,7 @@ private:
|
|||
DecodeHighlightingModel decode_highlighing_model_;
|
||||
DecodeHighlightingModel next_decode_highlighing_model_;
|
||||
bool highlight_by_mode_;
|
||||
bool include_WAE_entities_;
|
||||
int LotW_days_since_upload_;
|
||||
|
||||
TransceiverFactory::ParameterPack rig_params_;
|
||||
|
@ -609,6 +617,7 @@ private:
|
|||
bool miles_;
|
||||
bool quick_call_;
|
||||
bool disable_TX_on_73_;
|
||||
bool force_call_1st_;
|
||||
bool alternate_bindings_;
|
||||
int watchdog_;
|
||||
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::quick_call () const {return m_->quick_call_;}
|
||||
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_;}
|
||||
int Configuration::watchdog () const {return m_->watchdog_;}
|
||||
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::split_mode () const {return m_->split_mode ();}
|
||||
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_;}
|
||||
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
|
||||
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_;}
|
||||
DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;}
|
||||
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)
|
||||
{
|
||||
|
@ -947,6 +959,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||
, station_insert_action_ {tr ("&Insert ..."), nullptr}
|
||||
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
|
||||
, highlight_by_mode_ {false}
|
||||
, include_WAE_entities_ {false}
|
||||
, LotW_days_since_upload_ {0}
|
||||
, last_port_type_ {TransceiverFactory::Capabilities::none}
|
||||
, 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);
|
||||
|
||||
// delegates
|
||||
auto frequencies_item_delegate = new QStyledItemDelegate {this};
|
||||
frequencies_item_delegate->setItemEditorFactory (item_editor_factory ());
|
||||
ui_->frequencies_table_view->setItemDelegate (frequencies_item_delegate);
|
||||
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {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});
|
||||
|
||||
|
@ -1157,9 +1168,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
|||
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
|
||||
|
||||
// stations delegates
|
||||
auto stations_item_delegate = new QStyledItemDelegate {this};
|
||||
stations_item_delegate->setItemEditorFactory (item_editor_factory ());
|
||||
ui_->stations_table_view->setItemDelegate (stations_item_delegate);
|
||||
ui_->stations_table_view->setItemDelegateForColumn (StationList::offset_column, new FrequencyDeltaDelegate {this});
|
||||
ui_->stations_table_view->setItemDelegateForColumn (StationList::band_column, new ForeignKeyDelegate {&bands_, &next_stations_, 0, StationList::band_column, this});
|
||||
|
||||
// stations actions
|
||||
|
@ -1242,6 +1251,7 @@ void Configuration::impl::initialize_models ()
|
|||
ui_->miles_check_box->setChecked (miles_);
|
||||
ui_->quick_call_check_box->setChecked (quick_call_);
|
||||
ui_->disable_TX_on_73_check_box->setChecked (disable_TX_on_73_);
|
||||
ui_->force_call_1st_check_box->setChecked (force_call_1st_);
|
||||
ui_->alternate_bindings_check_box->setChecked (alternate_bindings_);
|
||||
ui_->tx_watchdog_spin_box->setValue (watchdog_);
|
||||
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 ());
|
||||
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_);
|
||||
|
||||
set_rig_invariants ();
|
||||
|
@ -1463,6 +1474,7 @@ void Configuration::impl::read_settings ()
|
|||
if (!highlight_items.size ()) highlight_items = DecodeHighlightingModel::default_items ();
|
||||
decode_highlighing_model_.items (highlight_items);
|
||||
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_users_.set_age_constraint (LotW_days_since_upload_);
|
||||
|
||||
|
@ -1496,6 +1508,7 @@ void Configuration::impl::read_settings ()
|
|||
miles_ = settings_->value ("Miles", false).toBool ();
|
||||
quick_call_ = settings_->value ("QuickCall", false).toBool ();
|
||||
disable_TX_on_73_ = settings_->value ("73TxDisable", false).toBool ();
|
||||
force_call_1st_ = settings_->value ("ForceCallFirst", false).toBool ();
|
||||
alternate_bindings_ = settings_->value ("AlternateBindings", false).toBool ();
|
||||
watchdog_ = settings_->value ("TxWatchdog", 6).toInt ();
|
||||
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 ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
|
||||
settings_->setValue ("HighlightByMode", highlight_by_mode_);
|
||||
settings_->setValue ("IncludeWAEEntities", include_WAE_entities_);
|
||||
settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_);
|
||||
settings_->setValue ("toRTTY", log_as_RTTY_);
|
||||
settings_->setValue ("dBtoComments", report_in_comments_);
|
||||
|
@ -1598,6 +1612,7 @@ void Configuration::impl::write_settings ()
|
|||
settings_->setValue ("Miles", miles_);
|
||||
settings_->setValue ("QuickCall", quick_call_);
|
||||
settings_->setValue ("73TxDisable", disable_TX_on_73_);
|
||||
settings_->setValue ("ForceCallFirst", force_call_1st_);
|
||||
settings_->setValue ("AlternateBindings", alternate_bindings_);
|
||||
settings_->setValue ("TxWatchdog", watchdog_);
|
||||
settings_->setValue ("Tx2QSO", TX_messages_);
|
||||
|
@ -2042,6 +2057,7 @@ void Configuration::impl::accept ()
|
|||
miles_ = ui_->miles_check_box->isChecked ();
|
||||
quick_call_ = ui_->quick_call_check_box->isChecked ();
|
||||
disable_TX_on_73_ = ui_->disable_TX_on_73_check_box->isChecked ();
|
||||
force_call_1st_ = ui_->force_call_1st_check_box->isChecked ();
|
||||
alternate_bindings_ = ui_->alternate_bindings_check_box->isChecked ();
|
||||
watchdog_ = ui_->tx_watchdog_spin_box->value ();
|
||||
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);
|
||||
}
|
||||
|
||||
if (ui_->accept_udp_requests_check_box->isChecked () != accept_udp_requests_)
|
||||
{
|
||||
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_port_ = ui_->n1mm_server_port_spin_box->value ();
|
||||
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_);
|
||||
}
|
||||
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_users_.set_age_constraint (LotW_days_since_upload_);
|
||||
|
||||
|
|
|
@ -127,6 +127,7 @@ public:
|
|||
bool miles () const;
|
||||
bool quick_call () const;
|
||||
bool disable_TX_on_73 () const;
|
||||
bool force_call_1st() const;
|
||||
bool alternate_bindings() const;
|
||||
int watchdog () const;
|
||||
bool TX_messages () const;
|
||||
|
@ -147,6 +148,7 @@ public:
|
|||
bool EMEonly() const;
|
||||
bool post_decodes () const;
|
||||
QString opCall() const;
|
||||
void opCall (QString const&);
|
||||
QString udp_server_name () const;
|
||||
port_type udp_server_port () const;
|
||||
QString n1mm_server_name () const;
|
||||
|
@ -175,6 +177,7 @@ public:
|
|||
LotWUsers const& lotw_users () const;
|
||||
DecodeHighlightingModel const& decode_highlighting () const;
|
||||
bool highlight_by_mode () const;
|
||||
bool include_WAE_entities () const;
|
||||
|
||||
enum class SpecialOperatingActivity {NONE, NA_VHF, EU_VHF, FIELD_DAY, RTTY, FOX, HOUND};
|
||||
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_port_changed (port_type server_port) const;
|
||||
Q_SIGNAL void accept_udp_requests_changed (bool checked) const;
|
||||
|
||||
// signal updates to decode highlighting
|
||||
Q_SIGNAL void decode_highlighting_changed (DecodeHighlightingModel const&) const;
|
||||
|
|
204
Configuration.ui
204
Configuration.ui
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>546</width>
|
||||
<height>536</height>
|
||||
<height>553</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -301,7 +301,14 @@
|
|||
<string>Behavior</string>
|
||||
</property>
|
||||
<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">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
|
@ -347,10 +354,27 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="decode_at_52s_check_box">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="enable_VHF_features_check_box">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -367,31 +391,35 @@
|
|||
</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="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">
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="monitor_last_used_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>
|
||||
<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>Allow Tx frequency changes while transmitting</string>
|
||||
<string>Monitor returns to last used frequency</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="CW_id_after_73_check_box">
|
||||
|
@ -444,34 +472,6 @@ quiet period when decoding is done.</string>
|
|||
</item>
|
||||
</layout>
|
||||
</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">
|
||||
<widget class="QCheckBox" name="quick_call_check_box">
|
||||
<property name="toolTip">
|
||||
|
@ -482,6 +482,13 @@ text message.</string>
|
|||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -1912,7 +1919,7 @@ for assessing propagation and system performance.</string>
|
|||
<item>
|
||||
<widget class="QGroupBox" name="n1mm_group_box">
|
||||
<property name="title">
|
||||
<string>N1MM Logger+ Broadcasts</string>
|
||||
<string>Secondary UDP Server (deprecated)</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_15">
|
||||
<item row="0" column="0" colspan="2">
|
||||
|
@ -1928,7 +1935,7 @@ for assessing propagation and system performance.</string>
|
|||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="n1mm_server_name_label">
|
||||
<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 name="buddy">
|
||||
<cstring>n1mm_server_name_line_edit</cstring>
|
||||
|
@ -1945,7 +1952,7 @@ for assessing propagation and system performance.</string>
|
|||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="n1mm_server_port_label">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>N1MM Server port number:</p></body></html></string>
|
||||
<string>Server port number:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>n1mm_server_port_spin_box</cstring>
|
||||
|
@ -2287,6 +2294,23 @@ Right click for insert and delete options.</string>
|
|||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -2299,35 +2323,6 @@ Right click for insert and delete options.</string>
|
|||
<string>Logbook of the World User Validation</string>
|
||||
</property>
|
||||
<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">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
|
@ -2362,6 +2357,35 @@ Right click for insert and delete options.</string>
|
|||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -2469,7 +2493,7 @@ Right click for insert and delete options.</string>
|
|||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="gbSpecialOpActivity">
|
||||
<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 name="checkable">
|
||||
<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>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ARRL RTTY Roundup</string>
|
||||
<string>RTTY Roundup messages</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">special_op_activity_button_group</string>
|
||||
|
@ -3079,12 +3103,12 @@ Right click for insert and delete options.</string>
|
|||
</connections>
|
||||
<buttongroups>
|
||||
<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="split_mode_button_group"/>
|
||||
<buttongroup name="TX_mode_button_group"/>
|
||||
<buttongroup name="PTT_method_button_group"/>
|
||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
||||
<buttongroup name="CAT_handshake_button_group"/>
|
||||
<buttongroup name="TX_audio_source_button_group"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
|
|
@ -127,7 +127,7 @@ int DXLabSuiteCommanderTransceiver::do_start ()
|
|||
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
|
||||
}
|
||||
|
||||
poll ();
|
||||
do_poll ();
|
||||
return resolution;
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,7 @@ void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
|
|||
update_mode (m);
|
||||
}
|
||||
|
||||
void DXLabSuiteCommanderTransceiver::poll ()
|
||||
void DXLabSuiteCommanderTransceiver::do_poll ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
bool quiet {false};
|
||||
|
|
|
@ -39,7 +39,7 @@ protected:
|
|||
void do_mode (MODE) override;
|
||||
void do_ptt (bool on) override;
|
||||
|
||||
void poll () override;
|
||||
void do_poll () override;
|
||||
|
||||
private:
|
||||
MODE get_mode (bool no_debug = false);
|
||||
|
|
|
@ -38,5 +38,7 @@
|
|||
<string>True</string>
|
||||
<key>NSRequiresAquaSystemAppearance</key>
|
||||
<true/>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>This app requires microphone access to receive signals.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
23
Detector.cpp
23
Detector.cpp
|
@ -2,6 +2,7 @@
|
|||
#include <QDateTime>
|
||||
#include <QtAlgorithms>
|
||||
#include <QDebug>
|
||||
#include <math.h>
|
||||
#include "commons.h"
|
||||
|
||||
#include "moc_Detector.cpp"
|
||||
|
@ -10,7 +11,7 @@ extern "C" {
|
|||
void fil4_(qint16*, qint32*, qint16*, qint32*);
|
||||
}
|
||||
|
||||
Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds,
|
||||
Detector::Detector (unsigned frameRate, double periodLengthInSeconds,
|
||||
unsigned downSampleFactor, QObject * parent)
|
||||
: AudioDevice (parent)
|
||||
, m_frameRate (frameRate)
|
||||
|
@ -54,12 +55,14 @@ void Detector::clear ()
|
|||
|
||||
qint64 Detector::writeData (char const * data, qint64 maxSize)
|
||||
{
|
||||
int ns=secondInPeriod();
|
||||
if(ns < m_ns) { // When ns has wrapped around to zero, restart the buffers
|
||||
static unsigned mstr0=999999;
|
||||
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;
|
||||
m_bufferPos = 0;
|
||||
}
|
||||
m_ns=ns;
|
||||
mstr0=mstr;
|
||||
|
||||
// no torn frames
|
||||
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 ())) {
|
||||
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
|
||||
<< " frames of data on the floor!"
|
||||
<< dec_data.params.kin << ns;
|
||||
<< dec_data.params.kin << mstr;
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
//
|
||||
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;
|
||||
|
||||
Q_SIGNAL void framesWritten (qint64) const;
|
||||
|
@ -40,10 +41,9 @@ protected:
|
|||
|
||||
private:
|
||||
void clear (); // discard buffer contents
|
||||
unsigned secondInPeriod () const;
|
||||
|
||||
unsigned m_frameRate;
|
||||
unsigned m_period;
|
||||
double m_period;
|
||||
unsigned m_downSampleFactor;
|
||||
qint32 m_samplesPerFFT; // after any down sampling
|
||||
qint32 m_ns;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "EmulateSplitTransceiver.hpp"
|
||||
|
||||
#include "moc_EmulateSplitTransceiver.cpp"
|
||||
|
||||
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
|
||||
: Transceiver {parent}
|
||||
, wrapped_ {std::move (wrapped)}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
class EmulateSplitTransceiver final
|
||||
: public Transceiver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// takes ownership of wrapped Transceiver
|
||||
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped,
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace
|
|||
int constexpr yaesu_delay {250};
|
||||
}
|
||||
|
||||
#include "moc_HRDTransceiver.cpp"
|
||||
|
||||
void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
|
||||
{
|
||||
(*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;
|
||||
}
|
||||
|
||||
void HRDTransceiver::poll ()
|
||||
void HRDTransceiver::do_poll ()
|
||||
{
|
||||
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
|
||||
bool quiet {false};
|
||||
|
|
|
@ -27,6 +27,8 @@ class QByteArray;
|
|||
class HRDTransceiver final
|
||||
: public PollingTransceiver
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
|
||||
|
||||
|
@ -48,7 +50,7 @@ protected:
|
|||
void do_ptt (bool on) override;
|
||||
|
||||
// Implement the PollingTransceiver interface.
|
||||
void poll () override;
|
||||
void do_poll () override;
|
||||
|
||||
private:
|
||||
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
|
||||
}
|
||||
|
||||
poll ();
|
||||
do_poll ();
|
||||
|
||||
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
|
||||
return resolution;
|
||||
|
@ -898,7 +898,7 @@ void HamlibTransceiver::do_mode (MODE mode)
|
|||
update_mode (mode);
|
||||
}
|
||||
|
||||
void HamlibTransceiver::poll ()
|
||||
void HamlibTransceiver::do_poll ()
|
||||
{
|
||||
#if !WSJT_TRACE_CAT_POLLS
|
||||
#if defined (NDEBUG)
|
||||
|
|
|
@ -21,7 +21,7 @@ extern "C"
|
|||
class HamlibTransceiver final
|
||||
: public PollingTransceiver
|
||||
{
|
||||
Q_OBJECT; // for translation context
|
||||
Q_OBJECT // for translation context
|
||||
|
||||
public:
|
||||
static void register_transceivers (TransceiverFactory::Transceivers *);
|
||||
|
@ -40,7 +40,7 @@ class HamlibTransceiver final
|
|||
void do_mode (MODE) override;
|
||||
void do_ptt (bool) override;
|
||||
|
||||
void poll () override;
|
||||
void do_poll () override;
|
||||
|
||||
void error_check (int ret_code, QString const& doing) const;
|
||||
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 multimedia support and serial port is necessary as well as the
|
||||
core Qt v5 components, normally installing the Qt multimedia
|
||||
development package and Qt serialport development package are
|
||||
sufficient to pull in all the required Qt components and dependants as
|
||||
a single transaction. On some systems the Qt multimedia plugin
|
||||
component is separate in the distribution repository an it may also
|
||||
need installing.
|
||||
Qt v5 multimedia support, serial port, and Linguist is necessary as
|
||||
well as the core Qt v5 components, normally installing the Qt
|
||||
multimedia development, Qt serialport development packages, and the Qt
|
||||
Linguist packages are sufficient to pull in all the required Qt
|
||||
components and dependants as a single transaction. On some systems
|
||||
the Qt multimedia plugin component is separate in the distribution
|
||||
repository an it may also need installing.
|
||||
|
||||
The single precision FFTW v3 library libfftw3f is required along with
|
||||
the libfftw library development package. Normally installing the
|
||||
|
@ -256,47 +256,6 @@ The above commands will build hamlib and install it into
|
|||
~/hamlib-prefix. If `make install-strip` fails, try `make install`.
|
||||
|
||||
|
||||
Qt
|
||||
--
|
||||
|
||||
NOTE: As of Qt v5.4 building Qt from source on Mac OS X is no longer
|
||||
necessary since the Qt team have switched to using the modern libc++
|
||||
Standard C++ Library for all distributable run time
|
||||
components. Instead you may simply download a binary installer for OS
|
||||
X 64-bit. The binary installer is here:
|
||||
|
||||
http://www.qt.io/download
|
||||
|
||||
The binary Qt distributions prior to Qt v5.4 from
|
||||
http://www.qt.io/download unfortunately are built to use the libstdc++
|
||||
C++ support library, WSJT-X uses a less geriatric C++ dialect which
|
||||
uses the libc++ C++ support library. This means that you need to
|
||||
build Qt from sources. This is not difficult but does take some time.
|
||||
|
||||
Download the Qt source tarball from
|
||||
http://www.qt.io/download-open-source/, the link is about half way
|
||||
down the page, you want the full sources tar ball shown as a 'tar.gz'
|
||||
link.
|
||||
|
||||
Unpack the sources and cd into the top level directory then type:
|
||||
|
||||
$ ./configure -prefix ~/local/qt-macx-clang -opensource \
|
||||
-confirm-license -platform macx-clang -silent -nomake tests \
|
||||
-nomake examples -sdk macosx10.10 -skip qtwebkit \
|
||||
-skip qtwebkit-examples -skip qtquick1 -skip qtconnectivity \
|
||||
-skip qtlocation -skip qtsensors -skip qtscript \
|
||||
-skip qtwebsockets -skip qtwebengine -skip qtwebchannel \
|
||||
-skip qtwayland -skip qtquickcontrols -skip qtdeclarative \
|
||||
-skip qtxmlpatterns -skip qtenginio
|
||||
$ make -j4
|
||||
$ make install
|
||||
|
||||
If you are building on 10.8 or don't have the 10.10 Mac SDK (Xcode 6)
|
||||
available, you can substitute '-sdk macosx10.9' above.
|
||||
|
||||
The build above will take a few hours to complete.
|
||||
|
||||
|
||||
CMake
|
||||
-----
|
||||
Although CMake is available via MacPorts I prefer to use the binary
|
||||
|
@ -328,6 +287,13 @@ $ sudo chgrp wheel /usr/local/bin
|
|||
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
|
||||
------
|
||||
First fetch the source from the repository:
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include <QUdpSocket>
|
||||
#include <QHostInfo>
|
||||
|
@ -35,6 +36,7 @@ public:
|
|||
impl (QString const& id, QString const& version, QString const& revision,
|
||||
port_type server_port, MessageClient * self)
|
||||
: self_ {self}
|
||||
, enabled_ {false}
|
||||
, id_ {id}
|
||||
, version_ {version}
|
||||
, revision_ {revision}
|
||||
|
@ -79,6 +81,7 @@ public:
|
|||
Q_SLOT void host_info_results (QHostInfo);
|
||||
|
||||
MessageClient * self_;
|
||||
bool enabled_;
|
||||
QString id_;
|
||||
QString version_;
|
||||
QString revision_;
|
||||
|
@ -160,6 +163,12 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||
schema_ = in.schema ();
|
||||
}
|
||||
|
||||
if (!enabled_)
|
||||
{
|
||||
TRACE_UDP ("message processing disabled for id:" << in.id ());
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// message format is described in NetworkMessage.hpp
|
||||
//
|
||||
|
@ -200,6 +209,15 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::Close:
|
||||
TRACE_UDP ("Close");
|
||||
if (check_status (in) != Fail)
|
||||
{
|
||||
last_message_.clear ();
|
||||
Q_EMIT self_->close ();
|
||||
}
|
||||
break;
|
||||
|
||||
case NetworkMessage::Replay:
|
||||
TRACE_UDP ("Replay");
|
||||
if (check_status (in) != Fail)
|
||||
|
@ -261,6 +279,42 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
|
|||
}
|
||||
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:
|
||||
// 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
|
||||
, QString const& report, QString const& tx_mode
|
||||
, 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
|
||||
, 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 ())
|
||||
{
|
||||
|
@ -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 ()
|
||||
<< tx_enabled << transmitting << decoding << rx_df << tx_df << de_call.toUtf8 ()
|
||||
<< de_grid.toUtf8 () << dx_grid.toUtf8 () << watchdog_timeout << sub_mode.toUtf8 ()
|
||||
<< fast_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);
|
||||
<< 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 << "frequency tolerance:" << frequency_tolerance << "T/R period:" << tr_period << "configuration name:" << configuration_name);
|
||||
m_->send_message (out, message);
|
||||
}
|
||||
}
|
||||
|
@ -527,7 +588,7 @@ void MessageClient::logged_ADIF (QByteArray const& ADIF_record)
|
|||
{
|
||||
QByteArray message;
|
||||
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;
|
||||
TRACE_UDP ("ADIF:" << ADIF);
|
||||
m_->send_message (out, message);
|
||||
|
|
|
@ -47,12 +47,16 @@ public:
|
|||
// change the server port messages are sent to
|
||||
Q_SLOT void set_server_port (port_type server_port = 0u);
|
||||
|
||||
// enable incoming messages
|
||||
Q_SLOT void enable (bool);
|
||||
|
||||
// outgoing messages
|
||||
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
|
||||
, 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
|
||||
, 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
|
||||
, QString const& mode, QString const& message, bool low_confidence
|
||||
, bool off_air);
|
||||
|
@ -89,6 +93,10 @@ public:
|
|||
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);
|
||||
|
||||
// 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
|
||||
// all decodes
|
||||
Q_SIGNAL void replay ();
|
||||
|
@ -105,6 +113,16 @@ public:
|
|||
// callsign request for the specified call
|
||||
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
|
||||
// lookup fails
|
||||
Q_SIGNAL void error (QString const&) const;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "MessageServer.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
|
||||
#include <QNetworkInterface>
|
||||
#include <QUdpSocket>
|
||||
|
@ -16,6 +17,11 @@
|
|||
|
||||
#include "moc_MessageServer.cpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
auto quint32_max = std::numeric_limits<quint32>::max ();
|
||||
}
|
||||
|
||||
class MessageServer::impl
|
||||
: public QUdpSocket
|
||||
{
|
||||
|
@ -238,8 +244,8 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
|
|||
bool tx_enabled {false};
|
||||
bool transmitting {false};
|
||||
bool decoding {false};
|
||||
qint32 rx_df {-1};
|
||||
qint32 tx_df {-1};
|
||||
quint32 rx_df {quint32_max};
|
||||
quint32 tx_df {quint32_max};
|
||||
QByteArray de_call;
|
||||
QByteArray de_grid;
|
||||
QByteArray dx_grid;
|
||||
|
@ -247,9 +253,12 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
|
|||
QByteArray sub_mode;
|
||||
bool fast_mode {false};
|
||||
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
|
||||
>> 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)
|
||||
{
|
||||
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 (dx_grid), watchdog_timeout
|
||||
, QString::fromUtf8 (sub_mode), fast_mode
|
||||
, special_op_mode);
|
||||
, special_op_mode, frequency_tolerance, tr_period
|
||||
, QString::fromUtf8 (configuration_name));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
, 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
|
||||
Q_SLOT void replay (QString const& id);
|
||||
|
||||
|
@ -72,15 +75,25 @@ public:
|
|||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||
, 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
|
||||
// matching message
|
||||
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
|
||||
, 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
|
||||
, 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 decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time
|
||||
, quint32 delta_frequency, QString const& mode, QString const& message
|
||||
|
|
|
@ -14,16 +14,35 @@
|
|||
#include "WFPalette.hpp"
|
||||
#include "models/IARURegions.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;
|
||||
return our_item_editor_factory;
|
||||
class ItemEditorFactory final
|
||||
: 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 ()
|
||||
{
|
||||
auto item_editor_factory = new ItemEditorFactory;
|
||||
QItemEditorFactory::setDefaultFactory (item_editor_factory);
|
||||
|
||||
// types in Radio.hpp are registered in their own translation unit
|
||||
// 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
|
||||
// Q_ENUM macro only seems to register the unqualified name
|
||||
|
||||
item_editor_factory ()->registerEditor (qMetaTypeId<Radio::Frequency> (), new QStandardItemEditorCreator<FrequencyLineEdit> ());
|
||||
//auto frequency_delta_type_id = qRegisterMetaType<Radio::FrequencyDelta> ("FrequencyDelta");
|
||||
item_editor_factory ()->registerEditor (qMetaTypeId<Radio::FrequencyDelta> (), new QStandardItemEditorCreator<FrequencyDeltaLineEdit> ());
|
||||
item_editor_factory->registerEditor (qMetaTypeId<QDateTime> (), new QStandardItemEditorCreator<DateTimeEdit> ());
|
||||
|
||||
// Frequency list model
|
||||
qRegisterMetaTypeStreamOperators<FrequencyList_v2::Item> ("Item_v2");
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
#ifndef META_DATA_REGISTRY_HPP__
|
||||
#define META_DATA_REGISTRY_HPP__
|
||||
|
||||
class QItemEditorFactory;
|
||||
|
||||
QItemEditorFactory * item_editor_factory ();
|
||||
void register_types ();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,15 +25,15 @@ double constexpr Modulator::m_twoPi;
|
|||
// unsigned m_nspd=1.2*48000.0/wpm;
|
||||
// m_nspd=3072; //18.75 WPM
|
||||
|
||||
Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
|
||||
Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds,
|
||||
QObject * parent)
|
||||
: AudioDevice {parent}
|
||||
, m_quickClose {false}
|
||||
, m_phi {0.0}
|
||||
, m_toneSpacing {0.0}
|
||||
, m_fSpread {0.0}
|
||||
, m_frameRate {frameRate}
|
||||
, m_period {periodLengthInSeconds}
|
||||
, m_frameRate {frameRate}
|
||||
, m_state {Idle}
|
||||
, m_tuning {false}
|
||||
, m_cwLevel {false}
|
||||
|
@ -45,19 +45,15 @@ Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
|
|||
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
||||
double frequency, double toneSpacing,
|
||||
SoundOutput * stream, Channel channel,
|
||||
bool synchronize, bool fastMode, double dBSNR, int TRperiod)
|
||||
bool synchronize, bool fastMode, double dBSNR, double TRperiod)
|
||||
{
|
||||
Q_ASSERT (stream);
|
||||
// Time according to this computer which becomes our base time
|
||||
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||
|
||||
// qDebug() << "ModStart" << symbolsLength << framesPerSymbol
|
||||
// << frequency << toneSpacing;
|
||||
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
|
||||
|
||||
if(m_state != Idle) stop();
|
||||
|
||||
m_quickClose = false;
|
||||
|
||||
m_symbolsLength = symbolsLength;
|
||||
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
|
||||
m_frequency0 = 0.;
|
||||
|
@ -69,7 +65,9 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||
m_toneSpacing = toneSpacing;
|
||||
m_bFastMode=fastMode;
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if(m_bFastMode) m_ic=0;
|
||||
|
@ -92,14 +87,11 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
|||
if (synchronize && !m_tuning && !m_bFastMode) {
|
||||
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)) {
|
||||
//### FT4 parameters
|
||||
m_ic=0;
|
||||
m_silentFrames=0;
|
||||
}
|
||||
// qDebug() << "Mod AA" << symbolsLength << framesPerSymbol << toneSpacing;
|
||||
// qDebug() << "Mod AB" << delay_ms << mstr << m_ic << m_silentFrames;
|
||||
|
||||
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
|
||||
// << m_ic << m_silentFrames << m_silentFrames/48000.0
|
||||
// << mstr << fmod(double(ms0),1000.0*m_period);
|
||||
|
||||
|
||||
initialize (QIODevice::ReadOnly, channel);
|
||||
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
|
||||
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;
|
||||
static bool bCwId=false;
|
||||
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) m_nspd=2560; // 22.5 WPM
|
||||
|
||||
|
@ -259,7 +251,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||
i1= m_symbolsLength * 4.0 * m_nsps;
|
||||
}
|
||||
if(m_bFastMode and !m_tuning) {
|
||||
i1=m_TRperiod*48000 - 24000;
|
||||
i1=m_TRperiod*48000.0 - 24000.0;
|
||||
i0=i1-816;
|
||||
}
|
||||
|
||||
|
@ -267,7 +259,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
|
|||
|
||||
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
|
||||
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 (isym != m_isym0 || m_frequency != m_frequency0) {
|
||||
if(itone[0]>=100) {
|
||||
|
|
|
@ -23,7 +23,7 @@ class Modulator
|
|||
public:
|
||||
enum ModulatorState {Synchronizing, Active, Idle};
|
||||
|
||||
Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent = nullptr);
|
||||
Modulator (unsigned frameRate, double periodLengthInSeconds, QObject * parent = nullptr);
|
||||
|
||||
void close () override;
|
||||
|
||||
|
@ -31,14 +31,14 @@ public:
|
|||
double frequency () const {return m_frequency;}
|
||||
bool isActive () const {return m_state != Idle;}
|
||||
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_ms0(qint64 ms) {m_ms0=ms;}
|
||||
|
||||
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency,
|
||||
double toneSpacing, SoundOutput *, Channel = Mono,
|
||||
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 tune (bool newState = true);
|
||||
Q_SLOT void setFrequency (double newFrequency) {m_frequency = newFrequency;}
|
||||
|
@ -72,14 +72,14 @@ private:
|
|||
double m_fac;
|
||||
double m_toneSpacing;
|
||||
double m_fSpread;
|
||||
double m_TRperiod;
|
||||
double m_period;
|
||||
|
||||
qint64 m_silentFrames;
|
||||
qint64 m_ms0;
|
||||
qint32 m_TRperiod;
|
||||
qint16 m_ramp;
|
||||
|
||||
unsigned m_frameRate;
|
||||
unsigned m_period;
|
||||
ModulatorState volatile m_state;
|
||||
|
||||
bool volatile m_tuning;
|
||||
|
|
|
@ -65,6 +65,8 @@ namespace
|
|||
class NameDialog final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NameDialog (QString const& current_name,
|
||||
QStringList const& current_names,
|
||||
|
@ -112,6 +114,8 @@ namespace
|
|||
class ExistingNameDialog final
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExistingNameDialog (QStringList const& current_names, QWidget * parent = nullptr)
|
||||
: QDialog {parent}
|
||||
|
@ -155,13 +159,16 @@ public:
|
|||
bool exit ();
|
||||
|
||||
QSettings settings_;
|
||||
QString current_;
|
||||
|
||||
// switch to this configuration
|
||||
void select_configuration (QString const& target_name);
|
||||
|
||||
private:
|
||||
using Dictionary = QMap<QString, QVariant>;
|
||||
|
||||
// create a configuration maintenance sub menu
|
||||
QMenu * create_sub_menu (QMainWindow * main_window,
|
||||
QMenu * parent,
|
||||
QMenu * create_sub_menu (QMenu * parent,
|
||||
QString const& menu_title,
|
||||
QActionGroup * = nullptr);
|
||||
|
||||
|
@ -171,32 +178,32 @@ private:
|
|||
// write the settings values from the dictionary to the current group
|
||||
void load_from (Dictionary const&, bool add_placeholder = true);
|
||||
|
||||
// switch to this configuration
|
||||
void select_configuration (QMainWindow *, QMenu const *);
|
||||
|
||||
// clone this configuration
|
||||
void clone_configuration (QMainWindow * main_window, QMenu *, QMenu const *);
|
||||
void clone_configuration (QMenu *, QMenu const *);
|
||||
|
||||
// update this configuration from another
|
||||
void clone_into_configuration (QMainWindow *, QMenu const *);
|
||||
void clone_into_configuration (QMenu const *);
|
||||
|
||||
// reset configuration to default values
|
||||
void reset_configuration (QMainWindow *, QMenu const *);
|
||||
void reset_configuration (QMenu const *);
|
||||
|
||||
// change configuration name
|
||||
void rename_configuration (QMainWindow *, QMenu *);
|
||||
void rename_configuration (QMenu *);
|
||||
|
||||
// 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
|
||||
QMainWindow * main_window_;
|
||||
bool name_change_emit_pending_; // delayed until menu built
|
||||
|
||||
QFont original_font_;
|
||||
QString current_;
|
||||
|
||||
// action to take on restart
|
||||
enum class RepositionType {unchanged, replace, save_and_replace} reposition_type_;
|
||||
RepositionType reposition_type_;
|
||||
Dictionary new_settings_;
|
||||
bool exit_flag_; // false means loop around with new
|
||||
// configuration
|
||||
|
@ -263,6 +270,16 @@ void MultiSettings::create_menu_actions (QMainWindow * main_window, QMenu * 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 ()
|
||||
{
|
||||
return m_->exit ();
|
||||
|
@ -271,6 +288,7 @@ bool MultiSettings::exit ()
|
|||
MultiSettings::impl::impl (MultiSettings const * parent, QString const& config_name)
|
||||
: settings_ {settings_path (), QSettings::IniFormat}
|
||||
, parent_ {parent}
|
||||
, main_window_ {nullptr}
|
||||
, name_change_emit_pending_ {true}
|
||||
, reposition_type_ {RepositionType::unchanged}
|
||||
, exit_flag_ {true}
|
||||
|
@ -407,13 +425,14 @@ bool MultiSettings::impl::reposition ()
|
|||
// and, reset
|
||||
void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu * menu)
|
||||
{
|
||||
main_window_ = main_window;
|
||||
auto const& current_group = settings_.group ();
|
||||
if (current_group.size ()) settings_.endGroup ();
|
||||
SettingsGroup alternatives {&settings_, multi_settings_root_group};
|
||||
// get the current configuration name
|
||||
auto const& current_configuration_name = settings_.value (multi_settings_current_name_key, tr (default_string)).toString ();
|
||||
// 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
|
||||
default_menu->menuAction ()->setChecked (true);
|
||||
|
||||
|
@ -423,7 +442,7 @@ void MultiSettings::impl::create_menu_actions (QMainWindow * main_window, QMenu
|
|||
// add all the other 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);
|
||||
|
@ -446,8 +465,7 @@ bool MultiSettings::impl::exit ()
|
|||
return reposition ();
|
||||
}
|
||||
|
||||
QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
||||
QMenu * parent_menu,
|
||||
QMenu * MultiSettings::impl::create_sub_menu (QMenu * parent_menu,
|
||||
QString const& menu_title,
|
||||
QActionGroup * action_group)
|
||||
{
|
||||
|
@ -456,7 +474,7 @@ QMenu * MultiSettings::impl::create_sub_menu (QMainWindow * main_window,
|
|||
sub_menu->menuAction ()->setCheckable (true);
|
||||
|
||||
// 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
|
||||
// there is an issue with depopulating in QMenu::aboutToHide()
|
||||
// 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};
|
||||
sub_menu->addAction (select_action);
|
||||
connect (select_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
||||
select_configuration (main_window, sub_menu);
|
||||
connect (select_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||
select_configuration (sub_menu->title ());
|
||||
});
|
||||
sub_menu->addSeparator ();
|
||||
}
|
||||
|
||||
auto clone_action = new QAction {tr ("&Clone"), this};
|
||||
sub_menu->addAction (clone_action);
|
||||
connect (clone_action, &QAction::triggered, [this, main_window, parent_menu, sub_menu] (bool) {
|
||||
clone_configuration (main_window, parent_menu, sub_menu);
|
||||
connect (clone_action, &QAction::triggered, [this, parent_menu, sub_menu] (bool) {
|
||||
clone_configuration (parent_menu, sub_menu);
|
||||
});
|
||||
|
||||
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};
|
||||
sub_menu->addAction (clone_into_action);
|
||||
connect (clone_into_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
||||
clone_into_configuration (main_window, sub_menu);
|
||||
connect (clone_into_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||
clone_into_configuration (sub_menu);
|
||||
});
|
||||
}
|
||||
if (current_group.size ()) settings_.beginGroup (current_group);
|
||||
auto reset_action = new QAction {tr ("R&eset"), this};
|
||||
sub_menu->addAction (reset_action);
|
||||
connect (reset_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
||||
reset_configuration (main_window, sub_menu);
|
||||
connect (reset_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||
reset_configuration (sub_menu);
|
||||
});
|
||||
|
||||
auto rename_action = new QAction {tr ("&Rename ..."), this};
|
||||
sub_menu->addAction (rename_action);
|
||||
connect (rename_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
||||
rename_configuration (main_window, sub_menu);
|
||||
connect (rename_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||
rename_configuration (sub_menu);
|
||||
});
|
||||
|
||||
if (!is_current)
|
||||
{
|
||||
auto delete_action = new QAction {tr ("&Delete"), this};
|
||||
sub_menu->addAction (delete_action);
|
||||
connect (delete_action, &QAction::triggered, [this, main_window, sub_menu] (bool) {
|
||||
delete_configuration (main_window, sub_menu);
|
||||
connect (delete_action, &QAction::triggered, [this, sub_menu] (bool) {
|
||||
delete_configuration (sub_menu);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -554,11 +572,9 @@ void MultiSettings::impl::load_from (Dictionary const& dictionary, bool add_plac
|
|||
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 (target_name != current_)
|
||||
if (main_window_ && target_name != current_)
|
||||
{
|
||||
{
|
||||
auto const& current_group = settings_.group ();
|
||||
|
@ -573,13 +589,11 @@ void MultiSettings::impl::select_configuration (QMainWindow * main_window, QMenu
|
|||
// and set up the restart
|
||||
current_ = target_name;
|
||||
Q_EMIT parent_->configurationNameChanged (unescape_ampersands (current_));
|
||||
reposition_type_ = RepositionType::save_and_replace;
|
||||
exit_flag_ = false;
|
||||
main_window->close ();
|
||||
restart (RepositionType::save_and_replace);
|
||||
}
|
||||
}
|
||||
|
||||
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 ();
|
||||
if (current_group.size ()) settings_.endGroup ();
|
||||
|
@ -612,12 +626,15 @@ void MultiSettings::impl::clone_configuration (QMainWindow * main_window, QMenu
|
|||
load_from (source_settings);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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 ();
|
||||
if (current_group.size ()) settings_.endGroup ();
|
||||
auto const& target_name = menu->title ();
|
||||
|
@ -638,11 +655,12 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||
}
|
||||
|
||||
// pick a source configuration
|
||||
ExistingNameDialog dialog {sources, main_window};
|
||||
ExistingNameDialog dialog {sources, main_window_};
|
||||
if (sources.size () && (1 == sources.size () || QDialog::Accepted == dialog.exec ()))
|
||||
{
|
||||
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 ("Confirm overwrite of all values for configuration \"%1\" with values from \"%2\"?")
|
||||
.arg (unescape_ampersands (target_name))
|
||||
|
@ -665,9 +683,7 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||
if (target_name == current_)
|
||||
{
|
||||
// restart with new settings
|
||||
reposition_type_ = RepositionType::replace;
|
||||
exit_flag_ = false;
|
||||
main_window->close ();
|
||||
restart (RepositionType::replace);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -682,11 +698,15 @@ void MultiSettings::impl::clone_into_configuration (QMainWindow * main_window, Q
|
|||
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 ();
|
||||
|
||||
if (MessageBox::Yes != MessageBox::query_message (main_window,
|
||||
if (!main_window_
|
||||
|| MessageBox::Yes != MessageBox::query_message (main_window_,
|
||||
tr ("Reset Configuration"),
|
||||
tr ("Confirm reset to default values for configuration \"%1\"?")
|
||||
.arg (unescape_ampersands (target_name))))
|
||||
|
@ -697,10 +717,8 @@ void MultiSettings::impl::reset_configuration (QMainWindow * main_window, QMenu
|
|||
if (target_name == current_)
|
||||
{
|
||||
// restart with default settings
|
||||
reposition_type_ = RepositionType::replace;
|
||||
new_settings_.clear ();
|
||||
exit_flag_ = false;
|
||||
main_window->close ();
|
||||
restart (RepositionType::replace);
|
||||
}
|
||||
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 ();
|
||||
if (current_group.size ()) settings_.endGroup ();
|
||||
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 ();
|
||||
|
||||
// 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 (target_name == current_)
|
||||
|
@ -760,8 +781,9 @@ void MultiSettings::impl::rename_configuration (QMainWindow * main_window, QMenu
|
|||
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 ();
|
||||
|
||||
if (target_name == current_)
|
||||
|
@ -770,7 +792,8 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
|
|||
}
|
||||
else
|
||||
{
|
||||
if (MessageBox::Yes != MessageBox::query_message (main_window,
|
||||
if (!main_window_
|
||||
|| MessageBox::Yes != MessageBox::query_message (main_window_,
|
||||
tr ("Delete Configuration"),
|
||||
tr ("Confirm deletion of configuration \"%1\"?")
|
||||
.arg (unescape_ampersands (target_name))))
|
||||
|
@ -789,3 +812,12 @@ void MultiSettings::impl::delete_configuration (QMainWindow * main_window, QMenu
|
|||
// update the menu
|
||||
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.
|
||||
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.
|
||||
QSettings * settings ();
|
||||
|
||||
|
|
|
@ -116,15 +116,18 @@
|
|||
* Tx Enabled bool
|
||||
* Transmitting bool
|
||||
* Decoding bool
|
||||
* Rx DF qint32
|
||||
* Tx DF qint32
|
||||
* Rx DF quint32
|
||||
* Tx DF quint32
|
||||
* DE call utf8
|
||||
* DE grid utf8
|
||||
* DX grid utf8
|
||||
* Tx Watchdog bool
|
||||
* Sub-mode utf8
|
||||
* 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
|
||||
* changes to allow the server to track the relevant state of each
|
||||
|
@ -133,19 +136,22 @@
|
|||
*
|
||||
* Application start up,
|
||||
* "Enable Tx" button status changes,
|
||||
* Dial frequency changes,
|
||||
* Changes to the "DX Call" field,
|
||||
* Operating mode, sub-mode or fast mode changes,
|
||||
* Transmit mode changed (in dual JT9+JT65 mode),
|
||||
* Changes to the "Rpt" spinner,
|
||||
* After an old decodes replay sequence (see Replay below),
|
||||
* When switching between Tx and Rx mode,
|
||||
* At the start and end of decoding,
|
||||
* When the Rx DF changes,
|
||||
* When the Tx DF changes,
|
||||
* When settings are exited,
|
||||
* When the DX call or grid changes,
|
||||
* When the Tx watchdog is set or reset.
|
||||
* dial frequency changes,
|
||||
* changes to the "DX Call" field,
|
||||
* operating mode, sub-mode or fast mode changes,
|
||||
* transmit mode changed (in dual JT9+JT65 mode),
|
||||
* changes to the "Rpt" spinner,
|
||||
* after an old decodes replay sequence (see Replay below),
|
||||
* when switching between Tx and Rx mode,
|
||||
* at the start and end of decoding,
|
||||
* when the Rx DF changes,
|
||||
* when the Tx DF changes,
|
||||
* when settings are exited,
|
||||
* when the DX call or grid changes,
|
||||
* 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
|
||||
* setting selected in the WSJT-X "Settings->Advanced->Special
|
||||
|
@ -159,6 +165,10 @@
|
|||
* 5 -> FOX
|
||||
* 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
|
||||
* Id (unique key) utf8
|
||||
|
@ -271,11 +281,12 @@
|
|||
* button.
|
||||
*
|
||||
*
|
||||
* Close Out 6 quint32
|
||||
* Close Out/In 6 quint32
|
||||
* Id (unique key) utf8
|
||||
*
|
||||
* 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
|
||||
|
@ -414,6 +425,35 @@
|
|||
* the last instance only instead of all instances of the
|
||||
* specified call be highlighted or have it's highlighting
|
||||
* 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>
|
||||
|
@ -443,6 +483,8 @@ namespace NetworkMessage
|
|||
Location,
|
||||
LoggedADIF,
|
||||
HighlightCallsign,
|
||||
SwitchConfiguration,
|
||||
Configure,
|
||||
maximum_message_type_ // ONLY add new message types
|
||||
// immediately before here
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QDebug>
|
||||
#include <objbase.h>
|
||||
#include <QThread>
|
||||
#include <QEventLoop>
|
||||
|
||||
#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 ()
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "starting");
|
||||
|
@ -182,19 +192,26 @@ int OmniRigTransceiver::do_start ()
|
|||
.arg (readable_params_, 8, 16, QChar ('0'))
|
||||
.arg (writable_params_, 8, 16, QChar ('0'))
|
||||
.arg (rig_number_).toLocal8Bit ());
|
||||
|
||||
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)
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
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 ();
|
||||
int resolution {0};
|
||||
if (f)
|
||||
for (int i = 0; (f == 0) && (i < 5); ++i)
|
||||
{
|
||||
await_notification_with_timeout (1000);
|
||||
f = rig_->GetRxFrequency ();
|
||||
}
|
||||
update_rx_frequency (f);
|
||||
int resolution {0};
|
||||
if (OmniRig::PM_UNKNOWN == rig_->Vfo ()
|
||||
&& (writable_params_ & (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
|
||||
rig_->SetVfo (OmniRig::PM_VFOA);
|
||||
}
|
||||
f = state ().frequency ();
|
||||
if (f % 10) return resolution; // 1Hz resolution
|
||||
auto test_frequency = f - f % 100 + 55;
|
||||
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"));
|
||||
}
|
||||
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)
|
||||
{
|
||||
case -5: resolution = -1; break; // 10Hz truncated
|
||||
|
@ -244,6 +267,11 @@ int OmniRigTransceiver::do_start ()
|
|||
{
|
||||
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)
|
||||
{
|
||||
resolution = 2; // 20Hz rounded
|
||||
|
@ -264,19 +292,9 @@ int OmniRigTransceiver::do_start ()
|
|||
update_rx_frequency (f);
|
||||
return resolution;
|
||||
}
|
||||
}
|
||||
throw_qstring (tr ("OmniRig: Initialization timed out"));
|
||||
return 0; // keep compiler happy
|
||||
}
|
||||
|
||||
void OmniRigTransceiver::do_stop ()
|
||||
{
|
||||
if (offline_timer_)
|
||||
{
|
||||
offline_timer_->stop ();
|
||||
offline_timer_.reset ();
|
||||
}
|
||||
|
||||
QThread::msleep (200); // leave some time for pending
|
||||
// commands at the server end
|
||||
if (port_)
|
||||
|
@ -300,17 +318,6 @@ void OmniRigTransceiver::do_stop ()
|
|||
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)
|
||||
{
|
||||
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 ()
|
||||
{
|
||||
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
|
||||
}
|
||||
|
||||
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_ || rig_->isNull ()) return;
|
||||
readable_params_ = rig_->ReadableParams ();
|
||||
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 (readable_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)
|
||||
{
|
||||
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_ || rig_->isNull ()) return;
|
||||
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 (!offline_timer_->isActive ())
|
||||
{
|
||||
offline_timer_->start (); // give OmniRig time to recover
|
||||
}
|
||||
offline ("Rig went offline");
|
||||
}
|
||||
else
|
||||
{
|
||||
offline_timer_->stop ();
|
||||
update_rx_frequency (rig_->GetRxFrequency ());
|
||||
update_complete ();
|
||||
TRACE_CAT ("OmniRigTransceiver", "OmniRig frequency:" << state ().frequency ());
|
||||
Q_EMIT notified ();
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig params change: params = 0x%1 for rig %2"}
|
||||
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||
TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"}
|
||||
.arg (params, 8, 16, QChar ('0'))
|
||||
.arg (rig_number).toLocal8Bit ()
|
||||
<< "state before:" << state ());
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
if (!rig_ || rig_->isNull ()) return;
|
||||
// starting_ = false;
|
||||
TransceiverState old_state {state ()};
|
||||
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)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOAA");
|
||||
update_split (false);
|
||||
reversed_ = false;
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
|
@ -387,6 +397,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||
}
|
||||
if (params & OmniRig::PM_VFOAB)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOAB");
|
||||
update_split (true);
|
||||
reversed_ = false;
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
|
@ -394,6 +405,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||
}
|
||||
if (params & OmniRig::PM_VFOBA)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOBA");
|
||||
update_split (true);
|
||||
reversed_ = true;
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
|
@ -401,6 +413,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||
}
|
||||
if (params & OmniRig::PM_VFOBB)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOBB");
|
||||
update_split (false);
|
||||
reversed_ = true;
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
|
@ -408,154 +421,195 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
|
|||
}
|
||||
if (params & OmniRig::PM_VFOA)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOA");
|
||||
reversed_ = false;
|
||||
need_frequency = true;
|
||||
}
|
||||
if (params & OmniRig::PM_VFOB)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "VFOB");
|
||||
reversed_ = true;
|
||||
need_frequency = true;
|
||||
}
|
||||
|
||||
if (params & OmniRig::PM_FREQ)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "FREQ");
|
||||
need_frequency = true;
|
||||
}
|
||||
if (params & OmniRig::PM_FREQA)
|
||||
{
|
||||
auto f = rig_->FreqA ();
|
||||
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
|
||||
if (reversed_)
|
||||
{
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
update_other_frequency (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
}
|
||||
if (params & OmniRig::PM_FREQB)
|
||||
{
|
||||
auto f = rig_->FreqB ();
|
||||
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
|
||||
if (reversed_)
|
||||
{
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
update_other_frequency (f);
|
||||
}
|
||||
}
|
||||
if (need_frequency)
|
||||
{
|
||||
if (readable_params_ & OmniRig::PM_FREQA)
|
||||
{
|
||||
auto f = rig_->FreqA ();
|
||||
if (f)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
|
||||
if (reversed_)
|
||||
{
|
||||
update_other_frequency (rig_->FreqA ());
|
||||
update_other_frequency (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_rx_frequency (rig_->FreqA ());
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
}
|
||||
need_frequency = false;
|
||||
}
|
||||
if (readable_params_ & OmniRig::PM_FREQB)
|
||||
{
|
||||
auto f = rig_->FreqB ();
|
||||
if (f)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
|
||||
if (reversed_)
|
||||
{
|
||||
update_rx_frequency (rig_->FreqB ());
|
||||
update_rx_frequency (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_other_frequency (rig_->FreqB ());
|
||||
}
|
||||
need_frequency = false;
|
||||
update_other_frequency (f);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "PITCH");
|
||||
}
|
||||
if (params & OmniRig::PM_RITOFFSET)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "RITOFFSET");
|
||||
}
|
||||
if (params & OmniRig::PM_RIT0)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "RIT0");
|
||||
}
|
||||
if (params & OmniRig::PM_VFOEQUAL)
|
||||
{
|
||||
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_other_frequency (f);
|
||||
update_mode (map_mode (rig_->Mode ()));
|
||||
update_mode (m);
|
||||
}
|
||||
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_rx_frequency (temp);
|
||||
update_rx_frequency (f);
|
||||
update_mode (map_mode (rig_->Mode ()));
|
||||
}
|
||||
if (params & OmniRig::PM_SPLITON)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "SPLITON");
|
||||
update_split (true);
|
||||
}
|
||||
if (params & OmniRig::PM_SPLITOFF)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "SPLITOFF");
|
||||
update_split (false);
|
||||
}
|
||||
if (params & OmniRig::PM_RITON)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "RITON");
|
||||
}
|
||||
if (params & OmniRig::PM_RITOFF)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "RITOFF");
|
||||
}
|
||||
if (params & OmniRig::PM_XITON)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "XITON");
|
||||
}
|
||||
if (params & OmniRig::PM_XITOFF)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "XITOFF");
|
||||
}
|
||||
if (params & OmniRig::PM_RX)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "RX");
|
||||
update_PTT (false);
|
||||
}
|
||||
if (params & OmniRig::PM_TX)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "TX");
|
||||
update_PTT ();
|
||||
}
|
||||
if (params & OmniRig::PM_CW_U)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "CW-R");
|
||||
update_mode (CW_R);
|
||||
}
|
||||
if (params & OmniRig::PM_CW_L)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "CW");
|
||||
update_mode (CW);
|
||||
}
|
||||
if (params & OmniRig::PM_SSB_U)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "USB");
|
||||
update_mode (USB);
|
||||
}
|
||||
if (params & OmniRig::PM_SSB_L)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "LSB");
|
||||
update_mode (LSB);
|
||||
}
|
||||
if (params & OmniRig::PM_DIG_U)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "DATA-U");
|
||||
update_mode (DIG_U);
|
||||
}
|
||||
if (params & OmniRig::PM_DIG_L)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "DATA-L");
|
||||
update_mode (DIG_L);
|
||||
}
|
||||
if (params & OmniRig::PM_AM)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "AM");
|
||||
update_mode (AM);
|
||||
}
|
||||
if (params & OmniRig::PM_FM)
|
||||
{
|
||||
TRACE_CAT ("OmniRigTransceiver", "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 ());
|
||||
}
|
||||
Q_EMIT notified ();
|
||||
}
|
||||
|
||||
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)reply;
|
||||
|
||||
if (!omni_rig_ || omni_rig_->isNull ()) return;
|
||||
if (rig_number_ == rig_number)
|
||||
{
|
||||
if (!rig_ || rig_->isNull ()) return;
|
||||
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
|
||||
<< "with reply" << reply.toString ().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_mode (MODE) override;
|
||||
void do_ptt (bool on) override;
|
||||
void do_sync (bool force_signal, bool no_poll) override;
|
||||
|
||||
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_visible_change ();
|
||||
Q_SLOT void handle_rig_type_change (int rig_number);
|
||||
|
@ -62,7 +63,7 @@ private:
|
|||
QString rig_type_;
|
||||
int readable_params_;
|
||||
int writable_params_;
|
||||
QScopedPointer<QTimer> offline_timer_;
|
||||
// QScopedPointer<QTimer> offline_timer_;
|
||||
bool send_update_signal_;
|
||||
bool reversed_; // some rigs can reverse VFOs
|
||||
};
|
||||
|
|
|
@ -129,29 +129,34 @@ bool PollingTransceiver::do_pre_update ()
|
|||
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
|
||||
// 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.
|
||||
// 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_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_)
|
||||
{
|
||||
--retries_;
|
||||
if (force_signal || state () == next_state_ || !retries_)
|
||||
if (state () == next_state_ || !retries_)
|
||||
{
|
||||
// our client wants a signal regardless
|
||||
// or the expected state has arrived
|
||||
// or there are no more retries
|
||||
// the expected state has arrived or there are no more
|
||||
// retries
|
||||
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
|
||||
// requested a state update regardless of change or state has
|
||||
// here is the normal passive polling path where state has
|
||||
// changed asynchronously
|
||||
force_signal = true;
|
||||
}
|
||||
|
@ -165,17 +170,6 @@ void PollingTransceiver::do_sync (bool force_signal, bool no_poll)
|
|||
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)
|
||||
{
|
||||
message = e.what ();
|
||||
|
|
|
@ -39,11 +39,9 @@ protected:
|
|||
QObject * parent);
|
||||
|
||||
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
|
||||
// in a non-intrusive manner.
|
||||
virtual void poll () = 0;
|
||||
virtual void do_poll () = 0;
|
||||
|
||||
void do_post_start () 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)
|
||||
|
|
|
@ -42,8 +42,8 @@ namespace Radio
|
|||
//
|
||||
// Frequency type formatting
|
||||
//
|
||||
QString UDP_EXPORT frequency_MHz_string (Frequency, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT frequency_MHz_string (FrequencyDelta, QLocale const& = QLocale ());
|
||||
QString UDP_EXPORT frequency_MHz_string (Frequency, int precision = 6, 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 (double, int scale, 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.
|
||||
|
||||
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
|
||||
February 25, 2019
|
||||
---------------------
|
||||
|
|
|
@ -19,10 +19,10 @@ void TransceiverBase::start (unsigned sequence_number) noexcept
|
|||
QString message;
|
||||
try
|
||||
{
|
||||
last_sequence_number_ = sequence_number;
|
||||
may_update u {this, true};
|
||||
shutdown ();
|
||||
startup ();
|
||||
last_sequence_number_ = sequence_number;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||
QString message;
|
||||
try
|
||||
{
|
||||
last_sequence_number_ = sequence_number;
|
||||
may_update u {this, true};
|
||||
bool was_online {requested_.online ()};
|
||||
if (!s.online () && was_online)
|
||||
|
@ -115,7 +116,6 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||
// record what actually changed
|
||||
requested_.ptt (actual_.ptt ());
|
||||
}
|
||||
last_sequence_number_ = sequence_number;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
|
@ -133,10 +133,27 @@ void TransceiverBase::set (TransceiverState const& s,
|
|||
|
||||
void TransceiverBase::startup ()
|
||||
{
|
||||
Q_EMIT resolution (do_start ());
|
||||
do_post_start ();
|
||||
QString message;
|
||||
try
|
||||
{
|
||||
actual_.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 ()
|
||||
|
@ -163,8 +180,8 @@ void TransceiverBase::shutdown ()
|
|||
}
|
||||
do_stop ();
|
||||
do_post_stop ();
|
||||
actual_.online (false);
|
||||
requested_.online (false);
|
||||
actual_ = TransceiverState {};
|
||||
requested_ = TransceiverState {};
|
||||
}
|
||||
|
||||
void TransceiverBase::stop () noexcept
|
||||
|
@ -193,10 +210,13 @@ void TransceiverBase::stop () noexcept
|
|||
}
|
||||
|
||||
void TransceiverBase::update_rx_frequency (Frequency rx)
|
||||
{
|
||||
if (rx)
|
||||
{
|
||||
actual_.frequency (rx);
|
||||
requested_.frequency (rx); // track rig changes
|
||||
}
|
||||
}
|
||||
|
||||
void TransceiverBase::update_other_frequency (Frequency tx)
|
||||
{
|
||||
|
|
|
@ -112,8 +112,6 @@ protected:
|
|||
virtual void do_ptt (bool = true) = 0;
|
||||
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;}
|
||||
|
||||
// sub classes report rig state changes with these methods
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include "ClientWidget.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <QRegExp>
|
||||
#include <QColor>
|
||||
#include <QtWidgets>
|
||||
#include <QAction>
|
||||
#include <QDebug>
|
||||
|
||||
#include "validators/MaidenheadLocatorValidator.hpp"
|
||||
|
||||
|
@ -11,6 +14,9 @@ namespace
|
|||
//QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
|
||||
QRegExp message_alphabet {"[- @A-Za-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)
|
||||
{
|
||||
|
@ -21,9 +27,10 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id)
|
||||
: client_id_ {client_id}
|
||||
, rx_df_ (-1)
|
||||
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id, QObject * parent)
|
||||
: QSortFilterProxyModel {parent}
|
||||
, client_id_ {client_id}
|
||||
, rx_df_ (quint32_max)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -49,7 +56,7 @@ QVariant ClientWidget::IdFilterModel::data (QModelIndex const& proxy_index, int
|
|||
break;
|
||||
|
||||
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};
|
||||
}
|
||||
|
@ -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_)
|
||||
{
|
||||
|
@ -119,26 +126,48 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
|
|||
, QListWidget const * calls_of_interest, QWidget * parent)
|
||||
: QDockWidget {make_title (id, version, revision), parent}
|
||||
, id_ {id}
|
||||
, done_ {false}
|
||||
, 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_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}}
|
||||
, erase_both_action_ {new QAction {tr ("Erase &Both"), this}}
|
||||
, decodes_table_view_ {new QTableView}
|
||||
, beacons_table_view_ {new QTableView}
|
||||
, message_line_edit_ {new QLineEdit}
|
||||
, grid_line_edit_ {new QLineEdit}
|
||||
, decodes_table_view_ {new QTableView {this}}
|
||||
, beacons_table_view_ {new QTableView {this}}
|
||||
, message_line_edit_ {new QLineEdit {this}}
|
||||
, 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}
|
||||
, auto_off_button_ {new QPushButton {tr ("&Auto Off")}}
|
||||
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
|
||||
, de_label_ {new QLabel}
|
||||
, mode_label_ {new QLabel}
|
||||
, fast_mode_ {false}
|
||||
, frequency_label_ {new QLabel}
|
||||
, dx_label_ {new QLabel}
|
||||
, rx_df_label_ {new QLabel}
|
||||
, tx_df_label_ {new QLabel}
|
||||
, report_label_ {new QLabel}
|
||||
, columns_resized_ {false}
|
||||
{
|
||||
// set up widgets
|
||||
|
@ -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_both_action_);
|
||||
|
||||
auto form_layout = new QFormLayout;
|
||||
form_layout->addRow (tr ("Free text:"), message_line_edit_);
|
||||
form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_);
|
||||
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this});
|
||||
message_line_edit_->setValidator (&message_validator);
|
||||
grid_line_edit_->setValidator (&locator_validator);
|
||||
dx_grid_line_edit_->setValidator (&locator_validator);
|
||||
tr_period_spin_box_->setRange (5, 30);
|
||||
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) {
|
||||
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] () {
|
||||
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;
|
||||
auto decodes_layout = new QVBoxLayout {decodes_page};
|
||||
decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
decodes_layout->addWidget (decodes_table_view_);
|
||||
decodes_layout->addLayout (form_layout);
|
||||
decodes_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
decodes_layout_->addWidget (decodes_table_view_);
|
||||
decodes_layout_->addLayout (form_layout_);
|
||||
|
||||
auto beacons_proxy_model = new IdFilterModel {id_};
|
||||
beacons_proxy_model->setSourceModel (beacons_model);
|
||||
beacons_table_view_->setModel (beacons_proxy_model);
|
||||
beacons_proxy_model_.setSourceModel (beacons_model);
|
||||
beacons_table_view_->setModel (&beacons_proxy_model_);
|
||||
beacons_table_view_->verticalHeader ()->hide ();
|
||||
beacons_table_view_->hideColumn (0);
|
||||
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
beacons_table_view_->setContextMenuPolicy (Qt::ActionsContextMenu);
|
||||
beacons_table_view_->insertAction (nullptr, erase_action_);
|
||||
|
||||
auto beacons_page = new QWidget;
|
||||
auto beacons_layout = new QVBoxLayout {beacons_page};
|
||||
beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
beacons_layout->addWidget (beacons_table_view_);
|
||||
beacons_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
beacons_layout_->addWidget (beacons_table_view_);
|
||||
|
||||
decodes_stack_->addWidget (decodes_page);
|
||||
decodes_stack_->addWidget (beacons_page);
|
||||
decodes_stack_->addWidget (decodes_page_);
|
||||
decodes_stack_->addWidget (beacons_page_);
|
||||
|
||||
// stack alternative views
|
||||
auto content_layout = new QVBoxLayout;
|
||||
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
content_layout->addLayout (decodes_stack_);
|
||||
content_layout_->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
content_layout_->addLayout (decodes_stack_);
|
||||
|
||||
// set up controls
|
||||
auto control_button_box = new QDialogButtonBox;
|
||||
control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole);
|
||||
control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole);
|
||||
auto_off_button_ = control_button_box_->addButton (tr ("&Auto Off"), QDialogButtonBox::ActionRole);
|
||||
halt_tx_button_ = control_button_box_->addButton (tr ("&Halt Tx"), 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 */) {
|
||||
Q_EMIT do_halt_tx (id_, true);
|
||||
});
|
||||
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||
Q_EMIT do_halt_tx (id_, false);
|
||||
});
|
||||
content_layout->addWidget (control_button_box);
|
||||
content_layout_->addWidget (control_button_box_);
|
||||
|
||||
// set up status area
|
||||
auto status_bar = new QStatusBar;
|
||||
status_bar->addPermanentWidget (de_label_);
|
||||
status_bar->addPermanentWidget (mode_label_);
|
||||
status_bar->addPermanentWidget (frequency_label_);
|
||||
status_bar->addPermanentWidget (dx_label_);
|
||||
status_bar->addPermanentWidget (rx_df_label_);
|
||||
status_bar->addPermanentWidget (tx_df_label_);
|
||||
status_bar->addPermanentWidget (report_label_);
|
||||
content_layout->addWidget (status_bar);
|
||||
connect (this, &ClientWidget::topLevelChanged, status_bar, &QStatusBar::setSizeGripEnabled);
|
||||
status_bar_->addPermanentWidget (de_label_);
|
||||
status_bar_->addPermanentWidget (tx_mode_label_);
|
||||
status_bar_->addPermanentWidget (frequency_label_);
|
||||
status_bar_->addPermanentWidget (tx_df_label_);
|
||||
status_bar_->addPermanentWidget (report_label_);
|
||||
content_layout_->addWidget (status_bar_);
|
||||
connect (this, &ClientWidget::topLevelChanged, status_bar_, &QStatusBar::setSizeGripEnabled);
|
||||
|
||||
// set up central widget
|
||||
auto content_widget = new QFrame;
|
||||
content_widget->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
||||
content_widget->setLayout (content_layout);
|
||||
setWidget (content_widget);
|
||||
content_widget_->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
||||
setWidget (content_widget_);
|
||||
// setMinimumSize (QSize {550, 0});
|
||||
setFeatures (DockWidgetMovable | DockWidgetFloatable);
|
||||
setAllowedAreas (Qt::BottomDockWidgetArea);
|
||||
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 ()
|
||||
{
|
||||
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
|
||||
, 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
|
||||
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
|
||||
, quint8 special_op_mode)
|
||||
, bool watchdog_timeout, QString const& submode, bool fast_mode
|
||||
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
|
||||
, QString const& configuration_name)
|
||||
{
|
||||
if (id == id_)
|
||||
{
|
||||
fast_mode_ = fast_mode;
|
||||
fast_mode_check_box_->setChecked (fast_mode);
|
||||
decodes_proxy_model_.de_call (de_call);
|
||||
decodes_proxy_model_.rx_df (rx_df);
|
||||
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 (special)
|
||||
: QString {});
|
||||
mode_label_->setText (QString {"Mode: %1%2%3%4"}
|
||||
.arg (mode)
|
||||
.arg (sub_mode)
|
||||
.arg (fast_mode && !mode.contains (QRegularExpression {R"(ISCAT|MSK144)"}) ? "fast" : "")
|
||||
update_line_edit (mode_line_edit_, mode);
|
||||
update_spin_box (frequency_tolerance_spin_box_, frequency_tolerance
|
||||
, quint32_max == frequency_tolerance ? QString {"n/a"} : QString {});
|
||||
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 + ')'));
|
||||
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
||||
dx_label_->setText (dx_call.size () >= 0 ? QString {"DX: %1%2"}.arg (dx_call)
|
||||
.arg (dx_grid.size () ? '(' + dx_grid + ')' : QString {}) : QString {});
|
||||
rx_df_label_->setText (rx_df >= 0 ? QString {"Rx: %1"}.arg (rx_df) : "");
|
||||
tx_df_label_->setText (tx_df >= 0 ? QString {"Tx: %1"}.arg (tx_df) : "");
|
||||
update_line_edit (dx_call_line_edit_, dx_call);
|
||||
update_line_edit (dx_grid_line_edit_, dx_grid);
|
||||
if (rx_df != quint32_max) update_spin_box (rx_df_spin_box_, rx_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);
|
||||
update_dynamic_property (frequency_label_, "transmitting", transmitting);
|
||||
auto_off_button_->setEnabled (tx_enabled);
|
||||
halt_tx_button_->setEnabled (transmitting);
|
||||
update_dynamic_property (mode_label_, "decoding", decoding);
|
||||
update_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#ifndef WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||
#define WSJTX_UDP_CLIENT_WIDGET_MODEL_HPP__
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QObject>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "MessageServer.hpp"
|
||||
|
||||
|
@ -13,6 +13,20 @@ class QAbstractItemModel;
|
|||
class QModelIndex;
|
||||
class QColor;
|
||||
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;
|
||||
|
||||
|
@ -25,16 +39,18 @@ public:
|
|||
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
||||
, QString const& id, QString const& version, QString const& revision
|
||||
, QListWidget const * calls_of_interest, QWidget * parent = nullptr);
|
||||
void dispose ();
|
||||
~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
|
||||
, 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
|
||||
, 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
|
||||
, float delta_time, quint32 delta_frequency, QString const& mode
|
||||
, QString const& message, bool low_confidence, bool off_air);
|
||||
|
@ -45,6 +61,7 @@ public:
|
|||
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_close (QString const& id);
|
||||
Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier);
|
||||
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only);
|
||||
Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
|
||||
|
@ -52,30 +69,39 @@ public:
|
|||
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
|
||||
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
|
||||
, 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:
|
||||
QString id_;
|
||||
QListWidget const * calls_of_interest_;
|
||||
class IdFilterModel final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
IdFilterModel (QString const& client_id);
|
||||
IdFilterModel (QString const& client_id, QObject * = nullptr);
|
||||
|
||||
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;
|
||||
|
||||
protected:
|
||||
private:
|
||||
bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override;
|
||||
|
||||
private:
|
||||
QString client_id_;
|
||||
QString call_;
|
||||
QRegularExpression base_call_re_;
|
||||
int rx_df_;
|
||||
} decodes_proxy_model_;
|
||||
quint32 rx_df_;
|
||||
};
|
||||
|
||||
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_rx_frequency_action_;
|
||||
QAction * erase_both_action_;
|
||||
|
@ -83,17 +109,39 @@ private:
|
|||
QTableView * beacons_table_view_;
|
||||
QLineEdit * message_line_edit_;
|
||||
QLineEdit * grid_line_edit_;
|
||||
QStackedLayout * decodes_stack_;
|
||||
QAbstractButton * generate_messages_push_button_;
|
||||
QAbstractButton * auto_off_button_;
|
||||
QAbstractButton * halt_tx_button_;
|
||||
QLabel * de_label_;
|
||||
QLabel * mode_label_;
|
||||
bool fast_mode_;
|
||||
QLabel * frequency_label_;
|
||||
QLabel * dx_label_;
|
||||
QLabel * rx_df_label_;
|
||||
QLabel * tx_df_label_;
|
||||
QLabel * report_label_;
|
||||
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_;
|
||||
};
|
||||
|
||||
|
|
|
@ -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::decodes_cleared, dock, &ClientWidget::decodes_cleared);
|
||||
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_halt_tx, server_, &MessageServer::halt_tx);
|
||||
connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text);
|
||||
connect (dock, &ClientWidget::location, server_, &MessageServer::location);
|
||||
connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible);
|
||||
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
|
||||
connect (dock, &ClientWidget::switch_configuration, server_, &MessageServer::switch_configuration);
|
||||
connect (dock, &ClientWidget::configure, server_, &MessageServer::configure);
|
||||
dock_widgets_[id] = dock;
|
||||
server_->replay (id); // request decodes and status
|
||||
}
|
||||
|
@ -265,7 +268,7 @@ void MessageAggregatorMainWindow::remove_client (QString const& id)
|
|||
auto iter = dock_widgets_.find (id);
|
||||
if (iter != std::end (dock_widgets_))
|
||||
{
|
||||
(*iter)->close ();
|
||||
(*iter)->dispose ();
|
||||
dock_widgets_.erase (iter);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,8 @@ public:
|
|||
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
|
||||
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
|
||||
, bool /* watchdog_timeout */, QString const& sub_mode, bool /*fast_mode*/
|
||||
, quint8 /*special_op_mode*/)
|
||||
, quint8 /*special_op_mode*/, quint32 /*frequency_tolerance*/, quint32 /*tr_period*/
|
||||
, QString const& /*configuration_name*/)
|
||||
{
|
||||
if (id == id_)
|
||||
{
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace
|
|||
class Dialog
|
||||
: public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using BandList = QList<QString>;
|
||||
|
||||
|
@ -69,6 +71,8 @@ private:
|
|||
static int constexpr band_index_role {Qt::UserRole};
|
||||
};
|
||||
|
||||
#include "WSPRBandHopping.moc"
|
||||
|
||||
Dialog::Dialog (QSettings * settings, Configuration const * configuration, BandList const * WSPR_bands
|
||||
, QBitArray * bands, int * gray_line_duration, QWidget * parent)
|
||||
: QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint}
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#ifdef __cplusplus
|
||||
#include <cstdbool>
|
||||
extern "C" {
|
||||
#else
|
||||
#endif
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ set (UG_SRCS
|
|||
tutorial-example1.adoc
|
||||
tutorial-example2.adoc
|
||||
tutorial-example3.adoc
|
||||
tutorial-example4.adoc
|
||||
tutorial-main-window.adoc
|
||||
tutorial-wide-graph-settings.adoc
|
||||
utilities.adoc
|
||||
|
@ -77,6 +78,8 @@ set (UG_IMGS
|
|||
images/FreqCal_Graph.png
|
||||
images/FreqCal_Results.png
|
||||
images/freemsg.png
|
||||
images/ft4_decodes.png
|
||||
images/ft4_waterfall.png
|
||||
images/ft8_decodes.png
|
||||
images/FT8_waterfall.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]
|
||||
: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]
|
||||
:win64_openssl: https://slproweb.com/download/Win64OpenSSL_Light-1_0_2r.exe[Win64 OpenSSL Lite Package]
|
||||
:writelog: https://writelog.com/[Writelog]
|
||||
:wsjt_yahoo_group: https://groups.yahoo.com/neo/groups/wsjtgroup/info[WSJT Group]
|
||||
: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]
|
||||
:svn: http://subversion.apache.org/packages.html#windows[Subversion]
|
||||
: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_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]
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
|
@ -1,11 +1,12 @@
|
|||
// Status=review
|
||||
|
||||
Download and execute the package file {win32}, following these
|
||||
instructions:
|
||||
Download and execute the package file {win32} (WinXP, Vista, Win 7,
|
||||
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 `
|
||||
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
|
||||
installation directory and its subdirectories.
|
||||
|
@ -29,26 +30,31 @@ TIP: Your computer may be configured so that this directory is
|
|||
|
||||
[[OPENSSL]]
|
||||
|
||||
* image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error, role="right"]
|
||||
From this version onward _WSJT-X_ requires the _OpenSSL_ libraries
|
||||
to be installed. Suitable libraries may already be installed on your
|
||||
* image:LoTW_TLS_error.png[_WSJT-X_ LoTW download TLS error,
|
||||
role="right"] _WSJT-X_ requires the _OpenSSL_ libraries to be
|
||||
installed. Suitable libraries may already be installed on your
|
||||
system, if they are not you will see this error shortly after
|
||||
startup. To fix this you need to install the _OpenSSL_ libraries.
|
||||
|
||||
** You can download a suitable _OpenSSL_ package for from
|
||||
{win_openssl_packages}, you need the latest *Win32 v1.0.2 Lite*
|
||||
version (Note it is the Win32 package even if you are using a
|
||||
64-bit Windows operating system) which at the time of writing was
|
||||
{win32_openssl}.
|
||||
{win_openssl_packages}, you need the latest *Windows v1.0.2 Lite*
|
||||
version. For the 32-bit _WSJT-X_ build use the Win32 version of the
|
||||
_OpenSSL_ libraries, for the 64-bit _WSJT-X_ use the Win64 version
|
||||
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
|
||||
option to copy the _OpenSSL_ DLLs to the Windows system directory
|
||||
(this is important). +
|
||||
option to copy the _OpenSSL_ DLLs to the Windows system
|
||||
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
|
||||
_OpenSSL_ libraries then you also need to install the
|
||||
{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
|
||||
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
|
||||
_WSJT_.
|
||||
|
||||
_WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers nine different
|
||||
protocols or modes: *FT8*, *JT4*, *JT9*, *JT65*, *QRA64*, *ISCAT*,
|
||||
*MSK144*, *WSPR*, and *Echo*. The first five are designed for making
|
||||
reliable QSOs under extreme weak-signal conditions. They use nearly
|
||||
_WSJT-X_ Version {VERSION_MAJOR}.{VERSION_MINOR} offers ten different
|
||||
protocols or modes: *FT4*, *FT8*, *JT4*, *JT9*, *JT65*, *QRA64*,
|
||||
*ISCAT*, *MSK144*, *WSPR*, and *Echo*. The first six are designed for
|
||||
making reliable QSOs under weak-signal conditions. They use nearly
|
||||
identical message structure and source encoding. JT65 and QRA64 were
|
||||
designed for EME ("`moonbounce`") on the VHF/UHF bands and have also
|
||||
proven very effective for worldwide QRP communication on the HF bands.
|
||||
|
@ -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
|
||||
transmissions by each station, one sending in odd UTC minutes and the
|
||||
other even. FT8 is operationally similar but four times faster
|
||||
(15-second T/R sequences) and less sensitive by a few dB. On the HF
|
||||
bands, world-wide QSOs are possible with any of these modes using
|
||||
power levels of a few watts (or even milliwatts) and compromise
|
||||
antennas. On VHF bands and higher, QSOs are possible (by EME and
|
||||
other propagation types) at signal levels 10 to 15 dB below those
|
||||
required for CW.
|
||||
(15-second T/R sequences) and less sensitive by a few dB. FT4 is
|
||||
faster still (7.5 s T/R sequences) and especially well suited for
|
||||
radio contesting. On the HF bands, world-wide QSOs are possible with
|
||||
any of these modes using power levels of a few watts (or even
|
||||
milliwatts) and compromise antennas. On VHF bands and higher, QSOs
|
||||
are possible (by EME and other propagation types) at signal levels 10
|
||||
to 15 dB below those required for CW.
|
||||
|
||||
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`"
|
||||
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
|
||||
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
|
||||
feedback. For example, version 1.9.0-rc1, 1.9.0-rc2, etc., would
|
||||
be beta releases leading up to the final release of v1.9.0.
|
||||
feedback. For example, version 2.1.0-rc1, 2.1.0-rc2, etc., would
|
||||
be beta releases leading up to the final release of v2.1.0.
|
||||
Release candidates should be used _only_ during a short testing
|
||||
period. They carry an implied obligation to provide feedback to the
|
||||
program development group. Candidate releases should not be used on
|
||||
|
|
|
@ -1,40 +1,11 @@
|
|||
=== New in Version {VERSION}
|
||||
|
||||
For quick reference, here's a short list of features and capabilities
|
||||
added to _WSJT-X_ since Version 1.9.1:
|
||||
|
||||
- New FT8 and MSK144 protocols with 77-bit payloads permit these enhancements:
|
||||
|
||||
* Optimized contest messages for NA VHF, EU VHF, Field Day, RTTY Roundup
|
||||
|
||||
* 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.
|
||||
|
||||
The most important feature added to _WSJT-X_ since Version 2.0.1 is
|
||||
the new *FT4 protocol*, designed especially for radio contesting. It
|
||||
has T/R sequence length 7.5 s, bandwidth 80 Hz, and threshold
|
||||
sensitivity -17.5 dB. Version 2.1.0 also has improvements to FT8
|
||||
waveform generation, contest logging, rig control, the user interface,
|
||||
and accessibility, as well as a number of bug fixes.
|
||||
|
||||
=== 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
|
||||
QSOs into a fixed 72-bit length.
|
||||
|
||||
The information payload for FT8 and MSK144 contains 77 bits. The 5
|
||||
additional bits are used to flag special message types used for FT8
|
||||
DXpedition Mode, contesting, nonstandard callsigns, and a few other
|
||||
special types.
|
||||
The information payload for FT4, FT8, and MSK144 contains 77 bits.
|
||||
The 5 new bits added to the original 72 are used to flag special
|
||||
message types signifying special message types used for FT8 DXpedition
|
||||
Mode, contesting, nonstandard callsigns, and a few other
|
||||
possibilities.
|
||||
|
||||
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
|
||||
|
@ -67,18 +68,29 @@ _WSJT-X_ modes have continuous phase and constant envelope.
|
|||
[[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]]
|
||||
==== FT8
|
||||
|
||||
Forward error correction (FEC) in FT8 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 7×7 Costas arrays
|
||||
at the beginning, middle, and end of each transmission. Modulation is
|
||||
8-tone frequency-shift keying (8-FSK) at 12000/1920 = 6.25 baud. Each
|
||||
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.
|
||||
FT8 uses the same LDPC (174,91) code as FT4. Modulation is 8-tone
|
||||
frequency-shift keying (8-GFSK) at 12000/1920 = 6.25 baud.
|
||||
Synchronization uses 7×7 Costas arrays at the beginning, middle, and
|
||||
end of each transmission. Transmitted symbols carry 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]]
|
||||
==== 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)
|
||||
|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
|
||||
|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
|
||||
|
@ -246,6 +259,7 @@ comparable to tone spacing.
|
|||
[width="50%",cols="h,3*^",frame=topbot,options="header"]
|
||||
|=====================================
|
||||
|Mode |Tone Spacing |BW (Hz)|S/N (dB)
|
||||
|FT4 |20.8333 | 83.3 |-17.5
|
||||
|FT8 |6.25 | 50.0 |-21
|
||||
|JT4A |4.375| 17.5 |-23
|
||||
|JT4B |8.75 | 30.6 |-22
|
||||
|
|
|
@ -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
|
||||
include::tutorial-example3.adoc[]
|
||||
|
||||
[[TUT_EX4]]
|
||||
=== FT4
|
||||
include::tutorial-example4.adoc[]
|
||||
|
||||
[[MAKE_QSOS]]
|
||||
== Making QSOs
|
||||
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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 += \
|
||||
item_delegates/ForeignKeyDelegate.cpp \
|
||||
item_delegates/FrequencyItemDelegate.cpp \
|
||||
item_delegates/FrequencyDelegate.cpp \
|
||||
item_delegates/FrequencyDeltaDelegate.cpp \
|
||||
item_delegates/CallsignDelegate.cpp \
|
||||
item_delegates/MaidenheadLocatorItemDelegate.cpp
|
||||
|
||||
HEADERS += \
|
||||
item_delegates/ForeignKeyDelegate.hpp \
|
||||
item_delegates/FrequencyItemDelegate.hpp \
|
||||
item_delegates/FrequencyDelegate.hpp \
|
||||
item_delegates/FrequencyDeltaDelegate.hpp \
|
||||
item_delegates/CallsignDelegate.hpp \
|
||||
item_delegates/MaidenheadLocatorDelegate.hpp \
|
||||
item_delegates/DateTimeAsSecsSinceEpochDelegate.hpp
|
||||
item_delegates/MaidenheadLocatorDelegate.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
|
||||
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
|
||||
5 ... tbd
|
||||
5 TU; W9XYZ K1ABC R-07 FN 1 28 28 1 7 9 74 WWROF contest ?
|
||||
6 ... 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.
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -72,7 +72,18 @@ CQ W9XYZ EN37
|
|||
W9XYZ <YW18FIFA> R-09
|
||||
YW18FIFA <W9XYZ> RRR
|
||||
<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
|
||||
CQ YW18FIFA
|
||||
|
|
|
@ -172,6 +172,10 @@ subroutine pack77(msg0,i3,n3,c77)
|
|||
call pack77_4(nwords,w,i3,n3,c77)
|
||||
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
|
||||
800 i3=0
|
||||
n3=0
|
||||
|
@ -204,6 +208,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
|
|||
character*6 cexch,grid6
|
||||
character*4 grid4,cserial
|
||||
character*3 csec(NSEC)
|
||||
character*2 cfield
|
||||
character*38 c
|
||||
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
|
||||
logical unpk28_success,unpk77_success
|
||||
|
@ -491,8 +496,31 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
|
|||
else
|
||||
msg='CQ '//trim(call_2)
|
||||
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
|
||||
if(msg(1:4).eq.'CQ <') unpk77_success=.false.
|
||||
! if(msg(1:4).eq.'CQ <') unpk77_success=.false.
|
||||
|
||||
return
|
||||
end subroutine unpack77
|
||||
|
@ -1040,12 +1068,11 @@ subroutine pack77_3(nwords,w,i3,n3,c77)
|
|||
call chkcall(w(i1+1),bcall_2,ok2)
|
||||
if(.not.ok1 .or. .not.ok2) go to 900
|
||||
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. &
|
||||
crpt(3:3).eq.'9') then
|
||||
nserial=0
|
||||
read(w(nwords),*,err=1) nserial
|
||||
!1 i3=3
|
||||
! n3=0
|
||||
endif
|
||||
1 mult=' '
|
||||
imult=-1
|
||||
|
@ -1150,6 +1177,60 @@ subroutine pack77_4(nwords,w,i3,n3,c77)
|
|||
900 return
|
||||
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)
|
||||
|
||||
character*13 c13,w
|
||||
|
|
|
@ -1,16 +1,56 @@
|
|||
subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
|
||||
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &
|
||||
RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1,width2,bTx, &
|
||||
AzElFileName,jpleph)
|
||||
module astro_module
|
||||
use, intrinsic :: iso_c_binding, only : c_int, c_double, c_bool, c_char, c_ptr, c_size_t, c_f_pointer
|
||||
implicit none
|
||||
|
||||
implicit real*8 (a-h,o-z)
|
||||
character*6 mygrid,hisgrid,c1*1
|
||||
character*6 AzElFileName*(*),jpleph*(*)
|
||||
private
|
||||
public :: astrosub
|
||||
|
||||
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
|
||||
logical*1 bTx
|
||||
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, &
|
||||
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &
|
||||
|
@ -53,3 +93,5 @@ subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
|
|||
|
||||
999 return
|
||||
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*6 MyGrid,HisGrid,mygrid0,hisgrid0
|
||||
character(len=*) :: MyGrid,HisGrid
|
||||
character*6 mygrid0,hisgrid0
|
||||
real*8 utch,utch0
|
||||
logical HotABetter,IamEast
|
||||
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/
|
||||
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
|
||||
naz=0
|
||||
nel=0
|
||||
|
|
|
@ -133,8 +133,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||
n30fox(j)=n
|
||||
m=n30max-n
|
||||
if(len(trim(g2fox(j))).eq.4) then
|
||||
call azdist(mygrid,g2fox(j),0.d0,nAz,nEl,nDmiles,nDkm, &
|
||||
nHotAz,nHotABetter)
|
||||
call azdist(mygrid,g2fox(j)//' ',0.d0,nAz,nEl,nDmiles, &
|
||||
nDkm,nHotAz,nHotABetter)
|
||||
else
|
||||
nDkm=9999
|
||||
endif
|
||||
|
@ -152,8 +152,8 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
|
|||
if(params%nmode.eq.5) then
|
||||
call timer('decft4 ',0)
|
||||
call my_ft4%decode(ft4_decoded,id2,params%nQSOProgress,params%nfqso, &
|
||||
params%nutc,params%nfa,params%nfb,params%ndepth,ncontest, &
|
||||
mycall,hiscall)
|
||||
params%nutc,params%nfa,params%nfb,params%ndepth, &
|
||||
logical(params%lapcqonly),ncontest,mycall,hiscall)
|
||||
call timer('decft4 ',1)
|
||||
go to 800
|
||||
endif
|
||||
|
@ -517,7 +517,7 @@ contains
|
|||
decoded0=decoded
|
||||
|
||||
annot=' '
|
||||
if(ncontest.eq.0 .and. nap.ne.0) then
|
||||
if(nap.ne.0) then
|
||||
write(annot,'(a1,i1)') 'a',nap
|
||||
if(qual.lt.0.17) decoded0(37:37)='?'
|
||||
endif
|
||||
|
@ -598,15 +598,15 @@ contains
|
|||
decoded0=decoded
|
||||
|
||||
annot=' '
|
||||
if(ncontest.eq.0 .and. nap.ne.0) then
|
||||
if(nap.ne.0) then
|
||||
write(annot,'(a1,i1)') 'a',nap
|
||||
if(qual.lt.0.17) decoded0(37:37)='?'
|
||||
endif
|
||||
|
||||
write(*,1001) params%nutc,snr,dt,nint(freq),decoded0,annot
|
||||
1001 format(i6.6,i4,f5.1,i5,' ~ ',1x,a37,1x,a2)
|
||||
1001 format(i6.6,i4,f5.1,i5,' + ',1x,a37,1x,a2)
|
||||
write(13,1002) params%nutc,nint(sync),snr,dt,freq,0,decoded0
|
||||
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FT8')
|
||||
1002 format(i6.6,i4,i5,f6.1,f8.0,i4,3x,a37,' FT4')
|
||||
|
||||
call flush(6)
|
||||
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)
|
||||
|
||||
parameter (NMAX=30*12000)
|
||||
|
@ -6,6 +6,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||
integer*2 id2a(NMAX)
|
||||
integer*2 id2b(NMAX)
|
||||
integer narg(0:14)
|
||||
double precision trperiod
|
||||
real dat(30*12000)
|
||||
complex cdat(262145),cdat2(262145)
|
||||
real psavg(450)
|
||||
|
@ -41,7 +42,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||
nhashcalls=narg(12)
|
||||
|
||||
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(nmode.eq.102) then
|
||||
|
@ -53,7 +54,7 @@ subroutine fast_decode(id2,narg,ntrperiod,line,mycall_12, &
|
|||
cdat2=cdat
|
||||
ndat=ndat0
|
||||
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
|
||||
ndat=min(ndat,30*11025)
|
||||
call ana932(dat,ndat,cdat,npts) !Make downsampled analytic signal
|
||||
|
|
|
@ -41,6 +41,7 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||
endif
|
||||
smax=0.
|
||||
s=0.
|
||||
ipk=-99
|
||||
do i=ia,ib
|
||||
s(i)=real(cx(i))**2 + aimag(cx(i))**2
|
||||
if(s(i).gt.smax) then
|
||||
|
@ -49,6 +50,7 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||
endif
|
||||
enddo
|
||||
|
||||
if(ipk.ge.1) then
|
||||
call peakup(s(ipk-1),s(ipk),s(ipk+1),dx)
|
||||
fpeak=df * (ipk+dx)
|
||||
ap=(fpeak/fs+1.0/(2.0*NFFT))
|
||||
|
@ -67,6 +69,12 @@ subroutine freqcal(id2,k,nkhz,noffset,ntol,line)
|
|||
ave=xsum/nsum
|
||||
snr=db(smax/ave)
|
||||
pave=db(ave) + 8.0
|
||||
else
|
||||
snr=-99.9
|
||||
pave=-99.9
|
||||
fpeak=-99.9
|
||||
ferr=-99.9
|
||||
endif
|
||||
cflag=' '
|
||||
if(snr.lt.20.0) cflag='*'
|
||||
n=n+1
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
! Input: i*2 data in iwave() at sample rate 12000 Hz
|
||||
! Output: Complex data in c(), sampled at 1200 Hz
|
||||
subroutine ft4_downsample(dd,newdata,f0,c)
|
||||
|
||||
include 'ft4_params.f90'
|
||||
parameter (NFFT2=NMAX/16)
|
||||
integer*2 iwave(NMAX)
|
||||
parameter (NFFT2=NMAX/NDOWN)
|
||||
real dd(NMAX)
|
||||
complex c(0:NMAX/NDOWN-1)
|
||||
complex c1(0:NFFT2-1)
|
||||
complex cx(0:NMAX/2)
|
||||
|
@ -33,15 +30,15 @@ subroutine ft4_downsample(iwave,newdata,f0,c)
|
|||
endif
|
||||
|
||||
if(newdata) then
|
||||
x=iwave
|
||||
x=dd
|
||||
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
|
||||
endif
|
||||
i0=nint(f0/df)
|
||||
c1=0.
|
||||
c1(0)=cx(i0)
|
||||
if(i0.ge.0 .and. i0.le.NMAX/2) c1(0)=cx(i0)
|
||||
do i=1,NFFT2/2
|
||||
if(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(i)=cx(i0+i)
|
||||
if(i0-i.ge.0 .and. i0-i.le.NMAX/2) c1(NFFT2-i)=cx(i0-i)
|
||||
enddo
|
||||
c1=c1*window/NFFT2
|
||||
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
! 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 (ND=87) !Data symbols
|
||||
parameter (NS=16) !Sync symbols
|
||||
parameter (NN=NS+ND) !Sync and data symbols (103)
|
||||
parameter (NN2=NS+ND+2) !Total channel symbols (105)
|
||||
parameter (NSPS=512) !Samples per symbol at 12000 S/s
|
||||
parameter (NZ=NSPS*NN) !Sync and Data samples (52736)
|
||||
parameter (NZ2=NSPS*NN2) !Total samples in shaped waveform (53760)
|
||||
parameter (NMAX=5*12000) !Samples in iwave (60,000)
|
||||
parameter (NFFT1=2048, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
||||
parameter (NSPS=576) !Samples per symbol at 12000 S/s
|
||||
parameter (NZ=NSPS*NN) !Sync and Data samples (59328)
|
||||
parameter (NZ2=NSPS*NN2) !Total samples in shaped waveform (60480)
|
||||
parameter (NMAX=21*3456) !Samples in iwave (72576)
|
||||
parameter (NFFT1=2304, NH1=NFFT1/2) !Length of FFTs for symbol spectra
|
||||
parameter (NSTEP=NSPS) !Coarse time-sync step size
|
||||
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
|
||||
include 'ft4_params.f90' !Set various constants
|
||||
parameter (NWAVE=NN*NSPS)
|
||||
parameter (NZZ=18*3456) !62208
|
||||
parameter (NZZ=21*3456) !72576
|
||||
type(hdr) h !Header for .wav file
|
||||
character arg*12,fname*17
|
||||
character msg37*37,msgsent37*37
|
||||
|
@ -51,19 +51,18 @@ program ft4sim
|
|||
hmod=1.0 !Modulation index (0.5 is MSK, 1.0 is FSK)
|
||||
tt=NSPS*dt !Duration of symbols (s)
|
||||
baud=1.0/tt !Keying rate (baud)
|
||||
txt=NZ*dt !Transmission length (s)
|
||||
txt=NZ2*dt !Transmission length (s)
|
||||
|
||||
bandwidth_ratio=2500.0/(fs/2.0)
|
||||
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
||||
if(snrdb.gt.90.0) sig=1.0
|
||||
txt=NN*NSPS/12000.0
|
||||
|
||||
! Source-encode, then get itone()
|
||||
i3=-1
|
||||
n3=-1
|
||||
call pack77(msg37,i3,n3,c77)
|
||||
read(c77,'(77i1)') msgbits
|
||||
call genft4(msg37,0,msgsent37,itone)
|
||||
call genft4(msg37,0,msgsent37,msgbits,itone)
|
||||
write(*,*)
|
||||
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
|
||||
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+2)*NSPS:)=0.
|
||||
|
||||
k=nint((xdt+0.5)/dt)
|
||||
k=nint((xdt+0.5)/dt)-NSPS
|
||||
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
|
||||
c=c0
|
||||
|
|
|
@ -6,17 +6,17 @@ program ft4sim_mult
|
|||
use packjt77
|
||||
include 'ft4_params.f90' !FT4 protocol constants
|
||||
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
|
||||
character arg*12,fname*17,cjunk*4
|
||||
character msg37*37,msgsent37*37,c77*77
|
||||
complex cwave0((NN+2)*NSPS)
|
||||
real wave0((NN+2)*NSPS)
|
||||
real wave(NZZ)
|
||||
real tmp(NZZ)
|
||||
integer itone(NN)
|
||||
integer*1 msgbits(77)
|
||||
integer*2 iwave(NZZ) !Generated full-length waveform
|
||||
integer icos4(4)
|
||||
data icos4/0,1,3,2/
|
||||
|
||||
! Get command-line argument(s)
|
||||
nargs=iargc()
|
||||
|
@ -53,17 +53,18 @@ program ft4sim_mult
|
|||
read(10,1003,end=100) cjunk,isnr,xdt0,ifreq,msg37
|
||||
1003 format(a4,30x,i3,f5.1,i5,1x,a37)
|
||||
if(cjunk.eq.'File') go to 100
|
||||
if(isnr.lt.-16) isnr=-16
|
||||
f0=ifreq*93.75/50.0
|
||||
if(isnr.lt.-17) isnr=-17
|
||||
f0=ifreq*960.0/576.0
|
||||
call random_number(r)
|
||||
xdt=r-0.5
|
||||
! Source-encode, then get itone()
|
||||
i3=-1
|
||||
n3=-1
|
||||
call pack77(msg37,i3,n3,c77)
|
||||
call genft4(msg37,0,msgsent37,itone)
|
||||
call genft4(msg37,0,msgsent37,msgbits,itone)
|
||||
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)
|
||||
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 pulse(6144) !512*4*3
|
||||
real dphi(0:240000-1)
|
||||
complex cwave(nwave)
|
||||
real pulse(6912) !576*4*3
|
||||
real dphi(0:250000-1)
|
||||
integer itone(nsym)
|
||||
logical first
|
||||
data first/.true./
|
||||
|
@ -12,7 +13,7 @@ subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
|
|||
twopi=8.0*atan(1.0)
|
||||
dt=1.0/fsample
|
||||
hmod=1.0
|
||||
! Compute the frequency-smoothing pulse
|
||||
! Compute the smoothed frequency-deviation pulse
|
||||
do i=1,3*nsps
|
||||
tt=(i-1.5*nsps)/real(nsps)
|
||||
pulse(i)=gfsk_pulse(1.0,tt)
|
||||
|
@ -34,19 +35,32 @@ subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
|
|||
phi=0.0
|
||||
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
||||
wave=0.
|
||||
if(icmplx.eq.1) cwave=0.
|
||||
k=0
|
||||
do j=0,nwave-1
|
||||
k=k+1
|
||||
if(icmplx.eq.0) then
|
||||
wave(k)=sin(phi)
|
||||
else
|
||||
cwave(k)=cmplx(cos(phi),sin(phi))
|
||||
endif
|
||||
phi=mod(phi+dphi(j),twopi)
|
||||
enddo
|
||||
|
||||
! Compute the ramp-up and ramp-down symbols
|
||||
if(icmplx.eq.0) then
|
||||
wave(1:nsps)=wave(1:nsps) * &
|
||||
(1.0-cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
|
||||
k1=(nsym+1)*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
|
||||
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
|
||||
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
|
||||
! Input:
|
||||
|
@ -11,7 +11,7 @@ subroutine genft4(msg0,ichk,msgsent,i4tone)
|
|||
! s16 + 87symbols + 2 ramp up/down = 105 total channel symbols
|
||||
! 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
|
||||
|
||||
|
@ -52,8 +52,16 @@ subroutine genft4(msg0,ichk,msgsent,i4tone)
|
|||
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
|
||||
|
||||
if(ichk.eq.1) go to 999
|
||||
read(c77,"(77i1)") msgbits
|
||||
msgbits=mod(msgbits+rvec,2)
|
||||
read(c77,'(77i1)',err=1) msgbits
|
||||
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)
|
||||
|
||||
! Grayscale mapping:
|
||||
|
|
|
@ -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)
|
||||
|
||||
include 'ft4_params.f90'
|
||||
|
@ -8,9 +8,8 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||
real x(NFFT1)
|
||||
real window(NFFT1)
|
||||
complex cx(0:NH1)
|
||||
real candidate(3,maxcand)
|
||||
integer*2 id(NMAX)
|
||||
integer indx(NH1)
|
||||
real candidate(2,maxcand),candidatet(2,maxcand)
|
||||
real dd(NMAX)
|
||||
integer ipk(1)
|
||||
equivalence (x,cx)
|
||||
logical first
|
||||
|
@ -26,42 +25,35 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||
|
||||
! 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+NFFT1-1
|
||||
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
|
||||
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
|
||||
savg=savg/NHSYM
|
||||
savsm=0.
|
||||
do i=8,NH1-7
|
||||
savsm(i)=sum(savg(i-7:i+7))/15.
|
||||
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
|
||||
if(savsm(i).ge.savsm(i-1) .and. savsm(i).ge.savsm(i+1) .and. &
|
||||
savsm(i).ge.syncmin) then
|
||||
|
@ -69,18 +61,26 @@ subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
|||
del=0.
|
||||
if(den.ne.0.0) del=0.5*(savsm(i-1)-savsm(i+1))/den
|
||||
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
|
||||
ncand=ncand+1
|
||||
if(ncand.gt.maxcand) then
|
||||
ncand=maxcand
|
||||
exit
|
||||
endif
|
||||
candidate(1,ncand)=fpeak
|
||||
candidate(2,ncand)=-99.99
|
||||
candidate(3,ncand)=speak
|
||||
candidatet(1,ncand)=fpeak
|
||||
candidatet(2,ncand)=speak
|
||||
if(ncand.eq.maxcand) exit
|
||||
endif
|
||||
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
|
||||
end subroutine getcandidates4
|
||||
|
|
|
@ -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 ctwk(2*NSS)
|
||||
complex z1,z2,z3,z4
|
||||
complex zz1,zz2,zz3,zz4
|
||||
logical first
|
||||
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
|
||||
data icos4a/0,1,3,2/
|
||||
|
@ -19,7 +18,7 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||
data first/.true./
|
||||
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
|
||||
twopi=8.0*atan(1.0)
|
||||
|
@ -60,7 +59,17 @@ subroutine sync4d(cd0,i0,ctwk,itwk,sync)
|
|||
z4=0.
|
||||
|
||||
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(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(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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
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 packjt77
|
||||
include 'ft4/ft4_params.f90'
|
||||
class(ft4_decoder), intent(inout) :: this
|
||||
procedure(ft4_decode_callback) :: callback
|
||||
parameter (NSS=NSPS/NDOWN)
|
||||
parameter (NZZ=18*3456)
|
||||
character message*37,msgsent*37,msg0*37
|
||||
parameter (NSS=NSPS/NDOWN,NDMAX=NMAX/NDOWN)
|
||||
character message*37,msgsent*37
|
||||
character c77*77
|
||||
character*37 decodes(100)
|
||||
character*512 data_dir,fname
|
||||
|
@ -42,44 +41,35 @@ contains
|
|||
character*6 hhmmss
|
||||
character*4 cqstr,cqstr0
|
||||
|
||||
complex cd2(0:NZZ/NDOWN-1) !Complex waveform
|
||||
complex cb(0:NZZ/NDOWN-1+NN*NSS)
|
||||
complex cd2(0:NDMAX-1) !Complex waveform
|
||||
complex cb(0:NDMAX-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 bitmetrics(2*NN,3)
|
||||
real dd(NMAX)
|
||||
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 candidate(2,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(NZZ) !Raw received data
|
||||
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 i4tone(103)
|
||||
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
|
||||
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 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/
|
||||
|
@ -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, &
|
||||
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,apbits,nappasses,naptypes, &
|
||||
mycall0,hiscall0,msg0,cqstr0,ctwk2
|
||||
save fs,dt,tt,txt,twopi,h,first,apbits,nappasses,naptypes, &
|
||||
mycall0,hiscall0,cqstr0,ctwk2
|
||||
|
||||
this%callback => callback
|
||||
hhmmss=cdatetime0(8:13)
|
||||
dxcall13=hiscall
|
||||
mycall13=mycall
|
||||
|
||||
if(first) then
|
||||
fs=12000.0/NDOWN !Sample rate after downsampling
|
||||
dt=1/fs !Sample interval after downsample (s)
|
||||
|
@ -100,12 +93,6 @@ contains
|
|||
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.
|
||||
|
@ -199,49 +186,88 @@ contains
|
|||
mycall0=mycall
|
||||
hiscall0=hiscall
|
||||
endif
|
||||
candidate=0.0
|
||||
ncand=0
|
||||
syncmin=1.2
|
||||
maxcand=100
|
||||
|
||||
ndecodes=0
|
||||
decodes=' '
|
||||
fa=nfa
|
||||
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 getcandidates4(iwave,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
||||
call getcandidates4(dd,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
|
||||
ncand,sbase)
|
||||
call timer('getcand4',1)
|
||||
|
||||
ndecodes=0
|
||||
dobigfft=.true.
|
||||
do icand=1,ncand
|
||||
f0=candidate(1,icand)
|
||||
snr=candidate(3,icand)-1.0
|
||||
snr=candidate(2,icand)-1.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)
|
||||
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)
|
||||
! 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
|
||||
if(isync.eq.1) then
|
||||
idfmin=-12
|
||||
idfmax=12
|
||||
idfstp=3
|
||||
ibmin=0
|
||||
ibmax=800
|
||||
ibmin=-344
|
||||
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
|
||||
else
|
||||
idfmin=idfbest-4
|
||||
idfmax=idfbest+4
|
||||
idfstp=1
|
||||
ibmin=max(0,ibest-5)
|
||||
ibmax=min(ibest+5,NZZ/NDOWN-1)
|
||||
ibmin=ibest-5
|
||||
ibmax=ibest+5
|
||||
ibstp=1
|
||||
endif
|
||||
ibest=-1
|
||||
smax=-99.
|
||||
idfbest=0
|
||||
smax=-99.
|
||||
call timer('sync4d ',0)
|
||||
do idf=idfmin,idfmax,idfstp
|
||||
do istart=ibmin,ibmax,ibstp
|
||||
|
@ -255,99 +281,30 @@ contains
|
|||
enddo
|
||||
call timer('sync4d ',1)
|
||||
enddo
|
||||
f0=f0+real(idfbest)
|
||||
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
|
||||
! write(*,3002) smax,ibest/750.0,f0
|
||||
!3002 format('b',3f8.2)
|
||||
if(iseg.eq.1) smax1=smax
|
||||
if(smax.lt.1.2) cycle
|
||||
if(iseg.gt.1 .and. smax.lt.smax1) cycle
|
||||
f1=f0+real(idfbest)
|
||||
if( f1.le.10.0 .or. f1.ge.4990.0 ) cycle
|
||||
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)
|
||||
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 timer('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 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) &
|
||||
)
|
||||
cd=0.
|
||||
if(ibest.ge.0) then
|
||||
it=min(NDMAX-1,ibest+NN*NSS-1)
|
||||
np=it-ibest+1
|
||||
cd(0:np-1)=cb(ibest:it)
|
||||
else
|
||||
print*,"Error - nsym must be 1, 2, or 4."
|
||||
cd(-ibest:ibest+NN*NSS-1)=cb(0:NN*NSS+2*ibest-1)
|
||||
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 normalizebmet(bmeta,2*NN)
|
||||
call normalizebmet(bmetb,2*NN)
|
||||
call normalizebmet(bmetc,2*NN)
|
||||
|
||||
call timer('bitmet ',0)
|
||||
call get_ft4_bitmetrics(cd,bitmetrics,badsync)
|
||||
call timer('bitmet ',1)
|
||||
if(badsync) cycle
|
||||
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/))
|
||||
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/))
|
||||
|
@ -356,21 +313,23 @@ contains
|
|||
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( 1: 58)=bitmetrics( 9: 66, 1)
|
||||
llra( 59:116)=bitmetrics( 75:132, 1)
|
||||
llra(117:174)=bitmetrics(141:198, 1)
|
||||
llra=scalefac*llra
|
||||
llrb( 1: 58)=bmetb( 9: 66)
|
||||
llrb( 59:116)=bmetb( 75:132)
|
||||
llrb(117:174)=bmetb(141:198)
|
||||
llrb( 1: 58)=bitmetrics( 9: 66, 2)
|
||||
llrb( 59:116)=bitmetrics( 75:132, 2)
|
||||
llrb(117:174)=bitmetrics(141:198, 2)
|
||||
llrb=scalefac*llrb
|
||||
llrc( 1: 58)=bmetc( 9: 66)
|
||||
llrc( 59:116)=bmetc( 75:132)
|
||||
llrc(117:174)=bmetc(141:198)
|
||||
llrc( 1: 58)=bitmetrics( 9: 66, 3)
|
||||
llrc( 59:116)=bitmetrics( 75:132, 3)
|
||||
llrc(117:174)=bitmetrics(141:198, 3)
|
||||
llrc=scalefac*llrc
|
||||
|
||||
apmag=maxval(abs(llra))*1.1
|
||||
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
|
||||
do ipass=1,npasses
|
||||
if(ipass.eq.1) llr=llra
|
||||
|
@ -382,8 +341,9 @@ contains
|
|||
endif
|
||||
|
||||
if(ipass .gt. 3) then
|
||||
llrd=llrc
|
||||
llrd=llra
|
||||
iaptype=naptypes(nQSOProgress,ipass-3)
|
||||
if(lapcqonly) iaptype=1
|
||||
|
||||
! ncontest=0 : NONE
|
||||
! 1 : NA_VHF
|
||||
|
@ -395,7 +355,7 @@ contains
|
|||
!
|
||||
! 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(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.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
|
||||
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)
|
||||
apmask(1:77)=1 ! mycall, hiscall, RRR|73|RR73
|
||||
if(iaptype.eq.6) llrd(1:77)=apmag*apbits(1:77)
|
||||
endif
|
||||
endif
|
||||
|
||||
llr=llrd
|
||||
endif
|
||||
max_iterations=40
|
||||
message77=0
|
||||
dmin=0.0
|
||||
call timer('bpdec174',0)
|
||||
call bpdecode174_91(llr,apmask,max_iterations,message77, &
|
||||
cw,nharderror,niterations)
|
||||
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( 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)
|
||||
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
|
||||
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
|
||||
xsnr=10*log10(snr)-14.8
|
||||
else
|
||||
xsnr=-20.0
|
||||
xsnr=-21.0
|
||||
endif
|
||||
nsnr=nint(max(-20.0,xsnr))
|
||||
xdt=ibest/750.0 - 0.5
|
||||
call this%callback(sync,nsnr,xdt,f0,message,iaptype,qual)
|
||||
if(ibest.ge.ibmax-15) msg0=message !Possible dupe candidate
|
||||
nsnr=nint(max(-21.0,xsnr))
|
||||
xdt=ibest/666.67 - 0.5
|
||||
!write(21,'(i6.6,i5,2x,f4.1,i6,2x,a37,2x,f4.1,3i3,f5.1,i4,i4,i4)') &
|
||||
! 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
|
||||
endif
|
||||
enddo !Sequence estimation
|
||||
if(nharderror.ge.0) exit
|
||||
enddo !3 DT segments
|
||||
enddo !Candidate list
|
||||
|
||||
enddo !Subtraction loop
|
||||
return
|
||||
end subroutine decode
|
||||
|
||||
|
|
|
@ -396,7 +396,7 @@ subroutine ft8b(dd0,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, &
|
|||
cycle
|
||||
endif
|
||||
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)
|
||||
xsig=0.0
|
||||
xnoi=0.0
|
||||
|
|
|
@ -91,7 +91,7 @@ program ft8sim
|
|||
msg0=msg
|
||||
do ifile=1,nfiles
|
||||
k=nint((xdt+0.5)/dt)
|
||||
ia=k
|
||||
ia=max(1,k)
|
||||
phi=0.0
|
||||
c0=0.0
|
||||
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)
|
||||
c=sig*c0
|
||||
|
||||
ib=k
|
||||
ib=min(k,NMAX)
|
||||
wave=real(c)
|
||||
peak=maxval(abs(wave(ia:ib)))
|
||||
nslots=1
|
||||
|
|
|
@ -57,6 +57,7 @@ program ft8sim_gfsk
|
|||
baud=1.0/tt !Keying rate (baud)
|
||||
bw=8*baud !Occupied bandwidth (Hz)
|
||||
txt=NZ*dt !Transmission length (s)
|
||||
bt=2.0
|
||||
bandwidth_ratio=2500.0/(fs/2.0)
|
||||
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
|
||||
if(snrdb.gt.90.0) sig=1.0
|
||||
|
@ -67,7 +68,7 @@ program ft8sim_gfsk
|
|||
n3=-1
|
||||
call pack77(msg37,i3,n3,c77)
|
||||
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(*,'(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.
|
||||
!
|
||||
|
@ -9,21 +9,20 @@ subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
|||
real pulse(23040)
|
||||
real dphi(0:(nsym+2)*nsps-1)
|
||||
integer itone(nsym)
|
||||
logical first
|
||||
data first/.true./
|
||||
save pulse,first,twopi,dt,hmod
|
||||
data ibt0/0/
|
||||
save pulse,twopi,dt,hmod,ibt0
|
||||
|
||||
if(first) then
|
||||
ibt=nint(10*bt)
|
||||
if(ibt0.ne.ibt) then
|
||||
twopi=8.0*atan(1.0)
|
||||
dt=1.0/fsample
|
||||
hmod=1.0
|
||||
bt=2.0
|
||||
! Compute the frequency-smoothing pulse
|
||||
do i=1,3*nsps
|
||||
tt=(i-1.5*nsps)/real(nsps)
|
||||
pulse(i)=gfsk_pulse(bt,tt)
|
||||
enddo
|
||||
first=.false.
|
||||
ibt0=nint(10*bt)
|
||||
endif
|
||||
|
||||
! Compute the smoothed frequency waveform.
|
||||
|
@ -43,6 +42,7 @@ subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
|
|||
phi=0.0
|
||||
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
|
||||
wave=0.
|
||||
if (icmplx .ne. 0) cwave=0. ! avoid writing to memory we may not have access to
|
||||
k=0
|
||||
do j=nsps,nsps+nwave-1 !Don't include dummy symbols
|
||||
k=k+1
|
||||
|
|
|
@ -25,7 +25,7 @@ subroutine genft8(msg,i3,n3,msgsent,msgbits,itone)
|
|||
msgsent='*** bad message *** '
|
||||
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
|
||||
|
||||
|
|
|
@ -11,16 +11,22 @@ subroutine subtractft8(dd,itone,f0,dt)
|
|||
|
||||
parameter (NMAX=15*12000,NFRAME=1920*79)
|
||||
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
|
||||
integer itone(79)
|
||||
logical first
|
||||
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
|
||||
|
||||
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.
|
||||
do i=1,nframe
|
||||
id=nstart-1+i
|
||||
|
|
|
@ -89,7 +89,12 @@ subroutine sync8(dd,nfa,nfb,syncmin,nfqso,maxcand,s,candidate, &
|
|||
enddo
|
||||
iz=ib-ia+1
|
||||
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.gt.nh1) ibase=nh1
|
||||
base=red(ibase)
|
||||
|
|
|
@ -156,7 +156,7 @@ contains
|
|||
nfreqz=nint(dfx)
|
||||
call timer('sync4 ',1)
|
||||
|
||||
nsnr=nint(snrx)
|
||||
nsnr=-26
|
||||
if(sync.lt.syncmin) then
|
||||
if (associated (this%decode_callback)) then
|
||||
call this%decode_callback(nsnr,dtxz,nfreqz,.false.,csync, &
|
||||
|
@ -166,6 +166,7 @@ contains
|
|||
endif
|
||||
|
||||
! We have achieved sync
|
||||
nsnr=nint(snrsync - 22.9)
|
||||
decoded=blank
|
||||
deepmsg=blank
|
||||
special=' '
|
||||
|
|
|
@ -22,7 +22,8 @@ program jt9
|
|||
!### ndepth was defined as 60001. Why???
|
||||
integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, &
|
||||
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) = [ &
|
||||
option ('help', .false., 'h', 'Display this help message', ''), &
|
||||
option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), &
|
||||
|
@ -224,7 +225,7 @@ program jt9
|
|||
endif
|
||||
|
||||
shared_data%id2=0 !??? Why is this necessary ???
|
||||
|
||||
if(mode.eq.5) npts=21*3456
|
||||
do iblk=1,npts/kstep
|
||||
k=iblk*kstep
|
||||
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)
|
||||
go to 4
|
||||
3 call timer('read_wav',1)
|
||||
print*,'EOF on input file ',infile
|
||||
print*,'EOF on input file ',trim(infile)
|
||||
exit
|
||||
4 call timer('read_wav',1)
|
||||
nhsym=(k-2048)/kstep
|
||||
|
@ -242,7 +243,7 @@ program jt9
|
|||
ingain=0
|
||||
call timer('symspec ',0)
|
||||
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)
|
||||
call timer('symspec ',1)
|
||||
endif
|
||||
|
|
|
@ -53,7 +53,8 @@ contains
|
|||
type(riff_descriptor) :: desc
|
||||
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
|
||||
inquire (unit=this%lun, pos=filepos)
|
||||
do
|
||||
|
@ -67,5 +68,6 @@ contains
|
|||
end if
|
||||
filepos = filepos + (desc%size + 1) / 2 * 2 ! pad to even alignment
|
||||
end do
|
||||
return
|
||||
end subroutine read
|
||||
end module readwav
|
||||
|
|
|
@ -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 <QDebug>
|
||||
#include <QDebugStateSaver>
|
||||
#include "Configuration.hpp"
|
||||
#include "Radio.hpp"
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
|
@ -155,14 +156,16 @@ typedef multi_index_container<
|
|||
class AD1CCty::impl final
|
||||
{
|
||||
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 ();
|
||||
using entity_by_id = entities_type::index<id>::type;
|
||||
entity_by_id::iterator e; // iterator into entity set
|
||||
|
||||
//
|
||||
|
@ -171,23 +174,26 @@ public:
|
|||
if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
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;
|
||||
result.continent = e->continent_;
|
||||
result.CQ_zone = e->CQ_zone_;
|
||||
result.ITU_zone = e->ITU_zone_;
|
||||
result.entity_name = e->name_;
|
||||
result.WAE_only = e->WAE_only_;
|
||||
result.latitude = e->lat_;
|
||||
result.longtitude = e->long_;
|
||||
result.UTC_offset = e->UTC_offset_;
|
||||
result.primary_prefix = e->primary_prefix_;
|
||||
result.continent = e.continent_;
|
||||
result.CQ_zone = e.CQ_zone_;
|
||||
result.ITU_zone = e.ITU_zone_;
|
||||
result.entity_name = e.name_;
|
||||
result.WAE_only = e.WAE_only_;
|
||||
result.latitude = e.lat_;
|
||||
result.longtitude = e.long_;
|
||||
result.UTC_offset = e.UTC_offset_;
|
||||
result.primary_prefix = e.primary_prefix_;
|
||||
|
||||
// check for overrides
|
||||
bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true};
|
||||
|
@ -220,6 +226,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
Configuration const * configuration_;
|
||||
QString path_;
|
||||
entities_type entities_;
|
||||
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)};
|
||||
m_->path_ = dataPath.exists (file_name)
|
||||
? 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);
|
||||
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 ())
|
||||
|
@ -397,9 +409,11 @@ auto AD1CCty::lookup (QString const& call) const -> Record
|
|||
auto p = m_->prefixes_.find (search_prefix);
|
||||
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);
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
#ifndef AD1C_CTY_HPP_
|
||||
#define AD1C_CTY_HPP_
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class QString;
|
||||
class Configuration;
|
||||
|
||||
//
|
||||
// AD1CCty - Fast access database of Jim Reisert, AD1C's, cty.dat
|
||||
// entity and entity override information file.
|
||||
//
|
||||
class AD1CCty final
|
||||
: public QObject
|
||||
, private boost::noncopyable
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -39,7 +41,7 @@ public:
|
|||
QString primary_prefix;
|
||||
};
|
||||
|
||||
explicit AD1CCty ();
|
||||
explicit AD1CCty (Configuration const *);
|
||||
~AD1CCty ();
|
||||
Record lookup (QString const& call) const;
|
||||
|
||||
|
|
|
@ -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