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
tags tags
GPATH
GRTAGS
GTAGS
*~ *~
junk* junk*
jnq* jnq*
@ -9,6 +12,9 @@ jnq*
*.mod *.mod
*.pro.user *.pro.user
*.txt *.txt
*.bak
!**/CMakeLists.txt
__pycache__
cmake-build-debug cmake-build-debug
cmake-build-release cmake-build-release
CMakeFiles CMakeFiles

View File

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

View File

@ -41,7 +41,8 @@ bool SoundInput::audioError () const
return result; return result;
} }
void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink, unsigned downSampleFactor, AudioDevice::Channel channel) void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, AudioDevice * sink
, unsigned downSampleFactor, AudioDevice::Channel channel)
{ {
Q_ASSERT (sink); Q_ASSERT (sink);
@ -62,14 +63,13 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
Q_EMIT error (tr ("Requested input audio format is not valid.")); Q_EMIT error (tr ("Requested input audio format is not valid."));
return; return;
} }
else if (!device.isFormatSupported (format))
if (!device.isFormatSupported (format))
{ {
// qDebug () << "Nearest supported audio format:" << device.nearestFormat (format); // qDebug () << "Nearest supported audio format:" << device.nearestFormat (format);
Q_EMIT error (tr ("Requested input audio format is not supported on device.")); Q_EMIT error (tr ("Requested input audio format is not supported on device."));
return; return;
} }
// qDebug () << "Selected audio input format:" << format; // qDebug () << "Selected audio input format:" << format;
m_stream.reset (new QAudioInput {device, format}); m_stream.reset (new QAudioInput {device, format});
if (audioError ()) if (audioError ())
@ -79,11 +79,20 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged); 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)) if (sink->initialize (QIODevice::WriteOnly, channel))
{ {
m_stream->start (sink); m_stream->start (sink);
audioError (); audioError ();
cummulative_lost_usec_ = -1;
//qDebug () << "SoundIn selected buffer size (bytes):" << m_stream->bufferSize () << "peirod size:" << m_stream->periodSize ();
} }
else 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) switch (newState)
{ {
@ -126,6 +135,7 @@ void SoundInput::handleStateChanged (QAudio::State newState) const
break; break;
case QAudio::ActiveState: case QAudio::ActiveState:
reset (false);
Q_EMIT status (tr ("Receiving")); Q_EMIT status (tr ("Receiving"));
break; break;
@ -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() void SoundInput::stop()
{ {
if (m_stream) if (m_stream)

View File

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

View File

@ -9,15 +9,6 @@
#include "moc_soundout.cpp" #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 SoundOutput::audioError () const
{ {
bool result (true); bool result (true);
@ -50,62 +41,76 @@ bool SoundOutput::audioError () const
return result; return result;
} }
void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, unsigned msBuffered) void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels, int frames_buffered)
{ {
Q_ASSERT (0 < channels && channels < 3); if (!device.isNull ())
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 ())
{ {
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) 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 // we have to set this before every start on the stream because the
// Windows implementation seems to forget the buffer size after a // Windows implementation seems to forget the buffer size after a
// stop. // stop.
m_stream->setBufferSize (m_stream->format().bytesForDuration((m_msBuffered ? m_msBuffered : MS_BUFFERED) * 1000)); //qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
// qDebug() << "B" << m_stream->bufferSize() << if (m_framesBuffered)
// m_stream->periodSize() << m_stream->notifyInterval(); {
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered));
#endif
}
m_stream->setCategory ("production"); m_stream->setCategory ("production");
m_stream->start (source); m_stream->start (source);
// qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
} }
void SoundOutput::suspend () void SoundOutput::suspend ()

View File

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

View File

@ -222,9 +222,12 @@ set (WSJT_QT_CONF_DESTINATION ${QT_CONF_DESTINATION} CACHE PATH "Path for the qt
# #
# Project sources # Project sources
# #
set (fort_qt_CXXSRCS
lib/shmem.cpp
)
set (wsjt_qt_CXXSRCS set (wsjt_qt_CXXSRCS
qt_helpers.cpp qt_helpers.cpp
lib/shmem.cpp
widgets/MessageBox.cpp widgets/MessageBox.cpp
MetaDataRegistry.cpp MetaDataRegistry.cpp
Network/NetworkServerLookup.cpp Network/NetworkServerLookup.cpp
@ -733,6 +736,7 @@ set (qcp_CXXSRCS
set (all_CXXSRCS set (all_CXXSRCS
${wsjt_CXXSRCS} ${wsjt_CXXSRCS}
${fort_qt_CXXSRCS}
${wsjt_qt_CXXSRCS} ${wsjt_qt_CXXSRCS}
${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_CXXSRCS}
${wsjtx_CXXSRCS} ${wsjtx_CXXSRCS}
@ -747,7 +751,6 @@ set (all_C_and_CXXSRCS
) )
set (TOP_LEVEL_RESOURCES set (TOP_LEVEL_RESOURCES
cty.dat
icons/Darwin/wsjtx.iconset/icon_128x128.png icons/Darwin/wsjtx.iconset/icon_128x128.png
contrib/gpl-v3-logo.svg contrib/gpl-v3-logo.svg
artwork/splash.png artwork/splash.png
@ -888,7 +891,7 @@ find_package (OpenMP)
# #
# fftw3 single precision library # fftw3 single precision library
# #
find_package (FFTW3 COMPONENTS double single threads REQUIRED) find_package (FFTW3 COMPONENTS single threads REQUIRED)
# #
# libhamlib setup # libhamlib setup
@ -929,7 +932,9 @@ endif ()
if (WSJT_GENERATE_DOCS) if (WSJT_GENERATE_DOCS)
add_subdirectory (doc) add_subdirectory (doc)
endif (WSJT_GENERATE_DOCS) endif (WSJT_GENERATE_DOCS)
if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
add_subdirectory (tests)
endif ()
# #
# Library building setup # Library building setup
@ -1259,6 +1264,11 @@ if (WIN32)
target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase) target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase)
endif (WIN32) endif (WIN32)
# build a library of package Qt functionality used in Fortran utilities
add_library (fort_qt STATIC ${fort_qt_CXXSRCS})
target_link_libraries (fort_qt Qt5::Core)
# build a library of WSJT Qt multimedia components
add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS}) add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS})
target_link_libraries (wsjt_qtmm Qt5::Multimedia) target_link_libraries (wsjt_qtmm Qt5::Multimedia)
@ -1306,9 +1316,9 @@ if (${OPENMP_FOUND} OR APPLE)
LINK_FLAGS -Wl,--stack,16777216 LINK_FLAGS -Wl,--stack,16777216
) )
endif () endif ()
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx wsjt_qt) target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt)
else (${OPENMP_FOUND} OR APPLE) else (${OPENMP_FOUND} OR APPLE)
target_link_libraries (jt9 wsjt_fort wsjt_cxx Qt5::Core) target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
endif (${OPENMP_FOUND} OR APPLE) endif (${OPENMP_FOUND} OR APPLE)
if(WSJT_BUILD_UTILS) if(WSJT_BUILD_UTILS)
@ -1572,6 +1582,7 @@ install (FILES
) )
install (FILES install (FILES
cty.dat
contrib/Ephemeris/JPLEPH contrib/Ephemeris/JPLEPH
DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
#COMPONENT runtime #COMPONENT runtime
@ -1852,11 +1863,11 @@ endif ()
set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") set (CPACK_DEBIAN_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}")
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}") set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "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_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-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) set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1)
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in" configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"

