Implementing VNAV support in FlightGear

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.


Note  Whenever possible, please refrain from modeling complex systems, like an FDM, autopilot or Route Manager with Nasal. This is primarily to help reduce Nasal overhead (especially GC overhead). It will also help to unify duplicated code. The FlightGear/SimGear code base already contains fairly generic and efficient systems and helpers, implemented in C++, that merely need to be better generalized and exposed to Nasal so that they can be used elsewhere. For example, this would enable Scripted AI Objects to use full FDM implementations and/or built-in route manager systems.

Technically, this is also the correct approach, as it allows us to easily reuse existing code that is known to be stable and working correctly, .

For details on exposing these C++ systems to Nasal, please refer to Nasal/CppBind. If in doubt, please get in touch via the mailing list or the forum first.




Objective: Determine how to implement the building blocks required for supporting VNAV (vertical navigation) in FlightGear (YaSim/JSBSim).

Related: ticket #640.

Background

Note  In its current form, this section/article is largely based on quotes collected from various related discussions/channels (devel list, forum etc) using the Instant-Cquotes script. Wiki users and other contributors are encouraged to help rewrite/edit contents accordingly to help get rid of unnecessary quoting (i.e. while the wiki is not intended to be a collection of quotes, quotes are sometimes the best/easiest way to bootstrap new articles, while also providing a good way to link back to related discussions in the archives).

While rewriting usually only entails changing first person speech to 3rd person. However, please try to retain references/links to the original discussion whenever possible.

Cquote1.png for over a decade, FlightGear had extremely poor support for any modern avionics (PFD, ND, CDU, FMC/FMS) - in particular in comparison to MSFS/FSX and the plethora of commercial addons - this has recently changed (well since 2.8 ), due to TheTom's Canvas system, which now makes it possible for everybody to easily create modern avionics like a PFD/ND or CDU, without having to be an expert C++ programmer with OpenGL/OSG experience - but supporting VNAV/LNAV still is tricky for other reasons, and currently not on anybody's agenda - it's not just something that can/should be tackled by a single aircraft developer, it requires lower-level changes in FlightGear's architecture and the way FDMs are used.
— Hooray (Sat Nov 30). Re: "triple 7" fmc development status?.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png FlightGear's support for airliner/bizjet-style "glass" avionics is only just beginning to emerge thanks to the Canvas system, EFIS modeling (PFD, ND, CDU, EICAS etc) and aircraft systems fidelity is non-existent basically and overall FG avionics are far from anything that you can find in (even outdated versions) of MS FS and X-Plane, which applies even more so to commercial add-ons
— Hooray (Thu Jun 26). Re: What to expect from FlightGear 3.2?.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png those are limitations in FG itself (and in its supported FDMs), not specific to the 777 - it is true, that we currently do not have any aircraft with a properly working FMS and performance database support.
— Hooray (Sat Nov 30). Re: "triple 7" fmc development status?.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png Overall, the 777 remains one of the most developed aircraft/airliners in FG, especially in comparison to hundreds of other aircraft. But compared to airliners supported by FSX or X-Plane, we are still lacking in that department and do not provide a complete solution (yet). And it is also not foreseeable when that will change, because it will require changes in the FDMs we're using or use of some 3rd party/proprietary performance database. Otherwise, it is not currently feasible to come up with VNAV/LNAV modes that properly work for different aircraft. The 3rd party approach is being used for TPPs data, but in the case of FDMs, we would need 100% accurate FDMs, too - so it would be better to directly use the FDM for computing a performance DB.
— Hooray (Sat Nov 30). Re: "triple 7" fmc development status?.
(powered by Instant-Cquotes)
Cquote2.png


Cquote1.png Some of our best-developed airliners have fairly extensive performance problems, i.e. frame rate / frame spacing - for example, the 777-200. In addition, FG itself (and its FDMs) lacks some hooks to support a real performance database, so that related autoflight (M/CDU) VNAV modes cannot currently be implemented (easily). These are the main showstoppers when it comes to modern airliners and bizjets in FG.

Overall, if you're primarily interested in modern glass cockpit aircraft (or have a background/experience with proprietary add-ons), you'll probably not enjoy FG as much as FSX and/or X-Plane for the time being.


— Hooray (Thu Jun 26). Re: What to expect from FlightGear 3.2?.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png FlightGear can definitely be very frustrating for people getting started with it, unfortunately this even applies to people who are intimately familiar with other flight simulators, including real life pilots. There are some great flight simulation products out there, and some commercial add-ons are awesome and must have taken thousands of hours to develop. FlightGear is mostly a volunteer effort, i.e. 95% of the people contributing to it, do so in their spare time, while having to work within certain constraints, such as multi-platform open source development, but also the GPL itself.

Obviously, this steep gradient between features offered by FG vs. commercial counterparts is something that we're hoping to address over time - but it is often a difficult effort to literally compete with commercial and proprietary products, simply because we do not have certain resources available, so we often need to make compromises and come up with unintuitive workarounds.


— Hooray (Sat Nov 30). Re: "triple 7" fmc development status?.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png Also, please keep in mind that there's nothing really wrong about FSX (or X-Plane) - and some of the more established commercial addons are of excellent quality, developed by teams with dozens of fulltime programmers who could often consult back with ATPL holders and type-rated pilots, but also freely use all sorts of data and documentation, we do not typically have this sort of expertise and support available here - which is why some things may take a while to mature unfortunately...
— Hooray (Sat Nov 30). Re: "triple 7" fmc development status?.
(powered by Instant-Cquotes)
Cquote2.png


Problem

In comparison with other flight simulators, FlightGear has been lacking support for simulating VNAV features found on many modern aircraft since day one.

So far, the general consensus has been that it simply isn't yet possible in FlightGear to properly implement VNAV because VNAV depends on having access to an aircraft-specific performance database to provide the proper pitch/thrust changes to make certain VNAV altitude/speed/time constraints, which boils down to a lack of support by the FDMs (JSBsim/YaSim) actually.

As an open source project, we do not have access to the corresponding flight data or such a performance database, and even if we did, we probably couldn't easily use said data due to its proprietary nature.

In addition, a real performance database would probably not serve us too well, because our FDM configurations are custom, too - i.e. the performance database would need to match the performance of the simulated aircraft.

Thus, a performance DB would ideally be provided by the FDM itself. Either in a pre-created fashion, i.e. compiled during an offline "test flight", or preferably at runtime by running a simulation of the FDM inside the actual FDM.

It is one of those "holy grails" in FlightGear, we are not aware of any aircraft that currently support a proper FMC/CDU "progress" page - creating the page is trivial, but making it functional requires some more work - look for threads with the keywords "performance database" or "vnav", and you'll see that this has been talked about for over half a decade meanwhile. (computing remaining fuel vs. fuel consumption per hour is fairly trivial, but once you want to compute range, you need to be fully aware of the flight plan (speeds, altitudes) and weather/configuration (and aircraft/engine performance in particular) to make a reasonable guess at remaining range).

So, we currently do not have any aircraft with a properly working FMS and performance database support.

Overall, airliners like the 777 in FlightGear remain among the most developed aircraft/airliners in FG, especially in comparison to hundreds of other aircraft. But compared to airliners supported by FSX or X-Plane, we are still lacking in that department and do not provide a complete solution (yet).

And it is also not foreseeable when that will change, because it will require changes in the FDMs we're using or use of some 3rd party/proprietary performance database. Otherwise, it is not currently feasible to come up with VNAV modes that properly work for different aircraft. The 3rd party approach is being used for TPPs data, but in the case of FDMs, we would need 100% accurate FDMs, too - so it would be better to directly use the FDM for computing a performance DB. Supporting VNAV still is tricky for other reasons, and currently not on anybody's agenda - it's not just something that can/should be tackled by a single aircraft developer, it requires lower-level changes in FlightGear's architecture and the way FDMs are used.

Cquote1.png Altitude constraints are a mess - in the short term, they are best avoided. Keep in mind the GPS code doesn't really do proper vertical navigation - it computes some data like the altitude change and climb/ descent rate for a leg, but I'm not sure if any real aircraft is driving the VNAV mode of an autopilot from it.

It's definitely something I will look at, but it's tied up with some other features. At some point I want to support GPS precision approaches and then obviously all the VNAV logic will need to be properly cleaned up - right now it's been copied mostly unchanged from the original gps code.

As always, user stories are good - what do you expect the behaviour to be from the route-manager? Just to track valid altitudes? For the autopilot VNAV to follow the altitude profile exactly? Something in between?

This is also tied up with accurate enroute time calculations and calculating top-of-climb / top-of-descent information, it's quite a mess to unpick.[1]
— James Turner
Cquote2.png
Cquote1.png VNAV doesn't really make sense right now - it's not really implemented.[2]
— ThorstenB
Cquote2.png
Cquote1.png I don't think any aircraft AP supports vertical route navigation yet - and the route-manage itself only has partial support for editing waypoint altitude. There's no real obstacle - waypoints store an altitude (and speed), and the AP can read it, but nobody spent time making it work yet. Vertical modes (VNAV) also tend to depend more on fuel/load and engine performance data from the FMS, which is an area that's been lacking in FlightGear, so hard to model the climb and descent profiles meaningfully. [3]
— Zakalawe
Cquote2.png


