Merge branch 'feat-refactor' into develop

This commit is contained in:
Bill Somerville 2019-07-04 13:16:03 +01:00
commit 0c49f6f872
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
6121 changed files with 125430 additions and 878614 deletions

4
.gitignore vendored
View File

@ -7,4 +7,6 @@ jnq*
*.exe
*.o
*.mod
*.pro.user
*.pro.user
cmake-build-debug
cmake-build-release

10
Audio/AudioDevice.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "AudioDevice.hpp"
bool AudioDevice::initialize (OpenMode mode, Channel channel)
{
m_channel = channel;
// open and ensure we are unbuffered if possible
return QIODevice::open (mode | QIODevice::Unbuffered);
}

View File

@ -38,7 +38,7 @@ namespace
{
auto end = reinterpret_cast<char const *> (::memchr (id, '\0', 4u));
auto len = end ? end - id : 4u;
memcpy (id_.data (), id, len);
::memcpy (id_.data (), id, len);
if (len < 4u)
{
memset (id_.data () + len, ' ', 4u - len);
@ -46,7 +46,7 @@ namespace
}
else
{
memcpy (id_.data (), "JUNK", 4u);
::memcpy (id_.data (), "JUNK", 4u);
}
}
@ -86,12 +86,12 @@ namespace
{
// set some sensible defaults for the "bext" fields
auto now = QDateTime::currentDateTimeUtc ();
std::strncpy (origination_date_,
now.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (),
sizeof origination_date_);
std::strncpy (origination_time_,
now.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (),
sizeof origination_time_);
::memcpy (origination_date_,
now.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (),
sizeof origination_date_);
::memcpy (origination_time_,
now.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (),
sizeof origination_time_);
auto uuid = QUuid::createUuid ().toRfc4122 ();
std::copy (uuid.cbegin (), uuid.cend (), umid_.data () + 16);
}
@ -354,6 +354,7 @@ bool BWFFile::impl::update_header ()
{
case BextVersion::v_0:
data->version_ = qToBigEndian<quint32> (data->version_);
// fall through
default:
data->loudness_value_ = qToBigEndian<quint16> (data->loudness_value_);
data->loudness_range_ = qToBigEndian<quint16> (data->loudness_range_);
@ -370,6 +371,7 @@ bool BWFFile::impl::update_header ()
{
case BextVersion::v_0:
data->version_ = qToLittleEndian<quint32> (data->version_);
// fall through
default:
data->loudness_value_ = qToLittleEndian<quint16> (data->loudness_value_);
data->loudness_range_ = qToLittleEndian<quint16> (data->loudness_range_);
@ -686,7 +688,7 @@ QByteArray BWFFile::bext_description () const
void BWFFile::bext_description (QByteArray const& description)
{
m_->header_dirty_ = true;
std::strncpy (m_->bext ()->description_, description.constData (), sizeof (BroadcastAudioExtension::description_));
::memcpy (m_->bext ()->description_, description.constData (), sizeof BroadcastAudioExtension::description_);
}
QByteArray BWFFile::bext_originator () const
@ -698,7 +700,7 @@ QByteArray BWFFile::bext_originator () const
void BWFFile::bext_originator (QByteArray const& originator)
{
m_->header_dirty_ = true;
std::strncpy (m_->bext ()->originator_, originator.constData (), sizeof (BroadcastAudioExtension::originator_));
::memcpy (m_->bext ()->originator_, originator.constData (), sizeof BroadcastAudioExtension::originator_);
}
QByteArray BWFFile::bext_originator_reference () const
@ -710,7 +712,7 @@ QByteArray BWFFile::bext_originator_reference () const
void BWFFile::bext_originator_reference (QByteArray const& reference)
{
m_->header_dirty_ = true;
std::strncpy (m_->bext ()->originator_reference_, reference.constData (), sizeof (BroadcastAudioExtension::originator_reference_));
::memcpy (m_->bext ()->originator_reference_, reference.constData (), sizeof BroadcastAudioExtension::originator_reference_);
}
QDateTime BWFFile::bext_origination_date_time () const
@ -723,12 +725,12 @@ QDateTime BWFFile::bext_origination_date_time () const
void BWFFile::bext_origination_date_time (QDateTime const& dt)
{
m_->header_dirty_ = true;
std::strncpy (m_->bext ()->origination_date_,
dt.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (),
sizeof (BroadcastAudioExtension::origination_date_));
std::strncpy (m_->bext ()->origination_time_,
dt.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (),
sizeof (BroadcastAudioExtension::origination_time_));
::memcpy (m_->bext ()->origination_date_,
dt.date ().toString ("yyyy-MM-dd").toLocal8Bit ().constData (),
sizeof BroadcastAudioExtension::origination_date_);
::memcpy (m_->bext ()->origination_time_,
dt.time ().toString ("hh-mm-ss").toLocal8Bit ().constData (),
sizeof BroadcastAudioExtension::origination_time_);
}
quint64 BWFFile::bext_time_reference () const

52
Audio/soundin.h Normal file
View File

@ -0,0 +1,52 @@
// -*- Mode: C++ -*-
#ifndef SOUNDIN_H__
#define SOUNDIN_H__
#include <QObject>
#include <QString>
#include <QDateTime>
#include <QScopedPointer>
#include <QPointer>
#include <QAudioInput>
#include "Audio/AudioDevice.hpp"
class QAudioDeviceInfo;
class QAudioInput;
// Gets audio data from sound sample source and passes it to a sink device
class SoundInput
: public QObject
{
Q_OBJECT;
public:
SoundInput (QObject * parent = nullptr)
: QObject {parent}
, m_sink {nullptr}
{
}
~SoundInput ();
// sink must exist from the start call until the next start call or
// stop call
Q_SLOT void start(QAudioDeviceInfo const&, int framesPerBuffer, AudioDevice * sink, unsigned downSampleFactor, AudioDevice::Channel = AudioDevice::Mono);
Q_SLOT void suspend ();
Q_SLOT void resume ();
Q_SLOT void stop ();
Q_SIGNAL void error (QString message) const;
Q_SIGNAL void status (QString message) const;
private:
// used internally
Q_SLOT void handleStateChanged (QAudio::State) const;
bool audioError () const;
QScopedPointer<QAudioInput> m_stream;
QPointer<AudioDevice> m_sink;
};
#endif

View File

@ -1,10 +0,0 @@
#include "AudioDevice.hpp"
bool AudioDevice::initialize (OpenMode mode, Channel channel)
{
m_channel = channel;
// open and ensure we are unbuffered if possible
return QIODevice::open (mode | QIODevice::Unbuffered);
}

View File

@ -226,7 +226,7 @@ set (wsjt_qt_CXXSRCS
qt_helpers.cpp
widgets/MessageBox.cpp
MetaDataRegistry.cpp
NetworkServerLookup.cpp
Network/NetworkServerLookup.cpp
revision_utils.cpp
WFPalette.cpp
Radio.cpp
@ -243,17 +243,17 @@ set (wsjt_qt_CXXSRCS
validators/LiveFrequencyValidator.cpp
GetUserId.cpp
TraceFile.cpp
AudioDevice.cpp
Transceiver.cpp
TransceiverBase.cpp
EmulateSplitTransceiver.cpp
TransceiverFactory.cpp
PollingTransceiver.cpp
HamlibTransceiver.cpp
HRDTransceiver.cpp
DXLabSuiteCommanderTransceiver.cpp
NetworkMessage.cpp
MessageClient.cpp
Audio/AudioDevice.cpp
Transceiver/Transceiver.cpp
Transceiver/TransceiverBase.cpp
Transceiver/EmulateSplitTransceiver.cpp
Transceiver/TransceiverFactory.cpp
Transceiver/PollingTransceiver.cpp
Transceiver/HamlibTransceiver.cpp
Transceiver/HRDTransceiver.cpp
Transceiver/DXLabSuiteCommanderTransceiver.cpp
Network/NetworkMessage.cpp
Network/MessageClient.cpp
widgets/LettersSpinBox.cpp
widgets/HintedSpinBox.cpp
widgets/RestrictedSpinBox.cpp
@ -271,7 +271,7 @@ set (wsjt_qt_CXXSRCS
EqualizationToolsDialog.cpp
widgets/DoubleClickablePushButton.cpp
widgets/DoubleClickableRadioButton.cpp
LotWUsers.cpp
Network/LotWUsers.cpp
models/DecodeHighlightingModel.cpp
widgets/DecodeHighlightingListView.cpp
models/FoxLog.cpp
@ -303,15 +303,15 @@ set (jt9_CXXSRCS
set (wsjtx_CXXSRCS
logbook/logbook.cpp
psk_reporter.cpp
Modulator.cpp
Detector.cpp
Network/psk_reporter.cpp
Modulator/Modulator.cpp
Detector/Detector.cpp
widgets/logqso.cpp
widgets/displaytext.cpp
decodedtext.cpp
Decoder/decodedtext.cpp
getfile.cpp
soundout.cpp
soundin.cpp
Audio/soundout.cpp
Audio/soundin.cpp
widgets/meterwidget.cpp
widgets/signalmeter.cpp
widgets/plotter.cpp
@ -324,12 +324,12 @@ set (wsjtx_CXXSRCS
widgets/astro.cpp
widgets/messageaveraging.cpp
widgets/colorhighlighting.cpp
WsprTxScheduler.cpp
WSPR/WsprTxScheduler.cpp
widgets/mainwindow.cpp
Configuration.cpp
main.cpp
wsprnet.cpp
WSPRBandHopping.cpp
Network/wsprnet.cpp
WSPR/WSPRBandHopping.cpp
widgets/ExportCabrillo.cpp
)
@ -353,7 +353,7 @@ if (WIN32)
set (wsjt_qt_CXXSRCS
${wsjt_qt_CXXSRCS}
OmniRigTransceiver.cpp
Transceiver/OmniRigTransceiver.cpp
)
endif (WIN32)
@ -682,13 +682,13 @@ set (wsjtx_UISRCS
set (UDP_library_CXXSRCS
Radio.cpp
RadioMetaType.cpp
NetworkMessage.cpp
MessageServer.cpp
Network/NetworkMessage.cpp
UDPExamples/MessageServer.cpp
)
set (UDP_library_HEADERS
Radio.hpp
MessageServer.hpp
UDPExamples/MessageServer.hpp
${PROJECT_BINARY_DIR}/udp_export.h
)
@ -919,7 +919,7 @@ set (CMAKE_VISIBILITY_INLINES_HIDDEN ON)
#
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -fexceptions -frtti")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -fexceptions -frtti")
if (NOT APPLE)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pragmas")
@ -1099,7 +1099,7 @@ set (LANGUAGES
foreach (lang_ ${LANGUAGES})
file (TO_NATIVE_PATH translations/wsjtx_${lang_}.ts ts_)
list (APPEND TS_FILES ${ts_})
endforeach ()
endforeach ()
if (UPDATE_TRANSLATIONS)
message (STATUS "UPDATE_TRANSLATIONS option is set.")
qt5_create_translation (

View File

@ -172,18 +172,18 @@
#include "item_delegates/ForeignKeyDelegate.hpp"
#include "item_delegates/FrequencyDelegate.hpp"
#include "item_delegates/FrequencyDeltaDelegate.hpp"
#include "TransceiverFactory.hpp"
#include "Transceiver.hpp"
#include "Transceiver/TransceiverFactory.hpp"
#include "Transceiver/Transceiver.hpp"
#include "models/Bands.hpp"
#include "models/IARURegions.hpp"
#include "models/Modes.hpp"
#include "models/FrequencyList.hpp"
#include "models/StationList.hpp"
#include "NetworkServerLookup.hpp"
#include "Network/NetworkServerLookup.hpp"
#include "widgets/MessageBox.hpp"
#include "validators/MaidenheadLocatorValidator.hpp"
#include "validators/CallsignValidator.hpp"
#include "LotWUsers.hpp"
#include "Network/LotWUsers.hpp"
#include "models/DecodeHighlightingModel.hpp"
#include "logbook/logbook.h"

View File

@ -6,8 +6,8 @@
#include "Radio.hpp"
#include "models/IARURegions.hpp"
#include "AudioDevice.hpp"
#include "Transceiver.hpp"
#include "Audio/AudioDevice.hpp"
#include "Transceiver/Transceiver.hpp"
#include "pimpl_h.hpp"

View File

@ -1,506 +0,0 @@
#include "DXLabSuiteCommanderTransceiver.hpp"
#include <QTcpSocket>
#include <QRegularExpression>
#include <QLocale>
#include <QThread>
#include <QDateTime>
#include "NetworkServerLookup.hpp"
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
namespace
{
char const * const commander_transceiver_name {"DX Lab Suite Commander"};
QString map_mode (Transceiver::MODE mode)
{
switch (mode)
{
case Transceiver::AM: return "AM";
case Transceiver::CW: return "CW";
case Transceiver::CW_R: return "CW-R";
case Transceiver::USB: return "USB";
case Transceiver::LSB: return "LSB";
case Transceiver::FSK: return "RTTY";
case Transceiver::FSK_R: return "RTTY-R";
case Transceiver::DIG_L: return "DATA-L";
case Transceiver::DIG_U: return "DATA-U";
case Transceiver::FM:
case Transceiver::DIG_FM:
return "FM";
default: break;
}
return "USB";
}
}
void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
{
(*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true};
}
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent)
: PollingTransceiver {poll_interval, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, server_ {address}
, commander_ {nullptr}
{
}
int DXLabSuiteCommanderTransceiver::do_start ()
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol);
if (!commander_)
{
commander_ = new QTcpSocket {this}; // QObject takes ownership
}
commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!commander_->waitForConnected ())
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to connect" << commander_->errorString ());
throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
}
// sleeps here are to ensure Commander has actually queried the rig
// rather than returning cached data which maybe stale or simply
// read backs of not yet committed values, the 2s delays are
// arbitrary but hopefully enough as the actual time required is rig
// and Commander setting dependent
int resolution {0};
QThread::msleep (2000);
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f && !(f % 10))
{
auto test_frequency = f - f % 100 + 55;
auto f_string = frequency_to_string (test_frequency);
auto params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
QThread::msleep (2000);
reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto new_frequency = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
switch (static_cast<Radio::FrequencyDelta> (new_frequency - test_frequency))
{
case -5: resolution = -1; break; // 10Hz truncated
case 5: resolution = 1; break; // 10Hz rounded
case -15: resolution = -2; break; // 20Hz truncated
case -55: resolution = -2; break; // 100Hz truncated
case 45: resolution = 2; break; // 100Hz rounded
}
if (1 == resolution) // may be 20Hz rounded
{
test_frequency = f - f % 100 + 51;
f_string = frequency_to_string (test_frequency);
params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
QThread::msleep (2000);
reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
new_frequency = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (9 == static_cast<Radio::FrequencyDelta> (new_frequency - test_frequency))
{
resolution = 2; // 20Hz rounded
}
}
f_string = frequency_to_string (f);
params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
}
}
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
}
do_poll ();
return resolution;
}
void DXLabSuiteCommanderTransceiver::do_stop ()
{
if (commander_)
{
commander_->close ();
delete commander_, commander_ = nullptr;
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped");
}
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ());
if (use_for_ptt_)
{
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
bool tx {!on};
auto start = QDateTime::currentMSecsSinceEpoch ();
// we must now wait for Tx on the rig, we will wait a short while
// before bailing out
while (tx != on && QDateTime::currentMSecsSinceEpoch () - start < 1000)
{
auto reply = command_with_reply ("<command:9>CmdSendTx<parameters:0>");
if (0 == reply.indexOf ("<CmdTX:"))
{
auto state = reply.mid (reply.indexOf ('>') + 1);
if ("ON" == state)
{
tx = true;
}
else if ("OFF" == state)
{
tx = false;
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "unexpected TX state" << state);
throw error {tr ("DX Lab Suite Commander sent an unrecognised TX state: ") + state};
}
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get TX unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX status: ") + reply};
}
if (tx != on) QThread::msleep (10); // don't thrash Commander
}
update_PTT (tx);
if (tx != on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "rig failed to respond to PTT: " << on);
throw error {tr ("DX Lab Suite Commander rig did not respond to PTT: ") + (on ? "ON" : "OFF")};
}
}
else
{
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
update_PTT (on);
}
}
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", f << state ());
auto f_string = frequency_to_string (f);
if (UNK != m && m != get_mode ())
{
auto m_string = map_mode (m);
auto params = ("<xcvrfreq:%1>" + f_string + "<xcvrmode:%2>" + m_string + "<preservesplitanddual:1>Y").arg (f_string.size ()).arg (m_string.size ());
simple_command (("<command:14>CmdSetFreqMode<parameters:%1>" + params).arg (params.size ()));
update_mode (m);
}
else
{
auto params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
}
update_rx_frequency (f);
}
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", tx << state ());
if (tx)
{
auto f_string = frequency_to_string (tx);
auto params = ("<xcvrfreq:%1>" + f_string + "<SuppressDual:1>Y").arg (f_string.size ());
if (UNK == mode)
{
params += "<SuppressModeChange:1>Y";
}
simple_command (("<command:11>CmdQSXSplit<parameters:%1>" + params).arg (params.size ()));
}
else
{
simple_command ("<command:8>CmdSplit<parameters:8><1:3>off");
}
update_split (tx);
update_other_frequency (tx);
}
void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", m << state ());
auto m_string = map_mode (m);
auto params = ("<1:%1>" + m_string).arg (m_string.size ());
simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ()));
update_mode (m);
}
void DXLabSuiteCommanderTransceiver::do_poll ()
{
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
bool quiet {false};
#else
bool quiet {true};
#endif
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f)
{
if (!state ().ptt ()) // Commander is not reliable on frequency
// polls while transmitting
{
update_rx_frequency (f);
}
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency: ") + reply};
}
if (state ().split ())
{
reply = command_with_reply ("<command:12>CmdGetTXFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdTXFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f)
{
if (!state ().ptt ()) // Commander is not reliable on frequency
// polls while transmitting
{
update_other_frequency (f);
}
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get tx frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency: ") + reply};
}
}
reply = command_with_reply ("<command:12>CmdSendSplit<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdSplit:"))
{
auto split = reply.mid (reply.indexOf ('>') + 1);
if ("ON" == split)
{
update_split (true);
}
else if ("OFF" == split)
{
update_split (false);
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected split state" << split);
throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get split mode unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status: ") + reply};
}
get_mode (quiet);
}
auto DXLabSuiteCommanderTransceiver::get_mode (bool no_debug) -> MODE
{
MODE m {UNK};
auto reply = command_with_reply ("<command:11>CmdSendMode<parameters:0>", no_debug);
if (0 == reply.indexOf ("<CmdMode:"))
{
auto mode = reply.mid (reply.indexOf ('>') + 1);
if ("AM" == mode)
{
m = AM;
}
else if ("CW" == mode)
{
m = CW;
}
else if ("CW-R" == mode)
{
m = CW_R;
}
else if ("FM" == mode || "WBFM" == mode)
{
m = FM;
}
else if ("LSB" == mode)
{
m = LSB;
}
else if ("USB" == mode)
{
m = USB;
}
else if ("RTTY" == mode)
{
m = FSK;
}
else if ("RTTY-R" == mode)
{
m = FSK_R;
}
else if ("PKT" == mode || "DATA-L" == mode || "Data-L" == mode || "DIGL" == mode)
{
m = DIG_L;
}
else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode || "DIGU" == mode)
{
m = DIG_U;
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected mode name" << mode);
throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'};
}
update_mode (m);
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode: ") + reply};
}
return m;
}
void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd);
}
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed:" << commander_->errorString ());
throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()};
}
}
QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
.arg (cmd)
.arg (commander_->errorString ())
};
}
// waitForReadReady appears to be unreliable on Windows timing out
// when data is waiting so retry a few times
unsigned retries {5};
bool replied {false};
while (!replied && --retries)
{
replied = commander_->waitForReadyRead ();
if (!replied && commander_->error () != commander_->SocketTimeoutError)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "failed to read reply:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd)
.arg (commander_->errorString ())
};
}
}
if (!replied)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd)
};
}
auto result = commander_->readAll ();
// qDebug () << "result: " << result;
// for (int i = 0; i < result.size (); ++i)
// {
// qDebug () << i << ":" << hex << int (result[i]);
// }
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
}
return result; // converting raw UTF-8 bytes to QString
}
bool DXLabSuiteCommanderTransceiver::write_to_port (QString const& s)
{
auto data = s.toLocal8Bit ();
auto to_send = data.constData ();
auto length = data.size ();
qint64 total_bytes_sent {0};
while (total_bytes_sent < length)
{
auto bytes_sent = commander_->write (to_send + total_bytes_sent, length - total_bytes_sent);
if (bytes_sent < 0 || !commander_->waitForBytesWritten ())
{
return false;
}
total_bytes_sent += bytes_sent;
}
return true;
}
QString DXLabSuiteCommanderTransceiver::frequency_to_string (Frequency f) const
{
// number is localized and in kHz, avoid floating point translation
// errors by adding a small number (0.1Hz)
return QString {"%L1"}.arg (f / 1e3 + 1e-4, 10, 'f', 3);
}
auto DXLabSuiteCommanderTransceiver::string_to_frequency (QString s) const -> Frequency
{
// temporary hack because Commander is returning invalid UTF-8 bytes
s.replace (QChar {QChar::ReplacementCharacter}, locale_.groupSeparator ());
// remove DP - relies on n.nnn kHz format so we can do ulonglong
// conversion to Hz
bool ok;
// auto f = locale_.toDouble (s, &ok); // use when CmdSendFreq and
// CmdSendTxFreq reinstated
auto f = QLocale::c ().toDouble (s, &ok); // temporary fix
if (!ok)
{
throw error {tr ("DX Lab Suite Commander sent an unrecognized frequency")};
}
return (f + 1e-4) * 1e3;
}

85
Decoder/decodedtext.h Normal file
View File

@ -0,0 +1,85 @@
// -*- Mode: C++ -*-
/*
* Class to handle the formatted string as returned from the fortran decoder
*
* VK3ACF August 2013
*/
#ifndef DECODEDTEXT_H
#define DECODEDTEXT_H
#include <QString>
/*
012345678901234567890123456789012345678901
^ ^ ^ ^ ^ ^
2343 -11 0.8 1259 # CQ VP2X/GM4WJS GL33
2343 -11 0.8 1259 # CQ 999 VP2V/GM4WJS
2343 -11 0.8 1259 # YV6BFE F6GUU R-08
2343 -19 0.3 718 # VE6WQ SQ2NIJ -14
2343 -7 0.3 815 # KK4DSD W7VP -16
2343 -13 0.1 3627 @ CT1FBK IK5YZT R+02
0605 Tx 1259 # CQ VK3ACF QF22
*/
class DecodedText
{
public:
explicit DecodedText (QString const& message);
QString string() const { return string_; };
QStringList messageWords () const;
int indexOf(QString s) const { return string_.indexOf(s); };
int indexOf(QString s, int i) const { return string_.indexOf(s,i); };
QString mid(int f, int t) const { return string_.mid(f,t); };
QString left(int i) const { return string_.left(i); };
void clear() { string_.clear(); };
QString CQersCall() const;
bool isJT65() const;
bool isJT9() const;
bool isTX() const;
bool isStandardMessage () const {return is_standard_;}
bool isLowConfidence () const;
int frequencyOffset() const; // hertz offset from the tuned dial or rx frequency, aka audio frequency
int snr() const;
float dt() const;
// find and extract any report. Returns true if this is a standard message
bool report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report) const;
// get the first message text word, usually the call
QString call() const;
// get the second word, most likely the de call and the third word, most likely grid
void deCallAndGrid(/*out*/QString& call, QString& grid) const;
unsigned timeInSeconds() const;
// returns a string of the SNR field with a leading + or - followed by two digits
QString report() const;
private:
// These define the columns in the decoded text where fields are to be found.
// We rely on these columns being the same in the fortran code (lib/decoder.f90) that formats the decoded text
enum Columns {column_time = 0,
column_snr = 5,
column_dt = 9,
column_freq = 14,
column_mode = 19,
column_qsoText = 22 };
QString string_;
int padding_;
QString message_;
QString message0_;
bool is_standard_;
};
#endif // DECODEDTEXT_H

View File

