Autonomy Software C++ 24.5.1
Welcome to the Autonomy Software repository of the Mars Rover Design Team (MRDT) at Missouri University of Science and Technology (Missouri S&T)! API reference contains the source code and other resources for the development of the autonomy software for our Mars rover. The Autonomy Software project aims to compete in the University Rover Challenge (URC) by demonstrating advanced autonomous capabilities and robust navigation algorithms.
Loading...
Searching...
No Matches
controllers::PredictiveStanleyController Class Reference

This class implements the Predictive Stanley Controller. This controller is used to follow a path using the Stanley method with predictive control. More...

#include <PredictiveStanleyController.h>

Collaboration diagram for controllers::PredictiveStanleyController:

Classes

struct  DriveVector
 

Public Member Functions

 PredictiveStanleyController (const double dControlGain=0.1, const double dAngularVelocityLimit=90.0, const int nPredictionHorizon=5, const double dPredictionTimeStep=0.01)
 Construct a new Predictive Stanley Controller:: Predictive Stanley Controller object.
 
 ~PredictiveStanleyController ()
 Destroy the Predictive Stanley Controller:: Predictive Stanley Controller object.
 
DriveVector Calculate (const geoops::RoverPose &stCurrentPose, const double dMaxSpeed=constants::NAVIGATING_MOTOR_POWER)
 Calculate an updated steering angle for the rover based on the current pose using the predictive stanley controller.
 
void SetReferencePath (const std::vector< geoops::Waypoint > &vReferencePath)
 Sets the path that the controller will follow.
 
void SetReferencePath (const std::vector< geoops::UTMCoordinate > &vReferencePath)
 Sets the path that the controller will follow.
 
void SetReferencePath (const std::vector< geoops::GPSCoordinate > &vReferencePath)
 Sets the path that the controller will follow.
 
void SetControlGain (const double dControlGain)
 Setter for the control gain of the stanley controller.
 
void SetAngularVelocityLimit (const double dAngularVelocityLimit)
 Setter for the angular velocity limit of the stanley controller.
 
std::vector< geoops::WaypointGetReferencePath () const
 Accessor for the reference path that the controller is following.
 
double GetControlGain () const
 Accessor for the control gain of the stanley controller.
 
double GetAngularVelocityLimit () const
 Accessor for the angular velocity limit of the stanley controller.
 
double GetReferencePathTargetIndex () const
 Accessor for the current target index in the reference path.
 

Private Member Functions

geoops::Waypoint FindClosestWaypointInPath (const geoops::UTMCoordinate &stCurrentPosition)
 Given the current position of the rover, find the point on the reference path that is closest to the rover's front axle position (based on the wheelbase). This is what makes sure the rover progresses forward in indexes along the path.
 

Private Attributes

UnicycleModel m_UnicycleModel
 
double m_dControlGain
 
double m_dAngularVelocityLimit
 
int m_nPredictionHorizon
 
double m_dPredictionTimeStep
 
int m_nCurrentReferencePathTargetIndex
 
std::vector< geoops::Waypointm_vReferencePath
 

Detailed Description

This class implements the Predictive Stanley Controller. This controller is used to follow a path using the Stanley method with predictive control.

Note
See docs/WhitePapers/2020-A-Path-Tracking-Algorithm-Using-Predictive-Stanley-Lateral-Controller.pdf
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10

Constructor & Destructor Documentation

◆ PredictiveStanleyController()

controllers::PredictiveStanleyController::PredictiveStanleyController ( const double  dControlGain = 0.1,
const double  dAngularVelocityLimit = 90.0,
const int  nPredictionHorizon = 5,
const double  dPredictionTimeStep = 0.01 
)

Construct a new Predictive Stanley Controller:: Predictive Stanley Controller object.

Parameters
dControlGain- The control gain for the controller.
dSteeringAngleLimit- The maximum steering angle the rover can turn.
nPredictionHorizon- The number of predictions to make.
dPredictionTimeStep- The time step to predict the future state. How far into the future to predict.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
45 {
46 // Initialize member variables.
47 m_dControlGain = dControlGain;
48 m_dAngularVelocityLimit = dAngularVelocityLimit;
49 m_nPredictionHorizon = nPredictionHorizon;
50 m_dPredictionTimeStep = dPredictionTimeStep;
51 m_nCurrentReferencePathTargetIndex = 0;
52 m_UnicycleModel = UnicycleModel(0.0, 0.0, 0.0);
53 }
This class implements the Unicycle Model. This model is used to predict the future state of the rover...
Definition UnicycleModel.hpp:35