Cquote1.png What you need is a simplified performance model of the aircraft or, rather, a set of simplified performance models as function of your relevant variables which in your case may only be c.g. position. Once you have such a model, predicting flight paths is not difficult. The problem is getting such a model. The only way I've ever done it was totake a full-envelope aerodynamic model, a full-envelope thrust model, trim the aircraft at lots of Mach-altitude points, and derive a simple model at each point. Generally, what you want is lift-coefficient as a function of angle of attack, a drag polar model, and some simple moment coefficients so you can predict rotational effects (such as turning and turn rate). Building a program to beat the crap out of a standard model is the only sane way to approach the issue of generating performance models. Good luck.[4]
— Lee Duke
Cquote2.png
Cquote1.png I've seen the use of simple simulation within a simulation, where performance data is being calculated. It was not within JSBSim, however. This is an interesting prospect. Nobody has ever done this, up to now. My initial reaction was pessimistic. However, the flight control components within JSBSim are fairly capable, and you are permitted to define arbitrary functions. So, my guess at this time is, yes, you should be able to set up JSBSim to calculate *some* performance data. It may be a lot of work, but it should be possible. [5]
— Jon S. Berndt
Cquote2.png


Cquote1.png Without being very familiar with the FG source code, the usual way to do this is to call the FDM. Isn't that sort of thing exactly what JSBSim is for? The distinction between a flight planner and a flight simulator is the bounds on the integrations. A simulator will integrate over a frame; a flight planner much longer.[6]
— MAKG
Cquote2.png


Cquote1.png Yes, this is an interesting problem. I worked on the Shuttle Abort Flight Management project several years ago. At each second during the shuttle ascent, several simulations were run quickly to determine viable abort locations at that instant and given the vehicle configuration. It has occurred to me that a JSBSim control <system> could be created that would use a simplified approach to predicting where the aircraft would be in ten, twenty, and thirty seconds into the future, based on current velocities, rates, and accelerations. I think it's possible. It would be a good exercise for someone![7]
— Jon S. Berndt
Cquote2.png


  1. James Turner (Thu, 08 Oct 2009 15:17:18 -0700). New route manager?.
  2. ThorstenB (Sun Sep 25, 2011 9:53 am). B777-200ER VNAV.
  3. Zakalawe (Thu Oct 13, 2011 6:03 am). Getting started with Route Manager.
  4. Lee Duke (2007-03-19 17:35:10). JSBSim Issue#29: performance database/prediction.
  5. Jon S. Berndt (2008-01-07). JSBSim Issue#29: performance database/prediction.
  6. MAKG (Thu Nov 05, 2009 11:55 am). flight path trace in autopilot mode.
  7. Jon S. Berndt (Wed Nov 11, 2009 8:48 pm). flight path trace in autopilot mode.

VNAV

The Vertical Navigation (VNAV) function of the Flight Management System (FMS) serves as an intelligent agent during all flight phases by automatically selecting appropriate targets (e.g. altitude, speed, and vertical speed) and pitch/thrust control modes to satisfy the objectives of each leg of the flightplan.

The VNAV function provides three automated features:

  • VNAV automatically selects altitude targets and speed targets according to pilot MCP entries and the altitude and speed constraints in the FMS flightplan.
  • VNAV automatically selects pitch and thrust control modes to fly to the targets. For example during descent, VNAV chooses between a FLCH descent, a vertical speed (fixed rate-of descent), and an FMS path descent. In the case where VNAV selects vertical speed control mode, VNAV also selects the vertical speed target.
  • for the descent and approach, VNAV automatically provides an optimum path that is used as the reference for all automated altitude/speed target and control mode selections.

FMS VNAV Rules:

  • VNAV does not function until all PERFORMANCE INIT information has been programmed into the CDU
  • VNAV is available for all phases of flight
  • VNAV never passes through the altitude pre-selector
  • VNAV keeps the aircraft as high as possible for as long as possible
  • VPATH angles are from 1 to 6 degrees


The typical VNAV function automatically chooses the active altitude target from a possible list of sixteen, and chooses the active speed target from a possible list of twenty-six.

For more info, see:

References

The Performance Database

There are typically two loadable databases that support the core flight management functions. These are the navigation database which must be updated on a monthly cycle and the performance database that only gets updated if there’s been a change in the aircraft performance characteristics (i.e., engine variants or structural variants affecting the drag of the aircraft).

The performance database contains aircraft/engine model data consisting of drag, thrust, fuel flow, speed/altitude envelope, thrust limits, and a variety of optimized and tactical speed schedules that are unique to the aircraft.

Typically, the performance database contains data sets for

  • take-off and configuration speeds
  • approach and landing speeds


In real aircraft, the purpose of the so called "performance database" is to reduce the need for the pilot to refer to a performance manual during flight and to provide the FMC (flight management computer) with data required to transmit pitch and thrust commands to the FCC (flight control computer. The performance database is also used by the FMC to provide detailed predictions along the entire aircraft trajectory. The data stored in the database includes accurate aircraft drag and engine model data, optimal speed data, maximum altitudes, and maximum and minimum speeds. The FMS onboard the aircraft also performs fuel consumption predictions based on a reference model: the FMS performance database. The PERF FACTOR entered in the M/CDU helps to the FMS predictions. Implementing aircraft performance monitoring aims to determine the monitored fuel factor.

The main FMS aircraft performance predictions deal with:

  • fuel consumption, time,
  • climb and descent path,
  • recommended maximum altitude, and
  • optimization of speeds and cruise altitude taking into account economic criteria

defined by the airline Cost Index.

The PDB is derived from the IFP aircraft databases. Slight simplifications were taken into account because of the limited size of the FMS memory. For example, only one air conditioning setting is available (LO/ECON as appropriate). Per design, the aircraft performance databases are stored in the FMS Perf Data Base (PDB). There is only a single PDB per family of aircraft.

At the center of the FMS functionality is the flight plan construction and subsequent construction of the four-dimensional aircraft trajectory defined by the specified flight plan legs and constraints and the aircraft performance. Flight plan and trajectory prediction work together to produce the four-dimensional trajectory and consolidate all the relevant trajectory information into a flight plan/profile buffer.

References

Trajectory Prediction

VNAV for the 777

First part of VNAV is the control of vertical movement of the aircraft. Second and more important part of that is "to predict and optimize the vertical path. Guidance includes control of the pitch axis and control of the throttle." and the other fancy stuff it can do today to optimize the flight - but only in combination with a FMS/FMC.

The 777 and the Citation X don't even use VNAV in their simplest way, i.e. by just targeting the altitude or using FLC-mode. (We don't even have a correct FLC-Mode implemented yet!!) Even if we have a realistic FMS- it would be useless without a correct Autopilot.

To develop VNAV support, we should take the 777 as it is in the base package and has currently the best developed AP. ThorstenB has put a lot of time in it, and we have an user here who knows the real one.


References

JSBSim

JSBSim is an open source Flight Dynamics Model (FDM) software library that models the flight dynamics of an aerospace vehicle. The library has been incorporated into FlightGear and OpenEaagles. It can also be called from a small standalone program to create a scripted batch simulation tool. JSBSim has been in development and use since 1996, and has been built on all of the most popular platforms in use today including those running Linux, Macintosh, and Microsoft Windows operating systems. JSBSim is written in C++ and uses XML configuration files. To date, JSBSim is the most mature FDM engine supported by FlightGear, being independently developed and maintained. JSBSim includes extensive support for systems modeling and several building blocks to simulate complex systems, including autoflight/autopilot systems.

In comparison with the YASim section below, the section on JSBSim has quickly become pretty detailed already (within just 2-3 days actually) - and it seems that unlike YASim, JSBSim is pretty straightforward to extend.

In fact, JSBSim is designed to be easily extensible via various infrastructure hooks obviously, despite a lack of any detailed documentation on extending JSBSim, and it's also easy to build from source (completely self-contained, no external dependencies), as well as being actively maintained by a sizable community of developers (vs. YASim development being de-facto stalled, and patches next to imposible to get reviewed/committed).

While JSBSim itself deals obviously with very sophisticated concepts, its code base is surprisingly straightforward actually in many places, despite being written in C++ - there's very little use of modern C++ concepts actually.

Indeed, one could say that JSBSim code looks a lot like "C with classes", so the barrier to entry is really low, even for people who really only have working C knowledge, even the legacy FlightGear code base is making use of more modern C++ concepts. JSBSim also contains many useful building blocks for creating sophisticated systems simulations, which will surely come in handy when playing with VNAV support.

Last but not least, JSBSim provides a capability to easily run and test things in a standalone mode, without having to build FlightGear from source, and without requiring any external dependencies, it also already contains various aircraft, engine models, autopilots, tests and scripted scenarios that only take seconds to get working, even for people with zero prior JSBSim/FDM exposure.

In summary, without having had any previous JSBSim/FDM experience, I've been able to make working modifications within just 10-15 minutes of playing with the code (including cloning and building from source), and by looking at the reference manual every now and then. This speaks for itself and for JSBSim and as a viable testbed, which can be also seen in the amount of useful information that's been gathered here meanwhile (some of which should probably be contributed back to the JSBSim reference manual).