@ -1,125 +0,0 @@
#include "Detector.hpp"
#include <QDateTime>
#include <QtAlgorithms>
#include <QDebug>
#include <math.h>
#include "commons.h"
#include "moc_Detector.cpp"
extern "C" {
void fil4_(qint16*, qint32*, qint16*, qint32*);
}
Detector::Detector (unsigned frameRate, double periodLengthInSeconds,
unsigned downSampleFactor, QObject * parent)
: AudioDevice (parent)
, m_frameRate (frameRate)
, m_period (periodLengthInSeconds)
, m_downSampleFactor (downSampleFactor)
, m_samplesPerFFT {max_buffer_size}
, m_ns (999)
, m_buffer ((downSampleFactor > 1) ?
new short [max_buffer_size * downSampleFactor] : nullptr)
, m_bufferPos (0)
{
(void)m_frameRate; // quell compiler warning
clear ();
}
void Detector::setBlockSize (unsigned n)
{
m_samplesPerFFT = n;
}
bool Detector::reset ()
{
clear ();
// don't call base call reset because it calls seek(0) which causes
// a warning
return isOpen ();
}
void Detector::clear ()
{
// set index to roughly where we are in time (1ms resolution)
// qint64 now (QDateTime::currentMSecsSinceEpoch ());
// unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
// dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
dec_data.params.kin = 0;
m_bufferPos = 0;
// fill buffer with zeros (G4WJS commented out because it might cause decoder hangs)
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
}
qint64 Detector::writeData (char const * data, qint64 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
if(mstr < mstr0) { //When mstr has wrapped around to 0, restart the buffer
dec_data.params.kin = 0;
m_bufferPos = 0;
}
mstr0=mstr;
// no torn frames
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ())));
// these are in terms of input frames (not down sampled)
size_t framesAcceptable ((sizeof (dec_data.d2) /
sizeof (dec_data.d2[0]) - dec_data.params.kin) * m_downSampleFactor);
size_t framesAccepted (qMin (static_cast<size_t> (maxSize /
bytesPerFrame ()), framesAcceptable));
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) {
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
<< " frames of data on the floor!"
<< dec_data.params.kin << mstr;
}
for (unsigned remaining = framesAccepted; remaining; ) {
size_t numFramesProcessed (qMin (m_samplesPerFFT *
m_downSampleFactor - m_bufferPos, remaining));
if(m_downSampleFactor > 1) {
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
numFramesProcessed, &m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if(m_bufferPos==m_samplesPerFFT*m_downSampleFactor) {
qint32 framesToProcess (m_samplesPerFFT * m_downSampleFactor);
qint32 framesAfterDownSample (m_samplesPerFFT);
if(m_downSampleFactor > 1 && dec_data.params.kin>=0 &&
dec_data.params.kin < (NTMAX*12000 - framesAfterDownSample)) {
fil4_(&m_buffer[0], &framesToProcess, &dec_data.d2[dec_data.params.kin],
&framesAfterDownSample);
dec_data.params.kin += framesAfterDownSample;
} else {
// qDebug() << "framesToProcess = " << framesToProcess;
// qDebug() << "dec_data.params.kin = " << dec_data.params.kin;
// qDebug() << "secondInPeriod = " << secondInPeriod();
// qDebug() << "framesAfterDownSample" << framesAfterDownSample;
}
Q_EMIT framesWritten (dec_data.params.kin);
m_bufferPos = 0;
}
} else {
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
numFramesProcessed, &dec_data.d2[dec_data.params.kin]);
m_bufferPos += numFramesProcessed;
dec_data.params.kin += numFramesProcessed;
if (m_bufferPos == static_cast<unsigned> (m_samplesPerFFT)) {
Q_EMIT framesWritten (dec_data.params.kin);
m_bufferPos = 0;
}
}
remaining -= numFramesProcessed;
}
return maxSize; // we drop any data past the end of the buffer on
// the floor until the next period starts
}

View File

@ -1,59 +0,0 @@
#ifndef DETECTOR_HPP__
#define DETECTOR_HPP__
#include "AudioDevice.hpp"
#include <QScopedArrayPointer>
//
// output device that distributes data in predefined chunks via a signal
//
// the underlying device for this abstraction is just the buffer that
// stores samples throughout a receiving period
//
class Detector : public AudioDevice
{
Q_OBJECT;
public:
//
// if the data buffer were not global storage and fixed size then we
// might want maximum size passed as constructor arguments
//
// we down sample by a factor of 4
//
// the samplesPerFFT argument is the number after down sampling
//
Detector (unsigned frameRate, double periodLengthInSeconds, unsigned downSampleFactor = 4u,
QObject * parent = 0);
void setTRPeriod(double p) {m_period=p;}
bool reset () override;
Q_SIGNAL void framesWritten (qint64) const;
Q_SLOT void setBlockSize (unsigned);
protected:
qint64 readData (char * /* data */, qint64 /* maxSize */) override
{
return -1; // we don't produce data
}
qint64 writeData (char const * data, qint64 maxSize) override;
private:
void clear (); // discard buffer contents
unsigned m_frameRate;
double m_period;
unsigned m_downSampleFactor;
qint32 m_samplesPerFFT; // after any down sampling
qint32 m_ns;
static size_t const max_buffer_size {7 * 512};
QScopedArrayPointer<short> m_buffer; // de-interleaved sample buffer
// big enough for all the
// samples for one increment of
// data (a signals worth) at
// the input sample rate
unsigned m_bufferPos;
};
#endif

124
Detector/Detector.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "Detector.hpp"
#include <QDateTime>
#include <QtAlgorithms>
#include <QDebug>
#include <math.h>
#include "commons.h"
#include "moc_Detector.cpp"
extern "C" {
void fil4_(qint16*, qint32*, qint16*, qint32*);
}
Detector::Detector (unsigned frameRate, double periodLengthInSeconds,
unsigned downSampleFactor, QObject * parent)
: AudioDevice (parent)
, m_frameRate (frameRate)
, m_period (periodLengthInSeconds)
, m_downSampleFactor (downSampleFactor)
, m_samplesPerFFT {max_buffer_size}
, m_buffer ((downSampleFactor > 1) ?
new short [max_buffer_size * downSampleFactor] : nullptr)
, m_bufferPos (0)
{
(void)m_frameRate; // quell compiler warning
clear ();
}
void Detector::setBlockSize (unsigned n)
{
m_samplesPerFFT = n;
}
bool Detector::reset ()
{
clear ();
// don't call base call reset because it calls seek(0) which causes
// a warning
return isOpen ();
}
void Detector::clear ()
{
// set index to roughly where we are in time (1ms resolution)
// qint64 now (QDateTime::currentMSecsSinceEpoch ());
// unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000));
// dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast<unsigned> (sizeof (dec_data.d2) / sizeof (dec_data.d2[0])));
dec_data.params.kin = 0;
m_bufferPos = 0;
// fill buffer with zeros (G4WJS commented out because it might cause decoder hangs)
// qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0);
}
qint64 Detector::writeData (char const * data, qint64 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
if(mstr < mstr0) { //When mstr has wrapped around to 0, restart the buffer
dec_data.params.kin = 0;
m_bufferPos = 0;
}
mstr0=mstr;
// no torn frames
Q_ASSERT (!(maxSize % static_cast<qint64> (bytesPerFrame ())));
// these are in terms of input frames (not down sampled)
size_t framesAcceptable ((sizeof (dec_data.d2) /
sizeof (dec_data.d2[0]) - dec_data.params.kin) * m_downSampleFactor);
size_t framesAccepted (qMin (static_cast<size_t> (maxSize /
bytesPerFrame ()), framesAcceptable));
if (framesAccepted < static_cast<size_t> (maxSize / bytesPerFrame ())) {
qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted
<< " frames of data on the floor!"
<< dec_data.params.kin << mstr;
}
for (unsigned remaining = framesAccepted; remaining; ) {
size_t numFramesProcessed (qMin (m_samplesPerFFT *
m_downSampleFactor - m_bufferPos, remaining));
if(m_downSampleFactor > 1) {
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
numFramesProcessed, &m_buffer[m_bufferPos]);
m_bufferPos += numFramesProcessed;
if(m_bufferPos==m_samplesPerFFT*m_downSampleFactor) {
qint32 framesToProcess (m_samplesPerFFT * m_downSampleFactor);
qint32 framesAfterDownSample (m_samplesPerFFT);
if(m_downSampleFactor > 1 && dec_data.params.kin>=0 &&
dec_data.params.kin < (NTMAX*12000 - framesAfterDownSample)) {
fil4_(&m_buffer[0], &framesToProcess, &dec_data.d2[dec_data.params.kin],
&framesAfterDownSample);
dec_data.params.kin += framesAfterDownSample;
} else {
// qDebug() << "framesToProcess = " << framesToProcess;
// qDebug() << "dec_data.params.kin = " << dec_data.params.kin;
// qDebug() << "secondInPeriod = " << secondInPeriod();
// qDebug() << "framesAfterDownSample" << framesAfterDownSample;
}
Q_EMIT framesWritten (dec_data.params.kin);
m_bufferPos = 0;
}
} else {
store (&data[(framesAccepted - remaining) * bytesPerFrame ()],
numFramesProcessed, &dec_data.d2[dec_data.params.kin]);
m_bufferPos += numFramesProcessed;
dec_data.params.kin += numFramesProcessed;
if (m_bufferPos == static_cast<unsigned> (m_samplesPerFFT)) {
Q_EMIT framesWritten (dec_data.params.kin);
m_bufferPos = 0;
}
}
remaining -= numFramesProcessed;
}
return maxSize; // we drop any data past the end of the buffer on
// the floor until the next period starts
}

58
Detector/Detector.hpp Normal file
View File

@ -0,0 +1,58 @@
#ifndef DETECTOR_HPP__
#define DETECTOR_HPP__
#include "Audio/AudioDevice.hpp"
#include <QScopedArrayPointer>
//
// output device that distributes data in predefined chunks via a signal
//
// the underlying device for this abstraction is just the buffer that
// stores samples throughout a receiving period
//
class Detector : public AudioDevice
{
Q_OBJECT;
public:
//
// if the data buffer were not global storage and fixed size then we
// might want maximum size passed as constructor arguments
//
// we down sample by a factor of 4
//
// the samplesPerFFT argument is the number after down sampling
//
Detector (unsigned frameRate, double periodLengthInSeconds, unsigned downSampleFactor = 4u,
QObject * parent = 0);
void setTRPeriod(double p) {m_period=p;}
bool reset () override;
Q_SIGNAL void framesWritten (qint64) const;
Q_SLOT void setBlockSize (unsigned);
protected:
qint64 readData (char * /* data */, qint64 /* maxSize */) override
{
return -1; // we don't produce data
}
qint64 writeData (char const * data, qint64 maxSize) override;
private:
void clear (); // discard buffer contents
unsigned m_frameRate;
double m_period;
unsigned m_downSampleFactor;
qint32 m_samplesPerFFT; // after any down sampling
static size_t const max_buffer_size {7 * 512};
QScopedArrayPointer<short> m_buffer; // de-interleaved sample buffer
// big enough for all the
// samples for one increment of
// data (a signals worth) at
// the input sample rate
unsigned m_bufferPos;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,591 +0,0 @@
#include "MessageServer.hpp"
#include <stdexcept>
#include <limits>
#include <QNetworkInterface>
#include <QUdpSocket>
#include <QString>
#include <QTimer>
#include <QHash>
#include "Radio.hpp"
#include "NetworkMessage.hpp"
#include "qt_helpers.hpp"
#include "pimpl_impl.hpp"
#include "moc_MessageServer.cpp"
namespace
{
auto quint32_max = std::numeric_limits<quint32>::max ();
}
class MessageServer::impl
: public QUdpSocket
{
Q_OBJECT;
public:
impl (MessageServer * self, QString const& version, QString const& revision)
: self_ {self}
, version_ {version}
, revision_ {revision}
, port_ {0u}
, clock_ {new QTimer {this}}
{
// register the required types with Qt
Radio::register_types ();
connect (this, &QIODevice::readyRead, this, &MessageServer::impl::pending_datagrams);
connect (this, static_cast<void (impl::*) (SocketError)> (&impl::error)
, [this] (SocketError /* e */)
{
Q_EMIT self_->error (errorString ());
});
connect (clock_, &QTimer::timeout, this, &impl::tick);
clock_->start (NetworkMessage::pulse * 1000);
}
enum StreamStatus {Fail, Short, OK};
void leave_multicast_group ();
void join_multicast_group ();
void parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg);
void tick ();
void pending_datagrams ();
StreamStatus check_status (QDataStream const&) const;
void send_message (QDataStream const& out, QByteArray const& message, QHostAddress const& address, port_type port)
{
if (OK == check_status (out))
{
writeDatagram (message, address, port);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
}
MessageServer * self_;
QString version_;
QString revision_;
port_type port_;
QHostAddress multicast_group_address_;
static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint;
struct Client
{
Client () = default;
Client (QHostAddress const& sender_address, port_type const& sender_port)
: sender_address_ {sender_address}
, sender_port_ {sender_port}
, negotiated_schema_number_ {2} // not 1 because it's broken
, last_activity_ {QDateTime::currentDateTime ()}
{
}
Client (Client const&) = default;
Client& operator= (Client const&) = default;
QHostAddress sender_address_;
port_type sender_port_;
quint32 negotiated_schema_number_;
QDateTime last_activity_;
};
QHash<QString, Client> clients_; // maps id to Client
QTimer * clock_;
};
MessageServer::impl::BindMode constexpr MessageServer::impl::bind_mode_;
#include "MessageServer.moc"
void MessageServer::impl::leave_multicast_group ()
{
if (!multicast_group_address_.isNull () && BoundState == state ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
{
for (auto const& interface : QNetworkInterface::allInterfaces ())
{
if (QNetworkInterface::CanMulticast & interface.flags ())
{
leaveMulticastGroup (multicast_group_address_, interface);
}
}
}
}
void MessageServer::impl::join_multicast_group ()
{
if (BoundState == state ()
&& !multicast_group_address_.isNull ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
{
auto mcast_iface = multicastInterface ();
if (IPv4Protocol == multicast_group_address_.protocol ()
&& IPv4Protocol != localAddress ().protocol ())
{
close ();
bind (QHostAddress::AnyIPv4, port_, bind_mode_);
}
bool joined {false};
for (auto const& interface : QNetworkInterface::allInterfaces ())
{
if (QNetworkInterface::CanMulticast & interface.flags ())
{
// Windows requires outgoing interface to match
// interface to be joined while joining, at least for
// IPv4 it seems to
setMulticastInterface (interface);
joined |= joinMulticastGroup (multicast_group_address_, interface);
}
}
if (!joined)
{
multicast_group_address_.clear ();
}
setMulticastInterface (mcast_iface);
}
}
void MessageServer::impl::pending_datagrams ()
{
while (hasPendingDatagrams ())
{
QByteArray datagram;
datagram.resize (pendingDatagramSize ());
QHostAddress sender_address;
port_type sender_port;
if (0 <= readDatagram (datagram.data (), datagram.size (), &sender_address, &sender_port))
{
parse_message (sender_address, sender_port, datagram);
}
}
}
void MessageServer::impl::parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg)
{
try
{
//
// message format is described in NetworkMessage.hpp
//
NetworkMessage::Reader in {msg};
auto id = in.id ();
if (OK == check_status (in))
{
if (!clients_.contains (id))
{
auto& client = (clients_[id] = {sender, sender_port});
QByteArray client_version;
QByteArray client_revision;
if (NetworkMessage::Heartbeat == in.type ())
{
// negotiate a working schema number
in >> client.negotiated_schema_number_;
if (OK == check_status (in))
{
auto sn = NetworkMessage::Builder::schema_number;
client.negotiated_schema_number_ = std::min (sn, client.negotiated_schema_number_);
// reply to the new client informing it of the
// negotiated schema number
QByteArray message;
NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id, client.negotiated_schema_number_};
hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
<< version_.toUtf8 () << revision_.toUtf8 ();
if (impl::OK == check_status (hb))
{
writeDatagram (message, client.sender_address_, client.sender_port_);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
}
// we don't care if this fails to read
in >> client_version >> client_revision;
}
Q_EMIT self_->client_opened (id, QString::fromUtf8 (client_version),
QString::fromUtf8 (client_revision));
}
clients_[id].last_activity_ = QDateTime::currentDateTime ();
//
// message format is described in NetworkMessage.hpp
//
switch (in.type ())
{
case NetworkMessage::Heartbeat:
//nothing to do here as time out handling deals with lifetime
break;
case NetworkMessage::Clear:
Q_EMIT self_->decodes_cleared (id);
break;
case NetworkMessage::Status:
{
// unpack message
Frequency f;
QByteArray mode;
QByteArray dx_call;
QByteArray report;
QByteArray tx_mode;
bool tx_enabled {false};
bool transmitting {false};
bool decoding {false};
quint32 rx_df {quint32_max};
quint32 tx_df {quint32_max};
QByteArray de_call;
QByteArray de_grid;
QByteArray dx_grid;
bool watchdog_timeout {false};
QByteArray sub_mode;
bool fast_mode {false};
quint8 special_op_mode {0};
quint32 frequency_tolerance {quint32_max};
quint32 tr_period {quint32_max};
QByteArray configuration_name;
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
>> fast_mode >> special_op_mode >> frequency_tolerance >> tr_period >> configuration_name;
if (check_status (in) != Fail)
{
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
, QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
, tx_enabled, transmitting, decoding, rx_df, tx_df
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
, QString::fromUtf8 (dx_grid), watchdog_timeout
, QString::fromUtf8 (sub_mode), fast_mode
, special_op_mode, frequency_tolerance, tr_period
, QString::fromUtf8 (configuration_name));
}
}
break;
case NetworkMessage::Decode:
{
// unpack message
bool is_new {true};
QTime time;
qint32 snr;
float delta_time;
quint32 delta_frequency;
QByteArray mode;
QByteArray message;
bool low_confidence {false};
bool off_air {false};
in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode
>> message >> low_confidence >> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->decode (is_new, id, time, snr, delta_time, delta_frequency
, QString::fromUtf8 (mode), QString::fromUtf8 (message)
, low_confidence, off_air);
}
}
break;
case NetworkMessage::WSPRDecode:
{
// unpack message
bool is_new {true};
QTime time;
qint32 snr;
float delta_time;
Frequency frequency;
qint32 drift;
QByteArray callsign;
QByteArray grid;
qint32 power;
bool off_air {false};
in >> is_new >> time >> snr >> delta_time >> frequency >> drift >> callsign >> grid >> power
>> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->WSPR_decode (is_new, id, time, snr, delta_time, frequency, drift
, QString::fromUtf8 (callsign), QString::fromUtf8 (grid)
, power, off_air);
}
}
break;
case NetworkMessage::QSOLogged:
{
QDateTime time_off;
QByteArray dx_call;
QByteArray dx_grid;
Frequency dial_frequency;
QByteArray mode;
QByteArray report_sent;
QByteArray report_received;
QByteArray tx_power;
QByteArray comments;
QByteArray name;
QDateTime time_on; // Note: LOTW uses TIME_ON for their +/- 30-minute time window
QByteArray operator_call;
QByteArray my_call;
QByteArray my_grid;
QByteArray exchange_sent;
QByteArray exchange_rcvd;
in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received
>> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid
>> exchange_sent >> exchange_rcvd;
if (check_status (in) != Fail)
{
Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
, dial_frequency, QString::fromUtf8 (mode), QString::fromUtf8 (report_sent)
, QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power)
, QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
, QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
, QString::fromUtf8 (my_grid), QString::fromUtf8 (exchange_sent)
, QString::fromUtf8 (exchange_rcvd));
}
}
break;
case NetworkMessage::Close:
Q_EMIT self_->client_closed (id);
clients_.remove (id);
break;
case NetworkMessage::LoggedADIF:
{
QByteArray ADIF;
in >> ADIF;
if (check_status (in) != Fail)
{
Q_EMIT self_->logged_ADIF (id, ADIF);
}
}
break;
default:
// Ignore
break;
}
}
else
{
Q_EMIT self_->error ("MessageServer warning: invalid UDP message received");
}
}
catch (std::exception const& e)
{
Q_EMIT self_->error (QString {"MessageServer exception: %1"}.arg (e.what ()));
}
catch (...)
{
Q_EMIT self_->error ("Unexpected exception in MessageServer");
}
}
void MessageServer::impl::tick ()
{
auto now = QDateTime::currentDateTime ();
auto iter = std::begin (clients_);
while (iter != std::end (clients_))
{
if (now > (*iter).last_activity_.addSecs (NetworkMessage::pulse))
{
Q_EMIT self_->clear_decodes (iter.key ());
Q_EMIT self_->client_closed (iter.key ());
iter = clients_.erase (iter); // safe while iterating as doesn't rehash
}
else
{
++iter;
}
}
}
auto MessageServer::impl::check_status (QDataStream const& stream) const -> StreamStatus
{
auto stat = stream.status ();
StreamStatus result {Fail};
switch (stat)
{
case QDataStream::ReadPastEnd:
result = Short;
break;
case QDataStream::ReadCorruptData:
Q_EMIT self_->error ("Message serialization error: read corrupt data");
break;
case QDataStream::WriteFailed:
Q_EMIT self_->error ("Message serialization error: write error");
break;
default:
result = OK;
break;
}
return result;
}
MessageServer::MessageServer (QObject * parent, QString const& version, QString const& revision)
: QObject {parent}
, m_ {this, version, revision}
{
}
void MessageServer::start (port_type port, QHostAddress const& multicast_group_address)
{
if (port != m_->port_
|| multicast_group_address != m_->multicast_group_address_)
{
m_->leave_multicast_group ();
if (impl::BoundState == m_->state ())
{
m_->close ();
}
m_->multicast_group_address_ = multicast_group_address;
auto address = m_->multicast_group_address_.isNull ()
|| impl::IPv4Protocol != m_->multicast_group_address_.protocol () ? QHostAddress::Any : QHostAddress::AnyIPv4;
if (port && m_->bind (address, port, m_->bind_mode_))
{
m_->port_ = port;
m_->join_multicast_group ();
}
else
{
m_->port_ = 0;
}
}
}
void MessageServer::clear_decodes (QString const& id, quint8 window)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Clear, id, (*iter).negotiated_schema_number_};
out << window;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode
, QString const& message_text, bool low_confidence, quint8 modifiers)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Reply, id, (*iter).negotiated_schema_number_};
out << time << snr << delta_time << delta_frequency << mode.toUtf8 ()
<< message_text.toUtf8 () << low_confidence << modifiers;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::replay (QString const& id)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Replay, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::close (QString const& id)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Close, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::halt_tx (QString const& id, bool auto_only)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, id, (*iter).negotiated_schema_number_};
out << auto_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::free_text (QString const& id, QString const& text, bool send)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, id, (*iter).negotiated_schema_number_};
out << text.toUtf8 () << send;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::location (QString const& id, QString const& loc)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Location, id, (*iter).negotiated_schema_number_};
out << loc.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::highlight_callsign (QString const& id, QString const& callsign
, QColor const& bg, QColor const& fg, bool last_only)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, id, (*iter).negotiated_schema_number_};
out << callsign.toUtf8 () << bg << fg << last_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::switch_configuration (QString const& id, QString const& configuration_name)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, id, (*iter).negotiated_schema_number_};
out << configuration_name.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::configure (QString const& id, QString const& mode, quint32 frequency_tolerance
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
, QString const& dx_call, QString const& dx_grid, bool generate_messages)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Configure, id, (*iter).negotiated_schema_number_};
out << mode.toUtf8 () << frequency_tolerance << submode.toUtf8 () << fast_mode << tr_period << rx_df
<< dx_call.toUtf8 () << dx_grid.toUtf8 () << generate_messages;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}

View File

@ -6,11 +6,11 @@
#include "Radio.hpp"
#include "models/FrequencyList.hpp"
#include "AudioDevice.hpp"
#include "Audio/AudioDevice.hpp"
#include "Configuration.hpp"
#include "models/StationList.hpp"
#include "Transceiver.hpp"
#include "TransceiverFactory.hpp"
#include "Transceiver/Transceiver.hpp"
#include "Transceiver/TransceiverFactory.hpp"
#include "WFPalette.hpp"
#include "models/IARURegions.hpp"
#include "models/DecodeHighlightingModel.hpp"

View File