◆ ~PredictiveStanleyController()

controllers::PredictiveStanleyController::~PredictiveStanleyController ( )

Destroy the Predictive Stanley Controller:: Predictive Stanley Controller object.

Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
63 {
64 // Nothing to do yet.
65 }

Member Function Documentation

◆ Calculate()

PredictiveStanleyController::DriveVector controllers::PredictiveStanleyController::Calculate ( const geoops::RoverPose stCurrentPose,
const double  dMaxSpeed = constants::NAVIGATING_MOTOR_POWER 
)

Calculate an updated steering angle for the rover based on the current pose using the predictive stanley controller.

Parameters
stCurrentPose- The current pose of the rover.
dMaxSpeed- The maximum speed the rover can travel.
Returns
double - The new output steering angle for the rover.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
79 {
80 // Create instance variables.
81 double dSteeringAngle = 0.0;
82 std::vector<UnicycleModel::Prediction> vPredictions;
83
84 // Check if the reference path is empty.
85 if (m_vReferencePath.empty())
86 {
87 // Submit logger message.
88 LOG_WARNING(logging::g_qSharedLogger, "PredictiveStanleyController::Calculate: Reference path is empty. Cannot calculate drive powers.");
89
90 return DriveVector{0.0, 0.0};
91 }
92
93 // Check if we are at the end of the path. Normally stanley would continue driving in the last direction of the calculated path
94 // headings, but we want to make sure we get to the end point, so we'll just drive straight to it once at the end of the path.
95 // We check if we are on the last segment (size - 2).
96 if (m_nCurrentReferencePathTargetIndex >= static_cast<int>(m_vReferencePath.size()) - 2)
97 {
98 // Get the start and end points of the last segment.
99 geoops::UTMCoordinate stLastPoint = m_vReferencePath.back().GetUTMCoordinate();
100 geoops::UTMCoordinate stSecondToLastPoint = m_vReferencePath[m_vReferencePath.size() - 2].GetUTMCoordinate();
101 geoops::UTMCoordinate stRoverPos = stCurrentPose.GetUTMCoordinate();
102
103 // Calculate vector of the last segment (Start -> End).
104 double dSegmentX = stLastPoint.dEasting - stSecondToLastPoint.dEasting;
105 double dSegmentY = stLastPoint.dNorthing - stSecondToLastPoint.dNorthing;
106 double dSegmentLenSq = dSegmentX * dSegmentX + dSegmentY * dSegmentY;
107
108 // Calculate vector from Segment Start -> Rover.
109 double dRoverVectorX = stRoverPos.dEasting - stSecondToLastPoint.dEasting;
110 double dRoverVectorY = stRoverPos.dNorthing - stSecondToLastPoint.dNorthing;
111
112 // Project rover onto the segment vector with a dot product.
113 // t represents the normalized distance along the segment (0.0 = start, 1.0 = end).
114 double dNormalDistance = 0.0;
115 if (dSegmentLenSq > 1e-6)
116 {
117 dNormalDistance = (dRoverVectorX * dSegmentX + dRoverVectorY * dSegmentY) / dSegmentLenSq;
118 }
119
120 // Check if we have passed the end (t >= 1.0) or are very close to it.
121 // We add a small tolerance (e.g., 0.99) or strictly check t >= 1.0.
122 if (dNormalDistance >= 1.0)
123 {
124 // We have reached or passed the end.
125 // Calculate heading to the last point to ensure we turn around if we overshot.
126 double dHeadingToLastWaypoint = geoops::CalculateGeoMeasurement(stRoverPos, stLastPoint).dStartRelativeBearing;
127 return DriveVector{dHeadingToLastWaypoint, 1.0};
128 }
129 }
130
131 // Update the unicycle model with the current state.
132 m_UnicycleModel.UpdateState(stCurrentPose.GetUTMCoordinate().dEasting, stCurrentPose.GetUTMCoordinate().dNorthing, stCurrentPose.GetCompassHeading());
133 // Predict the future state of the model.
134 m_UnicycleModel.Predict(m_dPredictionTimeStep, m_nPredictionHorizon, vPredictions);
135
136 // Loop through all the predicted future states to compute the steering angle.
137 for (size_t nIter = 0; nIter < vPredictions.size(); ++nIter)
138 {
139 // Create instance variables.
140 double dPredictedXPosition = vPredictions[nIter].dXPosition;
141 double dPredictedYPosition = vPredictions[nIter].dYPosition;
142 double dPredictedTheta = vPredictions[nIter].dTheta;
143
144 // Create a UTM coordinate for the predicted position.
145 geoops::UTMCoordinate stPredictedPosition = stCurrentPose.GetUTMCoordinate();
146 stPredictedPosition.dEasting = dPredictedXPosition;
147 stPredictedPosition.dNorthing = dPredictedYPosition;
148 // Find the closest point to the reference path.
149 geoops::Waypoint stClosestWaypoint = FindClosestWaypointInPath(stPredictedPosition);
150
151 // Compute the path forward vector from the segment start -> next waypoint (use index set by FindClosestWaypointInPath).
152 int nIdx = m_nCurrentReferencePathTargetIndex;
153 int nNextIdx = std::min(nIdx + 1, static_cast<int>(m_vReferencePath.size() - 1));
154 const geoops::UTMCoordinate& stSegmentStart = m_vReferencePath[nIdx].GetUTMCoordinate();
155 const geoops::UTMCoordinate& stSegmentEnd = m_vReferencePath[nNextIdx].GetUTMCoordinate();
156
157 // Forward vector of the path segment.
158 double dForwardVectorX = stSegmentEnd.dEasting - stSegmentStart.dEasting;
159 double dForwardVectorY = stSegmentEnd.dNorthing - stSegmentStart.dNorthing;
160 double dForwardNorm = std::hypot(dForwardVectorX, dForwardVectorY);
161 // Degenerate segment. Skip this prediction step.
162 if (dForwardNorm < 1e-9)
163 {
164 continue;
165 }
166
167 // Unit forward vector.
168 double dFwdUnitX = dForwardVectorX / dForwardNorm;
169 double dFwdUnitY = dForwardVectorY / dForwardNorm;
170 // Math angle of segment (atan2 gives angle from +X (East), CCW positive) in degrees.
171 double dSegmentMathDeg = std::atan2(dForwardVectorY, dForwardVectorX) * (180.0 / M_PI);
172 // Convert math angle to COMPASS heading (0 = North, clockwise positive):
173 // compass = (90 - math) mod 360.
174 double dPathHeadingDeg = std::fmod(90.0 - dSegmentMathDeg + 360.0, 360.0);
175 // Predicted theta is produced by UnicycleModel in DEGREES (compass). Treat as degrees.
176 double dPredThetaDeg = dPredictedTheta;
177 // Small helper: signed smallest-angle difference in degrees in (-180, 180].
178 std::function<double(double, double)> AngleDiffDeg = [](double targetDeg, double sourceDeg) -> double
179 {
180 double diff = std::fmod(targetDeg - sourceDeg + 540.0, 360.0) - 180.0;
181 return diff;
182 };
183 // Heading error (degrees): positive means we should rotate clockwise to match target (compass conv).
184 double dHeadingError = AngleDiffDeg(dPathHeadingDeg, dPredThetaDeg);
185
186 // Vehicle vector relative to the closest point. (projection)
187 double dVehicleVectorX = dPredictedXPosition - stClosestWaypoint.GetUTMCoordinate().dEasting;
188 double dVehicleVectorY = dPredictedYPosition - stClosestWaypoint.GetUTMCoordinate().dNorthing;
189 // Longitudinal projection and lateral vector.
190 double dLongitudinal = dVehicleVectorX * dFwdUnitX + dVehicleVectorY * dFwdUnitY;
191 double dLateralX = dVehicleVectorX - dLongitudinal * dFwdUnitX;
192 double dLateralY = dVehicleVectorY - dLongitudinal * dFwdUnitY;
193 double dLateralDistance = std::hypot(dLateralX, dLateralY);
194 // Sign using cross product. (left positive)
195 double dCross = dFwdUnitX * dVehicleVectorY - dFwdUnitY * dVehicleVectorX;
196 double dCrossTrackError = (dCross > 0.0 ? 1.0 : -1.0) * dLateralDistance; // meters
197
198 // Apply an exponential weight factor that decreases as we predict further into the future.
199 double dTimeWeight = std::exp(-1.5 * static_cast<double>(nIter));
200
201 // Use model velocity if available, otherwise fall back to provided dMaxSpeed
202 double dModelSpeed = m_UnicycleModel.GetVelocity();
203 double dSpeed = (dModelSpeed > 0.0) ? dModelSpeed : dMaxSpeed;
204 dSpeed = std::max(dSpeed, 1e-4);
205
206 // Stanley control law to compute the steering angle.
207 double dStanleyTermRad = std::atan2(m_dControlGain * dCrossTrackError, dSpeed);
208 double dStanleyTermDeg = dStanleyTermRad * (180.0 / M_PI);
209 // Combine the heading error and the stanley term.
210 dSteeringAngle += (dStanleyTermDeg + dHeadingError) * dTimeWeight;
211
212 // Limit the steering angle to the given limit.
213 dSteeringAngle = std::clamp(dSteeringAngle, -m_dAngularVelocityLimit, m_dAngularVelocityLimit);
214 }
215
216 // The new steering heading must be from 0-360 degrees.
217 double dAbsoluteHeadingGoal = numops::InputAngleModulus(stCurrentPose.GetCompassHeading() + dSteeringAngle, 0.0, 360.0);
218
219 return DriveVector{dAbsoluteHeadingGoal, dMaxSpeed};
220 }
void UpdateState(const double dXPosition, const double dYPosition, const double dTheta)
Update the state of the model, given a new position and heading. This method will automatically calcu...
Definition UnicycleModel.hpp:151
double GetVelocity() const
Accessor for the Velocity private member.
Definition UnicycleModel.hpp:300
void Predict(const double dTimeStep, const int nNumPredictions, std::vector< Prediction > &vPredictions)
Accessor for the State private member.
Definition UnicycleModel.hpp:187
geoops::Waypoint FindClosestWaypointInPath(const geoops::UTMCoordinate &stCurrentPosition)
Given the current position of the rover, find the point on the reference path that is closest to the ...
Definition PredictiveStanleyController.cpp:375
GeoMeasurement CalculateGeoMeasurement(const GPSCoordinate &stCoord1, const GPSCoordinate &stCoord2)
The shortest path between two points on an ellipsoid at (lat1, lon1) and (lat2, lon2) is called the g...
Definition GeospatialOperations.hpp:553
constexpr T InputAngleModulus(T tValue, T tMinValue, T tMaxValue)
Calculates the modulus of an input angle.
Definition NumberOperations.hpp:165
double GetCompassHeading() const
Accessor for the Compass Heading private member.
Definition GeospatialOperations.hpp:787
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:767
This struct stores/contains information about a UTM coordinate.
Definition GeospatialOperations.hpp:211
This struct is used by the WaypointHandler class to store location, size, and type information about ...
Definition GeospatialOperations.hpp:423
const geoops::UTMCoordinate & GetUTMCoordinate() const
Accessor for the geoops::UTMCoordinate member variable.
Definition GeospatialOperations.hpp:508
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetReferencePath() [1/3]

