1
0
Fork 0
A320-family/Nasal/FMGC/flightplan.nas
Jonathan Redpath 04177e434c Make the FMGC respect speed constraints in flightplan (while still meeting speed limit)
Also fix bug with incorrect groundspeed node, and prevent an error in
replay mode
2022-07-03 18:40:52 +01:00

962 lines
No EOL
32 KiB
Text

# A3XX FMGC Flightplan Driver
# Copyright (c) 2022 Josh Davidson (Octal450) and Jonathan Redpath (legoboyvdlp)
var wpDep = nil;
var wpArr = nil;
var pos = nil;
var geoPosPrev = geo.Coord.new();
var currentLegCourseDist = nil;
var courseDistanceFrom = nil;
var sizeWP = nil;
var magTrueError = 0;
var storeCourse = nil;
var DEBUG_DISCONT = 0;
# Props.getNode
var magHDG = props.globals.getNode("/orientation/heading-magnetic-deg", 1);
var trueHDG = props.globals.getNode("/orientation/heading-deg", 1);
var flightPlanController = {
flightplans: [createFlightplan(), createFlightplan(), createFlightplan(), nil],
temporaryFlag: [0, 0],
# These flags are only for the main flgiht-plan
active: props.globals.initNode("/autopilot/route-manager/active", 0, "BOOL"),
changed: props.globals.initNode("/autopilot/route-manager/flightplan-changed", 0, "BOOL"),
currentToWpt: nil, # container for the current TO waypoint ghost
currentToWptIndex: props.globals.initNode("/autopilot/route-manager/current-wp", 1, "INT"),
currentToWptIndexTemp: 0,
currentToWptIndexTemp2: 0,
currentToWptID: props.globals.initNode("/autopilot/route-manager/wp[0]/id", "", "STRING"),
courseToWpt: props.globals.initNode("/autopilot/route-manager/wp[0]/true-bearing-deg", 0, "DOUBLE"),
courseMagToWpt: props.globals.initNode("/autopilot/route-manager/wp[0]/bearing-deg", 0, "DOUBLE"),
distToWpt: props.globals.initNode("/autopilot/route-manager/wp[0]/dist", 0, "DOUBLE"),
wptType: nil,
wptTypeNoAdvanceDelete: 0,
# Temporary flightplan will use flightplan[0] and flightplan[1]
num: [props.globals.initNode("/FMGC/flightplan[0]/num", 0, "INT"), props.globals.initNode("/FMGC/flightplan[1]/num", 0, "INT"), props.globals.initNode("/autopilot/route-manager/route/num", 0, "INT")],
arrivalIndex: [0, 0, 0],
arrivalDist: props.globals.getNode("/autopilot/route-manager/distance-remaining-nm"),
fromWptTime: nil,
fromWptAlt: nil,
_timeTemp: nil,
_altTemp: nil,
decelPoint: nil,
lvlOffPoint: nil,
init: func() {
me.resetFlightplan(2);
me.insertPPOS(2);
me.addDiscontinuity(1, 2, 1);
me.flightPlanChanged(2);
me.flightplans[2].activate();
},
reset: func() {
me.temporaryFlag[0] = 0;
me.temporaryFlag[1] = 0;
me.resetFlightplan(0);
me.resetFlightplan(1);
me.resetFlightplan(2);
me.decelPoint = nil;
setprop("/instrumentation/nd/symbols/decel/show", 0);
me.lvlOffPoint = nil;
setprop("/autopilot/route-manager/vnav/ec/latitude-deg", 0); # necessary to prevent canvas glitching out because properties don't exist
setprop("/autopilot/route-manager/vnav/ed/latitude-deg", 0);
setprop("/autopilot/route-manager/vnav/ec/longitude-deg", 0);
setprop("/autopilot/route-manager/vnav/ed/longitude-deg", 0);
setprop("/autopilot/route-manager/vnav/ec/show", 0);
setprop("/autopilot/route-manager/vnav/ed/show", 0);
me.flightplans[2].activate();
},
resetFlightplan: func(n) {
me.flightplans[n].cleanPlan();
me.flightplans[n].departure = nil;
me.flightplans[n].destination = nil;
mcdu.isNoTransArr[n] = 0;
mcdu.isNoTransDep[n] = 0;
mcdu.isNoSid[n] = 0;
mcdu.isNoStar[n] = 0;
mcdu.isNoVia[n] = 0;
me.arrivalIndex[n] = 0; # reset arrival index calculations
},
oldCurrentWp: 0,
lastSequencedCurrentWP: 0,
createTemporaryFlightPlan: func(n) {
me.resetFlightplan(n);
me.flightplans[n] = me.flightplans[2].clone();
me.temporaryFlag[n] = 1;
if (canvas_mcdu.myDirTo[n] != nil) {
canvas_mcdu.myDirTo[n].updateTmpy();
}
if (canvas_mcdu.myHold[n] != nil) {
canvas_mcdu.myHold[n].updateTmpy();
}
if (canvas_mcdu.myAirways[n] != nil) {
canvas_mcdu.myAirways[n].updateTmpy();
}
fmgc.windController.createTemporaryWinds(n);
me.oldCurrentWp = FPLN.currentWP.getValue();
me.flightPlanChanged(n);
},
loadFlightPlan: func(path) {
call(func {
me.flightplans[3] = createFlightplan(path);
}, nil, var err = []);
if (size(err) or me.flightplans[3] == nil) {
print(err[0]);
print("Load failed.");
}
me.destroyTemporaryFlightPlan(3, 1);
},
destroyTemporaryFlightPlan: func(n, a) { # a = 1 activate, a = 0 erase, s = 0 don't call flightplan changed
if (a == 1) {
flightPlanTimer.stop();
me.resetFlightplan(2);
me.flightplans[2] = me.flightplans[n].clone();
me.flightplans[2].activate();
if (n != 3) {
if (mcdu.isNoSid[n] == 1) {
mcdu.isNoSid[2] = 1;
} else {
mcdu.isNoSid[2] = 0;
}
if (mcdu.isNoStar[n] == 1) {
mcdu.isNoStar[2] = 1;
} else {
mcdu.isNoStar[2] = 0;
}
if (mcdu.isNoVia[n] == 1) {
mcdu.isNoVia[2] = 1;
} else {
mcdu.isNoVia[2] = 0;
}
if (mcdu.isNoTransDep[n] == 1) {
mcdu.isNoTransDep[2] = 1;
} else {
mcdu.isNoTransDep[2] = 0;
}
if (mcdu.isNoTransArr[n] == 1) {
mcdu.isNoTransArr[2] = 1;
} else {
mcdu.isNoTransArr[2] = 0;
}
}
me.flightPlanChanged(2);
flightPlanTimer.start();
}
if (n == 3) {
me.flightPlanChanged(n);
return;
}
me.temporaryFlag[n] = 0;
me.flightPlanChanged(2);
me.resetFlightplan(n);
if (canvas_mcdu.myDirTo[n] != nil) {
canvas_mcdu.myDirTo[n].updateTmpy();
}
if (me.DirToIndex != nil) {
me.currentToWptIndex.setValue(me.DirToIndex);
me.DirToIndex = nil;
}
fmgc.windController.destroyTemporaryWinds(n, a);
if (FPLN.currentWP.getValue() != me.oldCurrentWp) {
FPLN.currentWP.setValue(me.oldCurrentWp);
}
me.flightPlanChanged(n);
},
updateAirports: func(dep, arr, plan) {
me.resetFlightplan(plan);
me.flightplans[plan].departure = airportinfo(dep);
me.flightplans[plan].destination = airportinfo(arr);
if (plan == 2) {
if (me.temporaryFlag[0]) { me.destroyTemporaryFlightPlan(0, 0); }
if (me.temporaryFlag[1]) { me.destroyTemporaryFlightPlan(1, 0); }
me.arrivalIndex = [0, 0, 0]; # reset arrival index calculations
}
me.addDiscontinuity(1, plan);
# reset mcdu if it exists
if (canvas_mcdu.myFpln[0] != nil) { canvas_mcdu.myFpln[0].scroll = 0; }
if (canvas_mcdu.myFpln[1] != nil) { canvas_mcdu.myFpln[1].scroll = 0; }
if (canvas_mcdu.myArrival[0] != nil) { canvas_mcdu.myArrival[0].reset(); }
if (canvas_mcdu.myArrival[1] != nil) { canvas_mcdu.myArrival[1].reset(); }
if (canvas_mcdu.myDeparture[0] != nil) { canvas_mcdu.myDeparture[0].reset(); }
if (canvas_mcdu.myDeparture[1] != nil) { canvas_mcdu.myDeparture[1].reset(); }
me.flightPlanChanged(plan);
},
calculateTimeAltitudeOnSequence: func() {
me._timeTemp = math.round(getprop("/sim/time/utc/minute") + (getprop("/sim/time/utc/second") / 60));
if (me._timeTemp < 10) {
me._timeTemp = "0" ~ me._timeTemp;
}
me.fromWptTime = getprop("/sim/time/utc/hour") ~ me._timeTemp;
me._altTemp = getprop("/systems/navigation/adr/output/baro-alt-corrected-1-capt");
if (me._altTemp > fmgc.FMGCInternal.transAlt) {
me.fromWptAlt = "FL" ~ math.round(me._altTemp / 100);
} else {
if (me._altTemp > 0) {
me.fromWptAlt = math.round(me._altTemp);
} else {
me.fromWptAlt = "M" ~ math.round(me._altTemp);
}
}
},
autoSequencing: func() {
if (!me.active.getBoolValue()) { return; }
if (pts.Sim.pause.getBoolValue()) { return; }
me.calculateTimeAltitudeOnSequence();
# Advancing logic
me.currentToWptIndexTemp = me.currentToWptIndex.getValue();
# TODO - after sequencing discontinuity, FPLN should show PPOS then DISCONTINUITY
# Clearing that discontinuity is not allowed, you must exit using DIRTO, or else using NAV ARM and overfly
# TODO - triple click - confirm, is it only with DES disengage, or also with the NAV loss?
# TODO - I think that it only goes to VS when in DES mode
if (me.flightplans[2].getWP(me.currentToWptIndexTemp + 1).wp_type == "discontinuity" or me.flightplans[2].getWP(me.currentToWptIndexTemp + 1).wp_type == "vectors") {
if (fmgc.Output.lat.getValue() == 1) {
fmgc.Input.lat.setValue(3);
}
me.currentToWptIndex.setValue(me.currentToWptIndexTemp + 2);
me.lastSequencedCurrentWP = me.currentToWptIndexTemp + 2;
} else {
me.currentToWptIndex.setValue(me.currentToWptIndexTemp + 1);
me.lastSequencedCurrentWP = me.currentToWptIndexTemp + 1;
if (me.num[2].getValue() > 2 and me.currentToWptIndexTemp >= 1) {
for (var i = 0; i <= 2; i += 1) {
if (i == 2 or me.temporaryFlag[i]) {
me.flightplans[i].getWP(me.currentToWptIndexTemp - 1).hidden = 1;
}
}
}
}
},
# changeOverflyType - toggle flyby type of passed waypoint
# args: index, plan, computer
# index: index to toggle
# plan: plan on which operation is performed
# If the passed waypoint exists, toggle its flyover attribute
changeOverFlyType: func(index, plan) {
wp = me.flightplans[plan].getWP(index);
if (wp == nil or wp.wp_name == "DISCONTINUITY" or wp.wp_name == "VECTORS") { return 1; };
wp.fly_type = (wp.fly_type == "flyBy") ? "flyOver" : "flyBy";
return 2;
},
# for these two remember to call flightPlanChanged. We are assuming this is called from a function which will all flightPlanChanged itself.
# addDiscontinuity - insert discontinuity at passed index
# args: index, plan
# index: index to add at
# plan: plan to add to
# Check if a discontinuity already exists either immediately before or at that index
# If it does, don't add another one
# Optional flag DEBUG_DISCONT to disable discontinuities totally
addDiscontinuity: func(index, plan, force = 0) {
if (DEBUG_DISCONT) { return; }
if (force) {
me.flightplans[plan].insertWP(createDiscontinuity(), index);
return;
}
if (me.flightplans[plan].getWP(index) != nil) { # index is not nil
if (me.flightplans[plan].getWP(index - 1) != nil) { # index -1 is also not nil
if (me.flightplans[plan].getWP(index).wp_name != "DISCONTINUITY" and me.flightplans[plan].getWP(index - 1).wp_name != "DISCONTINUITY") {
me.flightplans[plan].insertWP(createDiscontinuity(), index);
}
} else { # -1 is nil
if (me.flightplans[plan].getWP(index).wp_name != "DISCONTINUITY") {
me.flightplans[plan].insertWP(createDiscontinuity(), index);
}
}
} elsif (me.flightplans[plan].getWP(index - 1) != nil) { # index is nil, -1 is not
if (me.flightplans[plan].getWP(index - 1).wp_name != "DISCONTINUITY") {
me.flightplans[plan].insertWP(createDiscontinuity(), index);
}
} else { # both are nil??
debug.dump("Error in discontinuities; won't try to add one");
}
},
# insertTP - insert PPOS waypoint denoted "T-P" at specified index
# args: n, index
# n: flightplan to which the PPOS waypoint will be inserted
# index: index which the waypoint will be at.
insertTP: func(n, index) {
me.flightplans[n].insertWP(createWP(geo.aircraft_position(), "T-P"), index);
fmgc.windController.insertWind(n, index, 0, "T-P");
},
insertPPOS: func(n, index = 0) {
me.flightplans[n].insertWP(createWP(geo.aircraft_position(), "PPOS"), index);
fmgc.windController.insertWind(n, index, 0, "PPOS");
},
# childWPBearingDistance - return waypoint at bearing and distance from specified waypoint ghost
# args: wpt, bearing, dist, name, typeStr
# wpt: waypoint ghost
# bearing: bearing of waypoint to be created from specified waypoint
# distance: distance of waypoint to be created from specified waypoint, nautical miles
# name: name of waypoint to be created
# typeStr: optional argument to be passed to createWP, must be one of "sid", "star" "approach" "missed" or "pseudo"
childWPBearingDistance: func(wpt, bearing, dist) {
var coordinates = greatCircleMove(wpt.lat, wpt.lon, num(bearing), num(dist));
return coordinates;
},
# insertNOSID - create default SID and add to flightplan
# args: n: plan on which the SID will be created
# The default SID is a leg from departure runway to a point 2.5 miles on the runway extended centreline
# if NO SID has already been inserted, we will not insert another one.
insertNOSID: func(n) {
var wptStore = me.flightplans[n].getWP(0);
if (wptStore.wp_type == "runway") {
if (me.flightplans[n].getWP(1).id == "1500") { # check if we have NO SID already loaded
me.deleteWP(1, n, 1);
}
# fudge the altitude since we cannot create a hdgtoAlt from nasal. Assume 600 feet per mile - 2.5 miles
me.flightplans[n].insertWP(createWP(me.childWPBearingDistance(wptStore, me.flightplans[n].departure_runway.heading, 2.5 + (me.flightplans[n].departure_runway.length * M2NM)), "1500", "sid"), 1);
me.flightplans[n].getWP(1).fly_type = "flyOver";
me.flightplans[n].getWP(1).setAltitude(1500, "at");
fmgc.windController.insertWind(n, 1, 0, "1500");
}
me.flightPlanChanged(n);
},
# insertNOSTAR - create default STAR and add to flightplan
# args: n: plan on which the STAR will be created
# The default STAR is a leg from departure runway to a point 5 miles on the runway extended centreline
# if NO STAR has already been inserted, we will not insert another one.
insertNOSTAR: func(n) {
var wptStore = me.flightplans[n].getWP(me.arrivalIndex[n]);
if (wptStore.wp_type == "runway") {
if (me.flightplans[n].getWP(me.arrivalIndex[n] - 1).id == "CF") { # check if we have NO STAR already loaded
me.deleteWP(me.arrivalIndex[n] - 1, n, 1);
}
var hdg = me.flightplans[n].destination_runway.heading + 180;
if (hdg > 360) {
hdg = hdg - 360;
}
me.flightplans[n].insertWP(createWP(me.childWPBearingDistance(wptStore, hdg, 5), "CF", "star"), me.arrivalIndex[n]);
me.flightplans[n].getWP(me.arrivalIndex[n]).fly_type = "flyOver";
fmgc.windController.insertWind(n, me.arrivalIndex[n], 0, "CF");
}
me.flightPlanChanged(n);
},
# directTo - create leg direct from present position to a specified waypoint
# args: waypointGhost, plan
# waypointGost: waypoint ghost of the waypoint
# plan: plan on which the direct to leg will be created
# We first insert a PPOS waypoint at index 1
# We check if the flightplan already contains the waypoint passed to the function
# If it exists, we delete intermediate waypoints
# If it does not, we insert the waypoint at index 2 and add a discontinuity at index 3
# In either case, we delete the current FROM waypoint, index 0, and call flightPlanChanged to recalculate
# We attempt to get the distance from the aircraft current position to the chosen waypoint and update mcdu with it
DirToIndex: nil,
directTo: func(waypointGhost, plan) {
if (me.flightplans[plan].indexOfWP(waypointGhost) == -1) {
me.insertTP(plan, me.currentToWptIndex.getValue());
# use createWP here as createWPFrom doesn't accept waypoints
# createWPFrom worked before... but be sure!
me.flightplans[plan].insertWP(createWP(waypointGhost, waypointGhost.id), me.currentToWptIndex.getValue() + 1);
fmgc.windController.insertWind(plan, me.currentToWptIndex.getValue() + 1, 0, waypointGhost.id);
me.addDiscontinuity(me.currentToWptIndex.getValue() + 2, plan);
me.DirToIndex = me.currentToWptIndex.getValue() + 1;
} else {
var indexWP = me.flightplans[plan].indexOfWP(waypointGhost);
me.insertTP(plan, indexWP);
me.deleteTillIndex(waypointGhost, me.currentToWptIndex.getValue(), plan, 1);
indexWP = me.flightplans[plan].indexOfWP(waypointGhost);
me.hideTillIndex(indexWP - 2, plan);
me.DirToIndex = indexWP;
}
var curAircraftPosDirTo = geo.aircraft_position();
canvas_mcdu.myDirTo[plan].updateDist(me.flightplans[plan].getWP(me.currentToWptIndex.getValue() + 1).courseAndDistanceFrom(curAircraftPosDirTo)[1]);
},
deleteWP: func(index, n, a = 0) { # a = 1, means adding a waypoint via deleting intermediate
var wp = me.flightplans[n].getWP(index);
if ((left(wp.wp_name, 4) != FMGCInternal.depApt and left(wp.wp_name, 4) != FMGCInternal.arrApt) and me.flightplans[n].getPlanSize() > 2) {
if (wp.id != "DISCONTINUITY" and a == 0) { # if it is a discont, don't make a new one
me.flightplans[n].deleteWP(index);
fmgc.windController.deleteWind(n, index);
if (me.flightplans[n].getWP(index) != nil) { # This refers to the next one after the one we deleted
if (me.flightplans[n].getWP(index).id != "DISCONTINUITY") { # else, if the next one isn't a discont, add one
me.addDiscontinuity(index, n);
}
}
} else {
if (wp.id == "DISCONTINUITY" and index > 0 and (me.flightplans[n].getWP(index - 1).id == "PPOS" or find("VECTORS", me.flightplans[n].getWP(index - 1).id) != -1)) {
return 1;
} else {
me.flightplans[n].deleteWP(index);
fmgc.windController.deleteWind(n, index);
}
}
me.flightPlanChanged(n);
canvas_nd.A3XXRouteDriver.triggerSignal("fp-removed");
return 2;
} else {
return 1;
}
},
# deleteTillIndex - helper that deletes waypoints up to a passed waypoint already in flightplan
# uses a while loop to delete a certain number of waypoints between passed index and
# index of waypoint alredy in flightplan
deleteTillIndex: func(wpGhost, index, plan, offset = 0) {
var numToDel = me.flightplans[plan].indexOfWP(wpGhost) - index - offset;
while (numToDel > 0) {
me.deleteWP(index, plan, 1);
numToDel -= 1;
}
return 2;
},
hideTillIndex: func(index, plan) {
var numToDel = index;
while (numToDel >= 0) {
me.flightplans[plan].getWP(index - numToDel).hidden = 1;
numToDel -= 1;
}
return 2;
},
# createDuplicateNames - helper to spawn DUPLICATENAMES page
# args: ghostContainer, index, flag, plan
# ghostContainer: vector of fgPositioned ghosts
# index: index
# flag: is it a navaids DUPLICATENAMES page or not?
# plan: plan
# flagPBD: do we return back to PBD handler or to default waypoint handler?
# flagPROG: do we return back to PROG handler or to default waypoint handler (only if flagPBD false)
createDuplicateNames: func(ghostContainer, index, flag, plan, flagPBD = 0, bearing = -999, distance = -99, flagPROG = 0) {
if (canvas_mcdu.myDuplicate[plan] != nil) {
canvas_mcdu.myDuplicate[plan].del();
}
canvas_mcdu.myDuplicate[plan] = nil;
canvas_mcdu.myDuplicate[plan] = mcdu.duplicateNamesPage.new(ghostContainer, index, flag, plan, flagPBD, bearing, distance, flagPROG);
setprop("MCDU[" ~ plan ~ "]/page", "DUPLICATENAMES");
},
insertAirport: func(text, index, plan, override = 0, overrideIndex = -1) {
if (index == 0) {
return 1;
}
var airport = findAirportsByICAO(text);
if (size(airport) == 0) {
return 0;
}
if (size(airport) == 1 or override) {
var indexToInsert = -1;
if (override) {
indexToInsert = overrideIndex;
} else {
indexToInsert = 0;
}
var indexPresent = me.flightplans[plan].indexOfWP(airport[indexToInsert]);
if (indexPresent == -1 or indexPresent > me.arrivalIndex[plan]) {
me.flightplans[plan].insertWP(createWPFrom(airport[indexToInsert]), index);
fmgc.windController.insertWind(plan, index, 0, text);
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
} else {
return me.deleteTillIndex(airport[indexToInsert], index, plan);
}
} elsif (size(airport) >= 1) {
me.createDuplicateNames(airport, index, 0, plan);
return 2;
}
},
insertFix: func(text, index, plan, override = 0, overrideIndex = -1) {
if (index == 0) {
return 1;
}
var fix = findFixesByID(text);
if (size(fix) == 0) {
return 0;
}
if (size(fix) == 1 or override) {
var indexToInsert = -1;
if (override) {
indexToInsert = overrideIndex;
} else {
indexToInsert = 0;
}
var indexPresent = me.flightplans[plan].indexOfWP(fix[indexToInsert]);
if (indexPresent == -1 or indexPresent > me.arrivalIndex[plan]) {
me.flightplans[plan].insertWP(createWPFrom(fix[indexToInsert]), index);
fmgc.windController.insertWind(plan, index, 1, text);
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
} else {
return me.deleteTillIndex(fix[indexToInsert], index, plan);
}
} elsif (size(fix) >= 1) {
me.createDuplicateNames(fix, index, 0, plan);
return 2;
}
},
insertNavaid: func(text, index, plan, override = 0, overrideIndex = -1) {
if (index == 0) {
return 1;
}
var navaid = findNavaidsByID(text);
if (size(navaid) == 0) {
return 0;
}
if (size(navaid) == 1 or override) {
var indexToInsert = -1;
if (override) {
indexToInsert = overrideIndex;
} else {
indexToInsert = 0;
}
var indexPresent = me.flightplans[plan].indexOfWP(navaid[indexToInsert]);
if (indexPresent == -1 or indexPresent > me.arrivalIndex[plan]) {
me.flightplans[plan].insertWP(createWPFrom(navaid[indexToInsert]), index);
fmgc.windController.insertWind(plan, index, 1, text);
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
} else {
return me.deleteTillIndex(navaid[indexToInsert], index, plan);
}
} elsif (size(navaid) >= 1) {
me.createDuplicateNames(navaid, index, 1, plan);
return 2;
}
},
insertDBWP: func(wpGhost, index, plan) {
if (index == 0 or wpGhost == nil) {
return 1;
}
var indexCurr = me.flightplans[plan].indexOfWP(wpGhost);
if (indexCurr == -1 or indexCurr > me.arrivalIndex[plan]) {
# use createWP here as createWPFrom doesn't accept waypoints
me.flightplans[plan].insertWP(createWP(wpGhost, wpGhost.wp_name), index);
fmgc.windController.insertWind(plan, index, 1, wpGhost.wp_name);
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
} else {
return me.deleteTillIndex(wpGhost, index, plan);
}
},
insertLatLonFix: func(text, index, plan) {
if (index == 0) {
return 1;
}
var lat = split("/", text)[0];
var lon = split("/", text)[1];
var latDecimal = mcdu.stringToDegrees(lat, "lat");
var lonDecimal = mcdu.stringToDegrees(lon, "lon");
if (latDecimal > 90 or latDecimal < -90 or lonDecimal > 180 or lonDecimal < -180) {
return 1;
}
var waypoint = pilotWaypoint.new({lat: latDecimal, lon: lonDecimal}, "LL");
var addDb = WaypointDatabase.addWP(waypoint);
if (addDb != 2) {
return addDb;
}
me.flightplans[plan].insertWP(waypoint.wpGhost, index);
fmgc.windController.insertWind(plan, index, 1, "LL");
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
},
# getWPforPBD - parse scratchpad text to find waypoint ghost for PBD
# args: text, index, plan
# text: scratchpad text
# index: index at which waypoint will be inserted
# plan: plan to which waypoint will be inserted
# return:
# 0: not in database
# 1: notAllowed
# 2: o.k.
getWPforPBD: func(text, index, plan, override = 0, overrideIndex = -1) {
if (index == 0) {
return 1;
}
var textSplit = split("/", text);
if (size(split(".", textSplit[2])) != 1 or size(textSplit[1]) < 2 or size(textSplit[1]) > 3) {
return 1;
}
var wpGhost = nil;
var wpGhostContainer = nil;
var type = nil;
if (size(textSplit[0]) == 5) {
wpGhostContainer = findFixesByID(textSplit[0]);
if (size(wpGhostContainer) == 0) {
return 0;
}
type = "fix";
} elsif (size(textSplit[0]) == 4) {
wpGhostContainer = findAirportsByICAO(textSplit[0]);
if (size(wpGhostContainer) == 0) {
return 0;
}
type = "airport";
} elsif (size(textSplit[0]) == 3 or size(textSplit[0]) == 2) {
wpGhostContainer = findNavaidsByID(textSplit[0]);
if (size(wpGhostContainer) == 0) {
return 0;
}
type = "navaid";
} else {
return 1;
}
if (size(wpGhostContainer) == 1 or override) {
if (!override) {
wpGhost = wpGhostContainer[0];
} else {
wpGhost = wpGhostContainer[overrideIndex];
}
} else {
if (type == "navaid") {
me.createDuplicateNames(wpGhostContainer, index, 1, plan, 1, num(textSplit[1]), num(textSplit[2]), 0);
} else {
me.createDuplicateNames(wpGhostContainer, index, 0, plan, 1, num(textSplit[1]), num(textSplit[2]), 0);
}
return 2;
}
var localMagvar = magvar(wpGhost.lat, wpGhost.lon);
return me.insertPlaceBearingDistance(wpGhost, textSplit[1] + localMagvar, textSplit[2], index, plan); # magnetic to true? I don't know. But this works!
},
getNavCount: func(plan) {
var count = 0;
for (var wpt = 0; wpt < me.flightplans[plan].getPlanSize(); wpt += 1) {
if (me.flightplans[plan].getWP(wpt).wp_type == "navaid") {
count += 1;
}
}
return count;
},
getDepartureCount: func(plan) {
var count = 0;
for (var wpt = 0; wpt < me.flightplans[plan].getPlanSize(); wpt += 1) {
if (me.flightplans[plan].getWP(wpt).wp_role == "sid") {
count += 1;
}
}
return count;
},
getArrivalCount: func(plan) {
var count = 0;
for (var wpt = 0; wpt < me.flightplans[plan].getPlanSize(); wpt += 1) {
if (me.flightplans[plan].getWP(wpt).wp_role == "star" or me.flightplans[plan].getWP(wpt).wp_role == "approach" or me.flightplans[plan].getWP(wpt).wp_role == "missed") {
count += 1;
}
}
return count;
},
getPlanSizeNoDiscont: func(plan) {
var count = 0;
for (var wpt = 0; wpt < me.flightplans[plan].getPlanSize(); wpt += 1) {
if (me.flightplans[plan].getWP(wpt).wp_name != "DISCONTINUITY") {
count += 1;
}
}
return count;
},
calculateDecelPoint: func() {
if (me.getPlanSizeNoDiscont(2) <= 1 or fmgc.FMGCInternal.decel) {
setprop("/instrumentation/nd/symbols/decel/show", 0);
return;
}
me.indexDecel = 0;
for (var wpt = 0; wpt < me.flightplans[2].getPlanSize(); wpt += 1) {
if (me.flightplans[2].getWP(wpt).wp_role == "approach") {
me.indexDecel = wpt;
break;
}
if (wpt == me.flightplans[2].getPlanSize()) {
me.indexDecel = me.arrivalIndex - 2;
break;
}
}
me.dist = me.flightplans[2].getWP(me.indexDecel).leg_distance - 7;
if (me.dist < 0) {
me.dist = 0.1;
}
me.decelPoint = me.flightplans[2].pathGeod(me.indexDecel - 1, me.dist);
setprop("/instrumentation/nd/symbols/decel/latitude-deg", me.decelPoint.lat);
setprop("/instrumentation/nd/symbols/decel/longitude-deg", me.decelPoint.lon);
setprop("/instrumentation/nd/symbols/decel/show", 1);
me.indexTemp = me.indexDecel;
me.distTemp = 7;
if (me.flightplans[2].getWP(me.indexTemp).leg_distance < 7) {
while (me.distTemp > 0 and me.indexTemp > 0) {
me.distTemp -= me.flightplans[2].getWP(me.indexTemp).leg_distance;
me.indexTemp -= 1;
}
me.indexTemp += 1;
}
setprop("/instrumentation/nd/symbols/decel/index", me.indexTemp);
},
calculateLvlOffPoint: func(deltaAltitude) {
me.distLvl = (deltaAltitude * pts.Velocities.groundspeedKt.getValue()) / (fmgc.Internal.vs.getValue() * 60);
if (me.active.getBoolValue() and fmgc.Output.lat.getValue() == 1 and me.distLvl >= 0) { # NAV
me.lvlOffPoint = me.flightplans[2].pathGeod(me.currentToWptIndex.getValue() - 1, me.flightplans[2].getWP(me.currentToWptIndex.getValue()).leg_distance - me.distToWpt.getValue() + me.distLvl);
} elsif (fmgc.Output.lat.getValue() == 0 and me.distLvl >= 0) { # HDG TRK
var coord = geo.aircraft_position();
coord.apply_course_distance(getprop("/orientation/track-magnetic-deg"), me.distLvl * NM2M);
var latGeo = coord.lat();
var lonGeo = coord.lon();
me.lvlOffPoint = {lat: latGeo, lon: lonGeo};
} else {
setprop("/autopilot/route-manager/vnav/ec/show", 0);
setprop("/autopilot/route-manager/vnav/ed/show", 0);
me.lvlOffPoint = nil;
}
if (deltaAltitude >= 100 and me.lvlOffPoint != nil) {
setprop("/autopilot/route-manager/vnav/ec/latitude-deg", me.lvlOffPoint.lat);
setprop("/autopilot/route-manager/vnav/ec/longitude-deg", me.lvlOffPoint.lon);
setprop("/autopilot/route-manager/vnav/ec/show", 1);
setprop("/autopilot/route-manager/vnav/ed/show", 0);
} elsif (deltaAltitude <= -100 and me.lvlOffPoint != nil) {
setprop("/autopilot/route-manager/vnav/ed/latitude-deg", me.lvlOffPoint.lat);
setprop("/autopilot/route-manager/vnav/ed/longitude-deg", me.lvlOffPoint.lon);
setprop("/autopilot/route-manager/vnav/ec/show", 0);
setprop("/autopilot/route-manager/vnav/ed/show", 1);
}
},
# insertPlaceBearingDistance - insert PBD waypoint at specified index,
# at some specified bearing, distance from a specified location
# args: wp, index, plan
# wpt: waypoint ghost
# index: index to insert at in plan
# plan: plan to insert to
insertPlaceBearingDistance: func(wp, bearing, distance, index, plan) {
var waypoint = pilotWaypoint.new(me.childWPBearingDistance(wp, bearing, distance), "PBD");
var addDb = WaypointDatabase.addWP(waypoint);
if (addDb != 2) {
return addDb;
}
me.flightplans[plan].insertWP(waypoint.wpGhost, index);
fmgc.windController.insertWind(plan, index, 0, "PBD");
me.addDiscontinuity(index + 1, plan);
me.flightPlanChanged(plan);
return 2;
},
scratchpad: func(text, index, plan) { # return 0 not in database, 1 not allowed, 2 success, 3 = not allowed due to dir to, 4 = database full
if (mcdu.dirToFlag) {
return 3;
}
if (index == me.currentToWptIndex.getValue()) {
return 1;
# TODO - implement the PPOS - DISCONT feature
# me.insertPPOS(thePlan, index - 1);
# me.addDiscontinuity(index - 1, thePlan, 1);
}
if (!me.temporaryFlag[plan]) {
if (text == "CLR" and me.flightplans[2].getWP(index).wp_name == "DISCONTINUITY") {
if (me.flightplans[2].getPlanSize() == 3 and me.flightplans[2].departure_runway == nil and me.flightplans[2].destination_runway == nil and index == 1) {
return 1;
}
var thePlan = 2;
} else {
fmgc.flightPlanController.createTemporaryFlightPlan(plan);
var thePlan = plan;
}
} else {
var thePlan = plan;
}
# check waypoints database here
var wpFromDB = WaypointDatabase.getWP(text);
if (wpFromDB != nil) {
return me.insertDBWP(wpFromDB, index, thePlan);
}
if (size(split("/", text)) == 3) {
return me.getWPforPBD(text, index, thePlan);
} elsif (text == "@") {
return me.changeOverFlyType(index, thePlan);
} elsif (text == "CLR") {
return me.deleteWP(index, thePlan, 0);
} elsif (size(text) > 12) {
return me.insertLatLonFix(text, index, thePlan);
} elsif (size(text) == 5) {
return me.insertFix(text, index, thePlan);
} elsif (size(text) == 4) {
return me.insertAirport(text, index, thePlan);
} elsif (size(text) == 3 or size(text) == 2) {
return me.insertNavaid(text, index, thePlan);
} else {
return 1;
}
},
flightPlanChanged: func(n) {
me.updatePlans(1);
fmgc.windController.updatePlans();
# push update to fuel
if (fmgc.FMGCInternal.blockConfirmed) {
fmgc.FMGCInternal.fuelCalculating = 0;
fmgc.fuelCalculating.setValue(0);
fmgc.FMGCInternal.fuelCalculating = 1;
fmgc.fuelCalculating.setValue(1);
}
if (n == 2) flightPlanController.changed.setBoolValue(1);
canvas_nd.A3XXRouteDriver.triggerSignal("fp-added");
},
# runDecel - used to ensure that only flightplanchanged will update the decel point
updatePlans: func(runDecel = 0) {
if (fmgc.FMGCInternal.toFromSet and me.flightplans[2].departure != nil and me.flightplans[2].destination != nil) { # check if flightplan exists
if (!me.active.getBoolValue()) {
if (me.currentToWptIndex.getValue() < 1) {
var errs = [];
call(func {
if (me.flightplans[2].getWP(1).id != "DISCONTINUITY") {
me.currentToWptIndex.setValue(1);
}
}, nil, nil, nil, errs);
if (size(errs) != 0) { debug.printerror(errs); }
}
me.active.setValue(1);
}
} elsif (me.active.getBoolValue()) {
me.active.setValue(0);
}
if (me.active.getBoolValue() and me.currentToWptIndex.getValue() == -1) {
me.currentToWptIndex.setValue(me.lastSequencedCurrentWP);
}
for (var n = 0; n <= 2; n += 1) {
for (var wpt = 0; wpt < me.flightplans[n].getPlanSize(); wpt += 1) { # Iterate through the waypoints and update their data
var waypointHashStore = me.flightplans[n].getWP(wpt);
if (left(waypointHashStore.wp_name, 4) == fmgc.FMGCInternal.arrApt and wpt != 0) {
if (me.arrivalIndex[n] != wpt) {
me.arrivalIndex[n] = wpt;
if (canvas_mcdu.myFpln[0] != nil) {
canvas_mcdu.myFpln[0].destInfo();
}
if (canvas_mcdu.myFpln[1] != nil) {
canvas_mcdu.myFpln[1].destInfo();
}
}
}
}
}
if (runDecel) {
me.calculateDecelPoint();
}
var deltaAltitude = fmgc.Input.alt.getValue() - pts.Instrumentation.Altimeter.indicatedFt.getValue();
if (abs(deltaAltitude) >= 100) {
me.calculateLvlOffPoint(deltaAltitude);
} else {
setprop("/autopilot/route-manager/vnav/ec/show", 0);
setprop("/autopilot/route-manager/vnav/ed/show", 0);
}
for (var i = 0; i <= 1; i += 1) {
if (canvas_mcdu.myFpln[i] != nil) {
canvas_mcdu.myFpln[i].updatePlan();
}
if (canvas_mcdu.myDirTo[i] != nil) {
canvas_mcdu.myDirTo[i].updateFromFpln();
}
}
},
};
var flightPlanTimer = maketimer(0.1, flightPlanController, flightPlanController.updatePlans);