Howto:Getting started with Glass Cockpit Avionics Development

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
Note  This article is targeted at people wanting to develop/extend MFD-based features like a PFD, ND, EFB, CDU for FlightGear without having to touch any C++ code, and without having to rebuild FlightGear from source. The article intends to cover the fundamentals to help create a working prototype. The article is focused on describing techniques for accomplishing a number of important design goals, namely:
  • prototype/develop the MFDs a standalone Canvas GUI dialog, this will ensure loose coupling
  • supporting multiple concurrently active instances of each MFD (imagine having 5+ independent MFDs running)
  • no aircraft specific dependencies, using a wrapper for configuring such things via delegates
  • introducing frameworks for shared/overlapping functionality, likely to be similar/identical for related efforts
Cquote1.png It is already possible to use the canvas for instruments and HUDs. It will be particularly easy to implement MFD-instruments (airliners, biz jets) and modern glass-cockpit avionics, all of this without having to touch any C++ code, and without having to rebuild FlightGear.

— Hooray (Sat Jul 07). Re: How to use .
(powered by Instant-Cquotes)

For charting/mapping purposes, there's a dedicated framework called MapStructure. People facing performance issues or wanting to debug/troubleshoot Canvas based efforts, will want to check out Canvas Troubleshooting. People interested in extending the underlying C++ code, are encouraged to check out Canvas Development.

For scripted PFD/NDs or any other other modern avionics (MFDs), there's a dedicated subsystem in FlightGear, called Canvas. This is a fully scriptable, 100% hardware-accelerated off-screen rendering target using FBOs for RTT (render-to-texture) to satisfy arbitrary 2D drawing needs (instruments, avionics, HUDs, widgets, GUIs etc).

Canvas texture are dynamically updated on demand and can be placed in the simulator using virtual placements, such as:

  • aircraft (interior/exterior, e.g. cockpit/livery)
  • scenery
  • GUI dialogs/windows
  • HUDs

People interested in modern glass cockpit This is a link to a Wikipedia article will probably want to read up on FlightGear scripting using the built-in scripting language (called Nasal ) as well as the Canvas FlightGear 2D rendering framework, which implements a fully hardware-accelerated scenegraph (using OSG/OpenSceneGraph-based) on top of the FlightGear Property Tree, exposed to scripting space using the Nasal/CppBind framework.

Nasal works very much like JavaScript in a browser - it can be used to extend/create simulator features without touching the c++ code or rebuilding the binary using the built-in Nasal Console or the Interactive Nasal Console for rapid prototyping:

Nasal-console-3.0.png Interactive-Nasal-Console-REPL.png

2D rendering is accomplished through a hardware-accelerated 2D rendering API implemented on top of our property tree called "Canvas".

The 2D rendering API is a wrapper on top of ShivaVG (OpenVG, vector graphics), OSG (OpenSceneGraph) primitives and a handful of rendering primitives (raster images, groups, maps, paths).

Nasal has scriping space bindings (wrappers) for those APIs. This is the way glass cockpit displays can be fully implemented in scripting space these days. Even without a lot of coding.

Nasal, in conjunction with Canvas, can be used to create modern avionics fairly easily, including PFDs and NDs but also standalone GPS units or any other MFD — for instance, the following images are screen shots showing instruments and GUI dialogs that were solely created in scripting space using Nasal scripting and Canvas for 2D drawing:

To get started with MFD development, you'll want to start reading up on Nasal coding - for example using the following articles as entry points:

Once you understand basic Nasal scripting and OOP (object oriented programming), you'll want to look at the Nasal/Canvas tutorials and APIs:

This should provide a pretty good foundation for anybody wanting to contribute to Nasal/Canvas based efforts like the PFD, ND or other MFD efforts.

However, people wanting to get involved in these efforts don't necessarily need to know how to write code though - there are many other ways to help, i.e. by creating 3D models for instruments, or by creating vector graphics using Inkscape. The most recent effort involving a generic framework for glass cockpit avionics is Project Farmin. I'd suggest to get in touch via the Canvas forum.

