Adj. to new perf arc speed req

This commit is contained in:
Kilian Hofmann 2025-07-14 21:50:00 +02:00
parent d90ba6c5e3
commit e41d002cef
17 changed files with 132 additions and 46 deletions

View File

@ -1,2 +0,0 @@
- Revisit CA/FA/VA legs
- Revisit CD/FD/VD/FC legs

View File

@ -14,4 +14,4 @@ fetch = async (path: string) => {
const parser = await Parser.instance(); const parser = await Parser.instance();
console.log(JSON.stringify(await parser.parse(10657))); console.log(JSON.stringify(await parser.parse(10480)));

View File

@ -19,7 +19,7 @@ export const generateTangentArc = (
const line: LineSegment[] = [[start.longitude, start.latitude]]; const line: LineSegment[] = [[start.longitude, start.latitude]];
// Check if there even is an arc // Check if there even is an arc
if (crsFromOrigin !== crsIntoEndpoint) { if (!crsFromOrigin.equal(crsIntoEndpoint)) {
// Course to the end of the arc // Course to the end of the arc
let crsFromStartToEnd; let crsFromStartToEnd;
if (!turnDir || turnDir === "E") { if (!turnDir || turnDir === "E") {
@ -75,7 +75,7 @@ export const generateTangentArc = (
crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;
} }
while (crsOrthogonalOnOrigin !== crsOrthogonalOnEndpoint) { while (!crsOrthogonalOnOrigin.equal(crsOrthogonalOnEndpoint)) {
if (turnDir === "R") { if (turnDir === "R") {
const delta = ( const delta = (
crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin

View File

@ -1,7 +1,6 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { generateTangentArc } from "./generateTangentArc.ts"; import { generateTangentArc } from "./generateTangentArc.ts";
import { generatePerformanceArc } from "./generatePerformanceArc.ts"; import { generatePerformanceArc } from "./generatePerformanceArc.ts";
import Parser from "../parser.ts";
/** /**
* @param crsIntoEndpoint Course into endpoint * @param crsIntoEndpoint Course into endpoint
@ -9,6 +8,7 @@ import Parser from "../parser.ts";
* @param crsFromOrigin Course from arc origin point * @param crsFromOrigin Course from arc origin point
* @param start Waypoint where leg starts * @param start Waypoint where leg starts
* @param end Waypoint where leg ends * @param end Waypoint where leg ends
* @param speed Speed for performance arc
* @param turnDir Turn direction * @param turnDir Turn direction
* @returns Line segments from `start` to `end` * @returns Line segments from `start` to `end`
*/ */
@ -18,6 +18,7 @@ export const handleTurnAtFix = (
crsFromOrigin: number, crsFromOrigin: number,
start: NavFix, start: NavFix,
end: NavFix, end: NavFix,
speed: number,
turnDir?: TurnDirection turnDir?: TurnDirection
) => { ) => {
const line: LineSegment[] = []; const line: LineSegment[] = [];
@ -36,7 +37,7 @@ export const handleTurnAtFix = (
crsIntoIntercept, crsIntoIntercept,
crsFromOrigin, crsFromOrigin,
start, start,
start.speed ? start.speed : Parser.AC_SPEED, speed,
turnDir turnDir
); );

View File

@ -1,39 +1,59 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts";
import Parser from "../parser.ts"; import Parser from "../parser.ts";
import { computeSpeed } from "../utils/computeSpeed.ts"; import { computeSpeed } from "../utils/computeSpeed.ts";
import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts";
export const TerminatorsCA = ( export const TerminatorsCA = (
leg: CATerminalEntry, leg: CATerminalEntry,
previousFix: NavFix, previousFix: NavFix,
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const speed = computeSpeed(leg, previousFix);
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
let line: LineSegment[] = [];
if (previousFix.isFlyOver) {
line = generatePerformanceArc(
crsIntoEndpoint,
lastCourse,
previousFix,
speed,
leg.TurnDir
);
} else {
line.push([previousFix.longitude, previousFix.latitude]);
}
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
if (line.length > 1) {
lastCourse = geolib.getGreatCircleBearing(
{
latitude: line.at(-2)![1],
longitude: line.at(-2)![0],
},
arcEnd
);
}
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint( ...geolib.computeDestinationPoint(
previousFix, arcEnd,
( (
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) /
Parser.AC_VS) * Parser.AC_VS) *
((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60)
).toMetre(), ).toMetre(),
leg.Course.toTrue(previousFix) crsIntoEndpoint
), ),
name: leg.Alt, name: leg.Alt,
isFlyOver: true, isFlyOver: true,
altitude: leg.Alt.parseAltitude(), altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
speed: computeSpeed(leg, previousFix), speed: computeSpeed(leg, previousFix),
speedConstraint: leg.SpeedLimit, speedConstraint: leg.SpeedLimit,
altitudeConstraint: leg.Alt, altitudeConstraint: leg.Alt,
}; };
const line = handleTurnAtFix( line.push([targetFix.longitude, targetFix.latitude]);
leg.Course.toTrue(previousFix),
leg.Course.toTrue(previousFix),
lastCourse,
previousFix,
targetFix,
leg.TurnDir
);
return [targetFix, line]; return [targetFix, line];
}; };

View File

@ -49,7 +49,9 @@ export const TerminatorsCD = (
} }
// Navaid in front of us // Navaid in front of us
else { else {
remainingDistance = distToNavaid - remainingDistance; // Navaid will not be passed before distance is hit
if (distToNavaid > remainingDistance)
remainingDistance = distToNavaid - remainingDistance;
} }
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),

View File

@ -7,6 +7,8 @@ export const TerminatorsCF = (
lastCourse: number, lastCourse: number,
waypoint?: Waypoint waypoint?: Waypoint
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const speed = computeSpeed(leg, previousFix);
const targetFix: NavFix = { const targetFix: NavFix = {
latitude: leg.WptLat, latitude: leg.WptLat,
longitude: leg.WptLon, longitude: leg.WptLon,
@ -24,6 +26,7 @@ export const TerminatorsCF = (
lastCourse, lastCourse,
previousFix, previousFix,
targetFix, targetFix,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -10,6 +10,7 @@ export const TerminatorsCI = (
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix); const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
const speed = computeSpeed(leg, previousFix);
// Compute INTC // Compute INTC
const interceptFix: NavFix = { const interceptFix: NavFix = {
@ -32,6 +33,7 @@ export const TerminatorsCI = (
lastCourse, lastCourse,
previousFix, previousFix,
interceptFix, interceptFix,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -8,6 +8,7 @@ export const TerminatorsCR = (
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const crsIntoEndpoint = leg.Course.toTrue(previousFix); const crsIntoEndpoint = leg.Course.toTrue(previousFix);
const speed = computeSpeed(leg, previousFix);
const interceptFix: NavFix = { const interceptFix: NavFix = {
...computeIntersection( ...computeIntersection(
@ -29,6 +30,7 @@ export const TerminatorsCR = (
lastCourse, lastCourse,
previousFix, previousFix,
interceptFix, interceptFix,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -1,22 +1,53 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts";
import { computeSpeed } from "../utils/computeSpeed.ts"; import { computeSpeed } from "../utils/computeSpeed.ts";
import Parser from "../parser.ts"; import Parser from "../parser.ts";
import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts";
export const TerminatorsFA = ( export const TerminatorsFA = (
leg: FATerminalEntry, leg: FATerminalEntry,
previousFix: NavFix, previousFix: NavFix,
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const refFix = {
latitude: leg.WptLat,
longitude: leg.WptLon,
};
const speed = computeSpeed(leg, previousFix);
const crsIntoEndpoint = leg.Course.toTrue(refFix);
let line: LineSegment[] = [];
if (previousFix.isFlyOver) {
line = generatePerformanceArc(
crsIntoEndpoint,
lastCourse,
previousFix,
speed,
leg.TurnDir
);
} else {
line.push([previousFix.longitude, previousFix.latitude]);
}
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
if (line.length > 1) {
lastCourse = geolib.getGreatCircleBearing(
{
latitude: line.at(-2)![1],
longitude: line.at(-2)![0],
},
arcEnd
);
}
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint( ...geolib.computeDestinationPoint(
{ latitude: leg.WptLat, longitude: leg.WptLon }, arcEnd,
( (
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) /
Parser.AC_VS) * Parser.AC_VS) *
((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60)
).toMetre(), ).toMetre(),
leg.Course.toTrue(previousFix) crsIntoEndpoint
), ),
name: leg.Alt, name: leg.Alt,
isFlyOver: true, isFlyOver: true,
@ -26,14 +57,7 @@ export const TerminatorsFA = (
altitudeConstraint: leg.Alt, altitudeConstraint: leg.Alt,
}; };
const line = handleTurnAtFix( line.push([targetFix.longitude, targetFix.latitude]);
leg.Course.toTrue(previousFix),
leg.Course.toTrue(previousFix),
lastCourse,
previousFix,
targetFix,
leg.TurnDir
);
return [targetFix, line]; return [targetFix, line];
}; };

View File

@ -53,7 +53,9 @@ export const TerminatorsFD = (
} }
// Navaid in front of us // Navaid in front of us
else { else {
remainingDistance = distToNavaid - remainingDistance; // Navaid will not be passed before distance is hit
if (distToNavaid > remainingDistance)
remainingDistance = distToNavaid - remainingDistance;
} }
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),

View File

@ -1,11 +1,14 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts"; import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts";
import { computeSpeed } from "../utils/computeSpeed.ts";
export const TerminatorsFM = ( export const TerminatorsFM = (
leg: FMTerminalEntry, leg: FMTerminalEntry,
previousFix: NavFix, previousFix: NavFix,
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const speed = computeSpeed(leg, previousFix);
const endpoint = geolib.computeDestinationPoint( const endpoint = geolib.computeDestinationPoint(
previousFix, previousFix,
(10).toMetre(), (10).toMetre(),
@ -18,6 +21,7 @@ export const TerminatorsFM = (
lastCourse, lastCourse,
previousFix, previousFix,
endpoint, endpoint,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -1,7 +1,7 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts";
import Parser from "../parser.ts"; import Parser from "../parser.ts";
import { computeSpeed } from "../utils/computeSpeed.ts"; import { computeSpeed } from "../utils/computeSpeed.ts";
import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts";
// NOTE: No wind adjustments to be made, no clue how *that* would draw // NOTE: No wind adjustments to be made, no clue how *that* would draw
export const TerminatorsVA = ( export const TerminatorsVA = (
@ -9,32 +9,52 @@ export const TerminatorsVA = (
previousFix: NavFix, previousFix: NavFix,
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const speed = computeSpeed(leg, previousFix);
const crsIntoEndpoint = leg.Course.toTrue(previousFix);
let line: LineSegment[] = [];
if (previousFix.isFlyOver) {
line = generatePerformanceArc(
crsIntoEndpoint,
lastCourse,
previousFix,
speed,
leg.TurnDir
);
} else {
line.push([previousFix.longitude, previousFix.latitude]);
}
const arcEnd = { latitude: line.at(-1)![1], longitude: line.at(-1)![0] };
if (line.length > 1) {
lastCourse = geolib.getGreatCircleBearing(
{
latitude: line.at(-2)![1],
longitude: line.at(-2)![0],
},
arcEnd
);
}
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint( ...geolib.computeDestinationPoint(
previousFix, arcEnd,
( (
((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) /
Parser.AC_VS) * Parser.AC_VS) *
((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60)
).toMetre(), ).toMetre(),
leg.Course.toTrue(previousFix) crsIntoEndpoint
), ),
name: leg.Alt, name: leg.Alt,
isFlyOver: true, isFlyOver: true,
altitude: leg.Alt.parseAltitude(), altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude,
speed: computeSpeed(leg, previousFix), speed: computeSpeed(leg, previousFix),
speedConstraint: leg.SpeedLimit, speedConstraint: leg.SpeedLimit,
altitudeConstraint: leg.Alt, altitudeConstraint: leg.Alt,
}; };
const line = handleTurnAtFix( line.push([targetFix.longitude, targetFix.latitude]);
leg.Course.toTrue(previousFix),
leg.Course.toTrue(previousFix),
lastCourse,
previousFix,
targetFix,
leg.TurnDir
);
return [targetFix, line]; return [targetFix, line];
}; };

View File

@ -50,7 +50,9 @@ export const TerminatorsVD = (
} }
// Navaid in front of us // Navaid in front of us
else { else {
remainingDistance = distToNavaid - remainingDistance; // Navaid will not be passed before distance is hit
if (distToNavaid > remainingDistance)
remainingDistance = distToNavaid - remainingDistance;
} }
const targetFix: NavFix = { const targetFix: NavFix = {
...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse),

View File

@ -11,6 +11,7 @@ export const TerminatorsVI = (
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix); const [crs, nextFix] = getCourseAndFixForIntercepts(nextLeg, previousFix);
const speed = computeSpeed(leg, previousFix);
// Compute INTC // Compute INTC
const interceptFix: NavFix = { const interceptFix: NavFix = {
@ -33,6 +34,7 @@ export const TerminatorsVI = (
lastCourse, lastCourse,
previousFix, previousFix,
interceptFix, interceptFix,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -1,5 +1,6 @@
import * as geolib from "geolib"; import * as geolib from "geolib";
import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts"; import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts";
import { computeSpeed } from "../utils/computeSpeed.ts";
// NOTE: No wind adjustments to be made, no clue how *that* would draw // NOTE: No wind adjustments to be made, no clue how *that* would draw
export const TerminatorsVM = ( export const TerminatorsVM = (
@ -7,6 +8,8 @@ export const TerminatorsVM = (
previousFix: NavFix, previousFix: NavFix,
lastCourse: number lastCourse: number
): [NavFix?, LineSegment[]?] => { ): [NavFix?, LineSegment[]?] => {
const speed = computeSpeed(leg, previousFix);
const endpoint = geolib.computeDestinationPoint( const endpoint = geolib.computeDestinationPoint(
previousFix, previousFix,
(10).toMetre(), (10).toMetre(),
@ -19,6 +22,7 @@ export const TerminatorsVM = (
lastCourse, lastCourse,
previousFix, previousFix,
endpoint, endpoint,
speed,
leg.TurnDir leg.TurnDir
); );

View File

@ -38,7 +38,7 @@ String.prototype.parseAltitude = function () {
case 12: { case 12: {
const upper = Number.parseInt(this.substring(0, 5)); const upper = Number.parseInt(this.substring(0, 5));
const lower = Number.parseInt(this.substring(6, 12)); const lower = Number.parseInt(this.substring(6, 12));
return (upper - lower) / 2; return lower + (upper - lower) / 2;
} }
default: default:
return -1; return -1;