@ -1,350 +0,0 @@
#include "Modulator.hpp"
#include <limits>
#include <qmath.h>
#include <QDateTime>
#include <QDebug>
#include "widgets/mainwindow.h" // TODO: G4WJS - break this dependency
#include "soundout.h"
#include "commons.h"
#include "moc_Modulator.cpp"
extern float gran(); // Noise generator (for tests only)
#define RAMP_INCREMENT 64 // MUST be an integral factor of 2^16
#if defined (WSJT_SOFT_KEYING)
# define SOFT_KEYING WSJT_SOFT_KEYING
#else
# define SOFT_KEYING 1
#endif
double constexpr Modulator::m_twoPi;
// float wpm=20.0;
// unsigned m_nspd=1.2*48000.0/wpm;
// m_nspd=3072; //18.75 WPM
Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds,
QObject * parent)
: AudioDevice {parent}
, m_quickClose {false}
, m_phi {0.0}
, m_toneSpacing {0.0}
, m_fSpread {0.0}
, m_period {periodLengthInSeconds}
, m_frameRate {frameRate}
, m_state {Idle}
, m_tuning {false}
, m_cwLevel {false}
, m_j0 {-1}
, m_toneFrequency0 {1500.0}
{
}
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
double frequency, double toneSpacing,
SoundOutput * stream, Channel channel,
bool synchronize, bool fastMode, double dBSNR, double TRperiod)
{
Q_ASSERT (stream);
// Time according to this computer which becomes our base time
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
if(m_state != Idle) stop();
m_quickClose = false;
m_symbolsLength = symbolsLength;
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
m_frequency0 = 0.;
m_phi = 0.;
m_addNoise = dBSNR < 0.;
m_nsps = framesPerSymbol;
m_frequency = frequency;
m_amp = std::numeric_limits<qint16>::max ();
m_toneSpacing = toneSpacing;
m_bFastMode=fastMode;
m_TRperiod=TRperiod;
unsigned delay_ms=1000;
if(m_nsps==1920) delay_ms=500; //FT8
if(m_nsps==576) delay_ms=300; //FT4
// noise generator parameters
if (m_addNoise) {
m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
m_fac = 3000.0;
if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
}
// round up to an exact portion of a second that allows for startup delays
m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
if(m_bFastMode) m_ic=0;
m_silentFrames = 0;
// calculate number of silent frames to send, so that audio will start at
// the nominal time "delay_ms" into the Tx sequence.
if (synchronize && !m_tuning && !m_bFastMode) {
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
}
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
// << m_ic << m_silentFrames << m_silentFrames/48000.0
// << mstr << fmod(double(ms0),1000.0*m_period);
initialize (QIODevice::ReadOnly, channel);
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
Synchronizing : Active));
m_stream = stream;
if (m_stream) m_stream->restart (this);
}
void Modulator::tune (bool newState)
{
m_tuning = newState;
if (!m_tuning) stop (true);
}
void Modulator::stop (bool quick)
{
m_quickClose = quick;
close ();
}
void Modulator::close ()
{
if (m_stream)
{
if (m_quickClose)
{
m_stream->reset ();
}
else
{
m_stream->stop ();
}
}
if (m_state != Idle)
{
Q_EMIT stateChanged ((m_state = Idle));
}
AudioDevice::close ();
}
qint64 Modulator::readData (char * data, qint64 maxSize)
{
double toneFrequency=1500.0;
if(m_nsps==6) {
toneFrequency=1000.0;
m_frequency=1000.0;
m_frequency0=1000.0;
}
if(maxSize==0) return 0;
Q_ASSERT (!(maxSize % qint64 (bytesPerFrame ()))); // no torn frames
Q_ASSERT (isOpen ());
qint64 numFrames (maxSize / bytesPerFrame ());
qint16 * samples (reinterpret_cast<qint16 *> (data));
qint16 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint16)));
qint64 framesGenerated (0);
// if(m_ic==0) qDebug() << "Modulator::readData" << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod));
switch (m_state)
{
case Synchronizing:
{
if (m_silentFrames) { // send silence up to first second
framesGenerated = qMin (m_silentFrames, numFrames);
for ( ; samples != end; samples = load (0, samples)) { // silence
}
m_silentFrames -= framesGenerated;
return framesGenerated * bytesPerFrame ();
}
Q_EMIT stateChanged ((m_state = Active));
m_cwLevel = false;
m_ramp = 0; // prepare for CW wave shaping
}
// fall through
case Active:
{
unsigned int isym=0;
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
if(m_TRperiod==3.0) slowCwId=false;
bool fastCwId=false;
static bool bCwId=false;
qint64 ms = QDateTime::currentMSecsSinceEpoch();
float tsec=0.001*(ms % int(1000*m_TRperiod));
if(m_bFastMode and (icw[0]>0) and (tsec > (m_TRperiod-5.0))) fastCwId=true;
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
// qDebug() << "Mod A" << m_ic << isym << tsec;
if(slowCwId or fastCwId) { // Transmit CW ID?
m_dphi = m_twoPi*m_frequency/m_frameRate;
if(m_bFastMode and !bCwId) {
m_frequency=1500; // Set params for CW ID
m_dphi = m_twoPi*m_frequency/m_frameRate;
m_symbolsLength=126;
m_nsps=4096.0*12000.0/11025.0;
m_ic=2246949;
m_nspd=2560; // 22.5 WPM
if(icw[0]*m_nspd/48000.0 > 4.0) m_nspd=4.0*48000.0/icw[0]; //Faster CW for long calls
}
bCwId=true;
unsigned ic0 = m_symbolsLength * 4 * m_nsps;
unsigned j(0);
while (samples != end) {
j = (m_ic - ic0)/m_nspd + 1; // symbol of this sample
bool level {bool (icw[j])};
m_phi += m_dphi;
if (m_phi > m_twoPi) m_phi -= m_twoPi;
qint16 sample=0;
float amp=32767.0;
float x=0;
if(m_ramp!=0) {
x=qSin(float(m_phi));
if(SOFT_KEYING) {
amp=qAbs(qint32(m_ramp));
if(amp>32767.0) amp=32767.0;
}
sample=round(amp*x);
}
if(m_bFastMode) {
sample=0;
if(level) sample=32767.0*x;
}
if (int (j) <= icw[0] && j < NUM_CW_SYMBOLS) { // stop condition
samples = load (postProcessSample (sample), samples);
++framesGenerated;
++m_ic;
} else {
Q_EMIT stateChanged ((m_state = Idle));
return framesGenerated * bytesPerFrame ();
}
// adjust ramp
if ((m_ramp != 0 && m_ramp != std::numeric_limits<qint16>::min ()) || level != m_cwLevel) {
// either ramp has terminated at max/min or direction has changed
m_ramp += RAMP_INCREMENT; // ramp
}
m_cwLevel = level;
}
return framesGenerated * bytesPerFrame ();
} else {
bCwId=false;
} //End of code for CW ID
double const baud (12000.0 / m_nsps);
// fade out parameters (no fade out for tuning)
unsigned int i0,i1;
if(m_tuning) {
i1 = i0 = (m_bFastMode ? 999999 : 9999) * m_nsps;
} else {
i0=(m_symbolsLength - 0.017) * 4.0 * m_nsps;
i1= m_symbolsLength * 4.0 * m_nsps;
}
if(m_bFastMode and !m_tuning) {
i1=m_TRperiod*48000.0 - 24000.0;
i0=i1-816;
}
qint16 sample;
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
isym=0;
if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000
if(m_bFastMode) isym=isym%m_symbolsLength;
if (isym != m_isym0 || m_frequency != m_frequency0) {
if(itone[0]>=100) {
m_toneFrequency0=itone[0];
} else {
if(m_toneSpacing==0.0) {
m_toneFrequency0=m_frequency + itone[isym]*baud;
} else {
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
}
}
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
m_isym0 = isym;
m_frequency0 = m_frequency; //???
}
int j=m_ic/480;
if(m_fSpread>0.0 and j!=m_j0) {
float x1=(float)qrand()/RAND_MAX;
float x2=(float)qrand()/RAND_MAX;
toneFrequency = m_toneFrequency0 + 0.5*m_fSpread*(x1+x2-1.0);
m_dphi = m_twoPi * toneFrequency / m_frameRate;
m_j0=j;
}
m_phi += m_dphi;
if (m_phi > m_twoPi) m_phi -= m_twoPi;
if (m_ic > i0) m_amp = 0.98 * m_amp;
if (m_ic > i1) m_amp = 0.0;
sample=qRound(m_amp*qSin(m_phi));
//Here's where we transmit from a precomputed wave[] array:
if(!m_tuning and (m_toneSpacing < 0)) {
m_amp=32767.0;
sample=qRound(m_amp*foxcom_.wave[m_ic]);
}
samples = load(postProcessSample(sample), samples);
++framesGenerated;
++m_ic;
}
if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
if (icw[0] == 0) {
// no CW ID to send
Q_EMIT stateChanged ((m_state = Idle));
return framesGenerated * bytesPerFrame ();
}
m_phi = 0.0;
}
m_frequency0 = m_frequency;
// done for this chunk - continue on next call
// qDebug() << "Mod B" << m_ic << i1 << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod));
while (samples != end) // pad block with silence
{
samples = load (0, samples);
++framesGenerated;
}
return framesGenerated * bytesPerFrame ();
}
// fall through
case Idle:
break;
}
Q_ASSERT (Idle == m_state);
return 0;
}
qint16 Modulator::postProcessSample (qint16 sample) const
{
if (m_addNoise) { // Test frame, we'll add noise
qint32 s = m_fac * (gran () + sample * m_snr / 32768.0);
if (s > std::numeric_limits<qint16>::max ()) {
s = std::numeric_limits<qint16>::max ();
}
if (s < std::numeric_limits<qint16>::min ()) {
s = std::numeric_limits<qint16>::min ();
}
sample = s;
}
return sample;
}

View File

@ -1,96 +0,0 @@
#ifndef MODULATOR_HPP__
#define MODULATOR_HPP__
#include <QAudio>
#include <QPointer>
#include "AudioDevice.hpp"
class SoundOutput;
//
// Input device that generates PCM audio frames that encode a message
// and an optional CW ID.
//
// Output can be muted while underway, preserving waveform timing when
// transmission is resumed.
//
class Modulator
: public AudioDevice
{
Q_OBJECT;
public:
enum ModulatorState {Synchronizing, Active, Idle};
Modulator (unsigned frameRate, double periodLengthInSeconds, QObject * parent = nullptr);
void close () override;
bool isTuning () const {return m_tuning;}
double frequency () const {return m_frequency;}
bool isActive () const {return m_state != Idle;}
void setSpread(double s) {m_fSpread=s;}
void setTRPeriod(double p) {m_period=p;}
void set_nsym(int n) {m_symbolsLength=n;}
void set_ms0(qint64 ms) {m_ms0=ms;}
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency,
double toneSpacing, SoundOutput *, Channel = Mono,
bool synchronize = true, bool fastMode = false,
double dBSNR = 99., double TRperiod=60.0);
Q_SLOT void stop (bool quick = false);
Q_SLOT void tune (bool newState = true);
Q_SLOT void setFrequency (double newFrequency) {m_frequency = newFrequency;}
Q_SIGNAL void stateChanged (ModulatorState) const;
protected:
qint64 readData (char * data, qint64 maxSize) override;
qint64 writeData (char const * /* data */, qint64 /* maxSize */) override
{
return -1; // we don't consume data
}
private:
qint16 postProcessSample (qint16 sample) const;
QPointer<SoundOutput> m_stream;
bool m_quickClose;
unsigned m_symbolsLength;
static double constexpr m_twoPi = 2.0 * 3.141592653589793238462;
unsigned m_nspd = 2048 + 512; // CW ID WPM factor = 22.5 WPM
double m_phi;
double m_dphi;
double m_amp;
double m_nsps;
double volatile m_frequency;
double m_frequency0;
double m_snr;
double m_fac;
double m_toneSpacing;
double m_fSpread;
double m_TRperiod;
double m_period;
qint64 m_silentFrames;
qint64 m_ms0;
qint16 m_ramp;
unsigned m_frameRate;
ModulatorState volatile m_state;
bool volatile m_tuning;
bool m_addNoise;
bool m_bFastMode;
bool m_cwLevel;
unsigned m_ic;
unsigned m_isym0;
int m_j0;
double m_toneFrequency0;
};
#endif

350
Modulator/Modulator.cpp Normal file
View File

@ -0,0 +1,350 @@
#include "Modulator.hpp"
#include <limits>
#include <qmath.h>
#include <QDateTime>
#include <QDebug>
#include "widgets/mainwindow.h" // TODO: G4WJS - break this dependency
#include "Audio/soundout.h"
#include "commons.h"
#include "moc_Modulator.cpp"
extern float gran(); // Noise generator (for tests only)
#define RAMP_INCREMENT 64 // MUST be an integral factor of 2^16
#if defined (WSJT_SOFT_KEYING)
# define SOFT_KEYING WSJT_SOFT_KEYING
#else
# define SOFT_KEYING 1
#endif
double constexpr Modulator::m_twoPi;
// float wpm=20.0;
// unsigned m_nspd=1.2*48000.0/wpm;
// m_nspd=3072; //18.75 WPM
Modulator::Modulator (unsigned frameRate, double periodLengthInSeconds,
QObject * parent)
: AudioDevice {parent}
, m_quickClose {false}
, m_phi {0.0}
, m_toneSpacing {0.0}
, m_fSpread {0.0}
, m_period {periodLengthInSeconds}
, m_frameRate {frameRate}
, m_state {Idle}
, m_tuning {false}
, m_cwLevel {false}
, m_j0 {-1}
, m_toneFrequency0 {1500.0}
{
}
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
double frequency, double toneSpacing,
SoundOutput * stream, Channel channel,
bool synchronize, bool fastMode, double dBSNR, double TRperiod)
{
Q_ASSERT (stream);
// Time according to this computer which becomes our base time
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
unsigned mstr = ms0 % int(1000.0*m_period); // ms into the nominal Tx start time
if(m_state != Idle) stop();
m_quickClose = false;
m_symbolsLength = symbolsLength;
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
m_frequency0 = 0.;
m_phi = 0.;
m_addNoise = dBSNR < 0.;
m_nsps = framesPerSymbol;
m_frequency = frequency;
m_amp = std::numeric_limits<qint16>::max ();
m_toneSpacing = toneSpacing;
m_bFastMode=fastMode;
m_TRperiod=TRperiod;
unsigned delay_ms=1000;
if(m_nsps==1920) delay_ms=500; //FT8
if(m_nsps==576) delay_ms=300; //FT4
// noise generator parameters
if (m_addNoise) {
m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
m_fac = 3000.0;
if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
}
// round up to an exact portion of a second that allows for startup delays
m_ic = (mstr / delay_ms) * m_frameRate * delay_ms / 1000;
if(m_bFastMode) m_ic=0;
m_silentFrames = 0;
// calculate number of silent frames to send, so that audio will start at
// the nominal time "delay_ms" into the Tx sequence.
if (synchronize && !m_tuning && !m_bFastMode) {
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
}
// qDebug() << "aa" << QDateTime::currentDateTimeUtc().toString("hh:mm:ss.zzz")
// << m_ic << m_silentFrames << m_silentFrames/48000.0
// << mstr << fmod(double(ms0),1000.0*m_period);
initialize (QIODevice::ReadOnly, channel);
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
Synchronizing : Active));
m_stream = stream;
if (m_stream) m_stream->restart (this);
}
void Modulator::tune (bool newState)
{
m_tuning = newState;
if (!m_tuning) stop (true);
}
void Modulator::stop (bool quick)
{
m_quickClose = quick;
close ();
}
void Modulator::close ()
{
if (m_stream)
{
if (m_quickClose)
{
m_stream->reset ();
}
else
{
m_stream->stop ();
}
}
if (m_state != Idle)
{
Q_EMIT stateChanged ((m_state = Idle));
}
AudioDevice::close ();
}
qint64 Modulator::readData (char * data, qint64 maxSize)
{
double toneFrequency=1500.0;
if(m_nsps==6) {
toneFrequency=1000.0;
m_frequency=1000.0;
m_frequency0=1000.0;
}
if(maxSize==0) return 0;
Q_ASSERT (!(maxSize % qint64 (bytesPerFrame ()))); // no torn frames
Q_ASSERT (isOpen ());
qint64 numFrames (maxSize / bytesPerFrame ());
qint16 * samples (reinterpret_cast<qint16 *> (data));
qint16 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint16)));
qint64 framesGenerated (0);
// if(m_ic==0) qDebug() << "Modulator::readData" << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod));
switch (m_state)
{
case Synchronizing:
{
if (m_silentFrames) { // send silence up to first second
framesGenerated = qMin (m_silentFrames, numFrames);
for ( ; samples != end; samples = load (0, samples)) { // silence
}
m_silentFrames -= framesGenerated;
return framesGenerated * bytesPerFrame ();
}
Q_EMIT stateChanged ((m_state = Active));
m_cwLevel = false;
m_ramp = 0; // prepare for CW wave shaping
}
// fall through
case Active:
{
unsigned int isym=0;
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
if(m_TRperiod==3.0) slowCwId=false;
bool fastCwId=false;
static bool bCwId=false;
qint64 ms = QDateTime::currentMSecsSinceEpoch();
float tsec=0.001*(ms % int(1000*m_TRperiod));
if(m_bFastMode and (icw[0]>0) and (tsec > (m_TRperiod-5.0))) fastCwId=true;
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
// qDebug() << "Mod A" << m_ic << isym << tsec;
if(slowCwId or fastCwId) { // Transmit CW ID?
m_dphi = m_twoPi*m_frequency/m_frameRate;
if(m_bFastMode and !bCwId) {
m_frequency=1500; // Set params for CW ID
m_dphi = m_twoPi*m_frequency/m_frameRate;
m_symbolsLength=126;
m_nsps=4096.0*12000.0/11025.0;
m_ic=2246949;
m_nspd=2560; // 22.5 WPM
if(icw[0]*m_nspd/48000.0 > 4.0) m_nspd=4.0*48000.0/icw[0]; //Faster CW for long calls
}
bCwId=true;
unsigned ic0 = m_symbolsLength * 4 * m_nsps;
unsigned j(0);
while (samples != end) {
j = (m_ic - ic0)/m_nspd + 1; // symbol of this sample
bool level {bool (icw[j])};
m_phi += m_dphi;
if (m_phi > m_twoPi) m_phi -= m_twoPi;
qint16 sample=0;
float amp=32767.0;
float x=0;
if(m_ramp!=0) {
x=qSin(float(m_phi));
if(SOFT_KEYING) {
amp=qAbs(qint32(m_ramp));
if(amp>32767.0) amp=32767.0;
}
sample=round(amp*x);
}
if(m_bFastMode) {
sample=0;
if(level) sample=32767.0*x;
}
if (int (j) <= icw[0] && j < NUM_CW_SYMBOLS) { // stop condition
samples = load (postProcessSample (sample), samples);
++framesGenerated;
++m_ic;
} else {
Q_EMIT stateChanged ((m_state = Idle));
return framesGenerated * bytesPerFrame ();
}
// adjust ramp
if ((m_ramp != 0 && m_ramp != std::numeric_limits<qint16>::min ()) || level != m_cwLevel) {
// either ramp has terminated at max/min or direction has changed
m_ramp += RAMP_INCREMENT; // ramp
}
m_cwLevel = level;
}
return framesGenerated * bytesPerFrame ();
} else {
bCwId=false;
} //End of code for CW ID
double const baud (12000.0 / m_nsps);
// fade out parameters (no fade out for tuning)
unsigned int i0,i1;
if(m_tuning) {
i1 = i0 = (m_bFastMode ? 999999 : 9999) * m_nsps;
} else {
i0=(m_symbolsLength - 0.017) * 4.0 * m_nsps;
i1= m_symbolsLength * 4.0 * m_nsps;
}
if(m_bFastMode and !m_tuning) {
i1=m_TRperiod*48000.0 - 24000.0;
i0=i1-816;
}
qint16 sample;
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
isym=0;
if(!m_tuning and m_TRperiod!=3.0) isym=m_ic/(4.0*m_nsps); //Actual fsample=48000
if(m_bFastMode) isym=isym%m_symbolsLength;
if (isym != m_isym0 || m_frequency != m_frequency0) {
if(itone[0]>=100) {
m_toneFrequency0=itone[0];
} else {
if(m_toneSpacing==0.0) {
m_toneFrequency0=m_frequency + itone[isym]*baud;
} else {
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
}
}
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
m_isym0 = isym;
m_frequency0 = m_frequency; //???
}
int j=m_ic/480;
if(m_fSpread>0.0 and j!=m_j0) {
float x1=(float)qrand()/RAND_MAX;
float x2=(float)qrand()/RAND_MAX;
toneFrequency = m_toneFrequency0 + 0.5*m_fSpread*(x1+x2-1.0);
m_dphi = m_twoPi * toneFrequency / m_frameRate;
m_j0=j;
}
m_phi += m_dphi;
if (m_phi > m_twoPi) m_phi -= m_twoPi;
if (m_ic > i0) m_amp = 0.98 * m_amp;
if (m_ic > i1) m_amp = 0.0;
sample=qRound(m_amp*qSin(m_phi));
//Here's where we transmit from a precomputed wave[] array:
if(!m_tuning and (m_toneSpacing < 0)) {
m_amp=32767.0;
sample=qRound(m_amp*foxcom_.wave[m_ic]);
}
samples = load(postProcessSample(sample), samples);
++framesGenerated;
++m_ic;
}
if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
if (icw[0] == 0) {
// no CW ID to send
Q_EMIT stateChanged ((m_state = Idle));
return framesGenerated * bytesPerFrame ();
}
m_phi = 0.0;
}
m_frequency0 = m_frequency;
// done for this chunk - continue on next call
// qDebug() << "Mod B" << m_ic << i1 << 0.001*(QDateTime::currentMSecsSinceEpoch() % (1000*m_TRperiod));
while (samples != end) // pad block with silence
{
samples = load (0, samples);
++framesGenerated;
}
return framesGenerated * bytesPerFrame ();
}
// fall through
case Idle:
break;
}
Q_ASSERT (Idle == m_state);
return 0;
}
qint16 Modulator::postProcessSample (qint16 sample) const
{
if (m_addNoise) { // Test frame, we'll add noise
qint32 s = m_fac * (gran () + sample * m_snr / 32768.0);
if (s > std::numeric_limits<qint16>::max ()) {
s = std::numeric_limits<qint16>::max ();
}
if (s < std::numeric_limits<qint16>::min ()) {
s = std::numeric_limits<qint16>::min ();
}
sample = s;
}
return sample;
}

96
Modulator/Modulator.hpp Normal file
View File

@ -0,0 +1,96 @@
#ifndef MODULATOR_HPP__
#define MODULATOR_HPP__
#include <QAudio>
#include <QPointer>
#include "Audio/AudioDevice.hpp"
class SoundOutput;
//
// Input device that generates PCM audio frames that encode a message
// and an optional CW ID.
//
// Output can be muted while underway, preserving waveform timing when
// transmission is resumed.
//
class Modulator
: public AudioDevice
{
Q_OBJECT;
public:
enum ModulatorState {Synchronizing, Active, Idle};
Modulator (unsigned frameRate, double periodLengthInSeconds, QObject * parent = nullptr);
void close () override;
bool isTuning () const {return m_tuning;}
double frequency () const {return m_frequency;}
bool isActive () const {return m_state != Idle;}
void setSpread(double s) {m_fSpread=s;}
void setTRPeriod(double p) {m_period=p;}
void set_nsym(int n) {m_symbolsLength=n;}
void set_ms0(qint64 ms) {m_ms0=ms;}
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency,
double toneSpacing, SoundOutput *, Channel = Mono,
bool synchronize = true, bool fastMode = false,
double dBSNR = 99., double TRperiod=60.0);
Q_SLOT void stop (bool quick = false);
Q_SLOT void tune (bool newState = true);
Q_SLOT void setFrequency (double newFrequency) {m_frequency = newFrequency;}
Q_SIGNAL void stateChanged (ModulatorState) const;
protected:
qint64 readData (char * data, qint64 maxSize) override;
qint64 writeData (char const * /* data */, qint64 /* maxSize */) override
{
return -1; // we don't consume data
}
private:
qint16 postProcessSample (qint16 sample) const;
QPointer<SoundOutput> m_stream;
bool m_quickClose;
unsigned m_symbolsLength;
static double constexpr m_twoPi = 2.0 * 3.141592653589793238462;
unsigned m_nspd = 2048 + 512; // CW ID WPM factor = 22.5 WPM
double m_phi;
double m_dphi;
double m_amp;
double m_nsps;
double volatile m_frequency;
double m_frequency0;
double m_snr;
double m_fac;
double m_toneSpacing;
double m_fSpread;
double m_TRperiod;
double m_period;
qint64 m_silentFrames;
qint64 m_ms0;
qint16 m_ramp;
unsigned m_frameRate;
ModulatorState volatile m_state;
bool volatile m_tuning;
bool m_addNoise;
bool m_bFastMode;
bool m_cwLevel;
unsigned m_ic;
unsigned m_isym0;
int m_j0;
double m_toneFrequency0;
};
#endif

View File