For these reasons, current VNAV-related experiments are being conducted with a focus on JSBSim for the time being, even though it is understood that we will eventually need to identify common building blocks in order to also support YASim at some point. But so far, it seems that extending JSBSim with the required building blocks is pretty straightforward, so that's what we're going to use/focus on.

Getting started

It is foreseeable that we may need to modify and extend JSBSim, i.e. to add new components - unfortunately, the "Extending JSBSim" chapter in the JSBSim reference manual is still just a placeholder without any content, so here are some stubs to help lower the barrier to entry when extending JSBSim.

Status (12/2013)

  • Get & Build JSBSim Done Done
  • Experiment with modifying JSBSim Done Done
  • Document a few useful JSBSim extension examples 60}% completed
  • Hook up JSBSim standalone to FlightGear Done Done
  • Modify the existing slave/child FDM capability to instantiate a full aircraft FDM 60}% completed
  • Add a new FCS component (XML files) that instantiates a child FDM 70}% completed
  • Expose child FDM parameters to the outer FDM and make them accessible for PID/FCS components Not done Not done
  • Modify the route manager dialog to export flight plans into JSBSim/FGScript format Done Done
  • Extend the route manager dialog to also export for info for each leg, including constraints (altitude, speeds) Not done Not done
  • Extend the FGScript template to compute FPAs for each leg, and add a monitoring routine that tracks compliance Not done Not done
  • Come up with a JSBSim script (FGScript) for a test flight with multiple waypoints (different altitudes) using the c310 in the KSFO area (default scenery) 70}% completed (now automated via modified route manager/flight plan exports)
  • Extend the modified route manager dialog to show the route 70}% completed
  • Extend the route manager to also show a vertical profile using a canvas-driven altitude/distance graph Not done Not done
  • Extend the systems/GNCUtilities.xml file to also support altitudes and compute required FPAs for each waypoint Not done Not done
  • look into encoding min/max pitch and thrust settings as part of the script, so that safety margins are not violated Not done Not done
  • ....
  • port the c310 example and use the 737 instead, because it better matches airliners available in FG (will also need AP work) Not done Not done
  • look into adapting FlightGear accordingly Not done Not done

Simulation within Simulation

Update: 12/2013

Due to the lack of a real performance DB, one of the earliest attempts related to this was running a 2nd (nested) FDM instance next to the main FDM (i.e. in parallel) with a greater time step (dT) when integrating, in order to gather values from it about the future state of the simulation, such as e.g. position in 10 seconds, 30 seconds or 10 minutes.

This turned out to be pretty straightforward to play with in JSBSim so far. Obviously, this bit could have also been accomplished with existing Autopilot components in FlightGear or JSBSim, with much less work, because it just involves an extrapolation/prediction.

However, the next step will involve exposing FDM parameters to the outer FDM/FCS, so that pitch and thrust settings can be modified via properties.

Basically, the idea is to initialize the 2nd FDM instance with values from the first/outer FDM (position, speeds, orientation, weight & balance etc) integrate over a longer time frame (less granularity).

This will allow us to ask questions like "where am I going to be in 5 minutes if I change pitch by 1 degree and thrust by 5%", i.e. stuff that cannot be easily solved by extrapolation, because it involves the whole world model, such as different altitude (air density), fuel consumption, temperature, winds etc.

The cool thing is, there's no guesswork or fancy maths involved here to make this work, because the real FDM will be running and provide the corresponding answers to the outer FDM (or PID controller!). Which will also allow us to determine how much fuel will be left at certain points along the route.

Subsequently, we need a way to encode flight plans in VNAV terms, which mostly boils down to formalizing typical VNAV constraints.

For starters, we can focus on flight path angles (FPAs), because that's what all VNAV/FMS internally work with. Computing the FPA between two waypoints and altitudes is also straightforward to do, i.e. just a matter of calculating the great circle distance (via Haversine, already supported by both, JSBSim and FG), and the altitude difference, i.e. groundspeed vs. altitude gradient required to maintain the FPA.

Thus, a basic test scenario would involve a route with various waypoints and different altitudes, so that we end up with different FPAs (gradients) to maintain on each leg. A PID controller (or FCS) could then basically use the 2nd nested/embedded FDM instance to come up with pitch/thrust settings for each leg and assemble a list of pitch/thrust settings.

In Pseudo-Code:

foreach(var waypoint; waypoints) {
 # compute distance between this and the next waypoint
 # compute altitude difference between this and the next waypoint
 # turn this into a gradient (altitude vs. groundspeed) and flight path angle (min/max FPA required) for the FMS 
 # add the FPA info to each leg of the flight plan (route manager in FG)
}

The second FDM (running as a child FDM), would then be presented with a list of waypoints and FPAs, and asked for pitch/thrust settings to make each gradient.

In its first iteration, we will keep this straightforward and focus only on the current leg, without applying any restrictions (such as 250 kts below FL100).

However, we will also need to really solve this in reverse for the whole route (like a real flight planning tool/FMS), i.e. each leg: In Pseudo-Code:

foreach(var waypoint; reverse(waypoints) ) {
 # get required FPA
 # get relevant constraints (groundspeed, vertical speed, min/max thrust and pitch)
 # compute a range of usable pitch/thrust settings to satisfy the constraints
 # rinse & repeat
}


Obviously, we need a way to also encode min/max speeds for different altitudes and aircraft configurations, so that the 2nd FDM doesn't come up with values that are outside safety margins. The 2nd FDM would also need a way to encode typical messages like "drag required" for example, whenever it cannot achieve a certain FPA without additional drag, i.e. when pitch/thrust changes alone won't suffice.

At that point it should be relatively straightforward to move some parts back into FlightGear, i.e. its AP system, which could have a new component that runs another FDM instance as part of a PID controller cascade to come up with pitch/thrust settings for each VNAV constraint.

Aircraft developers would then be required to encode aircraft-specific constraints that can be used by the system (such as min/max pitch, configuration speeds etc). Runtime-specific constraints would need to be set up via the property tree, so that they can be entered via the CDU/FMC (or route manager). This would typically involve different ECON profiles etc.

Standalone JSBSim

The standalone JSBSim interpreter is implemented in src/JSBSim.cpp - all options processing takes place in bool options(int count, char **arg), this is also the place where you can easily add new startup options, see for example:

diff --git a/src/JSBSim.cpp b/src/JSBSim.cpp
index 2a8b225..0dfa52d 100644
--- a/src/JSBSim.cpp
+++ b/src/JSBSim.cpp
@@ -680,8 +680,10 @@ bool options(int count, char **arg)
         exit(1);
       }
 
-    }
-    else //Unknown keyword so print the help file, the bad keyword and abort
+    } else if (keyword == "--new-option") {
+       std::cerr << "--new-option= works (value is:"<< value << ")- but not yet implemented-exiting!" << std::endl;
+       exit(1); 
+    } else //Unknown keyword so print the help file, the bad keyword and abort
     {
           PrintHelp();
           cerr << "The argument \"" << keyword << "\" cannot be interpreted as a file name or option." << endl;
@@ -735,8 +737,8 @@ void PrintHelp(void)
     cout << "    --simulation-rate=<rate (double)> specifies the sim dT time or frequency" << endl;
     cout << "                      If rate specified is less than 1, it is interpreted as" << endl;
     cout << "                      a time step size, otherwise it is assumed to be a rate in Hertz." << endl;
-    cout << "    --end=<time (double)> specifies the sim end time" << endl << endl;
-
+    cout << "    --end=<time (double)> specifies the sim end time" << endl;
+    cout << "    --new-option=(value) a  new JSBSim option" << endl << endl;
     cout << "  NOTE: There can be no spaces around the = sign when" << endl;
     cout << "        an option is followed by a filename" << endl << endl;
 }

Adding new Telnet commands

Analogous to FlightGear's telnet/props system, JSBSim has a similar mechanism for remotely debugging/inspecting a running JSBSim instance, this can be enabled by adding this to the FDM (XML file):

<input port="4000"/>

You can then connect to the running FDM instance by using this

telnet localhost 4000

The protocol is fairly simple and straightforward to extend, see /src/models/FGInput.cpp, to add a new command you just need to edit the bool FGInput::Run(bool Holding) method and add a new command, e.g. to a new test command that returns Hello World:

diff --git a/src/models/FGInput.cpp b/src/models/FGInput.cpp
index e189a49..1d1b4c4 100644
--- a/src/models/FGInput.cpp
+++ b/src/models/FGInput.cpp
@@ -234,11 +234,15 @@ bool FGInput::Run(bool Holding)
         "   hold\n"
         "   resume\n"
         "   iterate {value}\n"
+       "   test (just a test command\n"
         "   help\n"
         "   quit\n"
         "   info\n\n");
-
-      } else {
+       }
+       else if(command == "test") {
+               socket->Reply("Hello World from JSBSim !\n");
+       }
+        else {
         socket->Reply(string("Unknown command: ") +  token + string("\n"));
       }

I/O

For more sophisticated I/O needs, there's a separate folder in src/input_output/, which contains various examples on implementing FGOutput based I/O protocols. This is also the place where the FG interfacing machinery is to be found, specifically see FGOutputFG.cxx (basically, FlightGear's NetFDM struct is the transport means for any JSBSim/FG synchronization needs).

