diff --git a/README.md b/README.md index 5a965af..ca8a312 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,8 @@ LGAV BIBE1L SID (Cycle 2507, ID 10654) ### Instructions - From preceding fix, fly along `Course` until reaching `Alt`. +- If preceding fix was overfly, offset outbound `Course` to end of turn (tangent). +- Earliest leg terminations is at point of tangent. ### Units @@ -84,6 +86,8 @@ LGAV BIBE2F SID (Cycle 2507, ID 10657) - From preceding fix, fly along `Course` until reaching `Distance` from the navaid identified by `NavID`. +- If preceding fix was overfly, offset outbound `Course` to end of turn (tangent). +- Earliest leg terminations is at point of tangent. ### Units @@ -116,6 +120,7 @@ LGAV BIBE2F SID (Cycle 2507, ID 10657) - From preceding fix, fly `Course` to the fix identified by (`WptID`, `WptLat`, `WptLon`) *or* (`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`). +- If preceding fix was overfly, continue turn until intercept `Course`, then continue on `Course`. ### Units @@ -208,7 +213,7 @@ LGAV KOR1D SID (Cycle 2507, ID 10679) - `IsFlyOver`: Boolean indicating if fix is a mandatory fly over. -## Fix to Altitude (FA) +## Track from Fix to Altitude (FA) ### Example @@ -227,6 +232,8 @@ LGAV BIBE2F SID (Cycle 2507, ID 10657) - From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or* (`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` until reaching `Alt`. +- If preceding fix was overfly, continue turn until intercept `Course`, then continue on `Course`. +- Earliest leg terminations is at point of intercept. ### Units @@ -261,6 +268,9 @@ LIED CAR6F SID (Cycle 2507, ID 11798) - From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or* (`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` until reaching `Distance`. +- If preceding fix was overfly, continue turn until intercept `Course`, then continue on `Course`. +- Earliest leg terminations is at point of intercept. + ### Units @@ -292,8 +302,10 @@ LGAV BIBE2T SID (Cycle 2507, ID 10659) ### Instructions - From the fix identified by (`WptID`, `WptLat`, `WptLon`) *or* - (`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` until - reaching `Distance` from navaid identified by `NavID`. + (`NavID`, `NavLat`, `NavLon`, `NavDist`, `NavBear`), fly `Course` until reaching `Distance` from + navaid identified by `NavID`. +- If preceding fix was overfly, continue turn until intercept `Course`, then continue on `Course`. +- Earliest leg terminations is at point of intercept. ### Units diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e1c6495 --- /dev/null +++ b/TODO.md @@ -0,0 +1,2 @@ +- Revisit CA/FA/VA legs +- Revisit CD/FD/VD/FC legs \ No newline at end of file diff --git a/browser/src/App.tsx b/browser/src/App.tsx index 4545c5b..2280d23 100644 --- a/browser/src/App.tsx +++ b/browser/src/App.tsx @@ -8,7 +8,9 @@ import L from "leaflet"; const parser = await Parser.instance(); -const terminals = [10394, 10395, 10475, 10480, 10482, 10485, 10653, 10654]; +const terminals = [ + 10394, 10395, 10475, 10480, 10482, 10485, 10653, 10654, 10657, 10659, +]; function App() { const [selectedTerminal, setSelectedTerminal] = useState(terminals[0]); @@ -106,6 +108,7 @@ function App() { display: "flex", flexDirection: "column", gap: "10px", + width: "max-content", }} > {terminals.map((terminal) => ( @@ -113,14 +116,18 @@ function App() { key={terminal} style={{ display: "flex", - gap: "10px", + flexDirection: "column", background: selectedTerminal === terminal ? "#eeeeee" : undefined, }} + onClick={() => setSelectedTerminal(terminal)} > -
ID {terminal}
- + + {(() => { + const t = parser.terminals.find(({ ID }) => ID === terminal); + return `${t?.ICAO} - ${t?.FullName}`; + })()} + +
({terminal})
))} diff --git a/browser/src/parser/node.ts b/browser/src/parser/node.ts index 283e6e6..39d7f14 100644 --- a/browser/src/parser/node.ts +++ b/browser/src/parser/node.ts @@ -14,4 +14,4 @@ fetch = async (path: string) => { const parser = await Parser.instance(); -console.log(JSON.stringify(await parser.parse(10654))); +console.log(JSON.stringify(await parser.parse(10657))); diff --git a/browser/src/parser/parser.ts b/browser/src/parser/parser.ts index 377e4a7..6ce96c1 100644 --- a/browser/src/parser/parser.ts +++ b/browser/src/parser/parser.ts @@ -14,25 +14,29 @@ import { TerminatorsVD } from "./terminators/VD.ts"; import { TerminatorsRF } from "./terminators/RF.ts"; import { TerminatorsCA } from "./terminators/CA.ts"; import { TerminatorsDF } from "./terminators/DF.ts"; +import { TerminatorsFD } from "./terminators/FD.ts"; +import { TerminatorsFA } from "./terminators/FA.ts"; +import { TerminatorsCD } from "./terminators/CD.ts"; class Parser { private static _instance: Parser; - private waypoints: Waypoint[]; - private runways: Runway[]; - private terminals: Terminal[]; + private _waypoints: Waypoint[]; + private _runways: Runway[]; + private _terminals: Terminal[]; public static AC_SPEED = 250; - public static AC_VS = 1500; + public static AC_BANK = 30; + public static AC_VS = 1400; private constructor( waypoints: Waypoint[], runways: Runway[], terminals: Terminal[] ) { - this.waypoints = waypoints; - this.runways = runways; - this.terminals = terminals; + this._waypoints = waypoints; + this._runways = runways; + this._terminals = terminals; } public static instance = async () => { @@ -47,6 +51,16 @@ class Parser { return Parser._instance; }; + public get terminals() { + return this._terminals; + } + public get waypoints() { + return this._waypoints; + } + public get runways() { + return this._runways; + } + public parse = async (terminalID: number) => { // Private functions /** @@ -132,9 +146,15 @@ class Parser { update(fixToAdd, lineToAdd); break; } - case "CD": - console.error("Unknown TrackCode", leg.TrackCode); + case "CD": { + const [fixToAdd, lineToAdd] = TerminatorsCD( + leg as CDTerminalEntry, + previousFix, + lastCourse + ); + update(fixToAdd, lineToAdd); break; + } case "CF": { const [fixToAdd, lineToAdd] = TerminatorsCF( leg as CFTerminalEntry, @@ -174,11 +194,27 @@ class Parser { update(fixToAdd, lineToAdd); break; } - case "FA": + case "FA": { + const [fixToAdd, lineToAdd] = TerminatorsFA( + leg as FATerminalEntry, + previousFix, + lastCourse + ); + update(fixToAdd, lineToAdd); + break; + } case "FC": - case "FD": console.error("Unknown TrackCode", leg.TrackCode); break; + case "FD": { + const [fixToAdd, lineToAdd] = TerminatorsFD( + leg as FDTerminalEntry, + previousFix, + lastCourse + ); + update(fixToAdd, lineToAdd); + break; + } case "FM": { const [fixToAdd, lineToAdd] = TerminatorsFM( leg as FMTerminalEntry, diff --git a/browser/src/parser/pathGenerators/generatePerformanceArc.ts b/browser/src/parser/pathGenerators/generatePerformanceArc.ts index 289b76e..a839c07 100644 --- a/browser/src/parser/pathGenerators/generatePerformanceArc.ts +++ b/browser/src/parser/pathGenerators/generatePerformanceArc.ts @@ -1,10 +1,12 @@ import * as geolib from "geolib"; import Parser from "../parser.ts"; +import { computeTurnRate } from "../utils/computeTurnRate.ts"; /** * @param crsIntoEndpoint Course into arc endpoint * @param crsFromOrigin Course from arc origin point * @param start Arc origin point + * @param speed Speed within arc * @param turnDir Turn direction * @returns Line segments */ @@ -12,12 +14,15 @@ export const generatePerformanceArc = ( crsIntoEndpoint: number, crsFromOrigin: number, start: NavFix, + speed: number, turnDir?: TurnDirection ) => { + const turnRate = computeTurnRate(speed, Parser.AC_BANK); + const line: LineSegment[] = [[start.longitude, start.latitude]]; // Check if there even is an arc - if (crsIntoEndpoint !== crsFromOrigin) { + if (!crsFromOrigin.equal(crsIntoEndpoint)) { // Turn Dir if (!turnDir || turnDir === "E") { let prov = crsFromOrigin - crsIntoEndpoint; @@ -26,15 +31,18 @@ export const generatePerformanceArc = ( } // Generate arc - while (crsFromOrigin !== crsIntoEndpoint) { + while (!crsFromOrigin.equal(crsIntoEndpoint)) { + let time = 0; if (turnDir === "R") { const delta = (crsIntoEndpoint - crsFromOrigin).normaliseDegrees(); - crsFromOrigin += delta < 1 ? delta : 1; - crsFromOrigin = crsFromOrigin.normaliseDegrees(); + const increment = delta < 1 ? delta : 1; + crsFromOrigin = (crsFromOrigin + increment).normaliseDegrees(); + time = increment / turnRate; } else { const delta = (crsFromOrigin - crsIntoEndpoint).normaliseDegrees(); - crsFromOrigin -= delta < 1 ? delta : 1; - crsFromOrigin = crsFromOrigin.normaliseDegrees(); + const increment = delta < 1 ? delta : 1; + crsFromOrigin = (crsFromOrigin - increment).normaliseDegrees(); + time = increment / turnRate; } const arcFix = geolib.computeDestinationPoint( @@ -42,7 +50,7 @@ export const generatePerformanceArc = ( latitude: line.at(-1)![1], longitude: line.at(-1)![0], }, - ((start.speed ?? Parser.AC_SPEED) / 3600).toMetre(), + ((speed / 3600) * time).toMetre(), crsFromOrigin ); diff --git a/browser/src/parser/pathGenerators/handleTurnAtFix.ts b/browser/src/parser/pathGenerators/handleTurnAtFix.ts index 9f54d60..7845816 100644 --- a/browser/src/parser/pathGenerators/handleTurnAtFix.ts +++ b/browser/src/parser/pathGenerators/handleTurnAtFix.ts @@ -1,6 +1,7 @@ import * as geolib from "geolib"; import { generateTangentArc } from "./generateTangentArc.ts"; import { generatePerformanceArc } from "./generatePerformanceArc.ts"; +import Parser from "../parser.ts"; /** * @param crsIntoEndpoint Course into endpoint @@ -35,6 +36,7 @@ export const handleTurnAtFix = ( crsIntoIntercept, crsFromOrigin, start, + start.speed ? start.speed : Parser.AC_SPEED, turnDir ); diff --git a/browser/src/parser/terminators/CA.ts b/browser/src/parser/terminators/CA.ts index 36cd2fb..fb6ac9b 100644 --- a/browser/src/parser/terminators/CA.ts +++ b/browser/src/parser/terminators/CA.ts @@ -14,7 +14,7 @@ export const TerminatorsCA = ( ( ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) * - (Parser.AC_SPEED / 60) + ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) ).toMetre(), leg.Course.toTrue(previousFix) ), diff --git a/browser/src/parser/terminators/CD.ts b/browser/src/parser/terminators/CD.ts new file mode 100644 index 0000000..d73f20b --- /dev/null +++ b/browser/src/parser/terminators/CD.ts @@ -0,0 +1,67 @@ +import * as geolib from "geolib"; +import { computeSpeed } from "../utils/computeSpeed.ts"; +import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts"; + +// NOTE: Distance not adjusted for altitude in this demo +export const TerminatorsCD = ( + leg: CDTerminalEntry, + previousFix: NavFix, + lastCourse: number +): [NavFix?, LineSegment[]?] => { + const navaid = { + latitude: leg.NavLat, + longitude: leg.NavLon, + }; + const speed = computeSpeed(leg, previousFix); + + let line: LineSegment[] = []; + if (previousFix.isFlyOver) { + const crsIntoEndpoint = leg.Course.toTrue(previousFix); + + 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 crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid); + const distToNavaid = geolib.getDistance(arcEnd, navaid); + let remainingDistance = leg.Distance.toMetre(); + // Navaid behind us + if (Math.abs(crsToNavaid - lastCourse) > 90) { + remainingDistance -= distToNavaid; + } + // Navaid in front of us + else { + remainingDistance = distToNavaid - remainingDistance; + } + const targetFix: NavFix = { + ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), + name: leg.Distance.toString(), + isFlyOver: true, + altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude, + speed: computeSpeed(leg, previousFix), + speedConstraint: leg.SpeedLimit, + altitudeConstraint: leg.Alt, + }; + + line.push([targetFix.longitude, targetFix.latitude]); + + return [targetFix, line]; +}; diff --git a/browser/src/parser/terminators/DF.ts b/browser/src/parser/terminators/DF.ts index b05a5d9..326b289 100644 --- a/browser/src/parser/terminators/DF.ts +++ b/browser/src/parser/terminators/DF.ts @@ -1,6 +1,6 @@ import * as geolib from "geolib"; import { computeSpeed } from "../utils/computeSpeed.ts"; -import Parser from "../parser.ts"; +import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts"; export const TerminatorsDF = ( leg: DFTerminalEntry, @@ -8,58 +8,35 @@ export const TerminatorsDF = ( lastCourse: number, waypoint?: Waypoint ): [NavFix?, LineSegment[]?] => { + const speed = computeSpeed(leg, previousFix); + const targetFix: NavFix = { latitude: leg.WptLat, longitude: leg.WptLon, name: waypoint?.Ident ?? undefined, isFlyOver: leg.IsFlyOver, altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude, - speed: computeSpeed(leg, previousFix), + speed: speed, speedConstraint: leg.SpeedLimit, altitudeConstraint: leg.Alt, }; - const line: LineSegment[] = [[previousFix.longitude, previousFix.latitude]]; - + let line: LineSegment[] = []; if (previousFix.isFlyOver) { const crsIntoEndpoint = geolib.getGreatCircleBearing( previousFix, targetFix ); - // Check if there even is an arc - if (crsIntoEndpoint !== lastCourse) { - // Turn Dir - if (!leg.TurnDir || leg.TurnDir === "E") { - let prov = lastCourse - crsIntoEndpoint; - prov = prov > 180 ? prov - 360 : prov <= -180 ? prov + 360 : prov; - leg.TurnDir = prov > 0 ? "L" : "R"; - } - - // Generate arc - while (lastCourse !== crsIntoEndpoint) { - if (leg.TurnDir === "R") { - const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees(); - lastCourse += delta < 1 ? delta : 1; - lastCourse = lastCourse.normaliseDegrees(); - } else { - const delta = (lastCourse - crsIntoEndpoint).normaliseDegrees(); - lastCourse -= delta < 1 ? delta : 1; - lastCourse = lastCourse.normaliseDegrees(); - } - - const arcFix = geolib.computeDestinationPoint( - { - latitude: line.at(-1)![1], - longitude: line.at(-1)![0], - }, - ((previousFix.speed ?? Parser.AC_SPEED) / 3600).toMetre(), - lastCourse - ); - - line.push([arcFix.longitude, arcFix.latitude]); - } - } + line = generatePerformanceArc( + crsIntoEndpoint, + lastCourse, + previousFix, + speed, + leg.TurnDir + ); + } else { + line.push([previousFix.longitude, previousFix.latitude]); } line.push([targetFix.longitude, targetFix.latitude]); diff --git a/browser/src/parser/terminators/FA.ts b/browser/src/parser/terminators/FA.ts new file mode 100644 index 0000000..00ad68b --- /dev/null +++ b/browser/src/parser/terminators/FA.ts @@ -0,0 +1,39 @@ +import * as geolib from "geolib"; +import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.ts"; +import { computeSpeed } from "../utils/computeSpeed.ts"; +import Parser from "../parser.ts"; + +export const TerminatorsFA = ( + leg: FATerminalEntry, + previousFix: NavFix, + lastCourse: number +): [NavFix?, LineSegment[]?] => { + const targetFix: NavFix = { + ...geolib.computeDestinationPoint( + { latitude: leg.WptLat, longitude: leg.WptLon }, + ( + ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / + Parser.AC_VS) * + ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) + ).toMetre(), + leg.Course.toTrue(previousFix) + ), + name: leg.Alt, + isFlyOver: true, + altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude, + speed: computeSpeed(leg, previousFix), + speedConstraint: leg.SpeedLimit, + altitudeConstraint: leg.Alt, + }; + + const line = handleTurnAtFix( + leg.Course.toTrue(previousFix), + leg.Course.toTrue(previousFix), + lastCourse, + previousFix, + targetFix, + leg.TurnDir + ); + + return [targetFix, line]; +}; diff --git a/browser/src/parser/terminators/FD.ts b/browser/src/parser/terminators/FD.ts new file mode 100644 index 0000000..2702210 --- /dev/null +++ b/browser/src/parser/terminators/FD.ts @@ -0,0 +1,71 @@ +import * as geolib from "geolib"; +import { computeSpeed } from "../utils/computeSpeed.ts"; +import { generatePerformanceArc } from "../pathGenerators/generatePerformanceArc.ts"; + +// NOTE: Distance not adjusted for altitude in this demo +export const TerminatorsFD = ( + leg: FDTerminalEntry, + previousFix: NavFix, + lastCourse: number +): [NavFix?, LineSegment[]?] => { + const refFix = { + latitude: leg.WptLat, + longitude: leg.WptLon, + }; + const navaid = { + latitude: leg.NavLat, + longitude: leg.NavLon, + }; + const speed = computeSpeed(leg, previousFix); + + let line: LineSegment[] = []; + if (previousFix.isFlyOver) { + const crsIntoEndpoint = leg.Course.toTrue(refFix); + + 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 crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid); + const distToNavaid = geolib.getDistance(arcEnd, navaid); + let remainingDistance = leg.Distance.toMetre(); + // Navaid behind us + if (Math.abs(crsToNavaid - lastCourse) > 90) { + remainingDistance -= distToNavaid; + } + // Navaid in front of us + else { + remainingDistance = distToNavaid - remainingDistance; + } + const targetFix: NavFix = { + ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), + name: leg.Distance.toString(), + isFlyOver: true, + altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude, + speed: computeSpeed(leg, previousFix), + speedConstraint: leg.SpeedLimit, + altitudeConstraint: leg.Alt, + }; + + line.push([targetFix.longitude, targetFix.latitude]); + + return [targetFix, line]; +}; diff --git a/browser/src/parser/terminators/TF.ts b/browser/src/parser/terminators/TF.ts index 1e72cb9..153b546 100644 --- a/browser/src/parser/terminators/TF.ts +++ b/browser/src/parser/terminators/TF.ts @@ -39,6 +39,7 @@ export const TerminatorsTF = ( } // Generate arc + let condition = false; do { if (leg.TurnDir === "R") { const delta = (crsIntoEndpoint - lastCourse).normaliseDegrees(); @@ -55,14 +56,22 @@ export const TerminatorsTF = ( latitude: line.at(-1)![1], longitude: line.at(-1)![0], }, - ((previousFix.speed ?? Parser.AC_SPEED) / 3600).toMetre(), + ( + (previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 3600 + ).toMetre(), lastCourse ); line.push([arcFix.longitude, arcFix.latitude]); crsIntoEndpoint = geolib.getGreatCircleBearing(arcFix, targetFix); - } while (crsIntoEndpoint > trackIntoEndpoint); + + if (leg.TurnDir === "R") { + condition = crsIntoEndpoint > trackIntoEndpoint; + } else { + condition = crsIntoEndpoint < trackIntoEndpoint; + } + } while (condition); } } diff --git a/browser/src/parser/terminators/VA.ts b/browser/src/parser/terminators/VA.ts index f168db4..8e9a3f8 100644 --- a/browser/src/parser/terminators/VA.ts +++ b/browser/src/parser/terminators/VA.ts @@ -15,7 +15,7 @@ export const TerminatorsVA = ( ( ((leg.Alt.parseAltitude() - (previousFix.altitude ?? 0)) / Parser.AC_VS) * - (Parser.AC_SPEED / 60) + ((previousFix.speed ? previousFix.speed : Parser.AC_SPEED) / 60) ).toMetre(), leg.Course.toTrue(previousFix) ), diff --git a/browser/src/parser/terminators/VD.ts b/browser/src/parser/terminators/VD.ts index 4d48392..e706abc 100644 --- a/browser/src/parser/terminators/VD.ts +++ b/browser/src/parser/terminators/VD.ts @@ -1,19 +1,59 @@ import * as geolib from "geolib"; -import { handleTurnAtFix } from "../pathGenerators/handleTurnAtFix.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: Distance not adjusted for altitude in this demo export const TerminatorsVD = ( leg: VDTerminalEntry, previousFix: NavFix, lastCourse: number ): [NavFix?, LineSegment[]?] => { - const targetFix: NavFix = { - ...geolib.computeDestinationPoint( + const navaid = { + latitude: leg.NavLat, + longitude: leg.NavLon, + }; + const speed = computeSpeed(leg, previousFix); + + let line: LineSegment[] = []; + if (previousFix.isFlyOver) { + const crsIntoEndpoint = leg.Course.toTrue(previousFix); + + line = generatePerformanceArc( + crsIntoEndpoint, + lastCourse, previousFix, - leg.Distance.toMetre(), - leg.Course.toTrue(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 crsToNavaid = geolib.getGreatCircleBearing(arcEnd, navaid); + const distToNavaid = geolib.getDistance(arcEnd, navaid); + let remainingDistance = leg.Distance.toMetre(); + // Navaid behind us + if (Math.abs(crsToNavaid - lastCourse) > 90) { + remainingDistance -= distToNavaid; + } + // Navaid in front of us + else { + remainingDistance = distToNavaid - remainingDistance; + } + const targetFix: NavFix = { + ...geolib.computeDestinationPoint(arcEnd, remainingDistance, lastCourse), name: leg.Distance.toString(), isFlyOver: true, altitude: leg.Alt ? leg.Alt.parseAltitude() : previousFix.altitude, @@ -22,14 +62,7 @@ export const TerminatorsVD = ( altitudeConstraint: leg.Alt, }; - const line = handleTurnAtFix( - leg.Course.toTrue(previousFix), - leg.Course.toTrue(previousFix), - lastCourse, - previousFix, - targetFix, - leg.TurnDir - ); + line.push([targetFix.longitude, targetFix.latitude]); return [targetFix, line]; }; diff --git a/browser/src/parser/utils/computeTurnRate.ts b/browser/src/parser/utils/computeTurnRate.ts new file mode 100644 index 0000000..d612988 --- /dev/null +++ b/browser/src/parser/utils/computeTurnRate.ts @@ -0,0 +1,3 @@ +export const computeTurnRate = (speed: number, bank: number) => { + return (1091 * Math.tan(bank.toRadians())) / speed; +}; diff --git a/browser/src/parser/utils/extensions.ts b/browser/src/parser/utils/extensions.ts index 954a86d..163c1a1 100644 --- a/browser/src/parser/utils/extensions.ts +++ b/browser/src/parser/utils/extensions.ts @@ -25,6 +25,9 @@ Number.prototype.toTrue = function (fix) { Number.prototype.toMetre = function () { return (this as number) * 1852.0; }; +Number.prototype.equal = function (other: number) { + return Math.abs((this as number) - other) < 0.1; +}; String.prototype.parseAltitude = function () { switch (this.length) { diff --git a/browser/src/types/extensions.d.ts b/browser/src/types/extensions.d.ts index 10476b9..8670fcf 100644 --- a/browser/src/types/extensions.d.ts +++ b/browser/src/types/extensions.d.ts @@ -25,6 +25,10 @@ export declare global { * @returns Value converted from nmi to metres */ toMetre: () => number; + /** + * @returns True if delta is less than 0.1 + */ + equal: (other: number) => boolean; } interface String { diff --git a/browser/src/types/terminators/CD.d.ts b/browser/src/types/terminators/CD.d.ts new file mode 100644 index 0000000..081c4be --- /dev/null +++ b/browser/src/types/terminators/CD.d.ts @@ -0,0 +1,6 @@ +export declare global { + type CDTerminalEntry = Required< + Pick + > & + TerminalEntry; +} diff --git a/browser/src/types/terminators/FA.d.ts b/browser/src/types/terminators/FA.d.ts new file mode 100644 index 0000000..8e20c78 --- /dev/null +++ b/browser/src/types/terminators/FA.d.ts @@ -0,0 +1,18 @@ +export declare global { + type FATerminalEntry = Required< + Pick< + TerminalEntry, + | "WptID" + | "WptLat" + | "WptLon" + | "NavID" + | "NavLat" + | "NavLon" + | "NavBear" + | "NavDist" + | "Course" + | "Alt" + > + > & + TerminalEntry; +} diff --git a/browser/src/types/terminators/FD.d.ts b/browser/src/types/terminators/FD.d.ts new file mode 100644 index 0000000..84e5f4a --- /dev/null +++ b/browser/src/types/terminators/FD.d.ts @@ -0,0 +1,18 @@ +export declare global { + type FDTerminalEntry = Required< + Pick< + TerminalEntry, + | "WptID" + | "WptLat" + | "WptLon" + | "NavID" + | "NavLat" + | "NavLon" + | "NavBear" + | "NavDist" + | "Course" + | "Distance" + > + > & + TerminalEntry; +}