@ -0,0 +1,85 @@
#include "NetworkServerLookup.hpp"
#include <stdexcept>
#include <QHostInfo>
#include <QString>
std::tuple<QHostAddress, quint16>
network_server_lookup (QString query
, quint16 default_service_port
, QHostAddress default_host_address
, QAbstractSocket::NetworkLayerProtocol required_protocol)
{
query = query.trimmed ();
QHostAddress host_address {default_host_address};
quint16 service_port {default_service_port};
QString host_name;
if (!query.isEmpty ())
{
int port_colon_index {-1};
if ('[' == query[0])
{
// assume IPv6 combined address/port syntax [<address>]:<port>
auto close_bracket_index = query.lastIndexOf (']');
host_name = query.mid (1, close_bracket_index - 1);
port_colon_index = query.indexOf (':', close_bracket_index);
}
else
{
port_colon_index = query.lastIndexOf (':');
host_name = query.left (port_colon_index);
}
host_name = host_name.trimmed ();
if (port_colon_index >= 0)
{
bool ok;
service_port = query.mid (port_colon_index + 1).trimmed ().toUShort (&ok);
if (!ok)
{
throw std::runtime_error {"network server lookup error: invalid port"};
}
}
}
if (!host_name.isEmpty ())
{
auto host_info = QHostInfo::fromName (host_name);
if (host_info.addresses ().isEmpty ())
{
throw std::runtime_error {"network server lookup error: host name lookup failed"};
}
bool found {false};
for (int i {0}; i < host_info.addresses ().size () && !found; ++i)
{
host_address = host_info.addresses ().at (i);
switch (required_protocol)
{
case QAbstractSocket::IPv4Protocol:
case QAbstractSocket::IPv6Protocol:
if (required_protocol != host_address.protocol ())
{
break;
}
// fall through
case QAbstractSocket::AnyIPProtocol:
found = true;
break;
default:
throw std::runtime_error {"network server lookup error: invalid required protocol"};
}
}
if (!found)
{
throw std::runtime_error {"network server lookup error: no suitable host address found"};
}
}
return std::make_tuple (host_address, service_port);
}

133
Network/psk_reporter.cpp Normal file
View File

@ -0,0 +1,133 @@
// KISS Interface for posting spots to PSK Reporter web site
// Implemented by Edson Pereira PY2SDR
//
// Reports will be sent in batch mode every 5 minutes.
#include "psk_reporter.h"
#include <QHostInfo>
#include <QTimer>
#include "Network/MessageClient.hpp"
#include "moc_psk_reporter.cpp"
namespace
{
int constexpr MAX_PAYLOAD_LENGTH {1400};
}
PSK_Reporter::PSK_Reporter(MessageClient * message_client, QObject *parent) :
QObject {parent},
m_messageClient {message_client},
reportTimer {new QTimer {this}},
m_sequenceNumber {0}
{
m_header_h = "000Allllttttttttssssssssiiiiiiii";
// We use 50E2 and 50E3 for link Id
m_rxInfoDescriptor_h = "0003002C50E200040000"
"8002FFFF0000768F" // 2. Rx Call
"8004FFFF0000768F" // 4. Rx Grid
"8008FFFF0000768F" // 8. Rx Soft
"8009FFFF0000768F" // 9. Rx Antenna
"0000";
m_txInfoDescriptor_h = "0002003C50E30007"
"8001FFFF0000768F" // 1. Tx Call
"800500040000768F" // 5. Tx Freq
"800600010000768F" // 6. Tx snr
"800AFFFF0000768F" // 10. Tx Mode
"8003FFFF0000768F" // 3. Tx Grid
"800B00010000768F" // 11. Tx info src
"00960004"; // Report time
m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0'));
QHostInfo::lookupHost("report.pskreporter.info", this, SLOT(dnsLookupResult(QHostInfo)));
connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport()));
reportTimer->start(5*60*1000); // 5 minutes;
}
void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo)
{
m_rxCall = call;
m_rxGrid = gridSquare;
m_rxAnt = antenna;
m_progId = programInfo;
}
void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time )
{
QHash<QString,QString> spot;
spot["call"] = call;
spot["grid"] = grid;
spot["snr"] = snr;
spot["freq"] = freq;
spot["mode"] = mode;
spot["time"] = time;
m_spotQueue.enqueue(spot);
}
void PSK_Reporter::sendReport()
{
while (!m_spotQueue.isEmpty()) {
QString report_h;
// Header
QString header_h = m_header_h;
header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0')));
header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0')));
header_h.replace("iiiiiiii", m_randomId_h);
// Receiver information
QString rxInfoData_h = "50E2llll";
rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex();
rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex();
rxInfoData_h += "0000";
rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0')));
// Sender information
QString txInfoData_h = "50E3llll";
while (!m_spotQueue.isEmpty()
&& (header_h.size () + m_rxInfoDescriptor_h.size () + m_txInfoDescriptor_h.size () + rxInfoData_h.size () + txInfoData_h.size ()) / 2 < MAX_PAYLOAD_LENGTH) {
QHash<QString,QString> spot = m_spotQueue.dequeue();
txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0'));
txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2);
txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex();
txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC
txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0'));
}
txInfoData_h += "0000";
txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0')));
report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h;
//qDebug() << "Sending Report TX: ";
report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0')));
QByteArray report = QByteArray::fromHex(report_h.toUtf8());
// Send data to PSK Reporter site
if (!m_pskReporterAddress.isNull()) {
m_messageClient->send_raw_datagram (report, m_pskReporterAddress, 4739);
}
}
}
void PSK_Reporter::dnsLookupResult(QHostInfo info)
{
if (!info.addresses().isEmpty()) {
m_pskReporterAddress = info.addresses().at(0);
// qDebug() << "PSK Reporter IP: " << m_pskReporterAddress;
// deal with miss-configured settings that attempt to set a
// Pskreporter Internet address for the WSJT-X UDP protocol
// server address
m_messageClient->add_blocked_destination (m_pskReporterAddress);
}
}

View File

@ -1,86 +0,0 @@
#include "NetworkServerLookup.hpp"
#include <stdexcept>
#include <QHostInfo>
#include <QString>
std::tuple<QHostAddress, quint16>
network_server_lookup (QString query
, quint16 default_service_port
, QHostAddress default_host_address
, QAbstractSocket::NetworkLayerProtocol required_protocol)
{
query = query.trimmed ();
QHostAddress host_address {default_host_address};
quint16 service_port {default_service_port};
QString host_name;
if (!query.isEmpty ())
{
int port_colon_index {-1};
if ('[' == query[0])
{
// assume IPv6 combined address/port syntax [<address>]:<port>
auto close_bracket_index = query.lastIndexOf (']');
host_name = query.mid (1, close_bracket_index - 1);
port_colon_index = query.indexOf (':', close_bracket_index);
}
else
{
port_colon_index = query.lastIndexOf (':');
host_name = query.left (port_colon_index);
}
host_name = host_name.trimmed ();
if (port_colon_index >= 0)
{
bool ok;
service_port = query.mid (port_colon_index + 1).trimmed ().toUShort (&ok);
if (!ok)
{
throw std::runtime_error {"network server lookup error: invalid port"};
}
}
}
if (!host_name.isEmpty ())
{
auto host_info = QHostInfo::fromName (host_name);
if (host_info.addresses ().isEmpty ())
{
throw std::runtime_error {"network server lookup error: host name lookup failed"};
}
bool found {false};
for (int i {0}; i < host_info.addresses ().size () && !found; ++i)
{
host_address = host_info.addresses ().at (i);
switch (required_protocol)
{
case QAbstractSocket::IPv4Protocol:
case QAbstractSocket::IPv6Protocol:
if (required_protocol != host_address.protocol ())
{
break;
}
// drop through
case QAbstractSocket::AnyIPProtocol:
found = true;
break;
default:
throw std::runtime_error {"network server lookup error: invalid required protocol"};
}
}
if (!found)
{
throw std::runtime_error {"network server lookup error: no suitable host address found"};
}
}
return std::make_tuple (host_address, service_port);
}

View File