void controllers::PredictiveStanleyController::SetReferencePath ( const std::vector< geoops::Waypoint > &  vReferencePath)

Sets the path that the controller will follow.

Parameters
vReferencePath- A reference to a vector of waypoints that make up the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
231 {
232 // Reset the current target index.
233 m_nCurrentReferencePathTargetIndex = 0;
234 // Reset the bicycle model.
235 m_UnicycleModel.ResetState();
236
237 // Smooth the path by fitting it to a B-spline.
238 std::vector<geoops::Waypoint> vSmoothedPath = pathplanners::postprocessing::FitPathWithBSpline(vReferencePath);
239
240 // Set the reference path.
241 m_vReferencePath = vSmoothedPath;
242 }
void ResetState(const double dXPosition, const double dYPosition, const double dTheta)
Resets the state of the model to a new position and heading.
Definition UnicycleModel.hpp:109
std::vector< geoops::Waypoint > FitPathWithBSpline(const std::vector< geoops::Waypoint > &vRawPath)
Fits a B-spline to the given path using cubic interpolation.
Definition PathPostProcessing.hpp:51
Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetReferencePath() [2/3]

void controllers::PredictiveStanleyController::SetReferencePath ( const std::vector< geoops::UTMCoordinate > &  vReferencePath)

Sets the path that the controller will follow.