Maps & Charts


For anything involving dynamic maps and charts (including moving maps), you don't need to create a mapping system from scratch.

Thanks to MapStructure there already is a front-end agnostic scripting space framework in place that can be used for creating pretty much arbitrary maps and charts that are fully interactive, including support for resource management (timers, listeners, caching).

In addition, the MapStructure framework cannot only be used for creating custom avionics, but also for creating GUI dialogs - the back-end is implemented in a generic fashion, so that MapStructure consists primarily of a number of layers that may contain symbols using static or dynamic positions. Symbols can also be both, static and dynamic (i.e. animated).

MapStructure layers shown in a Canvas GUI dialog

The following code snippet is taken from Canvas Snippets and illustrates how a simple moving map can be implemented using ~20-30 lines of code:

var TestMap = root.createChild("map");
TestMap.setController("Aircraft position");
TestMap.setTranslation(    myCanvas.get("view[0]")/2,
var r = func(name,vis=1,zindex=nil) return caller(0)[0];

foreach(var type; [r('APT'), r('VOR') ] )
 TestMap.addLayer(factory: canvas.SymbolLayer, type_arg:, visible: type.vis, priority: type.zindex,);

The MapStructure framework already comes with a sizable library of existing layers that can be easily styled and customized for all kinds of different purposes.

For a list of available MapStructure layers, please refer to: Canvas MapStructure Layers.

In addition, it is is fairly easy to create new layers from scratch. And MapStructure-based maps can also be easily reused in legacy GUI/PUI dialogs, too:

User Interface (GUI)

Introduce custom Canvas GUI widgets and styles

Cquote1.png on the second level of abstraction, nearly every widget provides methods to change its state. eg. Button.setChecked/setDown/toggle or ScrollArea.scrollBy.
Cquote1.png Looking at examples of some of our more sophisticated MFDs -like the Avidyne Entegra R9 or even just the Garmin GPSMap196- that may sooner or later benefit from being able to use native Canvas GUI widgets (buttons, checkbox, labels etc) with a custom style, it seems that we should strive to encapsulate event handling by not hard-coding things addEventListener() for mouse events or keyboard events at the Widget.nas level, because such devices may typically have their own "virtual" input means, such as a virtual keypad, or buttons/rockers to navigate between controls.

Using delegates for such events would allow us to reuse the same base classes regardless of the "front-end" in use - for instance, a MFD may be set up such that it doesn't respond to mouse/keyboard events, but we may still want to support highlighting the corresponding widget elements.

— Hooray (Sun Jun 29). Widget.nas MVC & DefaultStyle.nas considerations.
(powered by Instant-Cquotes)
Cquote1.png The Avidyne Entegra R9 contains several examples where this kind of generalization would mean that the instrument could directly use a customized Canvas.Widget instead of their own hand-written widgets: gitorious/extra500/extra500/HEAD/Nasal/AvidyneEntegra9/widget.nas - but obviously their widgets must remain functional even without the implicit assumption that widgets will always be controlled via keyboard/mouse.

If we could agree to eventually support this use-case, Canvas/avionics developers could also automatically help implement/extend and grow our widget library, which would obviously help related efforts, such as porting PUI, and we would be perfectly in line with ARINC 661 :D

As an alternative, we could propvide an abstract base class/interface that encapsulates input handling which needs to be specifically implemented by each device, so that there are no mouse/keyboard-related assumptions at the Widget.nas level.

— Hooray (Sun Jun 29). Widget.nas MVC & DefaultStyle.nas considerations.
(powered by Instant-Cquotes)
Cquote1.png Regarding the UI parts you mentioned, those should probably be based on the existing ScrollList widget in $FG_ROOT/Nasal/canvas/gui/widgets
— Hooray (Sun Jul 27). Re: Garmin gns530.
(powered by Instant-Cquotes)
Cquote1.png it is already using MVC, the styling part is handled by $FG_ROOT/Nasal/canvas/gui/styles/DefaultStyle.nas, which basically determines appearance of widgets (size, fonts, images etc).
— Hooray (Mon Jun 30). Re: MessageBox/Widget API for .
(powered by Instant-Cquotes)
Cquote1.png I was going to adapt the MessageBox example to render a MessageBox inside a MFD instrument, but then I realized that we're always creating new "windows", i.e. the API doesn't currently provide any way to render to/reuse an existing Canvas - for more sophisticated MFD instruments (Avidyne Entegra R9, G1000, GPSMap196, Garmin 530), we'll probably need to ensure that all widgets and dialogs support being rendered in an existing canvas.
— Hooray (Tue Jun 24). MessageBox/Widget API for .
(powered by Instant-Cquotes)
Cquote1.png Given the degree of GUI support in both instruments, it would make sense to keep such use-cases in mind so that avionics developers can reuse/style existing widgets, and customize widgets or create new ones, that won't be instrument/aircraft-specific, but that will be just generic Canvas widgets. Overall, supporting such use-cases would then be fully in line with ARINC 661, i.e. fully supporting recursion in the form of a GUI widget rendered on a MFD, showin inside a GUI dialog :D
— Hooray (Tue Jun 24). MessageBox/.
(powered by Instant-Cquotes)

Scaleable Vector Graphics

Caution  Depending on your OS, you may be unaware of any parse errors returned by parsesvg - parsesvg()/svg.nas is a relatively simple Nasal module that only supports a tiny subset of SVG by mapping it to the corresponding Canvas/OpenVG primitives - it's a clever piece of code actually, but obviously there's a plethora of SVG features that are not supported by this Nasal module - thus, you should watch out for any errors/warnings, and if necessary revisit your SVG file, i.e. by converting some constructs and/or exporting the SVG to a simplified version.

You will however only be able to treat a SVG file as a canvas group if the file could be processed successfully.
I am not sure if those errors only show up in the console (startup window) or also the startup log file in $FG_HOME - if you're on some kind of OS that doesn't show a startup window, I'd suggest to check out the log file - or for the sake of simplicity, the Nasal Console (see the debug menu) which contains a dedicated Nasal-specific loglist widget, where all Nasal errors should up, too.

Depending on what you have in mind, we have a few howtos/tutorials on the wiki, including quite a few code snippets/examples in $FG_ROOT

You'd basically create your MFD via Inkscape, as one (or more) SVG file/s and then animate SVG elements via Nasal callbacks.

Canvas based gauges (MFDs) will typically be SVG files (created via Inkscape) and animated via Nasal timers/listeners.

To get started we need a SVG file representing the device (screen,keypad/buttons) - each functional element should be addressable by having an SVG id. Textures can be implemented using png images.

Then, we would assign event handlers for each MFD key.

$FG_ROOT/Nasal/canvas/svg.nas is our existing example on populating a canvas procedurally by parsing an XML file.

Basically, it looks for supported SVG primitives, and turns those into Canvas/OpenVG primitives by setting properties.

The svg parser (svg.nas) is hand-written and can be found in $FG_ROOT/Nasal/canvas/svg.nas, it can be easily extended to support additional primitives/tags, you just need to map them to the corresponding OpenVG equivalents, I think supporting shapes would make sense and shouldn't be too difficult.

Internally, canvas will turn all SVG commands into OpenVG primitives and then maintain a raster image which is rendered each frame, but usually only updated when absolutely necessary (modified/animated).

think about what a "layer" ultimately is: it is an entity to which you can render something and that you can easily show/hide, which kinda matches the definition of a canvas group - which is why ND/MapStructure layers are internally "just" Canvas groups - to see for yourself, use the property browser and inspect any ND/MapStructure tree.

You can directly load SVG files via parsesvg, and then use the findElemetbyID() call to look up an element and animate/transform it - but watch the console, our svg parser is still fairly basic and may be missing some features

you can just get a handle to the element that you're interested in via findElementByName(), and then write it to a group via parsesvg(), implement depth-ordering via the z-index property - if something doesn't work as expected, you can also open svg.nas and extend the parser to support your specific use-case/XML tag.

using z-index layering for your groups - a canvas is a "layer" basically.
But in this case I would suggest editing svg.nas and extend it support raster images via the <image> tag so that this will be automatically set up.

check the console for errors - our SVG parser is hand-written and only supports a subset of SVG instructions, so may need to be extended, or simply use supported inkscape primitives instead.

Cquote1.png think our svg.nas parser could also be extended to support external SVGs directly, i.e. using the <use> and <image> tags. The other concern here is performance - once instruments are self-contained and can be treated as such, we can also update them more selectively, i.e. we don't necessarily have to use timers for each individual element, but can instead update instruments in a holistic fashion.

Overall, that is something that would be very worthwhile to pursue, because we do have plans to re-implement the existing 2D panels C++ code using Canvas and Nasal, so any work even remotely related to this would help pave the way for this and establish the underlying structure and framework. Conceptually, it wouldn't matter if a "steam gauge" is a raster image (2D panels) or a SVG image, the required animations would be fairly similar.

— Hooray (Tue Jun 03). Re: Garmin GPSmap196.
(powered by Instant-Cquotes)
Cquote1.png The SVG standard explicitly suppots referencing other images (including raster images and SVGs) via the "image" tag - even recursively, so I'd be surprised if inkscape didn't support that.

However, our svg parser doesn't currently support this IIRC - but it's "just" Nasal code, so would probably just require ~10-15 lines of code to add support for the image/object tags. ... ent_in_svg

— Hooray (Mon Jan 20). Re: SVG editing skills needed.
(powered by Instant-Cquotes)

Animation Handling

Important Concepts

For anything involving MFDs (glass cockpit/EFIS-type avionics), you'll usually want to use Inkscape SVG files that are animated using Nasal/Canvas. For that, you should understand a few Nasal basics, especially object-oriented programming using Nasal (vectors, hashes, classes, objects, inheritance):

If you're just getting started, you may also want to check out the advice on creating a simple Nasal framework: Howto:Coding a simple Nasal Framework

Next, you need to understand how to load an Inkscape SVG file onto a Canvas, for prototyping purposes it make sense to use a GUI dialog during development (which can later on be trivially turned into an actual instrument) :

Canvas Snippets#Creating a Canvas GUI Window

To learn more about adding raster images (e.g. as background), see: Canvas Snippets#Adding Raster Images
Howto:Using raster images and nested canvases

To see how Inkscape SVG files can be loaded, please refer to the examples/tutorial at: Howto:Use SVG inside a Canvas

Next, there's also the possibility to reuse FlightGear's existing gauges and render/animate those using Canvas: Howto:Parsing 2D Instruments using the Canvas

The reference for Canvas/Image handling (raster images) can be found at: Canvas Image

Cquote1.png Any Nasal/Canvas code that is highly specific to a single aircraft/instrument or use-case or that only supports a single instance of a display/instrument, will sooner or later turn out to be a huge maintenance challenge unfortunately, no matter how sophisticated the code involved might be. Unfortunately, our ND/MapStructure code isn't necessarily as structured as some of the more recent examples, especially the Avidyne Entegra R9 code is freaking awesome in comparison, but not yet sufficiently generic to be useful outside the scope of the extra500/Avidyne R9 - equally, Torsten's RBAR EFIS is basically the most elegant, and most correct, approach towards implementing a Canvas/Nasal based animation framework, but isn't yet sufficiently unified and generalized to be usable elsewhere.

The other thing is that the ND/MapStructure code gets away without using any conventional design patterns - which makes it fairly accessible to less-experienced contributors, especially those new to coding in general - in fact, all those layers and SVG elements are animated using a declarative hash syntax, so there's very little explicit coding involved - at the cost of making things pretty verbose, and not as elegant obviously ...

— Hooray (Tue Oct 07). Re: Mirage 2000-5.
(powered by Instant-Cquotes)

Getting involved as a non-coder (i.e. artwork, research)

3D Modelling (Blender)



Getting involved in coding

Useful Snippets