@ -1,783 +0,0 @@
#include "OmniRigTransceiver.hpp"
#include <QTimer>
#include <QDebug>
#include <objbase.h>
#include <QThread>
#include <QEventLoop>
#include "qt_helpers.hpP"
#include "moc_OmniRigTransceiver.cpp"
namespace
{
auto constexpr OmniRig_transceiver_one_name = "OmniRig Rig 1";
auto constexpr OmniRig_transceiver_two_name = "OmniRig Rig 2";
}
auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
{
if (param & OmniRig::PM_CW_U)
{
return CW_R;
}
else if (param & OmniRig::PM_CW_L)
{
return CW;
}
else if (param & OmniRig::PM_SSB_U)
{
return USB;
}
else if (param & OmniRig::PM_SSB_L)
{
return LSB;
}
else if (param & OmniRig::PM_DIG_U)
{
return DIG_U;
}
else if (param & OmniRig::PM_DIG_L)
{
return DIG_L;
}
else if (param & OmniRig::PM_AM)
{
return AM;
}
else if (param & OmniRig::PM_FM)
{
return FM;
}
TRACE_CAT ("OmniRigTransceiver", "unrecognized mode");
throw_qstring (tr ("OmniRig: unrecognized mode"));
return UNK;
}
OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
{
switch (mode)
{
case AM: return OmniRig::PM_AM;
case CW: return OmniRig::PM_CW_L;
case CW_R: return OmniRig::PM_CW_U;
case USB: return OmniRig::PM_SSB_U;
case LSB: return OmniRig::PM_SSB_L;
case FSK: return OmniRig::PM_DIG_L;
case FSK_R: return OmniRig::PM_DIG_U;
case DIG_L: return OmniRig::PM_DIG_L;
case DIG_U: return OmniRig::PM_DIG_U;
case FM: return OmniRig::PM_FM;
case DIG_FM: return OmniRig::PM_FM;
default: break;
}
return OmniRig::PM_SSB_U; // quieten compiler grumble
}
void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2)
{
(*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
id1
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
(*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities {
id2
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
}
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped,
RigNumber n, TransceiverFactory::PTTMethod ptt_type,
QString const& ptt_port, QObject * parent)
: TransceiverBase {parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))}
, ptt_type_ {ptt_type}
, rig_number_ {n}
, readable_params_ {0}
, writable_params_ {0}
, send_update_signal_ {false}
, reversed_ {false}
{
}
// returns false on time out
bool OmniRigTransceiver::await_notification_with_timeout (int timeout)
{
QEventLoop el;
connect (this, &OmniRigTransceiver::notified, &el, [&el] () {el.exit (1);});
QTimer::singleShot (timeout, Qt::CoarseTimer, &el, [&el] () {el.exit (0);});
return 1 == el.exec (); // wait for notify or timer
}
int OmniRigTransceiver::do_start ()
{
TRACE_CAT ("OmniRigTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
omni_rig_.reset (new OmniRig::OmniRigX {this});
if (omni_rig_->isNull ())
{
TRACE_CAT ("OmniRigTransceiver", "failed to start COM server");
throw_qstring (tr ("Failed to start OmniRig COM server"));
}
// COM/OLE exceptions get signaled
connect (&*omni_rig_, SIGNAL (exception (int, QString, QString, QString)), this, SLOT (handle_COM_exception (int, QString, QString, QString)));
// IOmniRigXEvent interface signals
connect (&*omni_rig_, SIGNAL (VisibleChange ()), this, SLOT (handle_visible_change ()));
connect (&*omni_rig_, SIGNAL (RigTypeChange (int)), this, SLOT (handle_rig_type_change (int)));
connect (&*omni_rig_, SIGNAL (StatusChange (int)), this, SLOT (handle_status_change (int)));
connect (&*omni_rig_, SIGNAL (ParamsChange (int, int)), this, SLOT (handle_params_change (int, int)));
connect (&*omni_rig_
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
, this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&)));
TRACE_CAT ("OmniRigTransceiver", "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
<< "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ());
// fetch the interface of the RigX CoClass and instantiate a proxy object
switch (rig_number_)
{
case One: rig_.reset (new OmniRig::RigX (omni_rig_->Rig1 ())); break;
case Two: rig_.reset (new OmniRig::RigX (omni_rig_->Rig2 ())); break;
}
Q_ASSERT (rig_);
Q_ASSERT (!rig_->isNull ());
if (use_for_ptt_ && (TransceiverFactory::PTT_method_DTR == ptt_type_ || TransceiverFactory::PTT_method_RTS == ptt_type_))
{
// fetch the interface for the serial port if we need it for PTT
port_.reset (new OmniRig::PortBits (rig_->PortBits ()));
Q_ASSERT (port_);
Q_ASSERT (!port_->isNull ());
TRACE_CAT ("OmniRigTransceiver", "OmniRig RTS state:" << port_->Rts ());
if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
{
TRACE_CAT ("OmniRigTransceiver", "Failed to get exclusive use of serial port for PTT from OmniRig");
}
// start off so we don't accidentally key the radio
if (TransceiverFactory::PTT_method_DTR == ptt_type_)
{
port_->SetDtr (false);
}
else // RTS
{
port_->SetRts (false);
}
}
rig_type_ = rig_->RigType ();
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_type_)
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number_).toLocal8Bit ());
for (int i = 0; i < 5; ++i)
{
if (OmniRig::ST_ONLINE == rig_->Status ())
{
break;
}
await_notification_with_timeout (1000);
}
if (OmniRig::ST_ONLINE != rig_->Status ())
{
throw_qstring ("OmniRig: " + rig_->StatusStr ());
}
auto f = rig_->GetRxFrequency ();
for (int i = 0; (f == 0) && (i < 5); ++i)
{
await_notification_with_timeout (1000);
f = rig_->GetRxFrequency ();
}
update_rx_frequency (f);
int resolution {0};
if (OmniRig::PM_UNKNOWN == rig_->Vfo ()
&& (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
{
// start with VFO A (probably MAIN) on rigs that we
// can't query VFO but can set explicitly
rig_->SetVfo (OmniRig::PM_VFOA);
}
f = state ().frequency ();
if (f % 10) return resolution; // 1Hz resolution
auto test_frequency = f - f % 100 + 55;
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (test_frequency);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (test_frequency);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (test_frequency);
}
else
{
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
}
if (!await_notification_with_timeout (1000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 1: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
switch (rig_->GetRxFrequency () - test_frequency)
{
case -5: resolution = -1; break; // 10Hz truncated
case 5: resolution = 1; break; // 10Hz rounded
case -15: resolution = -2; break; // 20Hz truncated
case -55: resolution = -2; break; // 100Hz truncated
case 45: resolution = 2; break; // 100Hz rounded
}
if (1 == resolution) // may be 20Hz rounded
{
test_frequency = f - f % 100 + 51;
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (test_frequency);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (test_frequency);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (test_frequency);
}
if (!await_notification_with_timeout (2000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 2: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
if (9 == rig_->GetRxFrequency () - test_frequency)
{
resolution = 2; // 20Hz rounded
}
}
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (f);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (f);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (f);
}
update_rx_frequency (f);
return resolution;
}
void OmniRigTransceiver::do_stop ()
{
QThread::msleep (200); // leave some time for pending
// commands at the server end
if (port_)
{
port_->Unlock (); // release serial port
port_->clear ();
port_.reset ();
}
if (omni_rig_)
{
if (rig_)
{
rig_->clear ();
rig_.reset ();
}
omni_rig_->clear ();
omni_rig_.reset ();
CoUninitialize ();
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("OmniRigTransceiver", "stopped");
}
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
{
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
throw_qstring (tr ("OmniRig COM/OLE error: %1 at %2: %3 (%4)").arg (QString::number (code)).arg (source). arg (desc). arg (help));
}
void OmniRigTransceiver::handle_visible_change ()
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
}
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "rig type change: rig =" << rig_number);
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ());
}
}
void OmniRigTransceiver::handle_status_change (int rig_number)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", QString {"status change for rig %1"}.arg (rig_number).toLocal8Bit ());
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
auto const& status = rig_->StatusStr ().toLocal8Bit ();
TRACE_CAT ("OmniRigTransceiver", "OmniRig status change: new status = " << status);
if (OmniRig::ST_ONLINE != rig_->Status ())
{
offline ("Rig went offline");
}
else
{
Q_EMIT notified ();
}
// else
// {
// update_rx_frequency (rig_->GetRxFrequency ());
// update_complete ();
// TRACE_CAT ("OmniRigTransceiver", "frequency:" << state ().frequency ());
// }
}
}
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"}
.arg (params, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ()
<< "state before:" << state ());
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
// starting_ = false;
TransceiverState old_state {state ()};
auto need_frequency = false;
if (params & OmniRig::PM_VFOAA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAA");
update_split (false);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
update_other_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOAB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAB");
update_split (true);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
update_other_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOBA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBA");
update_split (true);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
update_rx_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOBB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBB");
update_split (false);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
update_rx_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOA");
reversed_ = false;
need_frequency = true;
}
if (params & OmniRig::PM_VFOB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOB");
reversed_ = true;
need_frequency = true;
}
if (params & OmniRig::PM_FREQ)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ");
need_frequency = true;
}
if (params & OmniRig::PM_FREQA)
{
auto f = rig_->FreqA ();
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
}
else
{
update_rx_frequency (f);
}
}
if (params & OmniRig::PM_FREQB)
{
auto f = rig_->FreqB ();
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
}
else
{
update_other_frequency (f);
}
}
if (need_frequency)
{
if (readable_params_ & OmniRig::PM_FREQA)
{
auto f = rig_->FreqA ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
}
else
{
update_rx_frequency (f);
}
}
}
if (readable_params_ & OmniRig::PM_FREQB)
{
auto f = rig_->FreqB ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
}
else
{
update_other_frequency (f);
}
}
}
if (readable_params_ & OmniRig::PM_FREQ && !state ().ptt ())
{
auto f = rig_->Freq ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ = " << f);
update_rx_frequency (f);
}
}
}
if (params & OmniRig::PM_PITCH)
{
TRACE_CAT ("OmniRigTransceiver", "PITCH");
}
if (params & OmniRig::PM_RITOFFSET)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFFSET");
}
if (params & OmniRig::PM_RIT0)
{
TRACE_CAT ("OmniRigTransceiver", "RIT0");
}
if (params & OmniRig::PM_VFOEQUAL)
{
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
auto m = map_mode (rig_->Mode ());
TRACE_CAT ("OmniRigTransceiver", QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m));
update_rx_frequency (f);
update_other_frequency (f);
update_mode (m);
}
if (params & OmniRig::PM_VFOSWAP)
{
TRACE_CAT ("OmniRigTransceiver", "VFOSWAP");
auto f = state ().tx_frequency ();
update_other_frequency (state ().frequency ());
update_rx_frequency (f);
update_mode (map_mode (rig_->Mode ()));
}
if (params & OmniRig::PM_SPLITON)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITON");
update_split (true);
}
if (params & OmniRig::PM_SPLITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITOFF");
update_split (false);
}
if (params & OmniRig::PM_RITON)
{
TRACE_CAT ("OmniRigTransceiver", "RITON");
}
if (params & OmniRig::PM_RITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFF");
}
if (params & OmniRig::PM_XITON)
{
TRACE_CAT ("OmniRigTransceiver", "XITON");
}
if (params & OmniRig::PM_XITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "XITOFF");
}
if (params & OmniRig::PM_RX)
{
TRACE_CAT ("OmniRigTransceiver", "RX");
update_PTT (false);
}
if (params & OmniRig::PM_TX)
{
TRACE_CAT ("OmniRigTransceiver", "TX");
update_PTT ();
}
if (params & OmniRig::PM_CW_U)
{
TRACE_CAT ("OmniRigTransceiver", "CW-R");
update_mode (CW_R);
}
if (params & OmniRig::PM_CW_L)
{
TRACE_CAT ("OmniRigTransceiver", "CW");
update_mode (CW);
}
if (params & OmniRig::PM_SSB_U)
{
TRACE_CAT ("OmniRigTransceiver", "USB");
update_mode (USB);
}
if (params & OmniRig::PM_SSB_L)
{
TRACE_CAT ("OmniRigTransceiver", "LSB");
update_mode (LSB);
}
if (params & OmniRig::PM_DIG_U)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-U");
update_mode (DIG_U);
}
if (params & OmniRig::PM_DIG_L)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-L");
update_mode (DIG_L);
}
if (params & OmniRig::PM_AM)
{
TRACE_CAT ("OmniRigTransceiver", "AM");
update_mode (AM);
}
if (params & OmniRig::PM_FM)
{
TRACE_CAT ("OmniRigTransceiver", "FM");
update_mode (FM);
}
if (old_state != state () || send_update_signal_)
{
update_complete ();
send_update_signal_ = false;
}
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
}
Q_EMIT notified ();
}
void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply)
{
(void)command;
(void)reply;
if (!omni_rig_ || omni_rig_->isNull ()) return;
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
<< "with reply" << reply.toString ().toLocal8Bit ()
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
TRACE_CAT ("OmniRigTransceiver", "rig number:" << rig_number_ << ':' << state ());
}
}
void OmniRigTransceiver::do_ptt (bool on)
{
TRACE_CAT ("OmniRigTransceiver", on << state ());
if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set PTT");
rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
}
else
{
if (port_)
{
if (TransceiverFactory::PTT_method_RTS == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set RTS");
port_->SetRts (on);
}
else // "DTR"
{
TRACE_CAT ("OmniRigTransceiver", "set DTR");
port_->SetDtr (on);
}
}
else
{
TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver");
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
}
}
update_PTT (on);
}
void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", f << state ());
if (UNK != m)
{
do_mode (m);
}
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (f);
update_rx_frequency (f);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (f);
update_rx_frequency (f);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (f);
update_rx_frequency (f);
}
else
{
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
}
}
void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", tx << state ());
bool split {tx != 0};
if (split)
{
if (UNK != m)
{
do_mode (m);
if (OmniRig::PM_UNKNOWN == rig_->Vfo ())
{
if (writable_params_ & OmniRig::PM_VFOEQUAL)
{
// nothing to do here because OmniRig will use VFO
// equalize to set the mode of the Tx VFO for us
}
else if ((writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
{
rig_->SetVfo (OmniRig::PM_VFOB);
do_mode (m);
rig_->SetVfo (OmniRig::PM_VFOA);
}
else if (writable_params_ & OmniRig::PM_VFOSWAP)
{
rig_->SetVfo (OmniRig::PM_VFOSWAP);
do_mode (m);
rig_->SetVfo (OmniRig::PM_VFOSWAP);
}
}
}
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on");
rig_->SetSplitMode (state ().frequency (), tx);
update_other_frequency (tx);
update_split (true);
}
else
{
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off");
rig_->SetSimplexMode (state ().frequency ());
update_split (false);
}
bool notify {false};
if (readable_params_ & OmniRig::PM_FREQ || !(readable_params_ & (OmniRig::PM_FREQA | OmniRig::PM_FREQB)))
{
update_other_frequency (tx); // async updates won't return this
// so just store it and hope
// operator doesn't change the
// "back" VFO on rig
notify = true;
}
if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
{
TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually");
update_split (split); // we can't read it so just set and
// hope op doesn't change it
notify = true;
}
if (notify)
{
update_complete ();
}
}
void OmniRigTransceiver::do_mode (MODE mode)
{
TRACE_CAT ("OmniRigTransceiver", mode << state ());
// TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
auto mapped = map_mode (mode);
if (mapped & writable_params_)
{
rig_->SetMode (mapped);
update_mode (mode);
}
else
{
offline ("OmniRig invalid mode");
}
}

View File

@ -1,74 +0,0 @@
#ifndef POLLING_TRANSCEIVER_HPP__
#define POLLING_TRANSCEIVER_HPP__
#include <QObject>
#include "TransceiverBase.hpp"
class QTimer;
//
// Polling Transceiver
//
// Helper base class that encapsulates the emulation of continuous
// update and caching of a transceiver state.
//
// Collaborations
//
// Implements the TransceiverBase post action interface and provides
// the abstract poll() operation for sub-classes to implement. The
// poll operation is invoked every poll_interval seconds.
//
// Responsibilities
//
// Because some rig interfaces don't immediately update after a state
// change request; this class allows a rig a few polls to stabilise
// to the requested state before signalling the change. This means
// that clients don't see intermediate states that are sometimes
// inaccurate, e.g. changing the split TX frequency on Icom rigs
// requires a VFO switch and polls while switched will return the
// wrong current frequency.
//
class PollingTransceiver
: public TransceiverBase
{
Q_OBJECT; // for translation context
protected:
explicit PollingTransceiver (int poll_interval, // in seconds
QObject * parent);
protected:
// Sub-classes implement this and fetch what they can from the rig
// in a non-intrusive manner.
virtual void do_poll () = 0;
void do_post_start () override final;
void do_post_stop () override final;
void do_post_frequency (Frequency, MODE) override final;
void do_post_tx_frequency (Frequency, MODE) override final;
void do_post_mode (MODE) override final;
void do_post_ptt (bool = true) override final;
bool do_pre_update () override final;
private:
void start_timer ();
void stop_timer ();
Q_SLOT void handle_timeout ();
int interval_; // polling interval in milliseconds
QTimer * poll_timer_;
// keep a record of the last state signalled so we can elide
// duplicate updates
Transceiver::TransceiverState last_signalled_state_;
// keep a record of expected state so we can compare with actual
// updates to determine when state changes have bubbled through
Transceiver::TransceiverState next_state_;
unsigned retries_; // number of incorrect polls left
};
#endif

View File

@ -54,7 +54,7 @@ Directory::Directory (Configuration const * configuration
connect (network_manager_, &QNetworkAccessManager::authenticationRequired
, this, &Directory::authentication);
connect (this, &Directory::itemChanged, [this] (QTreeWidgetItem * item) {
connect (this, &Directory::itemChanged, [] (QTreeWidgetItem * item) {
switch (item->type ())
{
case FileNode::Type:

View File

@ -0,0 +1,506 @@
#include "DXLabSuiteCommanderTransceiver.hpp"
#include <QTcpSocket>
#include <QRegularExpression>
#include <QLocale>
#include <QThread>
#include <QDateTime>
#include "Network/NetworkServerLookup.hpp"
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
namespace
{
char const * const commander_transceiver_name {"DX Lab Suite Commander"};
QString map_mode (Transceiver::MODE mode)
{
switch (mode)
{
case Transceiver::AM: return "AM";
case Transceiver::CW: return "CW";
case Transceiver::CW_R: return "CW-R";
case Transceiver::USB: return "USB";
case Transceiver::LSB: return "LSB";
case Transceiver::FSK: return "RTTY";
case Transceiver::FSK_R: return "RTTY-R";
case Transceiver::DIG_L: return "DATA-L";
case Transceiver::DIG_U: return "DATA-U";
case Transceiver::FM:
case Transceiver::DIG_FM:
return "FM";
default: break;
}
return "USB";
}
}
void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
{
(*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true};
}
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent)
: PollingTransceiver {poll_interval, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, server_ {address}
, commander_ {nullptr}
{
}
int DXLabSuiteCommanderTransceiver::do_start ()
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol);
if (!commander_)
{
commander_ = new QTcpSocket {this}; // QObject takes ownership
}
commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!commander_->waitForConnected ())
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to connect" << commander_->errorString ());
throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
}
// sleeps here are to ensure Commander has actually queried the rig
// rather than returning cached data which maybe stale or simply
// read backs of not yet committed values, the 2s delays are
// arbitrary but hopefully enough as the actual time required is rig
// and Commander setting dependent
int resolution {0};
QThread::msleep (2000);
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f && !(f % 10))
{
auto test_frequency = f - f % 100 + 55;
auto f_string = frequency_to_string (test_frequency);
auto params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
QThread::msleep (2000);
reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto new_frequency = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
switch (static_cast<Radio::FrequencyDelta> (new_frequency - test_frequency))
{
case -5: resolution = -1; break; // 10Hz truncated
case 5: resolution = 1; break; // 10Hz rounded
case -15: resolution = -2; break; // 20Hz truncated
case -55: resolution = -2; break; // 100Hz truncated
case 45: resolution = 2; break; // 100Hz rounded
}
if (1 == resolution) // may be 20Hz rounded
{
test_frequency = f - f % 100 + 51;
f_string = frequency_to_string (test_frequency);
params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
QThread::msleep (2000);
reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
new_frequency = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (9 == static_cast<Radio::FrequencyDelta> (new_frequency - test_frequency))
{
resolution = 2; // 20Hz rounded
}
}
f_string = frequency_to_string (f);
params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
}
}
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
}
do_poll ();
return resolution;
}
void DXLabSuiteCommanderTransceiver::do_stop ()
{
if (commander_)
{
commander_->close ();
delete commander_, commander_ = nullptr;
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped");
}
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ());
if (use_for_ptt_)
{
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
bool tx {!on};
auto start = QDateTime::currentMSecsSinceEpoch ();
// we must now wait for Tx on the rig, we will wait a short while
// before bailing out
while (tx != on && QDateTime::currentMSecsSinceEpoch () - start < 1000)
{
auto reply = command_with_reply ("<command:9>CmdSendTx<parameters:0>");
if (0 == reply.indexOf ("<CmdTX:"))
{
auto state = reply.mid (reply.indexOf ('>') + 1);
if ("ON" == state)
{
tx = true;
}
else if ("OFF" == state)
{
tx = false;
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "unexpected TX state" << state);
throw error {tr ("DX Lab Suite Commander sent an unrecognised TX state: ") + state};
}
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get TX unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX status: ") + reply};
}
if (tx != on) QThread::msleep (10); // don't thrash Commander
}
update_PTT (tx);
if (tx != on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "rig failed to respond to PTT: " << on);
throw error {tr ("DX Lab Suite Commander rig did not respond to PTT: ") + (on ? "ON" : "OFF")};
}
}
else
{
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
update_PTT (on);
}
}
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", f << state ());
auto f_string = frequency_to_string (f);
if (UNK != m && m != get_mode ())
{
auto m_string = map_mode (m);
auto params = ("<xcvrfreq:%1>" + f_string + "<xcvrmode:%2>" + m_string + "<preservesplitanddual:1>Y").arg (f_string.size ()).arg (m_string.size ());
simple_command (("<command:14>CmdSetFreqMode<parameters:%1>" + params).arg (params.size ()));
update_mode (m);
}
else
{
auto params = ("<xcvrfreq:%1>" + f_string).arg (f_string.size ());
simple_command (("<command:10>CmdSetFreq<parameters:%1>" + params).arg (params.size ()));
}
update_rx_frequency (f);
}
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", tx << state ());
if (tx)
{
auto f_string = frequency_to_string (tx);
auto params = ("<xcvrfreq:%1>" + f_string + "<SuppressDual:1>Y").arg (f_string.size ());
if (UNK == mode)
{
params += "<SuppressModeChange:1>Y";
}
simple_command (("<command:11>CmdQSXSplit<parameters:%1>" + params).arg (params.size ()));
}
else
{
simple_command ("<command:8>CmdSplit<parameters:8><1:3>off");
}
update_split (tx);
update_other_frequency (tx);
}
void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", m << state ());
auto m_string = map_mode (m);
auto params = ("<1:%1>" + m_string).arg (m_string.size ());
simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ()));
update_mode (m);
}
void DXLabSuiteCommanderTransceiver::do_poll ()
{
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
bool quiet {false};
#else
bool quiet {true};
#endif
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f)
{
if (!state ().ptt ()) // Commander is not reliable on frequency
// polls while transmitting
{
update_rx_frequency (f);
}
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency: ") + reply};
}
if (state ().split ())
{
reply = command_with_reply ("<command:12>CmdGetTXFreq<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdTXFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
if (f)
{
if (!state ().ptt ()) // Commander is not reliable on frequency
// polls while transmitting
{
update_other_frequency (f);
}
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get tx frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency: ") + reply};
}
}
reply = command_with_reply ("<command:12>CmdSendSplit<parameters:0>", quiet);
if (0 == reply.indexOf ("<CmdSplit:"))
{
auto split = reply.mid (reply.indexOf ('>') + 1);
if ("ON" == split)
{
update_split (true);
}
else if ("OFF" == split)
{
update_split (false);
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected split state" << split);
throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get split mode unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status: ") + reply};
}
get_mode (quiet);
}
auto DXLabSuiteCommanderTransceiver::get_mode (bool no_debug) -> MODE
{
MODE m {UNK};
auto reply = command_with_reply ("<command:11>CmdSendMode<parameters:0>", no_debug);
if (0 == reply.indexOf ("<CmdMode:"))
{
auto mode = reply.mid (reply.indexOf ('>') + 1);
if ("AM" == mode)
{
m = AM;
}
else if ("CW" == mode)
{
m = CW;
}
else if ("CW-R" == mode)
{
m = CW_R;
}
else if ("FM" == mode || "WBFM" == mode)
{
m = FM;
}
else if ("LSB" == mode)
{
m = LSB;
}
else if ("USB" == mode)
{
m = USB;
}
else if ("RTTY" == mode)
{
m = FSK;
}
else if ("RTTY-R" == mode)
{
m = FSK_R;
}
else if ("PKT" == mode || "DATA-L" == mode || "Data-L" == mode || "DIGL" == mode)
{
m = DIG_L;
}
else if ("PKT-R" == mode || "DATA-U" == mode || "Data-U" == mode || "DIGU" == mode)
{
m = DIG_U;
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected mode name" << mode);
throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'};
}
update_mode (m);
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode: ") + reply};
}
return m;
}
void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd);
}
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed:" << commander_->errorString ());
throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()};
}
}
QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, bool no_debug)
{
Q_ASSERT (commander_);
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander failed to send command \"%1\": %2\n")
.arg (cmd)
.arg (commander_->errorString ())
};
}
// waitForReadReady appears to be unreliable on Windows timing out
// when data is waiting so retry a few times
unsigned retries {5};
bool replied {false};
while (!replied && --retries)
{
replied = commander_->waitForReadyRead ();
if (!replied && commander_->error () != commander_->SocketTimeoutError)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "failed to read reply:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd)
.arg (commander_->errorString ())
};
}
}
if (!replied)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd)
};
}
auto result = commander_->readAll ();
// qDebug () << "result: " << result;
// for (int i = 0; i < result.size (); ++i)
// {
// qDebug () << i << ":" << hex << int (result[i]);
// }
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
}
return result; // converting raw UTF-8 bytes to QString
}
bool DXLabSuiteCommanderTransceiver::write_to_port (QString const& s)
{
auto data = s.toLocal8Bit ();
auto to_send = data.constData ();
auto length = data.size ();
qint64 total_bytes_sent {0};
while (total_bytes_sent < length)
{
auto bytes_sent = commander_->write (to_send + total_bytes_sent, length - total_bytes_sent);
if (bytes_sent < 0 || !commander_->waitForBytesWritten ())
{
return false;
}
total_bytes_sent += bytes_sent;
}
return true;
}
QString DXLabSuiteCommanderTransceiver::frequency_to_string (Frequency f) const
{
// number is localized and in kHz, avoid floating point translation
// errors by adding a small number (0.1Hz)
return QString {"%L1"}.arg (f / 1e3 + 1e-4, 10, 'f', 3);
}
auto DXLabSuiteCommanderTransceiver::string_to_frequency (QString s) const -> Frequency
{
// temporary hack because Commander is returning invalid UTF-8 bytes
s.replace (QChar {QChar::ReplacementCharacter}, locale_.groupSeparator ());
// remove DP - relies on n.nnn kHz format so we can do ulonglong
// conversion to Hz
bool ok;
// auto f = locale_.toDouble (s, &ok); // use when CmdSendFreq and
// CmdSendTxFreq reinstated
auto f = QLocale::c ().toDouble (s, &ok); // temporary fix
if (!ok)
{
throw error {tr ("DX Lab Suite Commander sent an unrecognized frequency")};
}
return (f + 1e-4) * 1e3;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,783 @@
#include "OmniRigTransceiver.hpp"
#include <QTimer>
#include <QDebug>
#include <objbase.h>
#include <QThread>
#include <QEventLoop>
#include "qt_helpers.hpp"
#include "moc_OmniRigTransceiver.cpp"
namespace
{
auto constexpr OmniRig_transceiver_one_name = "OmniRig Rig 1";
auto constexpr OmniRig_transceiver_two_name = "OmniRig Rig 2";
}
auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
{
if (param & OmniRig::PM_CW_U)
{
return CW_R;
}
else if (param & OmniRig::PM_CW_L)
{
return CW;
}
else if (param & OmniRig::PM_SSB_U)
{
return USB;
}
else if (param & OmniRig::PM_SSB_L)
{
return LSB;
}
else if (param & OmniRig::PM_DIG_U)
{
return DIG_U;
}
else if (param & OmniRig::PM_DIG_L)
{
return DIG_L;
}
else if (param & OmniRig::PM_AM)
{
return AM;
}
else if (param & OmniRig::PM_FM)
{
return FM;
}
TRACE_CAT ("OmniRigTransceiver", "unrecognized mode");
throw_qstring (tr ("OmniRig: unrecognized mode"));
return UNK;
}
OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
{
switch (mode)
{
case AM: return OmniRig::PM_AM;
case CW: return OmniRig::PM_CW_L;
case CW_R: return OmniRig::PM_CW_U;
case USB: return OmniRig::PM_SSB_U;
case LSB: return OmniRig::PM_SSB_L;
case FSK: return OmniRig::PM_DIG_L;
case FSK_R: return OmniRig::PM_DIG_U;
case DIG_L: return OmniRig::PM_DIG_L;
case DIG_U: return OmniRig::PM_DIG_U;
case FM: return OmniRig::PM_FM;
case DIG_FM: return OmniRig::PM_FM;
default: break;
}
return OmniRig::PM_SSB_U; // quieten compiler grumble
}
void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2)
{
(*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
id1
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
(*registry)[OmniRig_transceiver_two_name] = TransceiverFactory::Capabilities {
id2
, TransceiverFactory::Capabilities::none // COM isn't serial or network
, true // does PTT
, false // doesn't select mic/data (use OmniRig config file)
, true // can remote control RTS nd DTR
, true // asynchronous interface
};
}
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped,
RigNumber n, TransceiverFactory::PTTMethod ptt_type,
QString const& ptt_port, QObject * parent)
: TransceiverBase {parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))}
, ptt_type_ {ptt_type}
, rig_number_ {n}
, readable_params_ {0}
, writable_params_ {0}
, send_update_signal_ {false}
, reversed_ {false}
{
}
// returns false on time out
bool OmniRigTransceiver::await_notification_with_timeout (int timeout)
{
QEventLoop el;
connect (this, &OmniRigTransceiver::notified, &el, [&el] () {el.exit (1);});
QTimer::singleShot (timeout, Qt::CoarseTimer, &el, [&el] () {el.exit (0);});
return 1 == el.exec (); // wait for notify or timer
}
int OmniRigTransceiver::do_start ()
{
TRACE_CAT ("OmniRigTransceiver", "starting");
if (wrapped_) wrapped_->start (0);
CoInitializeEx (nullptr, 0 /*COINIT_APARTMENTTHREADED*/); // required because Qt only does this for GUI thread
omni_rig_.reset (new OmniRig::OmniRigX {this});
if (omni_rig_->isNull ())
{
TRACE_CAT ("OmniRigTransceiver", "failed to start COM server");
throw_qstring (tr ("Failed to start OmniRig COM server"));
}
// COM/OLE exceptions get signaled
connect (&*omni_rig_, SIGNAL (exception (int, QString, QString, QString)), this, SLOT (handle_COM_exception (int, QString, QString, QString)));
// IOmniRigXEvent interface signals
connect (&*omni_rig_, SIGNAL (VisibleChange ()), this, SLOT (handle_visible_change ()));
connect (&*omni_rig_, SIGNAL (RigTypeChange (int)), this, SLOT (handle_rig_type_change (int)));
connect (&*omni_rig_, SIGNAL (StatusChange (int)), this, SLOT (handle_status_change (int)));
connect (&*omni_rig_, SIGNAL (ParamsChange (int, int)), this, SLOT (handle_params_change (int, int)));
connect (&*omni_rig_
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
, this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&)));
TRACE_CAT ("OmniRigTransceiver", "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
<< "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ());
// fetch the interface of the RigX CoClass and instantiate a proxy object
switch (rig_number_)
{
case One: rig_.reset (new OmniRig::RigX (omni_rig_->Rig1 ())); break;
case Two: rig_.reset (new OmniRig::RigX (omni_rig_->Rig2 ())); break;
}
Q_ASSERT (rig_);
Q_ASSERT (!rig_->isNull ());
if (use_for_ptt_ && (TransceiverFactory::PTT_method_DTR == ptt_type_ || TransceiverFactory::PTT_method_RTS == ptt_type_))
{
// fetch the interface for the serial port if we need it for PTT
port_.reset (new OmniRig::PortBits (rig_->PortBits ()));
Q_ASSERT (port_);
Q_ASSERT (!port_->isNull ());
TRACE_CAT ("OmniRigTransceiver", "OmniRig RTS state:" << port_->Rts ());
if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
{
TRACE_CAT ("OmniRigTransceiver", "Failed to get exclusive use of serial port for PTT from OmniRig");
}
// start off so we don't accidentally key the radio
if (TransceiverFactory::PTT_method_DTR == ptt_type_)
{
port_->SetDtr (false);
}
else // RTS
{
port_->SetRts (false);
}
}
rig_type_ = rig_->RigType ();
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_type_)
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number_).toLocal8Bit ());
for (int i = 0; i < 5; ++i)
{
if (OmniRig::ST_ONLINE == rig_->Status ())
{
break;
}
await_notification_with_timeout (1000);
}
if (OmniRig::ST_ONLINE != rig_->Status ())
{
throw_qstring ("OmniRig: " + rig_->StatusStr ());
}
auto f = rig_->GetRxFrequency ();
for (int i = 0; (f == 0) && (i < 5); ++i)
{
await_notification_with_timeout (1000);
f = rig_->GetRxFrequency ();
}
update_rx_frequency (f);
int resolution {0};
if (OmniRig::PM_UNKNOWN == rig_->Vfo ()
&& (writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
{
// start with VFO A (probably MAIN) on rigs that we
// can't query VFO but can set explicitly
rig_->SetVfo (OmniRig::PM_VFOA);
}
f = state ().frequency ();
if (f % 10) return resolution; // 1Hz resolution
auto test_frequency = f - f % 100 + 55;
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (test_frequency);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (test_frequency);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (test_frequency);
}
else
{
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
}
if (!await_notification_with_timeout (1000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 1: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
switch (rig_->GetRxFrequency () - test_frequency)
{
case -5: resolution = -1; break; // 10Hz truncated
case 5: resolution = 1; break; // 10Hz rounded
case -15: resolution = -2; break; // 20Hz truncated
case -55: resolution = -2; break; // 100Hz truncated
case 45: resolution = 2; break; // 100Hz rounded
}
if (1 == resolution) // may be 20Hz rounded
{
test_frequency = f - f % 100 + 51;
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (test_frequency);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (test_frequency);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (test_frequency);
}
if (!await_notification_with_timeout (2000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 2: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
if (9 == rig_->GetRxFrequency () - test_frequency)
{
resolution = 2; // 20Hz rounded
}
}
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (f);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (f);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (f);
}
update_rx_frequency (f);
return resolution;
}
void OmniRigTransceiver::do_stop ()
{
QThread::msleep (200); // leave some time for pending
// commands at the server end
if (port_)
{
port_->Unlock (); // release serial port
port_->clear ();
port_.reset ();
}
if (omni_rig_)
{
if (rig_)
{
rig_->clear ();
rig_.reset ();
}
omni_rig_->clear ();
omni_rig_.reset ();
CoUninitialize ();
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("OmniRigTransceiver", "stopped");
}
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
{
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
throw_qstring (tr ("OmniRig COM/OLE error: %1 at %2: %3 (%4)").arg (QString::number (code)).arg (source). arg (desc). arg (help));
}
void OmniRigTransceiver::handle_visible_change ()
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
}
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "rig type change: rig =" << rig_number);
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ());
}
}
void OmniRigTransceiver::handle_status_change (int rig_number)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", QString {"status change for rig %1"}.arg (rig_number).toLocal8Bit ());
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
auto const& status = rig_->StatusStr ().toLocal8Bit ();
TRACE_CAT ("OmniRigTransceiver", "OmniRig status change: new status = " << status);
if (OmniRig::ST_ONLINE != rig_->Status ())
{
offline ("Rig went offline");
}
else
{
Q_EMIT notified ();
}
// else
// {
// update_rx_frequency (rig_->GetRxFrequency ());
// update_complete ();
// TRACE_CAT ("OmniRigTransceiver", "frequency:" << state ().frequency ());
// }
}
}
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"}
.arg (params, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ()
<< "state before:" << state ());
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
// starting_ = false;
TransceiverState old_state {state ()};
auto need_frequency = false;
if (params & OmniRig::PM_VFOAA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAA");
update_split (false);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
update_other_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOAB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAB");
update_split (true);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
update_other_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOBA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBA");
update_split (true);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
update_rx_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOBB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBB");
update_split (false);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
update_rx_frequency (rig_->FreqB ());
}
if (params & OmniRig::PM_VFOA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOA");
reversed_ = false;
need_frequency = true;
}
if (params & OmniRig::PM_VFOB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOB");
reversed_ = true;
need_frequency = true;
}
if (params & OmniRig::PM_FREQ)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ");
need_frequency = true;
}
if (params & OmniRig::PM_FREQA)
{
auto f = rig_->FreqA ();
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
}
else
{
update_rx_frequency (f);
}
}
if (params & OmniRig::PM_FREQB)
{
auto f = rig_->FreqB ();
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
}
else
{
update_other_frequency (f);
}
}
if (need_frequency)
{
if (readable_params_ & OmniRig::PM_FREQA)
{
auto f = rig_->FreqA ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
}
else
{
update_rx_frequency (f);
}
}
}
if (readable_params_ & OmniRig::PM_FREQB)
{
auto f = rig_->FreqB ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
}
else
{
update_other_frequency (f);
}
}
}
if (readable_params_ & OmniRig::PM_FREQ && !state ().ptt ())
{
auto f = rig_->Freq ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ = " << f);
update_rx_frequency (f);
}
}
}
if (params & OmniRig::PM_PITCH)
{
TRACE_CAT ("OmniRigTransceiver", "PITCH");
}
if (params & OmniRig::PM_RITOFFSET)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFFSET");
}
if (params & OmniRig::PM_RIT0)
{
TRACE_CAT ("OmniRigTransceiver", "RIT0");
}
if (params & OmniRig::PM_VFOEQUAL)
{
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
auto m = map_mode (rig_->Mode ());
TRACE_CAT ("OmniRigTransceiver", QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m));
update_rx_frequency (f);
update_other_frequency (f);
update_mode (m);
}
if (params & OmniRig::PM_VFOSWAP)
{
TRACE_CAT ("OmniRigTransceiver", "VFOSWAP");
auto f = state ().tx_frequency ();
update_other_frequency (state ().frequency ());
update_rx_frequency (f);
update_mode (map_mode (rig_->Mode ()));
}
if (params & OmniRig::PM_SPLITON)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITON");
update_split (true);
}
if (params & OmniRig::PM_SPLITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITOFF");
update_split (false);
}
if (params & OmniRig::PM_RITON)
{
TRACE_CAT ("OmniRigTransceiver", "RITON");
}
if (params & OmniRig::PM_RITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFF");
}
if (params & OmniRig::PM_XITON)
{
TRACE_CAT ("OmniRigTransceiver", "XITON");
}
if (params & OmniRig::PM_XITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "XITOFF");
}
if (params & OmniRig::PM_RX)
{
TRACE_CAT ("OmniRigTransceiver", "RX");
update_PTT (false);
}
if (params & OmniRig::PM_TX)
{
TRACE_CAT ("OmniRigTransceiver", "TX");
update_PTT ();
}
if (params & OmniRig::PM_CW_U)
{
TRACE_CAT ("OmniRigTransceiver", "CW-R");
update_mode (CW_R);
}
if (params & OmniRig::PM_CW_L)
{
TRACE_CAT ("OmniRigTransceiver", "CW");
update_mode (CW);
}
if (params & OmniRig::PM_SSB_U)
{
TRACE_CAT ("OmniRigTransceiver", "USB");
update_mode (USB);
}
if (params & OmniRig::PM_SSB_L)
{
TRACE_CAT ("OmniRigTransceiver", "LSB");
update_mode (LSB);
}
if (params & OmniRig::PM_DIG_U)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-U");
update_mode (DIG_U);
}
if (params & OmniRig::PM_DIG_L)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-L");
update_mode (DIG_L);
}
if (params & OmniRig::PM_AM)
{
TRACE_CAT ("OmniRigTransceiver", "AM");
update_mode (AM);
}
if (params & OmniRig::PM_FM)
{
TRACE_CAT ("OmniRigTransceiver", "FM");
update_mode (FM);
}
if (old_state != state () || send_update_signal_)
{
update_complete ();
send_update_signal_ = false;
}
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
}
Q_EMIT notified ();
}
void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& command, QVariant const& reply)
{
(void)command;
(void)reply;
if (!omni_rig_ || omni_rig_->isNull ()) return;
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
<< "with reply" << reply.toString ().toLocal8Bit ()
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
TRACE_CAT ("OmniRigTransceiver", "rig number:" << rig_number_ << ':' << state ());
}
}
void OmniRigTransceiver::do_ptt (bool on)
{
TRACE_CAT ("OmniRigTransceiver", on << state ());
if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set PTT");
rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
}
else
{
if (port_)
{
if (TransceiverFactory::PTT_method_RTS == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set RTS");
port_->SetRts (on);
}
else // "DTR"
{
TRACE_CAT ("OmniRigTransceiver", "set DTR");
port_->SetDtr (on);
}
}
else
{
TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver");
Q_ASSERT (wrapped_);
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
}
}
update_PTT (on);
}
void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", f << state ());
if (UNK != m)
{
do_mode (m);
}
if (OmniRig::PM_FREQ & writable_params_)
{
rig_->SetFreq (f);
update_rx_frequency (f);
}
else if (reversed_ && (OmniRig::PM_FREQB & writable_params_))
{
rig_->SetFreqB (f);
update_rx_frequency (f);
}
else if (!reversed_ && (OmniRig::PM_FREQA & writable_params_))
{
rig_->SetFreqA (f);
update_rx_frequency (f);
}
else
{
throw_qstring (tr ("OmniRig: don't know how to set rig frequency"));
}
}
void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", tx << state ());
bool split {tx != 0};
if (split)
{
if (UNK != m)
{
do_mode (m);
if (OmniRig::PM_UNKNOWN == rig_->Vfo ())
{
if (writable_params_ & OmniRig::PM_VFOEQUAL)
{
// nothing to do here because OmniRig will use VFO
// equalize to set the mode of the Tx VFO for us
}
else if ((writable_params_ & (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
== (OmniRig::PM_VFOA | OmniRig::PM_VFOB))
{
rig_->SetVfo (OmniRig::PM_VFOB);
do_mode (m);
rig_->SetVfo (OmniRig::PM_VFOA);
}
else if (writable_params_ & OmniRig::PM_VFOSWAP)
{
rig_->SetVfo (OmniRig::PM_VFOSWAP);
do_mode (m);
rig_->SetVfo (OmniRig::PM_VFOSWAP);
}
}
}
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on");
rig_->SetSplitMode (state ().frequency (), tx);
update_other_frequency (tx);
update_split (true);
}
else
{
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off");
rig_->SetSimplexMode (state ().frequency ());
update_split (false);
}
bool notify {false};
if (readable_params_ & OmniRig::PM_FREQ || !(readable_params_ & (OmniRig::PM_FREQA | OmniRig::PM_FREQB)))
{
update_other_frequency (tx); // async updates won't return this
// so just store it and hope
// operator doesn't change the
// "back" VFO on rig
notify = true;
}
if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
{
TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually");
update_split (split); // we can't read it so just set and
// hope op doesn't change it
notify = true;
}
if (notify)
{
update_complete ();
}
}
void OmniRigTransceiver::do_mode (MODE mode)
{
TRACE_CAT ("OmniRigTransceiver", mode << state ());
// TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
auto mapped = map_mode (mode);
if (mapped & writable_params_)
{
rig_->SetMode (mapped);
update_mode (mode);
}
else
{
offline ("OmniRig invalid mode");
}
}

View File

@ -0,0 +1,74 @@
#ifndef POLLING_TRANSCEIVER_HPP__
#define POLLING_TRANSCEIVER_HPP__
#include <QObject>
#include "Transceiver/TransceiverBase.hpp"
class QTimer;
//
// Polling Transceiver
//
// Helper base class that encapsulates the emulation of continuous
// update and caching of a transceiver state.
//
// Collaborations
//
// Implements the TransceiverBase post action interface and provides
// the abstract poll() operation for sub-classes to implement. The
// poll operation is invoked every poll_interval seconds.
//
// Responsibilities
//
// Because some rig interfaces don't immediately update after a state
// change request; this class allows a rig a few polls to stabilise
// to the requested state before signalling the change. This means
// that clients don't see intermediate states that are sometimes
// inaccurate, e.g. changing the split TX frequency on Icom rigs
// requires a VFO switch and polls while switched will return the
// wrong current frequency.
//
class PollingTransceiver
: public TransceiverBase
{
Q_OBJECT; // for translation context
protected:
explicit PollingTransceiver (int poll_interval, // in seconds
QObject * parent);
protected:
// Sub-classes implement this and fetch what they can from the rig
// in a non-intrusive manner.
virtual void do_poll () = 0;
void do_post_start () override final;
void do_post_stop () override final;
void do_post_frequency (Frequency, MODE) override final;
void do_post_tx_frequency (Frequency, MODE) override final;
void do_post_mode (MODE) override final;
void do_post_ptt (bool = true) override final;
bool do_pre_update () override final;
private:
void start_timer ();
void stop_timer ();
Q_SLOT void handle_timeout ();
int interval_; // polling interval in milliseconds
QTimer * poll_timer_;
// keep a record of the last state signalled so we can elide
// duplicate updates
Transceiver::TransceiverState last_signalled_state_;
// keep a record of expected state so we can compare with actual
// updates to determine when state changes have bubbled through
Transceiver::TransceiverState next_state_;
unsigned retries_; // number of incorrect polls left
};
#endif

View File

@ -0,0 +1,591 @@
#include "MessageServer.hpp"
#include <stdexcept>
#include <limits>
#include <QNetworkInterface>
#include <QUdpSocket>
#include <QString>
#include <QTimer>
#include <QHash>
#include "Radio.hpp"
#include "Network/NetworkMessage.hpp"
#include "qt_helpers.hpp"
#include "pimpl_impl.hpp"
#include "moc_MessageServer.cpp"
namespace
{
auto quint32_max = std::numeric_limits<quint32>::max ();
}
class MessageServer::impl
: public QUdpSocket
{
Q_OBJECT;
public:
impl (MessageServer * self, QString const& version, QString const& revision)
: self_ {self}
, version_ {version}
, revision_ {revision}
, port_ {0u}
, clock_ {new QTimer {this}}
{
// register the required types with Qt
Radio::register_types ();
connect (this, &QIODevice::readyRead, this, &MessageServer::impl::pending_datagrams);
connect (this, static_cast<void (impl::*) (SocketError)> (&impl::error)
, [this] (SocketError /* e */)
{
Q_EMIT self_->error (errorString ());
});
connect (clock_, &QTimer::timeout, this, &impl::tick);
clock_->start (NetworkMessage::pulse * 1000);
}
enum StreamStatus {Fail, Short, OK};
void leave_multicast_group ();
void join_multicast_group ();
void parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg);
void tick ();
void pending_datagrams ();
StreamStatus check_status (QDataStream const&) const;
void send_message (QDataStream const& out, QByteArray const& message, QHostAddress const& address, port_type port)
{
if (OK == check_status (out))
{
writeDatagram (message, address, port);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
}
MessageServer * self_;
QString version_;
QString revision_;
port_type port_;
QHostAddress multicast_group_address_;
static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint;
struct Client
{
Client () = default;
Client (QHostAddress const& sender_address, port_type const& sender_port)
: sender_address_ {sender_address}
, sender_port_ {sender_port}
, negotiated_schema_number_ {2} // not 1 because it's broken
, last_activity_ {QDateTime::currentDateTime ()}
{
}
Client (Client const&) = default;
Client& operator= (Client const&) = default;
QHostAddress sender_address_;
port_type sender_port_;
quint32 negotiated_schema_number_;
QDateTime last_activity_;
};
QHash<QString, Client> clients_; // maps id to Client
QTimer * clock_;
};
MessageServer::impl::BindMode constexpr MessageServer::impl::bind_mode_;
#include "MessageServer.moc"
void MessageServer::impl::leave_multicast_group ()
{
if (!multicast_group_address_.isNull () && BoundState == state ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
{
for (auto const& interface : QNetworkInterface::allInterfaces ())
{
if (QNetworkInterface::CanMulticast & interface.flags ())
{
leaveMulticastGroup (multicast_group_address_, interface);
}
}
}
}
void MessageServer::impl::join_multicast_group ()
{
if (BoundState == state ()
&& !multicast_group_address_.isNull ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
{
auto mcast_iface = multicastInterface ();
if (IPv4Protocol == multicast_group_address_.protocol ()
&& IPv4Protocol != localAddress ().protocol ())
{
close ();
bind (QHostAddress::AnyIPv4, port_, bind_mode_);
}
bool joined {false};
for (auto const& interface : QNetworkInterface::allInterfaces ())
{
if (QNetworkInterface::CanMulticast & interface.flags ())
{
// Windows requires outgoing interface to match
// interface to be joined while joining, at least for
// IPv4 it seems to
setMulticastInterface (interface);
joined |= joinMulticastGroup (multicast_group_address_, interface);
}
}
if (!joined)
{
multicast_group_address_.clear ();
}
setMulticastInterface (mcast_iface);
}
}
void MessageServer::impl::pending_datagrams ()
{
while (hasPendingDatagrams ())
{
QByteArray datagram;
datagram.resize (pendingDatagramSize ());
QHostAddress sender_address;
port_type sender_port;
if (0 <= readDatagram (datagram.data (), datagram.size (), &sender_address, &sender_port))
{
parse_message (sender_address, sender_port, datagram);
}
}
}
void MessageServer::impl::parse_message (QHostAddress const& sender, port_type sender_port, QByteArray const& msg)
{
try
{
//
// message format is described in NetworkMessage.hpp
//
NetworkMessage::Reader in {msg};
auto id = in.id ();
if (OK == check_status (in))
{
if (!clients_.contains (id))
{
auto& client = (clients_[id] = {sender, sender_port});
QByteArray client_version;
QByteArray client_revision;
if (NetworkMessage::Heartbeat == in.type ())
{
// negotiate a working schema number
in >> client.negotiated_schema_number_;
if (OK == check_status (in))
{
auto sn = NetworkMessage::Builder::schema_number;
client.negotiated_schema_number_ = std::min (sn, client.negotiated_schema_number_);
// reply to the new client informing it of the
// negotiated schema number
QByteArray message;
NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id, client.negotiated_schema_number_};
hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
<< version_.toUtf8 () << revision_.toUtf8 ();
if (impl::OK == check_status (hb))
{
writeDatagram (message, client.sender_address_, client.sender_port_);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
}
// we don't care if this fails to read
in >> client_version >> client_revision;
}
Q_EMIT self_->client_opened (id, QString::fromUtf8 (client_version),
QString::fromUtf8 (client_revision));
}
clients_[id].last_activity_ = QDateTime::currentDateTime ();
//
// message format is described in NetworkMessage.hpp
//
switch (in.type ())
{
case NetworkMessage::Heartbeat:
//nothing to do here as time out handling deals with lifetime
break;
case NetworkMessage::Clear:
Q_EMIT self_->decodes_cleared (id);
break;
case NetworkMessage::Status:
{
// unpack message
Frequency f;
QByteArray mode;
QByteArray dx_call;
QByteArray report;
QByteArray tx_mode;
bool tx_enabled {false};
bool transmitting {false};
bool decoding {false};
quint32 rx_df {quint32_max};
quint32 tx_df {quint32_max};
QByteArray de_call;
QByteArray de_grid;
QByteArray dx_grid;
bool watchdog_timeout {false};
QByteArray sub_mode;
bool fast_mode {false};
quint8 special_op_mode {0};
quint32 frequency_tolerance {quint32_max};
quint32 tr_period {quint32_max};
QByteArray configuration_name;
in >> f >> mode >> dx_call >> report >> tx_mode >> tx_enabled >> transmitting >> decoding
>> rx_df >> tx_df >> de_call >> de_grid >> dx_grid >> watchdog_timeout >> sub_mode
>> fast_mode >> special_op_mode >> frequency_tolerance >> tr_period >> configuration_name;
if (check_status (in) != Fail)
{
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
, QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
, tx_enabled, transmitting, decoding, rx_df, tx_df
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
, QString::fromUtf8 (dx_grid), watchdog_timeout
, QString::fromUtf8 (sub_mode), fast_mode
, special_op_mode, frequency_tolerance, tr_period
, QString::fromUtf8 (configuration_name));
}
}
break;
case NetworkMessage::Decode:
{
// unpack message
bool is_new {true};
QTime time;
qint32 snr;
float delta_time;
quint32 delta_frequency;
QByteArray mode;
QByteArray message;
bool low_confidence {false};
bool off_air {false};
in >> is_new >> time >> snr >> delta_time >> delta_frequency >> mode
>> message >> low_confidence >> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->decode (is_new, id, time, snr, delta_time, delta_frequency
, QString::fromUtf8 (mode), QString::fromUtf8 (message)
, low_confidence, off_air);
}
}
break;
case NetworkMessage::WSPRDecode:
{
// unpack message
bool is_new {true};
QTime time;
qint32 snr;
float delta_time;
Frequency frequency;
qint32 drift;
QByteArray callsign;
QByteArray grid;
qint32 power;
bool off_air {false};
in >> is_new >> time >> snr >> delta_time >> frequency >> drift >> callsign >> grid >> power
>> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->WSPR_decode (is_new, id, time, snr, delta_time, frequency, drift
, QString::fromUtf8 (callsign), QString::fromUtf8 (grid)
, power, off_air);
}
}
break;
case NetworkMessage::QSOLogged:
{
QDateTime time_off;
QByteArray dx_call;
QByteArray dx_grid;
Frequency dial_frequency;
QByteArray mode;
QByteArray report_sent;
QByteArray report_received;
QByteArray tx_power;
QByteArray comments;
QByteArray name;
QDateTime time_on; // Note: LOTW uses TIME_ON for their +/- 30-minute time window
QByteArray operator_call;
QByteArray my_call;
QByteArray my_grid;
QByteArray exchange_sent;
QByteArray exchange_rcvd;
in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received
>> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid
>> exchange_sent >> exchange_rcvd;
if (check_status (in) != Fail)
{
Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
, dial_frequency, QString::fromUtf8 (mode), QString::fromUtf8 (report_sent)
, QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power)
, QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
, QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
, QString::fromUtf8 (my_grid), QString::fromUtf8 (exchange_sent)
, QString::fromUtf8 (exchange_rcvd));
}
}
break;
case NetworkMessage::Close:
Q_EMIT self_->client_closed (id);
clients_.remove (id);
break;
case NetworkMessage::LoggedADIF:
{
QByteArray ADIF;
in >> ADIF;
if (check_status (in) != Fail)
{
Q_EMIT self_->logged_ADIF (id, ADIF);
}
}
break;
default:
// Ignore
break;
}
}
else
{
Q_EMIT self_->error ("MessageServer warning: invalid UDP message received");
}
}
catch (std::exception const& e)
{
Q_EMIT self_->error (QString {"MessageServer exception: %1"}.arg (e.what ()));
}
catch (...)
{
Q_EMIT self_->error ("Unexpected exception in MessageServer");
}
}
void MessageServer::impl::tick ()
{
auto now = QDateTime::currentDateTime ();
auto iter = std::begin (clients_);
while (iter != std::end (clients_))
{
if (now > (*iter).last_activity_.addSecs (NetworkMessage::pulse))
{
Q_EMIT self_->clear_decodes (iter.key ());
Q_EMIT self_->client_closed (iter.key ());
iter = clients_.erase (iter); // safe while iterating as doesn't rehash
}
else
{
++iter;
}
}
}
auto MessageServer::impl::check_status (QDataStream const& stream) const -> StreamStatus
{
auto stat = stream.status ();
StreamStatus result {Fail};
switch (stat)
{
case QDataStream::ReadPastEnd:
result = Short;
break;
case QDataStream::ReadCorruptData:
Q_EMIT self_->error ("Message serialization error: read corrupt data");
break;
case QDataStream::WriteFailed:
Q_EMIT self_->error ("Message serialization error: write error");
break;
default:
result = OK;
break;
}
return result;
}
MessageServer::MessageServer (QObject * parent, QString const& version, QString const& revision)
: QObject {parent}
, m_ {this, version, revision}
{
}
void MessageServer::start (port_type port, QHostAddress const& multicast_group_address)
{
if (port != m_->port_
|| multicast_group_address != m_->multicast_group_address_)
{
m_->leave_multicast_group ();
if (impl::BoundState == m_->state ())
{
m_->close ();
}
m_->multicast_group_address_ = multicast_group_address;
auto address = m_->multicast_group_address_.isNull ()
|| impl::IPv4Protocol != m_->multicast_group_address_.protocol () ? QHostAddress::Any : QHostAddress::AnyIPv4;
if (port && m_->bind (address, port, m_->bind_mode_))
{
m_->port_ = port;
m_->join_multicast_group ();
}
else
{
m_->port_ = 0;
}
}
}
void MessageServer::clear_decodes (QString const& id, quint8 window)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Clear, id, (*iter).negotiated_schema_number_};
out << window;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode
, QString const& message_text, bool low_confidence, quint8 modifiers)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Reply, id, (*iter).negotiated_schema_number_};
out << time << snr << delta_time << delta_frequency << mode.toUtf8 ()
<< message_text.toUtf8 () << low_confidence << modifiers;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::replay (QString const& id)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Replay, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::close (QString const& id)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Close, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::halt_tx (QString const& id, bool auto_only)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, id, (*iter).negotiated_schema_number_};
out << auto_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::free_text (QString const& id, QString const& text, bool send)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, id, (*iter).negotiated_schema_number_};
out << text.toUtf8 () << send;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::location (QString const& id, QString const& loc)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Location, id, (*iter).negotiated_schema_number_};
out << loc.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::highlight_callsign (QString const& id, QString const& callsign
, QColor const& bg, QColor const& fg, bool last_only)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, id, (*iter).negotiated_schema_number_};
out << callsign.toUtf8 () << bg << fg << last_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::switch_configuration (QString const& id, QString const& configuration_name)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, id, (*iter).negotiated_schema_number_};
out << configuration_name.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}
void MessageServer::configure (QString const& id, QString const& mode, quint32 frequency_tolerance
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
, QString const& dx_call, QString const& dx_grid, bool generate_messages)
{
auto iter = m_->clients_.find (id);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Configure, id, (*iter).negotiated_schema_number_};
out << mode.toUtf8 () << frequency_tolerance << submode.toUtf8 () << fast_mode << tr_period << rx_df
<< dx_call.toUtf8 () << dx_grid.toUtf8 () << generate_messages;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
}
}

