Canvas Radar

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
This article describes content/features that may not yet be available in the latest stable version of FlightGear (2020.3).
You may need to install some extra components, use the latest development (Git) version or even rebuild FlightGear from source, possibly from a custom topic branch using special build settings: .

This feature is scheduled for FlightGear 4.x. 30}% completed

If you'd like to learn more about getting your own ideas into FlightGear, check out Implementing new features for FlightGear.

Canvas Radar Framework
Started in 02/2014
Description Radar Framework
Maintainer(s) 5H1N0B1, Hooray
  • 5H1N0B1[2] (since 02/2014)
  • RevHardt[3] (06/2014)
Status Under active development as of 02/2014
Topic branches:
fgdata canvas-hackers team clone (topics/canvas-radar)

Canvas Radar is a MapStructure-layer for creating Radar displays in FlightGear using Nasal and Canvas, as of 02/2014 it is being actively developed by 5H1N0B1 with a focus on the Mirage 2000-5 fighter jet. The Radar implementation is heavily based on xiii's original code and will be further generalized and adapted to be usable in the MVC-centric design of Philosopher's MapStructure framework.

The Radar framework is being closely developed in conjunction with F-JYL's CanvasMFD framework to ensure that aircraft developers can easily adopt the framework in their own aircraft, without having to modify a ton of Nasal code.

Note  Contributors wanting to check out the topics/canvas-radar fgdata branch, need to follow these 3 steps:
  • git remote add canvas-hackers
  • git fetch canvas-hackers
  • git checkout --track -b canvas-radar canvas-hackers/topics/canvas-radar

This will give you a local branch named canvas-radar, so that you can easily pull/push changes. When pulling, it makes sense to pull with --rebase


Come up with a generic radar framework for MapStructure that is completely aircraft-agnostic, so that it can be easily adopted, integrated and customized by aircraft developers, but also by people doing ATC related projects, like ATC-FS or ATC-aircraft.

Currently, we're in touch with Adrian to see if his Radio Propagation could be rebased against git/next and exposed via Nasal/CppBind to provide a better option for radar modeling. This is something that we should probably discuss with xiii (developer of the original Nasal code) and vivian (agradar developer) at some point, vivian also expressed interest in updating the agradar to support the Radio Propagation code written by Adrian[4].

Also, i4dnf mentioned that we could benefit from making antenna profiles configurable for different aircraft. Adrian mentioned that he has access to the radar manual of the F16 and that he's familiar with the various operating modes.

