Board index FlightGear Development Nasal

Spoken GCA

Nasal is the scripting language of FlightGear.

Re: Spoken GCA

Postby clm76 » Sat Sep 23, 2017 7:21 am

Hi,
Omega95's VSD is part of some aircraft as far as I remember, there should be a topic on the forum detailing where the code is to be found.

I adapted the Omega95's VSD to the CitationX :

Image

The code :
Code: Select all
#### Citation X - Vertical Situation Display ####
#### Narendran M (c) 2014
#### Adapted by C. Le Moigne (clm76) - 2017 ####

props.globals.initNode("instrumentation/efis/inputs/vsd",0,"BOOL");

var alt = 0;
var   alt_ind = "/instrumentation/altimeter/indicated-altitude-ft";
var asel = "/autopilot/settings/asel";
var   curWpt = "/autopilot/route-manager/current-wp";
var dep_alt = "/autopilot/route-manager/departure/field-elevation-ft";
var dest_alt = "/autopilot/route-manager/destination/field-elevation-ft";
var dist_rem = "autopilot/route-manager/distance-remaining-nm";
var fp_active = "/autopilot/route-manager/active";
var   heading_ind =   "/instrumentation/heading-indicator/indicated-heading-deg";
var num_wpts = "/autopilot/route-manager/route/num";
var set_range = "/instrumentation/efis//inputs/range-nm";
var svg_path = "/Aircraft/CitationX/Models/Instruments/MFD/canvas/Images/vsd.svg";
var tg_alt = "autopilot/settings/tg-alt-ft";
var toggle_vsd = "/instrumentation/efis//inputs/vsd";
var totDist = "autopilot/route-manager/total-distance";
var   vert_spd = "/velocities/vertical-speed-fps";
var rangeHdg = [];
var brk_next = nil;
var color = nil;
var text = nil;
var pos = nil;
var check_hdg = nil;
var elev = nil;
var vs_fps = nil;
var gs_fps = nil;
var fpa = nil;
var info = nil;
var elevation = nil;

      #### VSD CLASS ###
