Nasal Flightplan: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (Use Nasal highlighter)
m (difficult to find this one)
Line 1: Line 1:
{{Template:Nasal Navigation}}
Discussion of interacting with the [[Route manager]] and flight-planning via Nasal.
Discussion of interacting with the [[Route manager]] and flight-planning via Nasal.



Revision as of 08:35, 8 December 2013


Discussion of interacting with the Route manager and flight-planning via Nasal.

Background

Several advanced aircraft have created FMS and CDU implementations using custom Nasal code, with some (or no) interaction with the C++ route-manager, and often with code that overlaps. There's a goal to improve this, while keeping the complex, especially aircraft-dependant functionality in Nasal so it can be tailored and extended easily.

Scott has defined a FMS procedure, flight-plan and waypoint interface in Nasal, which James intends to replicate and extend from the C++ side, and then hopefully aircraft can be ported to use this instead of their own route and flight plan structures. Thus the C++ code and Nasal model will be in sync, and the user experience will be better!

Data Sources

The current C++ code reads airways data from airways.dat, and can read the Level-D 767 procedure files if they are added to the Airports scenery data. However, it's intended that any other format can be supported.

Current Nasal Model

As defined by Scott several years ago, basically stable and working but independent of the route-manager at the moment. (Where does it load procedure data from? Or airways data?)

Flightplan

appendWP        : func(wp)      : int     // returns the index it was inserted at
insertWP        : func(wp, idx)
insertWPAfter   : func(wp, idx)
replaceWPAt     : func(wp, idx)
findWPName      : func(name)    : fmsWP
findWPType      : func(type)    : fmsWP
getWP           : func(idx)     : fmsWP
clearPlan       : func()
getPlanSize     : func()        : int
clearWPType     : func(type)

FMS DB functions

fmsDB
new             : func(icao)
getSIDList      : func(runway) 
getSTARList     : func(runway)  
getApproachList : func(runway, radio) 
getSid          : func(name)
getStar         : func(name)
getIAP          : func(name)

Procedure and Waypoint hashes

These return a fmsTP class or an array of fmsTP that then contain an array of fmsWP amongst other things;
        me.wp_name        = "";
        me.tp_type        = "";         # SID, STAR, IAP
        me.radio          = "";         # ILS, VOR, NDB, RNAV approach
        me.runways        = [];         # array of runway names of this procedure
        me.wpts           = [];         # array of fmsWP
        me.rwy_tw         = [];         # array of runway transition waypoints if SID or approach transition for STAR
        me.transitions    = [];         # array of transition paths


The fmsWP model class contains;
        me.wp_name        = "";         # fix, navaid or pseudo waypoint name
        me.wp_parent_name = "";         # if a SID/STAR WP then the SID/STAR name, or AIRWAY name
        me.wp_type        = "FIX";      # FIX, NAVAID, Termination Point, transition wp, Final Fix, Appr Fix, RWY transition, SID/STAR WP,
        me.fly_type       = "FlyOver";  # flyOver, flyBy, Hold, 
        me.action         = "DIRECT";   # direct, trk, intercept, vectors (not used)
        me.wp_lat         = 0.0;        # latitude
        me.wp_lon         = 0.0;        # longitude
        me.alt_cstr       = 0;          # alt constraint from db or calculated altitude in ft
        me.alt_cstr_ind   = 0;          # if the alt is a programmed constraint or just calculated (as part of STAR)
        me.spd_cstr       = 0;          # spd constraint in kts, mach or zero
        me.spd_cstr_ind   = 0;          # 0 - calculated speed, 1 - constraint
        me.hdg_radial     = 0.0;        # either heading/track or radial
        me.leg_distance   = 0;          # NM for this leg
        me.leg_bearing    = 0.0;        # deg bearing for this leg

Proposed Nasal API

  • The fmsDB is mostly subsumed by the methods James is adding to the airport hash - only a couple more are required to get a 'procedure' hash corresponding to a flightgear::Procedure
  • the fmsWP hash will stay almost the same, but gain some methods to set explicit or calculated speeds, and attach custom data.
  • the flightplan class will gain methods and state to tracy departure and arrival airport / runway / procedures explicit, as the C++ code already does
  • flightplan will support setting callback functions for Nasal to (re-) compute pseudo waypoints and VNAV profile when waypoints are added/removed/edited
  • Although flight plans will support loading and saving to a standard XML format, it should be possible with the API to use Nasal to parse (or write) any other file format, and then build the flight plan using the setter methods. Moving the standard XML loading and saving to Nasal (from C++) would be a nice bonus.

To Be Decided

  • In the current C++ API, the route-manager sets the runway / procedure, and the transition is automatically selected, and the waypoints inserted into the flight-plan. In the code below I'm (James) exposing this logic to Nasal, on the assumption that any other behaviour is more complicated and unnecessary. If it's better, to follow Scott's API above, then instead 'procedure' would simple expose its waypoints as an array, and flight plan would gain a 'insertWaypoints(type, waypoint_vec), where type is 'sid', 'star', 'approach' or similar. (And the code knows the correct insert location). This needs another callback when the departure or arrival data changes from 'outside', i.e the route-manager properties or similar.

