diff --git a/include/aman/types/Inbound.h b/include/aman/types/Inbound.h index f49a4f4..2f7ba5c 100644 --- a/include/aman/types/Inbound.h +++ b/include/aman/types/Inbound.h @@ -33,34 +33,30 @@ namespace aman { bool m_fixedPlan; std::string m_star; std::string m_runway; + Velocity m_groundSpeed; std::size_t m_nextStarWaypoint; std::vector m_arrivalRoute; Time m_timeToLose; UtcTime::Point m_waypointEstimatedTimeOfArrival; - Length m_trackmiles; - Time m_flighttime; - std::string m_predictedWaypoint; void updatePrediction(EuroScopePlugIn::CRadarTarget& target, const aman::AircraftSchedule& inbound); Velocity indicatedAirspeed(const Length& altitude) const noexcept; Velocity groundSpeed(const Length& altitude, const Velocity& ias, const Angle& heading); void createWindTables(const google::protobuf::RepeatedPtrField& wind); void predictETA(const EuroScopePlugIn::CRadarTarget& target, const EuroScopePlugIn::CFlightPlanPositionPredictions& predictions); - std::string findNextWaypointOnHeading(EuroScopePlugIn::CFlightPlan& plan); public: Inbound(EuroScopePlugIn::CRadarTarget& target, const aman::AircraftSchedule& inbound, const google::protobuf::RepeatedPtrField& wind); void update(EuroScopePlugIn::CRadarTarget& target, const aman::AircraftSchedule& inbound, const google::protobuf::RepeatedPtrField& wind); void update(EuroScopePlugIn::CRadarTarget& target); - void update(EuroScopePlugIn::CFlightPlan& plan); + void directTo(EuroScopePlugIn::CRadarTarget& radarTarget, const std::string& waypoint); bool fixedPlan() const noexcept; UtcTime::Point eta() const; UtcTime::Point pta() const; const Time& timeToLose() const noexcept; - const Length& trackmiles() const noexcept; - const Time& flighttime() const noexcept; - const std::string& predictedWaypoint() const noexcept; + const std::string& nextWaypoint() const noexcept; + const std::vector& arrivalRoute() const noexcept; static int matchToPredictedPath(const EuroScopePlugIn::CFlightPlanPositionPredictions& predictions, const GeoCoordinate& position, const Velocity& groundspeed, Length& trackmiles); diff --git a/src/PlugIn.cpp b/src/PlugIn.cpp index 815da80..ab056b3 100644 --- a/src/PlugIn.cpp +++ b/src/PlugIn.cpp @@ -71,8 +71,8 @@ PlugIn::PlugIn() : this->RegisterTagItemType("PTA", static_cast(PlugIn::TagItemElement::PlannedTimeOfArrival)); this->RegisterTagItemType("Delta time", static_cast(PlugIn::TagItemElement::DeltaTime)); this->RegisterTagItemType("Fixed plan indicator", static_cast(PlugIn::TagItemElement::FixedPlanIndicator)); - this->RegisterTagItemFunction("Force planning", static_cast(PlugIn::TagItemFunction::ForceToBackendMenu)); this->RegisterTagItemFunction("Runway selection", static_cast(PlugIn::TagItemFunction::RunwaySelectMenu)); + this->RegisterTagItemFunction("Direct to", static_cast(PlugIn::TagItemFunction::DirectToMenu)); this->DisplayUserMessage(PLUGIN_NAME, "INFO", (std::string("Loaded ") + PLUGIN_NAME + " " + PLUGIN_VERSION).c_str(), true, true, false, false, false); @@ -498,6 +498,32 @@ bool PlugIn::OnCompileCommand(const char* cmdline) { this->m_sweatboxValid = false; retval = true; } + else if (std::string::npos != message.find("FORCE")) { + const auto elements = String::splitString(message, " "); + + if (3 <= elements.size()) { + const auto callsign = elements[2]; + + const auto radarTarget = this->RadarTargetSelect(callsign.c_str()); + + if (true == radarTarget.IsValid()) { + std::string destination(radarTarget.GetCorrelatedFlightPlan().GetFlightPlanData().GetDestination()); + std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper); + std::lock_guard updateGuard(this->m_updateQueueLock); + + auto it = this->m_updateQueue.find(destination); + + if (this->m_updateQueue.end() != it) { + auto cIt = std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign); + + if (this->m_forcedToBackendCallsigns.cend() == cIt) + this->m_forcedToBackendCallsigns.push_back(callsign); + else + this->m_forcedToBackendCallsigns.erase(cIt); + } + } + } + } if (true == retval) { if (true == this->m_sweatboxValid) @@ -527,7 +553,7 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper); #pragma warning(default: 4244) - bool planExpected = false, forced = false; + bool forced = false; const gsl::span tagString(itemString, 16UL); this->m_updateQueueLock.lock(); @@ -538,9 +564,6 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug forced = this->m_forcedToBackendCallsigns.cend() != cIt; } - /* check if the inbound is expected to be planned */ - planExpected = this->m_updateQueue.cend() != this->m_updateQueue.find(destination); - this->m_updateQueueLock.unlock(); std::lock_guard guard(this->m_inboundsQueueLock); @@ -550,10 +573,16 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug switch (static_cast(itemCode)) { case TagItemElement::EstimatedTimeOfArrival: { - if (this->m_inbounds.cend() != it) - message = UtcTime::timeToString(it->second.eta(), "%H:%M"); - else if (true == forced || true == planExpected) + if (this->m_inbounds.cend() != it) { + const auto eta = it->second.eta(); + if (UtcTime::Point() == eta) + message = "??:??"; + else + message = UtcTime::timeToString(it->second.eta(), "%H:%M"); + } + else if (true == forced) { message = "??:??"; + } break; } case TagItemElement::PlannedTimeOfArrival: @@ -563,9 +592,9 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug if (UtcTime::Point() == pta) message = "??:??"; else - message = UtcTime::timeToString(it->second.pta(), "%H:%M"); + message = UtcTime::timeToString(pta, "%H:%M"); } - else if (true == forced || true == planExpected) { + else if (true == forced) { message = "??:??"; } break; @@ -578,7 +607,7 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug stream << ttl; message = stream.str(); } - else if (true == forced || true == planExpected) { + else if (true == forced) { message = "??"; } break; @@ -586,7 +615,7 @@ void PlugIn::OnGetTagItem(EuroScopePlugIn::CFlightPlan flightPlan, EuroScopePlug case TagItemElement::FixedPlanIndicator: if (this->m_inbounds.cend() != it && false == it->second.fixedPlan()) message = "*"; - else if (this->m_inbounds.cend() == it && (true == forced || true == planExpected)) + else if (this->m_inbounds.cend() == it && true == forced) message = "*"; break; default: @@ -603,54 +632,20 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE std::ignore = itemString; std::ignore = pt; - const auto radarTarget = this->RadarTargetSelectASEL(); + auto radarTarget = this->RadarTargetSelectASEL(); if (false == radarTarget.IsValid() || false == radarTarget.GetCorrelatedFlightPlan().IsValid()) return; std::string callsign(radarTarget.GetCallsign()); + std::lock_guard guardUpdate(this->m_updateQueueLock); + std::lock_guard guardInbound(this->m_inboundsQueueLock); + switch (static_cast(functionId)) { - case PlugIn::TagItemFunction::ForceToBackendMenu: - { - this->OpenPopupList(area, "AMAN", 1); - bool inList = this->m_forcedToBackendCallsigns.cend() != std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign); - this->AddPopupListElement("Add to AMAN", "", static_cast(PlugIn::TagItemFunction::ForceToBackendSelect), - false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, true == inList); - this->AddPopupListElement("Remove from AMAN", "", static_cast(PlugIn::TagItemFunction::ForceToBackendSelect), - false, EuroScopePlugIn::POPUP_ELEMENT_NO_CHECKBOX, false == inList); - break; - } - case PlugIn::TagItemFunction::ForceToBackendSelect: - { - std::string destination(radarTarget.GetCorrelatedFlightPlan().GetFlightPlanData().GetDestination()); - std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper); - - std::lock_guard guard(this->m_updateQueueLock); - auto it = this->m_updateQueue.find(destination); - - if (0 == std::strcmp(itemString, "Add to AMAN")) { - if (this->m_updateQueue.end() != it) { - auto cIt = std::find(this->m_forcedToBackendCallsigns.cbegin(), this->m_forcedToBackendCallsigns.cend(), callsign); - if (this->m_forcedToBackendCallsigns.cend() == cIt) - this->m_forcedToBackendCallsigns.push_back(callsign); - } - } - else { - if (this->m_updateQueue.end() != it) { - auto cIt = std::find(this->m_forcedToBackendCallsigns.begin(), this->m_forcedToBackendCallsigns.end(), callsign); - if (this->m_forcedToBackendCallsigns.cend() != cIt) - this->m_forcedToBackendCallsigns.erase(cIt); - } - } - - break; - } case PlugIn::TagItemFunction::RunwaySelectMenu: { std::string destination(radarTarget.GetCorrelatedFlightPlan().GetFlightPlanData().GetDestination()); std::transform(destination.begin(), destination.end(), destination.begin(), ::toupper); - - std::lock_guard guard(this->m_updateQueueLock); auto it = this->m_updateQueue.find(destination); if (this->m_updateQueue.cend() != it) { @@ -667,8 +662,6 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE } case PlugIn::TagItemFunction::RunwaySelect: { - std::lock_guard guard(this->m_updateQueueLock); - if (0 == std::strcmp(itemString, "Remove")) { auto it = this->m_selectedRunway.find(callsign); if (this->m_selectedRunway.end() != it) @@ -681,7 +674,38 @@ void PlugIn::OnFunctionCall(int functionId, const char* itemString, POINT pt, RE else this->m_selectedRunway.insert({ callsign, itemString }); } + break; + } + case PlugIn::TagItemFunction::DirectToMenu: + { + auto it = this->m_inbounds.find(callsign); + if (this->m_inbounds.cend() != it && 0 != it->second.arrivalRoute().size()) { + this->OpenPopupList(area, "Direct To", 2); + + for (const auto& waypoint : std::as_const(it->second.arrivalRoute())) { + std::string message; + + if (it->second.nextWaypoint() == waypoint.name()) + message = "-> "; + message += waypoint.name().c_str(); + + this->AddPopupListElement(message.c_str(), UtcTime::timeToString(waypoint.plannedArrivalTime(), "%H:%M").c_str(), + static_cast(PlugIn::TagItemFunction::DirectTo)); + } + } + + break; + } + case PlugIn::TagItemFunction::DirectTo: + { + std::string_view message(itemString); + if (0 != message.rfind("-> ", 0)) { + auto it = this->m_inbounds.find(callsign); + + if (this->m_inbounds.end() != it) + it->second.directTo(radarTarget, itemString); + } break; } default: @@ -722,13 +746,6 @@ void PlugIn::updateInbound(EuroScopePlugIn::CRadarTarget& radarTarget) { it->second.update(radarTarget); } -void PlugIn::updateInbound(EuroScopePlugIn::CFlightPlan& plan) { - std::lock_guard guard(this->m_inboundsQueueLock); - auto it = this->m_inbounds.find(plan.GetCorrelatedRadarTarget().GetCallsign()); - if (this->m_inbounds.end() != it) - it->second.update(plan); -} - void PlugIn::OnRadarTargetPositionUpdate(EuroScopePlugIn::CRadarTarget radarTarget) { /* do nothing if the reporter is not initialized and ignore invalid targets */ if (false == this->m_compatible || false == Backend::instance().initialized() || false == radarTarget.IsValid()) @@ -746,14 +763,18 @@ void PlugIn::OnRadarTargetPositionUpdate(EuroScopePlugIn::CRadarTarget radarTarg } void PlugIn::OnFlightPlanControllerAssignedDataUpdate(EuroScopePlugIn::CFlightPlan flightPlan, int type) { - /* handle only relevant changes */ - if (EuroScopePlugIn::CTR_DATA_TYPE_HEADING != type && EuroScopePlugIn::CTR_DATA_TYPE_DIRECT_TO != type) + auto radarTarget = flightPlan.GetCorrelatedRadarTarget(); + + if (EuroScopePlugIn::CTR_DATA_TYPE_DIRECT_TO != type || false == radarTarget.IsValid()) return; - if (false == flightPlan.GetCorrelatedRadarTarget().IsValid()) - return; - - this->updateInbound(flightPlan); + std::string waypoint(flightPlan.GetControllerAssignedData().GetDirectToPointName()); + if (0 != waypoint.length()) { + std::lock_guard guard(this->m_inboundsQueueLock); + auto it = this->m_inbounds.find(flightPlan.GetCorrelatedRadarTarget().GetCallsign()); + if (this->m_inbounds.end() != it) + it->second.directTo(radarTarget, waypoint); + } } void PlugIn::OnFlightPlanDisconnect(EuroScopePlugIn::CFlightPlan flightPlan) { diff --git a/src/PlugIn.h b/src/PlugIn.h index abe7df8..452ae9a 100644 --- a/src/PlugIn.h +++ b/src/PlugIn.h @@ -36,10 +36,10 @@ namespace aman { * @brief Defines the different internal and external tag functions */ enum class TagItemFunction { - ForceToBackendMenu = 0, /**< Opens the menu to force tracks */ - ForceToBackendSelect = 1, /**< Reacts on controller selection */ RunwaySelectMenu = 2, /**< Opens the runway selection menu */ - RunwaySelect = 3 /**< Selects the runway */ + RunwaySelect = 3, /**< Selects the runway */ + DirectToMenu = 4, /**< Opens the direct to menu */ + DirectTo = 5 /**< Selects the direct to */ }; private: @@ -60,7 +60,6 @@ namespace aman { void generateAircraftReportMessage(EuroScopePlugIn::CRadarTarget& radarTarget, aman::AircraftReport* report); void addUpdateQueue(EuroScopePlugIn::CRadarTarget& radarTarget); void updateInbound(EuroScopePlugIn::CRadarTarget& radarTarget); - void updateInbound(EuroScopePlugIn::CFlightPlan& plan); void updateSequence(std::shared_ptr& sequence); static void distanceToPredictedIaf(const EuroScopePlugIn::CRadarTarget& radarTarget, const EuroScopePlugIn::CFlightPlan& flightPlan, const EuroScopePlugIn::CPosition& iafPosition, aman::AircraftReport* report); diff --git a/src/types/Inbound.cpp b/src/types/Inbound.cpp index 2b59a33..076b12d 100644 --- a/src/types/Inbound.cpp +++ b/src/types/Inbound.cpp @@ -35,16 +35,11 @@ Inbound::Inbound(EuroScopePlugIn::CRadarTarget& target, const aman::AircraftSche m_fixedPlan(inbound.fixed()), m_star(inbound.arrivalroute()), m_runway(inbound.arrivalrunway()), - m_nextStarWaypoint(), + m_nextStarWaypoint(0), m_arrivalRoute(), - m_timeToLose(), - m_trackmiles(), - m_flighttime(), - m_predictedWaypoint() { + m_timeToLose() { this->createWindTables(wind); this->updatePrediction(target, inbound); - auto flightplan = target.GetCorrelatedFlightPlan(); - this->update(flightplan); } void Inbound::createWindTables(const google::protobuf::RepeatedPtrField& wind) { @@ -102,6 +97,11 @@ void Inbound::updatePrediction(EuroScopePlugIn::CRadarTarget& target, const aman updatedFlightplan = true; } + /* try to restore the direct to, if possible */ + std::string directTo; + if (this->m_nextStarWaypoint < this->m_arrivalRoute.size()) + directTo = gsl::at(this->m_arrivalRoute, this->m_nextStarWaypoint).name(); + this->m_arrivalRoute.clear(); this->m_arrivalRoute.reserve(inbound.waypoints_size()); auto route = target.GetCorrelatedFlightPlan().GetExtractedRoute(); @@ -125,8 +125,15 @@ void Inbound::updatePrediction(EuroScopePlugIn::CRadarTarget& target, const aman } } - if (true == found) + if (true == found) { this->m_arrivalRoute.push_back(ArrivalWaypoint(plannedPoint.name(), coordinate, altitude, ias, pta)); + + /* restore the direct to */ + if (0 != directTo.length() && plannedPoint.name() == directTo) { + this->m_nextStarWaypoint = this->m_arrivalRoute.size() - 1; + directTo.clear(); + } + } } } @@ -138,8 +145,7 @@ void Inbound::update(EuroScopePlugIn::CRadarTarget& target, const aman::Aircraft this->createWindTables(wind); this->updatePrediction(target, inbound); - auto flightplan = target.GetCorrelatedFlightPlan(); - this->update(flightplan); + this->update(target); } Velocity Inbound::indicatedAirspeed(const Length& altitude) const noexcept { @@ -281,42 +287,40 @@ int Inbound::matchToPredictedPath(const EuroScopePlugIn::CFlightPlanPositionPred void Inbound::predictETA(const EuroScopePlugIn::CRadarTarget& target, const EuroScopePlugIn::CFlightPlanPositionPredictions& predictions) { if (this->m_arrivalRoute.size() <= this->m_nextStarWaypoint || 1 >= predictions.GetPointsNumber()) { + this->m_waypointEstimatedTimeOfArrival = UtcTime::Point(); this->m_timeToLose = 0_s; return; } - /* get the ground speed */ - Velocity groundspeed; - if (0 == target.GetGS()) - groundspeed = static_cast(target.GetPosition().GetReportedGS()) * knot; - else - groundspeed = static_cast(target.GetGS()) * knot; - const auto& destination = gsl::at(this->m_arrivalRoute, this->m_nextStarWaypoint); Length distanceToNextWaypoint = 0_m; - Inbound::matchToPredictedPath(predictions, destination.position(), groundspeed, distanceToNextWaypoint); - this->m_trackmiles = distanceToNextWaypoint; + + auto idx = Inbound::matchToPredictedPath(predictions, destination.position(), this->m_groundSpeed, distanceToNextWaypoint); + if (idx >= predictions.GetPointsNumber()) { + this->m_waypointEstimatedTimeOfArrival = UtcTime::Point(); + this->m_timeToLose = 0_s; + } /* predict the flight and the descend */ - Velocity groundSpeed = static_cast(target.GetPosition().GetReportedGS()) * knot; + Velocity currentGroundSpeed = static_cast(target.GetPosition().GetReportedGS()) * knot; Length altitude = static_cast(target.GetPosition().GetFlightLevel()) * feet; Angle heading = __convert(predictions.GetPosition(0)).bearingTo(destination.position()); Time flightTime = 0_s; while (0.0_m < distanceToNextWaypoint) { - Length distance = groundSpeed * 1_s; + Length distance = this->m_groundSpeed * 1_s; if (altitude > destination.altitude()) { /* new descend required based on 3° glide */ if (((altitude - destination.altitude()).convert(feet) / 1000.0f * 3.0f) > distanceToNextWaypoint.convert(nauticmile)) { - const auto oldGS = groundSpeed; + const auto oldGS = currentGroundSpeed; const auto descendRate = oldGS * 1_s * std::sinf(0.0523599f); altitude -= descendRate; const auto newGS = this->groundSpeed(altitude, this->indicatedAirspeed(altitude), heading); - groundSpeed = std::min(groundSpeed, newGS); + currentGroundSpeed = std::min(currentGroundSpeed, newGS); - distance = (groundSpeed + oldGS) * 0.5f * 1_s; + distance = (currentGroundSpeed + oldGS) * 0.5f * 1_s; } } @@ -327,181 +331,40 @@ void Inbound::predictETA(const EuroScopePlugIn::CRadarTarget& target, const Euro this->m_waypointEstimatedTimeOfArrival = UtcTime::currentUtc() + std::chrono::seconds(static_cast(flightTime.convert(second))); const auto delta = std::chrono::duration_cast(destination.plannedArrivalTime() - UtcTime::currentUtc()); const auto plannedFlightTime = static_cast(delta.count()) * second; - const auto newTTL = plannedFlightTime - flightTime; - this->m_flighttime = flightTime; - - this->m_timeToLose = newTTL; + this->m_timeToLose = plannedFlightTime - flightTime; } void Inbound::update(EuroScopePlugIn::CRadarTarget& target) { + /* get the ground speed */ + if (0 == target.GetGS()) + this->m_groundSpeed = static_cast(target.GetPosition().GetReportedGS()) * knot; + else + this->m_groundSpeed = static_cast(target.GetGS()) * knot; + + /* check distance to current waypoint and use next one, if needed */ + if (this->m_nextStarWaypoint < this->m_arrivalRoute.size()) { + const auto maxDistance = this->m_groundSpeed * 60_s; + + if (maxDistance > gsl::at(this->m_arrivalRoute, this->m_nextStarWaypoint).position().distanceTo(__convert(target.GetPosition().GetPosition()))) + this->m_nextStarWaypoint += 1; + } + this->predictETA(target, target.GetCorrelatedFlightPlan().GetPositionPredictions()); } -std::string Inbound::findNextWaypointOnHeading(EuroScopePlugIn::CFlightPlan& plan) { - auto target = plan.GetCorrelatedRadarTarget(); - const auto course(static_cast(target.GetTrackHeading()) * degree); +void Inbound::directTo(EuroScopePlugIn::CRadarTarget& radarTarget, const std::string& waypoint) { + std::size_t i = 0; - const GeoCoordinate currentPosition(static_cast(target.GetPosition().GetPosition().m_Longitude) * degree, - static_cast(target.GetPosition().GetPosition().m_Latitude) * degree); - const auto projectedPosition = currentPosition.projection(course, 300_nm); - - /* calculate the average to get a good cartesian projection point */ - Angle avgLongitude = currentPosition.longitude(), avgLatitude = currentPosition.latitude(); - avgLongitude += projectedPosition.longitude(); - avgLatitude += projectedPosition.latitude(); - - for (const auto& waypoint : std::as_const(this->m_arrivalRoute)) { - avgLongitude += waypoint.position().longitude(); - avgLatitude += waypoint.position().latitude(); - } - - avgLongitude = avgLongitude / static_cast(this->m_arrivalRoute.size() + 2); - avgLatitude = avgLatitude / static_cast(this->m_arrivalRoute.size() + 2); - - GeographicLib::Gnomonic projection(GeographicLib::Geodesic::WGS84()); - Eigen::Vector2f start, end; - - /* create the flight line to find the intersections */ - projection.Forward(avgLatitude.convert(degree), avgLongitude.convert(degree), - currentPosition.latitude().convert(degree), currentPosition.longitude().convert(degree), - start[0], start[1]); - projection.Forward(avgLatitude.convert(degree), avgLongitude.convert(degree), - projectedPosition.latitude().convert(degree), projectedPosition.longitude().convert(degree), - end[0], end[1]); - - Angle offset = 380.0_deg; - std::size_t waypointIdx = this->m_arrivalRoute.size(); - const auto flightLine = Eigen::Hyperplane::Through(start, end); - for (std::size_t i = 0; i < this->m_arrivalRoute.size() - 1; ++i) { - const auto& waypointStart = gsl::at(this->m_arrivalRoute, i); - const auto& waypointEnd = gsl::at(this->m_arrivalRoute, i + 1); - - projection.Forward(avgLatitude.convert(degree), avgLongitude.convert(degree), - waypointStart.position().latitude().convert(degree), waypointStart.position().longitude().convert(degree), - start[0], start[1]); - projection.Forward(avgLatitude.convert(degree), avgLongitude.convert(degree), - waypointEnd.position().latitude().convert(degree), waypointEnd.position().longitude().convert(degree), - end[0], end[1]); - - /* get the intersection point */ - const auto segment = Eigen::Hyperplane::Through(start, end); - const auto intersection = flightLine.intersection(segment); - - /* - * - check if the intersection is inside the segment - * idea: - * - the controller can use wrong headings due to weather and maybe misinterpretations - * - allow an error of 20% for the line to find the best waypoint - */ - const auto segmentLength = (start - end).norm() * 1.2f; - const auto intersectionStartLength = (start - intersection).norm(); - const auto intersectionEndLength = (end - intersection).norm(); - - /* outside the segment */ - if (intersectionStartLength > segmentLength || intersectionEndLength > segmentLength) - continue; - - /* check if the offset is better than the last one */ - const auto deltaAngle = __normalize(waypointStart.position().bearingTo(waypointEnd.position()) - course).abs(); - if (deltaAngle < offset) { - offset = deltaAngle; - - /* find the closer waypoint */ - if (intersectionStartLength < intersectionEndLength) - waypointIdx = i; - else - waypointIdx = i + 1; + for (const auto& arrivalPoint : std::as_const(this->m_arrivalRoute)) { + if (arrivalPoint.name() == waypoint) { + this->m_nextStarWaypoint = i; + return; } + i += 1; } - /* found a good waypoint */ - if (this->m_arrivalRoute.size() > waypointIdx) - return gsl::at(this->m_arrivalRoute, waypointIdx).name(); - else - return gsl::at(this->m_arrivalRoute, 0).name(); -} - -void Inbound::update(EuroScopePlugIn::CFlightPlan& plan) { this->m_nextStarWaypoint = 0; - - if (0 == this->m_arrivalRoute.size()) { - this->m_timeToLose = 0_s; - return; - } - - /* find the point on the route */ - std::string direct(plan.GetControllerAssignedData().GetDirectToPointName()); - if (0 == direct.length()) - direct = this->findNextWaypointOnHeading(plan); - this->m_predictedWaypoint = direct; - - auto route = plan.GetExtractedRoute(); - int starEntry = route.GetPointsNumber(), directEntry = route.GetPointsNumber(); - - /* find the star entry on the route */ - for (int c = 0; c < route.GetPointsNumber(); ++c) { - std::string_view waypointName(route.GetPointName(c)); - if (waypointName == this->m_arrivalRoute.front().name()) { - starEntry = c; - break; - } - } - - if (0 != direct.length()) { - /* find the DIRECT waypoint */ - for (int c = 0; c < route.GetPointsNumber(); ++c) { - std::string_view waypointName(route.GetPointName(c)); - if (waypointName == direct) { - directEntry = c; - break; - } - } - } - else { - const auto& acPosition = __convert(plan.GetCorrelatedRadarTarget().GetPosition().GetPosition()); - Length minDistance(10000_nm); - - /* search the next waypoint on the route */ - for (int c = route.GetPointsNumber() - 1; c >= 0; --c) { - const auto& position = __convert(route.GetPointPosition(c)); - const auto distance = position.distanceTo(acPosition); - - if (distance < minDistance) { - direct = route.GetPointName(c); - minDistance = distance; - directEntry = c; - } - - if (c < starEntry) - break; - } - } - - /* search the direct to entry */ - if (directEntry > starEntry && directEntry < route.GetPointsNumber()) { - /* try to find the closest next waypoint */ - while (0 == this->m_nextStarWaypoint) { - for (std::size_t i = 0; i < this->m_arrivalRoute.size(); ++i) { - if (direct == gsl::at(this->m_arrivalRoute, i).name()) { - this->m_nextStarWaypoint = i; - break; - } - } - - if (0 == this->m_nextStarWaypoint) { - directEntry += 1; - if (directEntry >= route.GetPointsNumber()) { - this->m_nextStarWaypoint = this->m_arrivalRoute.size() - 1; - break; - } - - direct = std::string(route.GetPointName(directEntry)); - } - } - } - - const auto target = plan.GetCorrelatedRadarTarget(); - this->predictETA(target, plan.GetPositionPredictions()); + this->update(radarTarget); } bool Inbound::fixedPlan() const noexcept { @@ -522,14 +385,15 @@ const Time& Inbound::timeToLose() const noexcept { return this->m_timeToLose; } -const Length& Inbound::trackmiles() const noexcept { - return this->m_trackmiles; +const std::string& Inbound::nextWaypoint() const noexcept { + static std::string __fallback; + + if (this->m_nextStarWaypoint < this->m_arrivalRoute.size()) + return gsl::at(this->m_arrivalRoute, this->m_nextStarWaypoint).name(); + + return __fallback; } -const Time& Inbound::flighttime() const noexcept { - return this->m_flighttime; -} - -const std::string& Inbound::predictedWaypoint() const noexcept { - return this->m_predictedWaypoint; +const std::vector& Inbound::arrivalRoute() const noexcept { + return this->m_arrivalRoute; }