var vsd = {
   terrain_color:    [0.44, 0.19, 0.09, 0.5],    # A brownish color
   path_color:    [1,0,1,1],      # Magenta
   cstr_color:    [0,1,0,1],      # Green
   tod_color :      [1,1,0,1],      # Yellow
   elev_pts: 21,                        # Number of elevation data points
   elev_profile: [],
   range: 20,
   lastalt:0,
   lastaltset:0,
   alt_ceil: 10000,
   altitude: 0,
   peak: 0,
   alt_ceil_px: 180,      # Pixel length of vertical axis
   max_range_px: 810,   # Pixel length of horizontal axis
   terr_offset: 22,      # Offset between start of terrain polygon y and bottom_left corner
   bottom_left: {x:190, y:294},   # {x:x,y:y_max - y} of bottom-left corner of plot area - looks like canvas starts it's y axis from the top going down - old 233,294

   new: func() {
      var m = {parents:[vsd]};
      m.display = canvas.new({
         "name": "vsdScreen",
         "size": [1024, 320],
         "view": [1024, 320],
         "mipmapping": 1
      });
   
      m.display.addPlacement({"node": "Vsd.screen"});
   
      ### Create canvas group
      m.group = m.display.createGroup();            # Group for canvas elements and paths
      m.text = m.display.createGroup();               # Group for waypoints text
      m.terrain = m.group.createChild("path");   # Terrain Polygon
      m.path = m.group.createChild("path");         # Flightplan Path
   
      ### Load Vertical Situation Display
      canvas.parsesvg(m.group, svg_path);   
      setsize(m.elev_profile,m.elev_pts);

      ### Create empty geo.Coord object for waypoints calculation
      m.wpt_this = geo.Coord.new();
      m.wpt_next = geo.Coord.new();
      m.wpt_tod = geo.Coord.new();

      ### Display init ###
      m.h_range = getprop(set_range);
      m.group.getElementById("text_range1").setText(sprintf("%3.0f",m.h_range*0.25));
      m.group.getElementById("text_range2").setText(sprintf("%3.0f",m.h_range*0.5));
      m.group.getElementById("text_range3").setText(sprintf("%3.0f",m.h_range*0.75));
      m.group.getElementById("text_range4").setText(sprintf("%3.0f",m.h_range));
      m.group.getElementById("tgt_altitude").setText(sprintf("%5.0f",getprop(tg_alt)));

      ### Variables ###
      m.fp = flightplan();
      m.alt_set = getprop(tg_alt);
      m.lastWp = 0;
      m.lastWp_alt = 0;
      m.lastWp_dist = 0;
      m.prevWp_alt = 0;
      m.prevWp_dist = 0;
      m.v_alt = nil;

      return m;
   }, # end of new

         ### Listeners ###
   listen : func {      
      setlistener(fp_active, func(n) {
         if (n.getValue()) {
            me.fp = flightplan();
            me.tot_dist = getprop(totDist);
            me.update();
            me.v_alt = fms.vsd_alt(); # Call altitudes vector from fms
         }
      },0,0);

      setlistener(set_range, func(n) {
         me.range = n.getValue();
         if(me.range > 10) {
            me.group.getElementById("text_range1").setText(sprintf("%3.0f",me.range*0.25));
            me.group.getElementById("text_range2").setText(sprintf("%3.0f",me.range*0.5));
            me.group.getElementById("text_range3").setText(sprintf("%3.0f",me.range*0.75));
            me.group.getElementById("text_range4").setText(sprintf("%3.0f",me.range));
         } else {
            me.group.getElementById("text_range1").setText(sprintf("%1.1f",me.range*0.25));
            me.group.getElementById("text_range2").setText(sprintf("%1.0f",me.range*0.5));
            me.group.getElementById("text_range3").setText(sprintf("%1.1f",me.range*0.75));
            me.group.getElementById("text_range4").setText(sprintf("%1.0f",me.range));
         }
      },0,0);

      setlistener(tg_alt, func(n) {
         me.alt_set = n.getValue();
         me.group.getElementById("tgt_altitude").setText(sprintf("%5.0f",me.alt_set));
         me.newSetPos = -me.alt_ceil_px*(me.alt_set/me.alt_ceil);
         me.group.getElementById("altitude_set").setTranslation(0,me.newSetPos-me.lastaltset);
         me.lastaltset = me.newSetPos;
      },0,0);

   }, # end of listen

   update: func {
      if(getprop(toggle_vsd) == 1) {
         # Generate elevation profile      
         me.altitude = getprop(alt_ind);
         if(me.altitude == nil) {
            me.altitude = 0;
         }
         me.alt_ceil = (getprop(asel)*100 == 0 ? 1 :getprop(asel)*100);

         me.new_markerPos = -me.alt_ceil_px*(me.altitude/me.alt_ceil);

         # Vertical Flight Path
            me.numWpts = me.fp.getPlanSize();
            me.currWpt = me.fp.current;
            if (me.numWpts > 1 and me.currWpt >= 0) {
               me.path.del();
               me.text.removeAllChildren();
               me.path = me.group.createChild("path");
               me.path.setColor(me.path_color)
                      .moveTo(me.bottom_left.x,me.bottom_left.y-4+me.new_markerPos)
                      .setStrokeLineWidth(2)
                      .show();
               me.wpt_this.set_latlon(me.fp.getWP(me.currWpt).lat, me.fp.getWP(me.currWpt).lon);
               me.rteLen = geo.aircraft_position().distance_to(me.wpt_this)*M2NM;
               brk_next = 0;

               # Calculate distance between waypoints
               for(var i=me.currWpt; i<me.numWpts; i=i+1) {
                  if (i == 0) {alt = getprop(dep_alt)}
                  else if (i == me.numWpts-1) {alt = getprop(dest_alt)}
                  else {
                     if (me.v_alt != nil) { # plan not activated
                     #### BASIC ###
                        if(me.fp.getWP(i).wp_type == "basic" and me.fp.getWP(i).wp_role == nil) {

                           ### SIDS ###
                           if (me.fp.getWP(i).distance_along_route < me.tot_dist/2) {
                              if (me.v_alt.vector[i] <= 0) {
                                 alt = getprop(asel)*100;
                              } else {
                                 alt = me.v_alt.vector[i];
                              }
                           } else {

                           ### STARS ###
                              alt = me.v_alt.vector[i];                        
                           }
                        } else {

                        ### NAVAIDS ###
                              alt = me.v_alt.vector[i];
                        }
                     }
                  }
                  if(me.rteLen > me.range) {brk_next = 1}
                  me.path.lineTo(me.bottom_left.x + me.max_range_px*(me.rteLen/me.range), me.bottom_left.y - me.alt_ceil_px*(alt/me.alt_ceil));

                  # Add waypoint ident
                  if (me.fp.getWP(i).wp_name == 'TOD') {
                     color = me.tod_color;
                     text = "TOD";
                  } else {
                     color = me.path_color;
                     text = me.fp.getWP(i).id;
                  }

                  me.text.createChild("text")
                         .setAlignment("left-bottom")
                         .setColor(color)
                         .setFontSize(24,1.0)
                         .setTranslation(me.bottom_left.x + 10 + me.max_range_px*(me.rteLen/me.range), me.bottom_left.y - 10 - me.alt_ceil_px*(alt/me.alt_ceil))
                         .setText(text);

                  me.text.createChild("text")
                         .setAlignment("center-center")
                         .setColor(color)
                         .setFontSize(28,1.2)
                         .setTranslation(me.bottom_left.x + me.max_range_px*(me.rteLen/me.range), me.bottom_left.y - me.alt_ceil_px*(alt/me.alt_ceil))
                         .setText("*");

                  if(i<me.numWpts-1) {
                     me.wpt_this.set_latlon(me.fp.getWP(i).lat, me.fp.getWP(i).lon);
                     me.wpt_next.set_latlon(me.fp.getWP(i+1).lat, me.fp.getWP(i+1).lon);
                     append(rangeHdg, {range: me.rteLen, course: me.wpt_this.course_to(me.wpt_next)});
                     me.rteLen = me.rteLen + me.wpt_this.distance_to(me.wpt_next)*M2NM;
                  }
                  if(brk_next == 1) {break}
               }
            } else {
               me.path.hide();
               me.text.removeAllChildren();
            }
   
         pos = geo.aircraft_position();
   
         # Get terrain profile along the flightplan route if WPT is enabled. If WPT is not enabled, the rangeHdg vector should be empty, so it's just going to get the elevation profile along the indicated aircraft heading
      
         me.peak = 0;
         forindex(var j; me.elev_profile) {
            check_hdg = getprop(heading_ind);
            foreach(var wpt; rangeHdg) {
               if(j*(me.range/me.elev_pts) > wpt.range) {
                  check_hdg = wpt.course;
               } else {break}
            }
            pos.apply_course_distance(check_hdg,(me.range/me.elev_pts)*NM2M);
            elev = me.get_elevation(pos.lat(), pos.lon());
            me.elev_profile[j] = elev;
            if(elev > me.peak) {
               me.peak = elev;
            }
         }
         # Set Altitude Numbers
         me.terrain.del();
         me.terrain = me.group.createChild("path");
         me.terrain
               .setColorFill(me.terrain_color)
               .moveTo(me.bottom_left.x,me.bottom_left.y + me.terr_offset);
   
         # Draw Terrain
         forindex(var k; me.elev_profile) {
            me.terrain.lineTo(me.bottom_left.x+(k*(me.max_range_px/(me.elev_pts-1))), me.bottom_left.y -6- me.alt_ceil_px*(me.elev_profile[k]/me.alt_ceil));
         }
            me.terrain.lineTo(me.bottom_left.x+me.max_range_px,me.bottom_left.y+ me.terr_offset);
   
         me.group.getElementById("text_alt1").setText(sprintf("%5.0f",me.alt_ceil/2));
         me.group.getElementById("text_alt2").setText(sprintf("%5.0f",me.alt_ceil));
         me.group.getElementById("aircraft_marker").setTranslation(0,me.new_markerPos-me.lastalt);

         ### Speed Arrow ###
         vs_fps = getprop(vert_spd);
         if(vs_fps == nil) {
            vs_fps = 0;
         }
         gs_fps = getprop("/velocities/groundspeed-kt")*1.46667; # KTS to FPS
         if(gs_fps > 60) {
            fpa = math.atan2(vs_fps, gs_fps);
            me.group.getElementById("speed_arrow")
                     .setTranslation(0,me.new_markerPos-me.lastalt)
                     .setRotation(-fpa)
                     .show();         
         } else {
            me.group.getElementById("speed_arrow").hide();
         }
         me.lastalt = me.new_markerPos;
      } else {
         me.path.hide();
         me.text.removeAllChildren();
      }

      settimer(func me.update(),1);

   }, # end of update

   get_elevation : func (lat, lon) {
      info = geodinfo(lat, lon);
      if (info != nil) {var elevation = info[0] * M2FT;}
      else {elevation = -1.0; }
      return elevation;
   }, # end of get_elevation

}; # end of VSD