Example Nasal

Setting flight-plan global (non-waypoint) data. Potentially this can include lots of FMS data - cruise profile, EPR setting, company route ID and so on. But most of that is probably better handled in Nasal, so long as the flight plan object provides a way to persist it.

# methods on airport (as returned from airportinfo())
var apt = airportinfo('eddm');
var rwy = apt.runway('28L');

# at some point in the future (not for the next release) there will hopefully be:
var rwy = apt.getActiveRunwayFor('departure', <enroute point>);
# which will return the active runway(s) for an airport based on type (arrival / departure)
# and enroute direction (e.g., departing north or arriving from the west)

# get the active flight plan (the one being used by the route manager)
fms.flightplan()

# or create one from Nasal
fms.load('/path/to/xml')

# save the active flight plan
var fp = fms.flightplan()
fp.save("/path/to/xml")

# duplicate a flight-plan
var secondary = fmd.flightplan().copy();

var dest = airportinfo('EDDM')
var rwy = dest.runway('08R');
# the the arrival runway (and airport, automatically)
fp.setArrival(rwy)

# or if no runway is known / specified
fp.setArrival(dest) 

# optional, not used by anything in C++, but worth adding?
fg.setAlternate(<airport>);

Building procedures and transitions. As mentioned above there's a couple of different ways to handle this, the examples below assume the C++ code automatically deletes and re-inserts waypoints for procedures, but that's only one possible design.

# example for sids, stars are the same, approaches too
var allSids = apt.sids();

# SIDs for a specific runway - note these return SID IDs as string, for compatibility with existing code
var rwySids = apt.sids('28L')
# or (potentially)
var rwySids = rwy.sids();
# rwySids = ['TALLA', 'GRICE', 'BEKNO'] 

# here's the actual call to get a procedure object by ID, from an airport
var sid = apt.procedure('BEKNO')
# sid is very similar to fmsTP above, has runways[], ident, type, 

var star = dest.procedure('FOOBZ')
# methods: find transition for runway, find transition for location

# given a waypoint or geod, find the closest STAR transition point
var transition = star.findTransitionFor(fp.lastEnrouteLeg());

# or some other way to compute thr transition ....

# clear all existing arrival waypoints and rebuild from the star and approach

# similarly, find the best approach for our runway
var app = star.findRunwayTransition(rwy)

# finally, delete all existing arrival waypoints from the plan, and insert new
# waypoints from the transition, STAR and approach.
# the replaced 'buildArrival' in FGRouteManager and FGAirport (which is good!)
fp.setArrivalWaypoints(star, transition, app);

Setting waypoint restrictions / clearing them / setting computed speeds and altitudes from the FMS

var wpt = fp.firstEnrouteLeg();
# specify a manual user constraint on  this WP
# presumably do this from a CDU page
wpt.setSpeed(210);
# or in Mach
wpt.setMach(0.77)

wpt.setAltitude(18000);

# or (again from a CDU page?)
wpt.deleteRestrictions();

# save some data persistently to a flight plan leg
# (maybe not saved to XML? depends on the type)
wpt.setData('customfield', ..... any nasal var .... );

Inserting and deleting waypoints (possibly in batches)

# waypoint created from lat,lon, or a navaid, or anything else?
var wpt = fp.createWaypoint(<waypoint args>)

# manually insert a waypoint into the plan
fp.insertWayptAt(index, wpt);

# route along airways, and insert a whole bunch of waypoints
# this is needed for the route-manager dialog, maybe not for a
# real FMS interface....
var segment = airways.route(<some geo pos or similar>, <some other geo pos>);
# segment is a vector of waypoints now
fp.insertWaypoints(index, segment);

# in the future, VOR-VOR routing could be added (for GA flights, although this is rathe irrelevant thanks to GPS)
var route = routeVORtoVOR(<some pos>, <some other pos>)
fp.insertWaypoints(index, route);

# any other way of building up an array of waypoints is also find - suggestions?

Callback from C++ when the flight plan is modified, to allow the FMS to rebuild pseduo-waypoints and compute the VNAV profile. This function can be fairly intensive, but it's only going to be called occasionally, in response to user interaction.

# set the callback on the flight plan
fp.setUpdateCallback(cb);

var cb = func(fp)
{
    # delete the existing pseudso waypoints
    fp.clearWPType('pseudo') 
    
    # compute T/D and T/C location
    
    var wp = fp.createPseudoWP('T/D', ... other info .....);
    fp.insertWayptAt(wp , index);
    
    var tc = fp.createPseudoWP('T/C', ....)
    fp.insertWayptAt(tc, someindex);
    
# update the calculated speed / altitude values
# this needs the difficult VNAV bit to work forwards / backwards
# along the route.
    foreach (var leg, fp.legs()) {
        if (leg.alt_cstr == 'none') {
            var altitude = .... ;
            leg.setCalculatedAlt(altitude);
        }
        
        if (leg.speed_cstr == 'none') {
            var speed = .... ;
            leg.setCalculatedSpeed(speed);
        }
        
    }
}