View File

@ -147,7 +147,7 @@ public:
: server_ {new MessageServer {this}}
{
// connect up server
connect (server_, &MessageServer::error, [this] (QString const& message) {
connect (server_, &MessageServer::error, [] (QString const& message) {
std::cerr << tr ("Network Error: %1").arg ( message).toStdString () << std::endl;
});
connect (server_, &MessageServer::client_opened, this, &Server::add_client);

View File

@ -1,8 +0,0 @@
See ./index.html for information about this release. The "Getting Started"
section is a useful starting place.
---------------------------
Copyright Beman Dawes, 2008
Distributed under the Boost Software License, Version 1.0.
See ./LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt

View File

@ -1,312 +0,0 @@
# Copyright Vladimir Prus 2002-2006.
# Copyright Dave Abrahams 2005-2006.
# Copyright Rene Rivera 2005-2007.
# Copyright Douglas Gregor 2005.
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# Usage:
#
# b2 [options] [properties] [install|stage]
#
# Builds and installs Boost.
#
# Targets and Related Options:
#
# install Install headers and compiled library files to the
# ======= configured locations (below).
#
# --prefix=<PREFIX> Install architecture independent files here.
# Default; C:\Boost on Win32
# Default; /usr/local on Unix. Linux, etc.
#
# --exec-prefix=<EPREFIX> Install architecture dependent files here.
# Default; <PREFIX>
#
# --libdir=<DIR> Install library files here.
# Default; <EPREFIX>/lib
#
# --includedir=<HDRDIR> Install header files here.
# Default; <PREFIX>/include
#
# stage Build and install only compiled library files to the
# ===== stage directory.
#
# --stagedir=<STAGEDIR> Install library files here
# Default; ./stage
#
# Other Options:
#
# --build-type=<type> Build the specified pre-defined set of variations of
# the libraries. Note, that which variants get built
# depends on what each library supports.
#
# -- minimal -- (default) Builds a minimal set of
# variants. On Windows, these are static
# multithreaded libraries in debug and release
# modes, using shared runtime. On Linux, these are
# static and shared multithreaded libraries in
# release mode.
#
# -- complete -- Build all possible variations.
#
# --build-dir=DIR Build in this location instead of building within
# the distribution tree. Recommended!
#
# --show-libraries Display the list of Boost libraries that require
# build and installation steps, and then exit.
#
# --layout=<layout> Determine whether to choose library names and header
# locations such that multiple versions of Boost or
# multiple compilers can be used on the same system.
#
# -- versioned -- Names of boost binaries include
# the Boost version number, name and version of
# the compiler and encoded build properties. Boost
# headers are installed in a subdirectory of
# <HDRDIR> whose name contains the Boost version
# number.
#
# -- tagged -- Names of boost binaries include the
# encoded build properties such as variant and
# threading, but do not including compiler name
# and version, or Boost version. This option is
# useful if you build several variants of Boost,
# using the same compiler.
#
# -- system -- Binaries names do not include the
# Boost version number or the name and version
# number of the compiler. Boost headers are
# installed directly into <HDRDIR>. This option is
# intended for system integrators building
# distribution packages.
#
# The default value is 'versioned' on Windows, and
# 'system' on Unix.
#
# --buildid=ID Add the specified ID to the name of built libraries.
# The default is to not add anything.
#
# --python-buildid=ID Add the specified ID to the name of built libraries
# that depend on Python. The default is to not add
# anything. This ID is added in addition to --buildid.
#
# --help This message.
#
# --with-<library> Build and install the specified <library>. If this
# option is used, only libraries specified using this
# option will be built.
#
# --without-<library> Do not build, stage, or install the specified
# <library>. By default, all libraries are built.
#
# Properties:
#
# toolset=toolset Indicate the toolset to build with.
#
# variant=debug|release Select the build variant
#
# link=static|shared Whether to build static or shared libraries
#
# threading=single|multi Whether to build single or multithreaded binaries
#
# runtime-link=static|shared
# Whether to link to static or shared C and C++
# runtime.
#
# TODO:
# - handle boost version
# - handle python options such as pydebug
import boostcpp ;
import package ;
import sequence ;
import xsltproc ;
import set ;
import path ;
import link ;
path-constant BOOST_ROOT : . ;
constant BOOST_VERSION : 1.63.0 ;
constant BOOST_JAMROOT_MODULE : $(__name__) ;
boostcpp.set-version $(BOOST_VERSION) ;
use-project /boost/architecture : libs/config/checks/architecture ;
local all-headers =
[ MATCH .*libs/(.*)/include/boost : [ glob libs/*/include/boost libs/*/*/include/boost ] ] ;
for dir in $(all-headers)
{
link-directory $(dir)-headers : libs/$(dir)/include/boost : <location>. ;
explicit $(dir)-headers ;
}
if $(all-headers)
{
constant BOOST_MODULARLAYOUT : $(all-headers) ;
}
project boost
: requirements <include>.
[ boostcpp.architecture ]
[ boostcpp.address-model ]
# Disable auto-linking for all targets here, primarily because it caused
# troubles with V2.
<define>BOOST_ALL_NO_LIB=1
# Used to encode variant in target name. See the 'tag' rule below.
<tag>@$(__name__).tag
<conditional>@handle-static-runtime
# Comeau does not support shared lib
<toolset>como:<link>static
<toolset>como-linux:<define>_GNU_SOURCE=1
# When building docs within Boost, we want the standard Boost style
<xsl:param>boost.defaults=Boost
: usage-requirements <include>.
: build-dir bin.v2
;
# This rule is called by Boost.Build to determine the name of target. We use it
# to encode the build variant, compiler name and boost version in the target
# name.
#
rule tag ( name : type ? : property-set )
{
return [ boostcpp.tag $(name) : $(type) : $(property-set) ] ;
}
rule python-tag ( name : type ? : property-set )
{
return [ boostcpp.python-tag $(name) : $(type) : $(property-set) ] ;
}
rule handle-static-runtime ( properties * )
{
# Using static runtime with shared libraries is impossible on Linux, and
# dangerous on Windows. Therefore, we disallow it. This might be drastic,
# but it was disabled for a while without anybody complaining.
# For CW, static runtime is needed so that std::locale works.
if <link>shared in $(properties) && <runtime-link>static in $(properties) &&
! ( <toolset>cw in $(properties) )
{
ECHO "error: link=shared together with runtime-link=static is not allowed" ;
ECHO "error: such property combination is either impossible " ;
ECHO "error: or too dangerious to be of any use" ;
EXIT ;
}
}
all-libraries = [ MATCH .*libs/(.*)/build/.* : [ glob libs/*/build/Jamfile.v2 ]
[ glob libs/*/build/Jamfile ] ] ;
all-libraries = [ sequence.unique $(all-libraries) ] ;
# The function_types library has a Jamfile, but it's used for maintenance
# purposes, there's no library to build and install.
all-libraries = [ set.difference $(all-libraries) : function_types ] ;
# Setup convenient aliases for all libraries.
local rule explicit-alias ( id : targets + )
{
alias $(id) : $(targets) ;
explicit $(id) ;
}
# First, the complicated libraries: where the target name in Jamfile is
# different from its directory name.
explicit-alias prg_exec_monitor : libs/test/build//boost_prg_exec_monitor ;
explicit-alias test_exec_monitor : libs/test/build//boost_test_exec_monitor ;
explicit-alias unit_test_framework : libs/test/build//boost_unit_test_framework ;
explicit-alias bgl-vis : libs/graps/build//bgl-vis ;
explicit-alias serialization : libs/serialization/build//boost_serialization ;
explicit-alias wserialization : libs/serialization/build//boost_wserialization ;
for local l in $(all-libraries)
{
if ! $(l) in test graph serialization
{
explicit-alias $(l) : libs/$(l)/build//boost_$(l) ;
}
}
# Log has an additional target
explicit-alias log_setup : libs/log/build//boost_log_setup ;
alias headers : $(all-headers)-headers : : : <include>. ;
explicit headers ;
# Make project ids of all libraries known.
for local l in $(all-libraries)
{
use-project /boost/$(l) : libs/$(l)/build ;
}
if [ path.exists $(BOOST_ROOT)/tools/inspect ]
{
use-project /boost/tools/inspect : tools/inspect/build ;
}
if [ path.exists $(BOOST_ROOT)/libs/wave/tool ]
{
use-project /boost/libs/wave/tool : libs/wave/tool/build ;
}
# This rule should be called from libraries' Jamfiles and will create two
# targets, "install" and "stage", that will install or stage that library. The
# --prefix option is respected, but --with and --without options, naturally, are
# ignored.
#
# - libraries -- list of library targets to install.
#
rule boost-install ( libraries * )
{
package.install install
: <dependency>/boost//install-proper-headers $(install-requirements)
: # No binaries
: $(libraries)
: # No headers, it is handled by the dependency.
;
install stage : $(libraries) : <location>$(BOOST_STAGE_LOCATE) ;
module [ CALLER_MODULE ]
{
explicit stage ;
explicit install ;
}
}
# Creates a library target, adding autolink support and also creates
# stage and install targets via boost-install, above.
rule boost-lib ( name : sources * : requirements * : default-build * : usage-requirements * )
{
name = boost_$(name) ;
autolink = <link>shared:<define>BOOST_$(name:U)_DYN_LINK=1 ;
lib $(name)
: $(sources)
: $(requirements) $(autolink)
: $(default-build)
: $(usage-requirements) $(autolink)
;
boost-install $(name) ;
}
headers =
# The .SUNWCCh files are present in tr1 include directory and have to be
# installed (see http://lists.boost.org/Archives/boost/2007/05/121430.php).
[ path.glob-tree $(BOOST_ROOT)/boost : *.hpp *.ipp *.h *.inc *.SUNWCCh : CVS .svn ]
[ path.glob-tree $(BOOST_ROOT)/boost/compatibility/cpp_c_headers : c* : CVS .svn ]
[ path.glob boost/tr1/tr1 : * : bcc32 sun CVS .svn ]
;
# Declare special top-level targets that build and install the desired variants
# of the libraries.
boostcpp.declare-targets $(all-libraries) : $(headers) ;

View File

@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -7,11 +7,18 @@ upstream and master. To upgrade the content do the following:
```bash
git checkout upstream
mv README.md /tmp
rm -r *
# use the bcp tool to populate with the new Boost libraries
# use git add to stage any new files and directories
git commit -a -m "Updated Boost v1.63 libraries including ..."
git tag boost_1_63
mv /tmp/README.md .
# use the bcp tool to populate with the new Boost libraries from a clean boost install.
# Something like:
#
# bcp --boost=../boost_1_70_0 --unix-lines iterator range math numeric crc circular_buffer multi_index intrusive .
#
# Clean out any unwanted files and directories (e.g. libs and docs for a header only subset).
# Use git add to stage any new files and directories.
git commit -a -m "Updated Boost v1.70.0 libraries including ..."
git tag boost_1_70_0
git push origin
git checkout master
git merge upstream
@ -30,4 +37,4 @@ git-subtree-pull to import the changes like this:
```bash
git remote add -f boost git@bitbucket.org:g4wjs/boost.git # for convienence
git subtree pull --prefix boost boost master --squash
```
```

View File

@ -1,17 +0,0 @@
# Copyright (C) 2002-2003 David Abrahams.
# Copyright (C) 2002-2003 Vladimir Prus.
# Copyright (C) 2003,2007 Rene Rivera.
# Use, modification and distribution are subject to the
# Boost Software License, Version 1.0. (See accompanying file
# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# This is the initial file loaded by Boost Jam when run from any Boost library
# folder. It allows us to choose which Boost Build installation to use for
# building Boost libraries. Unless explicitly selected using a command-line
# option, the version included with the Boost library distribution is used (as
# opposed to any other Boost Build version installed on the user's sytem).
BOOST_ROOT = $(.boost-build-file:D) ;
BOOST_BUILD = [ MATCH --boost-build=(.*) : $(ARGV) ] ;
BOOST_BUILD ?= tools/build/src ;
boost-build $(BOOST_BUILD) ;

View File

@ -1,66 +0,0 @@
/*=============================================================================
Copyright 2002 William E. Kempf
Distributed under the Boost Software License, Version 1.0. (See accompany-
ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
H1
{
FONT-SIZE: 200%;
COLOR: #00008B;
}
H2
{
FONT-SIZE: 150%;
}
H3
{
FONT-SIZE: 125%;
}
H4
{
FONT-SIZE: 108%;
}
BODY
{
FONT-SIZE: 100%;
BACKGROUND-COLOR: #ffffff;
COLOR: #000000;
}
PRE
{
MARGIN-LEFT: 2em;
FONT-FAMILY: Courier,
monospace;
}
CODE
{
FONT-FAMILY: Courier,
monospace;
}
CODE.as_pre
{
white-space: pre;
}
.index
{
TEXT-ALIGN: left;
}
.page-index
{
TEXT-ALIGN: left;
}
.definition
{
TEXT-ALIGN: left;
}
.footnote
{
FONT-SIZE: 66%;
VERTICAL-ALIGN: super;
TEXT-DECORATION: none;
}
.function-semantics
{
CLEAR: left;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -27,7 +27,7 @@ namespace boost { namespace algorithm {
///
/// \note This function is part of the C++2011 standard library.
template<typename InputIterator, typename Predicate>
bool all_of ( InputIterator first, InputIterator last, Predicate p )
BOOST_CXX14_CONSTEXPR bool all_of ( InputIterator first, InputIterator last, Predicate p )
{
for ( ; first != last; ++first )
if ( !p(*first))
@ -43,7 +43,7 @@ bool all_of ( InputIterator first, InputIterator last, Predicate p )
/// \param p A predicate for testing the elements of the range
///
template<typename Range, typename Predicate>
bool all_of ( const Range &r, Predicate p )
BOOST_CXX14_CONSTEXPR bool all_of ( const Range &r, Predicate p )
{
return boost::algorithm::all_of ( boost::begin (r), boost::end (r), p );
}
@ -57,7 +57,7 @@ bool all_of ( const Range &r, Predicate p )
/// \param val A value to compare against
///
template<typename InputIterator, typename T>
bool all_of_equal ( InputIterator first, InputIterator last, const T &val )
BOOST_CXX14_CONSTEXPR bool all_of_equal ( InputIterator first, InputIterator last, const T &val )
{
for ( ; first != last; ++first )
if ( val != *first )
@ -73,7 +73,7 @@ bool all_of_equal ( InputIterator first, InputIterator last, const T &val )
/// \param val A value to compare against
///
template<typename Range, typename T>
bool all_of_equal ( const Range &r, const T &val )
BOOST_CXX14_CONSTEXPR bool all_of_equal ( const Range &r, const T &val )
{
return boost::algorithm::all_of_equal ( boost::begin (r), boost::end (r), val );
}

View File

@ -0,0 +1,553 @@
// (C) Copyright Herve Bronnimann 2004.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/*
Revision history:
1 July 2004
Split the code into two headers to lessen dependence on
Boost.tuple. (Herve)
26 June 2004
Added the code for the boost minmax library. (Herve)
*/
#ifndef BOOST_ALGORITHM_MINMAX_ELEMENT_HPP
#define BOOST_ALGORITHM_MINMAX_ELEMENT_HPP
/* PROPOSED STANDARD EXTENSIONS:
*
* minmax_element(first, last)
* Effect: std::make_pair( std::min_element(first, last),
* std::max_element(first, last) );
*
* minmax_element(first, last, comp)
* Effect: std::make_pair( std::min_element(first, last, comp),
* std::max_element(first, last, comp) );
*/
#include <utility> // for std::pair and std::make_pair
namespace boost {
namespace detail { // for obtaining a uniform version of minmax_element
// that compiles with VC++ 6.0 -- avoid the iterator_traits by
// having comparison object over iterator, not over dereferenced value
template <typename Iterator>
struct less_over_iter {
bool operator()(Iterator const& it1,
Iterator const& it2) const { return *it1 < *it2; }
};
template <typename Iterator, class BinaryPredicate>
struct binary_pred_over_iter {
explicit binary_pred_over_iter(BinaryPredicate const& p ) : m_p( p ) {}
bool operator()(Iterator const& it1,
Iterator const& it2) const { return m_p(*it1, *it2); }
private:
BinaryPredicate m_p;
};
// common base for the two minmax_element overloads
template <typename ForwardIter, class Compare >
std::pair<ForwardIter,ForwardIter>
basic_minmax_element(ForwardIter first, ForwardIter last, Compare comp)
{
if (first == last)
return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
// if only one element
ForwardIter second = first; ++second;
if (second == last)
return std::make_pair(min_result, max_result);
// treat first pair separately (only one comparison for first two elements)
ForwardIter potential_min_result = last;
if (comp(first, second))
max_result = second;
else {
min_result = second;
potential_min_result = first;
}
// then each element by pairs, with at most 3 comparisons per pair
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (comp(first, min_result)) {
min_result = first;
potential_min_result = last;
}
if (comp(max_result, second))
max_result = second;
} else {
if (comp(second, min_result)) {
min_result = second;
potential_min_result = first;
}
if (comp(max_result, first))
max_result = first;
}
first = ++second;
if (first != last) ++second;
}
// if odd number of elements, treat last element
if (first != last) { // odd number of elements
if (comp(first, min_result)) {
min_result = first;
potential_min_result = last;
}
else if (comp(max_result, first))
max_result = first;
}
// resolve min_result being incorrect with one extra comparison
// (in which case potential_min_result is necessarily the correct result)
if (potential_min_result != last
&& !comp(min_result, potential_min_result))
min_result = potential_min_result;
return std::make_pair(min_result,max_result);
}
} // namespace detail
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
minmax_element(ForwardIter first, ForwardIter last)
{
return detail::basic_minmax_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
minmax_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_minmax_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
}
/* PROPOSED BOOST EXTENSIONS
* In the description below, [rfirst,rlast) denotes the reversed range
* of [first,last). Even though the iterator type of first and last may
* be only a Forward Iterator, it is possible to explain the semantics
* by assuming that it is a Bidirectional Iterator. In the sequel,
* reverse(ForwardIterator&) returns the reverse_iterator adaptor.
* This is not how the functions would be implemented!
*
* first_min_element(first, last)
* Effect: std::min_element(first, last);
*
* first_min_element(first, last, comp)
* Effect: std::min_element(first, last, comp);
*
* last_min_element(first, last)
* Effect: reverse( std::min_element(reverse(last), reverse(first)) );
*
* last_min_element(first, last, comp)
* Effect: reverse( std::min_element(reverse(last), reverse(first), comp) );
*
* first_max_element(first, last)
* Effect: std::max_element(first, last);
*
* first_max_element(first, last, comp)
* Effect: max_element(first, last);
*
* last_max_element(first, last)
* Effect: reverse( std::max_element(reverse(last), reverse(first)) );
*
* last_max_element(first, last, comp)
* Effect: reverse( std::max_element(reverse(last), reverse(first), comp) );
*
* first_min_first_max_element(first, last)
* Effect: std::make_pair( first_min_element(first, last),
* first_max_element(first, last) );
*
* first_min_first_max_element(first, last, comp)
* Effect: std::make_pair( first_min_element(first, last, comp),
* first_max_element(first, last, comp) );
*
* first_min_last_max_element(first, last)
* Effect: std::make_pair( first_min_element(first, last),
* last_max_element(first, last) );
*
* first_min_last_max_element(first, last, comp)
* Effect: std::make_pair( first_min_element(first, last, comp),
* last_max_element(first, last, comp) );
*
* last_min_first_max_element(first, last)
* Effect: std::make_pair( last_min_element(first, last),
* first_max_element(first, last) );
*
* last_min_first_max_element(first, last, comp)
* Effect: std::make_pair( last_min_element(first, last, comp),
* first_max_element(first, last, comp) );
*
* last_min_last_max_element(first, last)
* Effect: std::make_pair( last_min_element(first, last),
* last_max_element(first, last) );
*
* last_min_last_max_element(first, last, comp)
* Effect: std::make_pair( last_min_element(first, last, comp),
* last_max_element(first, last, comp) );
*/
namespace boost {
// Min_element and max_element variants
namespace detail { // common base for the overloads
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_first_min_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter min_result = first;
while (++first != last)
if (comp(first, min_result))
min_result = first;
return min_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_last_min_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter min_result = first;
while (++first != last)
if (!comp(min_result, first))
min_result = first;
return min_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter max_result = first;
while (++first != last)
if (comp(max_result, first))
max_result = first;
return max_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter max_result = first;
while (++first != last)
if (!comp(first, max_result))
max_result = first;
return max_result;
}
} // namespace detail
template <typename ForwardIter>
ForwardIter
first_min_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_min_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
first_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_first_min_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
last_min_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
last_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_last_min_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
first_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
first_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_first_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
last_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
// Minmax_element variants -- comments removed
namespace detail {
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_first_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last)
return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = ++first;
if (second == last)
return std::make_pair(min_result, max_result);
if (comp(second, min_result))
min_result = second;
else
max_result = second;
first = ++second; if (first != last) ++second;
while (second != last) {
if (!comp(second, first)) {
if (comp(first, min_result))
min_result = first;
if (!comp(second, max_result))
max_result = second;
} else {
if (comp(second, min_result))
min_result = second;
if (!comp(first, max_result))
max_result = first;
}
first = ++second; if (first != last) ++second;
}
if (first != last) {
if (comp(first, min_result))
min_result = first;
else if (!comp(first, max_result))
max_result = first;
}
return std::make_pair(min_result, max_result);
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_last_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = ++first;
if (second == last)
return std::make_pair(min_result, max_result);
if (comp(max_result, second))
max_result = second;
else
min_result = second;
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (!comp(min_result, first))
min_result = first;
if (comp(max_result, second))
max_result = second;
} else {
if (!comp(min_result, second))
min_result = second;
if (comp(max_result, first))
max_result = first;
}
first = ++second; if (first != last) ++second;
}
if (first != last) {
if (!comp(min_result, first))
min_result = first;
else if (comp(max_result, first))
max_result = first;
}
return std::make_pair(min_result, max_result);
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_last_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = first; ++second;
if (second == last)
return std::make_pair(min_result,max_result);
ForwardIter potential_max_result = last;
if (comp(first, second))
max_result = second;
else {
min_result = second;
potential_max_result = second;
}
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (!comp(min_result, first))
min_result = first;
if (!comp(second, max_result)) {
max_result = second;
potential_max_result = last;
}
} else {
if (!comp(min_result, second))
min_result = second;
if (!comp(first, max_result)) {
max_result = first;
potential_max_result = second;
}
}
first = ++second;
if (first != last) ++second;
}
if (first != last) {
if (!comp(min_result, first))
min_result = first;
if (!comp(first, max_result)) {
max_result = first;
potential_max_result = last;
}
}
if (potential_max_result != last
&& !comp(potential_max_result, max_result))
max_result = potential_max_result;
return std::make_pair(min_result,max_result);
}
} // namespace detail
template <typename ForwardIter>
inline std::pair<ForwardIter,ForwardIter>
first_min_first_max_element(ForwardIter first, ForwardIter last)
{
return minmax_element(first, last);
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
first_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return minmax_element(first, last, comp);
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
first_min_last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_min_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
first_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_first_min_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
last_min_first_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_first_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
last_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_last_min_first_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
last_min_last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
last_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_last_min_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
} // namespace boost
#endif // BOOST_ALGORITHM_MINMAX_ELEMENT_HPP