The Flightgear Next Gen Radar would be able to (not sorted) :

  1. Be a lot faster as actual system (Using Canvas display and some C++ for high consuming function)
  2. Detect Heat/infrared
  3. Manage AESA radar. (Actually it's more complicated to manage non AESA radar -> cause of the sweep. -> No sweep for AESA)
  4. Manage DOPPLER radar DOPPLER (Actually, more complicated to manage "non doppler" radar. The purpose is to erase signature of airplane which are bellow us, and fly at low alt. We could even erase 0 kts aircraft -> The Pougatchev's Cobra should be able to make stall DOPPLER radar)
  5. BONUS : Terrain detection. Make an aircraft disapear if it's behind a mountain
  6. With the terrain detection : Allow to have a fast Terrain Following Mode
  7. Make it working with existing Radio Propagation code.
  8. Manage IFF/Transponder
  9. Display Radar on Nav Mfd
  10. Allow more than one radar on a plane, like AWACS or Russian aircraft which have a rear radar
  11. Manage laser targeting -> with a pod view and clicking (for non AI target like building)
  12. Allow simple re use of the radar on each aircraft military and also civilian
  13. Long term obj : Put a radar object in "each" missile on the missile.nas script
  14. More ?


Currently, we're playing around with a modified version of the MapStructure TFC (traffic) layer, to use this in a standalone XML dialog and customize it as needed. Once that is working as expected, we will investigate adding xiii's radar modeling code and replace the traffic/TCAS functionality accordingly. Afterwards, we will need to integrate this with the Canvas MFD Framework to ensure that aircraft developers can easily use the framework in their cockpits.

At that point, it would then also make sense to check out the Category:Fighter aircraft and Category:Carrier-based aircraft categories to get in touch with the developers and maintainers of aircraft with RADAR, and ask them for adoption/integration feedback.

Subsequently, it would make sense to look at existing hard-coded displays, like the agradar, groundradar and wxradar to support these use-cases, too. Specifically, ATC-FS and ATC-aircraft could greatly benefit from this effort.

5H1N0B1 also mentioned that a properly-designed component would allow other uses, too - such as for example, equipping Scripted AI Missiles with a real RADAR, or adding radar support to Bombable - likewise, it would be possible to add such radar displays to AWACS aircraft, or even to vessels like the Nimitz.

Radar/ATC Requirements

These are primarily requirements for the Mirage2000 that is currently being revamped by 5H1N0B1[5], these changes could also be used to add ATC support to AWACS aircraft[6].

  • Set up a dialog-based test bed Done Done
  • Prototype a simple ATC/RADAR layer 40}% completed
  • Terrain layer (elevation height maps using the terrain presampler) see: [7] and [8] Not done Not done (by 5H1N0B1)
  • Custom Controllers for:
    • azimuth-based filtering Not done Not done (by 5H1N0B1)
    • sweep mode (AESA) Not done Not done (by 5H1N0B1)
    • terrain awareness (filtering) Not done Not done (by 5H1N0B1)
    • heat detection based filtering Not done Not done (by 5H1N0B1)
  • select and animate symbols (targets) Not done Not done (by 5H1N0B1)
  • TCAS/transponder awareness for symbol animation/styling Not done Not done (by 5H1N0B1)
  • explore integrating this with Adrian's Radio propagation system via cppbind (Hooray) Not done Not done

Also see ATC-FS and ATC-aircraft


Displaying Scenery Objects

Cquote1.png Files are now automatically loaded. The function you changed is just a "self-test", but it demonstrates how to actually "use" a new layer. You can also refer to $FG_ROOT/gui/dialogs/map-canvas.xml for another example. I'd suggest to modify that file to use/test your new layer there. You will probably want to default it to visible:1 or add a corresponding checkbox (refer to the other layers/checkboxes as example)

You would then open the Map (Canvas) dialog via the EQUIPMENT menu to see your layer at work.
Using that method, will also allow you to easily reload the dialog via the DEBUG menu.
However, the layer itself cannot currently be reloaded (yet).

— Hooray (Sun Jun 22). Re: Get objects to show up on Map/Radar.
(powered by Instant-Cquotes)
Cquote1.png You would probably want to create a new helper/wrapper (data structure like a vector/hash) to which you add your positions (lat/lon) using geo.Coord objects - and then simply use that data structure from your searchCmd method.

In other words, modify the ufo editor to copy such info to a global data structure, i.e. initialized while booting (any Nasal submodule will do), and then traverse this data structure when running your searchCmd. There are other/better options, but this should be simple enough and get you going quickly.

For testing purposes, just make searchCmd() return a vector like [geo.aircraft_position()], which willl return a single position (your current position).

To make this a little more interesting use the .apply_course_distance() to apply offsets to the current position, e.g. to return 12 positions within 10 miles, all offset by a few degrees (in a circle):

— Hooray (Sun Jun 22). Re: Get objects to show up on Map/Radar.
(powered by Instant-Cquotes)
A simple MapStructure layer called DEMO that renders a circle of NDB symbols (SVGs) in the vicinity of the aircraft using geo.nas and its helpers like geo.aircraft_position() and the geo.Coord.apply_course_distance(course, dist) method as per [1]. See Canvas Radar to learn more.
Note  If you are adapting an existing navaid layer (e.g. NDB), you may still want to change your lcontroller file to provide a custom layer.searcher._equals function, and make it inherit from MultiSymbolLayer instead of NavaidSymbolLayer. To change the SVG symbol, change the path in your .symbol file accordingly.
diff --git a/Nasal/canvas/map/DEMO.lcontroller b/Nasal/canvas/map/DEMO.lcontroller
new file mode 100644
index 0000000..c8ea442
--- /dev/null
+++ b/Nasal/canvas/map/DEMO.lcontroller
@@ -0,0 +1,45 @@
+# See:
+# Class things:
+var name = 'DEMO';
+var parents = [SymbolLayer.Controller];
+var __self__ = caller(0)[0];
+SymbolLayer.Controller.add(name, __self__);
+SymbolLayer.add(name, {
+	parents: [MultiSymbolLayer],
+	type: name, # Symbol type
+	df_controller: __self__, # controller to use by default -- this one
+	df_style: {},
+var new = func(layer) {
+	var m = {
+		parents: [__self__],
+		layer: layer,
+		map:,
+		listeners: [],
+	};
+	layer.searcher._equals = func(l,r) 0;
+	m.addVisibilityListener();
+	return m;
+var del = func() {
+	foreach (var l; me.listeners)
+		removelistener(l);
+var searchCmd = func() {
+ print("DEMO.lcontroller searchCmd():");
+ var offset_deg = 30;
+ var offset_distance = 9000;
+ var results = [];
+for(var i=0; i<360;i+=offset_deg) {
+ var pos = geo.aircraft_position();
+ pos.apply_course_distance(i, offset_distance);
+ append(results, );
+ # print("Looping:", i);
+ }
+return results;
diff --git a/Nasal/canvas/map/DEMO.symbol b/Nasal/canvas/map/DEMO.symbol
new file mode 100644
index 0000000..7848330
--- /dev/null
+++ b/Nasal/canvas/map/DEMO.symbol
@@ -0,0 +1,7 @@
+# See:
+DotSym.makeinstance('DEMO', {
+	parents: [SVGSymbol],
+	svg_path: "/gui/dialogs/images/ndb_symbol.svg",
+	#cacheable: 1,
diff --git a/gui/dialogs/map-canvas.xml b/gui/dialogs/map-canvas.xml
index 6475325..e1bd439 100644
--- a/gui/dialogs/map-canvas.xml
+++ b/gui/dialogs/map-canvas.xml
@@ -427,7 +427,7 @@
 			# TODO: introduce some meta NAV layer that handles both VORs and NDBs, can we instantiate those layers directly ?
                         var r = func(name,vis=1,zindex=nil) return caller(0)[0];
                         # TODO: we'll need some z-indexing here, right now it's just random
-                        foreach(var type; [r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS'),  ] ) {
+                        foreach(var type; [r('DEMO'), r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS'),  ] ) {
                             if (1 and != 'APS' and != 'FLT') make_update_wrapper(;
                             TestMap.addLayer(factory: canvas.SymbolLayer, type_arg:,
                                              visible: type.vis, priority: type.zindex,

Once this works, you can basically refine your searchCmd() method, i.e. by using actual data from the "ufo editor"

Cquote1.png Assuming that you're using this method: Howto:Place_3D_objects_with_the_UFO#Loading_the_XML_file_as_part_of_FlightGear_scenery

... then, you only need to open the XML file to see where those objects end up when loaded into the property tree.
Next, open the property tree browser - and you'll find that you won't have to parse any XML, it will be all there already - probably somewhere under /models
You'll just need to use the props.nas APIs/helpers to get out the relevant lat/lon/altitude etc to populate your searchCmd() vector with geo.Coord objects via accordingly.

— Hooray (Sun Jun 22). Re: Get objects to show up on Map/Radar.
(powered by Instant-Cquotes)

Creating a custom ATC/RADAR Layer

People already having some Nasal experience (property tree, OOP), should be able to complete this 20-30 minutes, and you would end up with an ATC display and a custom symbol - next, you will want to "filter" traffic based on surrrounding features like terrain, altitude, radar settings and radio propagation etc.

If you find yourself getting stuck somewhere, look at some of the other .symbol/.lcontroller and .scontroller files - especially the shorter/simpler ones - and then read the wiki section about adding new layers a few times, and everything will start making sense

Basically, this should get you going quickly:

  • copy an existing set of files (better TFC* instead of VOR*), but name it "TARGET" instead of TFC: TARGET.symbol, TARGET.lcontroller, TARGET.symbol (RADAR may be a more appropriate name)
  • you need to change the name to TARGET inside the 3 new TARGET* files (see the top of the new files, where 'TFC' can be seen)
    • start by opening TARGET.scontroller and replace TFC with TARGET
    • next, open TARGET.symbol - this contains the draw() and update() callbacks to draw/update a single symbol and animate it accordingly, also replace TFC with TARGET
    • next, open TARGET.lcontroller - this contains the layer management code, a layer manages a vector of symbols, replace TFC with TARGET
  • now, add the new files to your instrument/display/dialog: there's typically a vector that contains the layer names to load, e.g. "VOR", "DME", and "TFC", and so you will need to add "TARGET" there as well. This is to tell the framework about your newly created layer files.
  • once that is done, use the new "TARGET" identifier in the foreach vector in the Nasal section of the dialog seen above and its embedded canvas/nasal/load block
  • this should give you a TCAS traffic (TFC) symbol using the TARGET handle when you run the dialog

(5H1N0B1 : I'm here)

  • once that is working, you can customize the symbol by opening the TARGET.symbol file which contains the draw routine - for example by changing the color or using some custom font/label, or just using a SVG file via parsesevg() - see the NDB.draw file for an example on how to display a custom SVG file
  • at this stage you should have a custom symbol rendered in all the places where MP/AI traffic is located, all driven via the TCAS function.
  • so the next step is to make the code a bit smarter, especially look at the TFC.lcontroller and TFC.scontroller files
  • it may even be a good idea to directly start with those, instead of the VOR files ...
  • once that is working, i.e. 1) you have a custom symbol shown and see all the surrounding MP/AI traffic, you have already successfully created a simple ATC/radar screen!
  • next, you would replace the data source (which is AI/MP traffic) with your own data source - for that, refer to the TARGET (TFC) files to see how the AI/MP traffic is added
  • once you have added your own data source, you can change the heuristics accordingly - to handle terrain, radar range etc.
  • your own objects should ideally be geo.nas Coord objects - that way, the system can directly support your traffic, you only need to derive from geo.Coord to make this happen:
var myPosition3D = {
 new: func {
  return {parents:[ ]};

Next, you will want to investigate the positionedSearch class, which provides a powerful means to do range-based filtering of positioned objects, these can in turn be filtered (searched) for other characteristics, i.e. based on range, terrain, radio propagation or heat etc. Take a look at TARGET.lcontroller and its searchCmd() helper.

If you need help doing this, please check out the canvas subforum, and also post some screen shots, so that we can better help you It would be a good idea to also start a fgdata topic branch so that we can better track your work and look at the code.

Custom Filtering

Once the previously outlined steps are working, I would consider customizing the searchCmd in your lcontroller file. At the moment, TFC.lcontoller works like this;a=blob;f=Nasal/canvas/map/TFC.lcontroller;hb=c78b2f936891bffad49e27d84d952a0d45540eef

The line where I added PLACEHOLDER is where you could insert your own logic, i.e. the radar-specific stuff from xiii's code - such as checking distance, azimuth, altitude - radar profile/signature, terrain etc - and only append the aircraft if the whole check evaluates to true.

You will find that the whole lcontroller file uses a wrapper called "TrafficModel" - you can either extend this to use your "Target" class directly, or change your Target class accordingly. The main thing is that your class should be derived from a geo.Coord object, so that it has the lat/lon/alt methods available and can be directly processed by MapStructure without requiring further changes.

You will see that the TrafficModel class is used in a few places - so this would need some changes if you use some different approach, but it's still simple. The external interface is all about having lat/lon/alt (positions) and the .equals() method Overall, TrafficModel is just a dumb helper class that is a wrapper for geo.Coord() objects, so there's no reason why you shouldn't be able to extend your own Target class accordingly, you can use the TrafficModel as a template. I would just suggest to maintain the MVC separation at all times.

Analogous to the in_range() helper, you could add other functions for your own filtering needs (altitude, terrain-obstruction etc) - it's better to use separate functions for each, than inflating a single function unnecessarily. As you can see, the in_range() function is unaware of TrafficModel specifics -it deals directly with lat/lon pairs..

So it's simple to reuse as is. TrafficModel itself is typically directly used via the constructor call: .new()