Interfacing to FlightGear

FlightGear can be used to visualize JSBSim standalone runs. To make use of the FlightGear interface, you need to edit your JSBSim/FDM xml file and add an output section to the toplevel fdm_config node:

<output name="localhost" type="FLIGHTGEAR" port="5600" rate="60" protocol="UDP"/>

This tells JSBSim to use port 5600 (via UDP) at a rate of 60 hz to send FDM updates to the corresponding FlightGear instance.

Next, to slave FlightGear to JSBSim in standalone mode via port 5600, you would use these additional parameters to disable the built-in FDM and enable native-fdm slaving at a frequency of 60hz, via UDP (localhost).

--fdm=null --native-fdm=socket,in,60,,5600,udp
--aircraft=c310-yasim
--airport=kgls

Obviously, it would also make sense to directly start up FlightGear at a location close to your initial FDM settings (which is why we added the KGLS setting). (Type of aircraft doesn't really matter, the FDM output will be applied to any model, no matter if it's a 747, the ufo or the ogeL).

After making these changes, and after starting FlightGear, it's time to start JSBSim, too:

JSBSim  --root=$HOME/sources/jsbsim-code/ --aircraft=c310 --initfile=ellington

Typically, you'll however want to use FGScript to create a completely scripted JSBSim run, which allows you to easily test new FDM features (and simulated systems) non-interactively:

JSBSim  --root=$HOME/sources/jsbsim-code/ --script=c3105

This will run the script in the folder scripts/c3105.xml, which in turn loads the c310 FDM, the initial settings from the ellington.xml file and the autopilot from c310ap.xml The flight can now be easily visualized and tracked using FlightGear integrated Map dialog (see screen shot on the right). While it normally take ~30 minutes to complete (using the --realtime option), JSBSim standalone can complete the entire flight in roughly 45 seconds.

Map dialog visualizing a scripted test flight via JSBSim standalone

FGScript

JSBSim is entirely built around the Property Tree concept (actually an outdated version of it - because JSBSim doesn't depend on SimGear, the old property tree code is still used in the source tree), scripting in JSBSim is also implemented on top of the property tree, it isn't really "scripting" per se, it's properties + XML, and some JSBSim specific building blocks to support events, conditions, notifications and some custom expressions like JSBSim functions.

Surprisingly, this rather simple mechanism is pretty powerful. To learn more about JSBSim's FGScript itself, you will want to check out the JSBSim Scripting Tutorial. Scripting examples are to be found in the scripts/ directory.

The scripting engine in JSBSim is to be found in src/input_output/FGScript.cpp - scripts are basically just XML files that are evaluated once during initialization, to set up several data structures (that are later on evaluated in the FGFDMexec()::Run by calling FGScript::RunScript), namely:

  • initialization stuff (aircraft, initialization file (position, altitude, etc)
  • input and output settings
  • local property/value settings
  • startup parameters (start time/end time and update rate of the simulation)
  • an arbitrary number of <event/> blocks

Scripts are loaded via bool FGScript::LoadScript()

A test profile

We will need a test profile with different waypoints and altitude constraints to perform various test flights, and do PID tuning. Creating FGScript files manually is pretty tedious and error-prone. However, using a slightly modified version of FlightGear's Route Manager dialog, we can easily export FlightGear flight plans into JSBSim/FGScript files and create various profiles automatically via Nasal.

route manager dialog with added support for exporting to JSBSim/FGScript XML files

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://jsbsim.sf.net/JSBSimScript.xsl"?>

<runscript xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://jsbsim.sf.net/JSBSimScript.xsd"
    name="C310-01A ksfo approach run" flightgear-version="2.99.0" 
    creation-date="2013-12-07T18:39:39">




<!-- this is an automatically created JSBSim FGScript XML file, see $FG_ROOT/gui/dialogs/route-manager.xml 
     DO NOT EDIT MANUALLY - edit $FG_ROOT/gui/dialogs/jsbsim-fgscript.template instead 
-->
  <description>For basic VNAV experiments</description>
  <!-- fdm we are using, and name of XML file we use to initialize our position  -->
  <use aircraft="c310" initialize="ksfo-approach"/>
  
  <run start="0.0" end="3600" dt="0.008333333">
    <property value="0"> simulation/notify-trigger </property>

    <event name="Standard Notify" persistent="true">
      <description>Output message at periodic intervals</description>
      <condition> simulation/notify-trigger ge 0.5 </condition>
      <set name="simulation/notify-trigger" value="0"/>
      <notify>
<!-- list of properties we want to inspect during each notification -->
        <property>ap/active-waypoint</property>
	<property>guidance/wp-distance</property>
        <property>guidance/target_wp_latitude_rad</property> 
        <property>guidance/target_wp_longitude_rad</property> 
  	<property>ap/altitude_setpoint</property>
 
        <property>position/h-agl-ft</property> 
        <property>velocities/vt-fps</property>
<!--
        <property>propulsion/engine[0]/propeller-rpm</property>
        <property>propulsion/engine[1]/propeller-rpm</property>
        <property>propulsion/engine[0]/power-hp</property>
        <property>propulsion/engine[1]/power-hp</property>
        <property>propulsion/engine[0]/thrust-lbs</property>
        <property>propulsion/engine[1]/thrust-lbs</property>
        <property>propulsion/engine[0]/blade-angle</property>
        <property>propulsion/engine[1]/blade-angle</property>
        <property>fcs/rudder-pos-rad</property>
        <property>fcs/mixture-pos-norm[1]</property>
        <property>fcs/yaw-trim-sum</property>
-->
       </notify>
    </event>

<!--	Strict typing: Since the units system (XML) isn't exposed/enforced via properties, some sanity checks here 
	Best practice: add this to each system/component for I/O properties 
-->

	<event name="is_valid_azimuth_check failed">
		<description>ERROR:Ensure that property value is within valid range</description>
		<condition logic="OR">
			ap/heading_setpoint gt 360
			ap/heading_setpoint lt 0.00
		</condition>
		<set name="simulation/terminate" value="1"/>

		<notify>
			<property>ap/heading_setpoint</property>
		</notify>
	</event>

	<event name="is_valid_radian_check failed">
		<description>ERROR:Ensure that property value is within valid range</description>
		<condition logic="OR">
			guidance/target_wp_latitude_rad gt 1.6
			guidance/target_wp_latitude_rad lt -1.6

			guidance/target_wp_longitude_rad gt 3.15
			guidance/target_wp_longitude_rad lt -3.15
	
		</condition>
		<set name="simulation/terminate" value="1"/>

		<notify>
			<property>guidance/target_wp_latitude_deg</property>
			<property>guidance/target_wp_longitude_rad</property>

		</notify>
	</event>

<!-- basic aircraft setup FIXME: trim in-air instead or do a proper takeoff from KSFO 100L-->
 
    <event name="Start engine">
      <description>
        Start engine and set initial heading and waypoints, turn on heading-hold mode.
      </description>
      <condition>simulation/sim-time-sec  ge  0.25</condition>
      <set name="fcs/mixture-cmd-norm[0]" value="1"/>
      <set name="fcs/mixture-cmd-norm[1]" value="1"/>
      <set name="fcs/advance-cmd-norm[0]" value="1.0"/>
      <set name="fcs/advance-cmd-norm[1]" value="1.0"/>
      <set name="propulsion/magneto_cmd" value="3"/>
      <set name="fcs/throttle-cmd-norm[0]" value="1.0"/>
      <set name="fcs/throttle-cmd-norm[1]" value="1.0"/>
      <set name="propulsion/starter_cmd" value="1"/>
      <set name="ap/attitude_hold" value="0"/>

 	<!-- initial waypoint: BARTN -->
      <set name="guidance/target_wp_latitude_rad" value="0.65534383642553"/>
      <set name="guidance/target_wp_longitude_rad" value="-2.130765632268315"/>

      <set name="ap/heading_setpoint" value="100"/>
      <set name="ap/heading-setpoint-select" value="0"/>
      <set name="ap/heading_hold" value="1"/>
      <set name="ap/yaw_damper" value="1"/>
      <set name="ap/active-waypoint" value="0"/>
      <set name="ap/altitude_setpoint" action="exp" value="5000.0" tc="10"/>
      <set name="simulation/notify-trigger" value="1"/>

      <notify>
	<property>ap/altitude_setpoint</property>
      </notify>
    </event>

    <event name="Raise landing gear">
      <condition>position/h-agl-ft  ge  40</condition>
      <set name="gear/gear-cmd-norm" value="0"/>
      <set name="fcs/advance-cmd-norm[0]" action="exp" value="0.85" tc="1.0"/>
      <set name="fcs/advance-cmd-norm[1]" action="exp" value="0.85" tc="1.0"/>
      <notify/>
    </event>

    <event name="Full Throttle">
      <condition>velocities/vc-fps ge 300</condition>
      <set name="fcs/throttle-cmd-norm[0]" value="1"/>
      <set name="fcs/throttle-cmd-norm[1]" value="1"/>
      <set name="simulation/notify-trigger" value="1"/>
      <set name="ap/altitude_setpoint" value="5000.0" tc="1.0"/>
      <set name="ap/altitude_hold" value="1"/>
      <notify>
	<property>ap/altitude_setpoint</property>
      </notify>
    </event>

    <event name="fly to first waypoint">
      <description>
        Set heading hold to selected waypoint (setpoint) instead of
        previously specified heading
      </description>
      <condition>simulation/sim-time-sec  ge  .5</condition>
      <set name="ap/heading-setpoint-select" value="1"/>
      <set name="ap/active-waypoint" value="1"/>
      <set name="simulation/notify-trigger" value="1"/>
 
      <notify>
        <property>guidance/wp-distance</property>
       </notify>
    </event>

   <event name="Terminate">
      <description> terminate
      </description>
      <condition>
        ap/active-waypoint eq 9
      </condition>
      <set name="simulation/terminate" value="1"/>
      <set name="simulation/notify-trigger" value="1"/>
 
      <notify>
        <property>guidance/wp-distance</property>
	<property>ap/altitude_setpoint</property>

       </notify>
    </event>
 
<!-- end of predefined dependencies -->

<!-- begin of automatically inserted waypoint events, see $FG_ROOT/gui/dialogs/route-manager.xml -->


<event name="Waypoint:8" continuous="false" persistent="false">
	<description>Waypoint #8 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 8
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.70824999999999/lon:-122.5996669999999 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.6581331169131249"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.139767848553598"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="0"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.6581331169131249"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.139767848553598"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="0"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="9"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:7" continuous="false" persistent="false">
	<description>Waypoint #7 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 7
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.571778/lon:-122.257861 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.655751231179065"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.133802208457343"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="5500"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.655751231179065"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.133802208457343"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="5500"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="8"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:6" continuous="false" persistent="false">
	<description>Waypoint #6 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 6
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.53594399999999/lon:-122.172889 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.65512580989562"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.132319167287033"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="1800"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.65512580989562"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.132319167287033"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="1800"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="7"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:5" continuous="false" persistent="false">
	<description>Waypoint #5 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 5
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.503528/lon:-122.096139 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.6545600439659401"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.130979627087658"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="3000"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.6545600439659401"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.130979627087658"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="3000"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="6"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:4" continuous="false" persistent="false">
	<description>Waypoint #4 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 4
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.461694/lon:-121.996528 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.6538299029274951"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.12924108716844"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="4500"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.6538299029274951"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.12924108716844"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="4500"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="5"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:3" continuous="false" persistent="false">
	<description>Waypoint #3 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 3
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.812167/lon:-121.747083 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.6599468107098476"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.124887450620778"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="9000"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.6599468107098476"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.124887450620778"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="9000"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="4"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:2" continuous="false" persistent="false">
	<description>Waypoint #2 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 2
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.548436/lon:-122.083878 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.65534383642553"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.130765632268315"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="7500"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.65534383642553"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.130765632268315"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="7500"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="3"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 


<event name="Waypoint:1" continuous="false" persistent="false">
	<description>Waypoint #1 </description>
	<!-- set up condition to trigger this waypoint -->
	<condition logic="AND">
		ap/active-waypoint eq 1
		guidance/wp-distance lt 1000 
	</condition>

	<!-- waypoint lat:37.62084548290601/lon:-122.3810480192308 -->
	<!-- coordinates in radians according to GNCUtilities.xml -->
	<set name="guidance/target_wp_latitude_rad" type="value"  value="0.6566076203104624"/>
	<set name="guidance/target_wp_longitude_rad" type="value" value="-2.13595222753618"/>

	<!-- add altitude TODO: speed constraints here -->
	<set name="ap/altitude_setpoint" type="value" value="4000"/>

	<!-- leg stuff here (not yet used)-->
	<!--
	<set name="guidance/leg/wp1/latitude-rad"  	type="value" value="0.6566076203104624"/>
	<set name="guidance/leg/wp1/longitude-rad" 	type="value" value="-2.13595222753618"/>
	<set name="guidance/leg/wp1/altitude-ft"	type="value" value="4000"/>
	-->

	<!-- now, set signal to activate next waypoint -->
	<set name="ap/active-waypoint" type="value" value="2"/>
	<set name="simulation/notify-trigger" type="value" value="1"/>
	<notify>
		<property>guidance/wp-distance</property>
 		<property>ap/altitude_setpoint</property>
	</notify>
</event> 

<!--end of waypoints/events -->
  </run>
</runscript>

Adding new Functions

Here's a stub that demonstrates how to add a new mathematical function named myFunction:

diff --git a/src/math/FGFunction.cpp b/src/math/FGFunction.cpp
index 6beeedb..03c38e0 100644
--- a/src/math/FGFunction.cpp
+++ b/src/math/FGFunction.cpp
@@ -107,6 +107,8 @@ const std::string FGFunction::not_string = "not";
 const std::string FGFunction::ifthen_string = "ifthen";
 const std::string FGFunction::switch_string = "switch";
 const std::string FGFunction::interpolate1d_string = "interpolate1d";
+const std::string FGFunction::myfunction_string = "myfunction";
+
 
 FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& prefix)
                                       : PropertyManager(propMan), Prefix(prefix)
@@ -228,6 +230,8 @@ FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& pr
     Type = eSwitch;
   } else if (operation == interpolate1d_string) {
     Type = eInterpolate1D;
+  } else if (operation == myfunction_string) {
+    Type = eMyFunction;
   } else if (operation != description_string) {
     cerr << "Bad operation " << operation << " detected in configuration file" << endl;
   }
