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);
63 QMutexLocker lock(&m_mutexCallsign);
69 QMutexLocker lock(&m_mutexCallsign);
70 m_callsign = callsign;
75 QMutexLocker lock(&m_mutexConnection);
79 void CAfvClient::initTransceivers()
82 QMutexLocker lock(&m_mutexTransceivers);
83 m_transceivers = { { 0, UniCom, 48.5, 11.5, 1000.0, 1000.0 }, { 1, UniCom, 48.5, 11.5, 1000.0, 1000.0 } };
85 m_enabledTransceivers = { 0, 1 };
86 m_transmittingTransceivers = { { 0 } };
90 this->connectWithContexts();
93 this->onTimerUpdate();
96 void CAfvClient::connectWithContexts()
98 if (m_connectedWithContext) {
return; }
99 if (!hasContexts()) {
return; }
104 this->disconnect(context);
105 context->disconnect(
this);
109 &CAfvClient::onUpdateTransceiversFromContext, Qt::QueuedConnection);
111 &CAfvClient::toggleTransmissionCapability, Qt::QueuedConnection);
113 m_connectedWithContext =
true;
116 void CAfvClient::fetchSimulatorSettings()
119 if (!hasContexts()) {
return; }
124 QPointer<CAfvClient> myself(
this);
126 if (myself) { this->fetchSimulatorSettings(); }
132 const bool changed = integrated != m_integratedComUnit;
134 m_integratedComUnit = integrated;
135 if (changed) { emit this->updatedFromOwnAircraftCockpit(); }
138 void CAfvClient::connectTo(
const QString &cid,
const QString &password,
const QString &callsign,
139 const QString &client)
141 if (QThread::currentThread() != thread())
144 QPointer<CAfvClient> myself(
this);
145 QMetaObject::invokeMethod(
this, [=]() {
146 if (myself) { connectTo(cid, password, callsign, client); }
152 this->connectWithContexts();
153 this->setCallsign(callsign);
155 QPointer<CAfvClient> myself(
this);
156 if (!this->isConnected() && m_retryConnectAttempt == 0)
160 if (!myself) {
return; }
161 if (m_retryConnectAttempt > 0) {
return; }
164 this->retryConnectTo(cid, password, callsign, client, QStringLiteral(
"No connection afer 20secs"));
170 QMutexLocker lock(&m_mutexConnection);
173 m_connection->connectTo(
174 cid, password, callsign, client,
176 this, [=](
bool authenticated) {
177 if (!myself) {
return; }
180 const QVector<StationDto> aliasedStations = m_connection->getAllAliasedStations();
181 this->setAliasedStations(aliasedStations);
182 this->onTimerUpdate();
190 QMutexLocker lock2(&m_mutex);
191 if (m_voiceServerTimer) { m_voiceServerTimer->start(PositionUpdatesMs); }
193 m_retryConnectAttempt = 0;
194 emit this->connectionStatusChanged(Connected);
198 myself->retryConnectTo(
199 cid, password, callsign, client,
200 QStringLiteral(
"AFV authentication failed for '%1' callsign '%2'").arg(cid, callsign));
201 emit this->connectionStatusChanged(Disconnected);
207 void CAfvClient::disconnectFrom(
bool stop)
209 if (QThread::currentThread() != thread())
212 QPointer<CAfvClient> myself(
this);
213 QMetaObject::invokeMethod(
this, [=]() {
214 if (myself) { disconnectFrom(stop); }
222 QMutexLocker lock(&m_mutexConnection);
223 m_connection->disconnectFrom();
226 m_heartBeatFailures = 0;
227 m_retryConnectAttempt = 0;
228 m_fsdConnectMismatches = 0;
230 emit connectionStatusChanged(Disconnected);
232 if (stop) { this->stopAudio(); }
235 QStringList CAfvClient::availableInputDevices()
const
237 return CAudioDeviceInfoList::allInputDevices().getDeviceNames();
240 QStringList CAfvClient::availableOutputDevices()
const
242 return CAudioDeviceInfoList::allOutputDevices().getDeviceNames();
245 void CAfvClient::setBypassEffects(
bool value)
247 QMutexLocker lock(&m_mutexSampleProviders);
248 if (m_soundcardSampleProvider) { m_soundcardSampleProvider->setBypassEffects(value); }
251 bool CAfvClient::isOutputMuted()
const
253 const int v = this->getNormalizedMasterOutputVolume();
257 void CAfvClient::setOutputMuted(
bool mute)
259 if (this->isOutputMuted() == mute) {
return; }
260 this->setNormalizedMasterOutputVolume(mute ? 0 : 50);
261 emit this->changedOutputMute(mute);
264 void CAfvClient::startAudio()
268 this->startAudio(inputDevice, outputDevice);
273 if (QThread::currentThread() != this->thread())
276 QPointer<CAfvClient> myself(
this);
277 QMetaObject::invokeMethod(
this, [=]() {
278 if (myself) { startAudio(inputDevice, outputDevice); }
284 inputDevice.
isValid() ? inputDevice : CAudioDeviceInfo::getDefaultInputDevice();
286 outputDevice.
isValid() ? outputDevice : CAudioDeviceInfo::getDefaultOutputDevice();
290 "Wrong output device");
294 if (this->usesSameDevices(useInputDevice, useOutputDevice))
303 this->initTransceivers();
309 QMutexLocker lock { &m_mutexSampleProviders };
310 if (m_soundcardSampleProvider)
312 m_soundcardSampleProvider->disconnect();
313 m_soundcardSampleProvider->deleteLater();
316 connect(m_soundcardSampleProvider, &CSoundcardSampleProvider::receivingCallsignsChanged,
this,
317 &CAfvClient::onReceivingCallsignsChanged);
319 if (m_outputSampleProvider) { m_outputSampleProvider->deleteLater(); }
327 QMutexLocker lock(&m_mutex);
329 m_output->start(useOutputDevice, m_outputSampleProvider);
330 m_input->start(useInputDevice);
333 m_voiceServerTimer->start(PositionUpdatesMs);
337 this->setReceiveAudio(
true);
339 this->onSettingsChanged();
344 this->onTimerUpdate();
346 emit this->startedAudio(useInputDevice, useOutputDevice);
348 if (this->isOutputMuted())
351 this->setOutputMuted(
false);
355 void CAfvClient::startAudio(
const QString &inputDeviceName,
const QString &outputDeviceName)
357 const CAudioDeviceInfo i = CAudioDeviceInfoList::allInputDevices().findByName(inputDeviceName);
358 const CAudioDeviceInfo o = CAudioDeviceInfoList::allOutputDevices().findByName(outputDeviceName);
359 this->startAudio(i, o);
362 void CAfvClient::stopAudio()
364 if (QThread::currentThread() != this->thread())
367 QPointer<CAfvClient> myself(
this);
368 QMetaObject::invokeMethod(
this, [=]() {
369 if (myself) stopAudio();
381 this->setReceiveAudio(
false);
385 QMutexLocker lock { &m_mutex };
391 if (this->isOutputMuted()) { this->setOutputMuted(
false); }
393 emit this->inputVolumePeakVU(0.0);
394 emit this->outputVolumePeakVU(0.0);
395 emit this->stoppedAudio();
398 void CAfvClient::restartAudio()
408 QPointer<CAfvClient> myself(
this);
410 if (myself) { myself->startAudio(); }
440 void CAfvClient::setReceiveAudio(
bool receive)
442 QMutexLocker lock(&m_mutexConnection);
443 if (!m_connection) {
return; }
444 m_connection->setReceiveAudio(receive);
447 void CAfvClient::enableTransceiver(quint16
id,
bool enable)
450 QMutexLocker lock(&m_mutexTransceivers);
451 if (enable) { m_enabledTransceivers.insert(
id); }
452 else { m_enabledTransceivers.remove(
id); }
455 this->updateTransceivers();
460 this->enableTransceiver(comUnitToTransceiverId(comUnit), enable);
463 bool CAfvClient::isEnabledTransceiver(quint16
id)
const
466 const auto enabledTransceivers = this->getEnabledTransceivers();
467 if (!enabledTransceivers.contains(
id)) {
return false; }
469 const auto transceivers = this->getTransceivers();
472 if (dto.id ==
id) {
return true; }
479 return this->isEnabledTransceiver(comUnitToTransceiverId(comUnit));
482 void CAfvClient::updateComFrequency(quint16
id, quint32 frequencyHz)
484 if (
id != 0 &&
id != 1) {
return; }
487 quint32 roundedFrequencyHz =
static_cast<quint32
>(qRound(frequencyHz / 1000.0)) * 1000;
488 roundedFrequencyHz = this->getAliasFrequencyHz(roundedFrequencyHz);
492 QMutexLocker lockTransceivers(&m_mutexTransceivers);
493 if (m_transceivers.size() >=
id + 1)
495 if (m_transceivers[
id].frequencyHz != roundedFrequencyHz)
498 m_transceivers[id].frequencyHz = roundedFrequencyHz;
506 this->updateTransceivers(
false);
512 const quint32 freqHz =
static_cast<quint32
>(comFrequency.
valueInteger(CFrequencyUnit::Hz()));
513 this->updateComFrequency(comUnitToTransceiverId(comUnit), freqHz);
521 void CAfvClient::updatePosition(
double latitudeDeg,
double longitudeDeg,
double heightMeters)
523 QMutexLocker lock(&m_mutexTransceivers);
526 transceiver.LatDeg = latitudeDeg;
527 transceiver.LonDeg = longitudeDeg;
528 transceiver.HeightAglM = heightMeters;
529 transceiver.HeightMslM = heightMeters;
533 void CAfvClient::updateTransceivers(
bool updateFrequencies)
540 this->updatePosition(ownAircraft.
latitude().
value(CAngleUnit::deg()),
544 if (updateFrequencies)
546 this->updateComFrequency(CComSystem::Com1, ownAircraft.
getCom1System());
547 this->updateComFrequency(CComSystem::Com2, ownAircraft.
getCom2System());
552 const auto transceivers = this->getTransceivers();
553 const auto enabledTransceivers = this->getEnabledTransceivers();
554 const QString callsign = this->getCallsign();
557 QVector<TransceiverDto> newEnabledTransceivers;
558 for (
const TransceiverDto &transceiver : transceivers)
560 if (enabledTransceivers.contains(transceiver.id)) { newEnabledTransceivers.push_back(transceiver); }
565 QMutexLocker lock(&m_mutexConnection);
566 if (m_connection) { m_connection->updateTransceivers(callsign, newEnabledTransceivers); }
569 QMutexLocker lock(&m_mutexSampleProviders);
570 if (m_soundcardSampleProvider)
572 m_soundcardSampleProvider->updateRadioTransceivers(newEnabledTransceivers);
577 void CAfvClient::setTransmittingTransceiver(quint16 transceiverID)
580 this->setTransmittingTransceivers({ tx });
585 this->setTransmittingTransceiver(comUnitToTransceiverId(comUnit));
588 void CAfvClient::setTransmittingTransceivers(
const QVector<TxTransceiverDto> &transceivers)
590 QMutexLocker lock(&m_mutexTransceivers);
591 m_transmittingTransceivers = transceivers;
594 bool CAfvClient::isTransmittingTransceiver(quint16
id)
const
596 QMutexLocker lock(&m_mutexTransceivers);
599 if (dto.id ==
id) {
return true; }
606 return this->isTransmittingTransceiver(comUnitToTransceiverId(comUnit));
609 void CAfvClient::setRxTx(
bool rx1,
bool tx1,
bool rx2,
bool tx2)
611 QVector<TxTransceiverDto> txs;
622 this->setTransmittingTransceivers(txs);
624 QSet<quint16> enabledTransceivers;
625 if (rx1 || tx1) { enabledTransceivers.insert(comUnitToTransceiverId(CComSystem::Com1)); }
627 if (rx2 || tx2) { enabledTransceivers.insert(comUnitToTransceiverId(CComSystem::Com2)); }
630 QMutexLocker lock(&m_mutexTransceivers);
631 m_enabledTransceivers = enabledTransceivers;
635 this->onTimerUpdate();
638 void CAfvClient::getRxTx(
bool &rx1,
bool &tx1,
bool &rx2,
bool &tx2)
const
645 const QSet<quint16> enabled = getEnabledTransceivers();
646 rx1 = enabled.contains(comUnitToTransceiverId(CComSystem::Com1));
647 rx2 = enabled.contains(comUnitToTransceiverId(CComSystem::Com2));
649 const QVector<TxTransceiverDto> transmits = getTransmittingTransceivers();
652 if (dto.id == comUnitToTransceiverId(CComSystem::Com1)) { tx1 =
true; }
653 if (dto.id == comUnitToTransceiverId(CComSystem::Com2)) { tx2 =
true; }
657 QVector<TransceiverDto> CAfvClient::getTransceivers()
const
659 QMutexLocker lock(&m_mutexTransceivers);
660 return m_transceivers;
663 QSet<quint16> CAfvClient::getEnabledTransceivers()
const
665 QMutexLocker lock(&m_mutexTransceivers);
666 return m_enabledTransceivers;
669 QVector<TxTransceiverDto> CAfvClient::getTransmittingTransceivers()
const
671 QMutexLocker lock(&m_mutexTransceivers);
672 return m_transmittingTransceivers;
675 void CAfvClient::setPtt(
bool active)
683 if (active && m_disableTransmissionCapability)
689 if (m_transmit == active) {
return; }
694 QMutexLocker lock(&m_mutexSampleProviders);
695 if (m_soundcardSampleProvider) { m_soundcardSampleProvider->pttUpdate(active, m_transmittingTransceivers); }
710 emit this->ptt(active, this->identifier());
713 double CAfvClient::getInputVolumeDb()
const
715 QMutexLocker lock(&m_mutex);
716 return m_inputVolumeDb;
719 bool CAfvClient::setInputVolumeDb(
double valueDb)
724 QPointer<CAfvClient> myself(
this);
726 if (!myself || !CAfvClient::hasContexts()) {
return; }
727 myself->setInputVolumeDb(valueDb);
732 if (valueDb > MaxDbIn) { valueDb = MaxDbIn; }
733 else if (valueDb < MinDbIn) { valueDb = MinDbIn; }
735 QMutexLocker lock(&m_mutex);
736 bool changed = !qFuzzyCompare(m_inputVolumeDb, valueDb);
739 m_inputVolumeDb = valueDb;
742 const double gainRatio = qPow(10, valueDb / 20.0);
743 changed = m_input->setGainRatio(gainRatio);
751 QMutexLocker lock(&m_mutexVolume);
752 if (comUnit == CComSystem::Com1)
753 return m_outputVolumeDbCom1;
754 else if (comUnit == CComSystem::Com2)
755 return m_outputVolumeDbCom2;
756 qFatal(
"Invalid COM unit");
762 QMutexLocker lock(&m_mutexVolume);
763 if (comUnit == CComSystem::Com1)
764 return m_outputGainRatioCom1;
765 else if (comUnit == CComSystem::Com2)
766 return m_outputGainRatioCom2;
767 qFatal(
"Invalid COM unit");
771 int CAfvClient::getNormalizedInputVolume()
const
773 const double db = this->getInputVolumeDb();
774 const double range = MaxDbIn - MinDbIn;
775 const int i = qRound((db - MinDbIn) / range * 100);
779 int CAfvClient::getNormalizedMasterOutputVolume()
const
781 QMutexLocker lock(&m_mutexVolume);
782 return m_outputMasterVolumeNormalized;
787 QMutexLocker lock(&m_mutexVolume);
788 if (comUnit == CComSystem::Com1) {
return m_outputVolumeCom1Normalized; }
789 else if (comUnit == CComSystem::Com2) {
return m_outputVolumeCom2Normalized; }
790 qFatal(
"Invalid ComUnit");
794 bool CAfvClient::setNormalizedInputVolume(
int volume)
796 if (volume < 0) { volume = 0; }
797 else if (volume > 100) { volume = 100; }
798 const double range = MaxDbIn - MinDbIn;
799 const double dB = MinDbIn + (volume * range / 100.0);
802 return this->setInputVolumeDb(dB);
805 bool CAfvClient::setNormalizedMasterOutputVolume(
int volume)
810 QPointer<CAfvClient> myself(
this);
812 if (!myself || !CAfvClient::hasContexts()) {
return; }
813 myself->setNormalizedMasterOutputVolume(volume);
818 bool changed =
false;
820 QMutexLocker lock(&m_mutexVolume);
821 changed = m_outputMasterVolumeNormalized != volume;
822 if (changed) { m_outputMasterVolumeNormalized = volume; }
826 int com1Normalized = getNormalizedComOutputVolume(CComSystem::Com1);
827 int com2Normalized = getNormalizedComOutputVolume(CComSystem::Com2);
828 setNormalizedComOutputVolume(CComSystem::Com1, com1Normalized);
829 setNormalizedComOutputVolume(CComSystem::Com2, com2Normalized);
836 if (volume < 0) { volume = 0; }
837 else if (volume > 100) { volume = 100; }
840 if (comUnit == CComSystem::Com1) { m_outputVolumeCom1Normalized = volume; }
841 else if (comUnit == CComSystem::Com2) { m_outputVolumeCom2Normalized = volume; }
842 else { qFatal(
"Invalid ComUnit"); }
845 volume = qRound((
double)volume * getNormalizedMasterOutputVolume() / 100);
848 double range = MaxDbOut;
850 if (volume >= 50) { volume -= 50; }
854 range = qAbs(MinDbOut);
856 dB += (volume * range / 50.0);
859 return this->setComOutputVolumeDb(comUnit, dB);
862 double CAfvClient::getInputVolumePeakVU()
const
864 QMutexLocker lock(&m_mutexInputStream);
865 return m_inputVolumeStream.PeakVU;
868 double CAfvClient::getOutputVolumePeakVU()
const
870 QMutexLocker lock(&m_mutexOutputStream);
871 return m_outputVolumeStream.PeakVU;
876 const bool transmit = m_transmit;
877 const bool loopback = m_loopbackOn;
878 const bool transmitHistory = m_transmitHistory;
879 const auto transceivers = this->getTransceivers();
881 if (loopback && transmit)
885 audioData.
callsign = QStringLiteral(
"loopback");
889 const RxTransceiverDto com1 = { 0, transceivers.size() > 0 ? transceivers[0].frequencyHz : UniCom, 1.0 };
890 const RxTransceiverDto com2 = { 1, transceivers.size() > 1 ? transceivers[1].frequencyHz : UniCom, 1.0 };
892 QMutexLocker lock(&m_mutexSampleProviders);
893 m_soundcardSampleProvider->addOpusSamples(audioData, { com1, com2 });
897 if (!this->isConnected()) {
return; }
899 const QString callsign = this->getCallsign();
900 const auto transmittingTransceivers = this->getTransmittingTransceivers();
901 if (!transmittingTransceivers.isEmpty())
905 AudioTxOnTransceiversDto dto;
906 dto.callsign = callsign.toStdString();
908 dto.audio = std::vector<char>(args.
audio.begin(), args.
audio.end());
909 dto.lastPacket =
false;
911 std::vector<TxTransceiverDto>(transmittingTransceivers.begin(), transmittingTransceivers.end());
912 QMutexLocker lock(&m_mutexConnection);
913 m_connection->sendToVoiceServer(dto);
916 if (!transmit && transmitHistory)
918 AudioTxOnTransceiversDto dto;
919 dto.callsign = callsign.toStdString();
921 dto.audio = std::vector<char>(args.
audio.begin(), args.
audio.end());
922 dto.lastPacket =
true;
924 std::vector<TxTransceiverDto>(transmittingTransceivers.begin(), transmittingTransceivers.end());
925 QMutexLocker lock(&m_mutexConnection);
926 m_connection->sendToVoiceServer(dto);
928 m_transmitHistory = transmit;
932 void CAfvClient::audioOutDataAvailable(
const AudioRxOnTransceiversDto &dto)
935 audioData.
audio = QByteArray(dto.audio.data(),
static_cast<int>(dto.audio.size()));
936 audioData.callsign = QString::fromStdString(dto.callsign);
937 audioData.lastPacket = dto.lastPacket;
938 audioData.sequenceCounter = dto.sequenceCounter;
940 QMutexLocker lock(&m_mutexSampleProviders);
941 m_soundcardSampleProvider->addOpusSamples(
942 audioData, QVector<RxTransceiverDto>(dto.transceivers.begin(), dto.transceivers.end()));
949 QMutexLocker lock(&m_mutexInputStream);
950 m_inputVolumeStream = args;
952 emit inputVolumePeakVU(args.
PeakVU);
959 QMutexLocker lock(&m_mutexOutputStream);
960 m_outputVolumeStream = args;
962 emit outputVolumePeakVU(args.
PeakVU);
965 QString CAfvClient::getReceivingCallsignsStringCom1()
const
967 QMutexLocker lock(&m_mutex);
968 if (!m_soundcardSampleProvider)
return {};
969 return m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com1));
972 QString CAfvClient::getReceivingCallsignsStringCom2()
const
974 QMutexLocker lock(&m_mutexSampleProviders);
975 if (!m_soundcardSampleProvider)
return {};
976 return m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com2));
981 QMutexLocker lock(&m_mutexSampleProviders);
982 if (!m_soundcardSampleProvider)
return {};
983 return m_soundcardSampleProvider->getReceivingCallsigns(comUnitToTransceiverId(CComSystem::Com1));
988 QMutexLocker lock(&m_mutexSampleProviders);
989 if (!m_soundcardSampleProvider)
return {};
990 return m_soundcardSampleProvider->getReceivingCallsigns(comUnitToTransceiverId(CComSystem::Com2));
993 QStringList CAfvClient::getReceivingCallsignsStringCom1Com2()
const
996 QMutexLocker lock(&m_mutexSampleProviders);
997 if (!m_soundcardSampleProvider) {
return { { QString(), QString() } }; }
998 coms << m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com1));
999 coms << m_soundcardSampleProvider->getReceivingCallsignsString(comUnitToTransceiverId(CComSystem::Com2));
1003 bool CAfvClient::updateVoiceServerUrl(
const QString &url)
1005 QMutexLocker lock(&m_mutexConnection);
1006 if (!m_connection) {
return false; }
1007 return m_connection->updateVoiceServerUrl(url);
1010 void CAfvClient::gracefulShutdown()
1013 this->disconnectFrom();
1014 this->quitAndWait();
1018 void CAfvClient::initialize()
1023 void CAfvClient::cleanup()
1026 if (m_winCoInitialized)
1029 m_winCoInitialized =
false;
1034 void CAfvClient::onTimerUpdate()
1040 this->updateFromOwnAircraft(aircraft,
false);
1043 this->autoLogoffWithoutFsdNetwork();
1046 this->fetchSimulatorSettings();
1051 this->updateTransceivers();
1055 this->checkServerHeartbeat();
1058 void CAfvClient::checkServerHeartbeat()
1060 if (!this->isStarted()) {
return; }
1061 if (!this->isConnected()) {
return; }
1063 if (this->isVoiceServerAlive())
1065 m_heartBeatFailures = 0;
1071 const int failures = ++m_heartBeatFailures;
1072 if (failures < 2) {
return; }
1074 QString un, pw, cs, client;
1076 QMutexLocker lock(&m_mutexConnection);
1077 un = m_connection->getUserName();
1078 pw = m_connection->getPassword();
1079 cs = m_connection->getCallsign();
1080 client = m_connection->getClient();
1082 if (un.isEmpty() || pw.isEmpty()) {
return; }
1085 if (this->isConnected()) { this->disconnectFrom(
false); }
1087 QPointer<CAfvClient> myself(
this);
1089 if (!myself) {
return; }
1090 const QString reason = QStringLiteral(
"Heartbeat failed %1 times").arg(failures);
1091 this->retryConnectTo(un, pw, cs, client, reason);
1095 void CAfvClient::onSettingsChanged()
1097 const CSettings audioSettings = m_audioSettings.get();
1103 this->setNormalizedInputVolume(iv);
1104 this->setNormalizedMasterOutputVolume(ov);
1105 this->setNormalizedComOutputVolume(CComSystem::Com1, ov1);
1106 this->setNormalizedComOutputVolume(CComSystem::Com2, ov2);
1110 void CAfvClient::autoLogoffWithoutFsdNetwork()
1112 if (!hasContexts()) {
return; }
1113 if (!this->isConnected())
1115 m_fsdConnectMismatches = 0;
1122 m_fsdConnectMismatches = 0;
1125 if (++m_fsdConnectMismatches < 2) {
return; }
1127 CLogMessage(
this).
warning(u
"Auto logoff AFV client because FSD no longer connected");
1128 this->disconnectFrom();
1137 transceiverCom1.
id = comUnitToTransceiverId(CComSystem::Com1);
1138 transceiverCom2.
id = comUnitToTransceiverId(CComSystem::Com2);
1141 const double latDeg = aircraft.
latitude().
value(CAngleUnit::deg());
1142 const double lngDeg = aircraft.
longitude().
value(CAngleUnit::deg());
1145 transceiverCom1.
LatDeg = transceiverCom2.
LatDeg = latDeg;
1146 transceiverCom1.
LonDeg = transceiverCom2.
LonDeg = lngDeg;
1156 transceiverCom1.
frequencyHz = this->getAliasFrequencyHz(f1);
1157 transceiverCom2.
frequencyHz = this->getAliasFrequencyHz(f2);
1159 QVector<TransceiverDto> newEnabledTransceivers;
1160 if (m_integratedComUnit)
1170 this->setNormalizedComOutputVolume(CComSystem::Com1, vol1);
1171 this->setNormalizedComOutputVolume(CComSystem::Com2, vol2);
1176 const bool e1 = rx1;
1177 const bool e2 = rx2;
1180 const QVector<TransceiverDto> newTransceivers { transceiverCom1, transceiverCom2 };
1181 QSet<quint16> newEnabledTransceiverIds;
1182 QVector<TxTransceiverDto> newTransmittingTransceivers;
1185 newEnabledTransceivers.push_back(transceiverCom1);
1186 newEnabledTransceiverIds.insert(transceiverCom1.
id);
1190 newEnabledTransceivers.push_back(transceiverCom2);
1191 newEnabledTransceiverIds.insert(transceiverCom2.
id);
1195 if (tx1 && e1) { newTransmittingTransceivers.push_back(transceiverCom1); }
1196 else if (tx2 && e2) { newTransmittingTransceivers.push_back(transceiverCom2); }
1200 QMutexLocker lock(&m_mutexTransceivers);
1201 m_transceivers = newTransceivers;
1202 m_enabledTransceivers = newEnabledTransceiverIds;
1203 m_transmittingTransceivers = newTransmittingTransceivers;
1209 const QSet<quint16> ids = getEnabledTransceivers();
1210 if (ids.contains(comUnitToTransceiverId(CComSystem::Com1)))
1212 newEnabledTransceivers.push_back(transceiverCom1);
1215 if (ids.contains(comUnitToTransceiverId(CComSystem::Com2)))
1217 newEnabledTransceivers.push_back(transceiverCom2);
1222 const QString callsign = this->getCallsign();
1225 QMutexLocker lock(&m_mutexConnection);
1229 m_connection->updateTransceivers(callsign, newEnabledTransceivers);
1234 QMutexLocker lock(&m_mutexSampleProviders);
1235 if (m_soundcardSampleProvider)
1237 m_soundcardSampleProvider->updateRadioTransceivers(newEnabledTransceivers);
1242 if (withSignals) { emit this->updatedFromOwnAircraftCockpit(); }
1247 if (originator == this->identifier()) {
return; }
1248 this->updateFromOwnAircraft(aircraft);
1258 case CComSystem::Com1:
1261 callsignsCom2 = this->getReceivingCallsignsCom2();
1264 case CComSystem::Com2:
1266 callsignsCom1 = this->getReceivingCallsignsCom1();
1270 emit this->receivedCallsignsChanged(callsignsCom1, callsignsCom2);
1271 emit this->receivingCallsignsChanged(args);
1274 void CAfvClient::retryConnectTo(
const QString &cid,
const QString &password,
const QString &callsign,
1275 const QString &client,
const QString &reason)
1277 if (this->isConnected()) {
return; }
1278 m_retryConnectAttempt++;
1280 const int retrySecs = qMin(3 * 60, m_retryConnectAttempt * 30);
1282 << retrySecs << m_retryConnectAttempt;
1283 this->reconnectTo(cid, password, callsign, client, retrySecs * 1000, msg);
1286 void CAfvClient::reconnectTo(
const QString &cid,
const QString &password,
const QString &callsign,
1292 emit this->afvConnectionFailure(msg);
1295 QPointer<CAfvClient> myself(
this);
1297 if (!myself) {
return; }
1298 if (myself->isConnected()) { return; }
1299 this->connectTo(cid, password, callsign, client);
1303 void CAfvClient::toggleTransmissionCapability(
bool disableTransmission)
1305 if (m_disableTransmissionCapability == disableTransmission) {
return; }
1306 m_disableTransmissionCapability = disableTransmission;
1308 if (disableTransmission)
1315 QVector<StationDto> CAfvClient::getAliasedStations()
const
1317 QMutexLocker lock(&m_mutex);
1318 return m_aliasedStations;
1321 void CAfvClient::setAliasedStations(
const QVector<StationDto> &stations)
1323 QMutexLocker lock(&m_mutex);
1324 m_aliasedStations = stations;
1327 quint32 CAfvClient::getAliasFrequencyHz(quint32 frequencyHz)
const
1330 quint32 roundedFrequencyHz =
static_cast<quint32
>(qRound(frequencyHz / 1000.0)) * 1000;
1334 QMutexLocker lock(&m_mutex);
1335 CFrequency roundedFrequency(
static_cast<int>(roundedFrequencyHz), CFrequencyUnit::Hz());
1336 const auto it = std::find_if(
1337 m_aliasedStations.constBegin(), m_aliasedStations.constEnd(), [roundedFrequency](
const StationDto &d) {
1338 if (d.frequencyAliasHz > 100000000 &&
1339 roundedFrequency.value(CFrequencyUnit::Hz()) > 100000000)
1341 const int aliasedFreqHz = static_cast<int>(qRound(d.frequencyAliasHz / 1000.0)) * 1000;
1342 return CComSystem::isSameFrequency(CFrequency(aliasedFreqHz, CFrequencyUnit::Hz()),
1345 return d.frequencyAliasHz == roundedFrequency.value(CFrequencyUnit::Hz());
1348 if (it != m_aliasedStations.constEnd())
1353 const CFrequency f(
static_cast<int>(roundedFrequencyHz), CFrequencyUnit::Hz());
1364 roundedFrequencyHz = it->frequencyHz;
1366 << closest.
getCallsign() << frequencyHz << it->frequencyHz;
1372 u
"Station '%1' NOT found! Candidate was '%2'. Using original frequency %3 Hz")
1379 roundedFrequencyHz = it->frequencyHz;
1381 << frequencyHz << it->frequencyHz;
1385 return roundedFrequencyHz;
1388 bool CAfvClient::isVoiceServerAlive()
const
1390 QMutexLocker lock(&m_mutexConnection);
1391 return m_connection && m_connection->isVoiceServerAlive();
1394 const QString &CAfvClient::getVoiceServerUrl()
const
1396 QMutexLocker lock(&m_mutexConnection);
1398 static const QString e;
1399 if (!m_connection) {
return e; }
1400 return m_connection->getVoiceServerUrl();
1403 bool CAfvClient::fuzzyMatchCallsign(
const QString &callsign,
const QString &compareTo)
const
1405 if (callsign.isEmpty() || compareTo.isEmpty()) {
return false; }
1411 this->getPrefixSuffix(callsign, prefixA, suffixA);
1412 this->getPrefixSuffix(compareTo, prefixB, suffixB);
1413 return (prefixA == prefixB) && (suffixA == suffixB);
1416 void CAfvClient::getPrefixSuffix(
const QString &callsign, QString &prefix, QString &suffix)
const
1418 thread_local
const QRegularExpression separator(
"[(\\-|_)]");
1419 const QStringList parts = callsign.split(separator);
1422 prefix = parts.size() > 0 ? parts.first() : QString();
1423 suffix = parts.size() > 1 ? parts.last() : QString();
1430 case CComSystem::Com1:
return 0;
1431 case CComSystem::Com2:
return 1;
1439 if (comUnitToTransceiverId(CComSystem::Com1) ==
id) {
return CComSystem::Com1; }
1440 if (comUnitToTransceiverId(CComSystem::Com2) ==
id) {
return CComSystem::Com2; }
1441 return CComSystem::Com1;
1444 void CAfvClient::deferredInit()
1447 this->initTransceivers();
1450 this->onSettingsChanged();
1456 bool CAfvClient::hasContexts()
1467 QPointer<CAfvClient> myself(
this);
1469 if (!myself || !CAfvClient::hasContexts()) {
return; }
1470 myself->setComOutputVolumeDb(comUnit, valueDb);
1474 if (comUnit != CComSystem::Com1 && comUnit != CComSystem::Com2) {
return false; }
1475 if (valueDb > MaxDbOut) { valueDb = MaxDbOut; }
1476 else if (valueDb < MinDbOut) { valueDb = MinDbOut; }
1478 const double gainRatio = qPow(10, valueDb / 20.0);
1479 bool changed =
false;
1481 QMutexLocker lock(&m_mutexVolume);
1482 if (comUnit == CComSystem::Com1)
1484 changed = !qFuzzyCompare(m_outputVolumeDbCom1, valueDb);
1487 m_outputVolumeDbCom1 = valueDb;
1488 m_outputGainRatioCom1 = gainRatio;
1493 changed = !qFuzzyCompare(m_outputVolumeDbCom2, valueDb);
1496 m_outputVolumeDbCom2 = valueDb;
1497 m_outputGainRatioCom2 = gainRatio;
1505 if (!m_mutexSampleProviders.tryLock(1000)) {
return false; }
1507 if (m_soundcardSampleProvider)
1509 changed = m_soundcardSampleProvider->setGainRatioForTransceiver(comUnit, gainRatio);
1511 m_mutexSampleProviders.unlock();
1517 QMutexLocker lock(&m_mutex);
1518 if (m_input) {
return m_input->device(); }
1525 QMutexLocker lock(&m_mutex);
1526 if (m_output) {
return m_output->device(); }
1533 QMutexLocker lock(&m_mutex);
1534 if (!m_output || !m_input) {
return false; }
1544 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.
QTimer m_updateTimer
timer which can be used by implementing classes
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.