View File

@ -30,8 +30,10 @@ namespace boost {
// a tolower functor
template<typename CharT>
struct to_lowerF : public std::unary_function<CharT, CharT>
struct to_lowerF
{
typedef CharT argument_type;
typedef CharT result_type;
// Constructor
to_lowerF( const std::locale& Loc ) : m_Loc( &Loc ) {}
@ -50,8 +52,10 @@ namespace boost {
// a toupper functor
template<typename CharT>
struct to_upperF : public std::unary_function<CharT, CharT>
struct to_upperF
{
typedef CharT argument_type;
typedef CharT result_type;
// Constructor
to_upperF( const std::locale& Loc ) : m_Loc( &Loc ) {}

View File

@ -40,7 +40,7 @@ namespace boost {
// Protected construction/destruction
// Default constructor
find_iterator_base() {};
find_iterator_base() {}
// Copy construction
find_iterator_base( const find_iterator_base& Other ) :
m_Finder(Other.m_Finder) {}

View File

@ -89,9 +89,10 @@ namespace boost {
template<
typename SeqT,
typename IteratorT=BOOST_STRING_TYPENAME SeqT::const_iterator >
struct copy_iterator_rangeF :
public std::unary_function< iterator_range<IteratorT>, SeqT >
struct copy_iterator_rangeF
{
typedef iterator_range<IteratorT> argument_type;
typedef SeqT result_type;
SeqT operator()( const iterator_range<IteratorT>& Range ) const
{
return copy_range<SeqT>(Range);

View File

@ -43,7 +43,6 @@ namespace boost {
The result is given as an \c iterator_range delimiting the match.
\param Search A substring to be searched for.
\param Comp An element comparison predicate
\return An instance of the \c first_finder object
*/
template<typename RangeT>
@ -84,7 +83,6 @@ namespace boost {
The result is given as an \c iterator_range delimiting the match.
\param Search A substring to be searched for.
\param Comp An element comparison predicate
\return An instance of the \c last_finder object
*/
template<typename RangeT>
@ -124,7 +122,6 @@ namespace boost {
\param Search A substring to be searched for.
\param Nth An index of the match to be find
\param Comp An element comparison predicate
\return An instance of the \c nth_finder object
*/
template<typename RangeT>
@ -230,7 +227,6 @@ namespace boost {
\param Begin Beginning of the range
\param End End of the range
\param Range The range.
\return An instance of the \c range_finger object
*/
template< typename ForwardIteratorT >

View File

@ -1,20 +0,0 @@
/*
(c) 2014-2015 Glen Joseph Fernandes
<glenjofe -at- gmail.com>
Distributed under the Boost Software
License, Version 1.0.
http://boost.org/LICENSE_1_0.txt
*/
#ifndef BOOST_ALIGN_ALIGN_HPP
#define BOOST_ALIGN_ALIGN_HPP
#include <boost/config.hpp>
#if !defined(BOOST_NO_CXX11_STD_ALIGN)
#include <boost/align/detail/align_cxx11.hpp>
#else
#include <boost/align/detail/align.hpp>
#endif
#endif

View File

@ -1,38 +0,0 @@
/*
(c) 2014-2016 Glen Joseph Fernandes
<glenjofe -at- gmail.com>
Distributed under the Boost Software
License, Version 1.0.
http://boost.org/LICENSE_1_0.txt
*/
#ifndef BOOST_ALIGN_DETAIL_ALIGN_HPP
#define BOOST_ALIGN_DETAIL_ALIGN_HPP
#include <boost/align/detail/is_alignment.hpp>
#include <boost/assert.hpp>
namespace boost {
namespace alignment {
inline void* align(std::size_t alignment, std::size_t size,
void*& ptr, std::size_t& space)
{
BOOST_ASSERT(detail::is_alignment(alignment));
if (size <= space) {
char* p = (char*)(((std::size_t)ptr + alignment - 1) &
~(alignment - 1));
std::size_t n = space - (p - static_cast<char*>(ptr));
if (size <= n) {
ptr = p;
space = n;
return p;
}
}
return 0;
}
} /* .alignment */
} /* .boost */
#endif

View File

@ -1,22 +0,0 @@
/*
(c) 2014 Glen Joseph Fernandes
<glenjofe -at- gmail.com>
Distributed under the Boost Software
License, Version 1.0.
http://boost.org/LICENSE_1_0.txt
*/
#ifndef BOOST_ALIGN_DETAIL_ALIGN_CXX11_HPP
#define BOOST_ALIGN_DETAIL_ALIGN_CXX11_HPP
#include <memory>
namespace boost {
namespace alignment {
using std::align;
} /* .alignment */
} /* .boost */
#endif

View File

@ -1,29 +0,0 @@
/*
(c) 2014 Glen Joseph Fernandes
<glenjofe -at- gmail.com>
Distributed under the Boost Software
License, Version 1.0.
http://boost.org/LICENSE_1_0.txt
*/
#ifndef BOOST_ALIGN_DETAIL_IS_ALIGNMENT_HPP
#define BOOST_ALIGN_DETAIL_IS_ALIGNMENT_HPP
#include <boost/config.hpp>
#include <cstddef>
namespace boost {
namespace alignment {
namespace detail {
BOOST_CONSTEXPR inline bool is_alignment(std::size_t value)
BOOST_NOEXCEPT
{
return (value > 0) && ((value & (value - 1)) == 0);
}
} /* .detail */
} /* .alignment */
} /* .boost */
#endif

View File

@ -12,11 +12,11 @@
// with features contributed and bugs found by
// Antony Polukhin, Ed Brey, Mark Rodgers,
// Peter Dimov, and James Curran
// when: July 2001, April 2013 - May 2013
// when: July 2001, April 2013 - 2019
#include <algorithm>
#include "boost/config.hpp"
#include <boost/config.hpp>
#include <boost/type_index.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/decay.hpp>
@ -27,9 +27,10 @@
#include <boost/throw_exception.hpp>
#include <boost/static_assert.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/core/addressof.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/is_const.hpp>
#include <boost/mpl/if.hpp>
#include <boost/type_traits/conditional.hpp>
namespace boost
{
@ -108,7 +109,7 @@ namespace boost
return *this;
}
// move assignement
// move assignment
any & operator=(any&& rhs) BOOST_NOEXCEPT
{
rhs.swap(*this);
@ -148,7 +149,7 @@ namespace boost
public: // types (public so any_cast can be non-friend)
#endif
class placeholder
class BOOST_SYMBOL_VISIBLE placeholder
{
public: // structors
@ -244,7 +245,9 @@ namespace boost
ValueType * any_cast(any * operand) BOOST_NOEXCEPT
{
return operand && operand->type() == boost::typeindex::type_id<ValueType>()
? &static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
? boost::addressof(
static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
)
: 0;
}
@ -260,7 +263,7 @@ namespace boost
typedef BOOST_DEDUCED_TYPENAME remove_reference<ValueType>::type nonref;
nonref * result = any_cast<nonref>(&operand);
nonref * result = any_cast<nonref>(boost::addressof(operand));
if(!result)
boost::throw_exception(bad_any_cast());
@ -268,13 +271,20 @@ namespace boost
// `ValueType` is not a reference. Example:
// `static_cast<std::string>(*result);`
// which is equal to `std::string(*result);`
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_<
boost::is_reference<ValueType>,
typedef BOOST_DEDUCED_TYPENAME boost::conditional<
boost::is_reference<ValueType>::value,
ValueType,
BOOST_DEDUCED_TYPENAME boost::add_reference<ValueType>::type
>::type ref_type;
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4172) // "returning address of local variable or temporary" but *result is not local!
#endif
return static_cast<ref_type>(*result);
#ifdef BOOST_MSVC
# pragma warning(pop)
#endif
}
template<typename ValueType>
@ -306,7 +316,9 @@ namespace boost
template<typename ValueType>
inline ValueType * unsafe_any_cast(any * operand) BOOST_NOEXCEPT
{
return &static_cast<any::holder<ValueType> *>(operand->content)->held;
return boost::addressof(
static_cast<any::holder<ValueType> *>(operand->content)->held
);
}
template<typename ValueType>
@ -317,6 +329,7 @@ namespace boost
}
// Copyright Kevlin Henney, 2000, 2001, 2002. All rights reserved.
// Copyright Antony Polukhin, 2013-2019.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@ -127,11 +127,11 @@ public:
}
// used for text output
operator int () const {
operator base_type () const {
return t;
}
// used for text input
operator int_least16_t &() {
operator base_type &() {
return t;
}
bool operator==(const class_id_type & rhs) const {
@ -151,7 +151,10 @@ private:
public:
object_id_type(): t(0) {};
// note: presumes that size_t >= unsigned int.
explicit object_id_type(const std::size_t & t_) : t(t_){
// use explicit cast to silence useless warning
explicit object_id_type(const std::size_t & t_) : t(static_cast<base_type>(t_)){
// make quadriple sure that we haven't lost any real integer
// precision
BOOST_ASSERT(t_ <= boost::integer_traits<base_type>::const_max);
}
object_id_type(const object_id_type & t_) :
@ -162,11 +165,11 @@ public:
return *this;
}
// used for text output
operator uint_least32_t () const {
operator base_type () const {
return t;
}
// used for text input
operator uint_least32_t & () {
operator base_type & () {
return t;
}
bool operator==(const object_id_type & rhs) const {

View File

@ -102,17 +102,29 @@ protected:
}
void load_override(class_id_type & t){
library_version_type lvt = this->get_library_version();
/*
* library versions:
* boost 1.39 -> 5
* boost 1.43 -> 7
* boost 1.47 -> 9
*
*
* 1) in boost 1.43 and inferior, class_id_type is always a 16bit value, with no check on the library version
* --> this means all archives with version v <= 7 are written with a 16bit class_id_type
* 2) in boost 1.44 this load_override has disappeared (and thus boost 1.44 is not backward compatible at all !!)
* 3) recent boosts reintroduced load_override with a test on the version :
* - v > 7 : this->detail_common_iarchive::load_override(t, version)
* - v > 6 : 16bit
* - other : 32bit
* --> which is obviously incorrect, see point 1
*
* the fix here decodes class_id_type on 16bit for all v <= 7, which seems to be the correct behaviour ...
*/
if(boost::archive::library_version_type(7) < lvt){
this->detail_common_iarchive::load_override(t);
}
else
if(boost::archive::library_version_type(6) < lvt){
int_least16_t x=0;
* this->This() >> x;
t = boost::archive::class_id_type(x);
}
else{
int x=0;
int_least16_t x=0;
* this->This() >> x;
t = boost::archive::class_id_type(x);
}

View File

@ -50,7 +50,7 @@ namespace std{
//#include <boost/mpl/placeholders.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/array_wrapper.hpp>
#include <boost/archive/basic_streambuf_locale_saver.hpp>
#include <boost/archive/codecvt_null.hpp>

View File

@ -45,7 +45,7 @@ namespace std{
//#include <boost/mpl/placeholders.hpp>
#include <boost/serialization/is_bitwise_serializable.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/array_wrapper.hpp>
#include <boost/archive/basic_streambuf_locale_saver.hpp>
#include <boost/archive/codecvt_null.hpp>

View File

@ -21,7 +21,7 @@
//
// note the fact that on libraries without wide characters, ostream is
// is not a specialization of basic_ostream which in fact is not defined
// in such cases. So we can't use basic_ostream<IStream::char_type> but rather
// in such cases. So we can't use basic_istream<IStream::char_type> but rather
// use two template parameters
#include <boost/config.hpp>

View File

@ -175,8 +175,6 @@ protected:
template<class T>
void save(const T & t){
boost::io::ios_flags_saver fs(os);
boost::io::ios_precision_saver ps(os);
typename is_float<T>::type tf;
save_impl(t, tf);
}

View File

@ -89,8 +89,7 @@ protected:
// leaving the archive in an undetermined state
BOOST_ARCHIVE_OR_WARCHIVE_DECL void
load_override(class_id_type & t);
BOOST_ARCHIVE_OR_WARCHIVE_DECL void
load_override(class_id_optional_type & /* t */){}
void load_override(class_id_optional_type & /* t */){}
BOOST_ARCHIVE_OR_WARCHIVE_DECL void
load_override(object_id_type & t);
BOOST_ARCHIVE_OR_WARCHIVE_DECL void

View File

@ -18,8 +18,11 @@
#include <locale>
#include <cstddef> // NULL, size_t
#ifndef BOOST_NO_CWCHAR
#include <cwchar> // for mbstate_t
#endif
#include <boost/config.hpp>
#include <boost/serialization/force_include.hpp>
#include <boost/archive/detail/auto_link_archive.hpp>
#include <boost/archive/detail/abi_prefix.hpp> // must be the last header
@ -60,9 +63,10 @@ public:
};
template<>
class BOOST_SYMBOL_VISIBLE codecvt_null<wchar_t> : public std::codecvt<wchar_t, char, std::mbstate_t>
class BOOST_WARCHIVE_DECL codecvt_null<wchar_t> :
public std::codecvt<wchar_t, char, std::mbstate_t>
{
virtual BOOST_WARCHIVE_DECL std::codecvt_base::result
virtual std::codecvt_base::result
do_out(
std::mbstate_t & state,
const wchar_t * first1,
@ -72,7 +76,7 @@ class BOOST_SYMBOL_VISIBLE codecvt_null<wchar_t> : public std::codecvt<wchar_t,
char * last2,
char * & next2
) const;
virtual BOOST_WARCHIVE_DECL std::codecvt_base::result
virtual std::codecvt_base::result
do_in(
std::mbstate_t & state,
const char * first1,
@ -92,7 +96,7 @@ public:
explicit codecvt_null(std::size_t no_locale_manage = 0) :
std::codecvt<wchar_t, char, std::mbstate_t>(no_locale_manage)
{}
virtual ~codecvt_null(){};
//virtual ~codecvt_null(){};
};
} // namespace archive

View File

@ -35,11 +35,12 @@ class extended_type_info;
// note: referred to as Curiously Recurring Template Patter (CRTP)
template<class Archive>
class BOOST_SYMBOL_VISIBLE common_iarchive :
class BOOST_SYMBOL_VISIBLE common_iarchive :
public basic_iarchive,
public interface_iarchive<Archive>
{
friend class interface_iarchive<Archive>;
friend class basic_iarchive;
private:
virtual void vload(version_type & t){
* this->This() >> t;

View File

@ -38,6 +38,7 @@ class BOOST_SYMBOL_VISIBLE common_oarchive :
public interface_oarchive<Archive>
{
friend class interface_oarchive<Archive>;
friend class basic_oarchive;
private:
virtual void vsave(const version_type t){
* this->This() << t;

View File

@ -57,11 +57,10 @@ namespace std{
#include <boost/serialization/assume_abstract.hpp>
#ifndef BOOST_MSVC
#define DONT_USE_HAS_NEW_OPERATOR ( \
BOOST_WORKAROUND(__IBMCPP__, < 1210) \
|| defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x590) \
)
#if !defined(BOOST_MSVC) && \
(BOOST_WORKAROUND(__IBMCPP__, < 1210) || \
defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x590))
#define DONT_USE_HAS_NEW_OPERATOR 1
#else
#define DONT_USE_HAS_NEW_OPERATOR 0
#endif
@ -77,10 +76,10 @@ namespace std{
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/void_cast.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/collection_size_type.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/wrapper.hpp>
#include <boost/serialization/array_wrapper.hpp>
// the following is need only for dynamic cast of polymorphic pointers
#include <boost/archive/archive_exception.hpp>
@ -90,6 +89,8 @@ namespace std{
#include <boost/archive/detail/archive_serializer_map.hpp>
#include <boost/archive/detail/check.hpp>
#include <boost/core/addressof.hpp>
namespace boost {
namespace serialization {
@ -122,8 +123,7 @@ private:
virtual void destroy(/*const*/ void *address) const {
boost::serialization::access::destroy(static_cast<T *>(address));
}
protected:
// protected constructor since it's always created by singleton
public:
explicit iserializer() :
basic_iserializer(
boost::serialization::singleton<
@ -132,7 +132,6 @@ protected:
>::get_const_instance()
)
{}
public:
virtual BOOST_DLLEXPORT void load_object_data(
basic_iarchive & ar,
void *x,
@ -234,7 +233,7 @@ struct heap_allocation {
// that the class might have class specific new with NO
// class specific delete at all. Patches (compatible with
// C++03) welcome!
delete t;
(operator delete)(t);
}
};
struct doesnt_have_new_operator {
@ -243,7 +242,7 @@ struct heap_allocation {
}
static void invoke_delete(T * t) {
// Note: I'm reliance upon automatic conversion from T * to void * here
delete t;
(operator delete)(t);
}
};
static T * invoke_new() {
@ -306,7 +305,7 @@ private:
void * x,
const unsigned int file_version
) const BOOST_USED;
protected:
public:
// this should alway be a singleton so make the constructor protected
pointer_iserializer();
~pointer_iserializer();
@ -406,7 +405,7 @@ struct load_non_pointer_type {
struct load_standard {
template<class T>
static void invoke(Archive &ar, const T & t){
void * x = & const_cast<T &>(t);
void * x = boost::addressof(const_cast<T &>(t));
ar.load_object(
x,
boost::serialization::singleton<
@ -484,7 +483,7 @@ struct load_pointer_type {
};
template<class T>
static const basic_pointer_iserializer * register_type(Archive &ar, const T & /*t*/){
static const basic_pointer_iserializer * register_type(Archive &ar, const T* const /*t*/){
// there should never be any need to load an abstract polymorphic
// class pointer. Inhibiting code generation for this
// permits abstract base classes to be used - note: exception
@ -523,7 +522,7 @@ struct load_pointer_type {
}
template<class T>
static void check_load(T & /* t */){
static void check_load(T * const /* t */){
check_pointer_level< T >();
check_pointer_tracking< T >();
}
@ -537,8 +536,8 @@ struct load_pointer_type {
template<class Tptr>
static void invoke(Archive & ar, Tptr & t){
check_load(*t);
const basic_pointer_iserializer * bpis_ptr = register_type(ar, *t);
check_load(t);
const basic_pointer_iserializer * bpis_ptr = register_type(ar, t);
const basic_pointer_iserializer * newbpis_ptr = ar.load_pointer(
// note major hack here !!!
// I tried every way to convert Tptr &t (where Tptr might
@ -588,7 +587,14 @@ struct load_array_type {
boost::archive::archive_exception::array_size_too_short
)
);
ar >> serialization::make_array(static_cast<value_type*>(&t[0]),count);
// explict template arguments to pass intel C++ compiler
ar >> serialization::make_array<
value_type,
boost::serialization::collection_size_type
>(
static_cast<value_type *>(&t[0]),
count
);
}
};
@ -598,7 +604,7 @@ template<class Archive, class T>
inline void load(Archive & ar, T &t){
// if this assertion trips. It means we're trying to load a
// const object with a compiler that doesn't have correct
// funtion template ordering. On other compilers, this is
// function template ordering. On other compilers, this is
// handled below.
detail::check_const_loading< T >();
typedef

View File

@ -26,6 +26,7 @@
#include <cstddef> // NULL
#include <boost/config.hpp>
#include <boost/static_assert.hpp>
#include <boost/detail/workaround.hpp>
@ -56,8 +57,9 @@
#include <boost/serialization/type_info_implementation.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/void_cast.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/collection_size_type.hpp>
#include <boost/serialization/array_wrapper.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/archive/archive_exception.hpp>
@ -67,6 +69,8 @@
#include <boost/archive/detail/archive_serializer_map.hpp>
#include <boost/archive/detail/check.hpp>
#include <boost/core/addressof.hpp>
namespace boost {
namespace serialization {
@ -252,7 +256,7 @@ struct save_non_pointer_type {
template<class T>
static void invoke(Archive &ar, const T & t){
ar.save_object(
& t,
boost::addressof(t),
boost::serialization::singleton<
oserializer<Archive, T>
>::get_const_instance()
@ -260,6 +264,8 @@ struct save_non_pointer_type {
}
};
// adds class information to the archive. This includes
// serialization level and class version
struct save_conditional {
@ -337,7 +343,7 @@ struct save_pointer_type {
};
template<class T>
static const basic_pointer_oserializer * register_type(Archive &ar, T & /*t*/){
static const basic_pointer_oserializer * register_type(Archive &ar, T* const /*t*/){
// there should never be any need to save an abstract polymorphic
// class pointer. Inhibiting code generation for this
// permits abstract base classes to be used - note: exception
@ -404,7 +410,7 @@ struct save_pointer_type {
// if its not a pointer to a more derived type
const void *vp = static_cast<const void *>(&t);
if(*this_type == *true_type){
const basic_pointer_oserializer * bpos = register_type(ar, t);
const basic_pointer_oserializer * bpos = register_type(ar, &t);
ar.save_pointer(vp, bpos);
return;
}
@ -463,7 +469,7 @@ struct save_pointer_type {
template<class TPtr>
static void invoke(Archive &ar, const TPtr t){
register_type(ar, * t);
register_type(ar, t);
if(NULL == t){
basic_oarchive & boa
= boost::serialization::smart_cast_reference<basic_oarchive &>(ar);
@ -501,7 +507,14 @@ struct save_array_type
);
boost::serialization::collection_size_type count(c);
ar << BOOST_SERIALIZATION_NVP(count);
ar << serialization::make_array(static_cast<value_type const*>(&t[0]),count);
// explict template arguments to pass intel C++ compiler
ar << serialization::make_array<
const value_type,
boost::serialization::collection_size_type
>(
static_cast<const value_type *>(&t[0]),
count
);
}
};

View File

@ -0,0 +1,218 @@
#ifndef BOOST_ARCHIVE_DETAIL_POLYMORPHIC_IARCHIVE_ROUTE_HPP
#define BOOST_ARCHIVE_DETAIL_POLYMORPHIC_IARCHIVE_ROUTE_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// polymorphic_iarchive_route.hpp
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#include <string>
#include <ostream>
#include <cstddef>
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::size_t;
} // namespace std
#endif
#include <boost/cstdint.hpp>
#include <boost/integer_traits.hpp>
#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/archive/detail/abi_prefix.hpp> // must be the last header
namespace boost {
namespace serialization {
class extended_type_info;
} // namespace serialization
namespace archive {
namespace detail{
class basic_iserializer;
class basic_pointer_iserializer;
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4511 4512)
#endif
template<class ArchiveImplementation>
class polymorphic_iarchive_route :
public polymorphic_iarchive,
// note: gcc dynamic cross cast fails if the the derivation below is
// not public. I think this is a mistake.
public /*protected*/ ArchiveImplementation
{
private:
// these are used by the serialization library.
virtual void load_object(
void *t,
const basic_iserializer & bis
){
ArchiveImplementation::load_object(t, bis);
}
virtual const basic_pointer_iserializer * load_pointer(
void * & t,
const basic_pointer_iserializer * bpis_ptr,
const basic_pointer_iserializer * (*finder)(
const boost::serialization::extended_type_info & type
)
){
return ArchiveImplementation::load_pointer(t, bpis_ptr, finder);
}
virtual void set_library_version(library_version_type archive_library_version){
ArchiveImplementation::set_library_version(archive_library_version);
}
virtual library_version_type get_library_version() const{
return ArchiveImplementation::get_library_version();
}
virtual unsigned int get_flags() const {
return ArchiveImplementation::get_flags();
}
virtual void delete_created_pointers(){
ArchiveImplementation::delete_created_pointers();
}
virtual void reset_object_address(
const void * new_address,
const void * old_address
){
ArchiveImplementation::reset_object_address(new_address, old_address);
}
virtual void load_binary(void * t, std::size_t size){
ArchiveImplementation::load_binary(t, size);
}
// primitive types the only ones permitted by polymorphic archives
virtual void load(bool & t){
ArchiveImplementation::load(t);
}
virtual void load(char & t){
ArchiveImplementation::load(t);
}
virtual void load(signed char & t){
ArchiveImplementation::load(t);
}
virtual void load(unsigned char & t){
ArchiveImplementation::load(t);
}
#ifndef BOOST_NO_CWCHAR
#ifndef BOOST_NO_INTRINSIC_WCHAR_T
virtual void load(wchar_t & t){
ArchiveImplementation::load(t);
}
#endif
#endif
virtual void load(short & t){
ArchiveImplementation::load(t);
}
virtual void load(unsigned short & t){
ArchiveImplementation::load(t);
}
virtual void load(int & t){
ArchiveImplementation::load(t);
}
virtual void load(unsigned int & t){
ArchiveImplementation::load(t);
}
virtual void load(long & t){
ArchiveImplementation::load(t);
}
virtual void load(unsigned long & t){
ArchiveImplementation::load(t);
}
#if defined(BOOST_HAS_LONG_LONG)
virtual void load(boost::long_long_type & t){
ArchiveImplementation::load(t);
}
virtual void load(boost::ulong_long_type & t){
ArchiveImplementation::load(t);
}
#elif defined(BOOST_HAS_MS_INT64)
virtual void load(__int64 & t){
ArchiveImplementation::load(t);
}
virtual void load(unsigned __int64 & t){
ArchiveImplementation::load(t);
}
#endif
virtual void load(float & t){
ArchiveImplementation::load(t);
}
virtual void load(double & t){
ArchiveImplementation::load(t);
}
virtual void load(std::string & t){
ArchiveImplementation::load(t);
}
#ifndef BOOST_NO_STD_WSTRING
virtual void load(std::wstring & t){
ArchiveImplementation::load(t);
}
#endif
// used for xml and other tagged formats default does nothing
virtual void load_start(const char * name){
ArchiveImplementation::load_start(name);
}
virtual void load_end(const char * name){
ArchiveImplementation::load_end(name);
}
virtual void register_basic_serializer(const basic_iserializer & bis){
ArchiveImplementation::register_basic_serializer(bis);
}
virtual helper_collection &
get_helper_collection(){
return ArchiveImplementation::get_helper_collection();
}
public:
// this can't be inheriteded because they appear in mulitple
// parents
typedef mpl::bool_<true> is_loading;
typedef mpl::bool_<false> is_saving;
// the >> operator
template<class T>
polymorphic_iarchive & operator>>(T & t){
return polymorphic_iarchive::operator>>(t);
}
// the & operator
template<class T>
polymorphic_iarchive & operator&(T & t){
return polymorphic_iarchive::operator&(t);
}
// register type function
template<class T>
const basic_pointer_iserializer *
register_type(T * t = NULL){
return ArchiveImplementation::register_type(t);
}
// all current archives take a stream as constructor argument
template <class _Elem, class _Tr>
polymorphic_iarchive_route(
std::basic_istream<_Elem, _Tr> & is,
unsigned int flags = 0
) :
ArchiveImplementation(is, flags)
{}
virtual ~polymorphic_iarchive_route(){};
};
} // namespace detail
} // namespace archive
} // namespace boost
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
#include <boost/archive/detail/abi_suffix.hpp> // pops abi_suffix.hpp pragmas
#endif // BOOST_ARCHIVE_DETAIL_POLYMORPHIC_IARCHIVE_DISPATCH_HPP

View File

@ -0,0 +1,209 @@
#ifndef BOOST_ARCHIVE_DETAIL_POLYMORPHIC_OARCHIVE_ROUTE_HPP
#define BOOST_ARCHIVE_DETAIL_POLYMORPHIC_OARCHIVE_ROUTE_HPP
// MS compatible compilers support #pragma once
#if defined(_MSC_VER)
# pragma once
#endif
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// polymorphic_oarchive_route.hpp
// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#include <string>
#include <ostream>
#include <cstddef> // size_t
#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
using ::size_t;
} // namespace std
#endif
#include <boost/cstdint.hpp>
#include <boost/integer_traits.hpp>
#include <boost/archive/polymorphic_oarchive.hpp>
#include <boost/archive/detail/abi_prefix.hpp> // must be the last header
namespace boost {
namespace serialization {
class extended_type_info;
} // namespace serialization
namespace archive {
namespace detail{
class basic_oserializer;
class basic_pointer_oserializer;
#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable : 4511 4512)
#endif
template<class ArchiveImplementation>
class polymorphic_oarchive_route :
public polymorphic_oarchive,
// note: gcc dynamic cross cast fails if the the derivation below is
// not public. I think this is a mistake.
public /*protected*/ ArchiveImplementation
{
private:
// these are used by the serialization library.
virtual void save_object(
const void *x,
const detail::basic_oserializer & bos
){
ArchiveImplementation::save_object(x, bos);
}
virtual void save_pointer(
const void * t,
const detail::basic_pointer_oserializer * bpos_ptr
){
ArchiveImplementation::save_pointer(t, bpos_ptr);
}
virtual void save_null_pointer(){
ArchiveImplementation::save_null_pointer();
}
// primitive types the only ones permitted by polymorphic archives
virtual void save(const bool t){
ArchiveImplementation::save(t);
}
virtual void save(const char t){
ArchiveImplementation::save(t);
}
virtual void save(const signed char t){
ArchiveImplementation::save(t);
}
virtual void save(const unsigned char t){
ArchiveImplementation::save(t);
}
#ifndef BOOST_NO_CWCHAR
#ifndef BOOST_NO_INTRINSIC_WCHAR_T
virtual void save(const wchar_t t){
ArchiveImplementation::save(t);
}
#endif
#endif
virtual void save(const short t){
ArchiveImplementation::save(t);
}
virtual void save(const unsigned short t){
ArchiveImplementation::save(t);
}
virtual void save(const int t){
ArchiveImplementation::save(t);
}
virtual void save(const unsigned int t){
ArchiveImplementation::save(t);
}
virtual void save(const long t){
ArchiveImplementation::save(t);
}
virtual void save(const unsigned long t){
ArchiveImplementation::save(t);
}
#if defined(BOOST_HAS_LONG_LONG)
virtual void save(const boost::long_long_type t){
ArchiveImplementation::save(t);
}
virtual void save(const boost::ulong_long_type t){
ArchiveImplementation::save(t);
}
#elif defined(BOOST_HAS_MS_INT64)
virtual void save(const boost::int64_t t){
ArchiveImplementation::save(t);
}
virtual void save(const boost::uint64_t t){
ArchiveImplementation::save(t);
}
#endif
virtual void save(const float t){
ArchiveImplementation::save(t);
}
virtual void save(const double t){
ArchiveImplementation::save(t);
}
virtual void save(const std::string & t){
ArchiveImplementation::save(t);
}
#ifndef BOOST_NO_STD_WSTRING
virtual void save(const std::wstring & t){
ArchiveImplementation::save(t);
}
#endif
virtual library_version_type get_library_version() const{
return ArchiveImplementation::get_library_version();
}
virtual unsigned int get_flags() const {
return ArchiveImplementation::get_flags();
}
virtual void save_binary(const void * t, std::size_t size){
ArchiveImplementation::save_binary(t, size);
}
// used for xml and other tagged formats default does nothing
virtual void save_start(const char * name){
ArchiveImplementation::save_start(name);
}
virtual void save_end(const char * name){
ArchiveImplementation::save_end(name);
}
virtual void end_preamble(){
ArchiveImplementation::end_preamble();
}
virtual void register_basic_serializer(const detail::basic_oserializer & bos){
ArchiveImplementation::register_basic_serializer(bos);
}
virtual helper_collection &
get_helper_collection(){
return ArchiveImplementation::get_helper_collection();
}
public:
// this can't be inheriteded because they appear in mulitple
// parents
typedef mpl::bool_<false> is_loading;
typedef mpl::bool_<true> is_saving;
// the << operator
template<class T>
polymorphic_oarchive & operator<<(T & t){
return polymorphic_oarchive::operator<<(t);
}
// the & operator
template<class T>
polymorphic_oarchive & operator&(T & t){
return polymorphic_oarchive::operator&(t);
}
// register type function
template<class T>
const basic_pointer_oserializer *
register_type(T * t = NULL){
return ArchiveImplementation::register_type(t);
}
// all current archives take a stream as constructor argument
template <class _Elem, class _Tr>
polymorphic_oarchive_route(
std::basic_ostream<_Elem, _Tr> & os,
unsigned int flags = 0
) :
ArchiveImplementation(os, flags)
{}
virtual ~polymorphic_oarchive_route(){};
};
} // namespace detail
} // namespace archive
} // namespace boost
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif
#include <boost/archive/detail/abi_suffix.hpp> // pops abi_suffix.hpp pragmas
#endif // BOOST_ARCHIVE_DETAIL_POLYMORPHIC_OARCHIVE_DISPATCH_HPP

Some files were not shown because too many files have changed in this diff Show More