@@ -765,6 +769,9 @@ double FGFunction::GetValue(void) const
     else // 
     {temp = 1;}
     break;
+  case eMyFunction:
+    cerr << "FIXME: Implement myFunction!" << endl;
+    break;
   default:
     cerr << "Unknown function operation type" << endl;
     break;
diff --git a/src/math/FGFunction.h b/src/math/FGFunction.h
index 49d3e15..e5ba140 100644
--- a/src/math/FGFunction.h
+++ b/src/math/FGFunction.h
@@ -799,6 +799,7 @@ private:
   static const std::string ifthen_string;
   static const std::string switch_string;
   static const std::string interpolate1d_string;
+  static const std::string myfunction_string;
   double cachedValue;
   enum functionType {eTopLevel=0, eProduct, eDifference, eSum, eQuotient, ePow, eSqrt, eToRadians,
                      eToDegrees, eExp, eAbs, eSign, eSin, eCos, eTan, eASin, eACos, eATan, eATan2,
@@ -806,7 +807,7 @@ private:
                      eLog2, eLn, eLog10, eLT, eLE, eGE, eGT, eEQ, eNE,  eAND, eOR, eNOT,
                      eIfThen, eSwitch, eInterpolate1D, eRotation_alpha_local,
                      eRotation_beta_local, eRotation_gamma_local, eRotation_bf_to_wf,
-                     eRotation_wf_to_bf} Type;
+                     eRotation_wf_to_bf, eMyFunction } Type;
   std::string Name;
   std::string sCopyTo;        // Property name to copy function value to
   FGPropertyNode_ptr pCopyTo; // Property node for CopyTo property string

Adding new Control System Components

We'll probably want to extend JSBSim's FCS framework, to allow VNAV-related components to be registered and parametrized, such as for example:

  • flight-plan (encapsulates route, leg, waypoints and constraints)
  • trajectory-predict (runs a 2nd FDM instance to "look-ahead" for some point in time)
  • performance-compute (computes pitch/thrust changes for a certain VNAV constraint)
  • guidance-vertical


So, for illustration purposes, here's a stub to add an empty FGDummy component to JSBSim:

diff --git a/src/models/FGFCS.cpp b/src/models/FGFCS.cpp
index 59deaaf..5457632 100644
--- a/src/models/FGFCS.cpp
+++ b/src/models/FGFCS.cpp
@@ -64,6 +64,7 @@ INCLUDES
 #include "models/flight_control/FGWaypoint.h"
 #include "models/flight_control/FGAngles.h"
 #include "models/flight_control/FGDistributor.h"
+#include "models/flight_control/FGDummy.h"
 
 #include "FGFCSChannel.h"
 
@@ -642,6 +643,8 @@ bool FGFCS::Load(Element* el, SystemType systype)
           newChannel->Add(new FGAngles(this, component_element));
         } else if (component_element->GetName() == string("distributor")) {
           newChannel->Add(new FGDistributor(this, component_element));
+	} else if (component_element->GetName() == string("dummy")) {
+          newChannel->Add(new FGDummy(this, component_element));
         } else {
           cerr << "Unknown FCS component: " << component_element->GetName() << endl;
         }
diff --git a/src/models/flight_control/CMakeLists.txt b/src/models/flight_control/CMakeLists.txt
index e9a1a8d..8a67322 100644
--- a/src/models/flight_control/CMakeLists.txt
+++ b/src/models/flight_control/CMakeLists.txt
@@ -14,7 +14,8 @@ set(JSBSIM_MODELS_FLIGHT_CONTROL_SRC FGDeadBand.cpp
                                      FGMagnetometer.cpp
                                      FGAngles.cpp
                                      FGWaypoint.cpp
-                                     FGDistributor.cpp)
+                                     FGDistributor.cpp
+				     FGDummy.cpp)
 
 set(JSBSIM_MODELS_FLIGHT_CONTROL_HDR FGDeadBand.h
                                      FGFCSComponent.h
@@ -33,7 +34,8 @@ set(JSBSIM_MODELS_FLIGHT_CONTROL_HDR FGDeadBand.h
                                      FGSensorOrientation.h
                                      FGAngles.h
                                      FGWaypoint.h
-                                     FGDistributor.h)
+                                     FGDistributor.h
+				     FGDummy.h)
 
 add_library(FlightControl ${JSBSIM_MODELS_FLIGHT_CONTROL_HDR}
                           ${JSBSIM_MODELS_FLIGHT_CONTROL_SRC})
