1
0
Fork 0

RoutePath mag-var and point-on-path fixes

This commit is contained in:
James Turner 2014-12-21 19:53:16 +03:00
parent a1169e4f75
commit f80528f8d6

View file

@ -100,12 +100,7 @@ public:
{ {
const std::string& ty(wpt->type()); const std::string& ty(wpt->type());
if (wpt->flag(WPT_DYNAMIC)) { if (wpt->flag(WPT_DYNAMIC)) {
if ((ty == "hdgToAlt") || (ty == "radialIntercept") || (ty == "dmeIntercept")) { // presumption is that we always overfly such a waypoint
legCourse = wpt->headingRadialDeg();
legCourseValid = true;
}
// presumtpion is that we always overfly such a waypoint
if (ty == "hdgToAlt") { if (ty == "hdgToAlt") {
flyOver = true; flyOver = true;
} }
@ -114,7 +109,7 @@ public:
posValid = true; posValid = true;
if ((ty == "runway") || (ty == "hold")) { if ((ty == "runway") || (ty == "hold")) {
legCourse = wpt->headingRadialDeg(); legCourseTrue = wpt->headingRadialDeg() + magVarFor(pos);
legCourseValid = true; legCourseValid = true;
} }
@ -144,7 +139,7 @@ public:
pos = next->pos; pos = next->pos;
} }
} }
if (posValid && !legCourseValid && previous.posValid) { if (posValid && !legCourseValid && previous.posValid) {
// check for duplicate points now // check for duplicate points now
if (previous.wpt->matches(wpt)) { if (previous.wpt->matches(wpt)) {
@ -152,17 +147,32 @@ public:
} }
// we can compute leg course now // we can compute leg course now
legCourse = SGGeodesy::courseDeg(previous.pos, pos); legCourseTrue = SGGeodesy::courseDeg(previous.pos, pos);
legCourseValid = true; legCourseValid = true;
} }
} }
void computeLegCourse(const WayptData& previous)
{
if (!legCourseValid) {
if (posValid) {
legCourseTrue = SGGeodesy::courseDeg(previous.pos, pos);
legCourseValid = true;
} else if (isCourseConstrained()) {
double magVar = magVarFor(previous.pos);
legCourseTrue = wpt->headingRadialDeg() + magVar;
legCourseValid = true;
}
}
}
void computeTurn(double radiusM, const WayptData& previous, WayptData& next) void computeTurn(double radiusM, const WayptData& previous, WayptData& next)
{ {
assert(!skipped); assert(!skipped);
assert(legCourseValid && next.legCourseValid); assert(legCourseValid && next.legCourseValid);
turnAngle = next.legCourse - legCourse; turnAngle = next.legCourseTrue - legCourseTrue;
SG_NORMALIZE_RANGE(turnAngle, -180.0, 180.0); SG_NORMALIZE_RANGE(turnAngle, -180.0, 180.0);
turnRadius = radiusM; turnRadius = radiusM;
@ -170,9 +180,9 @@ public:
if (flyOver) { if (flyOver) {
turnEntryPos = pos; turnEntryPos = pos;
turnCenter = SGGeodesy::direct(pos, legCourse + p, turnRadius); turnCenter = SGGeodesy::direct(pos, legCourseTrue + p, turnRadius);
// use the leg course // use the leg course
turnExitPos = SGGeodesy::direct(turnCenter, next.legCourse - p, turnRadius); turnExitPos = SGGeodesy::direct(turnCenter, next.legCourseTrue - p, turnRadius);
if (!next.wpt->flag(WPT_DYNAMIC)) { if (!next.wpt->flag(WPT_DYNAMIC)) {
// distance perpendicular to next leg course, after turning // distance perpendicular to next leg course, after turning
@ -190,7 +200,7 @@ public:
// move by the distance to compensate // move by the distance to compensate
double d = turnRadius * 2.0 * sin(theta * SG_DEGREES_TO_RADIANS); double d = turnRadius * 2.0 * sin(theta * SG_DEGREES_TO_RADIANS);
turnExitPos = SGGeodesy::direct(turnExitPos, next.legCourse, d); turnExitPos = SGGeodesy::direct(turnExitPos, next.legCourseTrue, d);
overflightCompensationAngle = -theta; overflightCompensationAngle = -theta;
turnPathDistanceM = turnRadius * (fabs(turnAngle) + turnPathDistanceM = turnRadius * (fabs(turnAngle) +
@ -206,13 +216,17 @@ public:
increaseAngle = copysign(increaseAngle, turnAngle); increaseAngle = copysign(increaseAngle, turnAngle);
turnAngle += increaseAngle; turnAngle += increaseAngle;
turnExitPos = SGGeodesy::direct(turnCenter, legCourse + turnAngle - p, turnRadius); turnExitPos = SGGeodesy::direct(turnCenter, legCourseTrue + turnAngle - p, turnRadius);
// modify next leg course // modify next leg course
next.legCourse = SGGeodesy::courseDeg(turnExitPos, next.pos); next.legCourseTrue = SGGeodesy::courseDeg(turnExitPos, next.pos);
turnPathDistanceM = turnRadius * (fabs(turnAngle) * SG_DEGREES_TO_RADIANS); turnPathDistanceM = turnRadius * (fabs(turnAngle) * SG_DEGREES_TO_RADIANS);
} }
} else {
// next point is dynamic
// no compentsation needed
turnPathDistanceM = turnRadius * (fabs(turnAngle) * SG_DEGREES_TO_RADIANS);
} }
} else { } else {
hasEntry = true; hasEntry = true;
@ -220,12 +234,12 @@ public:
double halfAngle = turnAngle * 0.5; double halfAngle = turnAngle * 0.5;
double turnCenterOffset = turnRadius / cos(halfAngle * SG_DEGREES_TO_RADIANS); double turnCenterOffset = turnRadius / cos(halfAngle * SG_DEGREES_TO_RADIANS);
turnCenter = SGGeodesy::direct(pos, legCourse + halfAngle + p, turnCenterOffset); turnCenter = SGGeodesy::direct(pos, legCourseTrue + halfAngle + p, turnCenterOffset);
double distAlongPath = turnRadius * tan(fabs(halfAngle) * SG_DEGREES_TO_RADIANS); double distAlongPath = turnRadius * tan(fabs(halfAngle) * SG_DEGREES_TO_RADIANS);
turnEntryPos = SGGeodesy::direct(pos, legCourse, -distAlongPath); turnEntryPos = SGGeodesy::direct(pos, legCourseTrue, -distAlongPath);
turnExitPos = SGGeodesy::direct(pos, next.legCourse, distAlongPath); turnExitPos = SGGeodesy::direct(pos, next.legCourseTrue, distAlongPath);
turnPathDistanceM = turnRadius * (fabs(halfAngle) * SG_DEGREES_TO_RADIANS); turnPathDistanceM = turnRadius * (fabs(halfAngle) * SG_DEGREES_TO_RADIANS);
} }
@ -247,7 +261,7 @@ public:
double halfAngle = turnAngle * 0.5; double halfAngle = turnAngle * 0.5;
int steps = std::max(SGMiscd::roundToInt(fabs(halfAngle) / 3.0), 1); int steps = std::max(SGMiscd::roundToInt(fabs(halfAngle) / 3.0), 1);
double stepIncrement = halfAngle / steps; double stepIncrement = halfAngle / steps;
double h = legCourse; double h = legCourseTrue;
SGGeod p = turnEntryPos; SGGeod p = turnEntryPos;
double stepDist = (fabs(stepIncrement) / 360.0) * SGMiscd::twopi() * turnRadius; double stepDist = (fabs(stepIncrement) / 360.0) * SGMiscd::twopi() * turnRadius;
@ -272,7 +286,7 @@ public:
double stepIncrement = t / steps; double stepIncrement = t / steps;
// initial exit heading // initial exit heading
double h = legCourse + (flyOver ? 0.0 : (turnAngle * 0.5)); double h = legCourseTrue + (flyOver ? 0.0 : (turnAngle * 0.5));
double turnDirOffset = copysign(90.0, turnAngle); double turnDirOffset = copysign(90.0, turnAngle);
// compute the first point on the exit path. Depends on fly-over vs fly-by // compute the first point on the exit path. Depends on fly-over vs fly-by
@ -319,17 +333,18 @@ public:
// compute the compensation turn center - twice the turn radius // compute the compensation turn center - twice the turn radius
// from turnCenter // from turnCenter
SGGeod tc2 = SGGeodesy::direct(turnCenter, SGGeod tc2 = SGGeodesy::direct(turnCenter,
legCourse - overflightCompensationAngle - p, legCourseTrue - overflightCompensationAngle - p,
turnRadius * 2.0); turnRadius * 2.0);
theta = copysign(theta - turnAngle, overflightCompensationAngle); theta = copysign(theta - turnAngle, overflightCompensationAngle);
return SGGeodesy::direct(tc2, return SGGeodesy::direct(tc2,
legCourse - overflightCompensationAngle + theta + p, turnRadius); legCourseTrue - overflightCompensationAngle + theta + p, turnRadius);
} }
} }
theta = copysign(theta, turnAngle); theta = copysign(theta, turnAngle);
double halfAngle = turnAngle * 0.5; double halfAngle = turnAngle * 0.5;
return SGGeodesy::direct(turnCenter, legCourse - halfAngle + theta - p, turnRadius); double inboundCourse = legCourseTrue + (flyOver ? 0.0 : halfAngle);
return SGGeodesy::direct(turnCenter, inboundCourse + theta - p, turnRadius);
} }
SGGeod pointAlongEntryPath(double distanceM) const SGGeod pointAlongEntryPath(double distanceM) const
@ -338,13 +353,14 @@ public:
double theta = (distanceM / turnRadius) * SG_RADIANS_TO_DEGREES; double theta = (distanceM / turnRadius) * SG_RADIANS_TO_DEGREES;
theta = copysign(theta, turnAngle); theta = copysign(theta, turnAngle);
double p = copysign(90, turnAngle); double p = copysign(90, turnAngle);
return SGGeodesy::direct(turnCenter, legCourse + theta - p, turnRadius); double course = legCourseTrue - turnAngle + theta;
return SGGeodesy::direct(turnCenter, course - p, turnRadius);
} }
WayptRef wpt; WayptRef wpt;
bool hasEntry, posValid, legCourseValid, skipped; bool hasEntry, posValid, legCourseValid, skipped;
SGGeod pos, turnEntryPos, turnExitPos, turnCenter; SGGeod pos, turnEntryPos, turnExitPos, turnCenter;
double turnAngle, turnRadius, legCourse; double turnAngle, turnRadius, legCourseTrue;
double pathDistanceM; double pathDistanceM;
double turnPathDistanceM; // for flyBy, this is half the distance; for flyOver it's the completel distance double turnPathDistanceM; // for flyBy, this is half the distance; for flyOver it's the completel distance
double overflightCompensationAngle; double overflightCompensationAngle;
@ -406,8 +422,8 @@ public:
{ {
const WayptData& previous(previousValidWaypoint(index)); const WayptData& previous(previousValidWaypoint(index));
WayptRef wpt = waypoints[index].wpt; WayptRef wpt = waypoints[index].wpt;
assert(previous.posValid); assert(previous.posValid);
const std::string& ty(wpt->type()); const std::string& ty(wpt->type());
if (ty == "hdgToAlt") { if (ty == "hdgToAlt") {
HeadingToAltitude* h = (HeadingToAltitude*) wpt.get(); HeadingToAltitude* h = (HeadingToAltitude*) wpt.get();
@ -484,7 +500,7 @@ public:
waypoints[index].posValid = true; waypoints[index].posValid = true;
} else if (ty == "vectors") { } else if (ty == "vectors") {
waypoints[index].legCourse = SGGeodesy::courseDeg(previous.turnExitPos, waypoints[index].pos); waypoints[index].legCourseTrue = SGGeodesy::courseDeg(previous.turnExitPos, waypoints[index].pos);
waypoints[index].legCourseValid = true; waypoints[index].legCourseValid = true;
// no turn data // no turn data
} }
@ -694,15 +710,15 @@ void RoutePath::commonInit()
WayptData* nextPtr = ((i + 1) < d->waypoints.size()) ? &d->waypoints[i+1] : 0; WayptData* nextPtr = ((i + 1) < d->waypoints.size()) ? &d->waypoints[i+1] : 0;
d->waypoints[i].initPass1(d->waypoints[i-1], nextPtr); d->waypoints[i].initPass1(d->waypoints[i-1], nextPtr);
} }
for (unsigned int i=1; i<d->waypoints.size(); ++i) { for (unsigned int i=1; i<d->waypoints.size(); ++i) {
if (d->waypoints[i].skipped) { if (d->waypoints[i].skipped) {
continue; continue;
} }
const WayptData& prev(d->previousValidWaypoint(i));
d->waypoints[i].computeLegCourse(prev);
d->computeDynamicPosition(i); d->computeDynamicPosition(i);
const WayptData& prev(d->previousValidWaypoint(i));
double alt = 0.0; // FIXME double alt = 0.0; // FIXME
double gs = d->groundSpeedForAltitude(alt); double gs = d->groundSpeedForAltitude(alt);
@ -714,12 +730,7 @@ void RoutePath::commonInit()
nextIndex++; nextIndex++;
} }
WayptData& next(d->waypoints[nextIndex]); WayptData& next(d->waypoints[nextIndex]);
next.computeLegCourse(d->waypoints[i]);
if (!next.legCourseValid && next.posValid) {
// compute leg course now our own position is valid
next.legCourse = SGGeodesy::courseDeg(d->waypoints[i].pos, next.pos);
next.legCourseValid = true;
}
if (next.legCourseValid) { if (next.legCourseValid) {
d->waypoints[i].computeTurn(radiusM, prev, next); d->waypoints[i].computeTurn(radiusM, prev, next);
@ -901,7 +912,7 @@ double RoutePath::trackForIndex(int index) const
{ {
if (d->waypoints[index].skipped) if (d->waypoints[index].skipped)
return trackForIndex(index - 1); return trackForIndex(index - 1);
return d->waypoints[index].legCourse; return d->waypoints[index].legCourseTrue;
} }
double RoutePath::distanceForIndex(int index) const double RoutePath::distanceForIndex(int index) const
@ -917,40 +928,58 @@ double RoutePath::distanceBetweenIndices(int from, int to) const
SGGeod RoutePath::positionForDistanceFrom(int index, double distanceM) const SGGeod RoutePath::positionForDistanceFrom(int index, double distanceM) const
{ {
int sz = (int) d->waypoints.size(); int sz = (int) d->waypoints.size();
if (index < 0) {
index = sz - 1; // map negative values to end of the route
}
if ((index < 0) || (index >= sz)) { if ((index < 0) || (index >= sz)) {
throw sg_range_exception("waypt index out of range", throw sg_range_exception("waypt index out of range",
"RoutePath::positionForDistanceFrom"); "RoutePath::positionForDistanceFrom");
} }
// find the actual leg we're within // find the actual leg we're within
while ((index < sz) && (d->waypoints[index].pathDistanceM < distanceM)) { if (distanceM < 0.0) {
++index; // scan backwards
distanceM -= d->waypoints[index].pathDistanceM; while ((index > 0) && (distanceM < 0.0)) {
--index;
distanceM += d->waypoints[index].pathDistanceM;
}
if (distanceM < 0.0) {
// still negative, return route start
return d->waypoints[0].pos;
}
} else {
// scan forwards
int nextIndex = index + 1;
while ((nextIndex < sz) && (d->waypoints[nextIndex].pathDistanceM < distanceM)) {
distanceM -= d->waypoints[nextIndex].pathDistanceM;
index = nextIndex++;
}
} }
if (index >= sz) { if ((index + 1) >= sz) {
// past route end, just return final position // past route end, just return final position
return d->waypoints[sz - 1].pos; return d->waypoints[sz - 1].pos;
} else if (index == 0) {
return d->waypoints[0].pos;
} }
const WayptData& wpt(d->waypoints[index]); const WayptData& wpt(d->waypoints[index]);
const WayptData& prev(d->previousValidWaypoint(index)); const WayptData& next(d->waypoints[index+1]);
// check turn exit of previous
if (prev.turnPathDistanceM > distanceM) { if (wpt.turnPathDistanceM > distanceM) {
// on the exit path of previous wpt // on the exit path of current wpt
return prev.pointAlongExitPath(distanceM); return wpt.pointAlongExitPath(distanceM);
} else {
distanceM -= wpt.turnPathDistanceM;
} }
double corePathDistance = wpt.pathDistanceM - wpt.turnPathDistanceM; double corePathDistance = next.pathDistanceM - next.turnPathDistanceM;
if (wpt.hasEntry && (distanceM > corePathDistance)) { if (next.hasEntry && (distanceM > corePathDistance)) {
// on the entry path // on the entry path of next waypoint
return wpt.pointAlongEntryPath(distanceM - corePathDistance); return next.pointAlongEntryPath(distanceM - corePathDistance);
} }
// linear between turn exit and turn entry points // linear between turn exit and turn entry points
SGGeod previousTurnExit = prev.turnExitPos; return SGGeodesy::direct(wpt.turnExitPos, next.legCourseTrue, distanceM);
distanceM -= prev.turnPathDistanceM;
return SGGeodesy::direct(previousTurnExit, wpt.legCourse, distanceM);
} }