Merge branch 'feat-dropped-frames' into develop

This commit is contained in:
Bill Somerville 2020-08-16 03:35:10 +01:00
commit c99f236dca
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
26 changed files with 1653 additions and 1384 deletions

6
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -222,9 +222,12 @@ set (WSJT_QT_CONF_DESTINATION ${QT_CONF_DESTINATION} CACHE PATH "Path for the qt
#
# Project sources
#
set (fort_qt_CXXSRCS
lib/shmem.cpp
)
set (wsjt_qt_CXXSRCS
qt_helpers.cpp
lib/shmem.cpp
widgets/MessageBox.cpp
MetaDataRegistry.cpp
Network/NetworkServerLookup.cpp
@ -730,6 +733,7 @@ set (qcp_CXXSRCS
set (all_CXXSRCS
${wsjt_CXXSRCS}
${fort_qt_CXXSRCS}
${wsjt_qt_CXXSRCS}
${wsjt_qtmm_CXXSRCS}
${wsjtx_CXXSRCS}
@ -744,7 +748,6 @@ set (all_C_and_CXXSRCS
)
set (TOP_LEVEL_RESOURCES
cty.dat
icons/Darwin/wsjtx.iconset/icon_128x128.png
contrib/gpl-v3-logo.svg
artwork/splash.png
@ -926,7 +929,9 @@ endif ()
if (WSJT_GENERATE_DOCS)
add_subdirectory (doc)
endif (WSJT_GENERATE_DOCS)
if (EXISTS ${CMAKE_SOURCE_DIR}/tests AND IS_DIRECTORY ${CMAKE_SOURCE_DIR}/tests)
add_subdirectory (tests)
endif ()
#
# Library building setup
@ -1256,6 +1261,11 @@ if (WIN32)
target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase)
endif (WIN32)
# build a library of package Qt functionality used in Fortran utilities
add_library (fort_qt STATIC ${fort_qt_CXXSRCS})
target_link_libraries (fort_qt Qt5::Core)
# build a library of WSJT Qt multimedia components
add_library (wsjt_qtmm STATIC ${wsjt_qtmm_CXXSRCS} ${wsjt_qtmm_GENUISRCS})
target_link_libraries (wsjt_qtmm Qt5::Multimedia)
@ -1303,9 +1313,9 @@ if (${OPENMP_FOUND} OR APPLE)
LINK_FLAGS -Wl,--stack,16777216
)
endif ()
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx wsjt_qt)
target_link_libraries (jt9 wsjt_fort_omp wsjt_cxx fort_qt)
else (${OPENMP_FOUND} OR APPLE)
target_link_libraries (jt9 wsjt_fort wsjt_cxx Qt5::Core)
target_link_libraries (jt9 wsjt_fort wsjt_cxx fort_qt)
endif (${OPENMP_FOUND} OR APPLE)
if(WSJT_BUILD_UTILS)
@ -1563,6 +1573,7 @@ install (FILES
)
install (FILES
cty.dat
contrib/Ephemeris/JPLEPH
DESTINATION ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}
#COMPONENT runtime

View File

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

View File

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

View File

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

View File

@ -105,7 +105,14 @@ void Modulator::start (QString mode, unsigned symbolsLength, double framesPerSym
// qDebug() << "delay_ms:" << delay_ms << "mstr:" << mstr << "m_silentFrames:" << m_silentFrames << "m_ic:" << m_ic << "m_state:" << m_state;
m_stream = stream;
if (m_stream) m_stream->restart (this);
if (m_stream)
{
m_stream->restart (this);
}
else
{
qDebug () << "Modulator::start: no audio output stream assigned";
}
}
void Modulator::tune (bool newState)

View File

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

View File

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

View File

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

View File

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

1609
cty.dat

File diff suppressed because it is too large Load Diff

View File

@ -319,12 +319,12 @@ AD1CCty::AD1CCty (Configuration const * configuration)
{
Q_ASSERT (configuration);
// TODO: G4WJS - consider doing the following asynchronously to
// speed up startup. Not urgent as it takes less than 1s on a Core
// speed up startup. Not urgent as it takes less than 0.5s on a Core
// i7 reading BIG CTY.DAT.
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
m_->path_ = dataPath.exists (file_name)
? dataPath.absoluteFilePath (file_name) // user override
: QString {":/"} + file_name; // or original in the resources FS
: configuration->data_dir ().absoluteFilePath (file_name); // or original
QFile file {m_->path_};
if (file.open (QFile::ReadOnly))
{

View File

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

View File

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

View File

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

View File

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

23
tests/CMakeLists.txt Normal file
View File

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

138
tests/test_qt_helpers.cpp Normal file
View File

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

View File

@ -209,6 +209,8 @@ namespace
QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"};
auto quint32_max = std::numeric_limits<quint32>::max ();
constexpr int N_WIDGETS {34};
constexpr int rx_chunk_size {3456}; // audio samples at 12000 Hz
constexpr int tx_audio_buffer_size {48000 / 5}; // audio frames at 48000 Hz
bool message_is_73 (int type, QStringList const& msg_parts)
{
@ -283,7 +285,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_idleMinutes {0},
m_nSubMode {0},
m_nclearave {1},
m_pctx {0},
m_nseq {0},
m_nWSPRdecodes {0},
m_k0 {9999999},
@ -309,7 +310,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_bShMsgs {false},
m_bSWL {false},
m_uploading {false},
m_txNext {false},
m_grid6 {false},
m_tuneup {false},
m_bTxTime {false},
@ -394,8 +394,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
},
m_sfx {"P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A"},
mem_jt9 {shdmem},
m_msAudioOutputBuffered (0u),
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
m_downSampleFactor (downSampleFactor),
m_audioThreadPriority (QThread::HighPriority),
m_bandEdited {false},
@ -471,9 +469,23 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (this, &MainWindow::startAudioInputStream, m_soundInput, &SoundInput::start);
connect (this, &MainWindow::suspendAudioInputStream, m_soundInput, &SoundInput::suspend);
connect (this, &MainWindow::resumeAudioInputStream, m_soundInput, &SoundInput::resume);
connect (this, &MainWindow::reset_audio_input_stream, m_soundInput, &SoundInput::reset);
connect (this, &MainWindow::finished, m_soundInput, &SoundInput::stop);
connect(m_soundInput, &SoundInput::error, this, &MainWindow::showSoundInError);
// connect(m_soundInput, &SoundInput::status, this, &MainWindow::showStatusMessage);
connect (m_soundInput, &SoundInput::dropped_frames, this, [this] (qint32 dropped_frames, qint64 usec) {
if (dropped_frames > 48000 / 5) // 1/5 second
{
showStatusMessage (tr ("%1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3));
}
if (dropped_frames > 48000) // 1 second
{
MessageBox::warning_message (this
, tr ("Audio Source")
, tr ("Reduce system load")
, tr ("Excessive dropped samples - %1 (%2 sec) audio frames dropped").arg (dropped_frames).arg (usec / 1.e6, 5, 'f', 3));
}
});
connect (&m_audioThread, &QThread::finished, m_soundInput, &QObject::deleteLater);
connect (this, &MainWindow::finished, this, &MainWindow::close);
@ -937,14 +949,24 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect (&m_wav_future_watcher, &QFutureWatcher<void>::finished, this, &MainWindow::diskDat);
connect(&watcher3, SIGNAL(finished()),this,SLOT(fast_decode_done()));
Q_EMIT startAudioInputStream (m_config.audio_input_device (), m_framesAudioInputBuffered, m_detector, m_downSampleFactor, m_config.audio_input_channel ());
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered);
if (!m_config.audio_input_device ().isNull ())
{
Q_EMIT startAudioInputStream (m_config.audio_input_device ()
, rx_chunk_size * m_downSampleFactor
, m_detector, m_downSampleFactor, m_config.audio_input_channel ());
}
if (!m_config.audio_output_device ().isNull ())
{
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device ()
, AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2
, tx_audio_buffer_size);
}
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
ui->label_9->setStyleSheet("QLabel{background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{background-color: #aabec8}");
ui->label_9->setStyleSheet("QLabel{color: #000000; background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{color: #000000; background-color: #aabec8}");
// this must be done before initializing the mode as some modes need
// to turn off split on the rig e.g. WSPR
@ -972,7 +994,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
}
m_saveDecoded=ui->actionSave_decoded->isChecked();
m_saveAll=ui->actionSave_all->isChecked();
ui->sbTxPercent->setValue(m_pctx);
ui->TxPowerComboBox->setCurrentIndex(int(.3 * m_dBm + .2));
ui->cbUploadWSPR_Spots->setChecked(m_uploadWSPRSpots);
if((m_ndepth&7)==1) ui->actionQuickDecode->setChecked(true);
@ -995,12 +1016,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_isort=-3;
m_max_dB=70;
m_CQtype="CQ";
if(m_mode=="WSPR" and m_pctx>0) {
QPalette palette {ui->sbTxPercent->palette ()};
palette.setColor(QPalette::Base,Qt::yellow);
ui->sbTxPercent->setPalette(palette);
}
fixStop();
VHF_features_enabled(m_config.enable_VHF_features());
m_wideGraph->setVHF(m_config.enable_VHF_features());
@ -1144,7 +1159,7 @@ void MainWindow::writeSettings()
m_settings->setValue("GUItab",ui->tabWidget->currentIndex());
m_settings->setValue("OutBufSize",outBufSize);
m_settings->setValue ("HoldTxFreq", ui->cbHoldTxFreq->isChecked ());
m_settings->setValue("PctTx",m_pctx);
m_settings->setValue("PctTx", ui->sbTxPercent->value ());
m_settings->setValue("dBm",m_dBm);
m_settings->setValue("RR73",m_send_RR73);
m_settings->setValue ("WSPRPreferType1", ui->WSPR_prefer_type_1_check_box->isChecked ());
@ -1235,7 +1250,8 @@ void MainWindow::readSettings()
ui->TxFreqSpinBox->setValue(0); // ensure a change is signaled
ui->TxFreqSpinBox->setValue(m_settings->value("TxFreq",1500).toInt());
m_ndepth=m_settings->value("NDepth",3).toInt();
m_pctx=m_settings->value("PctTx",20).toInt();
ui->sbTxPercent->setValue (m_settings->value ("PctTx", 20).toInt ());
on_sbTxPercent_valueChanged (ui->sbTxPercent->value ());
m_dBm=m_settings->value("dBm",37).toInt();
m_send_RR73=m_settings->value("RR73",false).toBool();
if(m_send_RR73) {
@ -1244,7 +1260,6 @@ void MainWindow::readSettings()
}
ui->WSPR_prefer_type_1_check_box->setChecked (m_settings->value ("WSPRPreferType1", true).toBool ());
m_uploadWSPRSpots=m_settings->value("UploadSpots",false).toBool();
if(!m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}");
ui->cbNoOwnCall->setChecked(m_settings->value("NoOwnCall",false).toBool());
ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool());
// setup initial value of tx attenuator
@ -1277,8 +1292,6 @@ void MainWindow::readSettings()
// use these initialisation settings to tune the audio o/p buffer
// size and audio thread priority
m_settings->beginGroup ("Tune");
m_msAudioOutputBuffered = m_settings->value ("Audio/OutputBufferMs").toInt ();
m_framesAudioInputBuffered = m_settings->value ("Audio/InputBufferFrames", RX_SAMPLE_RATE / 10).toInt ();
m_audioThreadPriority = static_cast<QThread::Priority> (m_settings->value ("Audio/ThreadPriority", QThread::HighPriority).toInt () % 8);
m_settings->endGroup ();
@ -1536,6 +1549,10 @@ void MainWindow::dataSink(qint64 frames)
if(m_mode!="WSPR") decode(); //Start decoder
if(m_mode=="FT8" and !m_diskData and (m_ihsym==m_earlyDecode or m_ihsym==m_earlyDecode2)) return;
if (!m_diskData)
{
Q_EMIT reset_audio_input_stream (true); // signals dropped samples
}
if(!m_diskData and (m_saveAll or m_saveDecoded or m_mode=="WSPR" or m_mode=="FST4W")) {
//Always save unless "Save None"; may delete later
if(m_TRperiod < 60) {
@ -1614,14 +1631,15 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int
format.setChannelCount (1);
format.setSampleSize (16);
format.setSampleType (QAudioFormat::SignedInt);
auto source = QString {"%1, %2"}.arg (my_callsign).arg (my_grid);
auto comment = QString {"Mode=%1%2, Freq=%3%4"}
.arg (mode)
.arg (QString {(mode.contains ('J') && !mode.contains ('+')) || mode.startsWith ("FST4")
? QString {", Sub Mode="} + QChar {'A' + sub_mode}
: QString {}})
.arg (Radio::frequency_MHz_string (frequency))
.arg (QString {mode!="WSPR" ? QString {", DXCall=%1, DXGrid=%2"}
auto source = QString {"%1; %2"}.arg (my_callsign).arg (my_grid);
auto comment = QString {"Mode=%1%2; Freq=%3%4"}
.arg (mode)
.arg (QString {(mode.contains ('J') && !mode.contains ('+'))
|| mode.startsWith ("FST4") || mode.startsWith ("QRA")
? QString {"; Sub Mode="} + QString::number (int (samples / 12000)) + QChar {'A' + sub_mode}
: QString {}})
.arg (Radio::frequency_MHz_string (frequency))
.arg (QString {mode!="WSPR" ? QString {"; DXCall=%1; DXGrid=%2"}
.arg (his_call)
.arg (his_grid).toLocal8Bit () : ""});
BWFFile::InfoDictionary list_info {
@ -1783,6 +1801,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
auto callsign = m_config.my_callsign ();
auto my_grid = m_config.my_grid ();
SpecOp nContest0=m_config.special_op_id();
auto psk_on = m_config.spot_to_psk_reporter ();
if (QDialog::Accepted == m_config.exec ()) {
checkMSK144ContestType();
if (m_config.my_callsign () != callsign) {
@ -1796,18 +1815,25 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
on_dxGridEntry_textChanged (m_hisGrid); // recalculate distances in case of units change
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
if(m_config.spot_to_psk_reporter ()) pskSetLocal ();
pskSetLocal ();
// this will close the connection to PSKReporter if it has been
// disabled
if (psk_on && !m_config.spot_to_psk_reporter ())
{
m_psk_Reporter.sendReport (true);
}
if(m_config.restart_audio_input ()) {
Q_EMIT startAudioInputStream (m_config.audio_input_device (),
m_framesAudioInputBuffered, m_detector, m_downSampleFactor,
m_config.audio_input_channel ());
Q_EMIT startAudioInputStream (m_config.audio_input_device ()
, rx_chunk_size * m_downSampleFactor
, m_detector, m_downSampleFactor
, m_config.audio_input_channel ());
}
if(m_config.restart_audio_output ()) {
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (),
AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2,
m_msAudioOutputBuffered);
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device ()
, AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2
, tx_audio_buffer_size);
}
displayDialFrequency ();
@ -1909,18 +1935,14 @@ void MainWindow::on_autoButton_clicked (bool checked)
m_nclearave=1;
echocom_.nsum=0;
}
if(m_mode=="WSPR" or m_mode=="FST4W") {
QPalette palette {ui->sbTxPercent->palette ()};
if(m_auto or m_pctx==0) {
palette.setColor(QPalette::Base,Qt::white);
} else {
palette.setColor(QPalette::Base,Qt::yellow);
}
ui->sbTxPercent->setPalette(palette);
}
m_tAutoOn=QDateTime::currentMSecsSinceEpoch()/1000;
}
void MainWindow::on_sbTxPercent_valueChanged (int n)
{
update_dynamic_property (ui->sbTxPercent, "notx", !n);
}
void MainWindow::auto_tx_mode (bool state)
{
ui->autoButton->setChecked (state);
@ -2279,8 +2301,8 @@ bool MainWindow::eventFilter (QObject * object, QEvent * event)
void MainWindow::createStatusBar() //createStatusBar
{
tx_status_label.setAlignment (Qt::AlignHCenter);
tx_status_label.setMinimumSize (QSize {150, 18});
tx_status_label.setStyleSheet ("QLabel{background-color: #00ff00}");
tx_status_label.setMinimumSize (QSize {100, 18});
tx_status_label.setStyleSheet ("QLabel{color: #000000; background-color: #00ff00}");
tx_status_label.setFrameStyle (QFrame::Panel | QFrame::Sunken);
statusBar()->addWidget (&tx_status_label);
@ -2328,37 +2350,37 @@ void MainWindow::setup_status_bar (bool vhf)
mode_label.setText (m_mode);
}
if ("ISCAT" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff9933}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff9933}");
} else if ("JT9" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6ec7}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6ec7}");
} else if ("JT4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #cc99ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #cc99ff}");
} else if ("Echo" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #66ffff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ffff}");
} else if ("JT9+JT65" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ffff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ffff66}");
} else if ("JT65" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #66ff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #66ff66}");
} else if ("QRA64" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #99ff33}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff33}");
} else if ("MSK144" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6666}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6666}");
} else if ("FT4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff0099}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff0099}");
} else if ("FT8" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff6699}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff6699}");
} else if ("FST4" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #99ff66}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #99ff66}");
} else if ("FST4W" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #6699ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #6699ff}");
} else if ("FreqCal" == m_mode) {
mode_label.setStyleSheet ("QLabel{background-color: #ff9933}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff9933}");
}
last_tx_label.setText (QString {});
if (m_mode.contains (QRegularExpression {R"(^(Echo|ISCAT))"})) {
if (band_hopping_label.isVisible ()) statusBar ()->removeWidget (&band_hopping_label);
} else if (m_mode=="WSPR") {
mode_label.setStyleSheet ("QLabel{background-color: #ff66ff}");
mode_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff66ff}");
if (!band_hopping_label.isVisible ()) {
statusBar ()->addWidget (&band_hopping_label);
band_hopping_label.show ();
@ -2689,7 +2711,7 @@ void MainWindow::on_actionOpen_triggered() //Open File
m_path=fname;
int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1);
tx_status_label.setStyleSheet("QLabel{background-color: #99ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #99ffff}");
tx_status_label.setText(" " + baseName + " ");
on_stopButton_clicked();
m_diskData=true;
@ -2769,7 +2791,7 @@ void MainWindow::on_actionOpen_next_in_directory_triggered() //Open Next
m_path=fname;
int i1=fname.lastIndexOf("/");
QString baseName=fname.mid(i1+1);
tx_status_label.setStyleSheet("QLabel{background-color: #99ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #99ffff}");
tx_status_label.setText(" " + baseName + " ");
m_diskData=true;
read_wav_file (fname);
@ -2922,7 +2944,7 @@ void MainWindow::on_actionSpecial_mouse_commands_triggered()
<td><b>Click</b> to set Rx frequency.<br/>
<b>Shift-click</b> to set Tx frequency.<br/>
<b>Ctrl-click</b> or <b>Right-click</b> to set Rx and Tx frequencies.<br/>
<b>Double-click</b> to also decode at Rx frequency.<br/>
<b>Double-click</b> to also decode at Rx frequency.<br/>
</td>
</tr>
<tr>
@ -2930,10 +2952,10 @@ void MainWindow::on_actionSpecial_mouse_commands_triggered()
<td><b>Double-click</b> to copy second callsign to Dx Call,<br/>
locator to Dx Grid, change Rx and Tx frequency to<br/>
decoded signal's frequency, and generate standard<br/>
messages.<br/>
If <b>Hold Tx Freq</b> is checked or first callsign in message<br/>
is your own call, Tx frequency is not changed unless <br/>
<b>Ctrl</b> is held down.<br/>
messages.<br/>
If <b>Hold Tx Freq</b> is checked or first callsign in message<br/>
is your own call, Tx frequency is not changed unless <br/>
<b>Ctrl</b> is held down.<br/>
</td>
</tr>
<tr>
@ -3109,56 +3131,58 @@ void MainWindow::decode() //decode()
//newdat=1 ==> this is new data, must do the big FFT
//nagain=1 ==> decode only at fQSO +/- Tol
char *to = (char*)mem_jt9->data();
char *from = (char*) dec_data.ipc;
int size=sizeof(struct dec_data);
if(dec_data.params.newdat==0) {
int noffset {offsetof (struct dec_data, params.nutc)};
to += noffset;
from += noffset;
size -= noffset;
}
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
float t0=m_t0;
float t1=m_t1;
qApp->processEvents(); //Update the waterfall
if(m_nPick > 0) {
t0=m_t0Pick;
t1=m_t1Pick;
if (auto * to = reinterpret_cast<char *> (mem_jt9->data()))
{
char *from = (char*) dec_data.ipc;
int size=sizeof(struct dec_data);
if(dec_data.params.newdat==0) {
int noffset {offsetof (struct dec_data, params.nutc)};
to += noffset;
from += noffset;
size -= noffset;
}
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
float t0=m_t0;
float t1=m_t1;
qApp->processEvents(); //Update the waterfall
if(m_nPick > 0) {
t0=m_t0Pick;
t1=m_t1Pick;
}
static short int d2b[360000];
narg[0]=dec_data.params.nutc;
if(m_kdone>int(12000.0*m_TRperiod)) {
m_kdone=int(12000.0*m_TRperiod);
}
narg[1]=m_kdone;
narg[2]=m_nSubMode;
narg[3]=dec_data.params.newdat;
narg[4]=dec_data.params.minSync;
narg[5]=m_nPick;
narg[6]=1000.0*t0;
narg[7]=1000.0*t1;
narg[8]=2; //Max decode lines per decode attempt
if(dec_data.params.minSync<0) narg[8]=50;
if(m_mode=="ISCAT") narg[9]=101; //ISCAT
if(m_mode=="JT9") narg[9]=102; //Fast JT9
if(m_mode=="MSK144") narg[9]=104; //MSK144
narg[10]=ui->RxFreqSpinBox->value();
narg[11]=ui->sbFtol->value ();
narg[12]=0;
narg[13]=-1;
narg[14]=m_config.aggressive();
memcpy(d2b,dec_data.d2,2*360000);
watcher3.setFuture (QtConcurrent::run (std::bind (fast_decode_,&d2b[0],
&narg[0],&m_TRperiod,&m_msg[0][0],
dec_data.params.mycall,dec_data.params.hiscall,8000,12,12)));
} else {
mem_jt9->lock ();
memcpy(to, from, qMin(mem_jt9->size(), size));
mem_jt9->unlock ();
to_jt9(m_ihsym,1,-1); //Send m_ihsym to jt9[.exe] and start decoding
decodeBusy(true);
}
}
static short int d2b[360000];
narg[0]=dec_data.params.nutc;
if(m_kdone>int(12000.0*m_TRperiod)) {
m_kdone=int(12000.0*m_TRperiod);
}
narg[1]=m_kdone;
narg[2]=m_nSubMode;
narg[3]=dec_data.params.newdat;
narg[4]=dec_data.params.minSync;
narg[5]=m_nPick;
narg[6]=1000.0*t0;
narg[7]=1000.0*t1;
narg[8]=2; //Max decode lines per decode attempt
if(dec_data.params.minSync<0) narg[8]=50;
if(m_mode=="ISCAT") narg[9]=101; //ISCAT
if(m_mode=="JT9") narg[9]=102; //Fast JT9
if(m_mode=="MSK144") narg[9]=104; //MSK144
narg[10]=ui->RxFreqSpinBox->value();
narg[11]=ui->sbFtol->value ();
narg[12]=0;
narg[13]=-1;
narg[14]=m_config.aggressive();
memcpy(d2b,dec_data.d2,2*360000);
watcher3.setFuture (QtConcurrent::run (std::bind (fast_decode_,&d2b[0],
&narg[0],&m_TRperiod,&m_msg[0][0],
dec_data.params.mycall,dec_data.params.hiscall,8000,12,12)));
} else {
mem_jt9->lock ();
memcpy(to, from, qMin(mem_jt9->size(), size));
mem_jt9->unlock ();
to_jt9(m_ihsym,1,-1); //Send m_ihsym to jt9[.exe] and start decoding
decodeBusy(true);
}
}
void::MainWindow::fast_decode_done()
@ -3206,12 +3230,14 @@ void::MainWindow::fast_decode_done()
void MainWindow::to_jt9(qint32 n, qint32 istart, qint32 idone)
{
dec_data_t * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data());
mem_jt9->lock ();
dd->ipc[0]=n;
if(istart>=0) dd->ipc[1]=istart;
if(idone>=0) dd->ipc[2]=idone;
mem_jt9->unlock ();
if (auto * dd = reinterpret_cast<dec_data_t *> (mem_jt9->data()))
{
mem_jt9->lock ();
dd->ipc[0]=n;
if(istart>=0) dd->ipc[1]=istart;
if(idone>=0) dd->ipc[2]=idone;
mem_jt9->unlock ();
}
}
void MainWindow::decodeDone ()
@ -3268,7 +3294,8 @@ void MainWindow::readFromStdout() //readFromStdout
line_read = line_read.left (64);
}
}
if(m_mode!="FT8" and m_mode!="FT4") {
if (m_mode!="FT8" and m_mode!="FT4"
&& !m_mode.startsWith ("FST4")) {
//Pad 22-char msg to at least 37 chars
line_read = line_read.left(44) + " " + line_read.mid(44);
}
@ -3486,9 +3513,8 @@ void MainWindow::readFromStdout() //readFromStdout
bool okToPost=(nsec > int(4*m_TRperiod)/5);
if(m_mode=="FST4W" and okToPost) {
line_read=line_read.left(22) + " CQ " + line_read.trimmed().mid(22);
int n=line_read.trimmed().size();
line_read=line_read.trimmed().left(n-3);
DecodedText FST4W_post {QString::fromUtf8(line_read.constData())};
auto p = line_read.lastIndexOf (' ');
DecodedText FST4W_post {QString::fromUtf8 (line_read.left (p).constData ())};
pskPost(FST4W_post);
} else {
if (stdMsg && okToPost) pskPost(decodedtext);
@ -3721,26 +3747,22 @@ void MainWindow::guiUpdate()
if(m_mode=="WSPR" or m_mode=="FST4W") {
if(m_nseq==0 and m_ntr==0) { //Decide whether to Tx or Rx
m_tuneup=false; //This is not an ATU tuneup
if(ui->sbTxPercent->isEnabled () && m_pctx==0) m_WSPR_tx_next = false; //Don't transmit if m_pctx=0
bool btx = m_auto && m_WSPR_tx_next; // To Tx, we need m_auto and
// scheduled transmit
if(m_auto and m_txNext) btx=true; //TxNext button overrides
if(m_auto && ui->sbTxPercent->isEnabled () && m_pctx==100) btx=true; //Always transmit
m_WSPR_tx_next = false;
if(btx) {
m_ntr=-1; //This says we will have transmitted
m_txNext=false;
ui->pbTxNext->setChecked(false);
ui->pbTxNext->setChecked (false);
m_bTxTime=true; //Start a WSPR or FST4W Tx sequence
} else {
// This will be a WSPR or FST4W Rx sequence.
// This will be a WSPR or FST4W Rx sequence.
m_ntr=1; //This says we will have received
m_bTxTime=false; //Start a WSPR or FST4W Rx sequence
}
}
} else {
// For all modes other than WSPR and Fst4W
// For all modes other than WSPR and Fst4W
m_bTxTime = (t2p >= tx1) and (t2p < tx2);
if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK;
if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) {
@ -4276,11 +4298,11 @@ void MainWindow::guiUpdate()
m_nsendingsh=0;
if(s[4]==64) m_nsendingsh=1;
if(m_nsendingsh==1 or m_currentMessageType==7) {
tx_status_label.setStyleSheet("QLabel{background-color: #66ffff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #66ffff}");
} else if(m_nsendingsh==-1 or m_currentMessageType==6) {
tx_status_label.setStyleSheet("QLabel{background-color: #ffccff}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ffccff}");
} else {
tx_status_label.setStyleSheet("QLabel{background-color: #ffff33}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ffff33}");
}
if(m_tune) {
tx_status_label.setText("Tx: TUNE");
@ -4299,11 +4321,11 @@ void MainWindow::guiUpdate()
}
} else if(m_monitoring) {
if (!m_tx_watchdog) {
tx_status_label.setStyleSheet("QLabel{background-color: #00ff00}");
tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #00ff00}");
auto t = tr ("Receiving");
if(m_mode=="MSK144") {
int npct=int(100.0*m_fCPUmskrtd/0.298667);
if(npct>90) tx_status_label.setStyleSheet("QLabel{background-color: #ff0000}");
if(npct>90) tx_status_label.setStyleSheet("QLabel{color: #000000; background-color: #ff0000}");
t += QString {" %1%"}.arg (npct, 2);
}
tx_status_label.setText (t);
@ -5017,7 +5039,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
// m_nextGrid=message_words.at(3);
// m_nextRpt=message.report();
// ui->labNextCall->setText("Next: " + m_nextCall);
// ui->labNextCall->setStyleSheet("QLabel {background-color: #66ff66}");
// ui->labNextCall->setStyleSheet("QLabel {color: #000000; background-color: #66ff66}");
// }
return;
}
@ -5238,11 +5260,11 @@ bool MainWindow::stdCall(QString const& w)
{
static QRegularExpression standard_call_re {
R"(
^\s* # optional leading spaces
^\s* # optional leading spaces
( [A-Z]{0,2} | [A-Z][0-9] | [0-9][A-Z] ) # part 1
( [0-9][A-Z]{0,3} ) # part 2
(/R | /P)? # optional suffix
\s*$ # optional trailing spaces
(/R | /P)? # optional suffix
\s*$ # optional trailing spaces
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
return standard_call_re.match (w).hasMatch ();
}
@ -5742,8 +5764,6 @@ void MainWindow::on_tx6_editingFinished() //tx6 edited
void MainWindow::on_RoundRobin_currentTextChanged(QString text)
{
ui->sbTxPercent->setEnabled (text == tr ("Random"));
m_WSPR_tx_next = false; // cancel any pending Tx to avoid
// undesirable consecutive Tx periods
}
@ -6852,7 +6872,11 @@ void MainWindow::band_changed (Frequency f)
}
m_lastBand.clear ();
m_bandEdited = false;
m_psk_Reporter.sendReport(); // Upload any queued spots before changing band
if (m_config.spot_to_psk_reporter ())
{
// Upload any queued spots before changing band
m_psk_Reporter.sendReport();
}
if (!m_transmitting) monitor (true);
if ("FreqCal" == m_mode)
{
@ -7178,14 +7202,15 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
{
Transceiver::TransceiverState old_state {m_rigState};
//transmitDisplay (s.ptt ());
if (s.ptt () && !m_rigState.ptt ()) { // safe to start audio
if (s.ptt () // && !m_rigState.ptt ()
) { // safe to start audio
// (caveat - DX Lab Suite Commander)
if (m_tx_when_ready && g_iptt) { // waiting to Tx and still needed
int ms_delay=1000*m_config.txDelay();
if(m_mode=="FT4") ms_delay=20;
ptt1Timer.start(ms_delay); //Start-of-transmission sequencer delay
m_tx_when_ready = false;
}
m_tx_when_ready = false;
}
m_rigState = s;
auto old_freqNominal = m_freqNominal;
@ -7221,9 +7246,7 @@ void MainWindow::handle_transceiver_update (Transceiver::TransceiverState const&
|| !(ui->cbCQTx->isEnabled () && ui->cbCQTx->isVisible () && ui->cbCQTx->isChecked()))) {
m_lastDialFreq = m_freqNominal;
m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000;
if (m_config.spot_to_psk_reporter ()) {
pskSetLocal ();
}
pskSetLocal ();
statusChanged();
m_wideGraph->setDialFreq(m_freqNominal / 1.e6);
}
@ -7526,6 +7549,8 @@ bool MainWindow::shortList(QString callsign)
void MainWindow::pskSetLocal ()
{
if (!m_config.spot_to_psk_reporter ()) return;
// find the station row, if any, that matches the band we are on
auto stations = m_config.stations ();
auto matches = stations->match (stations->index (0, StationList::band_column)
@ -8017,8 +8042,8 @@ void MainWindow::p1ReadFromStdout() //p1readFromStdout
QString MainWindow::beacon_start_time (int n)
{
auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod);
if (m_TRperiod < 60)
auto bt = qt_truncate_date_time_to (QDateTime::currentDateTimeUtc ().addSecs (n), m_TRperiod * 1.e3);
if (m_TRperiod < 60.)
{
return bt.toString ("HHmmss");
}
@ -8072,17 +8097,18 @@ void MainWindow::uploadWSPRSpots (bool direct_post, QString const& decode_text)
QString rfreq = QString("%1").arg((m_dialFreqRxWSPR + 1500) / 1e6, 0, 'f', 6);
QString tfreq = QString("%1").arg((m_dialFreqRxWSPR +
ui->TxFreqSpinBox->value()) / 1e6, 0, 'f', 6);
auto pct = QString::number (ui->autoButton->isChecked () ? ui->sbTxPercent->value () : 0);
if (!direct_post)
{
wsprNet->upload (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq,
m_mode, m_TRperiod, QString::number (ui->autoButton->isChecked () ? m_pctx : 0),
m_mode, m_TRperiod, pct,
QString::number (m_dBm), version (),
m_config.writeable_data_dir ().absoluteFilePath ("wspr_spots.txt"));
}
else
{
wsprNet->post (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq,
m_mode, m_TRperiod, QString::number (ui->autoButton->isChecked () ? m_pctx : 0),
m_mode, m_TRperiod, pct,
QString::number (m_dBm), version (), decode_text);
}
if (!decode_text.size ())
@ -8108,24 +8134,9 @@ void MainWindow::on_TxPowerComboBox_currentIndexChanged(int index)
m_dBm = ui->TxPowerComboBox->itemData (index).toInt ();
}
void MainWindow::on_sbTxPercent_valueChanged(int n)
{
m_pctx=n;
if(m_pctx>0) {
ui->pbTxNext->setEnabled(true);
} else {
m_txNext=false;
ui->pbTxNext->setChecked(false);
ui->pbTxNext->setEnabled(false);
}
}
void MainWindow::on_cbUploadWSPR_Spots_toggled(bool b)
{
m_uploadWSPRSpots=b;
if(m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet("");
if(!m_uploadWSPRSpots) ui->cbUploadWSPR_Spots->setStyleSheet(
"QCheckBox{background-color: yellow}");
}
void MainWindow::on_WSPRfreqSpinBox_valueChanged(int n)
@ -8135,11 +8146,20 @@ void MainWindow::on_WSPRfreqSpinBox_valueChanged(int n)
void MainWindow::on_pbTxNext_clicked(bool b)
{
m_txNext=b;
if (b && !ui->autoButton->isChecked ())
{
ui->autoButton->click (); // make sure Tx is possible
}
}
void MainWindow::WSPR_scheduling ()
{
if (ui->pbTxNext->isEnabled () && ui->pbTxNext->isChecked ())
{
// Tx Next button overrides all scheduling
m_WSPR_tx_next = true;
return;
}
QString t=ui->RoundRobin->currentText();
if(m_mode=="FST4W" and t != tr ("Random")) {
bool ok;
@ -8152,10 +8172,14 @@ void MainWindow::WSPR_scheduling ()
int nsec=ms/1000;
int ntr=m_TRperiod;
int j=((nsec+ntr-1) % (n*ntr))/ntr;
m_WSPR_tx_next=(i==j);
m_WSPR_tx_next = i == j;
return;
}
m_WSPR_tx_next = false;
if (!ui->sbTxPercent->isEnabled () || !ui->sbTxPercent->value ())
{
return; // don't schedule if %age disabled or zero
}
if (m_config.is_transceiver_online () // need working rig control for hopping
&& !m_config.is_dummy_rig ()
&& ui->band_hopping_group_box->isChecked ()) {
@ -8442,7 +8466,7 @@ void MainWindow::tx_watchdog (bool triggered)
m_bTxTime=false;
if (m_tune) stop_tuning ();
if (m_auto) auto_tx_mode (false);
tx_status_label.setStyleSheet ("QLabel{background-color: #ff0000}");
tx_status_label.setStyleSheet ("QLabel{color: #000000; background-color: #ff0000}");
tx_status_label.setText (tr ("Runaway Tx watchdog"));
QApplication::alert (this);
}

View File

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

View File

@ -376,6 +376,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: #000000;
background-color: #00ff00;
border-style: outset;
border-width: 1px;
@ -447,6 +448,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: cyan;
border-style: outset;
border-width: 1px;
@ -480,6 +482,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -523,6 +526,7 @@
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -997,6 +1001,7 @@ Not available to nonstandard callsign holders.</string>
<widget class="QPushButton" name="pbBestSP">
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -2158,6 +2163,12 @@ list. The list can be maintained in Settings (F2).</string>
<property name="toolTip">
<string>Percentage of minute sequences devoted to transmitting.</string>
</property>
<property name="styleSheet">
<string notr="true">QSpinBox:enabled[notx=&quot;true&quot;] {
color: rgb(0, 0, 0);
background-color: rgb(255, 255, 0);
}</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
@ -2341,6 +2352,12 @@ list. The list can be maintained in Settings (F2).</string>
<property name="toolTip">
<string>Upload decoded messages to WSPRnet.org.</string>
</property>
<property name="styleSheet">
<string notr="true">QCheckBox:unchecked {
color: rgb(0, 0, 0);
background-color: rgb(255, 255, 0);
}</string>
</property>
<property name="text">
<string>Upload spots</string>
</property>
@ -2380,6 +2397,7 @@ list. The list can be maintained in Settings (F2).</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton:checked {
color: rgb(0, 0, 0);
background-color: red;
border-style: outset;
border-width: 1px;
@ -2485,8 +2503,7 @@ list. The list can be maintained in Settings (F2).</string>
}
QLabel[oob=&quot;true&quot;] {
background-color: red;
}
</string>
}</string>
</property>
<property name="text">
<string>14.078 000</string>