diff --git a/src/models/flight_control/FGDummy.cpp b/src/models/flight_control/FGDummy.cpp
new file mode 100644
index 0000000..5c4dcf4
--- /dev/null
+++ b/src/models/flight_control/FGDummy.cpp
@@ -0,0 +1,28 @@
+#include <iostream>
+ 
+#include "FGDummy.h"
+#include "input_output/FGXMLElement.h"
+
+using namespace std;
+
+namespace JSBSim {
+
+FGDummy::FGDummy(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
+{
+}
+
+
+FGDummy::~FGDummy()
+{
+
+}
+
+
+bool FGDummy::Run(void )
+{
+
+  return true;
+}
+
+} //namespace JSBSim
+
diff --git a/src/models/flight_control/FGDummy.h b/src/models/flight_control/FGDummy.h
new file mode 100644
index 0000000..e4b0eeb
--- /dev/null
+++ b/src/models/flight_control/FGDummy.h
@@ -0,0 +1,32 @@
+#ifndef FGDUMMY_H
+#define FGDUMMY_H
+
+#include <iostream>
+#include <cstdlib>
+
+#include "FGFCSComponent.h"
+#include "math/FGCondition.h"
+#include "math/FGPropertyValue.h"
+#include "math/FGRealValue.h"
+
+namespace JSBSim {
+
+class FGDummy  : public FGFCSComponent
+{
+public:
+  /** Constructor
+      @param fcs a pointer to the parent FGFCS class
+      @param element a pointer to the Element (from the config file XML tree)
+             that represents this distributor component */
+  FGDummy(FGFCS* fcs, Element* element);
+
+  /// Destructor
+  ~FGDummy();
+
+  bool Run(void);
+
+private:
+
+}; // FGDummy class
+} // of namespace JSBSim
+#endif

FGWaypoint

There are some basic building blocks for supporting waypoints via the Haversine formula in $JSBSIM_SRC/model/flight_controls/FGWaypoint.cpp, currently these are:

  • waypoint_heading (computes the heading to the waypoint)
  • waypoint_distance (computes the distance between to waypoints)

The FGWaypoint class would need to be extended to help with the calculation of other types of waypoints related to VNAV. To add a stub for a new waypoint named waypoint_custom, these would be the required changes (lacking any implementation in FGWaypoint::Run() ):

  • edit the src/models/flight_control/FGWaypoint.h header file add a new enum for your custom waypoint type
  • edit the /src/models/flight_control/FGFCSComponent.cpp file to add a type mapping for the new waypoint
  • edit src/models/FGFCS.cpp FGFCS::Load() to map the new waypoint to the FGWaypoint class
  • implement the new functionality
diff --git a/src/models/FGFCS.cpp b/src/models/FGFCS.cpp
index 5457632..fc921cb 100644
--- a/src/models/FGFCS.cpp
+++ b/src/models/FGFCS.cpp
@@ -636,7 +636,8 @@ bool FGFCS::Load(Element* el, SystemType systype)
         } else if (component_element->GetName() == string("gyro")) {
           newChannel->Add(new FGGyro(this, component_element));
         } else if ((component_element->GetName() == string("waypoint_heading")) ||
-                   (component_element->GetName() == string("waypoint_distance")))
+                   (component_element->GetName() == string("waypoint_distance")) ||
+	    	   (component_element->GetName() == string("waypoint_custom")) )
         {
           newChannel->Add(new FGWaypoint(this, component_element));
         } else if (component_element->GetName() == string("angle")) {
diff --git a/src/models/flight_control/FGFCSComponent.cpp b/src/models/flight_control/FGFCSComponent.cpp
index 630f5cb..29ebbdc 100644
--- a/src/models/flight_control/FGFCSComponent.cpp
+++ b/src/models/flight_control/FGFCSComponent.cpp
@@ -110,6 +110,8 @@ FGFCSComponent::FGFCSComponent(FGFCS* _fcs, Element* element) : fcs(_fcs)
     Type = "WAYPOINT_HEADING";
   } else if (element->GetName() == string("waypoint_distance")) {
     Type = "WAYPOINT_DISTANCE";
+  } else if (element->GetName() == string("waypoint_custom")) {
+    Type = "WAYPOINT_CUSTOM";
   } else if (element->GetName() == string("angle")) {
     Type = "ANGLE";
   } else if (element->GetName() == string("distributor")) {
diff --git a/src/models/flight_control/FGWaypoint.cpp b/src/models/flight_control/FGWaypoint.cpp
index 0df8d07..4c5b707 100644
--- a/src/models/flight_control/FGWaypoint.cpp
+++ b/src/models/flight_control/FGWaypoint.cpp
@@ -58,6 +58,9 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, eleme
 {
   if      (Type == "WAYPOINT_HEADING")  WaypointType = eHeading;
   else if (Type == "WAYPOINT_DISTANCE") WaypointType = eDistance;
+  else if (Type == "WAYPOINT_CUSTOM") WaypointType = eCustom; 
+
+
 
   target_latitude_unit = 1.0;
   target_longitude_unit = 1.0;
@@ -123,7 +126,7 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, eleme
   } else {
       eUnit = eRad; // Default is radians if unspecified
     }
-  } else {
+  } else if (WaypointType == eDistance) {
     if (!unit.empty()) {
     if      (unit == "FT") eUnit = eFeet;
     else if (unit == "M")  eUnit = eMeters;
@@ -131,7 +134,10 @@ FGWaypoint::FGWaypoint(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, eleme
     } else {
       eUnit = eFeet; // Default is feet if unspecified
     }
-  }
+   }
+    else if (WaypointType ==eCustom) {
+    }
+
 
   FGFCSComponent::bind();
   Debug(0);
diff --git a/src/models/flight_control/FGWaypoint.h b/src/models/flight_control/FGWaypoint.h
index 4444671..0aac0db 100644
--- a/src/models/flight_control/FGWaypoint.h
+++ b/src/models/flight_control/FGWaypoint.h
@@ -123,7 +123,7 @@ private:
   double radius;
   std::string unit;
   enum {eNone=0, eDeg, eRad, eFeet, eMeters} eUnit;
-  enum {eNoType=0, eHeading, eDistance} WaypointType;
+  enum {eNoType=0, eHeading, eDistance, eCustom} WaypointType;
 
   void Debug(int from);
 };

Instancing another FDM

The whole VNAV effort would be greatly simplified if we could directly run and query the FDM for some future point in time, for example to run a trajectory prediciton calculatoin. This isn't directly supported by JSBSim at the moment. However, as a workaround, we could run a "simulation inside the simulation" by instantiating the JSBSim FDM recursively:

As in, run an independent instance of the FDM as part of the FCS, to solve for certain VNAV constraints (altitude, speed, time) via specific pitch/thrust settings. Instantiating a JSBSim FDM instance is straightforward, thanks to the FGFDMExec class, which manages a single JSBSim instance:

FGFDMExec* fdmex = new FGFDMExec(); // (1) Instantiation
bool result = fdmex->LoadModel("737"); // (2) Model loading
fdmex->Run();

Parent & Child FDMs

And as it turns out, being able to instantiate child/slave FDMs is another long-standing idea/feature request, i.e. supporting recursion in JSBSim FDMs and having multiple FDM instances per process (single-threaded). So to better understand the design of this feature and its current limitations, let's look at the original design/development discussions:

Cquote1.png One feature in JSBSim that I began and have not yet finished (pending other things) is a parent/child capability. You can (for instance) load a Mk82 on

an F-16 and the physical effects of the Mk82 will affect the F-16. The position is based on the position of the parent, and so is the orientation. However, the Mk82 position is not integrated, it always has a position based on the parent, UNTIL it is released. Then, the EOM begins integrating and the flyout is physics based.

The child objects are each their own individual JSBSim objects in their own right, and each could be loaded and flown by itself. The JSBSim executive is sort of recursive in this way. Each would be addressable in a way that I think Tony set up with Properties, but I can't recall what it is.

Technically, it is also possible for each droppable piece to have its own propulsion and flight control system, too. The possiblities there are quite

broad.[1]
— Jon S. Berndt
Cquote2.png
Cquote1.png Of course we would like a parent/child FDM system, and from all these postings is seems as if we nearly have. In the order of things, I don't think it is very high on the todo list.[2]
— Vivian Meazza
Cquote2.png


