mirror of https://github.com/saitohirga/WSJT-X.git
Merge branch 'feat-refactor' into develop
This commit is contained in:
commit
0c49f6f872
|
@ -7,4 +7,6 @@ jnq*
|
|||
*.exe
|
||||
*.o
|
||||
*.mod
|
||||
*.pro.user
|
||||
*.pro.user
|
||||
cmake-build-debug
|
||||
cmake-build-release
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 (
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
125
Detector.cpp
125
Detector.cpp
|
@ -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
|
||||
}
|
59
Detector.hpp
59
Detector.hpp
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
1172
HRDTransceiver.cpp
1172
HRDTransceiver.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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_);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
350
Modulator.cpp
350
Modulator.cpp
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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:
|
||||
|
|
|
@ -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
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
312
boost/Jamroot
312
boost/Jamroot
|
@ -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) ;
|
|
@ -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.
|
|
@ -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
|
||||
```
|
||||
```
|
||||
|
|
|
@ -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) ;
|
|
@ -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;
|
||||
}
|
BIN
boost/boost.png
BIN
boost/boost.png
Binary file not shown.
Before Width: | Height: | Size: 6.2 KiB |
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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 ) {}
|
||||
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 >
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
Loading…
Reference in New Issue