21 using namespace swift::core::context;
22 using namespace swift::core::afv::audio;
23 using namespace swift::core::afv::connection;
25 using namespace swift::misc::audio;
26 using namespace swift::misc::physical_quantities;
27 using namespace swift::misc::simulation;
28 using namespace swift::misc::aviation;
29 using namespace swift::sound;
30 using namespace swift::sound::sample_provider;
32 namespace swift::core::afv::clients
34 const QStringList &CAfvClient::getLogCategories()
40 CAfvClient::CAfvClient(
const QString &apiServer, QObject *owner)
43 m_output(new
COutput(this)), m_voiceServerTimer(new QTimer(this))
45 this->setObjectName(
"AFV client: " + apiServer);
48 connect(m_input, &CInput::opusDataAvailable,
this, &CAfvClient::opusDataAvailable);
49 connect(m_input, &CInput::inputVolumeStream,
this, &CAfvClient::inputVolumeStream);
51 connect(m_output, &COutput::outputVolumeStream,
this, &CAfvClient::outputVolumeStream);
52 connect(m_connection, &CClientConnection::audioReceived,
this, &CAfvClient::audioOutDataAvailable);
53 connect(m_voiceServerTimer, &QTimer::timeout,
this, &CAfvClient::onTimerUpdate);
61 QMutexLocker lock(&m_mutexCallsign);
67 QMutexLocker lock(&m_mutexCallsign);
68 m_callsign = callsign;
73 QMutexLocker lock(&m_mutexConnection);
77 void CAfvClient::initTransceivers()
80 QMutexLocker lock(&m_mutexTransceivers);
81 m_transceivers = { { 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 }, { 1, UniCom, 48.5, 11.5, 1000.0, 1000.0 } };
83 m_enabledTransceivers = { 0, 1 };
84 m_transmittingTransceivers = { { 0 } };
88 this->connectWithContexts();
91 this->onTimerUpdate();
94 void CAfvClient::connectWithContexts()
96 if (m_connectedWithContext) {
return; }
97 if (!hasContexts()) {
return; }
102 this->disconnect(context);
103 context->disconnect(
this);
107 &CAfvClient::onUpdateTransceiversFromContext, Qt::QueuedConnection);
109 &CAfvClient::toggleTransmissionCapability, Qt::QueuedConnection);
111 m_connectedWithContext =
true;
114 void CAfvClient::fetchSimulatorSettings()
117 if (!hasContexts()) {
return; }
122 QPointer<CAfvClient> myself(
this);
124 if (myself) { this->fetchSimulatorSettings(); }
130 const bool changed = integrated != m_integratedComUnit;
132 m_integratedComUnit = integrated;
133 if (changed) { emit this->updatedFromOwnAircraftCockpit(); }
136 void CAfvClient::connectTo(
const QString &cid,
const QString &password,
const QString &callsign,
137 const QString &client)
139 if (QThread::currentThread() != thread())
142 QPointer<CAfvClient> myself(
this);
143 QMetaObject::invokeMethod(
this, [=]() {
144 if (myself) { connectTo(cid, password, callsign, client); }
150 this->connectWithContexts();
151 this->setCallsign(callsign);
153 QPointer<CAfvClient> myself(
this);
154 if (!this->isConnected() && m_retryConnectAttempt == 0)
158 if (!myself) {
return; }
159 if (m_retryConnectAttempt > 0) {
return; }
162 this->retryConnectTo(cid, password, callsign, client, QStringLiteral(
"No connection afer 20secs"));
168 QMutexLocker lock(&m_mutexConnection);
171 m_connection->connectTo(
172 cid, password, callsign, client,
174 this, [=](
bool authenticated) {
175 if (!myself) {
return; }
178 const QVector<StationDto> aliasedStations = m_connection->getAllAliasedStations();
179 this->setAliasedStations(aliasedStations);
180 this->onTimerUpdate();
188 QMutexLocker lock2(&m_mutex);
189 if (m_voiceServerTimer) { m_voiceServerTimer->start(PositionUpdatesMs); }
191 m_retryConnectAttempt = 0;
192 emit this->connectionStatusChanged(Connected);
196 myself->retryConnectTo(
197 cid, password, callsign, client,
198 QStringLiteral(
"AFV authentication failed for '%1' callsign '%2'").arg(cid, callsign));
199 emit this->connectionStatusChanged(Disconnected);
205 void CAfvClient::disconnectFrom(
bool stop)
207 if (QThread::currentThread() != thread())
210 QPointer<CAfvClient> myself(
this);
211 QMetaObject::invokeMethod(
this, [=]() {
212 if (myself) { disconnectFrom(stop); }
220 QMutexLocker lock(&m_mutexConnection);
221 m_connection->disconnectFrom();
224 m_heartBeatFailures = 0;
225 m_retryConnectAttempt = 0;
226 m_fsdConnectMismatches = 0;
228 emit connectionStatusChanged(Disconnected);
230 if (stop) { this->stopAudio(); }
233 QStringList CAfvClient::availableInputDevices()
const
235 return CAudioDeviceInfoList::allInputDevices().getDeviceNames();
238 QStringList CAfvClient::availableOutputDevices()
const
240 return CAudioDeviceInfoList::allOutputDevices().getDeviceNames();
243 void CAfvClient::setBypassEffects(
bool value)
245 QMutexLocker lock(&m_mutexSampleProviders);
246 if (m_soundcardSampleProvider) { m_soundcardSampleProvider->setBypassEffects(value); }
249 bool CAfvClient::isOutputMuted()
const
251 const int v = this->getNormalizedMasterOutputVolume();
255 void CAfvClient::setOutputMuted(
bool mute)
257 if (this->isOutputMuted() == mute) {
return; }
258 this->setNormalizedMasterOutputVolume(mute ? 0 : 50);
259 emit this->changedOutputMute(mute);
262 void CAfvClient::startAudio()
266 this->startAudio(inputDevice, outputDevice);
271 if (QThread::currentThread() != this->thread())
274 QPointer<CAfvClient> myself(
this);
275 QMetaObject::invokeMethod(
this, [=]() {
276 if (myself) { startAudio(inputDevice, outputDevice); }
282 inputDevice.
isValid() ? inputDevice : CAudioDeviceInfo::getDefaultInputDevice();
284 outputDevice.
isValid() ? outputDevice : CAudioDeviceInfo::getDefaultOutputDevice();
288 "Wrong output device");
292 if (this->usesSameDevices(useInputDevice, useOutputDevice))
301 this->initTransceivers();
307 QMutexLocker lock { &m_mutexSampleProviders };
308 if (m_soundcardSampleProvider)
310 m_soundcardSampleProvider->disconnect();
311 m_soundcardSampleProvider->deleteLater();
314 connect(m_soundcardSampleProvider, &CSoundcardSampleProvider::receivingCallsignsChanged,
this,
315 &CAfvClient::onReceivingCallsignsChanged);
317 if (m_outputSampleProvider) { m_outputSampleProvider->deleteLater(); }
325 QMutexLocker lock(&m_mutex);
327 m_output->start(useOutputDevice, m_outputSampleProvider);
328 m_input->start(useInputDevice);
331 m_voiceServerTimer->start(PositionUpdatesMs);
335 this->setReceiveAudio(
true);
337 this->onSettingsChanged();
342 this->onTimerUpdate();
344 emit this->startedAudio(useInputDevice, useOutputDevice);
346 if (this->isOutputMuted())
349 this->setOutputMuted(
false);
353 void CAfvClient::startAudio(
const QString &inputDeviceName,
const QString &outputDeviceName)
355 const CAudioDeviceInfo i = CAudioDeviceInfoList::allInputDevices().findByName(inputDeviceName);
356 const CAudioDeviceInfo o = CAudioDeviceInfoList::allOutputDevices().findByName(outputDeviceName);
357 this->startAudio(i, o);
360 void CAfvClient::stopAudio()
362 if (QThread::currentThread() != this->thread())
365 QPointer<CAfvClient> myself(
this);
366 QMetaObject::invokeMethod(
this, [=]() {
367 if (myself) stopAudio();
379 this->setReceiveAudio(
false);
383 QMutexLocker lock { &m_mutex };
389 if (this->isOutputMuted()) { this->setOutputMuted(
false); }
391 emit this->inputVolumePeakVU(0.0);
392 emit this->outputVolumePeakVU(0.0);
393 emit this->stoppedAudio();
396 void CAfvClient::restartAudio()
406 QPointer<CAfvClient> myself(
this);
408 if (myself) { myself->startAudio(); }
438 void CAfvClient::setReceiveAudio(
bool receive)
440 QMutexLocker lock(&m_mutexConnection);
441 if (!m_connection) {
return; }
442 m_connection->setReceiveAudio(receive);
445 void CAfvClient::enableTransceiver(quint16
id,
bool enable)
448 QMutexLocker lock(&m_mutexTransceivers);
449 if (enable) { m_enabledTransceivers.insert(
id); }
450 else { m_enabledTransceivers.remove(
id); }
453 this->updateTransceivers();
458 this->enableTransceiver(comUnitToTransceiverId(comUnit), enable);
461 bool CAfvClient::isEnabledTransceiver(quint16
id)
const
464 const auto enabledTransceivers = this->getEnabledTransceivers();
465 if (!enabledTransceivers.contains(
id)) {
return false; }
467 const auto transceivers = this->getTransceivers();
468 return std::any_of(transceivers.cbegin(), transceivers.cend(),
474 return this->isEnabledTransceiver(comUnitToTransceiverId(comUnit));
477 void CAfvClient::updateComFrequency(quint16
id, quint32 frequencyHz)
479 if (
id != 0 &&
id != 1) {
return; }
482 quint32 roundedFrequencyHz =
static_cast<quint32
>(qRound(frequencyHz / 1000.0)) * 1000;
483 roundedFrequencyHz = this->getAliasFrequencyHz(roundedFrequencyHz);
487 QMutexLocker lockTransceivers(&m_mutexTransceivers);
488 if (m_transceivers.size() >=
id + 1)
490 if (m_transceivers[
id].frequencyHz != roundedFrequencyHz)
493 m_transceivers[id].frequencyHz = roundedFrequencyHz;
501 this->updateTransceivers(
false);
507 const auto freqHz =
static_cast<quint32
>(comFrequency.
valueInteger(CFrequencyUnit::Hz()));
508 this->updateComFrequency(comUnitToTransceiverId(comUnit), freqHz);
516 void CAfvClient::updatePosition(
double latitudeDeg,
double longitudeDeg,
double heightMeters)
518 QMutexLocker lock(&m_mutexTransceivers);
521 transceiver.LatDeg = latitudeDeg;
522 transceiver.LonDeg = longitudeDeg;
523 transceiver.HeightAglM = heightMeters;
524 transceiver.HeightMslM = heightMeters;
528 void CAfvClient::updateTransceivers(
bool updateFrequencies)
535 this->updatePosition(ownAircraft.
latitude().
value(CAngleUnit::deg()),
539 if (updateFrequencies)
541 this->updateComFrequency(CComSystem::Com1, ownAircraft.
getCom1System());
542 this->updateComFrequency(CComSystem::Com2, ownAircraft.
getCom2System());
547 const auto transceivers = this->getTransceivers();
548 const auto enabledTransceivers = this->getEnabledTransceivers();
549 const QString callsign = this->getCallsign();
552 QVector<TransceiverDto> newEnabledTransceivers;
553 for (
const TransceiverDto &transceiver : transceivers)
555 if (enabledTransceivers.contains(transceiver.id)) { newEnabledTransceivers.push_back(transceiver); }
560 QMutexLocker lock(&m_mutexConnection);
561 if (m_connection) { m_connection->updateTransceivers(callsign, newEnabledTransceivers); }
564 QMutexLocker lock(&m_mutexSampleProviders);
565 if (m_soundcardSampleProvider)
567 m_soundcardSampleProvider->updateRadioTransceivers(newEnabledTransceivers);
572 void CAfvClient::setTransmittingTransceiver(quint16 transceiverID)
575 this->setTransmittingTransceivers({ tx });
580 this->setTransmittingTransceiver(comUnitToTransceiverId(comUnit));
583 void CAfvClient::setTransmittingTransceivers(
const QVector<TxTransceiverDto> &transceivers)
585 QMutexLocker lock(&m_mutexTransceivers);
586 m_transmittingTransceivers = transceivers;
589 bool CAfvClient::isTransmittingTransceiver(quint16
id)
const
591 QMutexLocker lock(&m_mutexTransceivers);
592 return std::any_of(m_transmittingTransceivers.cbegin(), m_transmittingTransceivers.cend(),
598 return this->isTransmittingTransceiver(comUnitToTransceiverId(comUnit));
601 void CAfvClient::setRxTx(
bool rx1,
bool tx1,
bool rx2,
bool tx2)
603 QVector<TxTransceiverDto> txs;
614 this->setTransmittingTransceivers(txs);
616 QSet<quint16> enabledTransceivers;
617 if (rx1 || tx1) { enabledTransceivers.insert(comUnitToTransceiverId(CComSystem::Com1)); }
619 if (rx2 || tx2) { enabledTransceivers.insert(comUnitToTransceiverId(CComSystem::Com2)); }
622 QMutexLocker lock(&m_mutexTransceivers);
623 m_enabledTransceivers = enabledTransceivers;
627 this->onTimerUpdate();
630 void CAfvClient::getRxTx(
bool &rx1,
bool &tx1,
bool &rx2,
bool &tx2)
const
637 const QSet<quint16> enabled = getEnabledTransceivers();
638 rx1 = enabled.contains(comUnitToTransceiverId(CComSystem::Com1));
639 rx2 = enabled.contains(comUnitToTransceiverId(CComSystem::Com2));
641 const QVector<TxTransceiverDto> transmits = getTransmittingTransceivers();
644 if (dto.id == comUnitToTransceiverId(CComSystem::Com1)) { tx1 =
true; }
645 if (dto.id == comUnitToTransceiverId(CComSystem::Com2)) { tx2 =
true; }
649 QVector<TransceiverDto> CAfvClient::getTransceivers()
const
651 QMutexLocker lock(&m_mutexTransceivers);
652 return m_transceivers;
655 QSet<quint16> CAfvClient::getEnabledTransceivers()
const
657 QMutexLocker lock(&m_mutexTransceivers);
658 return m_enabledTransceivers;
661 QVector<TxTransceiverDto> CAfvClient::getTransmittingTransceivers()
const
663 QMutexLocker lock(&m_mutexTransceivers);
664 return m_transmittingTransceivers;
667 void CAfvClient::setPtt(
bool active)
675 if (active && m_disableTransmissionCapability)
681 if (m_transmit == active) {
return; }
686 QMutexLocker lock(&m_mutexSampleProviders);
687 if (m_soundcardSampleProvider) { m_soundcardSampleProvider->pttUpdate(active, m_transmittingTransceivers); }
702 emit this->ptt(active, this->identifier());
705 double CAfvClient::getInputVolumeDb()
const
707 QMutexLocker lock(&m_mutex);
708 return m_inputVolumeDb;
711 bool CAfvClient::setInputVolumeDb(
double valueDb)
716 QPointer<CAfvClient> myself(
this);
718 if (!myself || !CAfvClient::hasContexts()) {
return; }
719 myself->setInputVolumeDb(valueDb);
724 if (valueDb > MaxDbIn) { valueDb = MaxDbIn; }
725 else if (valueDb < MinDbIn) { valueDb = MinDbIn; }
727 QMutexLocker lock(&m_mutex);
728 bool changed = !qFuzzyCompare(m_inputVolumeDb, valueDb);
731 m_inputVolumeDb = valueDb;
734 const double gainRatio = qPow(10, valueDb / 20.0);
735 changed = m_input->setGainRatio(gainRatio);
743 QMutexLocker lock(&m_mutexVolume);
744 if (comUnit == CComSystem::Com1)
return m_outputVolumeDbCom1;
745 if (comUnit == CComSystem::Com2)
return m_outputVolumeDbCom2;
746 qFatal(
"Invalid COM unit");
752 QMutexLocker lock(&m_mutexVolume);
753 if (comUnit == CComSystem::Com1)
return m_outputGainRatioCom1;
754 if (comUnit == CComSystem::Com2)
return m_outputGainRatioCom2;
755 qFatal(
"Invalid COM unit");
759 int CAfvClient::getNormalizedInputVolume()
const
761 const double db = this->getInputVolumeDb();
762 const double range = MaxDbIn - MinDbIn;
763 const int i = qRound((db - MinDbIn) / range * 100);
767 int CAfvClient::getNormalizedMasterOutputVolume()
const
769 QMutexLocker lock(&m_mutexVolume);
770 return m_outputMasterVolumeNormalized;
775 QMutexLocker lock(&m_mutexVolume);
776 if (comUnit == CComSystem::Com1) {
return m_outputVolumeCom1Normalized; }
777 if (comUnit == CComSystem::Com2) {
return m_outputVolumeCom2Normalized; }
778 qFatal(
"Invalid ComUnit");
782 bool CAfvClient::setNormalizedInputVolume(
int volume)
784 if (volume < 0) { volume = 0; }
785 else if (volume > 100) { volume = 100; }
786 const double range = MaxDbIn - MinDbIn;
787 const double dB = MinDbIn + (volume * range / 100.0);
790 return this->setInputVolumeDb(dB);
793 bool CAfvClient::setNormalizedMasterOutputVolume(
int volume)
798 QPointer<CAfvClient> myself(
this);
800 if (!myself || !CAfvClient::hasContexts()) {
return; }
801 myself->setNormalizedMasterOutputVolume(volume);
806 bool changed =
false;
808 QMutexLocker lock(&m_mutexVolume);
809 changed = m_outputMasterVolumeNormalized != volume;
810 if (changed) { m_outputMasterVolumeNormalized = volume; }
814 int com1Normalized = getNormalizedComOutputVolume(CComSystem::Com1);
815 int com2Normalized = getNormalizedComOutputVolume(CComSystem::Com2);
816 setNormalizedComOutputVolume(CComSystem::Com1, com1Normalized);
817 setNormalizedComOutputVolume(CComSystem::Com2, com2Normalized);
824 if (volume < 0) { volume = 0; }
825 else if (volume > 100) { volume = 100; }
828 if (comUnit == CComSystem::Com1) { m_outputVolumeCom1Normalized = volume; }
829 else if (comUnit == CComSystem::Com2) { m_outputVolumeCom2Normalized = volume; }
830 else { qFatal(
"Invalid ComUnit"); }
833 volume = qRound((
double)volume * getNormalizedMasterOutputVolume() / 100);
836 double range = MaxDbOut;
838 if (volume >= 50) { volume -= 50; }
842 range = qAbs(MinDbOut);
844 dB += (volume * range / 50.0);
847 return this->setComOutputVolumeDb(comUnit, dB);
850 double CAfvClient::getInputVolumePeakVU()
const
852 QMutexLocker lock(&m_mutexInputStream);
853 return m_inputVolumeStream.PeakVU;
856 double CAfvClient::getOutputVolumePeakVU()
const
858 QMutexLocker lock(&m_mutexOutputStream);
859 return m_outputVolumeStream.PeakVU;
864 const bool transmit = m_transmit;
865 const bool loopback = m_loopbackOn;
866 const bool transmitHistory = m_transmitHistory;
867 const auto transceivers = this->getTransceivers();
869 if (loopback && transmit)
873 audioData.
callsign = QStringLiteral(
"loopback");
877 const RxTransceiverDto com1 = { 0, transceivers.size() > 0 ? transceivers[0].frequencyHz : UniCom, 1.0 };
878 const RxTransceiverDto com2 = { 1, transceivers.size() > 1 ? transceivers[1].frequencyHz : UniCom, 1.0 };
880 QMutexLocker lock(&m_mutexSampleProviders);
881 m_soundcardSampleProvider->addOpusSamples(audioData, { com1, com2 });
885 if (!this->isConnected()) {
return; }
887 const QString callsign = this->getCallsign();
888 const auto transmittingTransceivers = this->getTransmittingTransceivers();
889 if (!transmittingTransceivers.isEmpty())
893 AudioTxOnTransceiversDto dto;
894 dto.callsign = callsign.toStdString();
896 dto.audio = std::vector<char>(args.
audio.begin(), args.
audio.end());
897 dto.lastPacket =
false;
899 std::vector<TxTransceiverDto>(transmittingTransceivers.begin(), transmittingTransceivers.end());
900 QMutexLocker lock(&m_mutexConnection);
901 m_connection->sendToVoiceServer(dto);
904 if (!transmit && transmitHistory)
906 AudioTxOnTransceiversDto dto;
907 dto.callsign = callsign.toStdString();
909 dto.audio = std::vector<char>(args.
audio.begin(), args.
audio.end());
910 dto.lastPacket =
true;
912 std::vector<TxTransceiverDto>(transmittingTransceivers.begin(), transmittingTransceivers.end());
913 QMutexLocker lock(&m_mutexConnection);
914 m_connection->sendToVoiceServer(dto);
916 m_transmitHistory = transmit;
920 void CAfvClient::audioOutDataAvailable(
const AudioRxOnTransceiversDto &dto)
923 audioData.
audio = QByteArray(dto.audio.data(),
static_cast<int>(dto.audio.size()));
924 audioData.callsign = QString::fromStdString(dto.callsign);
925 audioData.lastPacket = dto.lastPacket;
926 audioData.sequenceCounter = dto.sequenceCounter;
928 QMutexLocker lock(&m_mutexSampleProviders);
929 m_soundcardSampleProvider->addOpusSamples(
930 audioData, QVector<RxTransceiverDto>(dto.transceivers.begin(), dto.transceivers.end()));
937 QMutexLocker lock(&m_mutexInputStream);
938 m_inputVolumeStream = args;
940 emit inputVolumePeakVU(args.
PeakVU);
947 QMutexLocker lock(&m_mutexOutputStream);
948 m_outputVolumeStream = args;
950 emit outputVolumePeakVU(args.
PeakVU);
953 QString CAfvClient::getReceivingCallsignsStringCom1()
const
955 QMutexLocker lock(&m_mutex);
956 if (!m_soundcardSampleProvider)
return {};
957 return m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com1));
960 QString CAfvClient::getReceivingCallsignsStringCom2()
const
962 QMutexLocker lock(&m_mutexSampleProviders);
963 if (!m_soundcardSampleProvider)
return {};
964 return m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com2));
969 QMutexLocker lock(&m_mutexSampleProviders);
970 if (!m_soundcardSampleProvider)
return {};
971 return m_soundcardSampleProvider->getReceivingCallsigns(comUnitToTransceiverId(CComSystem::Com1));
976 QMutexLocker lock(&m_mutexSampleProviders);
977 if (!m_soundcardSampleProvider)
return {};
978 return m_soundcardSampleProvider->getReceivingCallsigns(comUnitToTransceiverId(CComSystem::Com2));
981 QStringList CAfvClient::getReceivingCallsignsStringCom1Com2()
const
984 QMutexLocker lock(&m_mutexSampleProviders);
985 if (!m_soundcardSampleProvider) {
return { { QString(), QString() } }; }
986 coms << m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com1));
987 coms << m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com2));
991 bool CAfvClient::updateVoiceServerUrl(
const QString &url)
993 QMutexLocker lock(&m_mutexConnection);
994 if (!m_connection) {
return false; }
995 return m_connection->updateVoiceServerUrl(url);
998 void CAfvClient::gracefulShutdown()
1001 this->disconnectFrom();
1002 this->quitAndWait();
1006 void CAfvClient::initialize()
1011 void CAfvClient::cleanup()
1014 if (m_winCoInitialized)
1017 m_winCoInitialized =
false;
1022 void CAfvClient::onTimerUpdate()
1028 this->updateFromOwnAircraft(aircraft,
false);
1031 this->autoLogoffWithoutFsdNetwork();
1034 this->fetchSimulatorSettings();
1039 this->updateTransceivers();
1043 this->checkServerHeartbeat();
1046 void CAfvClient::checkServerHeartbeat()
1048 if (!this->isStarted()) {
return; }
1049 if (!this->isConnected()) {
return; }
1051 if (this->isVoiceServerAlive())
1053 m_heartBeatFailures = 0;
1059 const int failures = ++m_heartBeatFailures;
1060 if (failures < 2) {
return; }
1067 QMutexLocker lock(&m_mutexConnection);
1068 un = m_connection->getUserName();
1069 pw = m_connection->getPassword();
1070 cs = m_connection->getCallsign();
1071 client = m_connection->getClient();
1073 if (un.isEmpty() || pw.isEmpty()) {
return; }
1076 if (this->isConnected()) { this->disconnectFrom(
false); }
1078 QPointer<CAfvClient> myself(
this);
1080 if (!myself) {
return; }
1081 const QString reason = QStringLiteral(
"Heartbeat failed %1 times").arg(failures);
1082 this->retryConnectTo(un, pw, cs, client, reason);
1086 void CAfvClient::onSettingsChanged()
1088 const CSettings audioSettings = m_audioSettings.get();
1094 this->setNormalizedInputVolume(iv);
1095 this->setNormalizedMasterOutputVolume(ov);
1096 this->setNormalizedComOutputVolume(CComSystem::Com1, ov1);
1097 this->setNormalizedComOutputVolume(CComSystem::Com2, ov2);
1101 void CAfvClient::autoLogoffWithoutFsdNetwork()
1103 if (!hasContexts()) {
return; }
1104 if (!this->isConnected())
1106 m_fsdConnectMismatches = 0;
1113 m_fsdConnectMismatches = 0;
1116 if (++m_fsdConnectMismatches < 2) {
return; }
1118 CLogMessage(
this).
warning(u
"Auto logoff AFV client because FSD no longer connected");
1119 this->disconnectFrom();
1128 transceiverCom1.
id = comUnitToTransceiverId(CComSystem::Com1);
1129 transceiverCom2.
id = comUnitToTransceiverId(CComSystem::Com2);
1132 const double latDeg = aircraft.
latitude().
value(CAngleUnit::deg());
1133 const double lngDeg = aircraft.
longitude().
value(CAngleUnit::deg());
1136 transceiverCom1.
LatDeg = transceiverCom2.
LatDeg = latDeg;
1137 transceiverCom1.
LonDeg = transceiverCom2.
LonDeg = lngDeg;
1147 transceiverCom1.
frequencyHz = this->getAliasFrequencyHz(f1);
1148 transceiverCom2.
frequencyHz = this->getAliasFrequencyHz(f2);
1150 QVector<TransceiverDto> newEnabledTransceivers;
1151 if (m_integratedComUnit)
1161 this->setNormalizedComOutputVolume(CComSystem::Com1, vol1);
1162 this->setNormalizedComOutputVolume(CComSystem::Com2, vol2);
1167 const bool e1 = rx1;
1168 const bool e2 = rx2;
1171 const QVector<TransceiverDto> newTransceivers { transceiverCom1, transceiverCom2 };
1172 QSet<quint16> newEnabledTransceiverIds;
1173 QVector<TxTransceiverDto> newTransmittingTransceivers;
1176 newEnabledTransceivers.push_back(transceiverCom1);
1177 newEnabledTransceiverIds.insert(transceiverCom1.
id);
1181 newEnabledTransceivers.push_back(transceiverCom2);
1182 newEnabledTransceiverIds.insert(transceiverCom2.
id);
1186 if (tx1 && e1) { newTransmittingTransceivers.push_back(transceiverCom1); }
1187 else if (tx2 && e2) { newTransmittingTransceivers.push_back(transceiverCom2); }
1191 QMutexLocker lock(&m_mutexTransceivers);
1192 m_transceivers = newTransceivers;
1193 m_enabledTransceivers = newEnabledTransceiverIds;
1194 m_transmittingTransceivers = newTransmittingTransceivers;
1200 const QSet<quint16> ids = getEnabledTransceivers();
1201 if (ids.contains(comUnitToTransceiverId(CComSystem::Com1)))
1203 newEnabledTransceivers.push_back(transceiverCom1);
1206 if (ids.contains(comUnitToTransceiverId(CComSystem::Com2)))
1208 newEnabledTransceivers.push_back(transceiverCom2);
1213 const QString callsign = this->getCallsign();
1216 QMutexLocker lock(&m_mutexConnection);
1220 m_connection->updateTransceivers(callsign, newEnabledTransceivers);
1225 QMutexLocker lock(&m_mutexSampleProviders);
1226 if (m_soundcardSampleProvider)
1228 m_soundcardSampleProvider->updateRadioTransceivers(newEnabledTransceivers);
1233 if (withSignals) { emit this->updatedFromOwnAircraftCockpit(); }
1238 if (originator == this->identifier()) {
return; }
1239 this->updateFromOwnAircraft(aircraft);
1249 case CComSystem::Com1:
1252 callsignsCom2 = this->getReceivingCallsignsCom2();
1255 case CComSystem::Com2:
1257 callsignsCom1 = this->getReceivingCallsignsCom1();
1261 emit this->receivedCallsignsChanged(callsignsCom1, callsignsCom2);
1262 emit this->receivingCallsignsChanged(args);
1265 void CAfvClient::retryConnectTo(
const QString &cid,
const QString &password,
const QString &callsign,
1266 const QString &client,
const QString &reason)
1268 if (this->isConnected()) {
return; }
1269 m_retryConnectAttempt++;
1271 const int retrySecs = qMin(3 * 60, m_retryConnectAttempt * 30);
1273 << retrySecs << m_retryConnectAttempt;
1274 this->reconnectTo(cid, password, callsign, client, retrySecs * 1000, msg);
1277 void CAfvClient::reconnectTo(
const QString &cid,
const QString &password,
const QString &callsign,
1283 emit this->afvConnectionFailure(msg);
1286 QPointer<CAfvClient> myself(
this);
1288 if (!myself) {
return; }
1289 if (myself->isConnected()) { return; }
1290 this->connectTo(cid, password, callsign, client);
1294 void CAfvClient::toggleTransmissionCapability(
bool disableTransmission)
1296 if (m_disableTransmissionCapability == disableTransmission) {
return; }
1297 m_disableTransmissionCapability = disableTransmission;
1299 if (disableTransmission)
1306 QVector<StationDto> CAfvClient::getAliasedStations()
const
1308 QMutexLocker lock(&m_mutex);
1309 return m_aliasedStations;
1312 void CAfvClient::setAliasedStations(
const QVector<StationDto> &stations)
1314 QMutexLocker lock(&m_mutex);
1315 m_aliasedStations = stations;
1318 quint32 CAfvClient::getAliasFrequencyHz(quint32 frequencyHz)
const
1321 quint32 roundedFrequencyHz =
static_cast<quint32
>(qRound(frequencyHz / 1000.0)) * 1000;
1325 QMutexLocker lock(&m_mutex);
1326 CFrequency roundedFrequency(
static_cast<int>(roundedFrequencyHz), CFrequencyUnit::Hz());
1327 const auto it = std::find_if(m_aliasedStations.constBegin(), m_aliasedStations.constEnd(),
1328 [roundedFrequency](
const StationDto &d) {
1329 if (d.frequencyAliasHz > 100000000 &&
1330 roundedFrequency.value(CFrequencyUnit::Hz()) > 100000000)
1332 const int aliasedFreqHz = qRound(d.frequencyAliasHz / 1000.0) * 1000;
1333 return CComSystem::isSameFrequency(
1334 CFrequency(aliasedFreqHz, CFrequencyUnit::Hz()), roundedFrequency);
1336 return d.frequencyAliasHz == roundedFrequency.value(CFrequencyUnit::Hz());
1339 if (it != m_aliasedStations.constEnd())
1344 const CFrequency f(
static_cast<int>(roundedFrequencyHz), CFrequencyUnit::Hz());
1355 roundedFrequencyHz = it->frequencyHz;
1357 << closest.
getCallsign() << frequencyHz << it->frequencyHz;
1363 u
"Station '%1' NOT found! Candidate was '%2'. Using original frequency %3 Hz")
1370 roundedFrequencyHz = it->frequencyHz;
1372 << frequencyHz << it->frequencyHz;
1376 return roundedFrequencyHz;
1379 bool CAfvClient::isVoiceServerAlive()
const
1381 QMutexLocker lock(&m_mutexConnection);
1382 return m_connection && m_connection->isVoiceServerAlive();
1385 const QString &CAfvClient::getVoiceServerUrl()
const
1387 QMutexLocker lock(&m_mutexConnection);
1389 static const QString e;
1390 if (!m_connection) {
return e; }
1391 return m_connection->getVoiceServerUrl();
1394 bool CAfvClient::fuzzyMatchCallsign(
const QString &callsign,
const QString &compareTo)
const
1396 if (callsign.isEmpty() || compareTo.isEmpty()) {
return false; }
1402 this->getPrefixSuffix(callsign, prefixA, suffixA);
1403 this->getPrefixSuffix(compareTo, prefixB, suffixB);
1404 return (prefixA == prefixB) && (suffixA == suffixB);
1407 void CAfvClient::getPrefixSuffix(
const QString &callsign, QString &prefix, QString &suffix)
const
1409 thread_local
const QRegularExpression separator(
"[(\\-|_)]");
1410 const QStringList parts = callsign.split(separator);
1413 prefix = parts.size() > 0 ? parts.first() : QString();
1414 suffix = parts.size() > 1 ? parts.last() : QString();
1421 case CComSystem::Com1:
return 0;
1422 case CComSystem::Com2:
return 1;
1430 if (comUnitToTransceiverId(CComSystem::Com1) ==
id) {
return CComSystem::Com1; }
1431 if (comUnitToTransceiverId(CComSystem::Com2) ==
id) {
return CComSystem::Com2; }
1432 return CComSystem::Com1;
1435 void CAfvClient::deferredInit()
1438 this->initTransceivers();
1441 this->onSettingsChanged();
1447 bool CAfvClient::hasContexts()
1458 QPointer<CAfvClient> myself(
this);
1460 if (!myself || !CAfvClient::hasContexts()) {
return; }
1461 myself->setComOutputVolumeDb(comUnit, valueDb);
1465 if (comUnit != CComSystem::Com1 && comUnit != CComSystem::Com2) {
return false; }
1466 if (valueDb > MaxDbOut) { valueDb = MaxDbOut; }
1467 else if (valueDb < MinDbOut) { valueDb = MinDbOut; }
1469 const double gainRatio = qPow(10, valueDb / 20.0);
1470 bool changed =
false;
1472 QMutexLocker lock(&m_mutexVolume);
1473 if (comUnit == CComSystem::Com1)
1475 changed = !qFuzzyCompare(m_outputVolumeDbCom1, valueDb);
1478 m_outputVolumeDbCom1 = valueDb;
1479 m_outputGainRatioCom1 = gainRatio;
1484 changed = !qFuzzyCompare(m_outputVolumeDbCom2, valueDb);
1487 m_outputVolumeDbCom2 = valueDb;
1488 m_outputGainRatioCom2 = gainRatio;
1496 if (!m_mutexSampleProviders.tryLock(1000)) {
return false; }
1498 if (m_soundcardSampleProvider)
1500 changed = m_soundcardSampleProvider->setGainRatioForTransceiver(comUnit, gainRatio);
1502 m_mutexSampleProviders.unlock();
1508 QMutexLocker lock(&m_mutex);
1509 if (m_input) {
return m_input->device(); }
1516 QMutexLocker lock(&m_mutex);
1517 if (m_output) {
return m_output->device(); }
1524 QMutexLocker lock(&m_mutex);
1525 if (!m_output || !m_input) {
return false; }
1535 return this->isConnected() ? Connected : Disconnected;
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
const context::IContextOwnAircraft * getIContextOwnAircraft() const
Direct access to contexts if a CCoreFacade has been initialized.
const context::IContextNetwork * getIContextNetwork() const
Direct access to contexts if a CCoreFacade has been initialized.
bool isShuttingDown() const
Is application shutting down?
const context::IContextSimulator * getIContextSimulator() const
Direct access to contexts if a CCoreFacade has been initialized.
ConnectionStatus
Connection status.
bool isConnected() const
Is connected to network?
QString getCallsign() const
}@
void setCallsign(const QString &getCallsign)
Copy operations.
void setReceiveAudio(bool value)
Receiving audio?
bool isConnected() const
Is connected?
virtual swift::misc::aviation::CAtcStationList getOnlineStationsForFrequency(const swift::misc::physical_quantities::CFrequency &frequency) const =0
Online stations for frequency.
virtual bool isConnected() const =0
Network connected?
virtual swift::misc::simulation::CSimulatedAircraft getOwnAircraft() const =0
Get own aircraft.
virtual swift::misc::aviation::CAircraftSituation getOwnAircraftSituation() const =0
Get own aircraft.
virtual swift::misc::simulation::settings::CSimulatorSettings getSimulatorSettings() const =0
Get the current simulator settings.
Base class for a long-lived worker object which lives in its own thread.
Base class with a member CIdentifier to be inherited by a class which has an identity in the environm...
Value object encapsulating information identifying a component of a modular distributed swift process...
static const QString & vatsimSpecific()
VATSIM specific.
static const QString & audio()
Audio related.
Class for emitting a log message.
static void preformatted(const CStatusMessage &statusMessage)
Sends a verbatim, preformatted message to the log.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & validationError(const char16_t(&format)[N])
Set the severity to error, providing a format string, and adding the validation category.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Streamable status message, e.g.
bool isFailure() const
Operation considered unsuccessful.
static QString currentThreadInfo()
Info about current thread, for debug messages.
static bool isInThisThread(const QObject *toBeTested)
Is the current thread the object's thread?
Value object encapsulating information of a audio device.
bool isInputDevice() const
Input device.
bool isOutputDevice() const
Output device.
bool isValid() const
Valid audio device object?
bool matchesNameTypeMachineName(const CAudioDeviceInfo &device) const
Matching name, type and machine.
const QString & getName() const
Get the device name.
Value object encapsulating information of audio related settings.
int getOutVolumeCom2() const
Get volume for com2 (audio) 0..100.
int getOutVolume() const
Get volume (audio) 0..100.
bool isAudioEffectsEnabled() const
Audio effects enabled?
int getInVolume() const
Get mic.volume (audio 0..100)
int getOutVolumeCom1() const
Get volume for com1 (audio) 0..100.
const geo::CCoordinateGeodetic & getPosition() const
Get position.
Value object encapsulating information about an ATC station.
const CCallsign & getCallsign() const
Get callsign.
Value object for a list of ATC stations.
const QString & asString() const
Get callsign (normalized)
Value object for a set of callsigns.
bool isTransmitEnabled() const
Enabled?
int getVolumeReceive() const
Output volume 0..100.
bool isReceiveEnabled() const
Enabled?
swift::misc::physical_quantities::CFrequency getFrequencyActive() const
Active frequency.
CONTAINER findClosest(int number, const ICoordinateGeodetic &coordinate) const
Find 0..n objects closest to the given coordinate.
int valueInteger(MU unit) const
As integer value.
double value(MU unit) const
Value in given unit.
Comprehensive information of an aircraft.
virtual geo::CLatitude latitude() const
Latitude.
const aviation::CComSystem & getCom2System() const
Get COM2 system.
virtual geo::CLongitude longitude() const
Longitude.
const aviation::CAltitude & getAltitude() const
Get altitude.
const aviation::CComSystem & getCom1System() const
Get COM1 system.
bool isComIntegrated() const
COM unit integration.
Free functions in swift::misc.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...
bool lastPacket
Used to indicate to receiver that the sender has stopped sending.
QByteArray audio
Opus compressed audio.
QString callsign
Callsign that audio originates from.
uint sequenceCounter
Receiver optionally uses this in reordering algorithm/gap detection.
double HeightMslM
Properties.
double HeightAglM
Properties.
quint32 frequencyHz
Properties.
Transmit transceiver DTO.
uint sequenceCounter
sequence counter
QByteArray audio
audio data
QStringList receivingCallsigns
callsigns
quint16 transceiverID
transceiver id
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.