Cquote1.png One thing I've been playing around with (OK, _played_ around with - about a year ago) in JSBSim was the ability to add "child" objects to the main aircraft. Each child would be a separate instance of a JSBSim FDM. The idea would be to provide a way to model an aircraft with attached stores. Each store would transmit forces and moments to the parent aircraft (for example a Mirage) until it was released, at which time the FDM for that child object (for example a Mk-82 or AIM-9) would start integrating and do its own flyout - the parent aircraft now free of the effects of the store. Additionally, each store would have its own config file - and its own flight control system/autopilot/guidance. In theory, the possibilities are limitless for game playing, where one might <PC> set up a food drop tank to be released from an aircraft and make a mid-air "connection" to another aircraft, mid-flight</PC>, or <PC> a water delivery tank could be guided by homing device to a precision landing extremely close to a hostile anti-aircraft battery or needy family (in some cases these are coincident)</PC>. I've been threatening to complete this for a long time, but one day we'll allwake up and I'll announce that it is finished. [3]
— Jon S. Berndt
Cquote2.png
Cquote1.png That feature has not yet been implemented completely or tested. I started that one a long time ago, but there were aspects of it that I did not have time to explore. I still believe it will be possible to implement, but it will have to wait until after 1.0 is release (at least).

Also, I'm not sure that the capability as designed is what you are looking for. It was initially conceived as a way to have a multi-component vehicle (a staged rocket, for instance, or an aircraft and mounted missiles).

So, it sounds like you want to model multiple aircraft, but only one JSBSim instance? I don't know for sure how to solve your specific problem, but I wonder if JSBSim.cpp could be modified to start up several instances of FGFDMExec and manage those? Would that meet your requirement?[4]
— Jon S. Berndt
Cquote2.png


Cquote1.png Any time a multiple FDM would be used, the child FDMs would be "resident" for the entire run that the parent was in existence. Child FDMs will not ever be removed and additional ones added during a run. It is possible that a child FDM may cease integrating, but child FDMs are inherent and inseparable characteristics of a parent/child flight model.[5]
— Jon S. Berndt
Cquote2.png
Cquote1.png Multiple FDMs would be designed to work within Flightgear or standalone. The main model would always have the base property tree names. I assume that any other capability or feature such as nasal would not be affected. FlightGear would not know the difference - this is not a FlightGear capability. Multiple FDMs would be managed and run using JSBSim features. There is no difference between a single FDM and multi-FDM with JSBSim as far as FlightGear is concerned. The base aircraft will always be the base property tree - the first property tree. Let's say that the aircraft is the B-52 carrying the X-15. In the B-52 there may be a switch that tells a JSBSim system to drop the X-15. At that point, the X-15 would begin flying (the integration of the X-15 Equations of Motion would start). Obviously, there would need to be a way to link up a 3D model with a specific child flight model, but I don't expect that would be a major issue. I expect that for the near term (and I use the word "near" as a joke, since this multi-body capability has been in-work for many years), I expect FlightGear would not use this multi-body capability. I think it would be mostly a JSBSim batch mode feature. I'd like to see it used for multi-stage rockets, too, for instance. The challenge is to make sure that FlightGear is not adversely affected by the inclusion of this capability. I think Ron's suggested fix does that.[6]
— Jon S. Berndt
Cquote2.png
Cquote1.png It has long been my hope to develop the "child" modeling capability that currently has "stubs" set up in JSBSim. The idea is that a <child> element

within a "master" aircraft config file would cause the instantiation and loading of an FGFDMExec instance that is controlled by the master FGFDMExec instance. The child vehicle would update its state vector (location, orientation, velocity, attitude rates) based on the master (parent) vehicle. The child vehicle would be placed at a known location that relates the Vehicle Reference Point (VRP) of the parent and child vehicles. But, there are a lot of details that would need to be worked out for this to work. I think Dave is proposing something different than the multi-body sim capability that I am describing.

For all cases, though, there does need to be a way to set the state vector for a given vehicle. That would have to include setting past values, as

well. Then there's the question of how you detach a child vehicle. And lots of other questions.[7]
— Jon S. Berndt
Cquote2.png


Cquote1.png I've been interested in modeling multiple-bodies in JSBSim for some time. There are references to child model objects in the codebase, now. I've been wondering, however, if there is a better way to do this. It occurred to me that perhaps just having multiple instances of JSBSim running would work, where a state could be passed from the "core" instance, to other instances. This is not dissimilar to the way I have been considering implementing the capability, with the difference being in what object is responsible for managing the various instances of JSBSim. A reference case for such a capability is for a multistage rocket. We do have an example case that is being developed, now (the J246). However, once bodies (such as booster rockets) are dropped off they are not tracked. I simply zero out the mass of the spent boosters. However, what if instead we initialized an instance of JSBSim for each booster rocket that is dropped? The way that might work is to actually create an instance of JSBSim representing each booster rocket (or each individual piece). The initial configuration of the rocket would be done as it currently is in the J246 model, where the monolithic rocket is modeled. However, at staging, if the mass of the booster rockets is zeroed out, I envision that at that time we can initialize the "dormant" JSBSim instances that represent each booster rocket by itself at the current correct location, and let them fly free. This would, of course, require the application that manages a JSBSim instance to be set up to handle multiple JSBSim instances. However, I think this could be made to work.[8]
— Jon S. Berndt
Cquote2.png


Cquote1.png There was a discussion here a few years ago about setting up a "master/slave" capability for JSBSim, wherein an instance of the FDM could be slaved to some external source of position/orientation/velocity (POV). The discussion was started as a possible solution to the problem of how one models the launching of an X-15 from a mothership. The X-15 FDM, while in slaved mode, would still be iterating in order to keep a propulsion system, atmosphere, etc. running, as well as making sure that all the derived properties of the vehicle state are populated with reasonable numbers. When launched, the X-15 would start providing its own POV, hopefully without any initial transients. The external source of POV could be anything. Another instance of FGFDMExec, a fixed location for modeling a launch tower/rail, another app (i.e. FlightGear). The benefit of this over a multi-body solution is that the slaved object and master object aren't communicating over some elastic structure using forces.[9]
— Dave Culp
Cquote2.png


Cquote1.png I've looked further into the code and found some bugs and potential problems. The first one is in FGFDMExec's destructor. It has a try/catch block, in which it checks for Root == 0, and if so, deletes the object pointed to by the static pointer "master". Root can never be zero, so this part is never executed, and the FGPropertyManager object is never deleted. Root is never zero because it's set to be the same as master, in FGFDMExec's constructor right after the line master = new FGPropertyManager();. So to try to fix this I added a member variable "bool GotMaster" being set to true if Root was NULL in FGFDMExec's constructor (thus creating the master object), and replaced the check for Root == 0 to check if this flag is true. Then master gets deleted. Also I had to add "master = NULL;" after deleting it. This actually fixes my issue. I can create a FGFDMExec object, delete it again, and create another one, and so on. :-) But then I tried to run several aircraft at the same time. This works ok until I try deleting one of them, which causes a crash. I think this is basically caused because the master pointer is static and thereby shared by all the FGFDMExec objects. So then I just removed the static master pointer completely, changed the constructor so that if Root == 0 then Root = new FGPropertyManager(), and in the destructor, just delete Root instead. Now everything works just fine! :-) So I need some input from you guys now -- I can't really see the real use for the "master" pointer at all, it's barely used by the FGFDMExec class, and it's also not declared as a public variable (neither is Root). The child FDM's (If I understand JSBSim correctly) should get the Root pointer passed to them anyway so they also don't need it. Now they actually don't - in FGFDMExec::ReadChild, the object is created without passing the Root pointer, for some reason, so right now, my fix has probably broken something somewhere. What are the child FDM's for?[10]
— Ola Røer Thorsen
Cquote2.png


Cquote1.png Unfortunately, Ola's patch introduced a couple of glitches that prevent Flight Gear to run. One of them has originally been fixed by Frédéric Bouvier in the Flight Gear git repository. I have just transposed his patch and renamed the variable 'delete_root' in 'StandAlone' because I assume that a new 'Root' property manager would only be created when JSBSim is running in stand alone mode. The second issue crashes Flight Gear while starting because the allocation of a new FDMctr must be done if FDMctr was initially set to its default NULL value even if Root is not NULL. Otherwise a segmentation fault occurs when reaching the line 149 of src/FGFDMExec.cpp: IdFDM = (*FDMctr); // The main (parent) JSBSim instance is always the "zeroth"[11]
— Bertrand Coconnier
Cquote2.png


  1. Jon S. Berndt (Sat, 14 Feb 2004 13:43:55 -0800). Eye Candy.
  2. Vivian Meazza (Sat, 14 Feb 2004 13:43:55 -0800). Eye Candy.
  3. Jon S. Berndt (Wed, 05 Nov 2003 13:01:44 -0800). Multiplayer Server RFC.
  4. Jon S. Berndt (2008-04-13 03:38:51). Re-enabling multiple FDM's.
  5. Jon S. Berndt (2009-06-27 18:52:11). Multiple property tree problem/soution.
  6. Jon S. Berndt (2009-06-28 14:51:29). Multiple property tree problem/soution.
  7. Jon S. Berndt (2010-01-02 05:54:58). Slaved FDM.
  8. Jon S. Berndt (2011-01-05 13:24:04). Multi-body modeling.
  9. Dave Culp (2011-01-06 18:09:01). Multi-body modeling.
  10. Ola Røer Thorsen (2010-09-14 20:02:55). Release of v1.0 (with a fix suggestion to my multiple JSBSim objects issue).
  11. Bertrand Coconnier (2010-11-07 11:37:01). Release of v1.0 (with a fix suggestion to my multiple JSBSim objects issue).