Parameters
vReferencePath- A reference to a vector of UTM coordinates that make up the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
253 {
254 // Convert UTM coordinates to waypoints.
255 std::vector<geoops::Waypoint> vConvertedPath;
256 for (const geoops::UTMCoordinate& stUTMCoord : vReferencePath)
257 {
258 vConvertedPath.emplace_back(geoops::Waypoint(stUTMCoord, geoops::WaypointType::eNavigationWaypoint, 0.0, -1));
259 }
260
261 // Set the reference path.
262 this->SetReferencePath(vConvertedPath);
263 }
void SetReferencePath(const std::vector< geoops::Waypoint > &vReferencePath)
Sets the path that the controller will follow.
Definition PredictiveStanleyController.cpp:230
Here is the call graph for this function:

◆ SetReferencePath() [3/3]

void controllers::PredictiveStanleyController::SetReferencePath ( const std::vector< geoops::GPSCoordinate > &  vReferencePath)

Sets the path that the controller will follow.

Parameters
vReferencePath- A reference to a vector of GPS coordinates that make up the path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
274 {
275 // Convert GPS coordinates to waypoints.
276 std::vector<geoops::Waypoint> vConvertedPath;
277 for (const geoops::GPSCoordinate& stGPSCoord : vReferencePath)
278 {
279 vConvertedPath.emplace_back(geoops::Waypoint(stGPSCoord, geoops::WaypointType::eNavigationWaypoint, 0.0, -1));
280 }
281
282 // Set the reference path.
283 this->SetReferencePath(vConvertedPath);
284 }
This struct stores/contains information about a GPS data.
Definition GeospatialOperations.hpp:100
Here is the call graph for this function:

