swift
airspacemonitor.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "core/airspacemonitor.h"
5 
6 #include <QDateTime>
7 #include <QTime>
8 #include <QVariant>
9 #include <Qt>
10 
11 #include "config/buildconfig.h"
12 #include "core/aircraftmatcher.h"
13 #include "core/airspaceanalyzer.h"
14 #include "core/application.h"
16 #include "core/fsd/fsdclient.h"
18 #include "core/webdataservices.h"
24 #include "misc/eventloop.h"
26 #include "misc/iterator.h"
27 #include "misc/logmessage.h"
29 #include "misc/network/user.h"
31 #include "misc/pq/units.h"
33 #include "misc/range.h"
34 #include "misc/sequence.h"
35 #include "misc/statusmessagelist.h"
36 #include "misc/test/testing.h"
37 #include "misc/threadutils.h"
38 #include "misc/variant.h"
39 #include "misc/verify.h"
40 #include "misc/worker.h"
41 
42 using namespace swift::config;
43 using namespace swift::misc;
44 using namespace swift::misc::aviation;
45 using namespace swift::misc::geo;
46 using namespace swift::misc::simulation;
47 using namespace swift::misc::test;
48 using namespace swift::misc::json;
49 using namespace swift::misc::network;
50 using namespace swift::misc::physical_quantities;
51 using namespace swift::misc::weather;
52 using namespace swift::core::fsd;
53 using namespace swift::core::vatsim;
54 
55 namespace swift::core
56 {
57  CAirspaceMonitor::CAirspaceMonitor(IOwnAircraftProvider *ownAircraftProvider,
58  IAircraftModelSetProvider *modelSetProvider, CFSDClient *fsdClient,
59  QObject *parent)
60  : CRemoteAircraftProvider(parent), COwnAircraftAware(ownAircraftProvider), m_modelSetProvider(modelSetProvider),
61  m_fsdClient(fsdClient), m_analyzer(new CAirspaceAnalyzer(ownAircraftProvider, m_fsdClient, this))
62  {
63  this->setObjectName("CAirspaceMonitor");
64  this->enableReverseLookupMessages(sApp->isDeveloperFlagSet() || CBuildConfig::isLocalDeveloperDebugBuild() ?
65  RevLogEnabled :
66  RevLogEnabledSimplified);
67 
68  // FSD runs in its own thread!
69  connect(m_fsdClient, &CFSDClient::atcDataUpdateReceived, this, &CAirspaceMonitor::onAtcPositionUpdate);
70  connect(m_fsdClient, &CFSDClient::atisReplyReceived, this, &CAirspaceMonitor::onAtisReceived);
71  connect(m_fsdClient, &CFSDClient::atisLogoffTimeReplyReceived, this,
72  &CAirspaceMonitor::onAtisLogoffTimeReceived);
73  connect(m_fsdClient, &CFSDClient::flightPlanReceived, this, &CAirspaceMonitor::onFlightPlanReceived);
74  connect(m_fsdClient, &CFSDClient::realNameResponseReceived, this, &CAirspaceMonitor::onRealNameReplyReceived);
75  connect(m_fsdClient, &CFSDClient::planeInformationReceived, this, &CAirspaceMonitor::onIcaoCodesReceived);
76  connect(m_fsdClient, &CFSDClient::deletePilotReceived, this, &CAirspaceMonitor::onPilotDisconnected);
77  connect(m_fsdClient, &CFSDClient::deleteAtcReceived, this, &CAirspaceMonitor::onAtcControllerDisconnected);
78  connect(m_fsdClient, &CFSDClient::pilotDataUpdateReceived, this, &CAirspaceMonitor::onAircraftUpdateReceived);
79  connect(m_fsdClient, &CFSDClient::interimPilotDataUpdatedReceived, this,
80  &CAirspaceMonitor::onAircraftInterimUpdateReceived);
81  connect(m_fsdClient, &CFSDClient::visualPilotDataUpdateReceived, this,
82  &CAirspaceMonitor::onAircraftVisualUpdateReceived);
83  connect(m_fsdClient, &CFSDClient::euroscopeSimDataUpdatedReceived, this,
84  &CAirspaceMonitor::onAircraftSimDataUpdateReceived);
85  connect(m_fsdClient, &CFSDClient::com1FrequencyResponseReceived, this, &CAirspaceMonitor::onFrequencyReceived);
86  connect(m_fsdClient, &CFSDClient::capabilityResponseReceived, this,
87  &CAirspaceMonitor::onCapabilitiesReplyReceived);
88  connect(m_fsdClient, &CFSDClient::planeInformationFsinnReceived, this,
89  &CAirspaceMonitor::onCustomFSInnPacketReceived);
90  connect(m_fsdClient, &CFSDClient::serverResponseReceived, this, &CAirspaceMonitor::onServerReplyReceived);
91  connect(m_fsdClient, &CFSDClient::aircraftConfigReceived, this, &CAirspaceMonitor::onAircraftConfigReceived);
92  connect(m_fsdClient, &CFSDClient::connectionStatusChanged, this, &CAirspaceMonitor::onConnectionStatusChanged);
93 
94  Q_ASSERT_X(sApp && sApp->hasWebDataServices(), Q_FUNC_INFO, "Missing data reader");
95 
96  if (this->supportsVatsimDataFile())
97  {
98  connect(sApp->getWebDataServices()->getVatsimDataFileReader(), &CVatsimDataFileReader::dataFileRead, this,
99  &CAirspaceMonitor::onReceivedVatsimDataFile);
100  }
101 
102  // Force snapshot in the main event loop
105 
106  // Analyzer
107  connect(m_analyzer, &CAirspaceAnalyzer::timeoutAircraft, this, &CAirspaceMonitor::onPilotDisconnected,
109  connect(m_analyzer, &CAirspaceAnalyzer::timeoutAtc, this, &CAirspaceMonitor::onAtcControllerDisconnected,
111 
112  // timers
113  connect(&m_fastProcessTimer, &QTimer::timeout, this, &CAirspaceMonitor::fastProcessing);
114  connect(&m_slowProcessTimer, &QTimer::timeout, this, &CAirspaceMonitor::slowProcessing);
115  m_fastProcessTimer.start(FastProcessIntervalMs);
116  m_slowProcessTimer.start(SlowProcessIntervalMs);
117 
118  // dot command
120  }
121 
122  bool CAirspaceMonitor::updateFastPositionEnabled(const CCallsign &callsign, bool enableFastPositonUpdates)
123  {
124  const bool r = CRemoteAircraftProvider::updateFastPositionEnabled(callsign, enableFastPositonUpdates);
125  if (m_fsdClient && sApp && !sApp->isShuttingDown())
126  {
127  // thread safe update of m_network
128  const QPointer<CAirspaceMonitor> myself(this);
129  QTimer::singleShot(0, m_fsdClient, [=, this] {
130  if (!myself) { return; }
131  if (m_fsdClient)
132  {
133  if (enableFastPositonUpdates) { m_fsdClient->addInterimPositionReceiver(callsign); }
134  else { m_fsdClient->removeInterimPositionReceiver(callsign); }
135  }
136  });
137  }
138  return r;
139  }
140 
142  {
144  return cats;
145  }
146 
148  {
149  Q_ASSERT_X(m_analyzer, Q_FUNC_INFO, "No analyzer");
150  return m_analyzer->getLatestAirspaceAircraftSnapshot();
151  }
152 
154  {
155  CFlightPlan plan;
156  QPointer<CAirspaceMonitor> myself(this);
157 
158  // use cache, but not for own callsign (always reload)
159  if (m_flightPlanCache.contains(callsign)) { plan = m_flightPlanCache[callsign]; }
160  const bool ownAircraft = this->getOwnCallsign() == callsign;
161  if (ownAircraft || !plan.wasSentOrLoaded() || plan.timeDiffSentOrLoadedMs() > 30 * 1000)
162  {
163  // outdated, or not in cache at all, or NOT own aircraft
164  plan = CFlightPlan(); // reset
165  m_flightPlanCache.remove(callsign); // loading FP from network
166  m_fsdClient->sendClientQueryFlightPlan(callsign);
167 
168  // with this little trick we try to make an asynchronous signal / slot based approach
169  // a synchronous return value
170  CEventLoop eventLoop(this);
171  eventLoop.stopWhen(m_fsdClient, &CFSDClient::flightPlanReceived,
172  [=](const auto &cs, const auto &) { return cs == callsign; });
173  if (eventLoop.exec(1500))
174  {
175  if (!myself || !sApp || sApp->isShuttingDown()) // cppcheck-suppress knownConditionTrueFalse
176  {
177  return {};
178  }
179  if (m_flightPlanCache.contains(callsign)) { plan = m_flightPlanCache[callsign]; }
180  }
181  }
182  return plan;
183  }
184 
186  {
187  if (callsign.isEmpty()) { return {}; }
188 
189  // full flight plan's remarks
190  if (m_flightPlanCache.contains(callsign)) { return m_flightPlanCache[callsign].getFlightPlanRemarks(); }
191 
192  // remarks only
193  if (this->supportsVatsimDataFile())
194  {
196  }
197 
198  // unsupported
199  return {};
200  }
201 
203  {
205  return m_atcStationsOnline;
206  }
207 
209  {
210  CUserList users;
211  for (const CAtcStation &station : m_atcStationsOnline)
212  {
213  CUser user = station.getController();
214  if (!user.hasCallsign()) { user.setCallsign(station.getCallsign()); }
215  users.push_back(user);
216  }
217  for (const CSimulatedAircraft &aircraft : this->getAircraftInRange())
218  {
219  CUser user = aircraft.getPilot();
220  if (!user.hasCallsign()) { user.setCallsign(aircraft.getCallsign()); }
221  users.push_back(user);
222  }
223  return users;
224  }
225 
227  {
228  CUserList users;
229  if (callsigns.isEmpty()) { return users; }
230  CCallsignSet searchList(callsigns);
231 
232  // myself, which is not in the lists below
233  const CSimulatedAircraft myAircraft(getOwnAircraft());
234  if (!myAircraft.getCallsign().isEmpty() && searchList.contains(myAircraft.getCallsign()))
235  {
236  searchList.remove(myAircraft.getCallsign());
237  users.push_back(myAircraft.getPilot());
238  }
239 
240  // do aircraft first, this will handle most callsigns
241  for (const CSimulatedAircraft &aircraft : this->getAircraftInRange())
242  {
243  if (searchList.isEmpty()) { break; }
244  const CCallsign callsign = aircraft.getCallsign();
245  if (searchList.contains(callsign))
246  {
247  const CUser user = aircraft.getPilot();
248  users.push_back(user);
249  searchList.remove(callsign);
250  }
251  }
252 
253  for (const CAtcStation &station : m_atcStationsOnline)
254  {
255  if (searchList.isEmpty()) { break; }
256  const CCallsign callsign = station.getCallsign();
257  if (searchList.contains(callsign))
258  {
259  const CUser user = station.getController();
260  users.push_back(user);
261  searchList.remove(callsign);
262  }
263  }
264 
265  // we might have unresolved callsigns
266  // those are the ones not in range
267  for (const CCallsign &callsign : std::as_const(searchList))
268  {
269  const CUserList usersByCallsign = sApp->getWebDataServices()->getUsersForCallsign(callsign);
270  if (usersByCallsign.isEmpty())
271  {
272  const CUser user(callsign);
273  users.push_back(user);
274  }
275  else { users.push_back(usersByCallsign[0]); }
276  }
277  return users;
278  }
279 
281  {
282  CAtcStationList stations = m_atcStationsOnline.findIfComUnitTunedInChannelSpacing(comSystem);
283  if (stations.isEmpty()) { return {}; }
285  return stations.front();
286  }
287 
289  {
290  if (!this->isConnectedAndNotShuttingDown()) { return; }
291  const CSimulatedAircraftList aircraftInRange(this->getAircraftInRange());
292  for (const CSimulatedAircraft &aircraft : aircraftInRange)
293  {
294  // staggered version
295  const CCallsign cs(aircraft.getCallsign());
296  if (!m_queryPilot.contains(cs)) { m_queryPilot.enqueue(aircraft.getCallsign()); }
297  }
298  }
299 
301  {
302  if (!this->isConnectedAndNotShuttingDown()) { return; }
303  const CAtcStationList stations(this->getAtcStationsOnline());
304  for (const CAtcStation &station : stations)
305  {
306  const CCallsign cs = station.getCallsign();
307  if (!m_queryAtis.contains(cs)) { m_queryAtis.enqueue(cs); }
308  }
309  }
310 
311  void CAirspaceMonitor::testCreateDummyOnlineAtcStations(int number)
312  {
313  if (number < 1) { return; }
314  m_atcStationsOnline.push_back(CTesting::createAtcStations(number));
315  emit this->changedAtcStationsOnline();
316  }
317 
318  void CAirspaceMonitor::testAddAircraftParts(const CCallsign &callsign, const CAircraftParts &parts,
319  bool incremental)
320  {
321  this->onAircraftConfigReceived(callsign, incremental ? parts.toIncrementalJson() : parts.toFullJson(), 5000);
322  }
323 
325  {
326  static const QString nr("not ready");
327  static const QString icao("rec. ICAO");
328  static const QString fsinn("rec. FsInn");
329  static const QString ready("ready sent");
330  static const QString rec("recursive");
331  switch (r)
332  {
333  case NotReady: return nr;
334  case ReceivedIcaoCodes: return icao;
335  case ReceivedFsInnPacket: return fsinn;
336  case ReadyForMatchingSent: return ready;
337  case RecursiveCall: return rec;
338  default: break;
339  }
340  static const QString unknown("????");
341  return unknown;
342  }
343 
345  {
346  QStringList s;
347  if (r.testFlag(NotReady)) { s << enumFlagToString(NotReady); }
348  if (r.testFlag(ReceivedIcaoCodes)) { s << enumFlagToString(ReceivedIcaoCodes); }
349  if (r.testFlag(ReceivedFsInnPacket)) { s << enumFlagToString(ReceivedFsInnPacket); }
351  if (r.testFlag(RecursiveCall)) { s << enumFlagToString(RecursiveCall); }
352  return s.join(", ");
353  }
354 
355  bool CAirspaceMonitor::parseCommandLine(const QString &commandLine, const CIdentifier &originator)
356  {
357  Q_UNUSED(originator;)
358  if (commandLine.isEmpty()) { return false; }
359  static const QStringList cmds({ ".fsd" });
360  CSimpleCommandParser parser(cmds);
361  parser.parse(commandLine);
362  if (!parser.isKnownCommand()) { return false; }
363  if (parser.matchesCommand(".fsd"))
364  {
365  if (parser.countParts() < 2) { return false; }
366  if (parser.matchesPart(1, "range") && parser.countParts() > 2)
367  {
368  const QString r = parser.part(2);
369  const CLength d = CLength::parsedFromString(r);
370  this->setMaxRange(d);
371  }
372  }
373  return false;
374  }
375 
376  void CAirspaceMonitor::fastProcessing()
377  {
378  if (!this->isConnectedAndNotShuttingDown()) { return; }
379 
380  // only send one query
381  const bool send = this->sendNextStaggeredAtisQuery();
382  if (!send) { this->sendNextStaggeredPilotDataQuery(); }
383  }
384 
385  void CAirspaceMonitor::slowProcessing()
386  {
387  if (!this->isConnectedAndNotShuttingDown()) { return; }
388  this->queryAllOnlineAtcStations();
389  }
390 
392  {
393  m_flightPlanCache.clear();
394  m_tempFsInnPackets.clear();
395  m_readiness.clear();
396  this->removeAllOnlineAtcStations();
397  this->removeAllAircraft();
398  this->clearClients();
399 
400  m_foundInNonMovingAircraft = 0;
401  m_foundInElevationsOnGnd = 0;
402  }
403 
405 
407  {
408  const CSimulatedAircraftList aircraft = this->getAircraftInRange();
410  this->asyncReInitializeAllAircraft(aircraft, true);
411  return aircraft.size();
412  }
413 
415  {
416  if (range.isNull() || range.isNegativeWithEpsilonConsidered() || range.isZeroEpsilonConsidered())
417  {
418  // off
419  m_maxDistanceNM = -1;
420  m_maxDistanceNMHysteresis = -1;
421  CLogMessage(this).info(u"No airspace range restriction");
422  return;
423  }
424 
425  const int rIntNM = range.valueInteger(CLengthUnit::NM());
426  CLogMessage(this).info(u"Set airspace max. range to %1NM") << rIntNM;
427  m_maxDistanceNM = rIntNM;
428  m_maxDistanceNMHysteresis = qRound(rIntNM * 1.1);
429  }
430 
431  void CAirspaceMonitor::onRealNameReplyReceived(const CCallsign &callsign, const QString &realname)
432  {
433  if (!this->isConnectedAndNotShuttingDown() || realname.isEmpty()) { return; }
434  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return; }
435 
436  bool wasAtc = false;
437  const QString rn = CUser::beautifyRealName(realname);
438 
439  if (callsign.hasSuffix())
440  {
441  // very likely and ATC callsign
442  const CPropertyIndexVariantMap vm =
443  CPropertyIndexVariantMap({ CAtcStation::IndexController, CUser::IndexRealName }, rn);
444  const int c1 = this->updateOnlineStation(callsign, vm, false, true);
445  wasAtc = c1 > 0;
446  }
447 
448  if (!wasAtc)
449  {
450  const CPropertyIndexVariantMap vm =
451  CPropertyIndexVariantMap({ CSimulatedAircraft::IndexPilot, CUser::IndexRealName }, rn);
452  this->updateAircraftInRange(callsign, vm);
453  }
454 
455  // Client
457  CPropertyIndexVariantMap vm = CPropertyIndexVariantMap({ CClient::IndexUser, CUser::IndexRealName }, rn);
458  vm.addValue({ CClient::IndexVoiceCapabilities }, voiceCaps);
459  this->updateOrAddClient(callsign, vm, false);
460  }
461 
462  void CAirspaceMonitor::onCapabilitiesReplyReceived(const CCallsign &callsign, CClient::Capabilities clientCaps)
463  {
464  if (!this->isConnectedAndNotShuttingDown() || callsign.isEmpty()) { return; }
466  CPropertyIndexVariantMap vm(CClient::IndexCapabilities, CVariant::fromValue(clientCaps));
467  vm.addValue({ CClient::IndexVoiceCapabilities }, voiceCaps);
468  this->updateOrAddClient(callsign, vm, false);
469 
470  // for aircraft parts
471  if (clientCaps.testFlag(CClient::FsdWithAircraftConfig))
472  {
473  m_fsdClient->sendClientQueryAircraftConfig(callsign);
474  }
475  }
476 
477  void CAirspaceMonitor::onServerReplyReceived(const CCallsign &callsign, const QString &server)
478  {
479  if (!this->isConnectedAndNotShuttingDown() || callsign.isEmpty() || server.isEmpty()) { return; }
480  const CPropertyIndexVariantMap vm(CClient::IndexServer, server);
481  this->updateOrAddClient(callsign, vm);
482  }
483 
484  void CAirspaceMonitor::onFlightPlanReceived(const CCallsign &callsign, const CFlightPlan &flightPlan)
485  {
486  if (!this->isConnectedAndNotShuttingDown() || callsign.isEmpty()) { return; }
487  CFlightPlan plan(flightPlan);
488  plan.setWhenLastSentOrLoaded(QDateTime::currentDateTimeUtc());
489  m_flightPlanCache.insert(callsign, plan);
490  }
491 
492  void CAirspaceMonitor::removeAllOnlineAtcStations()
493  {
494  m_atcStationsOnline.clear();
495  m_queryAtis.clear();
496  }
497 
498  void CAirspaceMonitor::removeAllAircraft()
499  {
501 
502  // non thread safe parts
503  m_flightPlanCache.clear();
504  m_readiness.clear();
505  m_queryPilot.clear();
506  }
507 
508  void CAirspaceMonitor::removeFromAircraftCachesAndLogs(const CCallsign &callsign)
509  {
510  if (callsign.isEmpty()) { return; }
511  m_flightPlanCache.remove(callsign);
512  m_readiness.remove(callsign);
513  this->removeReverseLookupMessages(callsign);
514  }
515 
516  void CAirspaceMonitor::onReceivedVatsimDataFile()
517  {
518  Q_ASSERT(CThreadUtils::isInThisThread(this));
519  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return; }
520  CClientList clients(this->getClients()); // copy
521  bool changed = false;
522  for (auto &client : clients)
523  {
524  if (client.hasSpecifiedVoiceCapabilities()) { continue; } // we already have voice caps
525  const CVoiceCapabilities vc =
526  sApp->getWebDataServices()->getVoiceCapabilityForCallsign(client.getCallsign());
527  if (vc.isUnknown()) { continue; }
528  changed = true;
529  client.setVoiceCapabilities(vc);
530  }
531  if (!changed) { return; }
532  this->setClients(clients);
533  }
534 
535  CAirspaceMonitor::Readiness &CAirspaceMonitor::addMatchingReadinessFlag(const CCallsign &callsign,
537  {
538  Readiness &readiness = m_readiness[callsign].addFlag(mrf);
539  return readiness;
540  }
541 
542  void CAirspaceMonitor::sendReadyForModelMatching(const CCallsign &callsign, MatchingReadinessFlag rf)
543  {
544  if (!this->isConnectedAndNotShuttingDown()) { return; }
545  Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "missing callsign");
546 
547  // set flag and init ts
548  Readiness &readiness = this->addMatchingReadinessFlag(callsign, rf);
549 
550  // skip if already sent in the last x seconds
551  const qint64 ageMs = readiness.getAgeMs();
552  if (readiness.wasMatchingSent() && readiness.getAgeMs() < MMMaxAgeThresholdMs) { return; }
553 
554  // checking for min. situations ensures the aircraft is stable, can be interpolated ...
555  const CSimulatedAircraft remoteAircraft = this->getAircraftInRangeForCallsign(callsign);
556  const bool validRemoteCs = remoteAircraft.hasValidCallsign();
557  const bool minSituations = this->remoteAircraftSituationsCount(callsign) > 1;
558  const bool complete = validRemoteCs && minSituations &&
559  (readiness.receivedAll() ||
560  // disable, because it can be model string is unknown in FsInn data
561  // (remoteAircraft.getModel().getModelType() == CAircraftModel::TypeFSInnData) || // here
562  // we know we have all data
563  (remoteAircraft.hasModelString()) // we cannot expect more info
564  );
565 
566  const ReverseLookupLogging revLogEnabled = this->whatToReverseLog();
567  if (rf != Verified && validRemoteCs && ageMs <= MMMaxAgeMs && !complete)
568  {
569  static const QString ws("Wait for further data, '%1' age: %2ms ts: %3");
570  static const QString format("hh:mm:ss.zzz");
571  if (!revLogEnabled.testFlag(RevLogSimplifiedInfo))
572  {
574  callsign,
575  ws.arg(readiness.toQString()).arg(ageMs).arg(QDateTime::currentDateTimeUtc().toString(format)));
576  }
577 
578  const QPointer<CAirspaceMonitor> myself(this);
579  QTimer::singleShot(MMCheckAgainMs, this, [=, this]() {
580  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
581  if (!this->isAircraftInRange(callsign))
582  {
583  const CStatusMessage m =
584  CCallsign::logMessage(callsign, "No longer in range", CAirspaceMonitor::getLogCategories());
585  this->addReverseLookupMessage(callsign, m);
586  return;
587  }
588  if (rf != ReceivedFsInnPacket)
589  {
590  // here we call recursively like an FsInn packet was received
591  if (this->recallFsInnPacket(callsign)) { return; }
592  }
593 
594  // check again
595  this->sendReadyForModelMatching(callsign, RecursiveCall); // recursively
596  });
597 
598  // end as we will call again in some time
599  return;
600 
601  } // not yet complete
602 
603  // some checks for special conditions, e.g. logout -> empty list, but still signals pending
604  if (validRemoteCs)
605  {
606  static const QString readyForMatching(
607  "Ready (%1) for matching callsign '%2' with model type '%3', ICAO: '%4' '%5'");
608 
609  readiness.setFlag(ReadyForMatchingSent); // stored in readiness as reference
610  const QString readyMsg = readyForMatching.arg(readiness.toQString(), callsign.toQString(),
611  remoteAircraft.getModel().getModelTypeAsString(),
612  remoteAircraft.getAircraftIcaoCode().getDesignatorDbKey(),
613  remoteAircraft.getAirlineIcaoCode().getDesignatorDbKey());
614  const CStatusMessage m = CCallsign::logMessage(callsign, readyMsg, getLogCategories());
615  this->addReverseLookupMessage(callsign, m);
616 
617  emit this->readyForModelMatching(remoteAircraft);
618  }
619  else
620  {
621  const CStatusMessage m = CCallsign::logMessage(
622  callsign, "Ignoring this aircraft, not found in range list, disconnected, or no callsign",
624  this->addReverseLookupMessage(callsign, m);
625  m_readiness.remove(callsign);
626  }
627  }
628 
629  void CAirspaceMonitor::verifyReceivedIcaoData(const CCallsign &callsign)
630  {
631  if (callsign.isEmpty() || !this->isAircraftInRange(callsign)) { return; }
632 
633  // set flag and init ts
634  Readiness &readiness = m_readiness[callsign];
635  if (readiness.wasMatchingSent()) { return; }
636  if (readiness.wasVerified())
637  {
638  CLogMessage(this).warning(u"Verfied ICAO data of '%1' again, using it as it is! Most likely incomplete "
639  u"data from the other client")
640  << callsign;
641  this->sendReadyForModelMatching(callsign, Verified);
642  return;
643  }
644 
645  // new trial
646  readiness.addFlag(Verified);
647 
648  if (!readiness.receivedIcaoCodes())
649  {
650  CLogMessage(this).info(u"Query ICAO codes for '%1' again") << callsign;
651  this->sendInitialPilotQueries(callsign, true, true);
652 
653  // if the queries now yield a result all will be fine
654  // otherwise we need to check again
655  const QPointer<CAirspaceMonitor> myself(this);
656  QTimer::singleShot(MMVerifyMs, this, [=, this]() {
657  // makes sure we have ICAO data
658  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
659  this->verifyReceivedIcaoData(callsign);
660  });
661  return;
662  }
663 
664  // normally we should never get here
665  CLogMessage(this).info(u"Verified '%1' again, has ICAO codes, ready for matching!") << callsign;
666  this->sendReadyForModelMatching(callsign, Verified);
667  }
668 
669  void CAirspaceMonitor::onAtcPositionUpdate(const CCallsign &callsign, const CFrequency &frequency,
670  const CCoordinateGeodetic &position,
672  {
673  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "wrong thread");
674  if (!this->isConnectedAndNotShuttingDown()) { return; }
675 
676  const CAtcStationList stationsWithCallsign = m_atcStationsOnline.findByCallsign(callsign);
677  if (stationsWithCallsign.isEmpty())
678  {
679  CAtcStation station;
680 
681  // if connected to VATSIM, init with data from data file
682  if (this->getConnectedServer().getEcosystem() == CEcosystem::vatsim())
683  {
685  }
686 
687  station.setCallsign(callsign);
688  station.setRange(range);
689  station.setFrequency(frequency);
690  station.setPosition(position);
691  station.setOnline(true);
693 
694  m_atcStationsOnline.push_back(station);
695 
696  // subsequent queries
697  this->sendInitialAtcQueries(callsign);
698 
699  // update distances
701 
702  emit this->changedAtcStationsOnline();
703  }
704  else
705  {
706  // update
708  vm.addValue(CAtcStation::IndexFrequency, frequency);
709  vm.addValue(CAtcStation::IndexPosition, position);
710  vm.addValue(CAtcStation::IndexRange, range);
711  const int changed = m_atcStationsOnline.applyIfCallsign(callsign, vm, true);
712  if (changed > 0) { emit this->changedAtcStationsOnline(); }
713  }
714  }
715 
716  void CAirspaceMonitor::onAtcControllerDisconnected(const CCallsign &callsign)
717  {
718  Q_ASSERT(CThreadUtils::isInThisThread(this));
719  if (!this->isConnectedAndNotShuttingDown()) { return; }
720 
721  this->removeClient(callsign);
722  if (m_atcStationsOnline.containsCallsign(callsign))
723  {
724  const CAtcStation removedStation = m_atcStationsOnline.findFirstByCallsign(callsign);
725  m_atcStationsOnline.removeByCallsign(callsign);
726  emit this->changedAtcStationsOnline();
727  emit this->atcStationDisconnected(removedStation);
728  }
729  }
730 
731  void CAirspaceMonitor::onAtisReceived(const CCallsign &callsign, const CInformationMessage &atisMessage)
732  {
733  Q_ASSERT(CThreadUtils::isInThisThread(this));
734  if (!this->isConnectedAndNotShuttingDown() || callsign.isEmpty()) return;
735  const bool changedAtis = m_atcStationsOnline.updateIfMessageChanged(atisMessage, callsign, true);
736 
737  // signal
738  if (changedAtis) { emit this->changedAtisReceived(callsign); }
739  }
740 
741  void CAirspaceMonitor::onAtisLogoffTimeReceived(const CCallsign &callsign, const QString &zuluTime)
742  {
743  Q_ASSERT(CThreadUtils::isInThisThread(this));
744  if (!this->isConnectedAndNotShuttingDown()) { return; }
745 
746  if (zuluTime.length() == 5) // for example 2000z
747  {
748  // Logic to set logoff time
749 
750  QStringView zuluTimeView(zuluTime);
751  // TODO Replace with Qt 6.0 QStringView::slice()
752  zuluTimeView.chop(1); // Remove z
753 
754  bool ok {};
755  const int h = zuluTimeView.left(2).toInt(&ok);
756  if (!ok) { return; }
757  const int m = zuluTimeView.right(2).toInt(&ok);
758  if (!ok) { return; }
759  QDateTime logoffDateTime = QDateTime::currentDateTimeUtc();
760  logoffDateTime.setTime(QTime(h, m));
761 
762  const CPropertyIndexVariantMap vm(CAtcStation::IndexLogoffTime, CVariant(logoffDateTime));
763  this->updateOnlineStation(callsign, vm);
764  }
765  }
766 
767  void CAirspaceMonitor::onCustomFSInnPacketReceived(const CCallsign &callsign, const QString &airlineIcaoDesignator,
768  const QString &aircraftIcaoDesignator,
769  const QString &combinedAircraftType, const QString &modelString)
770  {
771  // it can happen this is called before any queries
772  // ES sends FsInn packets for callsigns such as ACCGER1, which are hard to distinguish
773  // 1) checking if they are already in the list checks again ATC position which is safe
774  // 2) the ATC alike callsign check is guessing
775  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "not in main thread");
776  if (!callsign.isValid()) { return; } // aircraft OBS, other invalid callsigns
777  if (!this->isConnectedAndNotShuttingDown()) { return; }
778 
779  const bool isAircraft = this->isAircraftInRange(callsign);
780  const bool isAtc = m_atcStationsOnline.containsCallsign(callsign);
781  if (!isAircraft && !isAtc)
782  {
783  // we have no idea what we are dealing with, so we store it
784  const FsInnPacket fsInn(aircraftIcaoDesignator, airlineIcaoDesignator, combinedAircraftType, modelString);
785  m_tempFsInnPackets[callsign] = fsInn;
786  return;
787  }
788 
789  // Request of other client, I can get the other's model from that
790  // we do not ignore model string here
791  const CPropertyIndexVariantMap vm(CClient::IndexModelString, modelString);
792  this->updateOrAddClient(callsign, vm);
793 
794  if (isAircraft)
795  {
796  this->addMatchingReadinessFlag(callsign, ReceivedFsInnPacket); // in any case we did receive it
797 
798  const ReverseLookupLogging reverseLookupEnabled = this->isReverseLookupMessagesEnabled();
799  CStatusMessageList reverseLookupMessages;
800  CStatusMessageList *pReverseLookupMessages =
801  reverseLookupEnabled.testFlag(RevLogEnabled) ? &reverseLookupMessages : nullptr;
802 
803  CCallsign::addLogDetailsToList(
804  pReverseLookupMessages, callsign,
805  QStringLiteral("FsInn data from network: aircraft '%1', airline '%2', model '%3', combined '%4'")
806  .arg(aircraftIcaoDesignator, airlineIcaoDesignator, modelString, combinedAircraftType));
807 
808  QString usedModelString = modelString;
809 
810  const CAircraftMatcherSetup setup = m_matchingSettings.get();
811  if (!modelString.isEmpty() && !setup.isReverseLookupModelString())
812  {
813  usedModelString.clear();
814  CCallsign::addLogDetailsToList(
815  pReverseLookupMessages, callsign,
816  QStringLiteral("FsInn modelstring '%1' ignored because of setup").arg(modelString));
817  }
818  else if (!CAircraftMatcher::isKnownModelString(modelString, callsign, pReverseLookupMessages))
819  {
820  // from the T701 test, do NOT use if model string is unknown
821  // this can overrride "swift livery strings", FsInn here only is useful with a known model string
822  usedModelString.clear();
823  CCallsign::addLogDetailsToList(
824  pReverseLookupMessages, callsign,
825  QStringLiteral("FsInn modelstring ignored, as modelstring '%1' is not known").arg(modelString));
826  }
827 
828  // if model string is empty, FsInn data are pointless
829  // in order not to override swift livery string data, we ignore those
830  if (!usedModelString.isEmpty())
831  {
832  this->addOrUpdateAircraftInRange(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, QString(),
833  usedModelString, CAircraftModel::TypeFSInnData,
834  pReverseLookupMessages);
835  this->addReverseLookupMessages(callsign, reverseLookupMessages);
836  }
837  this->sendReadyForModelMatching(callsign, ReceivedFsInnPacket); // from FSInn
838  }
839  }
840 
841  void CAirspaceMonitor::onIcaoCodesReceived(const CCallsign &callsign, const QString &aircraftIcaoDesignator,
842  const QString &airlineIcaoDesignator, const QString &livery)
843  {
844  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "not in main thread");
845  if (!this->isConnectedAndNotShuttingDown()) { return; }
846  if (CBuildConfig::isLocalDeveloperDebugBuild())
847  {
848  SWIFT_VERIFY_X(callsign.isValid(), Q_FUNC_INFO, "invalid callsign");
849  }
850  if (!callsign.isValid()) { return; }
851  if (!this->isAircraftInRange(callsign)) { return; } // FSD overload issue, do not do anything if unknown
852 
853  const ReverseLookupLogging reverseLookupEnabled = this->isReverseLookupMessagesEnabled();
854  CStatusMessageList reverseLookupMessages;
855  CStatusMessageList *pReverseLookupMessages =
856  reverseLookupEnabled.testFlag(RevLogEnabled) ? &reverseLookupMessages : nullptr;
857  CCallsign::addLogDetailsToList(pReverseLookupMessages, callsign,
858  QStringLiteral("Data from network: aircraft '%1', airline '%2', livery '%3'")
859  .arg(aircraftIcaoDesignator, airlineIcaoDesignator, livery),
861 
862  const CClient client = this->getClientOrDefaultForCallsign(callsign);
863  this->addOrUpdateAircraftInRange(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery,
865  pReverseLookupMessages);
866  this->addReverseLookupMessages(callsign, reverseLookupMessages);
867  this->sendReadyForModelMatching(callsign, ReceivedIcaoCodes); // ICAO codes received
868 
869  emit this->requestedNewAircraft(callsign, aircraftIcaoDesignator, airlineIcaoDesignator, livery);
870  }
871 
872  CAircraftModel CAirspaceMonitor::reverseLookupModelWithFlightplanData(
873  const CCallsign &callsign, const QString &aircraftIcaoString, const QString &airlineIcaoString,
874  const QString &liveryString, const QString &modelString, CAircraftModel::ModelType type,
875  CStatusMessageList *log, bool runMatchinScript)
876  {
877  const int modelSetCount = m_modelSetProvider->getModelSetCount();
878  CCallsign::addLogDetailsToList(
879  log, callsign, QStringLiteral("Reverse lookup (with FP data), model set count: %1").arg(modelSetCount),
881 
882  const DBTripleIds ids = CAircraftModel::parseNetworkLiveryString(liveryString);
883  const bool hasAnyId = ids.hasAnyId();
884  if (hasAnyId) { this->markAsSwiftClient(callsign); }
885 
886  CAircraftModel lookupModel; // result
887  const CAircraftModelList modelSet = m_modelSetProvider->getModelSet();
888  const CAircraftMatcherSetup setup = m_matchingSettings.get();
889  do {
890  // directly check model string
891  if (!modelString.isEmpty())
892  {
893  lookupModel = CAircraftMatcher::reverseLookupModelStringInDB(modelString, callsign,
894  setup.isReverseLookupModelString(), log);
895  if (lookupModel.hasValidDbKey()) { break; } // found by model string
896  }
897 
898  CLivery livery;
899  CAirlineIcaoCode airlineIcao;
900  CAircraftIcaoCode aircraftIcao;
901 
902  if (!setup.isReverseLookupSwiftLiveryIds())
903  {
904  CCallsign::addLogDetailsToList(
905  log, callsign, QStringLiteral("Reverse lookup of livery string '%1' disabled").arg(liveryString));
906  }
907  else if (hasAnyId)
908  {
909  if (ids.model >= 0)
910  {
911  lookupModel = CAircraftMatcher::reverseLookupModelId(ids.model, callsign, log);
912  if (lookupModel.hasValidDbKey()) { break; } // found by model id from livery string
913  }
914 
915  CAircraftMatcher::reverseLookupByIds(ids, aircraftIcao, livery, callsign, log);
916  if (livery.hasValidDbKey()) { airlineIcao = livery.getAirlineIcaoCode(); }
917 
918  if (aircraftIcao.hasValidDbKey() && livery.hasValidDbKey())
919  {
920  CCallsign::addLogDetailsToList(
921  log, callsign,
922  QStringLiteral("Using DB livery %1 and aircraft ICAO %2 to create model")
923  .arg(livery.getDbKeyAsString(), aircraftIcao.getDbKeyAsString()),
925 
926  // we have a valid livery from DB + valid aircraft ICAO from DB
927  lookupModel =
928  CAircraftModel(modelString, type, "By DB livery and aircraft ICAO", aircraftIcao, livery);
929  break;
930  }
931  }
932 
933  // now fuzzy search on aircraft
934  if (!aircraftIcao.hasValidDbKey())
935  {
936  aircraftIcao = CAircraftIcaoCode(aircraftIcaoString);
937  const bool knownAircraftIcao =
938  CAircraftMatcher::isKnownAircraftDesignator(aircraftIcaoString, callsign, log);
939  if (airlineIcao.isLoadedFromDb() && !knownAircraftIcao)
940  {
941  // we have no valid aircraft ICAO, so we do a fuzzy search among those
942  CCallsign::addLogDetailsToList(
943  log, callsign,
944  QStringLiteral("Fuzzy search among airline aircraft because '%1' is not known ICAO designator")
945  .arg(aircraftIcaoString));
946  const CAircraftIcaoCode foundIcao =
947  CAircraftMatcher::searchAmongAirlineAircraft(aircraftIcaoString, airlineIcao, callsign, log);
948  if (foundIcao.isLoadedFromDb()) { aircraftIcao = foundIcao; }
949  }
950  }
951 
952  // if we have a livery, we already know the airline, or the livery is a color livery
953  if (!airlineIcao.hasValidDbKey() && !livery.hasValidDbKey())
954  {
955  const CFlightPlanRemarks fpRemarks = this->tryToGetFlightPlanRemarks(callsign);
956  // const bool hasParsedAirlineRemarks = fpRemarks.hasParsedAirlineRemarks();
957 
958  QString airlineNameLookup;
959  QString telephonyLookup;
960  QString telephonyFromFp;
961  QString airlineNameFromFp;
962  QString airlineIcaoFromFp;
963 
964  if (fpRemarks.isEmpty())
965  {
966  CCallsign::addLogDetailsToList(log, callsign,
967  QStringLiteral("No flight plan remarks, skipping FP resolution"));
968  }
969  else
970  {
971  CCallsign::addLogDetailsToList(log, callsign,
972  QStringLiteral("FP remarks: '%1'").arg(fpRemarks.getRemarks()));
973  CCallsign::addLogDetailsToList(
974  log, callsign, QStringLiteral("FP rem.parsed: '%1'").arg(fpRemarks.toQString(true)));
975 
976  // FP data if any
978  callsign, log);
979  airlineNameFromFp =
981  airlineIcaoFromFp = fpRemarks.getAirlineIcao().getDesignator();
982 
983  // turn into names as in DB
984  airlineNameLookup = CAircraftMatcher::reverseLookupAirlineName(airlineNameFromFp);
985  telephonyLookup = CAircraftMatcher::reverseLookupTelephonyDesignator(telephonyFromFp);
986  if (!airlineNameLookup.isEmpty())
987  {
988  CCallsign::addLogDetailsToList(
989  log, callsign,
990  QStringLiteral("Using resolved airline name '%1' found by FP name '%2'")
991  .arg(airlineNameLookup, airlineNameFromFp),
993  }
994  if (!telephonyLookup.isEmpty())
995  {
996  CCallsign::addLogDetailsToList(
997  log, callsign,
998  QStringLiteral("Using resolved telephony designator '%1' found by FP telephoy '%2'")
999  .arg(telephonyLookup, telephonyFromFp),
1001  }
1002  }
1003 
1004  // This code is needed WITH and WITHOUT FP data
1005 
1006  // INFO: CModelMatcherComponent::reverseLookup() contains the simulated lookup
1007  // changed with T701: resolve first against model set, then all DB data
1008  // if an airline is ambiguous most likely the one in the set is the best choice
1010  callsign, airlineIcaoString, airlineIcaoFromFp, true, airlineNameFromFp, telephonyFromFp, modelSet,
1011  log);
1012 
1013  // not found, create a search patterm
1014  if (!airlineIcao.isLoadedFromDb())
1015  {
1016  if (!airlineIcao.hasValidDesignator())
1017  {
1018  airlineIcao.setDesignator(airlineIcaoString.isEmpty() ? callsign.getAirlinePrefix() :
1019  airlineIcaoString);
1020  }
1021  if (!airlineNameLookup.isEmpty()) { airlineIcao.setName(airlineNameLookup); }
1022  if (!telephonyLookup.isEmpty()) { airlineIcao.setTelephonyDesignator(telephonyLookup); }
1023 
1024  // already try to resolve at this stage by a smart lookup with all the filled data from above
1025  airlineIcao = CAircraftMatcher::reverseLookupAirlineIcao(airlineIcao, callsign, log);
1026  }
1027  }
1028 
1029  CCallsign::addLogDetailsToList(log, callsign,
1030  QStringLiteral("Used aircraft ICAO: '%1'").arg(aircraftIcao.toQString(true)),
1032  CCallsign::addLogDetailsToList(log, callsign,
1033  QStringLiteral("Used airline ICAO: '%1'").arg(airlineIcao.toQString(true)),
1035 
1036  // matching script is used below
1037  lookupModel = CAircraftMatcher::reverseLookupModel(callsign, aircraftIcao, airlineIcao, liveryString,
1038  modelString, setup, modelSet, type, log);
1039  }
1040  while (false);
1041 
1042  // model found
1043  lookupModel.setCallsign(callsign);
1044 
1045  // script
1046  if (runMatchinScript && setup.doRunMsReverseLookupScript())
1047  {
1048  const MatchingScriptReturnValues rv =
1049  CAircraftMatcher::reverseLookupScript(lookupModel, setup, modelSet, log);
1050  if (rv.runScriptAndModified())
1051  {
1052  if (rv.runScriptAndRerun())
1053  {
1054  CCallsign::addLogDetailsToList(
1055  log, callsign, QStringLiteral("Matching script: Re-run reverseLookupModelWithFlightplanData"),
1057  return CAirspaceMonitor::reverseLookupModelWithFlightplanData(
1059  rv.model.getLivery().getCombinedCode(), modelString, type, log, false);
1060  }
1061  lookupModel = rv.model;
1062  CCallsign::addLogDetailsToList(log, callsign,
1063  QStringLiteral("Matching script: Using model from matching script"),
1065  }
1066  }
1067  else { CCallsign::addLogDetailsToList(log, callsign, QStringLiteral("No reverse lookup script used")); }
1068 
1069  // done
1070  lookupModel.setCallsign(callsign); // set again just in case modified by script
1071  return lookupModel;
1072  }
1073 
1074  bool CAirspaceMonitor::addNewAircraftInRange(const CSimulatedAircraft &aircraft)
1075  {
1076  const CCallsign callsign = aircraft.getCallsign();
1077  Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Missing callsign");
1078 
1079  if (!sApp || sApp->isShuttingDown() || !sApp->getWebDataServices()) { return false; }
1080 
1081  CSimulatedAircraft newAircraft(aircraft);
1082  newAircraft.setRendered(false); // reset rendering
1083  newAircraft.calculcateAndUpdateRelativeDistanceAndBearing(
1084  this->getOwnAircraftPosition()); // distance from myself
1085 
1086  if (this->getConnectedServer().getEcosystem() == CEcosystem::vatsim())
1087  {
1089  }
1090  const bool added = CRemoteAircraftProvider::addNewAircraftInRange(newAircraft);
1091  if (added)
1092  {
1093  const QPointer<CAirspaceMonitor> myself(this);
1094  QTimer::singleShot(MMVerifyMs, this, [=, this]() {
1095  // makes sure we have ICAO data
1096  if (!myself || !sApp || sApp->isShuttingDown()) { return; }
1097  this->verifyReceivedIcaoData(callsign);
1098  });
1099 
1100  if (aircraft.hasModelString())
1101  {
1102  // most likely I could take the CG at this time from aircraft
1103  // to make sure it is really the DB value I query again
1104  const CAircraftModel model =
1106  const CLength cg = model.hasValidDbKey() ? model.getCG() : CLength::null();
1107  this->rememberCGFromDB(cg, aircraft.getModelString());
1108  this->rememberCGFromDB(cg, aircraft.getCallsign());
1109  }
1110  }
1111  return added;
1112  }
1113 
1114  void CAirspaceMonitor::asyncReInitializeAllAircraft(const CSimulatedAircraftList &aircraft,
1115  bool readyForModelMatching)
1116  {
1117  if (aircraft.isEmpty()) { return; }
1118  if (!sApp || sApp->isShuttingDown()) { return; }
1119 
1120  int c = 1;
1121  QPointer<CAirspaceMonitor> myself(this);
1122  for (const CSimulatedAircraft &ac : aircraft)
1123  {
1124  QTimer::singleShot(c++ * 25, this, [=, this] {
1125  if (!myself) { return; }
1126  myself->addNewAircraftInRange(ac);
1127  if (!readyForModelMatching) { return; }
1128  const CCallsign cs = ac.getCallsign();
1129 
1130  m_readiness.remove(cs); // cleanup
1131  const MatchingReadinessFlag ready = ReceivedAll;
1132  myself->sendReadyForModelMatching(cs, ready); // airspace monitor adding all aicraft
1133  });
1134  }
1135  }
1136 
1137  int CAirspaceMonitor::updateOnlineStation(const CCallsign &callsign, const CPropertyIndexVariantMap &vm,
1138  bool skipEqualValues, bool sendSignal)
1139  {
1140  const int c = m_atcStationsOnline.applyIfCallsign(callsign, vm, skipEqualValues);
1141  if (c > 0 && sendSignal) { emit this->changedAtcStationsOnline(); }
1142  return c;
1143  }
1144 
1145  bool CAirspaceMonitor::handleMaxRange(const CAircraftSituation &situation)
1146  {
1147  if (situation.isNull()) { return false; }
1148  if (m_maxDistanceNM < 0) { return true; }
1149  const int distanceNM =
1150  this->getOwnAircraft().calculateGreatCircleDistance(situation).valueInteger(CLengthUnit::NM());
1151  if (distanceNM > m_maxDistanceNMHysteresis)
1152  {
1153  this->removeAircraft(situation.getCallsign());
1154  return false;
1155  }
1156  return distanceNM <= m_maxDistanceNM;
1157  }
1158 
1159  bool CAirspaceMonitor::recallFsInnPacket(const CCallsign &callsign)
1160  {
1161  if (!m_tempFsInnPackets.contains(callsign)) { return false; }
1162  const FsInnPacket packet = m_tempFsInnPackets[callsign];
1163  m_tempFsInnPackets.remove(callsign);
1164  this->onCustomFSInnPacketReceived(callsign, packet.airlineIcaoDesignator, packet.aircraftIcaoDesignator,
1165  packet.combinedCode, packet.modelString);
1166  return true;
1167  }
1168 
1169  CSimulatedAircraft CAirspaceMonitor::addOrUpdateAircraftInRange(
1170  const CCallsign &callsign, const QString &aircraftIcao, const QString &airlineIcao, const QString &livery,
1171  const QString &modelString, CAircraftModel::ModelType modelType, CStatusMessageList *log)
1172  {
1173  const CSimulatedAircraft aircraft = this->getAircraftInRangeForCallsign(callsign);
1174  if (aircraft.hasValidCallsign())
1175  {
1176  // only if we do not have a DB model yet
1177  if (!aircraft.getModel().hasValidDbKey())
1178  {
1179  CAircraftModel model = this->reverseLookupModelWithFlightplanData(callsign, aircraftIcao, airlineIcao,
1180  livery, modelString, modelType, log);
1181  model.updateMissingParts(aircraft.getModel());
1182  // Use anonymous as originator here, since the remote aircraft provider is ourselves and the call to
1183  // updateAircraftModel() would return without doing anything.
1184  this->updateAircraftModel(callsign, model, CIdentifier::null());
1185  this->updateAircraftNetworkModel(callsign, model, CIdentifier::null());
1186  }
1187  }
1188  else
1189  {
1190  /* FSD overload issue, do NOT to add new aircraft other than from positions
1191  const CAircraftModel model = this->reverseLookupModelWithFlightplanData(callsign, aircraftIcao, airlineIcao,
1192  livery, modelString, modelType, log); const CSimulatedAircraft initAircraft(model);
1193  this->addNewAircraftInRange(initAircraft);
1194  */
1195  }
1196  return aircraft;
1197  }
1198 
1199  bool CAirspaceMonitor::extrapolateElevation(CAircraftSituationList &situations,
1200  const CAircraftSituationChange &change)
1201  {
1202  if (situations.size() < 3) { return false; }
1203  // Q_ASSERT_X(situations.m_tsAdjustedSortHint == CAircraftSituationList::AdjustedTimestampLatestFirst,
1204  // Q_FUNC_INFO, "Need latest first");
1205  const CAircraftSituation old = situations[1];
1206  const CAircraftSituation older = situations[2];
1207  return extrapolateElevation(situations.front(), old, older, change);
1208  }
1209 
1210  bool CAirspaceMonitor::extrapolateElevation(CAircraftSituation &situationToBeUpdated,
1211  const CAircraftSituation &oldSituation,
1212  const CAircraftSituation &olderSituation,
1213  const CAircraftSituationChange &oldChange)
1214  {
1215  if (situationToBeUpdated.hasGroundElevation()) { return false; }
1216 
1217  // if acceptable transfer
1218  if (oldSituation.transferGroundElevationFromMe(situationToBeUpdated))
1219  {
1220  // change or keep type is the question
1221  // situationToBeUpdated.setGroundElevationInfo(Extrapolated);
1222  return true;
1223  }
1224  if (oldSituation.isNull() || olderSituation.isNull()) { return false; }
1225 
1226  if (oldChange.isNull()) { return false; }
1227  if (oldChange.isConstOnGround() && oldChange.hasAltitudeDevWithinAllowedRange() &&
1229  {
1230  // we have almost const altitudes and elevations
1231  const double deltaAltFt = qAbs(situationToBeUpdated.getAltitude().value(CLengthUnit::ft()) -
1232  olderSituation.getAltitude().value(CLengthUnit::ft()));
1233  if (deltaAltFt <= CAircraftSituation::allowedAltitudeDeviation().value(CLengthUnit::ft()))
1234  {
1235  // the current alt is also not much different
1236  situationToBeUpdated.setGroundElevation(oldSituation.getGroundElevation(),
1237  CAircraftSituation::Extrapolated);
1238  return true;
1239  }
1240  }
1241 
1242  return false;
1243  }
1244 
1245  void CAirspaceMonitor::onAircraftUpdateReceived(const CAircraftSituation &situation,
1246  const CTransponder &transponder)
1247  {
1248  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Called in different thread");
1249  if (!this->isConnectedAndNotShuttingDown()) { return; }
1250 
1251  const CCallsign callsign(situation.getCallsign());
1252  Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
1253 
1254  if (this->isCopilotAircraft(callsign)) { return; }
1255 
1256  // range (FSD overload issue)
1257  const bool validMaxRange = this->handleMaxRange(situation);
1258  const bool existsInRange = this->isAircraftInRange(callsign); // AFTER valid max.range check!
1259  if (!validMaxRange && !existsInRange) { return; } // not valid at all
1260 
1261  // update client info
1262  this->autoAdjustCientGndCapability(situation);
1263 
1264  // store situation history
1265  this->storeAircraftSituation(situation); // updates situation
1266 
1267  // in case we only have
1268  if (!existsInRange && validMaxRange)
1269  {
1270  // NEW aircraft
1271  const bool hasFsInnPacket = m_tempFsInnPackets.contains(callsign);
1272 
1273  CSimulatedAircraft aircraft;
1274  aircraft.setCallsign(callsign);
1275  aircraft.setSituation(situation);
1276  aircraft.setTransponder(transponder);
1277  this->addNewAircraftInRange(aircraft);
1278  this->sendInitialPilotQueries(callsign, true, !hasFsInnPacket);
1279 
1280  // new client, there is a chance it has been already created by custom packet
1281  const CClient client(callsign);
1282  this->addNewClient(client);
1283  }
1284  else if (existsInRange)
1285  {
1286  // update, aircraft already exists
1288  vm.addValue(CSimulatedAircraft::IndexTransponder, transponder);
1289  vm.addValue(CSimulatedAircraft::IndexSituation, situation);
1290  vm.addValue(CSimulatedAircraft::IndexRelativeDistance, this->calculateDistanceToOwnAircraft(situation));
1291  vm.addValue(CSimulatedAircraft::IndexRelativeBearing, this->calculateBearingToOwnAircraft(situation));
1292  this->updateAircraftInRange(callsign, vm);
1293  }
1294  }
1295 
1296  void CAirspaceMonitor::onAircraftInterimUpdateReceived(const CAircraftSituation &situation)
1297  {
1298  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Called in different thread");
1299  if (!this->isConnectedAndNotShuttingDown()) { return; }
1300 
1301  const CCallsign callsign(situation.getCallsign());
1302 
1303  // checks
1304  Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
1305 
1306  if (isCopilotAircraft(callsign)) { return; }
1307  if (!this->isAircraftInRange(callsign)) { return; }
1308 
1309  if (CBuildConfig::isLocalDeveloperDebugBuild())
1310  {
1311  Q_ASSERT_X(!situation.isNaNVectorDouble(), Q_FUNC_INFO, "Detected NaN");
1312  Q_ASSERT_X(!situation.isInfVectorDouble(), Q_FUNC_INFO, "Detected inf");
1313  Q_ASSERT_X(situation.isValidVectorRange(), Q_FUNC_INFO, "out of range [-1,1]");
1314  }
1315 
1316  // Interim packets do not have groundspeed, hence set the last known value.
1317  // If there is no full position available yet, throw this interim position away.
1318  CAircraftSituation interimSituation(situation);
1319  CAircraftSituationList history = this->remoteAircraftSituations(callsign);
1320  if (history.isEmpty()) { return; } // we need one full situation at least
1321  const CAircraftSituation lastSituation = history.latestObject();
1322 
1323  // changed position, continue and copy values
1324  interimSituation.setCurrentUtcTime();
1325  interimSituation.setGroundSpeed(lastSituation.getGroundSpeed());
1326 
1327  // store situation history
1328  this->storeAircraftSituation(interimSituation);
1329 
1330  const bool samePosition = lastSituation.equalNormalVectorDouble(interimSituation);
1331  if (samePosition) { return; } // nothing to update
1332 
1333  // update aircraft
1334  this->updateAircraftInRangeDistanceBearing(callsign, interimSituation,
1335  this->calculateDistanceToOwnAircraft(interimSituation),
1336  this->calculateBearingToOwnAircraft(interimSituation));
1337  }
1338 
1339  void CAirspaceMonitor::onAircraftVisualUpdateReceived(const swift::misc::aviation::CAircraftSituation &situation)
1340  {
1342  Q_ASSERT_X(CThreadUtils::isInThisThread(this), Q_FUNC_INFO, "Called in different thread");
1343  if (!this->isConnectedAndNotShuttingDown()) { return; }
1344 
1345  const CCallsign callsign(situation.getCallsign());
1346 
1347  // checks
1348  Q_ASSERT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Empty callsign");
1349 
1350  if (isCopilotAircraft(callsign)) { return; }
1351  if (!this->isAircraftInRange(callsign)) { return; }
1352 
1353  if (CBuildConfig::isLocalDeveloperDebugBuild())
1354  {
1355  Q_ASSERT_X(!situation.isNaNVectorDouble(), Q_FUNC_INFO, "Detected NaN");
1356  Q_ASSERT_X(!situation.isInfVectorDouble(), Q_FUNC_INFO, "Detected inf");
1357  Q_ASSERT_X(situation.isValidVectorRange(), Q_FUNC_INFO, "out of range [-1,1]");
1358  }
1359 
1360  // Visual packets do not have groundspeed, hence set the last known value.
1361  // If there is no full position available yet, throw this interim position away.
1363  CAircraftSituation visualSituation(situation);
1364  CAircraftSituationList history = this->remoteAircraftSituations(callsign);
1365  if (history.empty()) { return; } // we need one full situation at least
1366  const CAircraftSituation lastSituation = history.latestObject();
1367 
1368  // changed position, continue and copy values
1369  visualSituation.setCurrentUtcTime();
1370  visualSituation.setGroundSpeed(lastSituation.getGroundSpeed());
1371 
1372  // store situation history
1373  this->storeAircraftSituation(visualSituation);
1374 
1375  const bool samePosition = lastSituation.equalNormalVectorDouble(visualSituation);
1376  if (samePosition) { return; } // nothing to update
1377 
1378  // update aircraft
1379  this->updateAircraftInRangeDistanceBearing(callsign, visualSituation,
1380  this->calculateDistanceToOwnAircraft(visualSituation),
1381  this->calculateBearingToOwnAircraft(visualSituation));
1382  }
1383 
1384  void CAirspaceMonitor::onAircraftSimDataUpdateReceived(const CAircraftSituation &situation,
1385  const CAircraftParts &parts, qint64 currentOffsetMs,
1386  const QString &aircraftIcao, const QString &airlineIcao)
1387  {
1388  onAircraftUpdateReceived(situation, CTransponder(2000, CTransponder::ModeC));
1389 
1390  const CSimulatedAircraft aircraft = getAircraftInRangeForCallsign(situation.getCallsign());
1391  const CAircraftModel &model = aircraft.getNetworkModel();
1392  if (model.getAircraftIcaoCodeDesignator() != aircraftIcao)
1393  {
1394  onIcaoCodesReceived(situation.getCallsign(), aircraftIcao, airlineIcao, airlineIcao);
1395  }
1396 
1399  onAircraftConfigReceived(situation.getCallsign(), parts.toFullJson(), currentOffsetMs);
1400  }
1401 
1402  void CAirspaceMonitor::onConnectionStatusChanged(CConnectionStatus oldStatus, CConnectionStatus newStatus)
1403  {
1404  Q_UNUSED(oldStatus)
1405  if (newStatus == CConnectionStatus::Connecting && m_fsdClient)
1406  {
1407  const CServer server = m_fsdClient->getServer();
1408  const bool isVatsim = server.getEcosystem().isSystem(CEcosystem::VATSIM);
1409  const CLength maxRange(isVatsim ? 125 : -1, CLengthUnit::NM());
1410  this->setMaxRange(maxRange);
1411  }
1412 
1413  if (newStatus.isDisconnected()) { this->clear(); }
1414  }
1415 
1416  void CAirspaceMonitor::onPilotDisconnected(const CCallsign &callsign)
1417  {
1418  Q_ASSERT(CThreadUtils::isInThisThread(this));
1419 
1420  // in case of inconsistencies I always remove here
1421  this->removeFromAircraftCachesAndLogs(callsign);
1422  const bool removed = CRemoteAircraftProvider::removeAircraft(callsign);
1423  this->removeClient(callsign);
1424  if (removed) { emit this->removedAircraft(callsign); }
1425  }
1426 
1427  void CAirspaceMonitor::onFrequencyReceived(const CCallsign &callsign, const CFrequency &frequency)
1428  {
1429  Q_ASSERT(CThreadUtils::isInThisThread(this));
1430 
1431  // update
1432  const CPropertyIndexVariantMap vm({ CSimulatedAircraft::IndexCom1System, CComSystem::IndexActiveFrequency },
1433  CVariant::fromValue(frequency));
1434  this->updateAircraftInRange(callsign, vm);
1435  }
1436 
1437  void CAirspaceMonitor::onAircraftConfigReceived(const CCallsign &callsign, const QJsonObject &jsonObject,
1438  qint64 currentOffsetMs)
1439  {
1440  Q_ASSERT(CThreadUtils::isInThisThread(this));
1441  SWIFT_AUDIT_X(!callsign.isEmpty(), Q_FUNC_INFO, "Need callsign");
1442  if (callsign.isEmpty()) { return; }
1443 
1444  // store parts
1445  this->storeAircraftParts(callsign, jsonObject, currentOffsetMs);
1446 
1447  // update client capability
1448  CClient client = this->getClientOrDefaultForCallsign(callsign);
1449  client.setUserCallsign(callsign); // make valid by setting a callsign
1450  if (client.hasCapability(CClient::FsdWithAircraftConfig)) { return; }
1451  client.addCapability(CClient::FsdWithAircraftConfig);
1452  this->setOtherClient(client);
1453  }
1454 
1455  CAircraftSituation CAirspaceMonitor::storeAircraftSituation(const CAircraftSituation &situation,
1456  bool allowTestOffset)
1457  {
1458  const CCallsign callsign(situation.getCallsign());
1459  SWIFT_VERIFY_X(!callsign.isEmpty(), Q_FUNC_INFO, "empty callsign");
1460  if (callsign.isEmpty()) { return situation; }
1461 
1462  CAircraftSituation correctedSituation(allowTestOffset ? this->addTestAltitudeOffsetToSituation(situation) :
1463  situation);
1464  bool needToRequestElevation = false;
1465  bool canLikelySkipNearGround = correctedSituation.canLikelySkipNearGroundInterpolation();
1466  do {
1467  // Check if we can bail out and ignore all elevation handling
1468  //
1469  // rational:
1470  // a) elevation handling is expensive, and might even requests elevation from sim.
1471  // b) elevations not needed pollute the cache with "useless" values
1472  //
1473  if (canLikelySkipNearGround || correctedSituation.hasGroundElevation()) { break; }
1474 
1475  // set a defined state
1476  correctedSituation.resetGroundElevation();
1477 
1478  // Guessing gives better values, also for smaller planes
1479  // and avoids unnecessary elevation fetching for low flying smaller GA aircraft
1481  if (!icao.hasDesignator()) { break; } // what is that?
1482  if (icao.isVtol())
1483  {
1484  // VTOLs over 60kts likely not on ground
1485  // it could be that a low flying helicopter near ground
1486  if (situation.getGroundSpeed().value(CSpeedUnit::kts()) > 60) { break; }
1487  }
1488  else
1489  {
1490  // NO VTOL
1491  CLength cg(nullptr);
1492  CSpeed rotateSpeed(nullptr);
1493  icao.guessModelParameters(cg, rotateSpeed);
1494  if (!rotateSpeed.isNull())
1495  {
1496  rotateSpeed *= 1.25; // some margin
1497  if (situation.getGroundSpeed() > rotateSpeed) { break; }
1498  }
1499  }
1500 
1501  // fetch from cache or request
1502  const CAircraftSituationChange changesBeforeStoring =
1504 
1505  if (!changesBeforeStoring.isNull())
1506  {
1507  canLikelySkipNearGround = changesBeforeStoring.isConstAscending();
1508  if (canLikelySkipNearGround) { break; }
1509  }
1510 
1511  // we NEED elevation
1512  // actually distance of 200k/h 100ms is just 6.1 meters
1513  using namespace std::chrono_literals;
1514  const CLength dpt = correctedSituation.getDistancePerTime(100ms, CElevationPlane::singlePointRadius());
1515  const CAircraftSituationList situationsBeforeStoring = this->remoteAircraftSituations(callsign);
1516  const CAircraftSituation situationWithElvBeforeStoring =
1517  situationsBeforeStoring.findClosestElevationWithinRange(correctedSituation, dpt);
1518  if (situationWithElvBeforeStoring.transferGroundElevationFromMe(correctedSituation, dpt))
1519  {
1520  // from nearby situations of own aircraft, data was transferred above
1521  // we use transfer first as it is slightly faster as cache
1522  }
1523  else
1524  {
1525  // from cache
1526  const CLength distance(correctedSituation.getDistancePerTime250ms(
1527  CElevationPlane::singlePointRadius())); // distance per ms
1528  const CElevationPlane ep = this->findClosestElevationWithinRange(correctedSituation, distance);
1529  needToRequestElevation = ep.isNull();
1530  Q_ASSERT_X(needToRequestElevation || !ep.getRadius().isNull(), Q_FUNC_INFO, "null radius");
1531 
1532  // also can handle NULL elevations
1533  correctedSituation.setGroundElevation(ep, CAircraftSituation::FromCache);
1534  }
1535 
1536  // we have a new situation, so we try to get the elevation
1537  // we will requested it, but we set it upfront either by
1538  //
1539  // a) average value from other planes in the vicinity (cache, not moving) or
1540  // b) by extrapolating
1541  //
1542  // if we would NOT preset it, we could end up with oscillation
1543  //
1544  if (!correctedSituation.hasGroundElevation())
1545  {
1546  // average elevation
1547  // 1) from cache
1548  // 2) from planes on ground not moving
1549  bool fromNonMoving = false;
1550  bool triedExtrapolation = false;
1551  bool couldNotExtrapolate = false;
1552  int fromWhere = -1; // debugging
1553 
1554  CElevationPlane averagePlane =
1555  this->averageElevationOfOnGroundAircraft(situation, CElevationPlane::minorAirportRadius(), 2, 3);
1556  if (averagePlane.isNull())
1557  {
1558  averagePlane = this->averageElevationOfNonMovingAircraft(
1559  situation, CElevationPlane::minorAirportRadius(), 2, 3);
1560  fromNonMoving = true;
1561  }
1562 
1563  // do we have an elevation yet?
1564  if (!averagePlane.isNull())
1565  {
1566  correctedSituation.setGroundElevation(averagePlane, CAircraftSituation::Average);
1567  if (fromNonMoving) { m_foundInNonMovingAircraft++; }
1568  else { m_foundInElevationsOnGnd++; }
1569  fromWhere = 10;
1570  }
1571  else
1572  {
1573  // values before updating (i.e. "storing") so the new situation is not yet considered
1574  if (situationsBeforeStoring.size() > 1)
1575  {
1576  const bool extrapolated =
1577  extrapolateElevation(correctedSituation, situationsBeforeStoring[0],
1578  situationsBeforeStoring[1], changesBeforeStoring);
1579  triedExtrapolation = true;
1580  couldNotExtrapolate = !extrapolated;
1581  fromWhere = 20;
1582  }
1583  }
1584  Q_UNUSED(fromWhere)
1585 
1586  // still no elevation
1587  if (!correctedSituation.hasGroundElevation())
1588  {
1589  if (CBuildConfig::isLocalDeveloperDebugBuild())
1590  {
1591  // experimental, could become ASSERT
1592  SWIFT_VERIFY_X(needToRequestElevation, Q_FUNC_INFO, "Request should already be set");
1593  }
1594  needToRequestElevation = true; // should be "true" already
1595 
1596  Q_UNUSED(triedExtrapolation)
1597  Q_UNUSED(couldNotExtrapolate)
1598  }
1599  else
1600  {
1601  // sanity check on the situation
1602  if (CBuildConfig::isLocalDeveloperDebugBuild())
1603  {
1604  SWIFT_VERIFY_X(!correctedSituation.getGroundElevation().isZeroEpsilonConsidered(), Q_FUNC_INFO,
1605  "Suspicious elevation");
1606  }
1607  }
1608  } // gnd. elevation
1609  }
1610  while (false); // do we need elevation, find on
1611 
1612  // do we already have ground details?
1613  if (situation.getOnGroundInfo().getGroundDetails() == COnGroundInfo::NotSetGroundDetails)
1614  {
1615  const CClient client = this->getClientOrDefaultForCallsign(callsign);
1616  if (client.hasCapability(CClient::FsdWithGroundFlag))
1617  {
1618  // we rely on situation gnd.flag
1619  correctedSituation.setOnGroundDetails(COnGroundInfo::InFromNetwork);
1620  }
1621  else if (client.hasCapability(CClient::FsdWithAircraftConfig))
1622  {
1623  const CAircraftPartsList parts = this->remoteAircraftParts(callsign);
1624  if (!parts.isEmpty()) { correctedSituation.adjustGroundFlag(parts, true); }
1625  }
1626  }
1627 
1628  // CG from provider
1629  const CLength cg = this->getSimulatorOrDbCG(
1630  callsign,
1631  this->getCGFromDB(
1632  callsign)); // always x-check against simulator to override guessed values and reflect changed CGs
1633  if (!cg.isNull()) { correctedSituation.setCG(cg); }
1634 
1635  // store corrected situation
1636  correctedSituation = CRemoteAircraftProvider::storeAircraftSituation(correctedSituation,
1637  false); // we already added offset if any
1638 
1639  // check if we need want to request
1640  if (needToRequestElevation && !canLikelySkipNearGround)
1641  {
1642  // we have not requested so far, but we are NEAR ground
1643  // we expect at least not transferred cache or we are moving and have no provider elevation yet
1644  if (correctedSituation.isOtherElevationInfoBetter(CAircraftSituation::FromCache, false) ||
1645  (correctedSituation.isMoving() &&
1646  correctedSituation.isOtherElevationInfoBetter(CAircraftSituation::FromProvider, false)))
1647  {
1648  needToRequestElevation = this->requestElevation(correctedSituation);
1649  }
1650  }
1651 
1652  Q_UNUSED(needToRequestElevation)
1653  return correctedSituation;
1654  }
1655 
1656  void CAirspaceMonitor::sendInitialAtcQueries(const CCallsign &callsign)
1657  {
1658  if (!this->isConnectedAndNotShuttingDown()) { return; }
1659  m_fsdClient->sendClientQueryRealName(callsign);
1660  m_fsdClient->sendClientQueryAtis(callsign); // request ATIS and voice rooms
1661  m_fsdClient->sendClientQueryCapabilities(callsign);
1662  m_fsdClient->sendClientQueryServer(callsign);
1663  }
1664 
1665  void CAirspaceMonitor::queryAllOnlineAtcStations()
1666  {
1667  if (!this->isConnectedAndNotShuttingDown()) { return; }
1668  const CAtcStationList onlineStations = this->getAtcStationsOnlineRecalculated();
1669  for (const CAtcStation &station : onlineStations)
1670  {
1671  const CCallsign cs = station.getCallsign();
1672  if (cs.isEmpty()) { continue; }
1673  m_fsdClient->sendClientQueryRealName(cs);
1674  }
1675  }
1676 
1677  bool CAirspaceMonitor::sendNextStaggeredAtisQuery()
1678  {
1679  if (m_queryAtis.isEmpty()) { return false; }
1680  if (!this->isConnectedAndNotShuttingDown()) { return false; }
1681  const CCallsign cs = m_queryAtis.dequeue();
1682  if (!m_atcStationsOnline.containsCallsign(cs)) { return false; }
1683  m_fsdClient->sendClientQueryAtis(cs);
1684  return true;
1685  }
1686 
1687  void CAirspaceMonitor::sendInitialPilotQueries(const CCallsign &callsign, bool withIcaoQuery, bool withFsInn)
1688  {
1689  if (!this->isConnectedAndNotShuttingDown()) { return; }
1690 
1691  if (withIcaoQuery) { m_fsdClient->sendPlaneInfoRequest(callsign); }
1692  if (withFsInn) { m_fsdClient->sendPlaneInfoRequestFsinn(callsign); }
1693 
1694  m_fsdClient->sendClientQueryCom1Freq(callsign);
1695  m_fsdClient->sendClientQueryRealName(callsign);
1696  m_fsdClient->sendClientQueryCapabilities(callsign);
1697  m_fsdClient->sendClientQueryServer(callsign);
1698  }
1699 
1700  bool CAirspaceMonitor::sendNextStaggeredPilotDataQuery()
1701  {
1702  if (m_queryPilot.isEmpty()) { return false; }
1703  if (!this->isConnectedAndNotShuttingDown()) { return false; }
1704  const CCallsign cs = m_queryPilot.dequeue();
1705  if (!this->isAircraftInRange(cs)) { return false; }
1706  m_fsdClient->sendClientQueryCom1Freq(cs);
1707 
1708  // we only query ICAO if we have none yet
1709  // it happens sometimes with some FSD servers (e.g our testserver) a first query is skipped
1710  // Important: this is only a workaround and must not replace a sendInitialPilotQueries
1711  if (!this->getAircraftInRangeForCallsign(cs).hasAircraftDesignator()) { m_fsdClient->sendPlaneInfoRequest(cs); }
1712  return true;
1713  }
1714 
1715  bool CAirspaceMonitor::isConnected() const
1716  {
1717  return m_fsdClient && m_fsdClient->getConnectionStatus().isConnected();
1718  }
1719 
1720  bool CAirspaceMonitor::isConnectedAndNotShuttingDown() const
1721  {
1722  if (!sApp || sApp->isShuttingDown()) { return false; }
1723  return this->isConnected();
1724  }
1725 
1726  const CServer &CAirspaceMonitor::getConnectedServer() const
1727  {
1728  static const CServer empty;
1729  if (!this->isConnected()) { return empty; }
1730  return m_fsdClient->getServer();
1731  }
1732 
1733  const CEcosystem &CAirspaceMonitor::getCurrentEcosystem() const
1734  {
1735  return this->getConnectedServer().getEcosystem();
1736  }
1737 
1738  bool CAirspaceMonitor::supportsVatsimDataFile() const
1739  {
1740  const bool dataFile =
1742  return dataFile && this->getConnectedServer().getEcosystem().isSystem(CEcosystem::VATSIM);
1743  }
1744 
1745  CLength CAirspaceMonitor::calculateDistanceToOwnAircraft(const CAircraftSituation &situation) const
1746  {
1747  CLength distance = this->getOwnAircraft().calculateGreatCircleDistance(situation);
1748  distance.switchUnit(CLengthUnit::NM());
1749  return distance;
1750  }
1751 
1752  CAngle CAirspaceMonitor::calculateBearingToOwnAircraft(const CAircraftSituation &situation) const
1753  {
1754  CAngle angle = this->getOwnAircraft().calculateBearing(situation);
1755  angle.switchUnit(CAngleUnit::deg());
1756  return angle;
1757  }
1758 
1759  bool CAirspaceMonitor::isCopilotAircraft(const CCallsign &callsign) const
1760  {
1761  if (!sApp || sApp->isShuttingDown() || !sApp->getIContextNetwork()) { return false; }
1762 
1763  // It is only relevant if we are logged in as observer
1765 
1766  const CCallsign ownCallsign = this->getOwnAircraft().getCallsign();
1767  return ownCallsign.isMaybeCopilotCallsign(callsign);
1768  }
1769 
1770  CAirspaceMonitor::FsInnPacket::FsInnPacket(const QString &aircraftIcaoDesignator,
1771  const QString &airlineIcaoDesignator, const QString &combinedCode,
1772  const QString &modelString)
1773  : aircraftIcaoDesignator(aircraftIcaoDesignator.trimmed().toUpper()),
1774  airlineIcaoDesignator(airlineIcaoDesignator.trimmed().toUpper()),
1775  combinedCode(combinedCode.trimmed().toUpper()), modelString(modelString.trimmed())
1776  {}
1777 
1778 } // namespace swift::core
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
static swift::misc::simulation::CAircraftModel reverseLookupModelId(int id, const swift::misc::aviation::CCallsign &callsign, swift::misc::CStatusMessageList *log)
Try to find model by id.
static swift::misc::simulation::CAircraftModel reverseLookupModelStringInDB(const QString &modelString, const swift::misc::aviation::CCallsign &callsign, bool doLookupString, swift::misc::CStatusMessageList *log)
Try to find model by model string.
static swift::misc::aviation::CAirlineIcaoCode failoverValidAirlineIcaoDesignatorModelsFirst(const swift::misc::aviation::CCallsign &callsign, const QString &primaryIcao, const QString &secondaryIcao, bool airlineFromCallsign, const QString &airlineName, const QString &airlineTelephony, const swift::misc::simulation::CAircraftModelList &models, swift::misc::CStatusMessageList *log=nullptr)
Return an valid airline ICAO code from a given model list and use webservices if NOT found.
static QString reverseLookupTelephonyDesignator(const QString &candidate, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Lookup of telephony designator.
static swift::misc::aviation::CAirlineIcaoCode reverseLookupAirlineIcao(const swift::misc::aviation::CAirlineIcaoCode &icaoPattern, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Try to find the DB corresponding ICAO code.
static QString reverseLookupAirlineName(const QString &candidate, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Lookup of airline name.
static swift::misc::simulation::MatchingScriptReturnValues reverseLookupScript(const swift::misc::simulation::CAircraftModel &inModel, const swift::misc::simulation::CAircraftMatcherSetup &setup, const swift::misc::simulation::CAircraftModelList &modelSet, swift::misc::CStatusMessageList *log)
Run the network reverse lookup script.
static bool isKnownModelString(const QString &candidate, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Is this aircraft designator known?
static swift::misc::aviation::CAircraftIcaoCode searchAmongAirlineAircraft(const QString &icaoString, const swift::misc::aviation::CAirlineIcaoCode &airline, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Search among the airline aircraft.
static swift::misc::simulation::CAircraftModel reverseLookupModel(const swift::misc::aviation::CCallsign &callsign, const swift::misc::aviation::CAircraftIcaoCode &networkAircraftIcao, const swift::misc::aviation::CAirlineIcaoCode &networkAirlineIcao, const QString &networkLiveryInfo, const QString &networkModelString, const swift::misc::simulation::CAircraftMatcherSetup &setup, const swift::misc::simulation::CAircraftModelList &modelSet, swift::misc::simulation::CAircraftModel::ModelType type, swift::misc::CStatusMessageList *log)
Try to find the corresponding data in DB and get best information for given data.
static int reverseLookupByIds(const swift::misc::simulation::DBTripleIds &ids, swift::misc::aviation::CAircraftIcaoCode &aircraftIcao, swift::misc::aviation::CLivery &livery, const swift::misc::aviation::CCallsign &logCallsign, swift::misc::CStatusMessageList *log=nullptr)
Lookup by ids.
static bool isKnownAircraftDesignator(const QString &candidate, const swift::misc::aviation::CCallsign &callsign={}, swift::misc::CStatusMessageList *log=nullptr)
Is this aircraft designator known?
Class monitoring and analyzing (closest aircraft, outdated aircraft / watchdog) airspace in backgroun...
void timeoutAircraft(const swift::misc::aviation::CCallsign &callsign)
Callsign has timed out.
void timeoutAtc(const swift::misc::aviation::CCallsign &callsign)
Callsign has timed out.
void airspaceAircraftSnapshot(const swift::misc::simulation::CAirspaceAircraftSnapshot &snapshot)
New aircraft snapshot.
swift::misc::simulation::CAirspaceAircraftSnapshot getLatestAirspaceAircraftSnapshot() const
Get the latest snapshot.
misc::network::CUserList getUsers() const
Returns the list of users we know about.
MatchingReadinessFlag
Matching readiness.
@ ReceivedFsInnPacket
FsInn pcket received.
@ ReadyForMatchingSent
Read for matching sending.
@ ReceivedIcaoCodes
ICAO codes received.
void requestAtisUpdates()
Request to update ATC stations' ATIS data from the network.
misc::aviation::CAtcStationList getAtcStationsOnlineRecalculated()
Recalculate distance to own aircraft.
misc::aviation::CAtcStationList getAtcStationsOnline() const
Returns the current online ATC stations.
static const QStringList & getLogCategories()
Log categories.
misc::aviation::CFlightPlanRemarks tryToGetFlightPlanRemarks(const misc::aviation::CCallsign &callsign) const
Try to get flight plan remarks.
void atcStationDisconnected(const swift::misc::aviation::CAtcStation &station)
ATC station disconnected.
misc::network::CUserList getUsersForCallsigns(const misc::aviation::CCallsignSet &callsigns) const
Returns a list of the users corresponding to the given callsigns.
void requestAircraftDataUpdates()
Request to update other clients' data from the network.
misc::aviation::CFlightPlan loadFlightPlanFromNetwork(const misc::aviation::CCallsign &callsign)
Returns the loaded flight plan for the given callsign.
void readyForModelMatching(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft)
Ready for model matching.
void changedAtisReceived(const swift::misc::aviation::CCallsign &callsign)
An ATIS has been received.
void changedAtcStationsOnline()
Online ATC stations were changed.
int reInitializeAllAircraft()
Re-init all aircrft.
misc::aviation::CAtcStation getAtcStationForComUnit(const misc::aviation::CComSystem &comSystem) const
Returns the closest ATC station operating on the given frequency, if any.
static QString enumToString(MatchingReadiness r)
As string.
void setMaxRange(const swift::misc::physical_quantities::CLength &range)
Max (FSD) range.
void clear()
Clear the contents.
static void registerHelp()
Register help.
static const QString & enumFlagToString(MatchingReadinessFlag r)
As string.
void gracefulShutdown()
Gracefully shut down, e.g. for thread safety.
bool updateFastPositionEnabled(const misc::aviation::CCallsign &callsign, bool enableFastPositonUpdates)
Members not implenented or fully implenented by CRemoteAircraftProvider.
misc::simulation::CAirspaceAircraftSnapshot getLatestAirspaceAircraftSnapshot() const
Members not implenented or fully implenented by CRemoteAircraftProvider.
bool isDeveloperFlagSet() const
Running with dev.flag?
Definition: application.h:173
bool hasWebDataServices() const
Web data services available?
const context::IContextNetwork * getIContextNetwork() const
Direct access to contexts if a CCoreFacade has been initialized.
bool isShuttingDown() const
Is application shutting down?
CWebDataServices * getWebDataServices() const
Get the web data services.
swift::misc::network::CUserList getUsersForCallsign(const swift::misc::aviation::CCallsign &callsign) const
Users by callsign.
void updateWithVatsimDataFileData(swift::misc::simulation::CSimulatedAircraft &aircraftToBeUdpated) const
Update with web data.
swift::misc::network::CVoiceCapabilities getVoiceCapabilityForCallsign(const swift::misc::aviation::CCallsign &callsign) const
Voice capabilities for given callsign.
swift::misc::simulation::CAircraftModel getModelForModelString(const QString &modelString) const
Model for model string if any.
vatsim::CVatsimDataFileReader * getVatsimDataFileReader() const
Data file reader.
swift::misc::aviation::CAtcStationList getAtcStationsForCallsign(const swift::misc::aviation::CCallsign &callsign) const
ATC stations by callsign.
virtual swift::misc::network::CLoginMode getLoginMode() const =0
Login mode.
FSD client Todo: Send (interim) data updates automatically Todo Check ':' in FSD messages....
Definition: fsdclient.h:85
void sendClientQueryCom1Freq(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:456
void sendClientQueryAtis(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:471
const swift::misc::network::CServer & getServer() const
Get the server.
Definition: fsdclient.h:161
void sendPlaneInfoRequest(const swift::misc::aviation::CCallsign &receiver)
Definition: fsdclient.cpp:715
void sendClientQueryRealName(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:461
void addInterimPositionReceiver(const swift::misc::aviation::CCallsign &receiver)
Interim positions.
Definition: fsdclient.h:211
void sendClientQueryServer(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:466
void removeInterimPositionReceiver(const swift::misc::aviation::CCallsign &receiver)
Interim positions.
Definition: fsdclient.h:215
void sendClientQueryAircraftConfig(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:481
void sendClientQueryFlightPlan(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:476
void sendPlaneInfoRequestFsinn(const swift::misc::aviation::CCallsign &callsign)
Definition: fsdclient.cpp:730
swift::misc::network::CConnectionStatus getConnectionStatus() const
Connection status.
Definition: fsdclient.h:249
swift::misc::aviation::CFlightPlanRemarks getFlightPlanRemarksForCallsign(const swift::misc::aviation::CCallsign &callsign) const
Flight plan remarks for callsign.
T get() const
Get a copy of the current value.
Definition: valuecache.h:408
void remove(const T &object)
Efficient remove using the find and erase of the implementation container. Typically O(log n).
Definition: collection.h:307
bool isEmpty() const
Synonym for empty.
Definition: collection.h:191
Utility class which blocks until a signal is emitted or timeout reached.
Definition: eventloop.h:20
bool exec(int timeoutMs)
Begin processing events until the timeout or stop condition occurs.
Definition: eventloop.h:51
void stopWhen(const T *sender, F signal)
Event loop will stop if the given signal is received.
Definition: eventloop.h:34
Value object encapsulating information identifying a component of a modular distributed swift process...
Definition: identifier.h:29
static const CIdentifier & null()
Null (empty) identifier.
Definition: identifier.cpp:84
static const QString & matching()
Matching.
static const QString & network()
Network specific, but not necessarily one specific flight network.
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Specialized value object compliant map for variants, based on indexes.
void addValue(const CPropertyIndex &index, const CVariant &value)
Add a value.
bool contains(const T &object) const
Return true if there is an element equal to given object. Uses the most efficient implementation avai...
Definition: range.h:109
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
const_reference frontOrDefault() const
Access the first element, or a default-initialized value if the sequence is empty.
Definition: sequence.h:239
bool empty() const
Returns true if the sequence is empty.
Definition: sequence.h:282
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference front()
Access the first element.
Definition: sequence.h:225
void clear()
Removes all elements in the sequence.
Definition: sequence.h:288
bool isEmpty() const
Synonym for empty.
Definition: sequence.h:285
Utility methods for simple line parsing used with the command line.
Streamable status message, e.g.
constexpr static auto SeverityWarning
Status severities.
Status messages, e.g. from Core -> GUI.
static bool isInThisThread(const QObject *toBeTested)
Is the current thread the object's thread?
Definition: threadutils.cpp:16
Wrapper around QVariant which provides transparent access to CValueObject methods of the contained ob...
Definition: variant.h:66
static CVariant fromValue(T &&value)
Construct a variant from a value.
Definition: variant.h:136
OBJ latestObject() const
Latest object.
Value object for ICAO classification.
bool hasDesignator() const
Aircraft designator?
bool isVtol() const
Is VTOL aircraft (helicopter, tilt wing)
QString getDesignatorDbKey() const
Designator and DB key.
void guessModelParameters(physical_quantities::CLength &guessedCGOut, physical_quantities::CSpeed &guessedVRotateOut) const
Guess aircraft model parameters.
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
QJsonObject toIncrementalJson() const
Incremental JSON object.
QJsonObject toFullJson() const
Full JSON Object.
Value object encapsulating a list of aircraft parts.
Value object about changes in situations.
bool isConstAscending() const
Constantly ascending?
bool hasAltitudeDevWithinAllowedRange() const
Altitude within CAircraftSituation::allowedAltitudeDeviation range.
bool hasElevationDevWithinAllowedRange() const
Elevation within CAircraftSituation::allowedAltitudeDeviation range.
bool isConstOnGround() const
Are all situations on ground?
Value object encapsulating information of an aircraft's situation.
const CAltitude & getGroundElevation() const
Elevation of the ground directly beneath.
bool hasGroundElevation() const
Is ground elevation value available.
bool setGroundElevation(const aviation::CAltitude &altitude, GndElevationInfo info, bool transferred=false)
Elevation of the ground directly beneath at the given situation.
bool transferGroundElevationFromMe(CAircraftSituation &transferToSituation, const physical_quantities::CLength &radius=geo::CElevationPlane::singlePointRadius()) const
Transfer from "this" situation to otherSituation.
aviation::COnGroundInfo getOnGroundInfo() const
On ground info.
const CCallsign & getCallsign() const
Corresponding callsign.
const physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
const CAltitude & getAltitude() const
Get altitude.
CAircraftSituation findClosestElevationWithinRange(const geo::ICoordinateGeodetic &coordinate, const physical_quantities::CLength &range=geo::CElevationPlane::singlePointRadius()) const
CLosest elevation within given range.
Value object for ICAO classification.
void setTelephonyDesignator(const QString &telephony)
Telephony designator such as "Speedbird".
QString getDesignatorDbKey() const
Designator and DB key.
const QString & getDesignator() const
Get airline, e.g. "DLH".
bool hasValidDesignator() const
Airline designator available?
void setName(const QString &name)
Set name.
void setDesignator(const QString &icaoDesignator)
Set airline, e.g. "DLH".
Value object encapsulating information about an ATC station.
Definition: atcstation.h:38
void setFrequency(const swift::misc::physical_quantities::CFrequency &frequency)
Set frequency.
Definition: atcstation.cpp:116
const CCallsign & getCallsign() const
Get callsign.
Definition: atcstation.h:84
void setCallsign(const CCallsign &callsign)
Set callsign.
Definition: atcstation.cpp:56
void setPosition(const swift::misc::geo::CCoordinateGeodetic &position)
Set position.
Definition: atcstation.h:144
bool setOnline(bool online)
Set online.
Definition: atcstation.cpp:128
void setRange(const physical_quantities::CLength &range)
Set range.
Definition: atcstation.h:150
Value object for a list of ATC stations.
CAtcStationList findIfComUnitTunedInChannelSpacing(const CComSystem &comUnit) const
Find 0..n stations tuned in frequency of COM unit (with channel spacing)
bool updateIfMessageChanged(const CInformationMessage &im, const CCallsign &callsign, bool overrideWithNewer)
Update if message changed.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
bool isMaybeCopilotCallsign(const CCallsign &pilotCallsign) const
Returns true if this is a co-pilot callsign of pilot. The logic is that the callsign is the same as t...
Definition: callsign.cpp:169
bool hasSuffix() const
Suffix such as "_TWR"?
Definition: callsign.cpp:294
bool isEmpty() const
Is empty?
Definition: callsign.h:63
QString getAirlinePrefix() const
Airline suffix (e.g. DLH1234 -> DLH) if applicable.
Definition: callsign.cpp:219
bool isValid() const
Valid callsign?
Definition: callsign.cpp:359
Value object for a set of callsigns.
Definition: callsignset.h:26
COM system (aka "radio")
Definition: comsystem.h:37
Value object for a flight plan.
Definition: flightplan.h:148
bool wasSentOrLoaded() const
Flight plan already sent.
Definition: flightplan.h:369
qint64 timeDiffSentOrLoadedMs() const
Received before n ms.
Definition: flightplan.h:372
Flight plan remarks, parsed values.
Definition: flightplan.h:46
const CAirlineIcaoCode & getAirlineIcao() const
Airline ICAO if provided in flight plan.
Definition: flightplan.h:67
bool isEmpty() const
Empty remarks?
Definition: flightplan.h:101
const QString & getFlightOperator() const
Operator, i.e. normally the airline name.
Definition: flightplan.h:64
const QString & getRadioTelephony() const
Radio telephony designator.
Definition: flightplan.h:61
const QString & getRemarks() const
The unparsed remarks.
Definition: flightplan.h:58
Value object encapsulating information message (ATIS, METAR, TAF)
Value object encapsulating information about an airpot.
Definition: livery.h:29
const CAirlineIcaoCode & getAirlineIcaoCode() const
Corresponding airline, if any.
Definition: livery.h:65
const QString & getCombinedCode() const
Combined code.
Definition: livery.h:71
OnGroundDetails getGroundDetails() const
Get ground details.
CONTAINER findByCallsign(const CCallsign &callsign) const
Find 0..n stations by callsign.
OBJ findFirstByCallsign(const CCallsign &callsign, const OBJ &ifNotFound={}) const
Find the first aircraft by callsign, if none return given one.
int applyIfCallsign(const CCallsign &callsign, const CPropertyIndexVariantMap &variantMap, bool skipEqualValues=true)
Apply for given callsign.
int removeByCallsign(const CCallsign &callsign)
Remove all objects with callsign.
bool containsCallsign(const CCallsign &callsign) const
Contains callsign?
QString getDbKeyAsString() const
DB key as string.
Definition: datastore.cpp:30
bool isLoadedFromDb() const
Loaded from DB.
Definition: datastore.cpp:49
bool hasValidDbKey() const
Has valid DB key.
Definition: datastore.h:102
Plane of same elevation, can be a single point or larger area (e.g. airport)
bool isNull() const
Existing value?
const physical_quantities::CLength & getRadius() const
Radius.
bool isNaNVectorDouble() const
Check values.
physical_quantities::CLength calculateGreatCircleDistance(const ICoordinateGeodetic &otherCoordinate) const
Great circle distance.
bool isInfVectorDouble() const
Check values.
bool isValidVectorRange() const
Check values.
bool equalNormalVectorDouble(const std::array< double, 3 > &otherVector) const
Is equal? Epsilon considered.
physical_quantities::CAngle calculateBearing(const ICoordinateGeodetic &otherCoordinate) const
Initial bearing.
physical_quantities::CLength calculcateAndUpdateRelativeDistanceAndBearing(const geo::ICoordinateGeodetic &position)
Calculcate distance and bearing to plane, set it, and return distance.
void calculcateAndUpdateRelativeDistanceAndBearing(const ICoordinateGeodetic &position)
Calculate distances.
void sortByDistanceToReferencePosition()
If distance is already set, just sort container.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:74
Another client software.
Definition: client.h:27
bool hasCapability(Capability capability) const
Has capability?
Definition: client.cpp:64
const QString & getQueriedModelString() const
Model.
Definition: client.h:140
void addCapability(Capability capability)
Add capability.
Definition: client.cpp:33
bool setUserCallsign(const swift::misc::aviation::CCallsign &callsign)
User's callsign.
Definition: client.cpp:70
Value object encapsulating a list of voice rooms.
Definition: clientlist.h:27
int removeClient(const aviation::CCallsign &callsign)
Remove client.
int updateOrAddClient(const aviation::CCallsign &callsign, const CPropertyIndexVariantMap &vm, bool skipEqualValues=true)
Update or add a client.
void markAsSwiftClient(const aviation::CCallsign &callsign)
Mark as other swift client.
bool setOtherClient(const swift::misc::network::CClient &client)
Set client for its callsign.
bool addNewClient(const CClient &client)
Add a new client, if existing nothing will be added.
void setClients(const CClientList &clients)
Set other clients.
CClientList getClients() const
clientprovider
void clearClients()
Set other clients.
CClient getClientOrDefaultForCallsign(const aviation::CCallsign &callsign) const
Other client for the given callsigns.
bool autoAdjustCientGndCapability(const aviation::CAircraftSituation &situation)
Adjust gnd.flag capability from situation.
Value object encapsulating information about a connection status.
bool isConnected() const
Query status.
bool isDisconnected() const
Query status.
Ecosystem of server belonging together.
Definition: ecosystem.h:21
bool isSystem(System s) const
Is system?
Definition: ecosystem.h:52
@ Observer
Login as observer.
Definition: loginmode.h:24
Value object encapsulating information of a FSD server.
Definition: server.h:28
const CEcosystem & getEcosystem() const
Get the ecosystem.
Definition: server.h:99
Value object encapsulating information of a user.
Definition: user.h:28
bool setCallsign(const aviation::CCallsign &callsign)
Set associated callsign.
Definition: user.cpp:62
bool hasCallsign() const
Has associated callsign?
Definition: user.h:89
Value object encapsulating a list of voice rooms.
Definition: userlist.h:26
Value object encapsulating information for voice capabilities.
bool isUnknown() const
Is capability known.
Physical unit angle (radians, degrees)
Definition: angle.h:23
Physical unit length (length)
Definition: length.h:18
bool isNegativeWithEpsilonConsidered() const
Value <= 0 epsilon considered.
int valueInteger(MU unit) const
As integer value.
PQ & switchUnit(const MU &newUnit)
Change unit, and convert value to maintain the same quantity.
double value(MU unit) const
Value in given unit.
bool isZeroEpsilonConsidered() const
Quantity value <= epsilon.
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
static DBTripleIds parseNetworkLiveryString(const QString &liveryString)
Split swift network string.
const aviation::CLivery & getLivery() const
Get livery.
const QString & getAircraftIcaoCodeDesignator() const
Aircraft ICAO code designator.
@ TypeFSInnData
model based on FSD ICAO data
Definition: aircraftmodel.h:78
@ TypeQueriedFromNetwork
model was queried by network protocol (ICAO data)
Definition: aircraftmodel.h:77
void setCallsign(const aviation::CCallsign &callsign)
Corresponding callsign if applicable.
const physical_quantities::CLength & getCG() const
Get center of gravity.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Aircraft ICAO code.
const QString & getModelTypeAsString() const
Model type.
const QString getAirlineIcaoCodeVDesignator() const
Airline ICAO code designator.
void updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority=true)
Update missing parts from another model.
Value object encapsulating a list of aircraft models.
Delegating class which can be directly used to access an.
aviation::CAircraftSituation getOwnAircraftSituation() const
Own aircraft's position.
aviation::CCallsign getOwnCallsign() const
Own aircraft's callsign.
swift::misc::geo::CCoordinateGeodetic getOwnAircraftPosition() const
Own aircraft's position.
CSimulatedAircraft getOwnAircraft() const
Own aircraft.
Implementaion of the interface, which can also be used for testing.
bool isAircraftInRange(const aviation::CCallsign &callsign) const
Is aircraft in range?
CSimulatedAircraftList getAircraftInRange() const
All remote aircraft.
virtual aviation::CAircraftSituation storeAircraftSituation(const aviation::CAircraftSituation &situation, bool allowTestAltitudeOffset=true)
Store an aircraft situation.
bool addNewAircraftInRange(const CSimulatedAircraft &aircraft)
Add new aircraft, ignored if aircraft already exists.
aviation::CAircraftSituation addTestAltitudeOffsetToSituation(const aviation::CAircraftSituation &situation) const
Add an offset for testing.
bool removeAircraft(const aviation::CCallsign &callsign)
Remove all aircraft in range.
int remoteAircraftSituationsCount(const aviation::CCallsign &callsign) const
Number of remote aircraft situations for callsign.
geo::CElevationPlane averageElevationOfNonMovingAircraft(const aviation::CAircraftSituation &reference, const physical_quantities::CLength &range, int minValues=1, int sufficientValues=2) const
Average elevation of aircraft in given range, which are NOT moving.
aviation::CAircraftSituationList remoteAircraftSituations(const aviation::CCallsign &callsign) const
Rendered aircraft situations (per callsign, time history)
aviation::CAircraftSituationChangeList remoteAircraftSituationChanges(const aviation::CCallsign &callsign) const
Aircraft changes.
int updateAircraftInRange(const aviation::CCallsign &callsign, const CPropertyIndexVariantMap &vm, bool skipEqualValues=true)
Update aircraft.
void storeAircraftParts(const aviation::CCallsign &callsign, const aviation::CAircraftParts &parts, bool removeOutdated)
Store an aircraft part.
bool updateAircraftInRangeDistanceBearing(const aviation::CCallsign &callsign, const aviation::CAircraftSituation &situation, const physical_quantities::CLength &distance, const physical_quantities::CAngle &bearing)
Update aircraft bearing, distance and situation.
void rememberCGFromDB(const physical_quantities::CLength &cgFromDB, const aviation::CCallsign &callsign)
CG values from DB.
void removeReverseLookupMessages(const aviation::CCallsign &callsign)
Remove the lookup messages.
void addReverseLookupMessages(const aviation::CCallsign &callsign, const CStatusMessageList &messages)
Reverse lookup messages.
void airspaceAircraftSnapshot(const swift::misc::simulation::CAirspaceAircraftSnapshot &snapshot)
New aircraft snapshot.
bool updateAircraftNetworkModel(const aviation::CCallsign &callsign, const CAircraftModel &model, const CIdentifier &originator)
Change network model.
bool updateAircraftModel(const aviation::CCallsign &callsign, const CAircraftModel &model, const CIdentifier &originator)
Change model.
physical_quantities::CLength getCGFromDB(const aviation::CCallsign &callsign) const
CG values from DB.
bool updateFastPositionEnabled(const aviation::CCallsign &callsign, bool enableFastPositonUpdates)
Change fast position updates.
ReverseLookupLogging whatToReverseLog() const
What to log?
ReverseLookupLogging isReverseLookupMessagesEnabled() const
Enabled reverse lookup logging?
CAircraftModel getAircraftInRangeModelForCallsign(const aviation::CCallsign &callsign) const
Aircraft model for callsign.
CSimulatedAircraft getAircraftInRangeForCallsign(const aviation::CCallsign &callsign) const
Aircraft for callsign.
aviation::CAircraftPartsList remoteAircraftParts(const aviation::CCallsign &callsign) const
All parts (per callsign, time history)
void enableReverseLookupMessages(ReverseLookupLogging enable)
Enable reverse lookup logging.
void removeAllAircraft()
Remove all aircraft in range.
void addReverseLookupMessage(const aviation::CCallsign &callsign, const CStatusMessage &message)
Reverse lookup messages.
void removedAircraft(const swift::misc::aviation::CCallsign &callsign)
An aircraft disappeared.
Comprehensive information of an aircraft.
void setCallsign(const aviation::CCallsign &callsign)
Set callsign.
bool hasModelString() const
Has model string?
void setSituation(const aviation::CAircraftSituation &situation)
Set situation. Won't overwrite the velocity unless it held the default value.
const network::CUser & getPilot() const
Get user.
const simulation::CAircraftModel & getNetworkModel() const
Get network model.
const aviation::CCallsign & getCallsign() const
Get callsign.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.
bool hasValidCallsign() const
Valid callsign?
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
void setTransponder(const aviation::CTransponder &transponder)
Set transponder.
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code if any.
const QString & getModelString() const
Get model string.
Value object encapsulating a list of aircraft.
bool requestElevation(const geo::ICoordinateGeodetic &reference, const aviation::CCallsign &callsign)
Request elevation, there is no guarantee the requested elevation will be available in the provider.
physical_quantities::CLength getSimulatorOrDbCG(const aviation::CCallsign &callsign, const physical_quantities::CLength &dbCG) const
Get CG per callsign, NULL if not found.
geo::CElevationPlane averageElevationOfOnGroundAircraft(const aviation::CAircraftSituation &reference, const physical_quantities::CLength &range, int minValues, int sufficientValues) const
Average elevation of "on ground" cached values.
geo::CElevationPlane findClosestElevationWithinRange(const geo::ICoordinateGeodetic &reference, const physical_quantities::CLength &range) const
Find closest elevation (or return NULL)
Value object encapsulating a list of aircraft models.
virtual int getModelSetCount() const =0
Get the model set models count.
virtual CAircraftModelList getModelSet() const =0
Get the model set models.
Direct threadsafe in memory access to own aircraft.
bool parseCommandLine(const QString &commandLine, const swift::misc::CIdentifier &originator)
Parse a given command line.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Generate data for testing purposes.
Definition: testdata.h:45
Free functions in swift::misc.
QDateTime currentDateTimeUtc()
void setTime(QTime time, QDateTime::TransitionResolution resolve)
QString toString(QStringView format) const const
void clear()
bool contains(const Key &key) const const
QHash< Key, T >::iterator insert(Key &&key, T &&value)
bool remove(const Key &key)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
void setObjectName(QAnyStringView name)
T dequeue()
void enqueue(T &&t)
QString arg(Args &&... args) const const
void clear()
bool isEmpty() const const
qsizetype length() const const
QString join(QChar separator) const const
QueuedConnection
QTextStream & ws(QTextStream &stream)
void start()
void timeout()
bool hasAnyId() const
Any valid id?
Definition: aircraftmodel.h:56
swift::misc::simulation::CAircraftModel model
the model
bool runScriptAndModified() const
Did run the script with modified result.
bool runScriptAndRerun() const
Did run the script and re-run requested.
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.
Definition: verify.h:38
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.
Definition: verify.h:26