Status: Child FDM Support

Meanwhile, since around ~2009, this has been partially implemented in JSBSim, which now contains support for a slaved <child> FDM element that is mated/coupled to the main FDM. The feature is still considered experimental and not supported by JSBSim developers, it is also currently disabled by default - especially in FlightGear, where it's causing problems/segfaults.

However, we can look at the corresponding code in order to learn how to instantiate an FGFDMExec instance as part of FCS system/channel in order to run an identical FDM instance in look-ahead mode, see $JSBSIM_SRC/FGFDMExec.cpp: bool FGFDMExec::ReadChild(Element* el)


bool FGFDMExec::ReadChild(Element* el)
{
  // Add a new childData object to the child FDM list
  // Populate that childData element with a new FDMExec object
  // Set the IsChild flag for that FDMExec object
  // Get the aircraft name
  // set debug level to print out no additional data for child objects
  // Load the model given the aircraft name
  // reset debug level to prior setting

  string token;

  struct childData* child = new childData;

  child->exec = new FGFDMExec(Root, FDMctr);
  child->exec->SetChild(true);

  string childAircraft = el->GetAttributeValue("name");
  string sMated = el->GetAttributeValue("mated");
  if (sMated == "false") child->mated = false; // child objects are mated by default.
  string sInternal = el->GetAttributeValue("internal");
  if (sInternal == "true") child->internal = true; // child objects are external by default.

  child->exec->SetAircraftPath( AircraftPath );
  child->exec->SetEnginePath( EnginePath );
  child->exec->SetSystemsPath( SystemsPath );
  child->exec->LoadModel(childAircraft);

  Element* location = el->FindElement("location");
  if (location) {
    child->Loc = location->FindElementTripletConvertTo("IN");
  } else {
    cerr << endl << highint << fgred << "  No location was found for this child object!" << reset << endl;
    exit(-1);
  }

  Element* orientation = el->FindElement("orient");
  if (orientation) {
    child->Orient = orientation->FindElementTripletConvertTo("RAD");
  } else if (debug_lvl > 0) {
    cerr << endl << highint << "  No orientation was found for this child object! Assuming 0,0,0." << reset << endl;
  }

  ChildFDMList.push_back(child);

  return true;
}

This sets up the child FDM entry in the FGFDMExec instance and adds a corresonding child FDM, i.e. another FGFDMExec instance. To see how this is currently handled at runtime, take a look at the FGFDMExec::Run() method, which interleaves execution of child FDMs with the main/parent FDM:

bool FGFDMExec::Run(void)
{
  bool success=true;
 
  Debug(2);
 
  for (unsigned int i=1; i<ChildFDMList.size(); i++) {
    ChildFDMList[i]->AssignState( (FGPropagate*)Models[ePropagate] ); // Transfer state to the child FDM
    ChildFDMList[i]->Run();
  }
 
  IncrTime();
 
  // returns true if success, false if complete
  if (Script != 0 && !IntegrationSuspended()) success = Script->RunScript();
 
  for (unsigned int i = 0; i < Models.size(); i++) {
    LoadInputs(i);
    Models[i]->Run(holding);
  }
 
  if (Terminate) success = false;
 
  return (success);
}


Now, as can be seen in the ReadChild() and Run() this is all currently specific to the use-cases discussed originally on the JSBSim/FlightGear-Devel mailing lists, so it cannot be directly used "as-is" for VNAV purposes, but needs to be slightly modified, or rather, re-implemented with the following requirements in mind:

  • add a new FCS component that instantiates a child FDM
  • prevent the sub-FDM instance from recursively instantiating additional instances
  • use the same FDM, but don't slave it to the parent FDM as an attached object
  • instead, allow the child FDM to run independently
  • allow input parameters to be specified for typical VNAV constraints
    • origin-waypoint (lat, lon, alt, speeds, time)
    • target-waypoint (lat, lon, alt, speeds, time)
    • only modify pitch/thrust settings to make those constraints
    • if successful, return pitch/thrust settings for the specified VNAV leg

Open Questions

Check the JSBSim docs/website: there's a compact little example called WNS (Waypoint Navigation System), for following/making lateral waypoints. All implemented in JSBSim directly, i.e. can be run/played with standalone. It's in the end of the reference manual, example #5 - just search for "waypoint" or "WNS".

From a standalone prototyping standpoint, JSBSim doesn't seem to currently have a 777 model included, the 737 seems to be more suitable for starters, because the aircraft is available, and various scripts for test flights exist already. Otherwise, the 777 FDM from FG would need to be copied to JSBSim and made to work in standalone mode.

Some of the more interesting questions to discuss with JSBSim experts would be:

  • better prototype this in standalone mode or not ?
  • possibly use a 2nd instance for the computations of the Performance DB, or can we use the same instance ?
  • how to do systems modeling in JSBSim that involves running the same FDM inside the simulation, and parametrizing it for reverse-solving, i.e. given altitude/speed constraints for legs, to get the proper pitch/thrust settings
  • how to save the output in some form of lookup table, either in-memory or to some separate file
  • how to run reverse-solver queries against such a solver to come up with the proper pitch/throttle changes.
  • whether the property tree interface suffices or if some separate command interface should be used for interfacing the solver to FG
  • any better/alternate ideas ?

YaSim

Cquote1.png VNAV stuff-- An interesting problem.

A large part of what the YASim solver does is pre-calculate an elevator incidence such that the aircraft can fly at "cruise" with neutral elevator and also have sufficient elevator throw to meet the "approach" criteria. Lift/weight and drag/thrust are held in static equilibrium, and the solver plays with stabilizer and elevator settings to meet the constraints. Essentially it's looking to balance pitching moments at both approach and cruise ends of the flight envelope. This is why in a YASim aircraft you do not set the elevator incidence-- the solver sets it in order to guarantee an aircraft that is more-or-less controllable.

Since the solver is essentially validating the aircraft at two predicted flight attitudes (approach/cruise), this suggests that someone could probably create similar routines to specify top-of-descent and bottom-of-descent parameters, feed in some additional constraints for wind vector, time, and desired flight economy profile, and have the revised solver return pitch and thrust requirements. Where the current solver assumes weight/lift at equilibrium, I'm going to guess it would probably iterate to find the appropriate weight/lift ratio that results in the correct altitude change over the time period. Once it finds one it returns the pitch/thrust settings used, else it aborts. It would be a messy little project, but I think it would be doable.

I would not envy anyone trying to do this. The YASim code base is, um, a bit challenging and has very little documentation.

I suspect this wouldn't work well as a run-time flight predictor, the YASim solver has to crunch a lot of numbers, but I'm guessing it could be used to build a database of profiles.

Assuming YASim elements could be hacked/revised to do this, I suspect such solutions would at best be ballpark approximations compared to the same profile flown in the actual simulator. They'd need to be verified with actual flight tests, and if someone is going to bother to properly conduct and log such tests, it makes the point of the solver a little questionable. Maybe I under-rate the possibilities or it could be refined over time, I dunno.

Also, VNAV profile data for any particular YASim aircraft would have to be re-built every time someone made a change to the YASim FDM. Seemingly minor FDM tweaks can have major YASim impacts, particularly if the developer isn't very experienced or knowledgeable with YASim. So VNAV data and the YASim aircraft FDM version would have to be tightly coupled. If someone commits a Git change to the aircraft's FDM, then the VNAV DB for that plane is hosed until it's re-calculated.[1]
— Buckaroo
Cquote2.png
Cquote1.png There are some pending merge requests to add some Yasim features, but we have an issue that since none of the current C++ developers own, or are experts in Yasim, we're reluctant to be the person who merges such changes, and potentially introduces subtle regressions.

Obviously this is chicken-and-egg, since no one can become expert enough in the code to become a maintainer :)

So, I'm more than happy to apply patches *providing* I can be convinced they are sane+reasonable from a pure code perspective (happy to help with that, too, if people are new to C++), and providing we have some assurance that a representative sample of yasim aircraft are unchanged or improved by the patch. Suggestions for that means in practice, are most welcome!

Otherwise I worry, given the nature of the solver, we'll keep optimising the solver for some aircraft, and making other existing aircraft worse - until someone tests them, and announced that they're no longer working.[2]
— James Turner
Cquote2.png
Cquote1.png I am still broadly happy to answer questions if posed (as long as I remember enough to come up with a meaningful answer). Just cc: me if you do, because my

latencies here are measured in weeks.

Bugs can always be fixed. What YASim needs is a maintainer, not really expertise per se. The latter comes from the former.[3]
— Andy Ross
Cquote2.png
  1. Buckaroo (Mon Dec 02, 2013 9:38 pm). YaSim vs. VNAV (by PM).
  2. James Turner (Fri, 05 Oct 2012 03:54:43 -0700). YASim and documentation.
  3. Andy Ross (Fri, 05 Oct 2012 03:54:43 -0700). YASim and documentation.

The FlightGear Interface: FGFDM

All FDMs supported by FlightGear need to copy the FGFDM.cpp/h files and implement the FDM interface required by FlightGear. This interface will probably need to be extended with VNAV-related classes, i.e. for performance/trajectory and guidance computations.

Also see: FDM engine feature standardization.


Misc Resources

Related Discussions