### START ###
var vsd_stl = setlistener("sim/signals/fdm-initialized", func {
   var vsd = vsd.new();
   vsd.listen();
   print("VSD ... Ok");
   removelistener(vsd_stl);
},0,0);

clm76
 
Posts: 149
Joined: Tue Oct 30, 2012 8:18 pm
Location: France - LFOH
Callsign: F-GCLM
Version: 2018.3.0
OS: Linux Mint 18.3

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 9:47 am

That's looking really good, thanks for sharing the code - have you considered generalizing this a little more so that this can become a reusable component to be added to other aircraft/use-cases as well ?
As a matter of fact, it should not be that difficult to turn this into a new MapStructure mode with vertical layers.
That way, things like the terrain profile, flight path/track and the route manager (flight plan) could simply be drawn using a vertical mode.

For starters, we would need a totally self-contained VSD that can be dropped into the Nasal Console to render to a Canvas/group.
Once that works, we would need to identify any aircraft specific property access, and move those out of the code.

And at that point we could move everythingg specific to a single layer (flight plan, flight path, terrain profile) to separate drawing files and adopt the MapStructure/MVC approach, to help separate the search heuristics from the drawing instructions.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 1:38 pm

Okay, regarding the gca submodule: I've just been tinkering with it, the way its initialization is currently set up using a gca.nas file in conjunction with a submodule is a bit problematic, because this basically means that the code will be loaded twice (unless I missed something of course).
In general, the gca.nas/loader module is not needed at all for submodules living in $FG_ROOT/Nasal
However, like I said, we could also turn the whole thing into an addon, especially if the plan is merge everything with the spoken ATC addon at some point in the future