View File

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

View File

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

View File

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

14
INSTALL
View File

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

View File

@ -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; // qDebug() << "delay_ms:" << delay_ms << "mstr:" << mstr << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_stream = stream; m_stream = stream;
if (m_stream) m_stream->restart (this); if (m_stream)
{
m_stream->restart (this);
}
else
{
qDebug () << "Modulator::start: no audio output stream assigned";
}
} }
void Modulator::tune (bool newState) void Modulator::tune (bool newState)
@ -172,15 +179,12 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
{ {
samples = load (0, samples); // silence samples = load (0, samples); // silence
} while (--m_silentFrames && samples != end); } while (--m_silentFrames && samples != end);
qDebug () << "played:" << framesGenerated << "silent frames";
if (!m_silentFrames) if (!m_silentFrames)
{ {
Q_EMIT stateChanged ((m_state = Active)); Q_EMIT stateChanged ((m_state = Active));
} }
} }
// qDebug() << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_cwLevel = false; m_cwLevel = false;
m_ramp = 0; // prepare for CW wave shaping 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 // queue a host address lookup
TRACE_UDP ("server host DNS lookup:" << server); 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); 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. // This timer sets the interval to check for spots to send.
connect (&report_timer_, &QTimer::timeout, [this] () {send_report ();}); 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 // This timer repeats the sending of IPFIX templates and receiver
// information if we are using UDP, in case server has been // information if we are using UDP, in case server has been
@ -80,7 +79,6 @@ public:
send_receiver_data_ = 3; // three times send_receiver_data_ = 3; // three times
} }
}); });
descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly
} }
void check_connection () void check_connection ()
@ -156,6 +154,25 @@ public:
// use this for pseudo connection with UDP, allows us to use // use this for pseudo connection with UDP, allows us to use
// QIODevice::write() instead of QUDPSocket::writeDatagram() // QIODevice::write() instead of QUDPSocket::writeDatagram()
socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly); socket_->connectToHost (HOST, SERVICE_PORT, QAbstractSocket::WriteOnly);
if (!report_timer_.isActive ())
{
report_timer_.start (MIN_SEND_INTERVAL * 1000);
}
if (!descriptor_timer_.isActive ())
{
descriptor_timer_.start (1 * 60 * 60 * 1000); // hourly
}
}
void stop ()
{
if (socket_)
{
socket_->disconnectFromHost ();
}
descriptor_timer_.stop ();
report_timer_.stop ();
} }
void send_report (bool send_residue = false); void send_report (bool send_residue = false);
@ -402,7 +419,13 @@ void PSKReporter::impl::send_report (bool send_residue)
writeUtfString (tx_out, spot.grid_); writeUtfString (tx_out, spot.grid_);
tx_out tx_out
<< quint8 (1u) // REPORTER_SOURCE_AUTOMATIC << 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 (); auto len = payload_.size () + tx_data_.size ();
@ -429,7 +452,13 @@ void PSKReporter::impl::send_report (bool send_residue)
// insert Length and Export Time // insert Length and Export Time
set_length (message, payload_); set_length (message, payload_);
message.device ()->seek (2 * sizeof (quint16)); 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 // Send data to PSK Reporter site
socket_->write (payload_); // TODO: handle errors 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) 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_) if (call != m_->rx_call_ || gridSquare != m_->rx_grid_ || antenna != m_->rx_ant_)
{ {
m_->send_receiver_data_ = m_->socket_ 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 bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Radio::Frequency freq
, QString const& mode, int snr) , QString const& mode, int snr)
{ {
m_->check_connection ();
if (m_->socket_ && m_->socket_->isValid ()) if (m_->socket_ && m_->socket_->isValid ())
{ {
if (QAbstractSocket::UnconnectedState == m_->socket_->state ()) if (QAbstractSocket::UnconnectedState == m_->socket_->state ())
@ -490,7 +521,14 @@ bool PSKReporter::addRemoteStation (QString const& call, QString const& grid, Ra
return false; 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 // Flush any pending spots to PSK Reporter
// //
void sendReport (); void sendReport (bool last = false);
Q_SIGNAL void errorOccurred (QString const& reason); Q_SIGNAL void errorOccurred (QString const& reason);

View File

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

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: You may also need to execute the following command in a terminal:
[example] [example]
sudo apt install libqt5multimedia5-plugins libqt5serialport5 libqt5sql5-sqlite libfftw3-single3 ....
sudo apt install libgfortran5 libqt5widgets5 libqt5network5 \
libqt5printsupport5 libqt5multimedia5-plugins libqt5serialport5 \
libqt5sql5-sqlite libfftw3-single3 libgomp1 libusb-1.0-0
....
Fedora, CentOS, Red Hat, and other rpm-based systems: Fedora, CentOS, Red Hat, and other rpm-based systems:
@ -70,4 +74,8 @@ sudo rpm -e wsjtx
You may also need to execute the following command in a terminal: You may also need to execute the following command in a terminal:
[example] [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*38 c
character*36 a2 character*36 a2
integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22 integer hashmy10,hashmy12,hashmy22,hashdx10,hashdx12,hashdx22
logical unpk28_success,unpk77_success logical unpk28_success,unpk77_success,unpkg4_success
logical dxcall13_set,mycall13_set logical dxcall13_set,mycall13_set
data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/,nzzz/46656/ data a2/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'/,nzzz/46656/
@ -364,10 +364,11 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
idbm=nint(idbm*10.0/3.0) idbm=nint(idbm*10.0/3.0)
call unpack28(n28,call_1,unpk28_success) call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false. if(.not.unpk28_success) unpk77_success=.false.
call to_grid4(igrid4,grid4) call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
write(crpt,'(i3)') idbm write(crpt,'(i3)') idbm
msg=trim(call_1)//' '//grid4//' '//trim(adjustl(crpt)) msg=trim(call_1)//' '//grid4//' '//trim(adjustl(crpt))
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 else if(itype.eq.2) then
! WSPR Type 2 ! WSPR Type 2
@ -417,10 +418,9 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
n28=n22+2063592 n28=n22+2063592
call unpack28(n28,call_1,unpk28_success) call unpack28(n28,call_1,unpk28_success)
if(.not.unpk28_success) unpk77_success=.false. if(.not.unpk28_success) unpk77_success=.false.
call to_grid(igrid6,grid6) call to_grid(igrid6,grid6,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
msg=trim(call_1)//' '//grid6 msg=trim(call_1)//' '//grid6
endif endif
else if(i3.eq.0 .and. n3.gt.6) then 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) if(i.ge.4) call add_call_to_recent_calls(call_2)
endif endif
if(igrid4.le.MAXGRID4) then if(igrid4.le.MAXGRID4) then
call to_grid4(igrid4,grid4) call to_grid4(igrid4,grid4,unpkg4_success)
if(.not.unpkg4_success) unpk77_success=.false.
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//grid4 if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//grid4
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//grid4 if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//grid4
if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false. if(msg(1:3).eq.'CQ ' .and. ir.eq.1) unpk77_success=.false.
@ -569,7 +570,7 @@ subroutine unpack77(c77,nrx,msg,unpk77_success)
nrs=52+irpt nrs=52+irpt
write(cexch,1022) nrs,iserial write(cexch,1022) nrs,iserial
1022 format(i2,i4.4) 1022 format(i2,i4.4)
call to_grid6(igrid6,grid6) call to_grid6(igrid6,grid6,unpk77_success)
if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//cexch//' '//grid6 if(ir.eq.0) msg=trim(call_1)//' '//trim(call_2)//' '//cexch//' '//grid6
if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//cexch//' '//grid6 if(ir.eq.1) msg=trim(call_1)//' '//trim(call_2)//' R '//cexch//' '//grid6
@ -1499,60 +1500,84 @@ subroutine add_call_to_recent_calls(callsign)
return return
end subroutine add_call_to_recent_calls end subroutine add_call_to_recent_calls
subroutine to_grid4(n,grid4) subroutine to_grid4(n,grid4,ok)
character*4 grid4 character*4 grid4
logical ok
ok=.false.
j1=n/(18*10*10) j1=n/(18*10*10)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10 n=n-j1*18*10*10
j2=n/(10*10) j2=n/(10*10)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10 n=n-j2*10*10
j3=n/10 j3=n/10
if (j3.lt.0.or.j3.gt.9) goto 900
j4=n-j3*10 j4=n-j3*10
if (j4.lt.0.or.j4.gt.9) goto 900
grid4(1:1)=char(j1+ichar('A')) grid4(1:1)=char(j1+ichar('A'))
grid4(2:2)=char(j2+ichar('A')) grid4(2:2)=char(j2+ichar('A'))
grid4(3:3)=char(j3+ichar('0')) grid4(3:3)=char(j3+ichar('0'))
grid4(4:4)=char(j4+ichar('0')) grid4(4:4)=char(j4+ichar('0'))
ok=.true.
return
900 return
end subroutine to_grid4 end subroutine to_grid4
subroutine to_grid6(n,grid6) subroutine to_grid6(n,grid6,ok)
character*6 grid6 character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*24*24) j1=n/(18*10*10*24*24)
if (j1.lt.0.or.j1.gt.17) goto 900
n=n-j1*18*10*10*24*24 n=n-j1*18*10*10*24*24
j2=n/(10*10*24*24) j2=n/(10*10*24*24)
if (j2.lt.0.or.j2.gt.17) goto 900
n=n-j2*10*10*24*24 n=n-j2*10*10*24*24
j3=n/(10*24*24) j3=n/(10*24*24)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*24*24 n=n-j3*10*24*24
j4=n/(24*24) j4=n/(24*24)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*24*24 n=n-j4*24*24
j5=n/24 j5=n/24
if (j5.lt.0.or.j5.gt.23) goto 900
j6=n-j5*24 j6=n-j5*24
if (j6.lt.0.or.j6.gt.23) goto 900
grid6(1:1)=char(j1+ichar('A')) grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A')) grid6(2:2)=char(j2+ichar('A'))
grid6(3:3)=char(j3+ichar('0')) grid6(3:3)=char(j3+ichar('0'))
grid6(4:4)=char(j4+ichar('0')) grid6(4:4)=char(j4+ichar('0'))
grid6(5:5)=char(j5+ichar('A')) grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A')) grid6(6:6)=char(j6+ichar('A'))
ok=.true.
return 900 return
end subroutine to_grid6 end subroutine to_grid6
subroutine to_grid(n,grid6) subroutine to_grid(n,grid6,ok)
! 4-, or 6-character grid ! 4-, or 6-character grid
character*6 grid6 character*6 grid6
logical ok
ok=.false.
j1=n/(18*10*10*25*25) 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 n=n-j1*18*10*10*25*25
j2=n/(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 n=n-j2*10*10*25*25
j3=n/(10*25*25) j3=n/(10*25*25)
if (j3.lt.0.or.j3.gt.9) goto 900
n=n-j3*10*25*25 n=n-j3*10*25*25
j4=n/(25*25) j4=n/(25*25)
if (j4.lt.0.or.j4.gt.9) goto 900
n=n-j4*25*25 n=n-j4*25*25
j5=n/25 j5=n/25
if (j5.lt.0.or.j5.gt.24) goto 900
j6=n-j5*25 j6=n-j5*25
if (j6.lt.0.or.j6.gt.24) goto 900
grid6='' grid6=''
grid6(1:1)=char(j1+ichar('A')) grid6(1:1)=char(j1+ichar('A'))
grid6(2:2)=char(j2+ichar('A')) grid6(2:2)=char(j2+ichar('A'))
@ -1562,8 +1587,9 @@ subroutine to_grid(n,grid6)
grid6(5:5)=char(j5+ichar('A')) grid6(5:5)=char(j5+ichar('A'))
grid6(6:6)=char(j6+ichar('A')) grid6(6:6)=char(j6+ichar('A'))
endif endif
ok=.true.
return 900 return
end subroutine to_grid end subroutine to_grid
end module packjt77 end module packjt77

View File

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

View File

@ -319,12 +319,12 @@ AD1CCty::AD1CCty (Configuration const * configuration)
{ {
Q_ASSERT (configuration); Q_ASSERT (configuration);
// TODO: G4WJS - consider doing the following asynchronously to // 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. // i7 reading BIG CTY.DAT.
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}; QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
m_->path_ = dataPath.exists (file_name) m_->path_ = dataPath.exists (file_name)
? dataPath.absoluteFilePath (file_name) // user override ? dataPath.absoluteFilePath (file_name) // user override
: QString {":/"} + file_name; // or original in the resources FS : configuration->data_dir ().absoluteFilePath (file_name); // or original
QFile file {m_->path_}; QFile file {m_->path_};
if (file.open (QFile::ReadOnly)) if (file.open (QFile::ReadOnly))
{ {

View File

@ -97,7 +97,7 @@ namespace
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// ### Add timestamps to all debug messages // ### 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 (); init_random_seed ();

View File

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

View File

@ -37,14 +37,14 @@ void update_dynamic_property (QWidget * widget, char const * property, QVariant
widget->update (); 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; 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; return dt;
} }

View File

@ -69,11 +69,11 @@ QString font_as_stylesheet (QFont const&);
// conditional style sheet updates // conditional style sheet updates
void update_dynamic_property (QWidget *, char const * property, QVariant const& value); void update_dynamic_property (QWidget *, char const * property, QVariant const& value);
// round a QDateTime instance to an interval // round a QDateTime instance to an integral interval of milliseconds
QDateTime qt_round_date_time_to (QDateTime dt, int seconds); QDateTime qt_round_date_time_to (QDateTime dt, int milliseconds);
// truncate a QDateTime to an interval // truncate a QDateTime to an integral interval of milliseconds
QDateTime qt_truncate_date_time_to (QDateTime dt, int seconds); QDateTime qt_truncate_date_time_to (QDateTime dt, int milliseconds);
template <class T> template <class T>
class VPtr 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"}; 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 (); auto quint32_max = std::numeric_limits<quint32>::max ();
constexpr int N_WIDGETS {34}; 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) 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_idleMinutes {0},
m_nSubMode {0}, m_nSubMode {0},
m_nclearave {1}, m_nclearave {1},
m_pctx {0},
m_nseq {0}, m_nseq {0},
m_nWSPRdecodes {0}, m_nWSPRdecodes {0},
m_k0 {9999999}, m_k0 {9999999},
@ -310,7 +311,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_bShMsgs {false}, m_bShMsgs {false},
m_bSWL {false}, m_bSWL {false},
m_uploading {false}, m_uploading {false},
m_txNext {false},
m_grid6 {false}, m_grid6 {false},
m_tuneup {false}, m_tuneup {false},
m_bTxTime {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"}, m_sfx {"P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A"},
mem_jt9 {shdmem}, mem_jt9 {shdmem},
m_msAudioOutputBuffered (0u),
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
m_downSampleFactor (downSampleFactor), m_downSampleFactor (downSampleFactor),
m_audioThreadPriority (QThread::HighPriority), m_audioThreadPriority (QThread::HighPriority),
m_bandEdited {false}, 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::startAudioInputStream, m_soundInput, &SoundInput::start);
connect (this, &MainWindow::suspendAudioInputStream, m_soundInput, &SoundInput::suspend); connect (this, &MainWindow::suspendAudioInputStream, m_soundInput, &SoundInput::suspend);
connect (this, &MainWindow::resumeAudioInputStream, m_soundInput, &SoundInput::resume); 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 (this, &MainWindow::finished, m_soundInput, &SoundInput::stop);
connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError); connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError);
// connect(m_soundInput, &SoundInput::status, this, &MainWindow::showStatusMessage); // 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 (&m_audioThread, &QThread::finished, m_soundInput, &QObject::deleteLater);
connect (this, &MainWindow::finished, this, &MainWindow::close); 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 (&m_wav_future_watcher, &QFutureWatcher<void>::finished, this, &MainWindow::diskDat);
connect(&watcher3, SIGNAL(finished()),this,SLOT(fast_decode_done())); 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 ()); if (!m_config.audio_input_device ().isNull ())
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered); {
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); Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook 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_9->setStyleSheet("QLabel{color: #000000; background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{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 // this must be done before initializing the mode as some modes need
// to turn off split on the rig e.g. WSPR // 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_saveDecoded=ui->actionSave_decoded->isChecked();
m_saveAll=ui->actionSave_all->isChecked(); m_saveAll=ui->actionSave_all->isChecked();
ui->sbTxPercent->setValue(m_pctx);
ui->TxPowerComboBox->setCurrentIndex(int(.3 * m_dBm + .2)); ui->TxPowerComboBox->setCurrentIndex(int(.3 * m_dBm + .2));
ui->cbUploadWSPR_Spots->setChecked(m_uploadWSPRSpots); ui->cbUploadWSPR_Spots->setChecked(m_uploadWSPRSpots);
if((m_ndepth&7)==1) ui->actionQuickDecode->setChecked(true); 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_isort=-3;
m_max_dB=70; m_max_dB=70;
m_CQtype="CQ"; 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(); fixStop();
VHF_features_enabled(m_config.enable_VHF_features()); VHF_features_enabled(m_config.enable_VHF_features());
m_wideGraph->setVHF(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("GUItab",ui->tabWidget->currentIndex());
m_settings->setValue("OutBufSize",outBufSize); m_settings->setValue("OutBufSize",outBufSize);
m_settings->setValue ("HoldTxFreq", ui->cbHoldTxFreq->isChecked ()); 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("dBm",m_dBm);
m_settings->setValue("RR73",m_send_RR73); m_settings->setValue("RR73",m_send_RR73);
m_settings->setValue ("WSPRPreferType1", ui->WSPR_prefer_type_1_check_box->isChecked ()); 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(0); // ensure a change is signaled
ui->TxFreqSpinBox->setValue(m_settings->value("TxFreq",1500).toInt()); ui->TxFreqSpinBox->setValue(m_settings->value("TxFreq",1500).toInt());
m_ndepth=m_settings->value("NDepth",3).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_dBm=m_settings->value("dBm",37).toInt();
m_send_RR73=m_settings->value("RR73",false).toBool(); m_send_RR73=m_settings->value("RR73",false).toBool();
if(m_send_RR73) { 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 ()); ui->WSPR_prefer_type_1_check_box->setChecked (m_settings->value ("WSPRPreferType1", true).toBool ());
m_uploadWSPRSpots=m_settings->value("UploadSpots",false).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->cbNoOwnCall->setChecked(m_settings->value("NoOwnCall",false).toBool());
ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool()); ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool());
// setup initial value of tx attenuator // setup initial value of tx attenuator
@ -1279,8 +1294,6 @@ void MainWindow::readSettings()
// use these initialisation settings to tune the audio o/p buffer // use these initialisation settings to tune the audio o/p buffer
// size and audio thread priority // size and audio thread priority
m_settings->beginGroup ("Tune"); 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_audioThreadPriority = static_cast<QThread::Priority> (m_settings->value ("Audio/ThreadPriority", QThread::HighPriority).toInt () % 8);
m_settings->endGroup (); m_settings->endGroup ();
@ -1545,6 +1558,10 @@ void MainWindow::dataSink(qint64 frames)
if(m_mode!="WSPR") decode(); //Start decoder 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_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")) { 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 //Always save unless "Save None"; may delete later
if(m_TRperiod < 60) { if(m_TRperiod < 60) {
@ -1623,14 +1640,15 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int
format.setChannelCount (1); format.setChannelCount (1);
format.setSampleSize (16); format.setSampleSize (16);
format.setSampleType (QAudioFormat::SignedInt); format.setSampleType (QAudioFormat::SignedInt);
auto source = QString {"%1, %2"}.arg (my_callsign).arg (my_grid); auto source = QString {"%1; %2"}.arg (my_callsign).arg (my_grid);
auto comment = QString {"Mode=%1%2, Freq=%3%4"} auto comment = QString {"Mode=%1%2; Freq=%3%4"}
.arg (mode) .arg (mode)
.arg (QString {(mode.contains ('J') && !mode.contains ('+')) || mode.startsWith ("FST4") .arg (QString {(mode.contains ('J') && !mode.contains ('+'))
? QString {", Sub Mode="} + QChar {'A' + sub_mode} || mode.startsWith ("FST4") || mode.startsWith ("QRA")
: QString {}}) ? QString {"; Sub Mode="} + QString::number (int (samples / 12000)) + QChar {'A' + sub_mode}
.arg (Radio::frequency_MHz_string (frequency)) : QString {}})
.arg (QString {mode!="WSPR" ? QString {", DXCall=%1, DXGrid=%2"} .arg (Radio::frequency_MHz_string (frequency))
.arg (QString {mode!="WSPR" ? QString {"; DXCall=%1; DXGrid=%2"}
.arg (his_call) .arg (his_call)
.arg (his_grid).toLocal8Bit () : ""}); .arg (his_grid).toLocal8Bit () : ""});
BWFFile::InfoDictionary list_info { BWFFile::InfoDictionary list_info {
@ -1792,6 +1810,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
auto callsign = m_config.my_callsign (); auto callsign = m_config.my_callsign ();
auto my_grid = m_config.my_grid (); auto my_grid = m_config.my_grid ();
SpecOp nContest0=m_config.special_op_id(); SpecOp nContest0=m_config.special_op_id();
auto psk_on = m_config.spot_to_psk_reporter ();
if (QDialog::Accepted == m_config.exec ()) { if (QDialog::Accepted == m_config.exec ()) {
checkMSK144ContestType(); checkMSK144ContestType();
if (m_config.my_callsign () != callsign) { 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 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 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 ()) { if(m_config.restart_audio_input ()) {
Q_EMIT startAudioInputStream (m_config.audio_input_device (), Q_EMIT startAudioInputStream (m_config.audio_input_device ()
m_framesAudioInputBuffered, m_detector, m_downSampleFactor, , rx_chunk_size * m_downSampleFactor
m_config.audio_input_channel ()); , m_detector, m_downSampleFactor
, m_config.audio_input_channel ());
} }
if(m_config.restart_audio_output ()) { if(m_config.restart_audio_output ()) {
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), Q_EMIT initializeAudioOutputStream (m_config.audio_output_device ()
AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, , AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2
m_msAudioOutputBuffered); , tx_audio_buffer_size);
} }
displayDialFrequency (); displayDialFrequency ();
@ -1918,18 +1944,14 @@ void MainWindow::on_autoButton_clicked (bool checked)
m_nclearave=1; m_nclearave=1;
echocom_.nsum=0; 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; 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) void MainWindow::auto_tx_mode (bool state)
{ {
ui->autoButton->setChecked (state); ui->autoButton->setChecked (state);
@ -2288,8 +2310,8 @@ bool MainWindow::eventFilter (QObject * object, QEvent * event)
void MainWindow::createStatusBar() //createStatusBar void MainWindow::createStatusBar() //createStatusBar
{ {
tx_status_label.setAlignment (Qt::AlignHCenter); tx_status_label.setAlignment (Qt::AlignHCenter);
tx_status_label.setMinimumSize (QSize {150, 18}); tx_status_label.setMinimumSize (QSize {100, 18});
tx_status_label.setStyleSheet ("QLabel{background-color: #00ff00}"); tx_status_label.setStyleSheet ("QLabel{color: #000000; background-color: #00ff00}");
tx_status_label.setFrameStyle (QFrame::Panel | QFrame::Sunken); tx_status_label.setFrameStyle (QFrame::Panel | QFrame::Sunken);
statusBar()->addWidget (&tx_status_label); statusBar()->addWidget (&tx_status_label);
@ -2337,39 +2359,39 @@ void MainWindow::setup_status_bar (bool vhf)
mode_label.setText (m_mode); mode_label.setText (m_mode);
} }
if ("ISCAT" == 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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) { } 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 {}); last_tx_label.setText (QString {});
if (m_mode.contains (QRegularExpression {R"(^(Echo|ISCAT))"})) { if (m_mode.contains (QRegularExpression {R"(^(Echo|ISCAT))"})) {
if (band_hopping_label.isVisible ()) statusBar ()->removeWidget (&band_hopping_label); if (band_hopping_label.isVisible ()) statusBar ()->removeWidget (&band_hopping_label);
} else if (m_mode=="WSPR") { } 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 ()) { if (!band_hopping_label.isVisible ()) {
statusBar ()->addWidget (&band_hopping_label); statusBar ()->addWidget (&band_hopping_label);
band_hopping_label.show (); band_hopping_label.show ();
@ -2700,7 +2722,7 @@ void MainWindow::on_actionOpen_triggered() //Open File
m_path=fname; m_path=fname;
int i1=fname.lastIndexOf("/"); int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1); 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 + " "); tx_status_label.setText(" " + baseName + " ");
on_stopButton_clicked(); on_stopButton_clicked();
m_diskData=true; m_diskData=true;
@ -2780,7 +2802,7 @@ void MainWindow::on_actionOpen_next_in_directory_triggered() //Open Next
m_path=fname; m_path=fname;
int i1=fname.lastIndexOf("/"); int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1); 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 + " "); tx_status_label.setText(" " + baseName + " ");
m_diskData=true; m_diskData=true;
read_wav_file (fname); read_wav_file (fname);
@ -2933,7 +2955,7 @@ void MainWindow::on_actionSpecial_mouse_commands_triggered()
<td><b>Click</b> to set Rx frequency.<br/> <td><b>Click</b> to set Rx frequency.<br/>
<b>Shift-click</b> to set Tx 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>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> </td>
</tr> </tr>
<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/> <td><b>Double-click</b> to copy second callsign to Dx Call,<br/>
locator to Dx Grid, change Rx and Tx frequency to<br/> locator to Dx Grid, change Rx and Tx frequency to<br/>
decoded signal's frequency, and generate standard<br/> decoded signal's frequency, and generate standard<br/>
messages.<br/> messages.<br/>
If <b>Hold Tx Freq</b> is checked or first callsign in message<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/> is your own call, Tx frequency is not changed unless <br/>
<b>Ctrl</b> is held down.<br/> <b>Ctrl</b> is held down.<br/>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -3122,56 +3144,58 @@ void MainWindow::decode() //decode()
//newdat=1 ==> this is new data, must do the big FFT //newdat=1 ==> this is new data, must do the big FFT
//nagain=1 ==> decode only at fQSO +/- Tol //nagain=1 ==> decode only at fQSO +/- Tol
char *to = (char*)mem_jt9->data(); if (auto * to = reinterpret_cast<char *> (mem_jt9->data()))
char *from = (char*) dec_data.ipc; {
int size=sizeof(struct dec_data); char *from = (char*) dec_data.ipc;
if(dec_data.params.newdat==0) { int size=sizeof(struct dec_data);
int noffset {offsetof (struct dec_data, params.nutc)}; if(dec_data.params.newdat==0) {
to += noffset; int noffset {offsetof (struct dec_data, params.nutc)};
from += noffset; to += noffset;
size -= noffset; from += noffset;
} size -= noffset;
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) { }
float t0=m_t0; if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
float t1=m_t1; float t0=m_t0;
qApp->processEvents(); //Update the waterfall float t1=m_t1;
if(m_nPick > 0) { qApp->processEvents(); //Update the waterfall
t0=m_t0Pick; if(m_nPick > 0) {
t1=m_t1Pick; 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() void::MainWindow::fast_decode_done()
@ -3219,12 +3243,14 @@ void::MainWindow::fast_decode_done()
void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone) void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone)
{ {
dec_data_t * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data()); if (auto * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data()))
mem_jt9->lock (); {
dd->ipc[0]=n; mem_jt9->lock ();
if(istart>=0) dd->ipc[1]=istart; dd->ipc[0]=n;
if(idone>=0) dd->ipc[2]=idone; if(istart>=0) dd->ipc[1]=istart;
mem_jt9->unlock (); if(idone>=0) dd->ipc[2]=idone;
mem_jt9->unlock ();
}
} }
void MainWindow::decodeDone () void MainWindow::decodeDone ()
@ -3281,7 +3307,8 @@ void MainWindow::readFromStdout() //readFromStdout
line_read = line_read.left (64); 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 //Pad 22-char msg to at least 37 chars
line_read = line_read.left(44) + " " + line_read.mid(44); 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); bool okToPost=(nsec > int(4*m_TRperiod)/5);
if(m_mode=="FST4W" and okToPost) { if(m_mode=="FST4W" and okToPost) {
line_read=line_read.left(22) + " CQ " + line_read.trimmed().mid(22); line_read=line_read.left(22) + " CQ " + line_read.trimmed().mid(22);
int n=line_read.trimmed().size(); auto p = line_read.lastIndexOf (' ');
line_read=line_read.trimmed().left(n-3); DecodedText FST4W_post {QString::fromUtf8 (line_read.left (p).constData ())};
DecodedText FST4W_post {QString::fromUtf8(line_read.constData())};
pskPost(FST4W_post); pskPost(FST4W_post);
} else { } else {
if (stdMsg && okToPost) pskPost(decodedtext); if (stdMsg && okToPost) pskPost(decodedtext);
@ -3741,26 +3767,22 @@ void MainWindow::guiUpdate()
if(m_mode=="WSPR" or m_mode=="FST4W") { if(m_mode=="WSPR" or m_mode=="FST4W") {
if(m_nseq==0 and m_ntr==0) { //Decide whether to Tx or Rx if(m_nseq==0 and m_ntr==0) { //Decide whether to Tx or Rx
m_tuneup=false; //This is not an ATU tuneup 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 bool btx = m_auto && m_WSPR_tx_next; // To Tx, we need m_auto and
// scheduled transmit // scheduled transmit
if(m_auto and m_txNext) btx=true; //TxNext button overrides m_WSPR_tx_next = false;
if(m_auto && ui->sbTxPercent->isEnabled () && m_pctx==100) btx=true; //Always transmit
if(btx) { if(btx) {
m_ntr=-1; //This says we will have transmitted 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 m_bTxTime=true; //Start a WSPR or FST4W Tx sequence
} else { } 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_ntr=1; //This says we will have received
m_bTxTime=false; //Start a WSPR or FST4W Rx sequence m_bTxTime=false; //Start a WSPR or FST4W Rx sequence
} }
} }
} else { } else {
// For all modes other than WSPR and Fst4W // For all modes other than WSPR and Fst4W
m_bTxTime = (t2p >= tx1) and (t2p < tx2); m_bTxTime = (t2p >= tx1) and (t2p < tx2);
if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK; if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK;
if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) { if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) {
@ -4301,11 +4323,11 @@ void MainWindow::guiUpdate()
m_nsendingsh=0; m_nsendingsh=0;
if(s[4]==64) m_nsendingsh=1; if(s[4]==64) m_nsendingsh=1;
if(m_nsendingsh==1 or m_currentMessageType==7) { 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) { } 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 { } else {
tx_status_label.setStyleSheet("QLabel{background-color: #ffff33}"); tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ffff33}");
} }
if(m_tune) { if(m_tune) {
tx_status_label.setText("Tx: TUNE"); tx_status_label.setText("Tx: TUNE");
@ -4324,11 +4346,11 @@ void MainWindow::guiUpdate()
} }
} else if(m_monitoring) { } else if(m_monitoring) {
if (!m_tx_watchdog) { 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"); auto t = tr ("Receiving");
if(m_mode=="MSK144") { if(m_mode=="MSK144") {
int npct=int(100.0*m_fCPUmskrtd/0.298667); 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); t += QString {" %1%"}.arg (npct, 2);
} }
tx_status_label.setText (t); tx_status_label.setText (t);
@ -4649,9 +4671,9 @@ void MainWindow::doubleClickOnCall2(Qt::KeyboardModifiers modifiers)
void MainWindow::doubleClickOnCall(Qt::KeyboardModifiers modifiers) void MainWindow::doubleClickOnCall(Qt::KeyboardModifiers modifiers)
{ {
QTextCursor cursor; QTextCursor cursor;
if(m_mode=="ISCAT") { if(m_mode=="ISCAT" or m_mode=="FST4W") {
MessageBox::information_message (this, MessageBox::information_message (this,
"Double-click not available for ISCAT mode"); "Double-click not available for ISCAT or FST4W mode");
return; return;
} }
if(m_decodedText2) { if(m_decodedText2) {
@ -5042,7 +5064,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
// m_nextGrid=message_words.at(3); // m_nextGrid=message_words.at(3);
// m_nextRpt=message.report(); // m_nextRpt=message.report();
// ui->labNextCall->setText("Next: " + m_nextCall); // ui->labNextCall->setText("Next: " + m_nextCall);
// ui->labNextCall->setStyleSheet("QLabel {background-color: #66ff66}"); // ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}");
// } // }
return; return;
} }
@ -5263,11 +5285,11 @@ bool MainWindow::stdCall(QString const& w)
{ {
static QRegularExpression standard_call_re { static QRegularExpression standard_call_re {
R"( R"(
^\s* # optional leading spaces ^\s* # optional leading spaces
( [A-Z]{0,2} | [A-Z][0-9] | [0-9][A-Z] ) # part 1 ( [A-Z]{0,2} | [A-Z][0-9] | [0-9][A-Z] ) # part 1
( [0-9][A-Z]{0,3} ) # part 2 ( [0-9][A-Z]{0,3} ) # part 2
(/R | /P)? # optional suffix (/R | /P)? # optional suffix
\s*$ # optional trailing spaces \s*$ # optional trailing spaces
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption}; )", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
return standard_call_re.match (w).hasMatch (); 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; t=t00 + rpt;
msgtype(t, ui->tx2); msgtype(t, ui->tx2);
t=t0 + "R" + rpt; t=t0 + "R" + rpt;
@ -5437,7 +5459,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
case Configuration::type_2_msg_1_full: case Configuration::type_2_msg_1_full:
msgtype(t + my_grid, ui->tx1); msgtype(t + my_grid, ui->tx1);
if (!eme_short_codes) { 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()) { SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code
} else { } else {
@ -5450,7 +5472,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
break; break;
case Configuration::type_2_msg_3_full: 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()) { SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3); msgtype(t + "R " + my_grid, ui->tx3);
msgtype(t + "RRR", ui->tx4); msgtype(t + "RRR", ui->tx4);
@ -5466,7 +5488,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
case Configuration::type_2_msg_5_only: case Configuration::type_2_msg_5_only:
msgtype(t00 + my_grid, ui->tx1); msgtype(t00 + my_grid, ui->tx1);
if (!eme_short_codes) { 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()) { SpecOp::NA_VHF == m_config.special_op_id()) {
msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code msgtype(t + "R " + my_grid, ui->tx3); // #### Unreachable code
msgtype(t + "RRR", ui->tx4); msgtype(t + "RRR", ui->tx4);
@ -5766,9 +5788,7 @@ void MainWindow::on_tx6_editingFinished() //tx6 edited
void MainWindow::on_RoundRobin_currentTextChanged(QString text) void MainWindow::on_RoundRobin_currentTextChanged(QString text)
{ {
ui->sbTxPercent->setEnabled(text=="Random"); ui->sbTxPercent->setEnabled (text == tr ("Random"));
m_WSPR_tx_next = false; // cancel any pending Tx to avoid
// undesirable consecutive Tx periods
} }
@ -6165,7 +6185,7 @@ void MainWindow::on_actionFT8_triggered()
ui->cbAutoSeq->setEnabled(false); ui->cbAutoSeq->setEnabled(false);
ui->tabWidget->setCurrentIndex(0); ui->tabWidget->setCurrentIndex(0);
ui->cbHoldTxFreq->setChecked(true); ui->cbHoldTxFreq->setChecked(true);
displayWidgets(nWidgets("11101000010011000001000000000011000")); displayWidgets(nWidgets("1110100001001100000100000000001100"));
ui->labDXped->setText(tr ("Hound")); ui->labDXped->setText(tr ("Hound"));
ui->txrb1->setChecked(true); ui->txrb1->setChecked(true);
ui->txrb2->setEnabled(false); ui->txrb2->setEnabled(false);
@ -6687,7 +6707,7 @@ void MainWindow::WSPR_config(bool b)
ui->label_7->setVisible(!b and ui->cbMenus->isChecked()); ui->label_7->setVisible(!b and ui->cbMenus->isChecked());
ui->logQSOButton->setVisible(!b); ui->logQSOButton->setVisible(!b);
ui->DecodeButton->setEnabled(!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->band_hopping_group_box->setVisible(true);
ui->RoundRobin->setVisible(m_mode=="FST4W"); ui->RoundRobin->setVisible(m_mode=="FST4W");
ui->RoundRobin->lineEdit()->setAlignment(Qt::AlignCenter); ui->RoundRobin->lineEdit()->setAlignment(Qt::AlignCenter);
@ -6897,7 +6917,11 @@ void MainWindow::band_changed (Frequency f)
} }
m_lastBand.clear (); m_lastBand.clear ();
m_bandEdited = false; 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 (!m_transmitting) monitor (true);
if ("FreqCal" == m_mode) if ("FreqCal" == m_mode)
{ {
@ -7223,14 +7247,15 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
{ {
Transceiver::TransceiverState old_state {m_rigState}; Transceiver::TransceiverState old_state {m_rigState};
//transmitDisplay (s.ptt ()); //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) // (caveat - DX Lab Suite Commander)
if (m_tx_when_ready && g_iptt) { // waiting to Tx and still needed if (m_tx_when_ready && g_iptt) { // waiting to Tx and still needed
int ms_delay=1000*m_config.txDelay(); int ms_delay=1000*m_config.txDelay();
if(m_mode=="FT4") ms_delay=20; if(m_mode=="FT4") ms_delay=20;
ptt1Timer.start(ms_delay); //Start-of-transmission sequencer delay ptt1Timer.start(ms_delay); //Start-of-transmission sequencer delay
m_tx_when_ready = false;
} }
m_tx_when_ready = false;
} }
m_rigState = s; m_rigState = s;
auto old_freqNominal = m_freqNominal; 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()))) { || !(ui->cbCQTx->isEnabled () && ui->cbCQTx->isVisible () && ui->cbCQTx->isChecked()))) {
m_lastDialFreq = m_freqNominal; m_lastDialFreq = m_freqNominal;
m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000; m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000;
if (m_config.spot_to_psk_reporter ()) { pskSetLocal ();
pskSetLocal ();
}
statusChanged(); statusChanged();
m_wideGraph->setDialFreq(m_freqNominal / 1.e6); m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
} }
@ -7585,6 +7608,8 @@ bool MainWindow::shortList(QString callsign)
void MainWindow::pskSetLocal () void MainWindow::pskSetLocal ()
{ {
if (!m_config.spot_to_psk_reporter ()) return;
// find the station row, if any, that matches the band we are on // find the station row, if any, that matches the band we are on
auto stations = m_config.stations (); auto stations = m_config.stations ();
auto matches = stations->match (stations->index (0, StationList::band_column) 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) QString MainWindow::beacon_start_time (int n)
{ {
auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod); auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod * 1.e3);
if (m_TRperiod < 60) if (m_TRperiod < 60.)
{ {
return bt.toString ("HHmmss"); 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 rfreq = QString("%1").arg((m_dialFreqRxWSPR + 1500) / 1e6, 0, 'f', 6);
QString tfreq = QString("%1").arg((m_dialFreqRxWSPR + QString tfreq = QString("%1").arg((m_dialFreqRxWSPR +
ui->TxFreqSpinBox->value()) / 1e6, 0, 'f', 6); ui->TxFreqSpinBox->value()) / 1e6, 0, 'f', 6);
auto pct = QString::number (ui->autoButton->isChecked () ? ui->sbTxPercent->value () : 0);
if (!direct_post) if (!direct_post)
{ {
wsprNet->upload (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq, 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 (), QString::number (m_dBm), version (),
m_config.writeable_data_dir ().absoluteFilePath ("wspr_spots.txt")); m_config.writeable_data_dir ().absoluteFilePath ("wspr_spots.txt"));
} }
else else
{ {
wsprNet->post (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq, 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); QString::number (m_dBm), version (), decode_text);
} }
if (!decode_text.size ()) if (!decode_text.size ())
@ -8161,24 +8187,9 @@ void MainWindow::on_TxPowerComboBox_currentIndexChanged(int index)
m_dBm = ui->TxPowerComboBox->itemData (index).toInt (); 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) void MainWindow::on_cbUploadWSPR_Spots_toggled(bool b)
{ {
m_uploadWSPRSpots=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) 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) 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 () 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(); QString t=ui->RoundRobin->currentText();
if(m_mode=="FST4W" and t!="Random") { if(m_mode=="FST4W" and t != tr ("Random")) {
bool ok; bool ok;
int i=t.left (1).toInt (&ok) - 1; int i=t.left (1).toInt (&ok) - 1;
if (!ok) return; if (!ok) return;
@ -8205,10 +8225,14 @@ void MainWindow::WSPR_scheduling ()
int nsec=ms/1000; int nsec=ms/1000;
int ntr=m_TRperiod; int ntr=m_TRperiod;
int j=((nsec+ntr-1) % (n*ntr))/ntr; int j=((nsec+ntr-1) % (n*ntr))/ntr;
m_WSPR_tx_next=(i==j); m_WSPR_tx_next = i == j;
return; return;
} }
m_WSPR_tx_next = false; 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 if (m_config.is_transceiver_online () // need working rig control for hopping
&& !m_config.is_dummy_rig () && !m_config.is_dummy_rig ()
&& ui->band_hopping_group_box->isChecked ()) { && ui->band_hopping_group_box->isChecked ()) {
@ -8495,7 +8519,7 @@ void MainWindow::tx_watchdog (bool triggered)
m_bTxTime=false; m_bTxTime=false;
if (m_tune) stop_tuning (); if (m_tune) stop_tuning ();
if (m_auto) auto_tx_mode (false); 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")); tx_status_label.setText (tr ("Runaway Tx watchdog"));
QApplication::alert (this); QApplication::alert (this);
} }

View File

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

View File

@ -376,6 +376,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: #000000;
background-color: #00ff00; background-color: #00ff00;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -447,6 +448,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: cyan; background-color: cyan;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -480,6 +482,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red; background-color: red;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -523,6 +526,7 @@
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red; background-color: red;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -553,43 +557,68 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QGridLayout" name="gridLayout_5" columnstretch="0,0,0,0,0"> <layout class="QGridLayout" name="gridLayout_5" rowstretch="1,0,1,1">
<item row="3" column="2"> <item row="0" column="1" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
<widget class="QLabel" name="labUTC"> <widget class="QPushButton" name="readFreq">
<property name="sizePolicy"> <property name="enabled">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <bool>false</bool>
<horstretch>0</horstretch> </property>
<verstretch>0</verstretch> <property name="toolTip">
</sizepolicy> <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>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QLabel { <string notr="true">QPushButton {
font-family: MS Shell Dlg 2; font-family: helvetica;
font-size: 16pt; font-size: 9pt;
background-color : black; font-weight: bold;
color : yellow; 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> }</string>
</property> </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"> <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>
<property name="alignment"> </widget>
<set>Qt::AlignCenter</set> </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>
<property name="margin"> <property name="accessibleName">
<number>5</number> <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> </property>
</widget> </widget>
</item> </item>
@ -972,6 +1001,7 @@ Not available to nonstandard callsign holders.</string>
<widget class="QPushButton" name="pbBestSP"> <widget class="QPushButton" name="pbBestSP">
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red; background-color: red;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -1103,19 +1133,6 @@ When not checked you can view the calibration results.</string>
</property> </property>
</widget> </widget>
</item> </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"> <item row="3" column="1">
<widget class="QSpinBox" name="sbSerialNumber"> <widget class="QSpinBox" name="sbSerialNumber">
<property name="alignment"> <property name="alignment">
@ -1148,6 +1165,19 @@ When not checked you can view the calibration results.</string>
</property> </property>
</widget> </widget>
</item> </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> </layout>
</item> </item>
<item> <item>
@ -2131,7 +2161,13 @@ list. The list can be maintained in Settings (F2).</string>
<item> <item>
<widget class="QSpinBox" name="sbTxPercent"> <widget class="QSpinBox" name="sbTxPercent">
<property name="toolTip"> <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>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
@ -2316,6 +2352,12 @@ list. The list can be maintained in Settings (F2).</string>
<property name="toolTip"> <property name="toolTip">
<string>Upload decoded messages to WSPRnet.org.</string> <string>Upload decoded messages to WSPRnet.org.</string>
</property> </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"> <property name="text">
<string>Upload spots</string> <string>Upload spots</string>
</property> </property>
@ -2351,10 +2393,11 @@ list. The list can be maintained in Settings (F2).</string>
<item> <item>
<widget class="QPushButton" name="pbTxNext"> <widget class="QPushButton" name="pbTxNext">
<property name="toolTip"> <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>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">QPushButton:checked { <string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red; background-color: red;
border-style: outset; border-style: outset;
border-width: 1px; border-width: 1px;
@ -2440,6 +2483,39 @@ list. The list can be maintained in Settings (F2).</string>
</widget> </widget>
</widget> </widget>
</item> </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"> <item row="0" column="4">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
@ -2447,6 +2523,123 @@ list. The list can be maintained in Settings (F2).</string>
</property> </property>
</widget> </widget>
</item> </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"> <item row="2" column="2">
<widget class="QWidget" name="DX_controls_widget" native="true"> <widget class="QWidget" name="DX_controls_widget" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
@ -2723,182 +2916,6 @@ list. The list can be maintained in Settings (F2).</string>
</layout> </layout>
</widget> </widget>
</item> </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> </layout>
</item> </item>
</layout> </layout>