14 #include "XPMPMultiplayer.h"
15 #include <XPLM/XPLMGraphics.h>
16 #include <XPLM/XPLMProcessing.h>
17 #include <XPLM/XPLMUtilities.h>
18 #include <XPLM/XPLMPlanes.h>
19 #include <XPLM/XPLMPlugin.h>
31 float XPMP_PrepListHook(
float,
float,
int,
void *);
33 using namespace swift::misc::simulation::xplane::qtfreeutils;
34 using namespace std::chrono_literals;
38 CTraffic::Plane::Plane(
void *id_,
const std::string &callsign_,
const std::string &aircraftIcao_,
39 const std::string &airlineIcao_,
const std::string &livery_,
const std::string &modelName_)
40 : id(id_), callsign(callsign_), aircraftIcao(aircraftIcao_), airlineIcao(airlineIcao_), livery(livery_),
43 std::memset(
static_cast<void *
>(&positions), 0,
sizeof(positions));
44 for (
auto &position : positions) { position.size =
sizeof(position); }
45 std::memset(
static_cast<void *
>(&surveillance), 0,
sizeof(surveillance));
46 surveillance.size =
sizeof(surveillance);
47 std::memset(
static_cast<void *
>(&surfaces), 0,
sizeof(surfaces));
48 surfaces.size =
sizeof(surfaces);
49 surfaces.lights.bcnLights = surfaces.lights.landLights = surfaces.lights.navLights =
50 surfaces.lights.strbLights = 1;
52 std::strncpy(label, callsign.c_str(),
sizeof(label));
53 for (
auto &position : positions) { memcpy(position.label, label,
sizeof(label)); }
55 std::srand(
static_cast<unsigned int>(std::time(
nullptr)));
56 surfaces.lights.timeOffset =
static_cast<uint16_t
>(std::rand() % 0xffff);
59 CTraffic *CTraffic::s_instance =
nullptr;
62 CTraffic::CTraffic(CSettingsProvider *settingsProvider)
63 : CDBusObject(settingsProvider),
64 m_followPlaneViewNextCommand(
"org/swift-project/xswiftbus/follow_next_plane",
65 "Changes plane view to follow next plane in sequence",
66 [this] { followNextPlane(); }),
67 m_followPlaneViewPreviousCommand(
"org/swift-project/xswiftbus/follow_previous_plane",
68 "Changes plane view to follow previous plane in sequence",
69 [
this] { followPreviousPlane(); })
73 XPLMRegisterKeySniffer(followAircraftKeySniffer, 1,
this);
76 this->setDrawingLabels(this->getSettings().isDrawingLabels());
83 assert(s_instance ==
this);
87 void CTraffic::setPlaneViewMenu(
const CMenu &planeViewSubMenu) { m_followPlaneViewSubMenu = planeViewSubMenu; }
89 bool CTraffic::initialize()
96 std::string related = dir +
"related.txt";
97 std::string doc8643 = dir +
"Doc8643.txt";
99 updateConfiguration();
100 auto err = XPMPMultiplayerInit(&m_configuration, related.c_str(), doc8643.c_str());
101 if (err && *err) { cleanup(); }
102 else { m_initialized =
true; }
105 return m_initialized;
108 bool CTraffic::acquireMultiplayerPlanes(std::string *owner)
110 if (!m_enabledMultiplayer)
112 auto err = XPMPMultiplayerEnable();
113 if (*err) { cleanup(); }
116 m_enabledMultiplayer =
true;
119 XPLMUnregisterFlightLoopCallback(XPMP_PrepListHook,
nullptr);
125 XPLMPluginID controller;
126 XPLMCountAircraft(&totalAircraft, &activeAircraft, &controller);
128 char pluginName[256];
129 XPLMGetPluginInfo(controller, pluginName,
nullptr,
nullptr,
nullptr);
130 *owner = std::string(pluginName);
131 return m_enabledMultiplayer;
134 void CTraffic::cleanup()
138 if (m_enabledMultiplayer)
140 m_enabledMultiplayer =
false;
141 XPMPMultiplayerDisable();
146 m_initialized =
false;
147 XPMPMultiplayerCleanup();
151 void CTraffic::emitSimFrame()
153 if (m_emitSimFrame) { sendDBusSignal(
"simFrame"); }
154 m_emitSimFrame = !m_emitSimFrame;
157 void CTraffic::emitPlaneAdded(
const std::string &callsign)
159 CDBusMessage signalPlaneAdded = CDBusMessage::createSignal(
160 XSWIFTBUS_TRAFFIC_OBJECTPATH, XSWIFTBUS_TRAFFIC_INTERFACENAME,
"remoteAircraftAdded");
161 signalPlaneAdded.beginArgumentWrite();
162 signalPlaneAdded.appendArgument(callsign);
163 sendDBusMessage(signalPlaneAdded);
166 void CTraffic::emitPlaneAddingFailed(
const std::string &callsign)
168 CDBusMessage signalPlaneAddingFailed = CDBusMessage::createSignal(
169 XSWIFTBUS_TRAFFIC_OBJECTPATH, XSWIFTBUS_TRAFFIC_INTERFACENAME,
"remoteAircraftAddingFailed");
170 signalPlaneAddingFailed.beginArgumentWrite();
171 signalPlaneAddingFailed.appendArgument(callsign);
172 sendDBusMessage(signalPlaneAddingFailed);
175 void CTraffic::switchToFollowPlaneView(
const std::string &callsign)
177 if (CTraffic::ownAircraftString() != callsign && !this->containsCallsign(callsign))
179 INFO_LOG(
"Cannot switch to follow " + callsign);
183 m_followPlaneViewCallsign = callsign;
188 XPLMCommandButtonPress(xplm_joy_v_fr1);
189 XPLMCommandButtonRelease(xplm_joy_v_fr1);
192 INFO_LOG(
"Switch to follow " + callsign);
193 XPLMControlCamera(xplm_ControlCameraUntilViewChanges, CTraffic::orbitPlaneFunc,
this);
196 void CTraffic::followNextPlane()
198 if (m_planesByCallsign.empty() || m_followPlaneViewCallsign.empty()) {
return; }
200 std::find(m_followPlaneViewSequence.begin(), m_followPlaneViewSequence.end(), m_followPlaneViewCallsign);
203 if (callsignIt != m_followPlaneViewSequence.end()) { callsignIt++; }
205 if (callsignIt == m_followPlaneViewSequence.end()) { callsignIt = m_followPlaneViewSequence.begin(); }
207 m_followPlaneViewCallsign = *callsignIt;
210 void CTraffic::followPreviousPlane()
212 if (m_planesByCallsign.empty() || m_followPlaneViewCallsign.empty()) {
return; }
214 std::find(m_followPlaneViewSequence.rbegin(), m_followPlaneViewSequence.rend(), m_followPlaneViewCallsign);
217 if (callsignIt != m_followPlaneViewSequence.rend()) { callsignIt++; }
219 if (callsignIt == m_followPlaneViewSequence.rend()) { callsignIt = m_followPlaneViewSequence.rbegin(); }
221 m_followPlaneViewCallsign = *callsignIt;
224 bool CTraffic::containsCallsign(
const std::string &callsign)
const
227 if (callsign.empty()) {
return false; }
228 const auto planeIt = m_planesByCallsign.find(callsign);
229 if (planeIt == m_planesByCallsign.end()) {
return false; }
237 void CTraffic::updateConfiguration()
239 m_configuration.maxFullAircraftRenderingDistance =
240 static_cast<float>(s_instance->getSettings().getMaxDrawDistanceNM());
241 m_configuration.enableSurfaceClamping =
true;
242 m_configuration.debug.modelMatching =
false;
245 std::string CTraffic::loadPlanesPackage(
const std::string &path)
249 auto err = XPMPMultiplayerLoadCSLPackages(path.c_str());
250 if (*err) {
return err; }
252 for (
int i = 0,
end = XPMPGetNumberOfInstalledModels(); i <
end; ++i)
254 const char *mixedcase;
255 XPMPGetModelInfo(i, &mixedcase,
nullptr,
nullptr,
nullptr);
256 std::string uppercase(mixedcase);
257 std::transform(uppercase.begin(), uppercase.end(), uppercase.begin(),
258 [](
char c) { return static_cast<char>(std::toupper(c)); });
259 m_modelStrings[uppercase] = mixedcase;
265 void CTraffic::setDefaultIcao(
const std::string &defaultIcao) { XPMPSetDefaultPlaneICAO(defaultIcao.c_str()); }
267 void CTraffic::setDrawingLabels(
bool drawing,
int rgb)
269 CSettings s = this->getSettings();
270 if (s.isDrawingLabels() != drawing)
272 s.setDrawingLabels(drawing);
273 this->setSettings(s);
275 if (rgb >= 0) { m_labels.setColor((rgb & 0xff0000) >> 16, (rgb & 0x00ff00) >> 8, rgb & 0x0000ff); }
276 if (drawing) { m_labels.show(); }
277 else { m_labels.hide(); }
280 bool CTraffic::isDrawingLabels()
const {
return m_labels.isVisible(); }
282 void CTraffic::setMaxPlanes(
int planes)
284 CSettings s = this->getSettings();
285 if (s.setMaxPlanes(planes)) { this->setSettings(s); }
288 void CTraffic::setMaxDrawDistance(
double nauticalMiles)
290 CSettings s = this->getSettings();
291 if (s.setMaxDrawDistanceNM(nauticalMiles)) { this->setSettings(s); }
294 void CTraffic::addPlane(
const std::string &callsign,
const std::string &modelName,
const std::string &aircraftIcao,
295 const std::string &airlineIcao,
const std::string &livery)
297 auto planeIt = m_planesByCallsign.find(callsign);
298 if (planeIt != m_planesByCallsign.end()) {
return; }
300 XPMPPlaneID
id =
nullptr;
301 if (modelName.empty() || m_modelStrings.count(modelName) == 0)
303 DEBUG_LOG(
"Model " + modelName +
" is unknown, falling back to basic xpmp2 model matching");
304 id = XPMPCreatePlane(aircraftIcao.c_str(), airlineIcao.c_str(), livery.c_str());
308 id = XPMPCreatePlaneWithModelName(m_modelStrings[modelName].c_str(), aircraftIcao.c_str(),
309 airlineIcao.c_str(), livery.c_str());
314 emitPlaneAddingFailed(callsign);
318 Plane *plane =
new Plane(
id, callsign, aircraftIcao, airlineIcao, livery, modelName);
319 m_planesByCallsign[callsign] = plane;
320 m_planesById[id] = plane;
323 CMenuItem planeViewMenuItem =
324 m_followPlaneViewSubMenu.item(callsign, [
this, callsign] { switchToFollowPlaneView(callsign); });
325 m_followPlaneViewMenuItems[callsign] = planeViewMenuItem;
326 m_followPlaneViewSequence.push_back(callsign);
328 emitPlaneAdded(callsign);
331 void CTraffic::removePlane(
const std::string &callsign)
333 auto menuItemIt = m_followPlaneViewMenuItems.find(callsign);
334 if (menuItemIt != m_followPlaneViewMenuItems.end())
336 m_followPlaneViewSubMenu.removeItem(menuItemIt->second);
337 m_followPlaneViewMenuItems.erase(menuItemIt);
340 auto planeIt = m_planesByCallsign.find(callsign);
341 if (planeIt == m_planesByCallsign.end()) {
return; }
343 m_followPlaneViewSequence.erase(
344 std::remove(m_followPlaneViewSequence.begin(), m_followPlaneViewSequence.end(), callsign),
345 m_followPlaneViewSequence.end());
347 Plane *plane = planeIt->second;
348 m_planesByCallsign.erase(callsign);
349 m_planesById.erase(plane->id);
350 XPMPDestroyPlane(plane->id);
354 void CTraffic::removeAllPlanes()
356 for (
const auto &kv : m_planesByCallsign)
358 Plane *plane = kv.second;
360 XPMPDestroyPlane(plane->id);
364 for (
const auto &kv : m_followPlaneViewMenuItems)
366 CMenuItem item = kv.second;
367 m_followPlaneViewSubMenu.removeItem(item);
370 m_planesByCallsign.clear();
371 m_planesById.clear();
372 m_followPlaneViewMenuItems.clear();
373 m_followPlaneViewSequence.clear();
376 void CTraffic::setPlanesPositions(
const std::vector<std::string> &callsigns, std::vector<double> latitudesDeg,
377 std::vector<double> longitudesDeg, std::vector<double> altitudesFt,
378 std::vector<double> pitchesDeg, std::vector<double> rollsDeg,
379 std::vector<double> headingsDeg,
const std::vector<bool> &onGrounds)
381 const bool setOnGround = onGrounds.size() == callsigns.size();
382 for (
size_t i = 0; i < callsigns.size(); i++)
384 auto planeIt = m_planesByCallsign.find(callsigns.at(i));
385 if (planeIt == m_planesByCallsign.end()) {
continue; }
387 Plane *plane = planeIt->second;
388 if (!plane) {
continue; }
389 plane->positions[2].lat = latitudesDeg.at(i);
390 plane->positions[2].lon = longitudesDeg.at(i);
391 plane->positions[2].elevation = altitudesFt.at(i);
392 plane->positions[2].pitch =
static_cast<float>(pitchesDeg.at(i));
393 plane->positions[2].roll =
static_cast<float>(rollsDeg.at(i));
394 plane->positions[2].heading =
static_cast<float>(headingsDeg.at(i));
395 plane->positions[2].offsetScale = 1.0f;
396 plane->positions[2].clampToGround =
true;
397 plane->positionTimes[2] = std::chrono::steady_clock::now();
400 if (plane->positionTimes[2] - plane->positionTimes[1] > 1s)
402 plane->positionTimes[0] = plane->positionTimes[1];
403 plane->positionTimes[1] = plane->positionTimes[2];
404 std::memcpy(&plane->positions[0], &plane->positions[1],
sizeof(plane->positions[0]));
405 std::memcpy(&plane->positions[1], &plane->positions[2],
sizeof(plane->positions[0]));
408 if (setOnGround) { plane->isOnGround = onGrounds.at(i); }
412 void CTraffic::setPlanesSurfaces(
const std::vector<std::string> &callsigns,
const std::vector<double> &gears,
413 const std::vector<double> &flaps,
const std::vector<double> &spoilers,
414 const std::vector<double> &speedBrakes,
const std::vector<double> &slats,
415 const std::vector<double> &wingSweeps,
const std::vector<double> &thrusts,
416 const std::vector<double> &elevators,
const std::vector<double> &rudders,
417 const std::vector<double> &ailerons,
const std::vector<bool> &landLights,
418 const std::vector<bool> &taxiLights,
const std::vector<bool> &beaconLights,
419 const std::vector<bool> &strobeLights,
const std::vector<bool> &navLights,
420 const std::vector<int> &lightPatterns)
422 const bool bundleTaxiLandingLights = this->getSettings().isBundlingTaxiAndLandingLights();
424 for (
size_t i = 0; i < callsigns.size(); i++)
426 auto planeIt = m_planesByCallsign.find(callsigns.at(i));
427 if (planeIt == m_planesByCallsign.end()) {
continue; }
429 Plane *plane = planeIt->second;
430 if (!plane) {
continue; }
432 plane->hasSurfaces =
true;
433 plane->targetGearPosition =
static_cast<float>(gears.at(i));
434 plane->surfaces.flapRatio =
static_cast<float>(flaps.at(i));
435 plane->surfaces.spoilerRatio =
static_cast<float>(spoilers.at(i));
436 plane->surfaces.speedBrakeRatio =
static_cast<float>(speedBrakes.at(i));
437 plane->surfaces.slatRatio =
static_cast<float>(slats.at(i));
438 plane->surfaces.wingSweep =
static_cast<float>(wingSweeps.at(i));
439 plane->surfaces.thrust =
static_cast<float>(thrusts.at(i));
440 plane->surfaces.yokePitch =
static_cast<float>(elevators.at(i));
441 plane->surfaces.yokeHeading =
static_cast<float>(rudders.at(i));
442 plane->surfaces.yokeRoll =
static_cast<float>(ailerons.at(i));
443 if (bundleTaxiLandingLights)
445 const bool on = landLights.at(i) || taxiLights.at(i);
446 plane->surfaces.lights.landLights = on;
447 plane->surfaces.lights.taxiLights = on;
451 plane->surfaces.lights.landLights = landLights.at(i);
452 plane->surfaces.lights.taxiLights = taxiLights.at(i);
454 plane->surfaces.lights.bcnLights = beaconLights.at(i);
455 plane->surfaces.lights.strbLights = strobeLights.at(i);
456 plane->surfaces.lights.navLights = navLights.at(i);
457 plane->surfaces.lights.flashPattern =
static_cast<unsigned int>(lightPatterns.at(i));
461 void CTraffic::setPlanesTransponders(
const std::vector<std::string> &callsigns,
const std::vector<int> &codes,
462 const std::vector<bool> &modeCs,
const std::vector<bool> &idents)
464 for (
size_t i = 0; i < callsigns.size(); i++)
466 auto planeIt = m_planesByCallsign.find(callsigns.at(i));
467 if (planeIt == m_planesByCallsign.end()) {
continue; }
469 Plane *plane = planeIt->second;
470 if (!plane) {
continue; }
472 plane->surveillance.code = codes.at(i);
473 if (idents.at(i)) { plane->surveillance.mode = xpmpTransponderMode_ModeC_Ident; }
474 else if (modeCs.at(i)) { plane->surveillance.mode = xpmpTransponderMode_ModeC; }
475 else { plane->surveillance.mode = xpmpTransponderMode_Standby; }
479 void CTraffic::getRemoteAircraftData(std::vector<std::string> &callsigns, std::vector<double> &latitudesDeg,
480 std::vector<double> &longitudesDeg, std::vector<double> &elevationsM,
481 std::vector<bool> &waterFlags, std::vector<double> &verticalOffsets)
const
483 if (callsigns.empty() || m_planesByCallsign.empty()) {
return; }
485 const auto requestedCallsigns = callsigns;
487 latitudesDeg.clear();
488 longitudesDeg.clear();
490 verticalOffsets.clear();
493 for (
const auto &requestedCallsign : requestedCallsigns)
495 const auto planeIt = m_planesByCallsign.find(requestedCallsign);
496 if (planeIt == m_planesByCallsign.end()) {
continue; }
498 const Plane *plane = planeIt->second;
501 const double latDeg = plane->positions[2].lat;
502 const double lonDeg = plane->positions[2].lon;
503 double groundElevation = 0.0;
504 bool isWater =
false;
505 if (getSettings().isTerrainProbeEnabled())
510 .getElevation(latDeg, lonDeg, plane->positions[2].elevation, requestedCallsign, isWater)
512 if (std::isnan(groundElevation)) { groundElevation = 0.0; }
515 callsigns.push_back(requestedCallsign);
516 latitudesDeg.push_back(latDeg);
517 longitudesDeg.push_back(lonDeg);
518 elevationsM.push_back(groundElevation);
519 waterFlags.push_back(isWater);
520 verticalOffsets.push_back(0);
524 std::array<double, 3> CTraffic::getElevationAtPosition(
const std::string &callsign,
double latitudeDeg,
525 double longitudeDeg,
double altitudeMeters,
526 bool &o_isWater)
const
528 if (!getSettings().isTerrainProbeEnabled())
530 return { { std::numeric_limits<double>::quiet_NaN(), latitudeDeg, longitudeDeg } };
533 const auto planeIt = m_planesByCallsign.find(callsign);
534 if (planeIt != m_planesByCallsign.end())
536 const Plane *plane = planeIt->second;
537 return plane->terrainProbe.getElevation(latitudeDeg, longitudeDeg, altitudeMeters, callsign, o_isWater);
541 return m_terrainProbe.getElevation(latitudeDeg, longitudeDeg, altitudeMeters,
542 callsign +
" (plane not found)", o_isWater);
546 void CTraffic::setFollowedAircraft(
const std::string &callsign) { this->switchToFollowPlaneView(callsign); }
548 void CTraffic::dbusDisconnectedHandler() { removeAllPlanes(); }
550 static const char *introspection_traffic = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
551 #include "org.swift_project.xswiftbus.traffic.xml"
554 DBusHandlerResult CTraffic::dbusMessageHandler(
const CDBusMessage &message_)
556 CDBusMessage message(message_);
557 const std::string sender = message.getSender();
558 const dbus_uint32_t serial = message.getSerial();
559 const bool wantsReply = message.wantsReply();
564 if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE)
566 if (message.getMethodName() ==
"Introspect") { sendDBusReply(sender, serial, introspection_traffic); }
568 else if (message.getInterfaceName() == XSWIFTBUS_TRAFFIC_INTERFACENAME)
570 if (message.getMethodName() ==
"acquireMultiplayerPlanes")
572 queueDBusCall([=]() {
574 bool acquired = acquireMultiplayerPlanes(&owner);
575 CDBusMessage reply = CDBusMessage::createReply(sender, serial);
576 reply.beginArgumentWrite();
577 reply.appendArgument(acquired);
578 reply.appendArgument(owner);
579 sendDBusMessage(reply);
582 else if (message.getMethodName() ==
"initialize") { sendDBusReply(sender, serial, initialize()); }
583 else if (message.getMethodName() ==
"cleanup")
585 maybeSendEmptyDBusReply(wantsReply, sender, serial);
586 queueDBusCall([=]() { cleanup(); });
588 else if (message.getMethodName() ==
"loadPlanesPackage")
591 message.beginArgumentRead();
592 message.getArgument(path);
593 queueDBusCall([=]() { sendDBusReply(sender, serial, loadPlanesPackage(path)); });
595 else if (message.getMethodName() ==
"setDefaultIcao")
597 std::string defaultIcao;
598 message.beginArgumentRead();
599 message.getArgument(defaultIcao);
600 queueDBusCall([=]() { setDefaultIcao(defaultIcao); });
602 else if (message.getMethodName() ==
"setMaxPlanes")
604 maybeSendEmptyDBusReply(wantsReply, sender, serial);
606 message.beginArgumentRead();
607 message.getArgument(planes);
608 queueDBusCall([=]() { setMaxPlanes(planes); });
610 else if (message.getMethodName() ==
"setMaxDrawDistance")
612 maybeSendEmptyDBusReply(wantsReply, sender, serial);
613 double nauticalMiles = 100;
614 message.beginArgumentRead();
615 message.getArgument(nauticalMiles);
616 queueDBusCall([=]() { setMaxDrawDistance(nauticalMiles); });
618 else if (message.getMethodName() ==
"addPlane")
620 maybeSendEmptyDBusReply(wantsReply, sender, serial);
621 std::string callsign;
622 std::string modelName;
623 std::string aircraftIcao;
624 std::string airlineIcao;
626 message.beginArgumentRead();
627 message.getArgument(callsign);
628 message.getArgument(modelName);
629 message.getArgument(aircraftIcao);
630 message.getArgument(airlineIcao);
631 message.getArgument(livery);
633 queueDBusCall([=]() { addPlane(callsign, modelName, aircraftIcao, airlineIcao, livery); });
635 else if (message.getMethodName() ==
"removePlane")
637 maybeSendEmptyDBusReply(wantsReply, sender, serial);
638 std::string callsign;
639 message.beginArgumentRead();
640 message.getArgument(callsign);
641 queueDBusCall([=]() { removePlane(callsign); });
643 else if (message.getMethodName() ==
"removeAllPlanes")
645 maybeSendEmptyDBusReply(wantsReply, sender, serial);
646 queueDBusCall([=]() { removeAllPlanes(); });
648 else if (message.getMethodName() ==
"setPlanesPositions")
650 maybeSendEmptyDBusReply(wantsReply, sender, serial);
651 std::vector<std::string> callsigns;
652 std::vector<double> latitudes;
653 std::vector<double> longitudes;
654 std::vector<double> altitudes;
655 std::vector<double> pitches;
656 std::vector<double> rolls;
657 std::vector<double> headings;
658 std::vector<bool> onGrounds;
659 message.beginArgumentRead();
660 message.getArgument(callsigns);
661 message.getArgument(latitudes);
662 message.getArgument(longitudes);
663 message.getArgument(altitudes);
664 message.getArgument(pitches);
665 message.getArgument(rolls);
666 message.getArgument(headings);
667 message.getArgument(onGrounds);
668 queueDBusCall([=]() {
669 setPlanesPositions(callsigns, latitudes, longitudes, altitudes, pitches, rolls, headings,
673 else if (message.getMethodName() ==
"setPlanesSurfaces")
675 maybeSendEmptyDBusReply(wantsReply, sender, serial);
676 std::vector<std::string> callsigns;
677 std::vector<double> gears;
678 std::vector<double> flaps;
679 std::vector<double> spoilers;
680 std::vector<double> speedBrakes;
681 std::vector<double> slats;
682 std::vector<double> wingSweeps;
683 std::vector<double> thrusts;
684 std::vector<double> elevators;
685 std::vector<double> rudders;
686 std::vector<double> ailerons;
687 std::vector<bool> landLights;
688 std::vector<bool> taxiLights;
689 std::vector<bool> beaconLights;
690 std::vector<bool> strobeLights;
691 std::vector<bool> navLights;
692 std::vector<int> lightPatterns;
693 message.beginArgumentRead();
694 message.getArgument(callsigns);
695 message.getArgument(gears);
696 message.getArgument(flaps);
697 message.getArgument(spoilers);
698 message.getArgument(speedBrakes);
699 message.getArgument(slats);
700 message.getArgument(wingSweeps);
701 message.getArgument(thrusts);
702 message.getArgument(elevators);
703 message.getArgument(rudders);
704 message.getArgument(ailerons);
705 message.getArgument(landLights);
706 message.getArgument(taxiLights);
707 message.getArgument(beaconLights);
708 message.getArgument(strobeLights);
709 message.getArgument(navLights);
710 message.getArgument(lightPatterns);
711 queueDBusCall([=]() {
712 setPlanesSurfaces(callsigns, gears, flaps, spoilers, speedBrakes, slats, wingSweeps, thrusts,
713 elevators, rudders, ailerons, landLights, taxiLights, beaconLights, strobeLights,
714 navLights, lightPatterns);
717 else if (message.getMethodName() ==
"setPlanesTransponders")
719 maybeSendEmptyDBusReply(wantsReply, sender, serial);
720 std::vector<std::string> callsigns;
721 std::vector<int> codes;
722 std::vector<bool> modeCs;
723 std::vector<bool> idents;
724 message.beginArgumentRead();
725 message.getArgument(callsigns);
726 message.getArgument(codes);
727 message.getArgument(modeCs);
728 message.getArgument(idents);
729 queueDBusCall([=]() { setPlanesTransponders(callsigns, codes, modeCs, idents); });
731 else if (message.getMethodName() ==
"getRemoteAircraftData")
733 std::vector<std::string> requestedCallsigns;
734 message.beginArgumentRead();
735 message.getArgument(requestedCallsigns);
736 queueDBusCall([=]() {
737 std::vector<std::string> callsigns = requestedCallsigns;
738 std::vector<double> latitudesDeg;
739 std::vector<double> longitudesDeg;
740 std::vector<double> elevationsM;
741 std::vector<bool> waterFlags;
742 std::vector<double> verticalOffsets;
743 getRemoteAircraftData(callsigns, latitudesDeg, longitudesDeg, elevationsM, waterFlags,
745 CDBusMessage reply = CDBusMessage::createReply(sender, serial);
746 reply.beginArgumentWrite();
747 reply.appendArgument(callsigns);
748 reply.appendArgument(latitudesDeg);
749 reply.appendArgument(longitudesDeg);
750 reply.appendArgument(elevationsM);
751 reply.appendArgument(waterFlags);
752 reply.appendArgument(verticalOffsets);
753 sendDBusMessage(reply);
756 else if (message.getMethodName() ==
"getElevationAtPosition")
758 std::string callsign;
761 double altitudeMeters;
762 message.beginArgumentRead();
763 message.getArgument(callsign);
764 message.getArgument(latitudeDeg);
765 message.getArgument(longitudeDeg);
766 message.getArgument(altitudeMeters);
767 queueDBusCall([=]() {
768 bool isWater =
false;
769 const auto elevation =
770 getElevationAtPosition(callsign, latitudeDeg, longitudeDeg, altitudeMeters, isWater);
771 CDBusMessage reply = CDBusMessage::createReply(sender, serial);
772 reply.beginArgumentWrite();
773 reply.appendArgument(callsign);
774 reply.appendArgument(elevation[0]);
775 reply.appendArgument(elevation[1]);
776 reply.appendArgument(elevation[2]);
777 reply.appendArgument(isWater);
778 sendDBusMessage(reply);
781 else if (message.getMethodName() ==
"setFollowedAircraft")
783 maybeSendEmptyDBusReply(wantsReply, sender, serial);
784 std::string callsign;
785 message.beginArgumentRead();
786 message.getArgument(callsign);
787 queueDBusCall([=]() { setFollowedAircraft(callsign); });
792 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
795 return DBUS_HANDLER_RESULT_HANDLED;
798 int CTraffic::process()
800 invokeQueuedDBusCalls();
802 setDrawingLabels(getSettings().isDrawingLabels(), getSettings().getLabelColor());
809 template <
typename T>
810 int memcmpPayload(T *dst, T *src)
812 return std::memcmp(
reinterpret_cast<char *
>(dst) +
sizeof(dst->size),
813 reinterpret_cast<char *
>(src) +
sizeof(src->size),
sizeof(*dst) -
sizeof(dst->size));
816 void CTraffic::doPlaneUpdates()
819 for (
const auto &pair : m_planesById)
821 Plane *plane = pair.second;
822 interpolatePosition(plane);
823 interpolateGear(plane);
824 m_updates.push_back({ plane->id, &plane->positions[3], &plane->surfaces, &plane->surveillance });
826 XPMPUpdatePlanes(m_updates.data(),
sizeof(XPMPUpdate_t), m_updates.size());
828 XPMP_PrepListHook(0, 0, 0,
nullptr);
831 void CTraffic::interpolatePosition(Plane *plane)
833 std::memcpy(&plane->positions[3], &plane->positions[2],
sizeof(plane->positions[2]));
835 const auto now = std::chrono::steady_clock::now();
836 const auto t1 = plane->positionTimes[2] - plane->positionTimes[0];
837 const auto t2 = now - plane->positionTimes[0];
841 if (t1 > 3s || t2 > 3s) {
return; }
843 const double dLat = plane->positions[2].lat - plane->positions[0].lat;
844 const double dLon = plane->positions[2].lon - plane->positions[0].lon;
845 const double dAlt = plane->positions[2].elevation - plane->positions[0].elevation;
847 plane->positions[3].lat = plane->positions[0].lat + dLat * t2 / t1;
848 plane->positions[3].lon = plane->positions[0].lon + dLon * t2 / t1;
849 plane->positions[3].elevation = plane->positions[0].elevation + dAlt * t2 / t1;
852 void CTraffic::interpolateGear(Plane *plane)
854 const auto now = std::chrono::steady_clock::now();
855 static const float epsilon = std::numeric_limits<float>::epsilon();
856 const float f = plane->surfaces.gearPosition - plane->targetGearPosition;
857 if (std::abs(f) > epsilon)
859 constexpr
float gearMoveTimeMs = 5000;
860 const auto gearPositionDiffRemaining = plane->targetGearPosition - plane->surfaces.gearPosition;
863 std::chrono::duration_cast<std::chrono::milliseconds>(now - plane->prevSurfacesLerpTime);
864 const auto gearPositionDiffThisFrame = (diffMs.count()) / gearMoveTimeMs;
865 plane->surfaces.gearPosition += std::copysign(gearPositionDiffThisFrame, gearPositionDiffRemaining);
866 plane->surfaces.gearPosition = std::max(0.0f, std::min(plane->surfaces.gearPosition, 1.0f));
868 plane->prevSurfacesLerpTime = now;
871 void CTraffic::Labels::draw()
873 static const double maxRangeM = 10000;
874 static const double metersPerFt = 0.3048;
875 std::array<float, 16> worldMat = m_worldMat.getAll();
876 std::array<float, 16> projMat = m_projMat.getAll();
877 double windowWidth =
static_cast<double>(m_windowWidth.get());
878 double windowHeight =
static_cast<double>(m_windowHeight.get());
879 XPLMCameraPosition_t camPos {};
880 XPLMReadCameraPosition(&camPos);
882 for (
const auto &pair : m_traffic->m_planesById)
884 char *text =
const_cast<char *
>(pair.second->label);
885 const XPMPPlanePosition_t &planePos = pair.second->positions[3];
887 double worldPos[4] { 0, 0, 0, 1 };
888 double localPos[4] {};
889 double windowPos[4] {};
890 XPLMWorldToLocal(planePos.lat, planePos.lon, planePos.elevation * metersPerFt, &worldPos[0], &worldPos[1],
892 if (distanceSquared(worldPos) > maxRangeM * maxRangeM) {
continue; }
893 matrixMultVec(localPos, worldMat.data(), worldPos);
894 matrixMultVec(windowPos, projMat.data(), localPos);
896 windowPos[3] = 1.0 / windowPos[3];
897 windowPos[0] *= windowPos[3];
898 windowPos[1] *= windowPos[3];
899 windowPos[2] *= windowPos[3];
901 if (windowPos[2] < 0.0 || windowPos[2] > 1.0)
905 XPLMDrawString(m_color.data(),
static_cast<int>(std::lround(windowWidth * (windowPos[0] * 0.5 + 0.5))),
906 static_cast<int>(std::lround(windowHeight * (windowPos[1] * 0.5 + 0.5))), text,
nullptr,
911 void CTraffic::Labels::matrixMultVec(
double out[4],
const float m[16],
const double v[4])
913 out[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
914 out[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
915 out[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
916 out[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
919 double CTraffic::Labels::distanceSquared(
const double pos[3])
const
921 const double dx = m_viewX.get() - pos[0];
922 const double dy = m_viewY.get() - pos[1];
923 const double dz = m_viewZ.get() - pos[2];
924 return dx * dx + dy * dy + dz * dz;
927 int CTraffic::orbitPlaneFunc(XPLMCameraPosition_t *cameraPosition,
int isLosingControl,
void *refcon)
929 constexpr
bool DEBUG =
false;
931 if (isLosingControl == 1)
938 auto *traffic =
static_cast<CTraffic *
>(refcon);
941 ERROR_LOG(
"Cannot convert CTraffic object");
949 traffic->m_followPlaneViewCallsign.clear();
955 if (!traffic->m_deltaCameraPosition.isInitialized || traffic->m_isSpacePressed)
957 int x = 0, y = 0, left = 0, top = 0, right = 0, bottom = 0;
961 XPLMGetScreenBoundsGlobal(&left, &top, &right, &bottom);
962 XPLMGetMouseLocationGlobal(&x, &y);
963 int w = right - left;
964 int h = top - bottom;
969 DEBUG_LOG(
"Follow aircraft coordinates w,h,x,y: " + std::to_string(w) +
" " + std::to_string(h) +
" " +
970 std::to_string(x) +
" " + std::to_string(y));
972 if (traffic->m_lastMouseX == x && traffic->m_lastMouseY == y && traffic->m_lastMouseX >= 0 &&
973 traffic->m_lastMouseY >= 0)
978 traffic->m_isSpacePressed =
false;
983 if (w < 100 || h < 100)
985 WARNING_LOG(
"Screen w/h too small " + std::to_string(w) +
"/" + std::to_string(h));
990 traffic->m_deltaCameraPosition.headingDeg =
991 normalizeToZero360Deg(1.25 * 360.0 *
static_cast<double>(x) /
static_cast<double>(w));
992 double usedCameraPitchDeg =
993 60.0 - (60.0 * 2.0 *
static_cast<double>(y) /
static_cast<double>(h));
997 if (usedCameraPitchDeg >= 85.0) { usedCameraPitchDeg = 85.0; }
998 else if (usedCameraPitchDeg <= -85.0) { usedCameraPitchDeg = -85.0; }
999 traffic->m_deltaCameraPosition.pitchDeg = usedCameraPitchDeg;
1004 const double distanceMeterM =
1005 traffic->m_followAircraftDistanceMultiplier *
1006 static_cast<double>(std::max(10, traffic->getSettings().getFollowAircraftDistanceM()));
1007 static const double PI = std::acos(-1);
1008 traffic->m_deltaCameraPosition.dxMeters =
1009 -distanceMeterM * sin(traffic->m_deltaCameraPosition.headingDeg * PI / 180.0);
1010 traffic->m_deltaCameraPosition.dzMeters =
1011 distanceMeterM * cos(traffic->m_deltaCameraPosition.headingDeg * PI / 180.0);
1012 traffic->m_deltaCameraPosition.dyMeters =
1013 -distanceMeterM * tan(traffic->m_deltaCameraPosition.pitchDeg * PI / 180.0);
1015 traffic->m_deltaCameraPosition.isInitialized =
true;
1018 double lxMeters = 0, lyMeters = 0, lzMeters = 0;
1019 static const double kFtToMeters = 0.3048;
1021 std::string modelName =
"unknown";
1022 if (traffic->m_followPlaneViewCallsign == CTraffic::ownAircraftString())
1024 lxMeters = traffic->m_ownAircraftPositionX.get();
1025 lyMeters = traffic->m_ownAircraftPositionY.get();
1026 lzMeters = traffic->m_ownAircraftPositionZ.get();
1027 modelName = CTraffic::ownAircraftString();
1031 if (traffic->m_planesByCallsign.empty())
1033 INFO_LOG(
"Follow aircraft, no planes to follow");
1034 traffic->m_followPlaneViewCallsign.clear();
1038 if (traffic->m_followPlaneViewCallsign.empty())
1040 INFO_LOG(
"Follow aircraft, no callsign to follow");
1041 traffic->m_followPlaneViewCallsign.clear();
1042 traffic->m_followAircraftDistanceMultiplier = 1.0;
1046 const auto planeIt = traffic->m_planesByCallsign.find(traffic->m_followPlaneViewCallsign);
1047 if (planeIt == traffic->m_planesByCallsign.end())
1049 INFO_LOG(
"Follow aircraft, no plane found for callsign " + traffic->m_followPlaneViewCallsign);
1050 traffic->m_followPlaneViewCallsign.clear();
1051 traffic->m_followAircraftDistanceMultiplier = 1.0;
1055 const Plane *plane = planeIt->second;
1058 ERROR_LOG(
"Follow aircraft, no plane from iterator for callsign " + traffic->m_followPlaneViewCallsign);
1059 traffic->m_followPlaneViewCallsign.clear();
1060 traffic->m_followAircraftDistanceMultiplier = 1.0;
1064 modelName = plane->modelName;
1065 if (!isValidPosition(plane->positions[3]))
1067 WARNING_LOG(
"Invalid follow aircraft position for " + plane->callsign);
1068 WARNING_LOG(
"Pos: " + pos2String(plane->positions[3]));
1073 if (plane->isOnGround)
1075 if (traffic->m_deltaCameraPosition.dyMeters < 10) { traffic->m_deltaCameraPosition.dyMeters = 10; }
1078 XPLMWorldToLocal(plane->positions[3].lat, plane->positions[3].lon,
1079 plane->positions[3].elevation * kFtToMeters, &lxMeters, &lyMeters, &lzMeters);
1083 cameraPosition->x =
static_cast<float>(lxMeters + traffic->m_deltaCameraPosition.dxMeters);
1084 cameraPosition->y =
static_cast<float>(lyMeters + traffic->m_deltaCameraPosition.dyMeters);
1085 cameraPosition->z =
static_cast<float>(lzMeters + traffic->m_deltaCameraPosition.dzMeters);
1086 cameraPosition->pitch =
1087 CTraffic::normalizeToPlusMinus180Deg(
static_cast<float>(traffic->m_deltaCameraPosition.pitchDeg));
1088 cameraPosition->heading =
1089 CTraffic::normalizeToPlusMinus180Deg(
static_cast<float>(traffic->m_deltaCameraPosition.headingDeg));
1090 cameraPosition->roll = 0.0;
1091 cameraPosition->zoom = 1.0;
1093 if (!isValidPosition(cameraPosition))
1096 WARNING_LOG(
"Pos: " + pos2String(cameraPosition));
1097 traffic->m_followAircraftDistanceMultiplier = 1.0;
1103 DEBUG_LOG(
"Camera: " + pos2String(cameraPosition));
1104 DEBUG_LOG(
"Follow aircraft " + traffic->m_followPlaneViewCallsign +
" " + modelName);
1110 int CTraffic::followAircraftKeySniffer(
char character, XPLMKeyFlags flags,
char virtualKey,
void *refcon)
1113 CTraffic *traffic =
static_cast<CTraffic *
>(refcon);
1114 if (!traffic || traffic->m_followPlaneViewCallsign.empty())
1120 if (virtualKey == XPLM_VK_SPACE)
1124 if (flags & xplm_DownFlag) { traffic->m_isSpacePressed =
true; }
1125 if (flags & xplm_UpFlag) { traffic->m_isSpacePressed =
false; }
1127 else if (virtualKey == XPLM_VK_DOWN && (flags & xplm_UpFlag))
1129 traffic->m_followAircraftDistanceMultiplier /= 1.2;
1131 else if (virtualKey == XPLM_VK_UP && (flags & xplm_UpFlag))
1133 traffic->m_followAircraftDistanceMultiplier *= 1.2;
1135 else if (virtualKey == XPLM_VK_ESCAPE && (flags & xplm_UpFlag))
1137 traffic->m_followAircraftDistanceMultiplier = 1.0;
1145 bool CTraffic::isPlusMinus180(
float v)
1147 if (std::isnan(v)) {
return false; }
1148 if (v > 180.00001f || v < -180.00001f) {
return false; }
1152 bool CTraffic::isPlusMinus180(
double v)
1154 if (std::isnan(v)) {
return false; }
1155 if (v > 180.00001 || v < -180.00001) {
return false; }
1159 float CTraffic::normalizeToPlusMinus180Deg(
float v)
1161 if (std::isnan(v)) {
return 0.0f; }
1162 return static_cast<float>(normalizeToPlusMinus180Deg(
static_cast<double>(v)));
1165 double CTraffic::normalizeToPlusMinus180Deg(
double v)
1167 if (std::isnan(v)) {
return 0.0; }
1169 if (n <= -180.0) {
return 180.0; }
1170 if (n > 180.0) {
return 180.0; }
1174 float CTraffic::normalizeToZero360Deg(
float v)
1176 if (std::isnan(v)) {
return 0.0f; }
1177 return static_cast<float>(normalizeToZero360Deg(
static_cast<double>(v)));
1180 double CTraffic::normalizeToZero360Deg(
double v)
1182 if (std::isnan(v)) {
return 0.0; }
1184 if (n >= 360.0) {
return 0.0; }
1185 if (n < 0.0) {
return 0.0; }
1189 bool CTraffic::isValidPosition(
const XPMPPlanePosition_t &position)
1191 if (!isPlusMinus180(position.lat)) {
return false; }
1192 if (!isPlusMinus180(position.lon)) {
return false; }
1193 if (!isPlusMinus180(position.pitch)) {
return false; }
1194 if (!isPlusMinus180(position.roll)) {
return false; }
1195 if (!isPlusMinus180(position.heading)) {
return false; }
1196 if (position.elevation < -2000.0 || position.elevation > 100000.0) {
return false; }
1201 bool CTraffic::isValidPosition(
const XPLMCameraPosition_t *camPos)
1203 if (!isPlusMinus180(camPos->roll)) {
return false; }
1204 if (!isPlusMinus180(camPos->pitch)) {
return false; }
1205 if (!isPlusMinus180(camPos->heading)) {
return false; }
1208 if (std::isnan(camPos->x)) {
return false; }
1209 if (std::isnan(camPos->y)) {
return false; }
1210 if (std::isnan(camPos->z)) {
return false; }
1215 std::string CTraffic::pos2String(
const XPMPPlanePosition_t &position)
1217 return "lat, lon, el: " + std::to_string(position.lat) +
"/" + std::to_string(position.lon) +
"/" +
1218 std::to_string(position.elevation) +
" prh: " + std::to_string(position.pitch) +
"/" +
1219 std::to_string(position.roll) +
"/" + std::to_string(position.heading);
1222 std::string CTraffic::pos2String(
const XPLMCameraPosition_t *camPos)
1224 if (!camPos) {
return "camera position invalid"; }
1225 return "x, y, z: " + std::to_string(camPos->x) +
"/" + std::to_string(camPos->y) +
"/" +
1226 std::to_string(camPos->z) +
" zoom: " + std::to_string(camPos->zoom) +
1227 " prh: " + std::to_string(camPos->pitch) +
"/" + std::to_string(camPos->roll) +
"/" +
1228 std::to_string(camPos->heading);
Plugin loaded by X-Plane which publishes a DBus service.
std::string g_xplanePath
Absolute xplane path.
std::string g_sep
Platform specific dir separator.
void initXPlanePath()
Init global xplane path.
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
double normalizeValue(const double value, const double start, const double end)
Normalize value to range start -> end (like for +-180degrees)
#define DEBUG_LOG(msg)
Logger convenience macros.
#define ERROR_LOG(msg)
Logger convenience macros.
#define INFO_LOG(msg)
Logger convenience macros.
#define WARNING_LOG(msg)
Logger convenience macros.