It also seems that you may be on Windows, right ?
The Gca folder extracts to $FG_ROOT/Nasal/Gca, but the submodule would load it into the "Gca" namespace, whereas you are explicitly loading into a "gca" namespace (case-sensitive).
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 2:02 pm

Anyway, looking at the code, I simply removed the gca.nas file and renamed the "Gca" folder to "gca", and everything seems to be working correctly.
To integrate the UI dialog with your existing code, I would simply open control.nas and navigate to the Control function there, and append a call to configureGUI after you create the gca class instance named "demo", commenting/removing the setPosition(), setGlidepath() and setFinalApproach() calls - because those can be handled by the UI now.

The only thing missing to actually show the dialog is to add a new file to $FG_ROOT/Nasal/gca/gca_gui.nas with the code I added to the wiki a few weeks ago: http://wiki.flightgear.org/Howto_talk:I ... GCA_system

With your recent changes introducing separate setters for your variables , it should be straightforward then to hook up each UI field to the GCAController class.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 2:22 pm

Okay, just gave it a try, and it actually works:

Image

The only additional change I had to make was adding a "setTransmissionInterval" method to the GCAController class, because you didn't provide one.
Other than that, I changed the UI class so that it uses GCAController.callback_name instead of strings to look up the method handles, which also meant using the Nasal call() API.

The next step would be hooking up the UI to the class and moving some of the heuristics in control.nas into the GUI file.

I have uploaded my gca_gui.nas file to the wiki, see: http://wiki.flightgear.org/Howto_talk:I ... GCA_system
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Sat Sep 23, 2017 3:10 pm

clm76 wrote in Sat Sep 23, 2017 7:21 am:I adapted the Omega95's VSD to the CitationX :
Thanks for share it. I will look the code to pick some ideas,
Hooray wrote in Sat Sep 23, 2017 1:38 pm:In general, the gca.nas/loader module is not needed at all for submodules living in $FG_ROOT/Nasal
I know, I only included it thinking in the future addon structure. (Will be renamed as main.nas)
Hooray wrote in Sat Sep 23, 2017 1:38 pm:It also seems that you may be on Windows, right ?
Nop, I'm on Ubuntu. The uppercase is a misstype. :oops:
About Thorsten's local-weather, I think he is not loading real neighboring tiles (terrain, etc) but creating virtual ones (only atmosferic).