◆ SetControlGain()

void controllers::PredictiveStanleyController::SetControlGain ( const double  dControlGain)

Setter for the control gain of the stanley controller.

Parameters
dControlGain- The control gain for the controller.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
295 {
296 m_dControlGain = dControlGain;
297 }
Here is the caller graph for this function:

◆ SetAngularVelocityLimit()

void controllers::PredictiveStanleyController::SetAngularVelocityLimit ( const double  dAngularVelocityLimit)

Setter for the angular velocity limit of the stanley controller.

Parameters
dAngularVelocityLimit- The angular velocity limit for the controller.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-11
308 {
309 m_dAngularVelocityLimit = dAngularVelocityLimit;
310 }
Here is the caller graph for this function:

◆ GetReferencePath()

std::vector< geoops::Waypoint > controllers::PredictiveStanleyController::GetReferencePath ( ) const

Accessor for the reference path that the controller is following.

Returns
std::vector<geoops::Waypoint> - A copy of the reference path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
347 {
348 return m_vReferencePath;
349 }
Here is the caller graph for this function:

◆ GetControlGain()

double controllers::PredictiveStanleyController::GetControlGain ( ) const

Accessor for the control gain of the stanley controller.

Returns
double - The control gain for the controller.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
321 {
322 return m_dControlGain;
323 }
Here is the caller graph for this function:

◆ GetAngularVelocityLimit()

double controllers::PredictiveStanleyController::GetAngularVelocityLimit ( ) const

Accessor for the angular velocity limit of the stanley controller.

Returns
double - The angular velocity limit for the controller.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-11
334 {
335 return m_dAngularVelocityLimit;
336 }
Here is the caller graph for this function:

◆ GetReferencePathTargetIndex()

double controllers::PredictiveStanleyController::GetReferencePathTargetIndex ( ) const

Accessor for the current target index in the reference path.

Returns
double - The current target index in the reference path.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
360 {
361 return m_nCurrentReferencePathTargetIndex;
362 }
Here is the caller graph for this function:

◆ FindClosestWaypointInPath()

geoops::Waypoint controllers::PredictiveStanleyController::FindClosestWaypointInPath ( const geoops::UTMCoordinate stCurrentPosition)
private

Given the current position of the rover, find the point on the reference path that is closest to the rover's front axle position (based on the wheelbase). This is what makes sure the rover progresses forward in indexes along the path.

