Fix more CF

This commit is contained in:
Kilian Hofmann 2025-07-19 15:14:46 +02:00
parent e9bab0306f
commit 73373420d3
10 changed files with 48 additions and 28 deletions

View File

@ -91,6 +91,11 @@ export const Map: FC<MapProps> = ({ airport, chart, procedures }) => {
shape: 'triangle', shape: 'triangle',
radius: 6, radius: 6,
}); });
else if (properties.isInitial)
return L.shapeMarker(latlng, {
shape: 'arrowhead',
radius: 6,
});
return L.shapeMarker(latlng, { return L.shapeMarker(latlng, {
shape: 'star-4', shape: 'star-4',

View File

@ -145,10 +145,12 @@ export const ProcedureSelect: FC<ProcedureSelectProps> = ({
key={terminal.ID} key={terminal.ID}
className="cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2 disabled:bg-gray-100" className="cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2 disabled:bg-gray-100"
onClick={() => { onClick={() => {
setInFlight(terminal.ID);
parser.loadTerminal(terminal.ID).then(() => { parser.loadTerminal(terminal.ID).then(() => {
setSelectedTerminal(terminal); setSelectedTerminal(terminal);
const transitions = new Set(parser.procedures.map((proc) => proc.Transition)); const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
handleSelection(Array.from(transitions), terminal); handleSelection(Array.from(transitions), terminal);
setInFlight(undefined);
}); });
}} }}
disabled={!!inFlight} disabled={!!inFlight}
@ -167,10 +169,12 @@ export const ProcedureSelect: FC<ProcedureSelectProps> = ({
key={terminal.ID} key={terminal.ID}
className="cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2 disabled:bg-gray-100" className="cursor-pointer rounded border border-gray-300 bg-gray-300 px-2 py-1 font-semibold focus:outline-2 focus-visible:outline-2 disabled:bg-gray-100"
onClick={() => { onClick={() => {
setInFlight(terminal.ID);
parser.loadTerminal(terminal.ID).then(() => { parser.loadTerminal(terminal.ID).then(() => {
setSelectedTerminal(terminal); setSelectedTerminal(terminal);
const transitions = new Set(parser.procedures.map((proc) => proc.Transition)); const transitions = new Set(parser.procedures.map((proc) => proc.Transition));
handleSelection(Array.from(transitions), terminal); handleSelection(Array.from(transitions), terminal);
setInFlight(undefined);
}); });
}} }}
disabled={!!inFlight} disabled={!!inFlight}

View File

@ -1,4 +1,5 @@
import computeDestinationPoint from 'geolib/es/computeDestinationPoint'; import computeDestinationPoint from 'geolib/es/computeDestinationPoint';
import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
import getPreciseDistance from 'geolib/es/getPreciseDistance'; import getPreciseDistance from 'geolib/es/getPreciseDistance';
/** /**
@ -6,7 +7,8 @@ import getPreciseDistance from 'geolib/es/getPreciseDistance';
* @param crsIntoOrigin Course into arc origin point * @param crsIntoOrigin Course into arc origin point
* @param start Arc origin point * @param start Arc origin point
* @param center Arc center point * @param center Arc center point
* @param turnDir * @param end Arc end point
* @param turnDir Turn Direction
* @returns Line segments * @returns Line segments
*/ */
export const generateRFArc = ( export const generateRFArc = (
@ -14,6 +16,7 @@ export const generateRFArc = (
crsIntoOrigin: number, crsIntoOrigin: number,
start: NavFix, start: NavFix,
center: NavFix, center: NavFix,
end: NavFix,
turnDir: TurnDirection turnDir: TurnDirection
) => { ) => {
const line: LineSegment[] = [[start.longitude, start.latitude]]; const line: LineSegment[] = [[start.longitude, start.latitude]];
@ -26,20 +29,10 @@ export const generateRFArc = (
turnDir = prov > 0 ? 'L' : 'R'; turnDir = prov > 0 ? 'L' : 'R';
} }
let crsOrthogonalOnOrigin; let crsOrthogonalOnOrigin = getGreatCircleBearing(center, start);
let crsOrthogonalOnEndpoint; const crsOrthogonalOnEndpoint = getGreatCircleBearing(center, end);
if (turnDir === 'R') {
crsOrthogonalOnOrigin = (crsIntoOrigin + 90).normaliseDegrees();
crsOrthogonalOnEndpoint = (crsIntoEndpoint + 90).normaliseDegrees();
} else {
crsOrthogonalOnOrigin = (crsIntoOrigin - 90).normaliseDegrees();
crsOrthogonalOnEndpoint = (crsIntoEndpoint - 90).normaliseDegrees();
}
const arcRad = getPreciseDistance(center, start); const arcRad = getPreciseDistance(center, start);
crsOrthogonalOnOrigin = crsOrthogonalOnOrigin.reciprocalCourse();
crsOrthogonalOnEndpoint = crsOrthogonalOnEndpoint.reciprocalCourse();
// Start turn immediately // Start turn immediately
if (turnDir === 'R') { if (turnDir === 'R') {
crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1; crsOrthogonalOnOrigin += crsOrthogonalOnOrigin < 1 ? crsOrthogonalOnOrigin : 1;

View File

@ -63,7 +63,7 @@ export const generateTangentArc = (
intcArcOnCrsIntoEndpoint, intcArcOnCrsIntoEndpoint,
crsOrthogonalOnEndpoint crsOrthogonalOnEndpoint
); );
if (Math.abs(crsOrthogonalOnEndpoint - crsOrthogonalOnOrigin) <= 0.1 && arcCenter) if (!crsOrthogonalOnEndpoint.equal(crsOrthogonalOnOrigin) && arcCenter)
arcRad = getPreciseDistance(arcCenter, start); arcRad = getPreciseDistance(arcCenter, start);
else { else {
arcRad = getPreciseDistance(start, end) / 2; arcRad = getPreciseDistance(start, end) / 2;

View File

@ -3,6 +3,7 @@ import getGreatCircleBearing from 'geolib/es/getGreatCircleBearing';
import getPreciseDistance from 'geolib/es/getPreciseDistance'; import getPreciseDistance from 'geolib/es/getPreciseDistance';
import Parser from '../parser'; import Parser from '../parser';
import { generateTangentArc } from '../pathGenerators/generateTangentArc'; import { generateTangentArc } from '../pathGenerators/generateTangentArc';
import { computeIntersection } from '../utils/computeIntersection';
import { computeSpeed } from '../utils/computeSpeed'; import { computeSpeed } from '../utils/computeSpeed';
import { computeTurnRate } from '../utils/computeTurnRate'; import { computeTurnRate } from '../utils/computeTurnRate';
@ -28,7 +29,6 @@ export const TerminatorsCF = (
IsFAF: leg.IsFAF, IsFAF: leg.IsFAF,
IsMAP: leg.IsMAP, IsMAP: leg.IsMAP,
}; };
const crsToIntercept = leg.Course.toTrue(targetFix);
// Compute overfly arc // Compute overfly arc
let arc1: LineSegment[] | null = null; let arc1: LineSegment[] | null = null;
@ -59,7 +59,13 @@ export const TerminatorsCF = (
if (endDist <= 25 || (endCrs <= crsIntoEndpoint + 1 && endCrs >= crsIntoEndpoint - 1)) arc = arc1; if (endDist <= 25 || (endCrs <= crsIntoEndpoint + 1 && endCrs >= crsIntoEndpoint - 1)) arc = arc1;
} }
if (!arc && previousFix.isFlyOver && (!lastCourse.equal(crsIntoEndpoint) || !lastCourse.equal(crsToIntercept))) { const intercept = computeIntersection(previousFix, lastCourse, targetFix, crsIntoEndpoint.reciprocalCourse());
if (
!arc &&
previousFix.isFlyOver &&
(!lastCourse.equal(crsIntoEndpoint) || !lastCourse.equal(crsIntoEndpoint)) &&
!(intercept?.latitude === targetFix.latitude && intercept.longitude === targetFix.longitude)
) {
const turnRate = computeTurnRate(speed, Parser.AC_BANK); const turnRate = computeTurnRate(speed, Parser.AC_BANK);
let updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix); let updatedCrsToIntercept = getGreatCircleBearing(previousFix, targetFix);
@ -72,19 +78,26 @@ export const TerminatorsCF = (
// Generate arc // Generate arc
let lastDistance = getPreciseDistance(previousFix, targetFix); let lastDistance = getPreciseDistance(previousFix, targetFix);
while (!updatedCrsToIntercept.equal(crsToIntercept)) { const reversal = Math.abs(lastCourse - crsIntoEndpoint) >= 90;
while (!updatedCrsToIntercept.equal(crsIntoEndpoint)) {
let interceptAngle = 0; let interceptAngle = 0;
if (leg.TurnDir === 'R') interceptAngle = Math.abs(lastCourse - crsToIntercept); if (leg.TurnDir === 'R') interceptAngle = Math.abs(lastCourse - crsIntoEndpoint);
else interceptAngle = Math.abs(crsToIntercept - lastCourse); else interceptAngle = Math.abs(crsIntoEndpoint - lastCourse);
let time = 0; let time = 0;
const increment = 0.1; const increment = 0.1;
if (interceptAngle < 44.9 || interceptAngle >= 45.1) { if (interceptAngle < 44.9 || interceptAngle >= 45.1) {
if (leg.TurnDir === 'R') { if (leg.TurnDir === 'R') {
lastCourse = (lastCourse + increment).normaliseDegrees(); lastCourse =
reversal || updatedCrsToIntercept >= crsIntoEndpoint
? (lastCourse + increment).normaliseDegrees()
: lastCourse;
time = increment / turnRate; time = increment / turnRate;
} else { } else {
lastCourse = (lastCourse - increment).normaliseDegrees(); lastCourse =
reversal || updatedCrsToIntercept <= crsIntoEndpoint
? (lastCourse - increment).normaliseDegrees()
: lastCourse;
time = increment / turnRate; time = increment / turnRate;
} }
} else time = increment / turnRate; } else time = increment / turnRate;

View File

@ -12,6 +12,7 @@ export const TerminatorsIF = (leg: IFTerminalEntry, waypoint?: Waypoint): NavFix
altitudeConstraint: leg.Alt, altitudeConstraint: leg.Alt,
IsFAF: leg.IsFAF, IsFAF: leg.IsFAF,
IsMAP: leg.IsMAP, IsMAP: leg.IsMAP,
isInitial: true,
}; };
return targetFix; return targetFix;

View File

@ -26,6 +26,7 @@ export const TerminatorsRF = (
lastCourse, lastCourse,
previousFix, previousFix,
{ latitude: leg.CenterLat, longitude: leg.CenterLon }, { latitude: leg.CenterLat, longitude: leg.CenterLon },
targetFix,
leg.TurnDir leg.TurnDir
); );

View File

@ -80,10 +80,12 @@ export const TerminatorsTF = (
const increment = 0.1; const increment = 0.1;
if (interceptAngle < 44.9 || interceptAngle >= 45.1) { if (interceptAngle < 44.9 || interceptAngle >= 45.1) {
if (leg.TurnDir === 'R') { if (leg.TurnDir === 'R') {
lastCourse = (lastCourse + increment).normaliseDegrees(); lastCourse =
updatedCrsToIntercept >= crsIntoEndpoint ? (lastCourse + increment).normaliseDegrees() : lastCourse;
time = increment / turnRate; time = increment / turnRate;
} else { } else {
lastCourse = (lastCourse - increment).normaliseDegrees(); lastCourse =
updatedCrsToIntercept >= crsIntoEndpoint ? (lastCourse - increment).normaliseDegrees() : lastCourse;
time = increment / turnRate; time = increment / turnRate;
} }
} else time = increment / turnRate; } else time = increment / turnRate;

View File

@ -38,16 +38,16 @@ Number.prototype.toTrue = function (fix) {
let lowerLatLowerLonMagVar = (magVarTable[lowerLatLowerLonOffset + 1] << 8) + magVarTable[lowerLatLowerLonOffset]; let lowerLatLowerLonMagVar = (magVarTable[lowerLatLowerLonOffset + 1] << 8) + magVarTable[lowerLatLowerLonOffset];
lowerLatLowerLonMagVar = (lowerLatLowerLonMagVar * 0x168) / 0x10000; lowerLatLowerLonMagVar = (lowerLatLowerLonMagVar * 0x168) / 0x10000;
lowerLatLowerLonMagVar = lowerLatLowerLonMagVar > 180 ? lowerLatLowerLonMagVar - 460 : lowerLatLowerLonMagVar; lowerLatLowerLonMagVar = lowerLatLowerLonMagVar > 180 ? lowerLatLowerLonMagVar - 360 : lowerLatLowerLonMagVar;
let lowerLatUpperLonMagVar = (magVarTable[lowerLatUpperLonOffset + 1] << 8) + magVarTable[lowerLatUpperLonOffset]; let lowerLatUpperLonMagVar = (magVarTable[lowerLatUpperLonOffset + 1] << 8) + magVarTable[lowerLatUpperLonOffset];
lowerLatUpperLonMagVar = (lowerLatUpperLonMagVar * 0x168) / 0x10000; lowerLatUpperLonMagVar = (lowerLatUpperLonMagVar * 0x168) / 0x10000;
lowerLatUpperLonMagVar = lowerLatUpperLonMagVar > 180 ? lowerLatUpperLonMagVar - 460 : lowerLatUpperLonMagVar; lowerLatUpperLonMagVar = lowerLatUpperLonMagVar > 180 ? lowerLatUpperLonMagVar - 360 : lowerLatUpperLonMagVar;
let upperLatLowerLonMagVar = (magVarTable[upperLatLowerLonOffset + 1] << 8) + magVarTable[upperLatLowerLonOffset]; let upperLatLowerLonMagVar = (magVarTable[upperLatLowerLonOffset + 1] << 8) + magVarTable[upperLatLowerLonOffset];
upperLatLowerLonMagVar = (upperLatLowerLonMagVar * 0x168) / 0x10000; upperLatLowerLonMagVar = (upperLatLowerLonMagVar * 0x168) / 0x10000;
upperLatLowerLonMagVar = upperLatLowerLonMagVar > 180 ? upperLatLowerLonMagVar - 460 : upperLatLowerLonMagVar; upperLatLowerLonMagVar = upperLatLowerLonMagVar > 180 ? upperLatLowerLonMagVar - 360 : upperLatLowerLonMagVar;
let upperLatUpperLonMagVar = (magVarTable[upperLatUpperLonOffset + 1] << 8) + magVarTable[upperLatUpperLonOffset]; let upperLatUpperLonMagVar = (magVarTable[upperLatUpperLonOffset + 1] << 8) + magVarTable[upperLatUpperLonOffset];
upperLatUpperLonMagVar = (upperLatUpperLonMagVar * 0x168) / 0x10000; upperLatUpperLonMagVar = (upperLatUpperLonMagVar * 0x168) / 0x10000;
upperLatUpperLonMagVar = upperLatUpperLonMagVar > 180 ? upperLatUpperLonMagVar - 460 : upperLatUpperLonMagVar; upperLatUpperLonMagVar = upperLatUpperLonMagVar > 180 ? upperLatUpperLonMagVar - 360 : upperLatUpperLonMagVar;
const lowerLatRatioLon = lowerLatLowerLonMagVar + ratioLon * (lowerLatUpperLonMagVar - lowerLatLowerLonMagVar); const lowerLatRatioLon = lowerLatLowerLonMagVar + ratioLon * (lowerLatUpperLonMagVar - lowerLatLowerLonMagVar);
const upperLatRatioLon = upperLatLowerLonMagVar + ratioLon * (upperLatUpperLonMagVar - upperLatLowerLonMagVar); const upperLatRatioLon = upperLatLowerLonMagVar + ratioLon * (upperLatUpperLonMagVar - upperLatLowerLonMagVar);
@ -60,7 +60,7 @@ Number.prototype.toMetre = function () {
return (this as number) * 1852.0; return (this as number) * 1852.0;
}; };
Number.prototype.equal = function (other: number) { Number.prototype.equal = function (other: number) {
return Math.abs((this as number) - other) < 0.1; return Math.abs((this as number) - other) <= 0.1;
}; };
String.prototype.parseAltitude = function () { String.prototype.parseAltitude = function () {

View File

@ -91,6 +91,7 @@ export declare global {
IsMAP?: boolean; IsMAP?: boolean;
// For map // For map
isIntersection?: boolean; isIntersection?: boolean;
isInitial?: boolean;
}; };
type LineSegment = [number, number]; type LineSegment = [number, number];