Anyway I'm considering another solution:
when Control() gets the destination airport, rwy and final_nm, I could save
Code: Select all
setprop("/gca/current-tile", geo.tile_path(<lat>, <lon>) ); # aircraft_position tile.
setprop("/gca/airport-tile", geo.tile_path(<lat>, <lon>) ); # airport tile.
setprop("/gca/final-tile", geo.tile_path(<lat>, <lon>) ); # final tile.
if all the 3 are the same tile, then the terrain profile will have no "holes". If not, in demo.timer(), (once per second)
Code: Select all
if(getprop("/gca/current-tile") != prev_tile ) {
   Par.setTerrain(); # not really rebuild profile but only fill the holes.
   }

In this way, the holes will fill up as the aircraft approaches.

Hooray wrote in Sat Sep 23, 2017 2:22 pm:Okay, just gave it a try, and it actually works.
I have uploaded my gca_gui.nas file to the wiki
Fine ! I will try it.
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 3:19 pm

Regarding the tile manager, I haven't been tracking FG development recently, but I think it ought to schedule terrain loading depending on demand - you could try to run a geodinfo() call for the opposite side of the planet and see if it actually returns sane values or not.

And yes, Thorsten is pre-sampling the terrain (again look for geodinfo() calls) and Torsten specifically provided a faster C++ version of that terrain presampler.
I think it's one of those features that was never properly documented by anyone (which really is the primary reason why I tend to using quoting so much...):

https://sourceforge.net/p/flightgear/ma ... /28606598/
Torsten wrote:The property /environment/terrain/area[0]/enables is set to a boolean
value of false on startup. This ensures the sampler is instantiated but
not running. You should set it to true when you enable local weather and
reset it to false when you disable local weather. Also, make sure to set
/environment/terrain/area[0]/input/use-aircraft-position to true if want
to sample the area around the aircraft's position.
So, No: the --prop switch will no longer be needed at startup, the
sampler is there but disabled, you can assume the system will be available.



https://sourceforge.net/p/flightgear/ma ... /27796966/
Thorsten wrote:Currently, Local Weather uses terrain
info in three ways:

1) a presampling routine gets gross features in the vicinity of the
aircraft, i.e. mean altitude, median altitude, max. altitude,... That's
now implemented by Torsten as C++ routine running in the background - I
don't know details and don't know if it would benefit



2) convective clouds are placed based on both terrain type and elevation.
For convective (not any other) clouds, there is extra work running for
2-30 seconds (dependent on number, terrain detail,...) in the background
at the rate of ~12 geodinfo() calls per frame (geodinfo is a rather
expensive operation - more than 25 per frame are simply out of the
question).

3) for the 3rd generation cloud-terrain model currently under development,
there's an additional need to probe elevation (but not terrain type) for
many cloud types - curently using geodinfo() as well

2) and 3) are among the most computationally intensive processes Local
Weather runs, simply because geodinfo() is expensive (though perhaps not
the most garbage-collection triggering ones). If that could be made much
faster, tile setup could be accelerated dramatically.

I wouldn't actually need the exact altitude/terrain type at a given
location for weather - an approximation which gets me some elevation and
terrain type within, say, 100 m of the specified coordinates would be just
as good, especially if it comes faster. If it really accelerates by a
large margin, then it allows the setup of tiles to be essentially in a
single frame, so one can really restart the system without the user
noticing.



See also:
https://sourceforge.net/p/flightgear/ma ... /26152557/
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 3:24 pm

Fine ! I will try it.


Don't expect too much for now.
From an integration standpoint, we are still a few steps away from hooking up everything correctly.
But I really only dropped the file into the submodule folder, and added a call to configureGUI() to control.nas, as well as extended GCAController to add the missing setTransmissionInterval() method.

I will definitely be following the topic and helping with this, but I am a little busy at the moment, so it may take me a few days to respond or post code snippets.
But please do get in touch if you are getting stuck somewhere, I am fairly familiar with Nasal and Canvas stuff, and should be able to work out stuff rather quickly - this is just to prevent you from having to reverse engineer stuff for hours ;-)