Parameters
stCurrentPosition- The current position of the rover.
dCurrentHeading- The current heading of the rover in degrees from 0-360.
Author
clayjay3 (clayt.nosp@m.onra.nosp@m.ycowe.nosp@m.n@gm.nosp@m.ail.c.nosp@m.om)
Date
2025-01-10
376 {
377 // Create instance variables.
378 geoops::Waypoint stClosestWaypoint;
379 double dClosestDistanceSq = std::numeric_limits<double>::max();
380 const size_t nWaypoints = m_vReferencePath.size();
381
382 // Check for empty path.
383 if (nWaypoints == 0)
384 {
385 return stClosestWaypoint;
386 }
387
388 // If only a single waypoint, return it directly.
389 if (nWaypoints == 1)
390 {
391 m_nCurrentReferencePathTargetIndex = 0;
392 return m_vReferencePath.front();
393 }
394
395 // We'll search across path segments (i -> i+1) and compute the closest point on each segment.
396 int nBestSegmentIndex = -1;
397 geoops::UTMCoordinate stBestProjection;
398
399 for (size_t siIter = m_nCurrentReferencePathTargetIndex; siIter < nWaypoints - 1; ++siIter)
400 {
401 const geoops::UTMCoordinate& stA = m_vReferencePath[siIter].GetUTMCoordinate();
402 const geoops::UTMCoordinate& stB = m_vReferencePath[siIter + 1].GetUTMCoordinate();
403
404 // Segment vector. (B - A)
405 double dSX = stB.dEasting - stA.dEasting;
406 double dSY = stB.dNorthing - stA.dNorthing;
407
408 // Vector from A -> P.
409 double dPX = stCurrentPosition.dEasting - stA.dEasting;
410 double dPY = stCurrentPosition.dNorthing - stA.dNorthing;
411
412 double sSegmentLengthSquared = dSX * dSX + dSY * dSY;
413 double dProjT = 0.0;
414 if (sSegmentLengthSquared > 0.0)
415 {
416 // projection parameter t = dot(P-A, B-A) / |B-A|^2.
417 dProjT = (dPX * dSX + dPY * dSY) / sSegmentLengthSquared;
418 dProjT = std::clamp(dProjT, 0.0, 1.0);
419 }
420 else
421 {
422 // Degenerate segment (A == B). Use A as projection.
423 dProjT = 0.0;
424 }
425
426 // Projection point coordinates.
427 double dProjX = stA.dEasting + dProjT * dSX;
428 double dProjY = stA.dNorthing + dProjT * dSY;
429
430 // Squared distance from P to projection.
431 double dx = stCurrentPosition.dEasting - dProjX;
432 double dy = stCurrentPosition.dNorthing - dProjY;
433 double distSq = dx * dx + dy * dy;
434
435 if (distSq < dClosestDistanceSq)
436 {
437 dClosestDistanceSq = distSq;
438 nBestSegmentIndex = static_cast<int>(siIter);
439 stBestProjection.dEasting = dProjX;
440 stBestProjection.dNorthing = dProjY;
441 stBestProjection.nZone = stA.nZone;
442 stBestProjection.bWithinNorthernHemisphere = stA.bWithinNorthernHemisphere;
443 }
444 }
445
446 // If we didn't find a segment (shouldn't happen), fall back to the closest waypoint vertex search.
447 if (nBestSegmentIndex < 0)
448 {
449 double dClosestDist = std::numeric_limits<double>::max();
450 for (size_t siIter = 0; siIter < nWaypoints; ++siIter)
451 {
452 double dDistance = geoops::CalculateGeoMeasurement(stCurrentPosition, m_vReferencePath[siIter].GetUTMCoordinate()).dDistanceMeters;
453 if (dDistance < dClosestDist)
454 {
455 dClosestDist = dDistance;
456 stClosestWaypoint = m_vReferencePath[siIter];
457 m_nCurrentReferencePathTargetIndex = static_cast<int>(siIter);
458 }
459 }
460 return stClosestWaypoint;
461 }
462
463 // Build a waypoint for the projected point.
464 // Use the properties of the segment start waypoint as a base, then set the UTM to the projection.
465 geoops::Waypoint stSegmentStart = m_vReferencePath[nBestSegmentIndex];
466 geoops::UTMCoordinate stBestCoord = geoops::UTMCoordinate(stBestProjection.dEasting,
467 stBestProjection.dNorthing,
468 stBestProjection.nZone,
469 stBestProjection.bWithinNorthernHemisphere,
470 stSegmentStart.GetUTMCoordinate().dAltitude,
471 stSegmentStart.GetUTMCoordinate().d2DAccuracy,
472 stSegmentStart.GetUTMCoordinate().d3DAccuracy,
473 stSegmentStart.GetUTMCoordinate().dMeridianConvergence,
474 stSegmentStart.GetUTMCoordinate().dScale,
475 stSegmentStart.GetUTMCoordinate().eCoordinateAccuracyFixType,
476 stSegmentStart.GetUTMCoordinate().bIsDifferential);
477 stClosestWaypoint = geoops::Waypoint(stBestCoord);
478
479 // Update the controller's current reference index to the segment start index so callers can use (index + 1).
480 m_nCurrentReferencePathTargetIndex = nBestSegmentIndex;
481
482 return stClosestWaypoint;
483 }
Here is the call graph for this function:
Here is the caller graph for this function:

The documentation for this class was generated from the following files: