Merge branch 'develop' into feat-fst280

This commit is contained in:
Bill Somerville 2020-08-17 18:33:00 +01:00
commit 4a2a181528
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
30 changed files with 1945 additions and 1644 deletions

6
.gitignore vendored
View File

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

View File

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

View File

@ -41,7 +41,8 @@ bool SoundInput::audioError () const
return result;
}
void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink, unsigned downSampleFactor, AudioDevice::Channel channel)
void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink
, unsigned downSampleFactor, AudioDevice::Channel channel)
{
Q_ASSERT (sink);
@ -62,14 +63,13 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
Q_EMIT error (tr ("Requested input audio format is not valid."));
return;
}
if (!device.isFormatSupported (format))
else if (!device.isFormatSupported (format))
{
// qDebug () << "Nearest supported audio format:" << device.nearestFormat (format);
Q_EMIT error (tr ("Requested input audio format is not supported on device."));
return;
}
// qDebug () << "Selected audio input format:" << format;
// qDebug () << "Selected audio input format:" << format;
m_stream.reset (new QAudioInput {device, format});
if (audioError ())
@ -79,11 +79,20 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged);
m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer));
//qDebug () << "SoundIn default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
// the Windows MME version of QAudioInput uses 1/5 of the buffer
// size for period size other platforms seem to optimize themselves
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer * 5));
#else
Q_UNUSED (framesPerBuffer);
#endif
if (sink->initialize (QIODevice::WriteOnly, channel))
{
m_stream->start (sink);
audioError ();
cummulative_lost_usec_ = -1;
//qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize () << "peirod size:" << m_stream->periodSize ();
}
else
{
@ -115,9 +124,9 @@ void SoundInput::resume ()
}
}
void SoundInput::handleStateChanged (QAudio::State newState) const
void SoundInput::handleStateChanged (QAudio::State newState)
{
// qDebug () << "SoundInput::handleStateChanged: newState:" << newState;
//qDebug () << "SoundInput::handleStateChanged: newState:" << newState;
switch (newState)
{
@ -126,6 +135,7 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
break;
case QAudio::ActiveState:
reset (false);
Q_EMIT status (tr ("Receiving"));
break;
@ -152,6 +162,22 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
}
}
void SoundInput::reset (bool report_dropped_frames)
{
if (m_stream)
{
if (cummulative_lost_usec_ >= 0 // don't report first time as we
// don't yet known latency
&& report_dropped_frames)
{
auto lost_usec = m_stream->elapsedUSecs () - m_stream->processedUSecs () - cummulative_lost_usec_;
Q_EMIT dropped_frames (m_stream->format ().framesForDuration (lost_usec), lost_usec);
//qDebug () << "SoundInput::reset: frames dropped:" << m_stream->format ().framesForDuration (lost_usec) << "sec:" << lost_usec / 1.e6;
}
cummulative_lost_usec_ = m_stream->elapsedUSecs () - m_stream->processedUSecs ();
}
}
void SoundInput::stop()
{
if (m_stream)

View File

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

View File

@ -9,15 +9,6 @@
#include "moc_soundout.cpp"
/*
#if defined (WIN32)
# define MS_BUFFERED 1000u
#else
# define MS_BUFFERED 2000u
#endif
*/
# define MS_BUFFERED 200u
bool SoundOutput::audioError () const
{
bool result (true);
@ -50,62 +41,76 @@ bool SoundOutput::audioError () const
return result;
}
void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered)
void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered)
{
Q_ASSERT (0 < channels && channels < 3);
m_msBuffered = msBuffered;
QAudioFormat format (device.preferredFormat ());
// qDebug () << "Preferred audio output format:" << format;
format.setChannelCount (channels);
format.setCodec ("audio/pcm");
format.setSampleRate (48000);
format.setSampleType (QAudioFormat::SignedInt);
format.setSampleSize (16);
format.setByteOrder (QAudioFormat::Endian (QSysInfo::ByteOrder));
if (!format.isValid ())
if (!device.isNull ())
{
Q_EMIT error (tr ("Requested output audio format is not valid."));
Q_ASSERT (0 < channels && channels < 3);
m_framesBuffered = frames_buffered;
QAudioFormat format (device.preferredFormat ());
// qDebug () << "Preferred audio output format:" << format;
format.setChannelCount (channels);
format.setCodec ("audio/pcm");
format.setSampleRate (48000);
format.setSampleType (QAudioFormat::SignedInt);
format.setSampleSize (16);
format.setByteOrder (QAudioFormat::Endian (QSysInfo::ByteOrder));
if (!format.isValid ())
{
Q_EMIT error (tr ("Requested output audio format is not valid."));
}
else if (!device.isFormatSupported (format))
{
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
}
else
{
// qDebug () << "Selected audio output format:" << format;
m_stream.reset (new QAudioOutput (device, format));
audioError ();
m_stream->setVolume (m_volume);
m_stream->setNotifyInterval(100);
error_ = false;
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
// qDebug() << "A" << m_volume << m_stream->notifyInterval();
}
}
if (!device.isFormatSupported (format))
{
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
}
// qDebug () << "Selected audio output format:" << format;
m_stream.reset (new QAudioOutput (device, format));
audioError ();
m_stream->setVolume (m_volume);
m_stream->setNotifyInterval(100);
connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged);
// qDebug() << "A" << m_volume << m_stream->notifyInterval();
}
void SoundOutput::restart (QIODevice * source)
{
Q_ASSERT (m_stream);
if (!m_stream)
{
if (!error_)
{
error_ = true; // only signal error once
Q_EMIT error (tr ("No audio output device configured."));
}
return;
}
else
{
error_ = false;
}
//
// This buffer size is critical since for proper sound streaming. If
// it is too short; high activity levels on the machine can starve
// the audio buffer. On the other hand the Windows implementation
// seems to take the length of the buffer in time to stop the audio
// stream even if reset() is used.
//
// 2 seconds seems a reasonable compromise except for Windows
// where things are probably broken.
//
// we have to set this before every start on the stream because the
// Windows implementation seems to forget the buffer size after a
// stop.
m_stream->setBufferSize (m_stream->format().bytesForDuration((m_msBuffered ? m_msBuffered : MS_BUFFERED) * 1000));
// qDebug() << "B" << m_stream->bufferSize() <<
// m_stream->periodSize() << m_stream->notifyInterval();
//qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
if (m_framesBuffered)
{
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered));
#endif
}
m_stream->setCategory ("production");
m_stream->start (source);
// qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
}
void SoundOutput::suspend ()

View File

@ -18,15 +18,16 @@ class SoundOutput
public:
SoundOutput ()
: m_msBuffered {0u}
: m_framesBuffered {0}
, m_volume {1.0}
, error_ {false}
{
}
qreal attenuation () const;
public Q_SLOTS:
void setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered = 0u);
void setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered = 0);
void restart (QIODevice *);
void suspend ();
void resume ();
@ -47,8 +48,9 @@ private Q_SLOTS:
private:
QScopedPointer<QAudioOutput> m_stream;
unsigned m_msBuffered;
int m_framesBuffered;
qreal m_volume;
bool error_;
};
#endif

View File

@ -222,9 +222,12 @@ set (WSJT_QT_CONF_DESTINATION ${QT_CONF_DESTINATION} CACHE PATH "Path for the qt
#
# Project sources
#
set (fort_qt_CXXSRCS
lib/shmem.cpp
)
set (wsjt_qt_CXXSRCS
qt_helpers.cpp
lib/shmem.cpp
widgets/MessageBox.cpp
MetaDataRegistry.cpp
Network/NetworkServerLookup.cpp
@ -733,6 +736,7 @@ set (qcp_CXXSRCS
set (all_CXXSRCS
${wsjt_CXXSRCS}
${fort_qt_CXXSRCS}
${wsjt_qt_CXXSRCS}
${wsjt_qtmm_CXXSRCS}
${wsjtx_CXXSRCS}
@ -747,7 +751,6 @@ set (all_C_and_CXXSRCS
)
set (TOP_LEVEL_RESOURCES
cty.dat
icons/Darwin/wsjtx.iconset/icon_128x128.png
contrib/gpl-v3-logo.svg
artwork/splash.png
@ -888,7 +891,7 @@ find_package (OpenMP)
#
# fftw3 single precision library
#
find_package (FFTW3 COMPONENTS double single threads REQUIRED)
find_package (FFTW3 COMPONENTS single threads REQUIRED)
#
# libhamlib setup
@ -929,7 +932,9 @@ endif ()
if (WSJT_GENERATE_DOCS)
add_subdirectory (doc)
endif (WSJT_GENERATE_DOCS)
if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
add_subdirectory (tests)
endif ()
#
# Library building setup
@ -1259,6 +1264,11 @@ if (WIN32)
target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase)
endif (WIN32)
# build a library of package Qt functionality used in Fortran utilities
add_library (fort_qt STATIC ${fort_qt_CXXSRCS})
target_link_libraries (fort_qt Qt5::Core)
# build a library of WSJT Qt multimedia components
add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS})
target_link_libraries (wsjt_qtmm Qt5::Multimedia)
@ -1306,9 +1316,9 @@ if (${OPENMP_FOUND} OR APPLE)
LINK_FLAGS -Wl,--stack,16777216
)
endif ()
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx wsjt_qt)
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt)
else (${OPENMP_FOUND} OR APPLE)
target_link_libraries (jt9 wsjt_fort wsjt_cxx Qt5::Core)
target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
endif (${OPENMP_FOUND} OR APPLE)
if(WSJT_BUILD_UTILS)
@ -1572,6 +1582,7 @@ install (FILES
)
install (FILES
cty.dat
contrib/Ephemeris/JPLEPH
DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
#COMPONENT runtime
@ -1852,11 +1863,11 @@ endif ()
set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran4 (>=7.3.0), libfftw3-single3 (>=3.3.7), libgomp1 (>8), libqt5serialport5 (>=5.9.5), libqt5multimedia5-plugins (>=5.9.5), libqt5widgets5 (>=5.9.5), libqt5network5 (>=5.9.5), libqt5printsupport5 (>=5.9.5), libqt5sql5-sqlite (>=5.9.5), libusb-1.0-0 (>=1.0.21)")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran5 (>=10), libfftw3-single3 (>=3.3.8), libgomp1 (>=10), libqt5serialport5 (>=5.12.8), libqt5multimedia5-plugins (>=5.12.8), libqt5widgets5 (>=5.12.8), libqt5network5 (>=5.12.8), libqt5printsupport5 (>=5.12.8), libqt5sql5-sqlite (>=5.12.8), libusb-1.0-0 (>=1.0.23)")
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase >= 5.6, qt5-qtserialport >= 5.6, qt5-qtmultimedia >= 5.6, qt5-qtsvg >= 5.6, libusbx >= 1.0.23, libgfortran >= 4.7, fftw-libs-double > 3.3.8, fftw-libs-single > 3.3.8")
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase >= 5.13.2, qt5-qtserialport >= 5.13.2, qt5-qtmultimedia >= 5.13.2, qt5-qtsvg >= 5.13.2, libusbx >= 1.0.23, libgfortran >= 10.0.1, libgomp >= 10.0.1, fftw-libs-single >= 3.3.8")
set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1)
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"

View File

@ -137,6 +137,8 @@
#include <QApplication>
#include <QMetaType>
#include <QList>
#include <QPair>
#include <QVariant>
#include <QSettings>
#include <QAudioDeviceInfo>
#include <QAudioInput>
@ -401,6 +403,7 @@ class Configuration::impl final
public:
using FrequencyDelta = Radio::FrequencyDelta;
using port_type = Configuration::port_type;
using audio_info_type = QPair<QAudioDeviceInfo, QList<QVariant> >;
explicit impl (Configuration * self
, QNetworkAccessManager * network_manager
@ -429,9 +432,14 @@ private:
void read_settings ();
void write_settings ();
bool load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *);
Q_SLOT void lazy_models_load (int);
void find_audio_devices ();
QAudioDeviceInfo find_audio_device (QAudio::Mode, QComboBox *, QString const& device_name);
void load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *);
void update_audio_channels (QComboBox const *, int, QComboBox *, bool);
void find_tab (QWidget *);
void initialize_models ();
bool split_mode () const
{
@ -477,8 +485,6 @@ private:
Q_SLOT void on_force_DTR_combo_box_currentIndexChanged (int);
Q_SLOT void on_force_RTS_combo_box_currentIndexChanged (int);
Q_SLOT void on_rig_combo_box_currentIndexChanged (int);
Q_SLOT void on_sound_input_combo_box_currentTextChanged (QString const&);
Q_SLOT void on_sound_output_combo_box_currentTextChanged (QString const&);
Q_SLOT void on_add_macro_push_button_clicked (bool = false);
Q_SLOT void on_delete_macro_push_button_clicked (bool = false);
Q_SLOT void on_PTT_method_button_group_buttonClicked (int);
@ -647,10 +653,8 @@ private:
bool pwrBandTuneMemory_;
QAudioDeviceInfo audio_input_device_;
bool default_audio_input_device_selected_;
AudioDevice::Channel audio_input_channel_;
QAudioDeviceInfo audio_output_device_;
bool default_audio_output_device_selected_;
AudioDevice::Channel audio_output_channel_;
friend class Configuration;
@ -976,8 +980,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
, transceiver_command_number_ {0}
, degrade_ {0.} // initialize to zero each run, not
// saved in settings
, default_audio_input_device_selected_ {false}
, default_audio_output_device_selected_ {false}
{
ui_->setupUi (this);
@ -1100,6 +1102,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
//
// setup hooks to keep audio channels aligned with devices
//
connect (ui_->configuration_tabs, &QTabWidget::currentChanged, this, &Configuration::impl::lazy_models_load);
{
using namespace std;
using namespace std::placeholders;
@ -1130,7 +1133,9 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
ui_->frequencies_table_view->setModel (&next_frequencies_);
ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
@ -1170,7 +1175,9 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
stations_.sort (StationList::band_column);
ui_->stations_table_view->setModel (&next_stations_);
ui_->stations_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->stations_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
ui_->stations_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->stations_table_view->verticalHeader ()->setResizeContentsPrecision (0);
ui_->stations_table_view->sortByColumn (StationList::band_column, Qt::AscendingOrder);
// stations delegates
@ -1189,18 +1196,6 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
//
ui_->highlighting_list_view->setModel (&next_decode_highlighing_model_);
//
// load combo boxes with audio setup choices
//
default_audio_input_device_selected_ = load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &audio_input_device_);
default_audio_output_device_selected_ = load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &audio_output_device_);
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_);
ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_);
enumerate_rigs ();
initialize_models ();
@ -1215,8 +1210,35 @@ Configuration::impl::~impl ()
write_settings ();
}
void Configuration::impl::lazy_models_load (int current_tab_index)
{
switch (current_tab_index)
{
case 2: // Audio
//
// load combo boxes with audio setup choices
//
load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &audio_input_device_);
load_audio_devices (QAudio::AudioOutput, ui_->sound_output_combo_box, &audio_output_device_);
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_);
ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_);
break;
default:
break;
}
}
void Configuration::impl::initialize_models ()
{
{
SettingsGroup g {settings_, "Configuration"};
find_audio_devices ();
}
auto pal = ui_->callsign_line_edit->palette ();
if (my_callsign_.isEmpty ())
{
@ -1238,6 +1260,7 @@ void Configuration::impl::initialize_models ()
ui_->sbDegrade->setValue (degrade_);
ui_->sbBandwidth->setValue (RxBandwidth_);
ui_->PTT_method_button_group->button (rig_params_.ptt_type)->setChecked (true);
ui_->save_path_display_label->setText (save_directory_.absolutePath ());
ui_->azel_path_display_label->setText (azel_directory_.absolutePath ());
ui_->CW_id_after_73_check_box->setChecked (id_after_73_);
@ -1390,61 +1413,7 @@ void Configuration::impl::read_settings ()
save_directory_.setPath (settings_->value ("SaveDir", default_save_directory_.absolutePath ()).toString ());
azel_directory_.setPath (settings_->value ("AzElDir", default_azel_directory_.absolutePath ()).toString ());
{
//
// retrieve audio input device
//
auto saved_name = settings_->value ("SoundInName").toString ();
// deal with special Windows default audio devices
auto default_device = QAudioDeviceInfo::defaultInputDevice ();
if (saved_name == default_device.deviceName ())
{
audio_input_device_ = default_device;
default_audio_input_device_selected_ = true;
}
else
{
default_audio_input_device_selected_ = false;
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (QAudio::AudioInput)) // available audio input devices
{
if (p.deviceName () == saved_name)
{
audio_input_device_ = p;
}
}
}
}
{
//
// retrieve audio output device
//
auto saved_name = settings_->value("SoundOutName").toString();
// deal with special Windows default audio devices
auto default_device = QAudioDeviceInfo::defaultOutputDevice ();
if (saved_name == default_device.deviceName ())
{
audio_output_device_ = default_device;
default_audio_output_device_selected_ = true;
}
else
{
default_audio_output_device_selected_ = false;
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) // available audio output devices
{
if (p.deviceName () == saved_name)
{
audio_output_device_ = p;
}
}
}
}
// retrieve audio channel info
audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ());
audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ());
find_audio_devices ();
type_2_msg_gen_ = settings_->value ("Type2MsgGen", QVariant::fromValue (Configuration::type_2_msg_3_full)).value<Configuration::Type2MsgGen> ();
@ -1547,6 +1516,27 @@ void Configuration::impl::read_settings ()
pwrBandTuneMemory_ = settings_->value("pwrBandTuneMemory",false).toBool ();
}
void Configuration::impl::find_audio_devices ()
{
//
// retrieve audio input device
//
auto saved_name = settings_->value ("SoundInName").toString ();
audio_input_device_ = find_audio_device (QAudio::AudioInput, ui_->sound_input_combo_box, saved_name);
audio_input_channel_ = AudioDevice::fromString (settings_->value ("AudioInputChannel", "Mono").toString ());
update_audio_channels (ui_->sound_input_combo_box, ui_->sound_input_combo_box->currentIndex (), ui_->sound_input_channel_combo_box, false);
ui_->sound_input_channel_combo_box->setCurrentIndex (audio_input_channel_);
//
// retrieve audio output device
//
saved_name = settings_->value("SoundOutName").toString();
audio_output_channel_ = AudioDevice::fromString (settings_->value ("AudioOutputChannel", "Mono").toString ());
audio_output_device_ = find_audio_device (QAudio::AudioOutput, ui_->sound_output_combo_box, saved_name);
update_audio_channels (ui_->sound_output_combo_box, ui_->sound_output_combo_box->currentIndex (), ui_->sound_output_channel_combo_box, true);
ui_->sound_output_channel_combo_box->setCurrentIndex (audio_output_channel_);
}
void Configuration::impl::write_settings ()
{
SettingsGroup g {settings_, "Configuration"};
@ -1566,25 +1556,8 @@ void Configuration::impl::write_settings ()
settings_->setValue ("PTTport", rig_params_.ptt_port);
settings_->setValue ("SaveDir", save_directory_.absolutePath ());
settings_->setValue ("AzElDir", azel_directory_.absolutePath ());
if (default_audio_input_device_selected_)
{
settings_->setValue ("SoundInName", QAudioDeviceInfo::defaultInputDevice ().deviceName ());
}
else
{
settings_->setValue ("SoundInName", audio_input_device_.deviceName ());
}
if (default_audio_output_device_selected_)
{
settings_->setValue ("SoundOutName", QAudioDeviceInfo::defaultOutputDevice ().deviceName ());
}
else
{
settings_->setValue ("SoundOutName", audio_output_device_.deviceName ());
}
settings_->setValue ("SoundInName", audio_input_device_.deviceName ());
settings_->setValue ("SoundOutName", audio_output_device_.deviceName ());
settings_->setValue ("AudioInputChannel", AudioDevice::toString (audio_input_channel_));
settings_->setValue ("AudioOutputChannel", AudioDevice::toString (audio_output_channel_));
settings_->setValue ("Type2MsgGen", QVariant::fromValue (type_2_msg_gen_));
@ -1658,6 +1631,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_);
settings_->setValue ("Region", QVariant::fromValue (region_));
settings_->setValue ("AutoGrid", use_dynamic_grid_);
settings_->sync ();
}
void Configuration::impl::set_rig_invariants ()
@ -1790,17 +1764,27 @@ void Configuration::impl::set_rig_invariants ()
bool Configuration::impl::validate ()
{
if (ui_->sound_input_combo_box->currentIndex () < 0
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioInput).empty ())
&& audio_input_device_.isNull ())
{
find_tab (ui_->sound_input_combo_box);
MessageBox::critical_message (this, tr ("Invalid audio input device"));
return false;
}
if (ui_->sound_input_channel_combo_box->currentIndex () < 0
&& audio_input_device_.isNull ())
{
find_tab (ui_->sound_input_combo_box);
MessageBox::critical_message (this, tr ("Invalid audio input device"));
return false;
}
if (ui_->sound_output_combo_box->currentIndex () < 0
&& !QAudioDeviceInfo::availableDevices (QAudio::AudioOutput).empty ())
&& audio_output_device_.isNull ())
{
MessageBox::critical_message (this, tr ("Invalid audio out device"));
return false;
find_tab (ui_->sound_output_combo_box);
MessageBox::information_message (this, tr ("Invalid audio output device"));
// don't reject as we can work without an audio output
}
if (!ui_->PTT_method_button_group->checkedButton ()->isEnabled ())
@ -1822,16 +1806,7 @@ bool Configuration::impl::validate ()
if (ui_->rbField_Day->isEnabled () && ui_->rbField_Day->isChecked () &&
!ui_->Field_Day_Exchange->hasAcceptableInput ())
{
for (auto * parent = ui_->Field_Day_Exchange->parentWidget (); parent; parent = parent->parentWidget ())
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
ui_->Field_Day_Exchange->setFocus ();
find_tab (ui_->Field_Day_Exchange);
MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
, tr ("You must input a valid ARRL Field Day exchange"));
return false;
@ -1840,16 +1815,7 @@ bool Configuration::impl::validate ()
if (ui_->rbRTTY_Roundup->isEnabled () && ui_->rbRTTY_Roundup->isChecked () &&
!ui_->RTTY_Exchange->hasAcceptableInput ())
{
for (auto * parent = ui_->RTTY_Exchange->parentWidget (); parent; parent = parent->parentWidget ())
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
ui_->RTTY_Exchange->setFocus ();
find_tab (ui_->RTTY_Exchange);
MessageBox::critical_message (this, tr ("Invalid Contest Exchange")
, tr ("You must input a valid ARRL RTTY Roundup exchange"));
return false;
@ -1970,59 +1936,19 @@ void Configuration::impl::accept ()
// Check to see whether SoundInThread must be restarted,
// and save user parameters.
{
auto const& device_name = ui_->sound_input_combo_box->currentText ();
if (device_name != audio_input_device_.deviceName ())
auto const& selected_device = ui_->sound_input_combo_box->currentData ().value<audio_info_type> ().first;
if (selected_device != audio_input_device_)
{
auto const& default_device = QAudioDeviceInfo::defaultInputDevice ();
if (device_name == default_device.deviceName ())
{
audio_input_device_ = default_device;
}
else
{
bool found {false};
Q_FOREACH (auto const& d, QAudioDeviceInfo::availableDevices (QAudio::AudioInput))
{
if (device_name == d.deviceName ())
{
audio_input_device_ = d;
found = true;
}
}
if (!found)
{
audio_input_device_ = default_device;
}
}
audio_input_device_ = selected_device;
restart_sound_input_device_ = true;
}
}
{
auto const& device_name = ui_->sound_output_combo_box->currentText ();
if (device_name != audio_output_device_.deviceName ())
auto const& selected_device = ui_->sound_output_combo_box->currentData ().value<audio_info_type> ().first;
if (selected_device != audio_output_device_)
{
auto const& default_device = QAudioDeviceInfo::defaultOutputDevice ();
if (device_name == default_device.deviceName ())
{
audio_output_device_ = default_device;
}
else
{
bool found {false};
Q_FOREACH (auto const& d, QAudioDeviceInfo::availableDevices (QAudio::AudioOutput))
{
if (device_name == d.deviceName ())
{
audio_output_device_ = d;
found = true;
}
}
if (!found)
{
audio_output_device_ = default_device;
}
}
audio_output_device_ = selected_device;
restart_sound_output_device_ = true;
}
}
@ -2306,16 +2232,6 @@ void Configuration::impl::on_PTT_method_button_group_buttonClicked (int /* id */
set_rig_invariants ();
}
void Configuration::impl::on_sound_input_combo_box_currentTextChanged (QString const& text)
{
default_audio_input_device_selected_ = QAudioDeviceInfo::defaultInputDevice ().deviceName () == text;
}
void Configuration::impl::on_sound_output_combo_box_currentTextChanged (QString const& text)
{
default_audio_output_device_selected_ = QAudioDeviceInfo::defaultOutputDevice ().deviceName () == text;
}
void Configuration::impl::on_add_macro_line_edit_editingFinished ()
{
ui_->add_macro_line_edit->setText (ui_->add_macro_line_edit->text ().toUpper ());
@ -2694,6 +2610,7 @@ void Configuration::impl::transceiver_frequency (Frequency f)
current_offset_ = stations_.offset (f);
cached_rig_state_.frequency (apply_calibration (f + current_offset_));
qDebug () << "Configuration::impl::transceiver_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
@ -2719,6 +2636,7 @@ void Configuration::impl::transceiver_tx_frequency (Frequency f)
cached_rig_state_.tx_frequency (apply_calibration (f + current_tx_offset_));
}
qDebug () << "Configuration::impl::transceiver_tx_frequency: n:" << transceiver_command_number_ + 1 << "f:" << f;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
}
@ -2727,6 +2645,7 @@ void Configuration::impl::transceiver_mode (MODE m)
{
cached_rig_state_.online (true); // we want the rig online
cached_rig_state_.mode (m);
qDebug () << "Configuration::impl::transceiver_mode: n:" << transceiver_command_number_ + 1 << "m:" << m;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
@ -2735,6 +2654,7 @@ void Configuration::impl::transceiver_ptt (bool on)
cached_rig_state_.online (true); // we want the rig online
set_cached_mode ();
cached_rig_state_.ptt (on);
qDebug () << "Configuration::impl::transceiver_ptt: n:" << transceiver_command_number_ + 1 << "on:" << on;
Q_EMIT set_transceiver (cached_rig_state_, ++transceiver_command_number_);
}
@ -2833,72 +2753,67 @@ void Configuration::impl::close_rig ()
}
}
// load the available audio devices into the selection combo box and
// select the default device if the current device isn't set or isn't
// available
bool Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * combo_box, QAudioDeviceInfo * device)
// find the audio device that matches the specified name, also
// populate into the selection combo box with any devices we find in
// the search
QAudioDeviceInfo Configuration::impl::find_audio_device (QAudio::Mode mode, QComboBox * combo_box
, QString const& device_name)
{
using std::copy;
using std::back_inserter;
bool result {false};
combo_box->clear ();
int current_index = -1;
int default_index = -1;
int extra_items {0};
auto const& default_device = (mode == QAudio::AudioInput ? QAudioDeviceInfo::defaultInputDevice () : QAudioDeviceInfo::defaultOutputDevice ());
// deal with special default audio devices on Windows
if ("Default Input Device" == default_device.deviceName ()
|| "Default Output Device" == default_device.deviceName ())
auto const& devices = QAudioDeviceInfo::availableDevices (mode);
Q_FOREACH (auto const& p, devices)
{
default_index = 0;
QList<QVariant> channel_counts;
auto scc = default_device.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->addItem (default_device.deviceName (), channel_counts);
++extra_items;
if (default_device == *device)
{
current_index = 0;
result = true;
}
}
Q_FOREACH (auto const& p, QAudioDeviceInfo::availableDevices (mode))
{
// qDebug () << "Audio device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
// qDebug () << "Audio device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
// convert supported channel counts into something we can store in the item model
QList<QVariant> channel_counts;
auto scc = p.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->addItem (p.deviceName (), channel_counts);
combo_box->addItem (p.deviceName (), QVariant::fromValue (audio_info_type {p, channel_counts}));
if (p.deviceName () == device_name)
{
current_index = combo_box->count () - 1;
combo_box->setCurrentIndex (current_index);
return p;
}
}
combo_box->setCurrentIndex (current_index);
return {};
}
// load the available audio devices into the selection combo box
void Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * combo_box
, QAudioDeviceInfo * device)
{
using std::copy;
using std::back_inserter;
combo_box->clear ();
int current_index = -1;
auto const& devices = QAudioDeviceInfo::availableDevices (mode);
Q_FOREACH (auto const& p, devices)
{
// qDebug () << "Audio device: input:" << (QAudio::AudioInput == mode) << "name:" << p.deviceName () << "preferred format:" << p.preferredFormat () << "endians:" << p.supportedByteOrders () << "codecs:" << p.supportedCodecs () << "channels:" << p.supportedChannelCounts () << "rates:" << p.supportedSampleRates () << "sizes:" << p.supportedSampleSizes () << "types:" << p.supportedSampleTypes ();
// convert supported channel counts into something we can store in the item model
QList<QVariant> channel_counts;
auto scc = p.supportedChannelCounts ();
copy (scc.cbegin (), scc.cend (), back_inserter (channel_counts));
combo_box->addItem (p.deviceName (), QVariant::fromValue (audio_info_type {p, channel_counts}));
if (p == *device)
{
current_index = combo_box->count () - 1;
}
else if (p == default_device)
{
default_index = combo_box->count () - 1;
}
}
if (current_index < 0) // not found - use default
{
*device = default_device;
result = true;
current_index = default_index;
}
combo_box->setCurrentIndex (current_index);
return result;
}
// enable only the channels that are supported by the selected audio device
@ -2910,7 +2825,8 @@ void Configuration::impl::update_audio_channels (QComboBox const * source_combo_
combo_box->setItemData (i, combo_box_item_disabled, Qt::UserRole - 1);
}
Q_FOREACH (QVariant const& v, source_combo_box->itemData (index).toList ())
Q_FOREACH (QVariant const& v
, (source_combo_box->itemData (index).value<audio_info_type> ().second))
{
// enable valid options
int n {v.toInt ()};
@ -2930,6 +2846,20 @@ void Configuration::impl::update_audio_channels (QComboBox const * source_combo_
}
}
void Configuration::impl::find_tab (QWidget * target)
{
for (auto * parent = target->parentWidget (); parent; parent = parent->parentWidget ())
{
auto index = ui_->configuration_tabs->indexOf (parent);
if (index != -1)
{
ui_->configuration_tabs->setCurrentIndex (index);
break;
}
}
target->setFocus ();
}
// load all the supported rig names into the selection combo box
void Configuration::impl::enumerate_rigs ()
{

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>559</width>
<height>553</height>
<width>554</width>
<height>557</height>
</rect>
</property>
<property name="windowTitle">
@ -1349,33 +1349,13 @@ radio interface behave as expected.</string>
<string>Soundcard</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="1">
<widget class="QComboBox" name="sound_output_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="1" column="0">
<widget class="QLabel" name="sound_output_label">
<property name="text">
<string>Ou&amp;tput:</string>
</property>
<property name="toolTip">
<string>Select the audio CODEC to use for transmitting.
If this is your default device for system sounds then
ensure that all system sounds are disabled otherwise
you will broadcast any systems sounds generated during
transmitting periods.</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="sound_input_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the audio CODEC to use for receiving.</string>
<property name="buddy">
<cstring>sound_output_combo_box</cstring>
</property>
</widget>
</item>
@ -1389,33 +1369,6 @@ transmitting periods.</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="sound_input_channel_combo_box">
<property name="toolTip">
<string>Select the channel to use for receiving.</string>
</property>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
<item>
<property name="text">
<string>Both</string>
</property>
</item>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="sound_output_channel_combo_box">
<property name="toolTip">
@ -1446,16 +1399,63 @@ both here.</string>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sound_output_label">
<property name="text">
<string>Ou&amp;tput:</string>
<item row="0" column="1">
<widget class="QComboBox" name="sound_input_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buddy">
<cstring>sound_output_combo_box</cstring>
<property name="toolTip">
<string>Select the audio CODEC to use for receiving.</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="sound_output_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Select the audio CODEC to use for transmitting.
If this is your default device for system sounds then
ensure that all system sounds are disabled otherwise
you will broadcast any systems sounds generated during
transmitting periods.</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="sound_input_channel_combo_box">
<property name="toolTip">
<string>Select the channel to use for receiving.</string>
</property>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
<item>
<property name="text">
<string>Both</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
@ -1493,7 +1493,8 @@ both here.</string>
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
<string notr="true">background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);</string>
</property>
<property name="text">
<string>TextLabel</string>
@ -1541,7 +1542,8 @@ both here.</string>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
<string notr="true">background-color: rgb(255, 255, 255);
color: rgb(0, 0, 0);</string>
</property>
<property name="text">
<string>TextLabel</string>
@ -2949,14 +2951,20 @@ Right click for insert and delete options.</string>
<tabstop>use_dynamic_grid</tabstop>
<tabstop>region_combo_box</tabstop>
<tabstop>type_2_msg_gen_combo_box</tabstop>
<tabstop>decodes_from_top_check_box</tabstop>
<tabstop>insert_blank_check_box</tabstop>
<tabstop>miles_check_box</tabstop>
<tabstop>TX_messages_check_box</tabstop>
<tabstop>DXCC_check_box</tabstop>
<tabstop>ppfx_check_box</tabstop>
<tabstop>font_push_button</tabstop>
<tabstop>decoded_text_font_push_button</tabstop>
<tabstop>monitor_off_check_box</tabstop>
<tabstop>monitor_last_used_check_box</tabstop>
<tabstop>quick_call_check_box</tabstop>
<tabstop>disable_TX_on_73_check_box</tabstop>
<tabstop>force_call_1st_check_box</tabstop>
<tabstop>alternate_bindings_check_box</tabstop>
<tabstop>CW_id_after_73_check_box</tabstop>
<tabstop>enable_VHF_features_check_box</tabstop>
<tabstop>tx_QSY_check_box</tabstop>
@ -2975,8 +2983,8 @@ Right click for insert and delete options.</string>
<tabstop>CAT_one_stop_bit_radio_button</tabstop>
<tabstop>CAT_two_stop_bit_radio_button</tabstop>
<tabstop>CAT_handshake_default_radio_button</tabstop>
<tabstop>CAT_handshake_none_radio_button</tabstop>
<tabstop>CAT_handshake_xon_radio_button</tabstop>
<tabstop>CAT_handshake_none_radio_button</tabstop>
<tabstop>CAT_handshake_hardware_radio_button</tabstop>
<tabstop>force_DTR_combo_box</tabstop>
<tabstop>force_RTS_combo_box</tabstop>
@ -3014,6 +3022,7 @@ Right click for insert and delete options.</string>
<tabstop>clear_DX_check_box</tabstop>
<tabstop>opCallEntry</tabstop>
<tabstop>psk_reporter_check_box</tabstop>
<tabstop>psk_reporter_tcpip_check_box</tabstop>
<tabstop>udp_server_line_edit</tabstop>
<tabstop>udp_server_port_spin_box</tabstop>
<tabstop>accept_udp_requests_check_box</tabstop>
@ -3028,9 +3037,13 @@ Right click for insert and delete options.</string>
<tabstop>stations_table_view</tabstop>
<tabstop>highlighting_list_view</tabstop>
<tabstop>reset_highlighting_to_defaults_push_button</tabstop>
<tabstop>highlight_by_mode_check_box</tabstop>
<tabstop>only_fields_check_box</tabstop>
<tabstop>include_WAE_check_box</tabstop>
<tabstop>rescan_log_push_button</tabstop>
<tabstop>LotW_CSV_URL_line_edit</tabstop>
<tabstop>LotW_CSV_fetch_push_button</tabstop>
<tabstop>LotW_days_since_upload_spin_box</tabstop>
<tabstop>LotW_CSV_fetch_push_button</tabstop>
<tabstop>sbNtrials</tabstop>
<tabstop>sbAggressive</tabstop>
<tabstop>cbTwoPass</tabstop>
@ -3039,13 +3052,18 @@ Right click for insert and delete options.</string>
<tabstop>sbTxDelay</tabstop>
<tabstop>cbx2ToneSpacing</tabstop>
<tabstop>cbx4ToneSpacing</tabstop>
<tabstop>rbLowSidelobes</tabstop>
<tabstop>rbMaxSensitivity</tabstop>
<tabstop>gbSpecialOpActivity</tabstop>
<tabstop>rbFox</tabstop>
<tabstop>rbHound</tabstop>
<tabstop>rbNA_VHF_Contest</tabstop>
<tabstop>rbEU_VHF_Contest</tabstop>
<tabstop>rbField_Day</tabstop>
<tabstop>Field_Day_Exchange</tabstop>
<tabstop>rbEU_VHF_Contest</tabstop>
<tabstop>rbRTTY_Roundup</tabstop>
<tabstop>RTTY_Exchange</tabstop>
<tabstop>rbWW_DIGI</tabstop>
</tabstops>
<resources/>
<connections>
@ -3116,12 +3134,12 @@ Right click for insert and delete options.</string>
</connections>
<buttongroups>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
</buttongroups>
</ui>

View File

@ -56,6 +56,7 @@ void Detector::clear ()
qint64 Detector::writeData (char const * data, qint64 maxSize)
{
//qDebug () << "Detector::writeData: size:" << maxSize;
static unsigned mstr0=999999;
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
@ -119,8 +120,7 @@ qint64 Detector::writeData (char const * data, qint64 maxSize)
remaining -= numFramesProcessed;
}
return maxSize; // we drop any data past the end of the buffer on
// the floor until the next period starts
// we drop any data past the end of the buffer on the floor until
// the next period starts
return maxSize;
}

14
INSTALL
View File

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

View File

@ -105,7 +105,14 @@ void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSym
// qDebug() << "delay_ms:" << delay_ms << "mstr:" << mstr << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_stream = stream;
if (m_stream) m_stream->restart (this);
if (m_stream)
{
m_stream->restart (this);
}
else
{
qDebug () << "Modulator::start: no audio output stream assigned";
}
}
void Modulator::tune (bool newState)
@ -172,15 +179,12 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
{
samples = load (0, samples); // silence
} while (--m_silentFrames && samples != end);
qDebug () << "played:" << framesGenerated << "silent frames";
if (!m_silentFrames)
{
Q_EMIT stateChanged ((m_state = Active));
}
}
// qDebug() << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_cwLevel = false;
m_ramp = 0; // prepare for CW wave shaping
}

View File

@ -465,7 +465,11 @@ void MessageClient::set_server (QString const& server)
{
// queue a host address lookup
TRACE_UDP ("server host DNS lookup:" << server);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, &MessageClient::impl::host_info_results);
#else
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo)));
#endif
}
}

View File

@ -65,7 +65,6 @@ public:
// This timer sets the interval to check for spots to send.
connect (&report_timer_, &QTimer::timeout, [this] () {send_report ();});
report_timer_.start (MIN_SEND_INTERVAL * 1000);
// This timer repeats the sending of IPFIX templates and receiver
// information if we are using UDP, in case server has been
@ -80,7 +79,6 @@ public:
send_receiver_data_ = 3; // three times
}
});
descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly
}
void check_connection ()
@ -156,6 +154,25 @@ public:
// use this for pseudo connection with UDP, allows us to use
// QIODevice::write() instead of QUDPSocket::writeDatagram()
socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly);
if (!report_timer_.isActive ())
{
report_timer_.start (MIN_SEND_INTERVAL * 1000);
}
if (!descriptor_timer_.isActive ())
{
descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly
}
}
void stop ()
{
if (socket_)
{
socket_->disconnectFromHost ();
}
descriptor_timer_.stop ();
report_timer_.stop ();
}
void send_report (bool send_residue = false);
@ -402,7 +419,13 @@ void PSKReporter::impl::send_report (bool send_residue)
writeUtfString (tx_out, spot.grid_);
tx_out
<< quint8 (1u) // REPORTER_SOURCE_AUTOMATIC
<< static_cast<quint32> (spot.time_.toSecsSinceEpoch ());
<< static_cast<quint32> (
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
spot.time_.toSecsSinceEpoch ()
#else
spot.time_.toMSecsSinceEpoch () / 1000
#endif
);
}
auto len = payload_.size () + tx_data_.size ();
@ -429,7 +452,13 @@ void PSKReporter::impl::send_report (bool send_residue)
// insert Length and Export Time
set_length (message, payload_);
message.device ()->seek (2 * sizeof (quint16));
message << static_cast<quint32> (QDateTime::currentDateTime ().toSecsSinceEpoch ());
message << static_cast<quint32> (
#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
QDateTime::currentDateTime ().toSecsSinceEpoch ()
#else
QDateTime::currentDateTime ().toMSecsSinceEpoch () / 1000
#endif
);
// Send data to PSK Reporter site
socket_->write (payload_); // TODO: handle errors
@ -465,6 +494,7 @@ void PSKReporter::reconnect ()
void PSKReporter::setLocalStation (QString const& call, QString const& gridSquare, QString const& antenna)
{
m_->check_connection ();
if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_)
{
m_->send_receiver_data_ = m_->socket_
@ -478,6 +508,7 @@ void PSKReporter::setLocalStation (QString const& call, QString const& gridSquar
bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq
, QString const& mode, int snr)
{
m_->check_connection ();
if (m_->socket_ && m_->socket_->isValid ())
{
if (QAbstractSocket::UnconnectedState == m_->socket_->state ())
@ -490,7 +521,14 @@ bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Ra
return false;
}
void PSKReporter::sendReport ()
void PSKReporter::sendReport (bool last)
{
m_->send_report (true);
if (m_->socket_ && QAbstractSocket::ConnectedState == m_->socket_->state ())
{
m_->send_report (true);
}
if (last)
{
m_->stop ();
}
}

View File

@ -29,7 +29,7 @@ public:
//
// Flush any pending spots to PSK Reporter
//
void sendReport ();
void sendReport (bool last = false);
Q_SIGNAL void errorOccurred (QString const& reason);

View File

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

1609
cty.dat

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,11 @@ sudo dpkg -P wsjtx
You may also need to execute the following command in a terminal:
[example]
sudo apt install libqt5multimedia5-plugins libqt5serialport5 libqt5sql5-sqlite libfftw3-single3
....
sudo apt install libgfortran5 libqt5widgets5 libqt5network5 \
libqt5printsupport5 libqt5multimedia5-plugins libqt5serialport5 \
libqt5sql5-sqlite libfftw3-single3 libgomp1 libusb-1.0-0
....
Fedora, CentOS, Red Hat, and other rpm-based systems:
@ -70,4 +74,8 @@ sudo rpm -e wsjtx
You may also need to execute the following command in a terminal:
[example]
sudo dnf install fftw-libs-single qt5-qtmultimedia qt5-qtserialport
....
sudo dnf install libgfortran fftw-libs-single qt5-qtbase \
qt5-qtmultimedia qt5-qtserialport qt5-qtsvg \
qt5-qtserialport libgomp libusbx
....

View File

@ -216,7 +216,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
character*38 c
character*36 a2
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
logical unpk28_success,unpk77_success
logical unpk28_success,unpk77_success,unpkg4_success
logical dxcall13_set,mycall13_set
data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/,nzzz/46656/
@ -364,10 +364,11 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
idbm=nint(idbm*10.0/3.0)
call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false.
call to_grid4(igrid4,grid4)
call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
write(crpt,'(i3)') idbm
msg=trim(call_1)//' '//grid4//' '//trim(adjustl(crpt))
call save_hash_call(call_1,n10,n12,n22) !### Is this OK here? ###
if (unpk77_success) call save_hash_call(call_1,n10,n12,n22) !### Is this OK here? ###
else if(itype.eq.2) then
! WSPR Type 2
@ -417,10 +418,9 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
n28=n22+2063592
call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false.
call to_grid(igrid6,grid6)
call to_grid(igrid6,grid6,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
msg=trim(call_1)//' '//grid6
endif
else if(i3.eq.0 .and. n3.gt.6) then
@ -452,7 +452,8 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
if(i.ge.4) call add_call_to_recent_calls(call_2)
endif
if(igrid4.le.MAXGRID4) then
call to_grid4(igrid4,grid4)
call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//grid4
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//grid4
if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false.
@ -569,7 +570,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
nrs=52+irpt
write(cexch,1022) nrs,iserial
1022 format(i2,i4.4)
call to_grid6(igrid6,grid6)
call to_grid6(igrid6,grid6,unpk77_success)
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//cexch//' '//grid6
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//cexch//' '//grid6
@ -1499,60 +1500,84 @@ subroutine add_call_to_recent_calls(callsign)
return
end subroutine add_call_to_recent_calls
subroutine to_grid4(n,grid4)
subroutine to_grid4(n,grid4,ok)
character*4 grid4
logical ok
ok=.false.
j1=n/(18*10*10)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10
j2=n/(10*10)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10
j3=n/10
if (j3.lt.0.or.j3.gt.9) goto 900
j4=n-j3*10
if (j4.lt.0.or.j4.gt.9) goto 900
grid4(1:1)=char(j1+ichar('A'))
grid4(2:2)=char(j2+ichar('A'))
grid4(3:3)=char(j3+ichar('0'))
grid4(4:4)=char(j4+ichar('0'))
return
ok=.true.
900 return
end subroutine to_grid4
subroutine to_grid6(n,grid6)
subroutine to_grid6(n,grid6,ok)
character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*24*24)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10*24*24
j2=n/(10*10*24*24)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10*24*24
j3=n/(10*24*24)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*24*24
j4=n/(24*24)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*24*24
j5=n/24
if (j5.lt.0.or.j5.gt.23) goto 900
j6=n-j5*24
if (j6.lt.0.or.j6.gt.23) goto 900
grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A'))
grid6(3:3)=char(j3+ichar('0'))
grid6(4:4)=char(j4+ichar('0'))
grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A'))
ok=.true.
return
900 return
end subroutine to_grid6
subroutine to_grid(n,grid6)
subroutine to_grid(n,grid6,ok)
! 4-, or 6-character grid
character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*25*25)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10*25*25
j2=n/(10*10*25*25)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10*25*25
j3=n/(10*25*25)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*25*25
j4=n/(25*25)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*25*25
j5=n/25
if (j5.lt.0.or.j5.gt.24) goto 900
j6=n-j5*25
if (j6.lt.0.or.j6.gt.24) goto 900
grid6=''
grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A'))
@ -1562,8 +1587,9 @@ subroutine to_grid(n,grid6)
grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A'))
endif
ok=.true.
return
900 return
end subroutine to_grid
end module packjt77

View File

@ -208,7 +208,6 @@ subroutine multimode_decoder(ss,id2,params,nfsample)
! We're in FST4 mode
ndepth=iand(params%ndepth,3)
iwspr=0
if(iand(params%ndepth,128).ne.0) iwspr=2
call timer('dec240 ',0)
call my_fst4%decode(fst4_decoded,id2,params%nutc, &
params%nQSOProgress,params%nfqso,params%nfa,params%nfb, &

View File

@ -319,12 +319,12 @@ AD1CCty::AD1CCty (Configuration const * 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
// speed up startup. Not urgent as it takes less than 0.5s 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
: QString {":/"} + file_name; // or original in the resources FS
: configuration->data_dir ().absoluteFilePath (file_name); // or original
QFile file {m_->path_};
if (file.open (QFile::ReadOnly))
{

View File

@ -97,7 +97,7 @@ namespace
int main(int argc, char *argv[])
{
// ### Add timestamps to all debug messages
// qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
// qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
init_random_seed ();

View File

@ -15,14 +15,14 @@ namespace
Radio::Frequency lower_bound_;
Radio::Frequency upper_bound_;
} constexpr ADIF_bands[] = {
{"2190m", 136000u, 137000u},
{"2190m", 135700u, 137800u},
{"630m", 472000u, 479000u},
{"560m", 501000u, 504000u},
{"160m", 1800000u, 2000000u},
{"80m", 3500000u, 4000000u},
{"60m", 5060000u, 5450000u},
{"40m", 7000000u, 7300000u},
{"30m", 10000000u, 10150000u},
{"30m", 10100000u, 10150000u},
{"20m", 14000000u, 14350000u},
{"17m", 18068000u, 18168000u},
{"15m", 21000000u, 21450000u},

View File

@ -37,14 +37,14 @@ void update_dynamic_property (QWidget * widget, char const * property, QVariant
widget->update ();
}
QDateTime qt_round_date_time_to (QDateTime dt, int seconds)
QDateTime qt_round_date_time_to (QDateTime dt, int milliseconds)
{
dt.setSecsSinceEpoch (dt.addSecs (seconds - 1).toSecsSinceEpoch () / seconds * seconds);
dt.setMSecsSinceEpoch (dt.addMSecs (milliseconds / 2).toMSecsSinceEpoch () / milliseconds * milliseconds);
return dt;
}
QDateTime qt_truncate_date_time_to (QDateTime dt, int seconds)
QDateTime qt_truncate_date_time_to (QDateTime dt, int milliseconds)
{
dt.setSecsSinceEpoch (dt.toSecsSinceEpoch () / seconds * seconds);
dt.setMSecsSinceEpoch (dt.toMSecsSinceEpoch () / milliseconds * milliseconds);
return dt;
}

View File

@ -69,11 +69,11 @@ QString font_as_stylesheet (QFont const&);
// conditional style sheet updates
void update_dynamic_property (QWidget *, char const * property, QVariant const& value);
// round a QDateTime instance to an interval
QDateTime qt_round_date_time_to (QDateTime dt, int seconds);
// round a QDateTime instance to an integral interval of milliseconds
QDateTime qt_round_date_time_to (QDateTime dt, int milliseconds);
// truncate a QDateTime to an interval
QDateTime qt_truncate_date_time_to (QDateTime dt, int seconds);
// truncate a QDateTime to an integral interval of milliseconds
QDateTime qt_truncate_date_time_to (QDateTime dt, int milliseconds);
template <class T>
class VPtr

23
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
find_package (Qt5Test 5 REQUIRED)
#
# Compiler options
#
set (CMAKE_CXX_STANDARD 11)
add_compile_options ("$<$<COMPILE_LANGUAGE:Fortran>:-Wall;-Wno-conversion;-fno-second-underscore;-fno-f2c>")
add_compile_options ("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-fbounds-check>")
add_compile_options ("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<NOT:$<CONFIG:Debug>>>:-funroll-all-loops>")
add_compile_options ("$<$<AND:$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>,$<OR:$<C_COMPILER_ID:GNU>,$<C_COMPILER_ID:Clang>,$<C_COMPILER_ID:AppleClang>>>:-Wall;-Wextra>")
add_compile_options ("$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:GNU>>:-Wno-pragmas>")
add_compile_options ("$<$<AND:$<OR:$<C_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:GNU>>,$<NOT:$<CONFIG:Debug>>>:-fdata-sections;-ffunction-sections>")
if (${OPENMP_FOUND} OR APPLE)
add_compile_options ("$<$<AND:$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>,$<C_COMPILER_ID:GNU>>:${OpenMP_C_FLAGS}>")
endif ()
# Tell CMake to run moc when necessary
set (CMAKE_AUTOMOC ON)
include_directories (${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR})
add_executable (test_qt_helpers test_qt_helpers.cpp)
target_link_libraries (test_qt_helpers wsjt_qt Qt5::Test)
add_test (test_qt_helpers test_qt_helpers)

138
tests/test_qt_helpers.cpp Normal file
View File

@ -0,0 +1,138 @@
#include <QtTest>
#include <QDateTime>
#include <QDebug>
#include "qt_helpers.hpp"
class TestQtHelpers
: public QObject
{
Q_OBJECT
public:
private:
Q_SLOT void round_15s_date_time_up ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 500}};
QCOMPARE (qt_round_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 30)));
}
Q_SLOT void truncate_15s_date_time_up ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 500}};
QCOMPARE (qt_truncate_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void round_15s_date_time_down ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 499}};
QCOMPARE (qt_round_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void truncate_15s_date_time_down ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 499}};
QCOMPARE (qt_truncate_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void round_15s_date_time_on ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 15}};
QCOMPARE (qt_round_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void truncate_15s_date_time_on ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 15}};
QCOMPARE (qt_truncate_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void round_15s_date_time_under ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 14, 999}};
QCOMPARE (qt_round_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void truncate_15s_date_time_under ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 14, 999}};
QCOMPARE (qt_truncate_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15)));
}
Q_SLOT void round_15s_date_time_over ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 15, 1}};
QCOMPARE (qt_round_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void truncate_15s_date_time_over ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 15, 1}};
QCOMPARE (qt_truncate_date_time_to (dt, 15000), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void round_7p5s_date_time_up ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 26, 250}};
QCOMPARE (qt_round_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 30)));
}
Q_SLOT void truncate_7p5s_date_time_up ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 26, 250}};
QCOMPARE (qt_truncate_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void round_7p5s_date_time_down ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 26, 249}};
QCOMPARE (qt_round_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void truncate_7p5s_date_time_down ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 26, 249}};
QCOMPARE (qt_truncate_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void round_7p5s_date_time_on ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 500}};
QCOMPARE (qt_round_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void truncate_7p5s_date_time_on ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 500}};
QCOMPARE (qt_truncate_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void round_7p5s_date_time_under ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 499}};
QCOMPARE (qt_round_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void truncate_7p5s_date_time_under ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 499}};
QCOMPARE (qt_truncate_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 15)));
}
Q_SLOT void round_7p5s_date_time_over ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 501}};
QCOMPARE (qt_round_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
Q_SLOT void truncate_7p5s_date_time_over ()
{
QDateTime dt {QDate {2020, 8, 6}, QTime {14, 15, 22, 501}};
QCOMPARE (qt_truncate_date_time_to (dt, 7500), QDateTime (QDate (2020, 8, 6), QTime (14, 15, 22, 500)));
}
};
QTEST_MAIN (TestQtHelpers);
#include "test_qt_helpers.moc"

View File

@ -210,6 +210,8 @@ namespace
QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"};
auto quint32_max = std::numeric_limits<quint32>::max ();
constexpr int N_WIDGETS {34};
constexpr int rx_chunk_size {3456}; // audio samples at 12000 Hz
constexpr int tx_audio_buffer_size {48000 / 5}; // audio frames at 48000 Hz
bool message_is_73 (int type, QStringList const& msg_parts)
{
@ -284,7 +286,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_idleMinutes {0},
m_nSubMode {0},
m_nclearave {1},
m_pctx {0},
m_nseq {0},
m_nWSPRdecodes {0},
m_k0 {9999999},
@ -310,7 +311,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_bShMsgs {false},
m_bSWL {false},
m_uploading {false},
m_txNext {false},
m_grid6 {false},
m_tuneup {false},
m_bTxTime {false},
@ -395,8 +395,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
},
m_sfx {"P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A"},
mem_jt9 {shdmem},
m_msAudioOutputBuffered (0u),
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
m_downSampleFactor (downSampleFactor),
m_audioThreadPriority (QThread::HighPriority),
m_bandEdited {false},
@ -472,9 +470,23 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (this, &MainWindow::startAudioInputStream, m_soundInput, &SoundInput::start);
connect (this, &MainWindow::suspendAudioInputStream, m_soundInput, &SoundInput::suspend);
connect (this, &MainWindow::resumeAudioInputStream, m_soundInput, &SoundInput::resume);
connect (this, &MainWindow::reset_audio_input_stream, m_soundInput, &SoundInput::reset);
connect (this, &MainWindow::finished, m_soundInput, &SoundInput::stop);
connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError);
// connect(m_soundInput, &SoundInput::status, this, &MainWindow::showStatusMessage);
connect (m_soundInput, &SoundInput::dropped_frames, this, [this] (qint32 dropped_frames, qint64 usec) {
if (dropped_frames > 48000 / 5) // 1/5 second
{
showStatusMessage (tr ("%1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3));
}
if (dropped_frames > 48000) // 1 second
{
MessageBox::warning_message (this
, tr ("Audio Source")
, tr ("Reduce system load")
, tr ("Excessive dropped samples - %1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3));
}
});
connect (&m_audioThread, &QThread::finished, m_soundInput, &QObject::deleteLater);
connect (this, &MainWindow::finished, this, &MainWindow::close);
@ -939,14 +951,24 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (&m_wav_future_watcher, &QFutureWatcher<void>::finished, this, &MainWindow::diskDat);
connect(&watcher3, SIGNAL(finished()),this,SLOT(fast_decode_done()));
Q_EMIT startAudioInputStream (m_config.audio_input_device (), m_framesAudioInputBuffered, m_detector, m_downSampleFactor, m_config.audio_input_channel ());
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered);
if (!m_config.audio_input_device ().isNull ())
{
Q_EMIT startAudioInputStream (m_config.audio_input_device ()
, rx_chunk_size * m_downSampleFactor
, m_detector, m_downSampleFactor, m_config.audio_input_channel ());
}
if (!m_config.audio_output_device ().isNull ())
{
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device ()
, AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2
, tx_audio_buffer_size);
}
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
ui->label_9->setStyleSheet("QLabel{background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{background-color: #aabec8}");
ui->label_9->setStyleSheet("QLabel{color: #000000; background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{color: #000000; background-color: #aabec8}");
// this must be done before initializing the mode as some modes need
// to turn off split on the rig e.g. WSPR
@ -974,7 +996,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
}
m_saveDecoded=ui->actionSave_decoded->isChecked();
m_saveAll=ui->actionSave_all->isChecked();
ui->sbTxPercent->setValue(m_pctx);
ui->TxPowerComboBox->setCurrentIndex(int(.3 * m_dBm + .2));
ui->cbUploadWSPR_Spots->setChecked(m_uploadWSPRSpots);
if((m_ndepth&7)==1) ui->actionQuickDecode->setChecked(true);
@ -997,12 +1018,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_isort=-3;
m_max_dB=70;
m_CQtype="CQ";
if(m_mode=="WSPR" and m_pctx>0) {
QPalette palette {ui->sbTxPercent->palette ()};
palette.setColor(QPalette::Base,Qt::yellow);
ui->sbTxPercent->setPalette(palette);
}
fixStop();
VHF_features_enabled(m_config.enable_VHF_features());
m_wideGraph->setVHF(m_config.enable_VHF_features());
@ -1146,7 +1161,7 @@ void MainWindow::writeSettings()
m_settings->setValue("GUItab",ui->tabWidget->currentIndex());
m_settings->setValue("OutBufSize",outBufSize);
m_settings->setValue ("HoldTxFreq", ui->cbHoldTxFreq->isChecked ());
m_settings->setValue("PctTx",m_pctx);
m_settings->setValue("PctTx", ui->sbTxPercent->value ());
m_settings->setValue("dBm",m_dBm);
m_settings->setValue("RR73",m_send_RR73);
m_settings->setValue ("WSPRPreferType1", ui->WSPR_prefer_type_1_check_box->isChecked ());
@ -1237,7 +1252,8 @@ void MainWindow::readSettings()
ui->TxFreqSpinBox->setValue(0); // ensure a change is signaled
ui->TxFreqSpinBox->setValue(m_settings->value("TxFreq",1500).toInt());
m_ndepth=m_settings->value("NDepth",3).toInt();
m_pctx=m_settings->value("PctTx",20).toInt();
ui->sbTxPercent->setValue (m_settings->value ("PctTx", 20).toInt ());
on_sbTxPercent_valueChanged (ui->sbTxPercent->value ());
m_dBm=m_settings->value("dBm",37).toInt();
m_send_RR73=m_settings->value("RR73",false).toBool();
if(m_send_RR73) {
@ -1246,7 +1262,6 @@ void MainWindow::readSettings()
}
ui->WSPR_prefer_type_1_check_box->setChecked (m_settings->value ("WSPRPreferType1", true).toBool ());
m_uploadWSPRSpots=m_settings->value("UploadSpots",false).toBool();
if(!m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}");
ui->cbNoOwnCall->setChecked(m_settings->value("NoOwnCall",false).toBool());
ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool());
// setup initial value of tx attenuator
@ -1279,8 +1294,6 @@ void MainWindow::readSettings()
// use these initialisation settings to tune the audio o/p buffer
// size and audio thread priority
m_settings->beginGroup ("Tune");
m_msAudioOutputBuffered = m_settings->value ("Audio/OutputBufferMs").toInt ();
m_framesAudioInputBuffered = m_settings->value ("Audio/InputBufferFrames", RX_SAMPLE_RATE / 10).toInt ();
m_audioThreadPriority = static_cast<QThread::Priority> (m_settings->value ("Audio/ThreadPriority", QThread::HighPriority).toInt () % 8);
m_settings->endGroup ();
@ -1545,6 +1558,10 @@ void MainWindow::dataSink(qint64 frames)
if(m_mode!="WSPR") decode(); //Start decoder
if(m_mode=="FT8" and !m_diskData and (m_ihsym==m_earlyDecode or m_ihsym==m_earlyDecode2)) return;
if (!m_diskData)
{
Q_EMIT reset_audio_input_stream (true); // signals dropped samples
}
if(!m_diskData and (m_saveAll or m_saveDecoded or m_mode=="WSPR" or m_mode=="FST4W")) {
//Always save unless "Save None"; may delete later
if(m_TRperiod < 60) {
@ -1623,14 +1640,15 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int
format.setChannelCount (1);
format.setSampleSize (16);
format.setSampleType (QAudioFormat::SignedInt);
auto source = QString {"%1, %2"}.arg (my_callsign).arg (my_grid);
auto comment = QString {"Mode=%1%2, Freq=%3%4"}
.arg (mode)
.arg (QString {(mode.contains ('J') && !mode.contains ('+')) || mode.startsWith ("FST4")
? QString {", Sub Mode="} + QChar {'A' + sub_mode}
: QString {}})
.arg (Radio::frequency_MHz_string (frequency))
.arg (QString {mode!="WSPR" ? QString {", DXCall=%1, DXGrid=%2"}
auto source = QString {"%1; %2"}.arg (my_callsign).arg (my_grid);
auto comment = QString {"Mode=%1%2; Freq=%3%4"}
.arg (mode)
.arg (QString {(mode.contains ('J') && !mode.contains ('+'))
|| mode.startsWith ("FST4") || mode.startsWith ("QRA")
? QString {"; Sub Mode="} + QString::number (int (samples / 12000)) + QChar {'A' + sub_mode}
: QString {}})
.arg (Radio::frequency_MHz_string (frequency))
.arg (QString {mode!="WSPR" ? QString {"; DXCall=%1; DXGrid=%2"}
.arg (his_call)
.arg (his_grid).toLocal8Bit () : ""});
BWFFile::InfoDictionary list_info {
@ -1792,6 +1810,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
auto callsign = m_config.my_callsign ();
auto my_grid = m_config.my_grid ();
SpecOp nContest0=m_config.special_op_id();
auto psk_on = m_config.spot_to_psk_reporter ();
if (QDialog::Accepted == m_config.exec ()) {
checkMSK144ContestType();
if (m_config.my_callsign () != callsign) {
@ -1805,18 +1824,25 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
on_dxGridEntry_textChanged (m_hisGrid); // recalculate distances in case of units change
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
if(m_config.spot_to_psk_reporter ()) pskSetLocal ();
pskSetLocal ();
// this will close the connection to PSKReporter if it has been
// disabled
if (psk_on && !m_config.spot_to_psk_reporter ())
{
m_psk_Reporter.sendReport (true);
}
if(m_config.restart_audio_input ()) {
Q_EMIT startAudioInputStream (m_config.audio_input_device (),
m_framesAudioInputBuffered, m_detector, m_downSampleFactor,
m_config.audio_input_channel ());
Q_EMIT startAudioInputStream (m_config.audio_input_device ()
, rx_chunk_size * m_downSampleFactor
, m_detector, m_downSampleFactor
, m_config.audio_input_channel ());
}
if(m_config.restart_audio_output ()) {
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (),
AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2,
m_msAudioOutputBuffered);
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device ()
, AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2
, tx_audio_buffer_size);
}
displayDialFrequency ();
@ -1918,18 +1944,14 @@ void MainWindow::on_autoButton_clicked (bool checked)
m_nclearave=1;
echocom_.nsum=0;
}
if(m_mode=="WSPR" or m_mode=="FST4W") {
QPalette palette {ui->sbTxPercent->palette ()};
if(m_auto or m_pctx==0) {
palette.setColor(QPalette::Base,Qt::white);
} else {
palette.setColor(QPalette::Base,Qt::yellow);
}
ui->sbTxPercent->setPalette(palette);
}
m_tAutoOn=QDateTime::currentMSecsSinceEpoch()/1000;
}
void MainWindow::on_sbTxPercent_valueChanged (int n)
{
update_dynamic_property (ui->sbTxPercent, "notx", !n);
}
void MainWindow::auto_tx_mode (bool state)
{
ui->autoButton->setChecked (state);
@ -2288,8 +2310,8 @@ bool MainWindow::eventFilter (QObject * object, QEvent * event)
void MainWindow::createStatusBar() //createStatusBar
{
tx_status_label.setAlignment (Qt::AlignHCenter);
tx_status_label.setMinimumSize (QSize {150, 18});
tx_status_label.setStyleSheet ("QLabel{background-color: #00ff00}");
tx_status_label.setMinimumSize (QSize {100, 18});
tx_status_label.setStyleSheet ("QLabel{color: #000000; background-color: #00ff00}");
tx_status_label.setFrameStyle (QFrame::Panel | QFrame::Sunken);
statusBar()->addWidget (&tx_status_label);
@ -2337,39 +2359,39 @@ void MainWindow::setup_status_bar (bool vhf)
mode_label.setText (m_mode);
}
if ("ISCAT" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff9933}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff9933}");
} else if ("JT9" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6ec7}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6ec7}");
} else if ("JT4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #cc99ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #cc99ff}");
} else if ("Echo" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #66ffff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ffff}");
} else if ("JT9+JT65" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ffff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ffff66}");
} else if ("JT65" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #66ff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}");
} else if ("QRA64" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #99ff33}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}");
} else if ("QRA66" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #99ff33}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}");
} else if ("MSK144" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6666}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6666}");
} else if ("FT4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff0099}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff0099}");
} else if ("FT8" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6699}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6699}");
} else if ("FST4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #99ff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff66}");
} else if ("FST4W" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #6699ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #6699ff}");
} else if ("FreqCal" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff9933}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff9933}");
}
last_tx_label.setText (QString {});
if (m_mode.contains (QRegularExpression {R"(^(Echo|ISCAT))"})) {
if (band_hopping_label.isVisible ()) statusBar ()->removeWidget (&band_hopping_label);
} else if (m_mode=="WSPR") {
mode_label.setStyleSheet ("QLabel{background-color: #ff66ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff66ff}");
if (!band_hopping_label.isVisible ()) {
statusBar ()->addWidget (&band_hopping_label);
band_hopping_label.show ();
@ -2700,7 +2722,7 @@ void MainWindow::on_actionOpen_triggered() //Open File
m_path=fname;
int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1);
tx_status_label.setStyleSheet("QLabel{background-color: #99ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #99ffff}");
tx_status_label.setText(" " + baseName + " ");
on_stopButton_clicked();
m_diskData=true;
@ -2780,7 +2802,7 @@ void MainWindow::on_actionOpen_next_in_directory_triggered() //Open Next
m_path=fname;
int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1);
tx_status_label.setStyleSheet("QLabel{background-color: #99ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #99ffff}");
tx_status_label.setText(" " + baseName + " ");
m_diskData=true;
read_wav_file (fname);
@ -2933,7 +2955,7 @@ void MainWindow::on_actionSpecial_mouse_commands_triggered()
<td><b>Click</b> to set Rx frequency.<br/>
<b>Shift-click</b> to set Tx frequency.<br/>
<b>Ctrl-click</b> or <b>Right-click</b> to set Rx and Tx frequencies.<br/>
<b>Double-click</b> to also decode at Rx frequency.<br/>
<b>Double-click</b> to also decode at Rx frequency.<br/>
</td>
</tr>
<tr>
@ -2941,10 +2963,10 @@ void MainWindow::on_actionSpecial_mouse_commands_triggered()
<td><b>Double-click</b> to copy second callsign to Dx Call,<br/>
locator to Dx Grid, change Rx and Tx frequency to<br/>
decoded signal's frequency, and generate standard<br/>
messages.<br/>
If <b>Hold Tx Freq</b> is checked or first callsign in message<br/>
is your own call, Tx frequency is not changed unless <br/>
<b>Ctrl</b> is held down.<br/>
messages.<br/>
If <b>Hold Tx Freq</b> is checked or first callsign in message<br/>
is your own call, Tx frequency is not changed unless <br/>
<b>Ctrl</b> is held down.<br/>
</td>
</tr>
<tr>
@ -3122,56 +3144,58 @@ void MainWindow::decode() //decode()
//newdat=1 ==> this is new data, must do the big FFT
//nagain=1 ==> decode only at fQSO +/- Tol
char *to = (char*)mem_jt9->data();
char *from = (char*) dec_data.ipc;
int size=sizeof(struct dec_data);
if(dec_data.params.newdat==0) {
int noffset {offsetof (struct dec_data, params.nutc)};
to += noffset;
from += noffset;
size -= noffset;
}
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
float t0=m_t0;
float t1=m_t1;
qApp->processEvents(); //Update the waterfall
if(m_nPick > 0) {
t0=m_t0Pick;
t1=m_t1Pick;
if (auto * to = reinterpret_cast<char *> (mem_jt9->data()))
{
char *from = (char*) dec_data.ipc;
int size=sizeof(struct dec_data);
if(dec_data.params.newdat==0) {
int noffset {offsetof (struct dec_data, params.nutc)};
to += noffset;
from += noffset;
size -= noffset;
}
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
float t0=m_t0;
float t1=m_t1;
qApp->processEvents(); //Update the waterfall
if(m_nPick > 0) {
t0=m_t0Pick;
t1=m_t1Pick;
}
static short int d2b[360000];
narg[0]=dec_data.params.nutc;
if(m_kdone>int(12000.0*m_TRperiod)) {
m_kdone=int(12000.0*m_TRperiod);
}
narg[1]=m_kdone;
narg[2]=m_nSubMode;
narg[3]=dec_data.params.newdat;
narg[4]=dec_data.params.minSync;
narg[5]=m_nPick;
narg[6]=1000.0*t0;
narg[7]=1000.0*t1;
narg[8]=2; //Max decode lines per decode attempt
if(dec_data.params.minSync<0) narg[8]=50;
if(m_mode=="ISCAT") narg[9]=101; //ISCAT
if(m_mode=="JT9") narg[9]=102; //Fast JT9
if(m_mode=="MSK144") narg[9]=104; //MSK144
narg[10]=ui->RxFreqSpinBox->value();
narg[11]=ui->sbFtol->value ();
narg[12]=0;
narg[13]=-1;
narg[14]=m_config.aggressive();
memcpy(d2b,dec_data.d2,2*360000);
watcher3.setFuture (QtConcurrent::run (std::bind (fast_decode_,&d2b[0],
&narg[0],&m_TRperiod,&m_msg[0][0],
dec_data.params.mycall,dec_data.params.hiscall,8000,12,12)));
} else {
mem_jt9->lock ();
memcpy(to, from, qMin(mem_jt9->size(), size));
mem_jt9->unlock ();
to_jt9(m_ihsym,1,-1); //Send m_ihsym to jt9[.exe] and start decoding
decodeBusy(true);
}
}
static short int d2b[360000];
narg[0]=dec_data.params.nutc;
if(m_kdone>int(12000.0*m_TRperiod)) {
m_kdone=int(12000.0*m_TRperiod);
}
narg[1]=m_kdone;
narg[2]=m_nSubMode;
narg[3]=dec_data.params.newdat;
narg[4]=dec_data.params.minSync;
narg[5]=m_nPick;
narg[6]=1000.0*t0;
narg[7]=1000.0*t1;
narg[8]=2; //Max decode lines per decode attempt
if(dec_data.params.minSync<0) narg[8]=50;
if(m_mode=="ISCAT") narg[9]=101; //ISCAT
if(m_mode=="JT9") narg[9]=102; //Fast JT9
if(m_mode=="MSK144") narg[9]=104; //MSK144
narg[10]=ui->RxFreqSpinBox->value();
narg[11]=ui->sbFtol->value ();
narg[12]=0;
narg[13]=-1;
narg[14]=m_config.aggressive();
memcpy(d2b,dec_data.d2,2*360000);
watcher3.setFuture (QtConcurrent::run (std::bind (fast_decode_,&d2b[0],
&narg[0],&m_TRperiod,&m_msg[0][0],
dec_data.params.mycall,dec_data.params.hiscall,8000,12,12)));
} else {
mem_jt9->lock ();
memcpy(to, from, qMin(mem_jt9->size(), size));
mem_jt9->unlock ();
to_jt9(m_ihsym,1,-1); //Send m_ihsym to jt9[.exe] and start decoding
decodeBusy(true);
}
}
void::MainWindow::fast_decode_done()
@ -3219,12 +3243,14 @@ void::MainWindow::fast_decode_done()
void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone)
{
dec_data_t * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data());
mem_jt9->lock ();
dd->ipc[0]=n;
if(istart>=0) dd->ipc[1]=istart;
if(idone>=0) dd->ipc[2]=idone;
mem_jt9->unlock ();
if (auto * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data()))
{
mem_jt9->lock ();
dd->ipc[0]=n;
if(istart>=0) dd->ipc[1]=istart;
if(idone>=0) dd->ipc[2]=idone;
mem_jt9->unlock ();
}
}
void MainWindow::decodeDone ()
@ -3281,7 +3307,8 @@ void MainWindow::readFromStdout() //readFromStdout
line_read = line_read.left (64);
}
}
if(m_mode!="FT8" and m_mode!="FT4") {
if (m_mode!="FT8" and m_mode!="FT4"
&& !m_mode.startsWith ("FST4")) {
//Pad 22-char msg to at least 37 chars
line_read = line_read.left(44) + " " + line_read.mid(44);
}
@ -3499,9 +3526,8 @@ void MainWindow::readFromStdout() //readFromStdout
bool okToPost=(nsec > int(4*m_TRperiod)/5);
if(m_mode=="FST4W" and okToPost) {
line_read=line_read.left(22) + " CQ " + line_read.trimmed().mid(22);
int n=line_read.trimmed().size();
line_read=line_read.trimmed().left(n-3);
DecodedText FST4W_post {QString::fromUtf8(line_read.constData())};
auto p = line_read.lastIndexOf (' ');
DecodedText FST4W_post {QString::fromUtf8 (line_read.left (p).constData ())};
pskPost(FST4W_post);
} else {
if (stdMsg && okToPost) pskPost(decodedtext);
@ -3741,26 +3767,22 @@ void MainWindow::guiUpdate()
if(m_mode=="WSPR" or m_mode=="FST4W") {
if(m_nseq==0 and m_ntr==0) { //Decide whether to Tx or Rx
m_tuneup=false; //This is not an ATU tuneup
if(ui->sbTxPercent->isEnabled () && m_pctx==0) m_WSPR_tx_next = false; //Don't transmit if m_pctx=0
bool btx = m_auto && m_WSPR_tx_next; // To Tx, we need m_auto and
// scheduled transmit
if(m_auto and m_txNext) btx=true; //TxNext button overrides
if(m_auto && ui->sbTxPercent->isEnabled () && m_pctx==100) btx=true; //Always transmit
m_WSPR_tx_next = false;
if(btx) {
m_ntr=-1; //This says we will have transmitted
m_txNext=false;
ui->pbTxNext->setChecked(false);
ui->pbTxNext->setChecked (false);
m_bTxTime=true; //Start a WSPR or FST4W Tx sequence
} else {
// This will be a WSPR or FST4W Rx sequence.
// This will be a WSPR or FST4W Rx sequence.
m_ntr=1; //This says we will have received
m_bTxTime=false; //Start a WSPR or FST4W Rx sequence
}
}
} else {
// For all modes other than WSPR and Fst4W
// For all modes other than WSPR and Fst4W
m_bTxTime = (t2p >= tx1) and (t2p < tx2);
if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK;
if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) {
@ -4301,11 +4323,11 @@ void MainWindow::guiUpdate()
m_nsendingsh=0;
if(s[4]==64) m_nsendingsh=1;
if(m_nsendingsh==1 or m_currentMessageType==7) {
tx_status_label.setStyleSheet("QLabel{background-color: #66ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #66ffff}");
} else if(m_nsendingsh==-1 or m_currentMessageType==6) {
tx_status_label.setStyleSheet("QLabel{background-color: #ffccff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ffccff}");
} else {
tx_status_label.setStyleSheet("QLabel{background-color: #ffff33}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ffff33}");
}
if(m_tune) {
tx_status_label.setText("Tx: TUNE");
@ -4324,11 +4346,11 @@ void MainWindow::guiUpdate()
}
} else if(m_monitoring) {
if (!m_tx_watchdog) {
tx_status_label.setStyleSheet("QLabel{background-color: #00ff00}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #00ff00}");
auto t = tr ("Receiving");
if(m_mode=="MSK144") {
int npct=int(100.0*m_fCPUmskrtd/0.298667);
if(npct>90) tx_status_label.setStyleSheet("QLabel{background-color: #ff0000}");
if(npct>90) tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ff0000}");
t += QString {" %1%"}.arg (npct, 2);
}
tx_status_label.setText (t);
@ -4649,9 +4671,9 @@ void MainWindow::doubleClickOnCall2(Qt::KeyboardModifiers modifiers)
void MainWindow::doubleClickOnCall(Qt::KeyboardModifiers modifiers)
{
QTextCursor cursor;
if(m_mode=="ISCAT") {
if(m_mode=="ISCAT" or m_mode=="FST4W") {
MessageBox::information_message (this,
"Double-click not available for ISCAT mode");
"Double-click not available for ISCAT or FST4W mode");
return;
}
if(m_decodedText2) {
@ -5042,7 +5064,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
// m_nextGrid=message_words.at(3);
// m_nextRpt=message.report();
// ui->labNextCall->setText("Next: " + m_nextCall);
// ui->labNextCall->setStyleSheet("QLabel {background-color: #66ff66}");
// ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}");
// }
return;
}
@ -5263,11 +5285,11 @@ bool MainWindow::stdCall(QString const& w)
{
static QRegularExpression standard_call_re {
R"(
^\s* # optional leading spaces
^\s* # optional leading spaces
( [A-Z]{0,2} | [A-Z][0-9] | [0-9][A-Z] ) # part 1
( [0-9][A-Z]{0,3} ) # part 2
(/R | /P)? # optional suffix
\s*$ # optional trailing spaces
(/R | /P)? # optional suffix
\s*$ # optional trailing spaces
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
return standard_call_re.match (w).hasMatch ();
}
@ -5384,7 +5406,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
}
}
if((m_mode!="MSK144" and m_mode!="FT8" and m_mode!="FT4")) {
if((m_mode!="MSK144" and m_mode!="FT8" and m_mode!="FT4" && m_mode != "FST4")) {
t=t00 + rpt;
msgtype(t, ui->tx2);
t=t0 + "R" + rpt;
@ -5437,7 +5459,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
case Configuration::type_2_msg_1_full:
msgtype(t + my_grid, ui->tx1);
if (!eme_short_codes) {
if((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4") &&
if((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4" || m_mode == "FST4") &&
SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code
} else {
@ -5450,7 +5472,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
break;
case Configuration::type_2_msg_3_full:
if ((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4") &&
if ((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4" || m_mode == "FST4") &&
SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3);
msgtype(t + "RRR", ui->tx4);
@ -5466,7 +5488,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
case Configuration::type_2_msg_5_only:
msgtype(t00 + my_grid, ui->tx1);
if (!eme_short_codes) {
if ((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4") &&
if ((m_mode=="MSK144" || m_mode=="FT8" || m_mode=="FT4" || m_mode == "FST4") &&
SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code
msgtype(t + "RRR", ui->tx4);
@ -5766,9 +5788,7 @@ void MainWindow::on_tx6_editingFinished() //tx6 edited
void MainWindow::on_RoundRobin_currentTextChanged(QString text)
{
ui->sbTxPercent->setEnabled(text=="Random");
m_WSPR_tx_next = false; // cancel any pending Tx to avoid
// undesirable consecutive Tx periods
ui->sbTxPercent->setEnabled (text == tr ("Random"));
}
@ -6165,7 +6185,7 @@ void MainWindow::on_actionFT8_triggered()
ui->cbAutoSeq->setEnabled(false);
ui->tabWidget->setCurrentIndex(0);
ui->cbHoldTxFreq->setChecked(true);
displayWidgets(nWidgets("11101000010011000001000000000011000"));
displayWidgets(nWidgets("1110100001001100000100000000001100"));
ui->labDXped->setText(tr ("Hound"));
ui->txrb1->setChecked(true);
ui->txrb2->setEnabled(false);
@ -6687,7 +6707,7 @@ void MainWindow::WSPR_config(bool b)
ui->label_7->setVisible(!b and ui->cbMenus->isChecked());
ui->logQSOButton->setVisible(!b);
ui->DecodeButton->setEnabled(!b);
ui->sbTxPercent->setEnabled (m_mode != "FST4W" || "Random" == ui->RoundRobin->currentText ());
ui->sbTxPercent->setEnabled (m_mode != "FST4W" || tr ("Random") == ui->RoundRobin->currentText ());
ui->band_hopping_group_box->setVisible(true);
ui->RoundRobin->setVisible(m_mode=="FST4W");
ui->RoundRobin->lineEdit()->setAlignment(Qt::AlignCenter);
@ -6897,7 +6917,11 @@ void MainWindow::band_changed (Frequency f)
}
m_lastBand.clear ();
m_bandEdited = false;
m_psk_Reporter.sendReport(); // Upload any queued spots before changing band
if (m_config.spot_to_psk_reporter ())
{
// Upload any queued spots before changing band
m_psk_Reporter.sendReport();
}
if (!m_transmitting) monitor (true);
if ("FreqCal" == m_mode)
{
@ -7223,14 +7247,15 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
{
Transceiver::TransceiverState old_state {m_rigState};
//transmitDisplay (s.ptt ());
if (s.ptt () && !m_rigState.ptt ()) { // safe to start audio
if (s.ptt () // && !m_rigState.ptt ()
) { // safe to start audio
// (caveat - DX Lab Suite Commander)
if (m_tx_when_ready && g_iptt) { // waiting to Tx and still needed
int ms_delay=1000*m_config.txDelay();
if(m_mode=="FT4") ms_delay=20;
ptt1Timer.start(ms_delay); //Start-of-transmission sequencer delay
m_tx_when_ready = false;
}
m_tx_when_ready = false;
}
m_rigState = s;
auto old_freqNominal = m_freqNominal;
@ -7266,9 +7291,7 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
|| !(ui->cbCQTx->isEnabled () && ui->cbCQTx->isVisible () && ui->cbCQTx->isChecked()))) {
m_lastDialFreq = m_freqNominal;
m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000;
if (m_config.spot_to_psk_reporter ()) {
pskSetLocal ();
}
pskSetLocal ();
statusChanged();
m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
}
@ -7585,6 +7608,8 @@ bool MainWindow::shortList(QString callsign)
void MainWindow::pskSetLocal ()
{
if (!m_config.spot_to_psk_reporter ()) return;
// find the station row, if any, that matches the band we are on
auto stations = m_config.stations ();
auto matches = stations->match (stations->index (0, StationList::band_column)
@ -8070,8 +8095,8 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout
QString MainWindow::beacon_start_time (int n)
{
auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod);
if (m_TRperiod < 60)
auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod * 1.e3);
if (m_TRperiod < 60.)
{
return bt.toString ("HHmmss");
}
@ -8125,17 +8150,18 @@ void MainWindow::uploadWSPRSpots (bool direct_post, QString const& decode_text)
QString rfreq = QString("%1").arg((m_dialFreqRxWSPR + 1500) / 1e6, 0, 'f', 6);
QString tfreq = QString("%1").arg((m_dialFreqRxWSPR +
ui->TxFreqSpinBox->value()) / 1e6, 0, 'f', 6);
auto pct = QString::number (ui->autoButton->isChecked () ? ui->sbTxPercent->value () : 0);
if (!direct_post)
{
wsprNet->upload (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq,
m_mode, m_TRperiod, QString::number (ui->autoButton->isChecked () ? m_pctx : 0),
m_mode, m_TRperiod, pct,
QString::number (m_dBm), version (),
m_config.writeable_data_dir ().absoluteFilePath ("wspr_spots.txt"));
}
else
{
wsprNet->post (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq,
m_mode, m_TRperiod, QString::number (ui->autoButton->isChecked () ? m_pctx : 0),
m_mode, m_TRperiod, pct,
QString::number (m_dBm), version (), decode_text);
}
if (!decode_text.size ())
@ -8161,24 +8187,9 @@ void MainWindow::on_TxPowerComboBox_currentIndexChanged(int index)
m_dBm = ui->TxPowerComboBox->itemData (index).toInt ();
}
void MainWindow::on_sbTxPercent_valueChanged(int n)
{
m_pctx=n;
if(m_pctx>0) {
ui->pbTxNext->setEnabled(true);
} else {
m_txNext=false;
ui->pbTxNext->setChecked(false);
ui->pbTxNext->setEnabled(false);
}
}
void MainWindow::on_cbUploadWSPR_Spots_toggled(bool b)
{
m_uploadWSPRSpots=b;
if(m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet("");
if(!m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet(
"QCheckBox{background-color: yellow}");
}
void MainWindow::on_WSPRfreqSpinBox_valueChanged(int n)
@ -8188,13 +8199,22 @@ void MainWindow::on_WSPRfreqSpinBox_valueChanged(int n)
void MainWindow::on_pbTxNext_clicked(bool b)
{
m_txNext=b;
if (b && !ui->autoButton->isChecked ())
{
ui->autoButton->click (); // make sure Tx is possible
}
}
void MainWindow::WSPR_scheduling ()
{
if (ui->pbTxNext->isEnabled () && ui->pbTxNext->isChecked ())
{
// Tx Next button overrides all scheduling
m_WSPR_tx_next = true;
return;
}
QString t=ui->RoundRobin->currentText();
if(m_mode=="FST4W" and t!="Random") {
if(m_mode=="FST4W" and t != tr ("Random")) {
bool ok;
int i=t.left (1).toInt (&ok) - 1;
if (!ok) return;
@ -8205,10 +8225,14 @@ void MainWindow::WSPR_scheduling ()
int nsec=ms/1000;
int ntr=m_TRperiod;
int j=((nsec+ntr-1) % (n*ntr))/ntr;
m_WSPR_tx_next=(i==j);
m_WSPR_tx_next = i == j;
return;
}
m_WSPR_tx_next = false;
if (!ui->sbTxPercent->isEnabled () || !ui->sbTxPercent->value ())
{
return; // don't schedule if %age disabled or zero
}
if (m_config.is_transceiver_online () // need working rig control for hopping
&& !m_config.is_dummy_rig ()
&& ui->band_hopping_group_box->isChecked ()) {
@ -8495,7 +8519,7 @@ void MainWindow::tx_watchdog (bool triggered)
m_bTxTime=false;
if (m_tune) stop_tuning ();
if (m_auto) auto_tx_mode (false);
tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}");
tx_status_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff0000}");
tx_status_label.setText (tr ("Runaway Tx watchdog"));
QApplication::alert (this);
}

View File

@ -348,6 +348,7 @@ private:
int TRperiod=60) const;
Q_SIGNAL void outAttenuationChanged (qreal) const;
Q_SIGNAL void toggleShorthand () const;
Q_SIGNAL void reset_audio_input_stream (bool report_dropped_frames) const;
private:
void set_mode (QString const& mode);
@ -447,7 +448,6 @@ private:
qint32 m_nclearave;
qint32 m_minSync;
qint32 m_dBm;
qint32 m_pctx;
qint32 m_nseq;
qint32 m_nWSPRdecodes;
qint32 m_k0;
@ -506,7 +506,6 @@ private:
bool m_bSWL;
bool m_uploadWSPRSpots;
bool m_uploading;
bool m_txNext;
bool m_grid6;
bool m_tuneup;
bool m_bTxTime;
@ -668,8 +667,6 @@ private:
QSharedMemory *mem_jt9;
QString m_QSOText;
unsigned m_msAudioOutputBuffered;
unsigned m_framesAudioInputBuffered;
unsigned m_downSampleFactor;
QThread::Priority m_audioThreadPriority;
bool m_bandEdited;

View File

@ -376,6 +376,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: #000000;
background-color: #00ff00;
border-style: outset;
border-width: 1px;
@ -447,6 +448,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: cyan;
border-style: outset;
border-width: 1px;
@ -480,6 +482,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -523,6 +526,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -553,43 +557,68 @@
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_5" columnstretch="0,0,0,0,0">
<item row="3" column="2">
<widget class="QLabel" name="labUTC">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<layout class="QGridLayout" name="gridLayout_5" rowstretch="1,0,1,1">
<item row="0" column="1" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QPushButton" name="readFreq">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="accessibleDescription">
<string>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
font-size: 16pt;
background-color : black;
color : yellow;
<string notr="true">QPushButton {
font-family: helvetica;
font-size: 9pt;
font-weight: bold;
background-color: white;
color: black;
border-style: solid;
border-width:1px;
border-radius:10px;
border-color: gray;
max-width:20px;
max-height:20px;
min-width:20px;
min-height:20px;
}
QPushButton[state=&quot;error&quot;] {
background-color: red;
}
QPushButton[state=&quot;warning&quot;] {
background-color: orange;
}
QPushButton[state=&quot;ok&quot;] {
background-color: #00ff00;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt; 2015 Jun 17 &lt;/p&gt;&lt;p align=&quot;center&quot;&gt; 01:23:45 &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>?</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="bandComboBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select operating band or enter frequency in MHz or enter kHz increment followed by k.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="margin">
<number>5</number>
<property name="accessibleName">
<string>Frequency entry</string>
</property>
<property name="accessibleDescription">
<string>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</string>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::NoInsert</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
@ -972,6 +1001,7 @@ Not available to nonstandard callsign holders.</string>
<widget class="QPushButton" name="pbBestSP">
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -1103,19 +1133,6 @@ When not checked you can view the calibration results.</string>
</property>
</widget>
</item>
<item row="9" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="sbSerialNumber">
<property name="alignment">
@ -1148,6 +1165,19 @@ When not checked you can view the calibration results.</string>
</property>
</widget>
</item>
<item row="9" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
@ -2131,7 +2161,13 @@ list. The list can be maintained in Settings (F2).</string>
<item>
<widget class="QSpinBox" name="sbTxPercent">
<property name="toolTip">
<string>Percentage of 2-minute sequences devoted to transmitting.</string>
<string>Percentage of minute sequences devoted to transmitting.</string>
</property>
<property name="styleSheet">
<string notr="true">QSpinBox:enabled[notx=&quot;true&quot;] {
color: rgb(0, 0, 0);
background-color: rgb(255, 255, 0);
}</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@ -2316,6 +2352,12 @@ list. The list can be maintained in Settings (F2).</string>
<property name="toolTip">
<string>Upload decoded messages to WSPRnet.org.</string>
</property>
<property name="styleSheet">
<string notr="true">QCheckBox:unchecked {
color: rgb(0, 0, 0);
background-color: rgb(255, 255, 0);
}</string>
</property>
<property name="text">
<string>Upload spots</string>
</property>
@ -2351,10 +2393,11 @@ list. The list can be maintained in Settings (F2).</string>
<item>
<widget class="QPushButton" name="pbTxNext">
<property name="toolTip">
<string>Transmit during the next 2-minute sequence.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Transmit during the next sequence.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -2440,6 +2483,39 @@ list. The list can be maintained in Settings (F2).</string>
</widget>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labDialFreq">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>USB dial frequency</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
font-size: 16pt;
color : yellow;
background-color : black;
}
QLabel[oob=&quot;true&quot;] {
background-color: red;
}</string>
</property>
<property name="text">
<string>14.078 000</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>5</number>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label">
<property name="text">
@ -2447,6 +2523,123 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="labUTC">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
font-size: 16pt;
background-color : black;
color : yellow;
}</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>2</number>
</property>
<property name="midLineWidth">
<number>0</number>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt; 2015 Jun 17 &lt;/p&gt;&lt;p align=&quot;center&quot;&gt; 01:23:45 &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>5</number>
</property>
</widget>
</item>
<item row="2" column="0" rowspan="2">
<widget class="SignalMeter" name="signal_meter_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;30dB recommended when only noise present&lt;br/&gt;Green when good&lt;br/&gt;Red when clipping may occur&lt;br/&gt;Yellow when too low&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="accessibleName">
<string>Rx Signal</string>
</property>
<property name="accessibleDescription">
<string>30dB recommended when only noise present
Green when good
Red when clipping may occur
Yellow when too low</string>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item row="1" column="4" rowspan="3">
<widget class="QSlider" name="outAttenuation">
<property name="toolTip">
<string>Adjust Tx audio level</string>
</property>
<property name="maximum">
<number>450</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>true</bool>
</property>
<property name="invertedControls">
<bool>true</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>50</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="sbNB">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="suffix">
<string> %</string>
</property>
<property name="prefix">
<string>NB </string>
</property>
<property name="maximum">
<number>25</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QWidget" name="DX_controls_widget" native="true">
<property name="sizePolicy">
@ -2723,182 +2916,6 @@ list. The list can be maintained in Settings (F2).</string>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QComboBox" name="bandComboBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Select operating band or enter frequency in MHz or enter kHz increment followed by k.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="accessibleName">
<string>Frequency entry</string>
</property>
<property name="accessibleDescription">
<string>Select operating band or enter frequency in MHz or enter kHz increment followed by k.</string>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="insertPolicy">
<enum>QComboBox::NoInsert</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLength</enum>
</property>
</widget>
</item>
<item row="0" column="1" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QPushButton" name="readFreq">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="accessibleDescription">
<string>If orange or red there has been a rig control failure, click to reset and read the dial frequency. S implies split mode.</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {
font-family: helvetica;
font-size: 9pt;
font-weight: bold;
background-color: white;
color: black;
border-style: solid;
border-width:1px;
border-radius:10px;
border-color: gray;
max-width:20px;
max-height:20px;
min-width:20px;
min-height:20px;
}
QPushButton[state=&quot;error&quot;] {
background-color: red;
}
QPushButton[state=&quot;warning&quot;] {
background-color: orange;
}
QPushButton[state=&quot;ok&quot;] {
background-color: #00ff00;
}</string>
</property>
<property name="text">
<string>?</string>
</property>
</widget>
</item>
<item row="2" column="4" rowspan="2">
<widget class="QSlider" name="outAttenuation">
<property name="toolTip">
<string>Adjust Tx audio level</string>
</property>
<property name="maximum">
<number>450</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>true</bool>
</property>
<property name="invertedControls">
<bool>true</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>50</number>
</property>
</widget>
</item>
<item row="2" column="0" rowspan="2">
<widget class="SignalMeter" name="signal_meter_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;30dB recommended when only noise present&lt;br/&gt;Green when good&lt;br/&gt;Red when clipping may occur&lt;br/&gt;Yellow when too low&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="accessibleName">
<string>Rx Signal</string>
</property>
<property name="accessibleDescription">
<string>30dB recommended when only noise present
Green when good
Red when clipping may occur
Yellow when too low</string>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labDialFreq">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>USB dial frequency</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel {
font-family: MS Shell Dlg 2;
font-size: 16pt;
color : yellow;
background-color : black;
}
QLabel[oob=&quot;true&quot;] {
background-color: red;
}
</string>
</property>
<property name="text">
<string>14.078 000</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>5</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="sbNB">
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="suffix">
<string> %</string>
</property>
<property name="prefix">
<string>NB </string>
</property>
<property name="maximum">
<number>25</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>