EDIT: Regarding terrain sampling, looking for docs, I stumbled across this - which is yet another method apparently: http://wiki.flightgear.org/Terrain_Detection
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Sat Sep 23, 2017 3:33 pm

Hooray wrote in Sat Sep 23, 2017 3:24 pm:Regarding terrain sampling, looking for docs, I stumbled across this - which is yet another method apparently: http://wiki.flightgear.org/Terrain_Detection
Will read that.

Regarding Canvas, is there a method to fill closed poligons with color? Or must use thick lines?
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Sat Sep 23, 2017 3:44 pm

rleibner wrote in Sat Sep 23, 2017 3:33 pm:Regarding Canvas, is there a method to fill closed poligons with color? Or must use thick lines?


The thing to keep in mind here is that the Canvas system is using OpenVG for 2D drawing purposes (via ShivaVG).
There is a dedicated wrapper module in $FG_ROOT/Nasal/canvas/api.nas

This contains wrappers for all Canvas rendering "primitives" (so called Canvas elements).
Specifically, you will want to look for Canvas.Path there (all of which are inhering from Canvas.Element).

Thus, you only need to look for the keyword "fill" in api.nas to see what is posssible, and you should find all supported OpenVG drawing modes:

# Path
# ==============================================================================
# Class for an (OpenVG) path element on a canvas
#
var Path = {
# Path segment commands (VGPathCommand)
VG_CLOSE_PATH: 0,
VG_MOVE_TO: 2,
VG_MOVE_TO_ABS: 2,
VG_MOVE_TO_REL: 3,
VG_LINE_TO: 4,
VG_LINE_TO_ABS: 4,
VG_LINE_TO_REL: 5,
VG_HLINE_TO: 6,
VG_HLINE_TO_ABS: 6,
VG_HLINE_TO_REL: 7,
VG_VLINE_TO: 8,
VG_VLINE_TO_ABS: 8,
VG_VLINE_TO_REL: 9,
VG_QUAD_TO: 10,
VG_QUAD_TO_ABS: 10,
VG_QUAD_TO_REL: 11,
VG_CUBIC_TO: 12,
VG_CUBIC_TO_ABS: 12,
VG_CUBIC_TO_REL: 13,
VG_SQUAD_TO: 14,
VG_SQUAD_TO_ABS: 14,
VG_SQUAD_TO_REL: 15,
VG_SCUBIC_TO: 16,
VG_SCUBIC_TO_ABS: 16,
VG_SCUBIC_TO_REL: 17,
VG_SCCWARC_TO: 20, # Note that CC and CCW commands are swapped. This is
VG_SCCWARC_TO_ABS:20, # needed due to the different coordinate systems used.
VG_SCCWARC_TO_REL:21, # In OpenVG values along the y-axis increase from bottom
VG_SCWARC_TO: 18, # to top, whereas in the Canvas system it is flipped.
VG_SCWARC_TO_ABS: 18,
VG_SCWARC_TO_REL: 19,
VG_LCCWARC_TO: 24,
VG_LCCWARC_TO_ABS:24,
VG_LCCWARC_TO_REL:25,
VG_LCWARC_TO: 22,
VG_LCWARC_TO_ABS: 22,
VG_LCWARC_TO_REL: 23,


For reference, see: https://www.khronos.org/files/openvg-qu ... e-card.pdf
http://old.siggraph.org/publications/20 ... OpenVG.pdf
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Tue Sep 26, 2017 5:19 am

This is the general (simplified) scheme in which I am working:
Image
Your comments are wellcome.
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Tue Sep 26, 2017 5:12 pm

for now, the gca_gui.nas file uses hard-coded default values for all fields (specified in the inputs vector containing hashes), it would be pretty straightforward to replace defaults with callbacks (functions) that are executed to provide said default values - that way, your existing heuristics could be moved to helper functions that populate the UI based on your current heuristics.

Apart from that, your drawing looks really good to me, and I would suggest to add it to the wiki to help clarify how the whole thing works under the hood.
I particularly like the little PAR screen ;-)
More seriously thouugh, we could also add tiny screenshots of the dialogs to the diagram and use a speaker symbol for the final "voice instruction" line.

Like I said previously, it would only require very tiny changes to also make the VGrid/HGrid parameters in the PARScreen configurable using the same UI.
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Tue Sep 26, 2017 7:03 pm

Hooray wrote in Tue Sep 26, 2017 5:12 pm: it would be pretty straightforward to replace defaults with callbacks

I have added a 2nd argument to configureGCA():
Code: Select all
var configureGCA = func( gcaObject, values ) { # second argument is a hash (accepts empty {} one ).

var defaults = {icao: 'KSFO' , rwy:'28R', channel:'/sim/messages/approach', interval:'5.00'};

foreach(var key; keys(values)) defaults[key] = values[key];
and changed (in input hashes) from default_value:'KSFO' to default_value:defaults.icao
It's working fine.

Other than that, I got errors running call(GCAController[field.callback], [value], gcaObject );
So, I've changed all input's callbak members from callback:GCAController.setPosition to callback:gcaObject.setPosition
and make the call() as call(field.callback, [value], gcaObject );

I am still debugging this structure, but it looks promising.
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

Re: Spoken GCA

Postby Hooray » Tue Sep 26, 2017 7:52 pm

Okay, that should work, too.
I am a little surprised seeing the code/error reports, I don't remember writing that code :oops:
I would have expected the callback to be specified in the form of callback:GCAController.setPosition and the actual invocation/call to use Nasal's call API: http://wiki.flightgear.org/Nasal_library#call.28.29

call(func[, args[, me[, locals[, error]]]);


Thus, I think the correct (but untested) line would be something like this:

Code: Select all
call(field.callback, [value], gcaObject );


I think the line you were quoting still stems from my original proposal which was using strings to specifcy the method name, and in that case the lookup would have been correct.
But the above should actually work.

There should be no need to use gcaObject instead of GCAController, and it may turn out to become problematic at a later time, because it introduces the assumption that there will only ever be a single gca running.

However, given the degree of progress we've been seeing here, it would not be far-fetched to use the same GCAController setup to also control scripted AI traffic in a semi-automated fashion, fairly easily.


EDIT: Seems you already fixed the bug, but you don't need to pass gcaObject directly, it will be done by the call() expression, which hands over a handle to the gcaObject - thus, you can simply pass GCAController.METHODNAME - and the "this" (me reference) will be passed in the form of gcaObject.


PS: To actually edit fields, navigate to the inputs vector (arrary) and copy a suitable line - it's a line of comma-separated hashes, all using the same keys:
  • text - the label to be used
  • default_value - the default value to be shown/used
  • focus - to set the widget focus to true/false
  • callback - the GCA method to invoke to update the GCA object (this will get a text/string from the input field)
  • tootip - the tooltip to be shown
  • validate: a string matching an entry in the validation_helpers hash to lookup a callback to validate the input
  • convert - to be used for converting values (e.g. from text to numbers for calculations) - currently unused
  • unit - only used for appending to the label

Thus, the VGrid/HGrid stuff can be easily customized using the same dialog if we want to
Please don't send support requests by PM, instead post your questions on the forum so that all users can contribute and benefit
Thanks & all the best,
Hooray
Help write next month's newsletter !
pui2canvas | MapStructure | Canvas Development | Programming resources
Hooray
 
Posts: 11349
Joined: Tue Mar 25, 2008 8:40 am

Re: Spoken GCA

Postby rleibner » Wed Sep 27, 2017 4:48 am

Hooray wrote in Tue Sep 26, 2017 7:52 pm:PS: To actually edit fields, navigate to the inputs vector (arrary) and copy a suitable line

Got it.
Hooray wrote in Tue Sep 26, 2017 7:52 pm:convert - to be used for converting values (e.g. from text to numbers for calculations) - currently unused

Note that setGlidepath() (and other settings) expects numeric args.
By now I'm using :
Code: Select all
setGlidepath: func(slope) {
me.destination.glidepath = num(slope) ;
},
but think it's proper to convert them in origin (if I knew how).
Rodolfo
*************************
Non-shared knowledge is lost knowledge
User avatar
rleibner
 
Posts: 246
Joined: Fri May 19, 2017 7:17 pm
Location: Uruguay - SUMU
Callsign: CX-BEX
Version: 2180.4.0
OS: Ubuntu 18.04

PreviousNext

Return to Nasal

Who is online

Users browsing this forum: No registered users and 1 guest