# ============================================================================== # Boeing Navigation Display by Gijs de Rooy # See: http://wiki.flightgear.org/Canvas_ND_Framework # ============================================================================== # This file makes use of the MapStructure framework, see: http://wiki.flightgear.org/Canvas_MapStructure # # Sooner or later, some parts will be revamped by coming up with a simple animation framework: http://wiki.flightgear.org/NavDisplay#mapping_vs._SVG_animation ## # pseudo DSL-ish: use these as placeholders in the config hash below var ALWAYS = func 1; var NOTHING = func nil; ## # TODO: move ND-specific implementation details into this lookup hash # so that other aircraft and ND types can be more easily supported # # any aircraft-specific ND behavior should be wrapped here, # to isolate/decouple things in the generic NavDisplay class # # TODO: move this to an XML config file (maybe supporting SGCondition and/or SGStateMachine markup for the logic?) # var NDStyles = { ## # this configures the 744 ND to help generalize the NavDisplay class itself 'Boeing': { font_mapper: func(family, weight) { if( family == "Liberation Sans" and weight == "normal" ) return "LiberationFonts/LiberationSans-Regular.ttf"; }, # where all the symbols are stored # TODO: SVG elements should be renamed to use boeing/airbus prefix # aircraft developers should all be editing the same ND.svg image # the code can deal with the differences now svg_filename: "Nasal/canvas/map/Images/boeingND.svg", ## ## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map) ## # TODO: phase out isMapStructure flag once map.nas & *.draw files are killed layers: [ # TODO: take z-indices from *.draw files -- now handled by MapStructure in the addLayer method. { name:'WXR_live', isMapStructure:1, always_update: 1, update_on:[ 'toggle_range','toggle_weather','toggle_display_mode','toggle_weather_live'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_weather') and nd.get_switch('toggle_weather_live') and nd.get_switch('toggle_display_mode') != "PLAN"; layer.group.setVisible(visible); if (visible) { layer.update(); } }, # end of layer update predicate 'z-index': -100, }, { name:'FIX', isMapStructure:1, update_on:['toggle_display_mode','toggle_range','toggle_waypoints'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { # print("Running fix layer predicate"); # toggle visibility here var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: FIX"); # (Hopefully) smart update layer.update(); } }, # end of layer update predicate 'z-index': -3, }, # end of FIX layer # Should redraw every 10 seconds TODO: use new MapStructure/WXR here once that works properly (Gijs should check first!) { name:'WXR', isMapStructure:1, update_on:[ {rate_hz: 0.1}, 'toggle_range','toggle_weather','toggle_display_mode', 'toggle_weather_live'], predicate: func(nd, layer) { #print("Running storms predicate"); var visible=nd.get_switch('toggle_weather') and !nd.get_switch('toggle_weather_live') and nd.get_switch('toggle_display_mode') != "PLAN"; layer.group.setVisible(visible); if (visible) { print("storms update requested! (timer issue when closing the dialog?)"); layer.update(); } }, # end of layer update predicate 'z-index': -4, }, # end of storms/WXR layer { name:'APS', isMapStructure:1, always_update: 1, update_on:['toggle_display_mode'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_display_mode') == "PLAN"; layer.group.setVisible( visible ); if (visible) { layer.update(); } }, }, { name:'APT', isMapStructure:1, update_on:['toggle_range','toggle_airports','toggle_display_mode'], predicate: func(nd, layer) { # toggle visibility here var visible=nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']); layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: APT"); layer.update(); } }, # end of layer update predicate 'z-index': -1, }, # end of APT layer # Should distinct between low and high altitude navaids. Hiding above 40 NM for now, to prevent clutter/lag. { name:'VOR', isMapStructure:1, update_on:['toggle_range','toggle_stations','toggle_display_mode'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { # toggle visibility here var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: VOR"); layer.update(); } }, # end of layer update predicate 'z-index': -2, }, # end of VOR layer { name:'DME', isMapStructure:1, update_on:['toggle_display_mode','toggle_range','toggle_stations'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); # toggle visibility here layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: DME"); layer.update(); } }, # end of layer update predicate 'z-index': -2, }, # end of DME layer { name:'TFC', isMapStructure:1, always_update: 1, update_on:['toggle_range','toggle_traffic'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_traffic'); layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: TFC"); layer.update(); } }, # end of layer update predicate 'z-index': 4, }, # end of traffic layer { name:'runway-nd', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { #print("runway-nd wants to be ported to MapStructure"); var visible = (nd.rangeNm() <= 40) and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']) ; if (visible) layer._model.init(); # clear & redraw layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of airports-nd layer { name:'RTE', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); layer.group.setVisible( visible ); if (visible) layer.update(); }, # end of layer update predicate 'z-index': 1, }, # end of route layer { name:'WPT', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); layer.group.setVisible( visible ); if (visible) layer.update(); }, # end of layer update predicate 'z-index': 2, }, # end of waypoint layer { name:'ALT-profile', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); layer.group.setVisible( visible ); if (visible) layer.update(); }, # end of layer update predicate 'z-index': 3, }, # end of altitude profile layer ## add other layers here, layer names must match the registered names as used in *.layer files for now ## this will all change once we're using Philosopher's MapStructure framework ], # end of vector with configured layers # This is where SVG elements are configured by providing "behavior" hashes, i.e. for animations # to animate each SVG symbol, specify behavior via callbacks (predicate, and true/false implementation) # SVG identifier, callback etc # TODO: update_on([]), update_mode (update() vs. timers/listeners) # TODO: support putting symbols on specific layers features: [ { # TODO: taOnly doesn't need to use getprop polling in update(), use a listener instead! id: 'taOnly', # the SVG ID impl: { # implementation hash init: func(nd, symbol), # for updateCenter stuff, called during initialization in the ctor predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") == 2, # the condition is_true: func(nd) nd.symbols.taOnly.show(), is_false: func(nd) nd.symbols.taOnly.hide(), }, # end of taOnly behavior/callbacks }, { id: 'tcasOff', impl: { init: func(nd, symbol), predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") == 0 and nd.get_switch('toggle_traffic') and (nd.in_mode('toggle_display_mode', ['MAP']) or (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered'))), is_true: func(nd) nd.symbols.tcasOff.show(), is_false: func(nd) nd.symbols.tcasOff.hide(), }, }, { id: 'tcasFail', impl: { init: func(nd, symbol), predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") != 0 and getprop("instrumentation/tcas/serviceable") != 1 and nd.get_switch('toggle_traffic') and (nd.in_mode('toggle_display_mode', ['MAP']) or (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered'))), is_true: func(nd) nd.symbols.tcasFail.show(), is_false: func(nd) nd.symbols.tcasFail.hide(), }, }, { id: 'tcasTest', impl: { init: func(nd, symbol), predicate: func(nd) getprop("instrumentation/tcas/inputs/self-test") == 1 and getprop("instrumentation/tcas/serviceable") == 1, is_true: func(nd) nd.symbols.tcasTest.show(), is_false: func(nd) nd.symbols.tcasTest.hide(), }, }, { id: 'tfc', impl: { init: func(nd, symbol), predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") != 0 and nd.get_switch('toggle_traffic') and (nd.in_mode('toggle_display_mode', ['MAP']) or (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered'))), is_true: func(nd) nd.symbols.tfc.show(), is_false: func(nd) nd.symbols.tfc.hide(), }, }, { id: 'traffic', impl: { init: func(nd, symbol), predicate: func(nd) getprop("instrumentation/tcas/outputs/traffic-alert") != 0 or getprop("instrumentation/tcas/outputs/advisory-alert"), is_true: func(nd) { nd.symbols.traffic.show(); if (getprop("instrumentation/tcas/outputs/traffic-alert")) nd.symbols.traffic.setColor(1,0,0); else nd.symbols.traffic.setColor(1,0.5,0); }, is_false: func(nd) nd.symbols.traffic.hide(), }, }, { id: 'tas', impl: { init: func(nd,symbol), predicate: func(nd) nd.aircraft_source.get_spd() > 100, is_true: func(nd) { nd.symbols.tas.setText(sprintf("%3.0f",nd.aircraft_source.get_spd() )); nd.symbols.tas.show(); }, is_false: func(nd) nd.symbols.tas.hide(), }, }, { id: 'tasLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.aircraft_source.get_spd() > 100, is_true: func(nd) nd.symbols.tasLbl.show(), is_false: func(nd) nd.symbols.tasLbl.hide(), }, }, { id: 'ilsFreq', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']), is_true: func(nd) { nd.symbols.ilsFreq.show(); if(getprop("instrumentation/nav/in-range")) nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/nav-id")); else nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt")); }, is_false: func(nd) nd.symbols.ilsFreq.hide(), }, }, { id: 'ilsLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']), is_true: func(nd) { nd.symbols.ilsLbl.show(); }, is_false: func(nd) nd.symbols.ilsLbl.hide(), }, }, { id: 'wpActiveId', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']), is_true: func(nd) { nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id")); nd.symbols.wpActiveId.show(); }, is_false: func(nd) nd.symbols.wpActiveId.hide(), }, # of wpActiveId.impl }, # of wpActiveId { id: 'wpActiveDist', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']), is_true: func(nd) { nd.symbols.wpActiveDist.setText(sprintf("%3.01f",getprop("/autopilot/route-manager/wp/dist"))); nd.symbols.wpActiveDist.show(); }, is_false: func(nd) nd.symbols.wpActiveDist.hide(), }, }, { id: 'wpActiveDistLbl', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']), is_true: func(nd) { nd.symbols.wpActiveDistLbl.show(); if(getprop("/autopilot/route-manager/wp/dist") > 1000) nd.symbols.wpActiveDistLbl.setText(" NM"); }, is_false: func(nd) nd.symbols.wpActiveDistLbl.hide(), }, }, { id: 'eta', impl: { init: func(nd,symbol), predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN']), is_true: func(nd) { var etaSec = getprop("/sim/time/utc/day-seconds")+getprop("autopilot/route-manager/wp/eta-seconds"); var h = math.floor(etaSec/3600); etaSec=etaSec-3600*h; var m = math.floor(etaSec/60); etaSec=etaSec-60*m; var s = etaSec/10; if (h>24) h=h-24; nd.symbols.eta.setText(sprintf("%02.0f%02.0f.%01.0fz",h,m,s)); nd.symbols.eta.show(); }, is_false: func(nd) nd.symbols.eta.hide(), }, # of eta.impl }, # of eta { id: 'gsGroup', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']), is_true: func(nd) { if(nd.get_switch('toggle_centered')) nd.symbols.gsGroup.setTranslation(0,0); else nd.symbols.gsGroup.setTranslation(0,150); nd.symbols.gsGroup.show(); }, is_false: func(nd) nd.symbols.gsGroup.hide(), }, }, { id: 'compassApp', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['APP','VOR'])), is_true: func(nd) { if(nd.get_switch('toggle_true_north')) var hdg = nd.aircraft_source.get_trk_tru(); else var hdg = nd.aircraft_source.get_trk_mag(); nd.symbols.compassApp.setRotation(-hdg*D2R); nd.symbols.compassApp.show(); }, is_false: func(nd) nd.symbols.compassApp.hide(), }, }, { id: 'compassMapCtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['MAP'])), is_true: func(nd) { if(nd.get_switch('toggle_true_north')) var hdg = nd.aircraft_source.get_trk_tru(); else var hdg = nd.aircraft_source.get_trk_mag(); nd.symbols.compassMapCtr.setRotation(-hdg*D2R); nd.symbols.compassMapCtr.show(); }, is_false: func(nd) nd.symbols.compassMapCtr.hide(), }, }, { id:'hdg', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']), is_true: func(nd) { var hdgText = ""; if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT") or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD")) { if(nd.get_switch('toggle_true_north')) hdgText = nd.aircraft_source.get_trk_tru(); else hdgText = nd.aircraft_source.get_trk_mag(); } else { if(nd.get_switch('toggle_true_north')) hdgText = nd.aircraft_source.get_hdg_tru(); else hdgText = nd.aircraft_source.get_hdg_mag(); } if(hdgText < 0.5) hdgText = 360 + hdgText; elsif(hdgText >= 360.5) hdgText = hdgText - 360; nd.symbols.hdg.setText(sprintf("%03.0f", hdgText)); }, is_false: NOTHING, }, }, { id:'hdgGroup', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']), is_true: func(nd) { nd.symbols.hdgGroup.show(); if(nd.get_switch('toggle_centered')) nd.symbols.hdgGroup.setTranslation(0,100); else nd.symbols.hdgGroup.setTranslation(0,0); }, is_false: func(nd) nd.symbols.hdgGroup.hide(), }, }, { id:'gs', impl: { init: func(nd,symbol), common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_gnd_spd() )), predicate: func(nd) nd.aircraft_source.get_gnd_spd() >= 30, is_true: func(nd) { nd.symbols.gs.setFontSize(36); }, is_false: func(nd) nd.symbols.gs.setFontSize(52), }, }, { id:'compass', impl: { init: func(nd,symbol), predicate: func(nd) ((nd.get_switch('toggle_centered') and !nd.in_mode('toggle_display_mode', ['PLAN'])) or nd.in_mode('toggle_display_mode', ['PLAN'])), is_true: func(nd) nd.symbols.compass.hide(), is_false: func(nd) { nd.symbols.compass.show(); nd.symbols.compass.setRotation(-nd.userHdgTrk*D2R); }, }, }, { id:'truMag', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_true_north'), is_true: func(nd) nd.symbols.truMag.setText("TRU"), is_false: func(nd) nd.symbols.truMag.setText("MAG"), }, }, { id:'northUp', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['PLAN']), is_true: func(nd) nd.symbols.northUp.show(), is_false: func(nd) nd.symbols.northUp.hide(), }, }, { id:'planArcs', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['PLAN']), is_true: func(nd) nd.symbols.planArcs.show(), is_false: func(nd) nd.symbols.planArcs.hide(), }, }, { id:'aplSymMap', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']) and !nd.get_switch('toggle_centered')), is_true: func(nd) nd.symbols.aplSymMap.show(), is_false: func(nd) nd.symbols.aplSymMap.hide(), }, }, { id:'aplSymMapCtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_centered')), is_true: func(nd) nd.symbols.aplSymMapCtr.show(), is_false: func(nd) nd.symbols.aplSymMapCtr.hide(), }, }, { id:'aplSymVor', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')), is_true: func(nd) nd.symbols.aplSymVor.show(), is_false: func(nd) nd.symbols.aplSymVor.hide(), }, }, { id:'rangeArcs', impl: { init: func(nd,symbol), predicate: func(nd) !nd.get_switch('toggle_centered') and nd.get_switch('toggle_rangearc'), is_true: func(nd) nd.symbols.rangeArcs.show(), is_false: func(nd) nd.symbols.rangeArcs.hide(), }, }, { id:'rangePln1', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN", is_true: func(nd) { nd.symbols.rangePln1.show(); nd.symbols.rangePln1.setText(sprintf("%3.0f",nd.rangeNm())); }, is_false: func(nd) nd.symbols.rangePln1.hide(), }, }, { id:'rangePln2', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN", is_true: func(nd) { nd.symbols.rangePln2.show(); nd.symbols.rangePln2.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.rangePln2.hide(), }, }, { id:'rangePln3', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN", is_true: func(nd) { nd.symbols.rangePln3.show(); nd.symbols.rangePln3.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.rangePln3.hide(), }, }, { id:'rangePln4', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN", is_true: func(nd) { nd.symbols.rangePln4.show(); nd.symbols.rangePln4.setText(sprintf("%3.0f",nd.rangeNm())); }, is_false: func(nd) nd.symbols.rangePln4.hide(), }, }, { id:'range', impl: { init: func(nd,symbol), predicate: func(nd) !nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.range.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.rangePln4.hide(), }, }, { id:'rangeCtr1', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['APP','MAP','VOR'])), is_true: func(nd) { nd.symbols.rangeCtr1.show(); nd.symbols.rangeCtr1.setText(sprintf("%g",nd.rangeNm()/4)); }, is_false: func(nd) nd.symbols.rangeCtr1.hide(), }, }, { id:'rangeCtr2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['APP','MAP','VOR'])), is_true: func(nd) { nd.symbols.rangeCtr2.show(); nd.symbols.rangeCtr2.setText(sprintf("%g",nd.rangeNm()/4)); }, is_false: func(nd) nd.symbols.rangeCtr2.hide(), }, }, { id:'altArc', impl: { init: func(nd,symbol), predicate: func(nd) (!nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['MAP'])), is_true: func(nd) { var altDiff = (getprop("autopilot/settings/target-altitude-ft") or 0)-(getprop("instrumentation/altimeter/indicated-altitude-ft") or 0); if (abs(nd.aircraft_source.get_vspd()) > 1 and altDiff/nd.aircraft_source.get_vspd() > 0) { var altRangeNm = altDiff/nd.aircraft_source.get_vspd()*nd.aircraft_source.get_gnd_spd()*KT2MPS*M2NM; if(altRangeNm > 1) { var altRangePx = (350/nd.rangeNm())*altRangeNm; if (altRangePx > 700) altRangePx = 700; nd.symbols.altArc.setTranslation(0,-altRangePx); } nd.symbols.altArc.show(); } else nd.symbols.altArc.hide(); }, is_false: func(nd) nd.symbols.altArc.hide(), }, }, { id:'altArcCtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.in_mode('toggle_display_mode', ['MAP'])), is_true: func(nd) { var altDiff = (getprop("autopilot/settings/target-altitude-ft") or 0)-(getprop("instrumentation/altimeter/indicated-altitude-ft") or 0); if (abs(nd.aircraft_source.get_vspd()) > 1 and altDiff/nd.aircraft_source.get_vspd() > 0) { var altRangeNm = altDiff/nd.aircraft_source.get_vspd()*nd.aircraft_source.get_gnd_spd()*KT2MPS*M2NM; if(altRangeNm > 1) { var altRangePx = (350/nd.rangeNm())*altRangeNm; if (altRangePx > 350) altRangePx = 350; nd.symbols.altArcCtr.setTranslation(0,-altRangePx); } nd.symbols.altArcCtr.show(); } else nd.symbols.altArcCtr.hide(); }, is_false: func(nd) nd.symbols.altArcCtr.hide(), }, }, { id:'crsLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) nd.symbols.crsLbl.show(), is_false: func(nd) nd.symbols.crsLbl.hide(), }, }, { id:'crs', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) { nd.symbols.crs.show(); if(getprop("instrumentation/nav/radials/selected-deg") != nil) nd.symbols.crs.setText(sprintf("%03.0f",getprop("instrumentation/nav/radials/selected-deg"))); }, is_false: func(nd) nd.symbols.crs.hide(), }, }, { id:'dmeLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) nd.symbols.dmeLbl.show(), is_false: func(nd) nd.symbols.dmeLbl.hide(), }, }, { id:'dme', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) { nd.symbols.dme.show(); if(getprop("instrumentation/dme/in-range")) nd.symbols.dme.setText(sprintf("%3.1f",getprop("instrumentation/dme/indicated-distance-nm"))); }, is_false: func(nd) nd.symbols.dme.hide(), }, }, { id:'trkInd2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')), is_true: func(nd) { if (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD") nd.symbols.trkInd2.setRotation(0); else nd.symbols.trkInd2.setRotation((nd.userTrk-nd.userHdg)*D2R); nd.symbols.trkInd2.show(); }, is_false: func(nd) nd.symbols.trkInd2.hide(), }, }, { id:'trkIndMapCtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_centered')), is_true: func(nd) { if (nd.get_switch('toggle_display_type') == "CRT" or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD")) nd.symbols.trkIndMapCtr.setRotation(0); else nd.symbols.trkIndMapCtr.setRotation((nd.userTrk-nd.userHdg)*D2R); nd.symbols.trkIndMapCtr.show(); }, is_false: func(nd) nd.symbols.trkIndMapCtr.hide(), }, }, { id:'vorCrsPtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.vorCrsPtr.show(); if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT") or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD")) nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R); else nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R); }, is_false: func(nd) nd.symbols.vorCrsPtr.hide(), }, }, { id:'vorCrsPtr2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.vorCrsPtr2.show(); if((nd.in_mode('toggle_display_mode', ['MAP']) and nd.get_switch('toggle_display_type') == "CRT") or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD")) nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_trk_mag())*D2R); else nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_mag())*D2R); }, is_false: func(nd) nd.symbols.vorCrsPtr2.hide(), }, }, { id: 'gsDiamond', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']) and getprop("instrumentation/nav/gs-in-range"), is_true: func(nd) { var gs_deflection = getprop("instrumentation/nav/gs-needle-deflection-norm"); if(gs_deflection != nil) nd.symbols.gsDiamond.setTranslation(gs_deflection*150,0); if(abs(gs_deflection) < 0.99) nd.symbols.gsDiamond.setColorFill(1,0,1,1); else nd.symbols.gsDiamond.setColorFill(0,0,0,1); }, is_false: func(nd) nd.symbols.gsGroup.hide(), }, }, { id:'locPtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")), is_true: func(nd) { nd.symbols.locPtr.show(); var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm"); nd.symbols.locPtr.setTranslation(deflection*150,0); if(abs(deflection) < 0.99) nd.symbols.locPtr.setColorFill(1,0,1,1); else nd.symbols.locPtr.setColorFill(0,0,0,1); }, is_false: func(nd) nd.symbols.locPtr.hide(), }, }, { id:'locPtr2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")), is_true: func(nd) { nd.symbols.locPtr2.show(); var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm"); nd.symbols.locPtr2.setTranslation(deflection*150,0); if(abs(deflection) < 0.99) nd.symbols.locPtr2.setColorFill(1,0,1,1); else nd.symbols.locPtr2.setColorFill(0,0,0,1); }, is_false: func(nd) nd.symbols.locPtr2.hide(), }, }, { id:'wind', impl: { init: func(nd,symbol), predicate: func(nd) (nd.aircraft_source.get_spd() > 100 and getprop("environment/wind-speed-kt") > 4), is_true: func(nd) { var windDir = getprop("environment/wind-from-heading-deg"); var windSpd = getprop("environment/wind-speed-kt"); if(!nd.get_switch('toggle_true_north')) windDir = windDir - getprop("environment/magnetic-variation-deg"); while(windDir < 0.5) windDir += 360; while(windDir > 360.5) windDir -= 360; if (windSpd > 6) nd.windShown = 1; if (nd.windShown != 0) { nd.symbols.wind.setText(sprintf("%03.0f° / %02.0f",windDir,windSpd)); nd.symbols.wind.show(); } else nd.symbols.wind.hide(); }, is_false: func(nd) { nd.windShown = 0; nd.symbols.wind.hide(); }, }, }, { id:'windArrow', impl: { init: func(nd,symbol), predicate: func(nd) (!(nd.in_mode('toggle_display_mode', ['PLAN']) and (nd.get_switch('toggle_display_type') == "LCD")) and nd.aircraft_source.get_spd() > 100 and getprop("environment/wind-speed-kt") > 4), is_true: func(nd) { if (nd.windShown != 0) { nd.symbols.windArrow.show(); var windArrowRot = getprop("environment/wind-from-heading-deg") - getprop("environment/magnetic-variation-deg"); if((nd.in_mode('toggle_display_mode', ['MAP','PLAN']) and nd.get_switch('toggle_display_type') == "CRT") or (nd.get_switch('toggle_track_heading') and nd.get_switch('toggle_display_type') == "LCD")) windArrowRot = windArrowRot - nd.aircraft_source.get_trk_mag(); else windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_mag(); nd.symbols.windArrow.setRotation(windArrowRot*D2R); } else { nd.symbols.windArrow.hide(); } }, is_false: func(nd) nd.symbols.windArrow.hide(), }, }, { id:'trendVector', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP'])), is_true: func(nd) { nd.symbols.trendVector.show(); var rollDeg = getprop("/orientation/roll-deg") or 0; if (abs(rollDeg) > 0.2 and nd.aircraft_source.get_gnd_spd() > 100) { var radius = abs(nd.aircraft_source.get_gnd_spd()*nd.aircraft_source.get_gnd_spd()/(68578.83369*math.tan(rollDeg*D2R))); var sec = 90; if (nd.rangeNm() <= 10) sec = 30; elsif (nd.rangeNm() == 20) sec = 60; var L = nd.aircraft_source.get_gnd_spd()*0.0002778*sec; var angle = L/radius; var factor = 346/(0.5*nd.rangeNm()); radius = radius*factor; if (radius > 65535) radius = 65535; # prevent 16-bit overflow var y = radius*math.sin(angle); var x = (1-math.cos(angle))*radius; if (rollDeg < 0) { x = -x; var cmd = 21; if (angle > math.pi and angle < 2 * math.pi) cmd = 25; } else { var cmd = 19; if (angle > math.pi and angle < 2 * math.pi) cmd = 23; } var y1 = 824; if (nd.get_switch('toggle_centered')) y1 = 562; nd.symbols.trendVector.setData([2,cmd],[512,y1,radius,radius,0,x,-y]); var dash = L*factor; var dashArray = [dash/3-3, 5]; if (nd.rangeNm() <= 10) dashArray = [dash]; elsif (nd.rangeNm() == 20) dashArray = [dash/2-2, 5]; nd.symbols.trendVector.setStrokeDashArray(dashArray); } }, is_false: func(nd) nd.symbols.trendVector.hide(), }, }, { id:'vorL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') == 1) { nd.symbols.vorL.setText("VOR L"); nd.symbols.vorL.setColor(0.195,0.96,0.097); } else { nd.symbols.vorL.setText("ADF L"); nd.symbols.vorL.setColor(0,0.6,0.85); } nd.symbols.vorL.show(); }, is_false: func(nd) nd.symbols.vorL.hide(), }, }, { id:'vorLId', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') == 1) { if(getprop("instrumentation/nav/in-range")) nd.symbols.vorLId.setText(getprop("instrumentation/nav/nav-id")); else nd.symbols.vorLId.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt")); nd.symbols.vorLId.setColor(0.195,0.96,0.097); } else { if((var navident=getprop("instrumentation/adf/ident")) != "") nd.symbols.vorLId.setText(navident); else nd.symbols.vorLId.setText(sprintf("%3d",getprop("instrumentation/adf/frequencies/selected-khz"))); nd.symbols.vorLId.setColor(0,0.6,0.85); } nd.symbols.vorLId.show(); }, is_false: func(nd) nd.symbols.vorLId.hide(), }, }, { id:'dmeLDist', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') == 1) { if(getprop("instrumentation/dme/in-range")) nd.symbols.dmeLDist.setText(sprintf("%3.1f",getprop("instrumentation/dme/indicated-distance-nm"))); else nd.symbols.dmeLDist.setText(" ---"); nd.symbols.dmeLDist.setColor(0.195,0.96,0.097); } else { nd.symbols.dmeLDist.setText(""); nd.symbols.dmeLDist.setColor(0,0.6,0.85); } nd.symbols.dmeLDist.show(); }, is_false: func(nd) nd.symbols.dmeLDist.hide(), }, }, { id:'dmeL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') == 1) { nd.symbols.dmeL.setText("DME"); nd.symbols.dmeL.setColor(0.195,0.96,0.097); } else { nd.symbols.dmeL.setText(""); nd.symbols.dmeL.setColor(0,0.6,0.85); } nd.symbols.dmeL.show(); }, is_false: func(nd) nd.symbols.dmeL.hide(), }, }, # TACAN added by tikibar (J. Williams) { id:'TCNArrow', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and !nd.get_switch('toggle_centered') and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan')) { nd.symbols.TCNArrow.show(); nd.symbols.TCNArrow.setRotation((getprop("instrumentation/tacan/indicated-bearing-true-deg")-nd.aircraft_source.get_hdg_tru())*D2R); } else { nd.symbols.TCNArrow.hide(); } }, is_false: func(nd) nd.symbols.TCNArrow.hide(), }, }, { id:'TCNArrow2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.get_switch('toggle_centered') and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan')) { nd.symbols.TCNArrow2.show(); nd.symbols.TCNArrow2.setRotation((getprop("instrumentation/tacan/indicated-bearing-true-deg")-nd.aircraft_source.get_hdg_tru())*D2R); } else { nd.symbols.TCNArrow2.hide(); } }, is_false: func(nd) nd.symbols.TCNArrow2.hide(), }, }, { id:'TACAN', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan')) { nd.symbols.TACAN.setText("TACAN"); nd.symbols.TACAN.show(); } else { nd.symbols.TACAN.hide(); } }, is_false: func(nd) nd.symbols.TACAN.hide(), }, }, { id:'TACANId', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan') and getprop("instrumentation/tacan/in-range")) { nd.symbols.TACANId.setText(getprop("instrumentation/tacan/ident")); nd.symbols.TACANId.show(); } else { nd.symbols.TACANId.hide(); } }, is_false: func(nd) nd.symbols.TACANId.hide(), }, }, { id:'TACANdme', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan') and getprop("instrumentation/tacan/in-range")) { nd.symbols.TACANdme.setText("DIST"); nd.symbols.TACANdme.show(); } else { nd.symbols.TACANdme.hide(); } }, is_false: func(nd) nd.symbols.TACANdme.hide(), }, }, { id:'TACANdmeDist', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['MAP','APP','VOR']) and nd.efis_switches['toggle_tacan'] != nil), is_true: func(nd) { if (nd.get_switch('toggle_tacan') and getprop("instrumentation/tacan/in-range")) { nd.symbols.TACANdmeDist.setText(sprintf("%3.1f",getprop("instrumentation/tacan/indicated-distance-nm"))); nd.symbols.TACANdmeDist.show(); } else { nd.symbols.TACANdmeDist.hide(); } }, is_false: func(nd) nd.symbols.TACANdmeDist.hide(), }, }, ], # end of vector with features }, # end of Boeing style "Airbus" : { font_mapper: func(family, weight) { if( family == "Liberation Sans" and weight == "normal" ) return "LiberationFonts/LiberationSans-Regular.ttf"; }, # where all the symbols are stored # TODO: SVG elements should be renamed to use boeing/airbus prefix # aircraft developers should all be editing the same ND.svg image # the code can deal with the differences now svg_filename: "Nasal/canvas/map/Airbus/Images/airbusND.svg", ## ## this loads and configures existing layers (currently, *.layer files in Nasal/canvas/map) ## options: { position_callback: func(nd, pos){ if(nd.get_switch('toggle_centered') or nd.get_switch('toggle_display_mode') == 'PLAN') pos.range = (nd.rangeNm() * 1.6156087432822295); }, translation_callback: func(nd){ var t = {x: 512, y: 824}; if(nd.get_switch('toggle_centered') or nd.get_switch('toggle_display_mode') == 'PLAN') t.y = 512; return t; }, defaults: { # Configurable Global Options: # - fplan_active: the boolean property used to determine if the flight plan is active # - lat_ctrl: the property used to determine if lateral flight mode is managed # Lateral managed mode is similar to Boeing LNAV and indicates that the # aircraft should follow the current active flight plan. By default # the lat_ctrl property is checked against 'fmgc' (managed) value. # You can use a custom value instead on 'fmgc' by overriding 'managed_val' # - managed_val: the value that the property defined by 'lat_ctrl' should have in case # that the lateral flight mode is managed. # You can easily override these options before creating the ND, example: # canvas.NDStyles['Airbus'].options.defaults.fplan_active = 'my/autpilot/f-plan/active' fplan_active: 'autopilot/route-manager/active', lat_ctrl: 'flight-management/control/lat-ctrl', managed_val: 'fmgc', ver_ctrl: 'flight-management/control/ver-ctrl', spd_ctrl: '/flight-management/control/spd-ctrl', current_wp: '/autopilot/route-manager/current-wp', ap1: '/flight-management/control/ap1-master', ap2: '/flight-management/control/ap2-master', nav1_frq: 'instrumentation/nav/frequencies/selected-mhz', nav2_frq: 'instrumentation/nav[1]/frequencies/selected-mhz', adf1_frq: 'instrumentation/adf/frequencies/selected-khz', adf2_frq: 'instrumentation/adf[1]/frequencies/selected-khz', dep_rwy: '/autopilot/route-manager/departure/runway', dest_rwy: '/autopilot/route-manager/destination/runway', wp_count: 'autopilot/route-manager/route/num', level_off_alt: '/autopilot/route-manager/vnav/level-off-alt', athr: '/flight-management/control/a-thrust', app_mode: 'instrumentation/nd/app-mode', chrono_node: 'instrumentation/chrono', active_route_color: [0.4,0.7,0.4], inactive_route_color: [0.95,0.95,0.21] }, map: { "z-index" : -1 } }, layers: [ # Should redraw every 10 seconds TODO: use new MapStructure/WXR here once that works properly (Gijs should check first!) { name:'WXR_live', isMapStructure:1, always_update: 1, update_on:[ 'toggle_range','toggle_weather','toggle_display_mode','toggle_weather_live'], predicate: func(nd, layer) { var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_weather_live') and nd.get_switch('toggle_display_mode') != "PLAN"; layer.group.setVisible(visible); if (visible) { layer.update(); } }, # end of layer update predicate options: { viewport_radius: 706 }, 'z-index': -100, }, { name:'WXR', isMapStructure:1, update_on:[ {rate_hz: 0.1}, 'toggle_range','toggle_weather','toggle_display_mode', 'toggle_weather_live'], predicate: func(nd, layer) { #print("Running storms predicate"); var visible=nd.get_switch('toggle_weather') and !nd.get_switch('toggle_weather_live') and nd.get_switch('toggle_display_mode') != "PLAN"; layer.group.setVisible(visible); if (visible) { print("storms update requested! (timer issue when closing the dialog?)"); layer.update(); } }, # end of layer update predicate 'z-index': -4, }, # end of storms/WXR layer { name:'FIX', isMapStructure:1, update_on:['toggle_range','toggle_waypoints', 'toggle_display_mode'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); layer.group.setVisible( visible ); if (visible) layer.update(); }, # end of layer update predicate style: { color: [0.69, 0, 0.39], text_offset: [20,10], text_color: [1,1,1] }, options: { draw_function: func(group){ group.createChild('path') .moveTo(-10,0) .lineTo(0,-17) .lineTo(10,0) .lineTo(0,17) .close() .setStrokeLineWidth(3) .setColor(color) .setScale(1); } } }, # end of FIX layer { name: 'ALT-profile', isMapStructure: 1, update_on: ['toggle_display_mode','toggle_range',{rate_hz: 2}], predicate: func(nd, layer) { var visible = nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']);# and nd.get_switch('toggle_fplan'); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, style: { default_color: [1,1,1], armed_color: [0.06,0.55,1], managed_color: [0.68, 0, 0.38] }, options: { # You can overwrite these with different nodes, before creating the ND # Example: canvas.NDStyles['Airbus'].layers['ALT-profile'].options.vnav_node = 'my/vnav/node'; # In order to display ALT-profile on your ND you have to create the various # nodes corresponding to the different ALT pseudowaypoint in your aircraft code. # Every node must be set into the 'vnav_node' configured here (you can override it). # Example: if you want to display the top-of-descent symbol you must create a 'td' # node into vnav_node. Something like this: # /autopilot/route-manager/vnav/td/ # Each node should have the latitude-deg and longitude-deg properties. # Available nodes are: # tc (top of climb) # td (top of descent) # ec (end of climb) # ed (end of descent) # sc (start of climb) # sd (start of descent) # If ec and ed are altitude constraints, their node should have the # boolean 'alt-cstr' property set to 1. vnav_node: "/autopilot/route-manager/vnav/", types: ["tc", "td", "ec", "ed","sc","sd"], svg_path: { tc: 'Nasal/canvas/map/Airbus/Images/airbus_tc.svg', td: 'Nasal/canvas/map/Airbus/Images/airbus_td.svg', ec: 'Nasal/canvas/map/Airbus/Images/airbus_ec.svg', ed: 'Nasal/canvas/map/Airbus/Images/airbus_ed.svg', sc: 'Nasal/canvas/map/Airbus/Images/airbus_sc.svg', sd: 'Nasal/canvas/map/Airbus/Images/airbus_sd.svg' }, listen: [ 'fplan_active', 'ver_ctrl', 'ap1', 'ap2', 'current_wp' ], draw_callback: func(){ var name = me.model.getName(); var grp = me.element.getElementById(name~'_symbol'); if(grp == nil) return; var dfcolor = me.getStyle('default_color'); var armed_color = me.getStyle('armed_color'); var managed_color = me.getStyle('managed_color'); #print('Draw: -> ' ~ name); if(name == 'td' or name == 'sd' or name == 'sc'){ var vnav_armed = me.model.getValue('vnav-armed'); if(vnav_armed) grp.setColor(armed_color); else grp.setColor(dfcolor); } elsif(name == 'ed' or name == 'ec'){ var is_cstr = me.model.getValue('alt-cstr'); if(is_cstr) grp.setColor(managed_color); else grp.setColor(armed_color); } }, init_after_callback: func{ var name = me.model.getName(); var grp = me.element.getElementById(name~'_symbol'); if(name != 'td' and name != 'sd' and name != 'sc'){ grp.setTranslation(-50,0); } } } }, { name:'APT', isMapStructure:1, update_on:['toggle_range','toggle_airports', 'toggle_display_mode'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, # end of layer update predicate style: { svg_path: 'Nasal/canvas/map/Airbus/Images/airbus_airport.svg', text_offset: [45, 35], label_font_color: [1,1,1], label_font_size: 28 } }, # end of APT layer { name:'VOR-airbus', isMapStructure:1, update_on:['toggle_range','toggle_vor','toggle_display_mode'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { # print("Running vor layer predicate"); # toggle visibility here var visible = nd.get_switch('toggle_vor') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, # end of layer update predicate # You can override default colors within the style # Default color: 'color' # Tuned color: 'tuned_color' # Example: # canvas.NDStyles['Airbus'].layers['VOR-airbus'].style.color = [1,1,1]; # canvas.NDStyles['Airbus'].layers['VOR-airbus'].style.tuned_color = [0,0,1]; style: {}, options:{ listen: [ 'nav1_frq', 'nav2_frq' ], } }, # end of VOR layer { name:'DME', isMapStructure:1, disabled:1, update_on:['toggle_display_mode','toggle_range','toggle_dme'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { var visible = nd.get_switch('toggle_dme') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); # toggle visibility here layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: DME"); layer.update(); } }, # end of layer update predicate options: { draw_dme: func(sym){ return sym.createChild("path") .moveTo(-13, 0) .arcSmallCW(13,13,0,26,0) .arcSmallCW(13,13,0,-26,0) .setStrokeLineWidth(2) .close(); }, draw_text: 1 }, style: { color_default: [0.9,0,0.47], color_tuned: [0,0.62,0.84], }, 'z-index': -2, }, # end of DME layer { name:'NDB', isMapStructure:1, update_on:['toggle_range','toggle_ndb','toggle_display_mode'], # FIXME: this is a really ugly place for controller code predicate: func(nd, layer) { var visible = nd.get_switch('toggle_ndb') and nd.in_mode('toggle_display_mode', ['MAP']) and (nd.rangeNm() <= 40); # print("Running vor layer predicate"); # toggle visibility here layer.group.setVisible( visible ); if (visible) { layer.update(); } }, # end of layer update predicate # You can override default colors within the style # Default color: 'color' # Tuned color: 'tuned_color' # Example: # canvas.NDStyles['Airbus'].layers['VOR-airbus'].style.color = [1,1,1]; # canvas.NDStyles['Airbus'].layers['VOR-airbus'].style.tuned_color = [0,0,1]; style: { #svg_path: 'Nasal/canvas/map/Airbus/Images/airbus_ndb.svg' svg_path: '' }, options: { listen: [ 'adf1_frq', 'adf2_frq' ], init_after_callback: func{ #me.element.removeAllChildren(); me.text_ndb = me.element.createChild("text") .setDrawMode( canvas.Text.TEXT ) .setText(me.model.id) .setFont("LiberationFonts/LiberationSans-Regular.ttf") .setFontSize(28) .setTranslation(25,10); me.ndb_sym = me.element.createChild('path'); me.ndb_sym.moveTo(-15,15) .lineTo(0,-15) .lineTo(15,15) .close() .setStrokeLineWidth(3) .setColor(0.69,0,0.39) .setScale(1,1); }, draw_callback: func{ var frq = me.model.frequency; if(frq != nil){ var dfcolor = me.getStyle('color', [0.9,0,0.47]); var tuned_color = me.getStyle('tuned_color', [0,0.62,0.84]); frq = frq / 100; var adf1_frq = getprop(me.options.adf1_frq); var adf2_frq = getprop(me.options.adf2_frq); if(adf1_frq == frq or adf2_frq == frq){ me.element.setColor(tuned_color, [Path]); } else { me.element.setColor(dfcolor, [Path]); } } }, } }, # end of NDB layer { name:'TFC', #disabled:1, always_update: 1, isMapStructure:1, update_on:['toggle_range','toggle_traffic'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_traffic'); layer.group.setVisible( visible ); if (visible) { #print("Updating MapStructure ND layer: TFC"); layer.update(); } }, # end of layer update predicate }, # end of traffic layer { name:'RWY-profile', isMapStructure:1, update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { var visible = (nd.rangeNm() <= 40) and nd.in_mode('toggle_display_mode', ['MAP','PLAN']) ; layer.group.setVisible( visible ); if (visible) { layer.update(); } }, # end of layer update predicate options: { listen: [ 'fplan_active', 'dep_rwy', 'dest_rwy' ] } }, # end of runway layer { name:'HOLD', isMapStructure: 1, always_update: 1, update_on:['toggle_range','toggle_display_mode','toggle_wpt_idx'], predicate: func(nd, layer) { var visible= nd.in_mode('toggle_display_mode', ['MAP','PLAN']); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, options: { hold_node: '/flight-management/hold', hold_init: 'flight-management/hold/init', points_node: '/flight-management/hold/points', first_point_node: '/flight-management/hold/points/point/lat', hold_wp: '/flight-management/hold/wp', hold_wp_idx: '/flight-management/hold/wp_id', range_dependant: 1, listen: [ 'first_point_node', 'fplan_active', 'lat_ctrl', 'current_wp', 'hold_wp', 'hold_init', 'hold_wp_idx' ] } # end of layer update predicate }, # end of HOLD layer { name:'RTE', isMapStructure: 1, update_on:['toggle_range','toggle_display_mode', 'toggle_cstr', 'toggle_wpt_idx'], predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, # end of layer update predicate options: { display_inactive_rte: 1, listen: [ 'fplan_active', 'lat_ctrl', 'current_wp', 'wp_count' ], draw_after_callback: func{ me.setRouteStyle(); } }, style: { line_width: 5, #inactive_color: [0.95,0.95,0.21], #active_color: [0.4,0.7,0.4], color: func{ if(!contains(me, 'inactive_color')){ me.inactive_color = me.getStyle('inactive_color'); if(me.inactive_color == nil) me.inactive_color = me.getOption('inactive_route_color'); } if(!contains(me, 'active_color')){ me.active_color = me.getStyle('active_color'); if(me.active_color == nil) me.active_color = me.getOption('active_route_color'); } var is_active = getprop(me.options.fplan_active); (is_active ? me.active_color : me.inactive_color); }, color_alternate_current: [0,0.62,0.84], color_missed: [0,0.62,0.84], color_temporary: func me.getStyle('inactive_color', me.getOption('inactive_route_color')), color_secondary: [1,1,1], color_alternate_secondary: [1,1,1], line_dash: func{ var lat_ctrl = getprop(me.options.lat_ctrl); var is_managed = (lat_ctrl == me.options.managed_val); var is_active = getprop(me.options.fplan_active); (is_managed and is_active ? [] : [32, 16]); }, line_dash_alternate_current: [32,16], line_dash_temporary: [32,16] } }, # end of route layer { name:'WPT-airbus', isMapStructure: 1, update_on:['toggle_range','toggle_display_mode', 'toggle_cstr', 'toggle_wpt_idx'], style: { wp_color: [0.4,0.7,0.4], current_wp_color: [1,1,1], constraint_color: [1,1,1], active_constraint_color: [0.69,0,0.39], missed_constraint_color: [1,0.57,0.14] }, predicate: func(nd, layer) { var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); layer.group.setVisible( visible ); if (visible) { layer.toggle_cstr = nd.get_switch('toggle_cstr'); layer.update(); } }, # end of layer update predicate options: { listen: [ 'fplan_active', 'lat_ctrl', 'ver_ctrl', 'current_wp', 'wp_count', 'dep_rwy', 'dest_rwy', 'level_off_alt' ], } }, # end of WPT layer { name: 'SPD-profile', isMapStructure: 1, update_on: ['toggle_display_mode','toggle_range',{rate_hz: 2}], predicate: func(nd, layer) { var visible = nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, style: { color: [0.69,0,0.39] }, options: { spd_node: "/autopilot/route-manager/spd/", listen: [ 'fplan_active' ] } }, { name: 'DECEL', isMapStructure: 1, update_on: ['toggle_display_mode','toggle_range'], predicate: func(nd, layer) { var visible = nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']); layer.group.setVisible( visible ); if (visible) { layer.update(); } }, options: { # Overridable options: # decel_node: node containing latitude-deg and longitude-deg used to mark the deceleration point # managed_speed_node: boolean property indicating that the aircraft is flying in managed speed mode listen: [ 'fplan_active', 'spd_ctrl', 'ver_ctrl', 'athr' ] }, style: { # managed_color: decelaration symbol color when the aircraft flies in managed speed mode # selected_color: decelaration symbol color when the aircraft flies in selected speed mode managed_color: [0.68, 0, 0.38], selected_color: [1,1,1] } }, { name:'APS', isMapStructure:1, always_update: 1, update_on:['toggle_display_mode'], predicate: func(nd, layer) { var visible = nd.get_switch('toggle_display_mode') == "PLAN"; layer.group.setVisible( visible ); if (visible) { layer.update(); } }, style: { svg_path: 'Nasal/canvas/map/Airbus/Images/airbusAirplane.svg', #translate: [-45,-52] }, options: { model: { parents: [geo.Coord], id: 999999, pos: props.globals.getNode('position'), type: 'position', latlon: func(){ me.pos = props.globals.getNode('position'); return [ me.pos.getValue("latitude-deg"), me.pos.getValue("longitude-deg"), me.pos.getValue("altitude-ft") ]; }, equals: func(o){me.id == o.id} }, init_after_callback: func{ var aplSymbol = me.element.getElementById("airplane"); aplSymbol.setTranslation(-45,-52); } } }, # end of storms/WXR layer ## add other layers here, layer names must match the registered names as used in *.layer files for now ## this will all change once we're using Philosopher's MapStructure framework ], # end of vector with configured layers # This is where SVG elements are configured by providing "behavior" hashes, i.e. for animations # to animate each SVG symbol, specify behavior via callbacks (predicate, and true/false implementation) # SVG identifier, callback etc # TODO: update_on([]), update_mode (update() vs. timers/listeners) # TODO: support putting symbols on specific layers features: [ { id: 'compass_mask', impl: { init: func(nd, symbol), predicate: func(nd) !nd.get_switch('toggle_centered'), is_true: func(nd) nd.symbols.compass_mask.show(), is_false: func(nd) nd.symbols.compass_mask.hide(), } }, { id: 'compass_mask_ctr', impl: { init: func(nd, symbol), predicate: func(nd) nd.get_switch('toggle_centered') or nd.in_mode('toggle_display_mode',['PLAN']), is_true: func(nd) nd.symbols.compass_mask_ctr.show(), is_false: func(nd) nd.symbols.compass_mask_ctr.hide(), } }, { # TODO: taOnly doesn't need to use getprop polling in update(), use a listener instead! id: 'taOnly', # the SVG ID impl: { # implementation hash init: func(nd, symbol), # for updateCenter stuff, called during initialization in the ctor predicate: func(nd) getprop("instrumentation/tcas/inputs/mode") == 2, # the condition is_true: func(nd) nd.symbols.taOnly.show(), # if true, run this is_false: func(nd) nd.symbols.taOnly.hide(), # if false, run this }, # end of taOnly behavior/callbacks }, # end of taOnly { id: 'tas', impl: { init: func(nd,symbol), predicate: func(nd) nd.aircraft_source.get_spd() > 100, is_true: func(nd) { nd.symbols.tas.setText(sprintf("%3.0f",getprop("/velocities/airspeed-kt") )); nd.symbols.tas.show(); }, is_false: func(nd) nd.symbols.tas.hide(), }, }, { id: 'tasLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.aircraft_source.get_spd() > 100, is_true: func(nd) nd.symbols.tasLbl.show(), is_false: func(nd) nd.symbols.tasLbl.hide(), }, }, { id: 'ilsFreq', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP', 'VOR']), is_true: func(nd) { nd.symbols.ilsFreq.show(); nd.symbols.ilsFreq.setText(getprop("instrumentation/nav/frequencies/selected-mhz-fmt")); if(nd.get_switch('toggle_display_mode') == 'APP') nd.symbols.ilsFreq.setColor(0.69,0,0.39); else nd.symbols.ilsFreq.setColor(1,1,1); }, is_false: func(nd) nd.symbols.ilsFreq.hide(), }, }, { id: 'ilsLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP', 'VOR']), is_true: func(nd) { nd.symbols.ilsLbl.show(); if(nd.get_switch('toggle_display_mode') == 'APP') nd.symbols.ilsLbl.setText('ILS'); else nd.symbols.ilsLbl.setText('VOR 1'); }, is_false: func(nd) nd.symbols.ilsLbl.hide(), }, }, { id: 'wpActiveId', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']), is_true: func(nd) { nd.symbols.wpActiveId.setText(getprop("/autopilot/route-manager/wp/id")); nd.symbols.wpActiveId.show(); }, is_false: func(nd) nd.symbols.wpActiveId.hide(), }, # of wpActiveId.impl }, # of wpActiveId { id: 'wpActiveCrs', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/id") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']), is_true: func(nd) { #var cur_wp = getprop("autopilot/route-manager/current-wp"); var deg = int(getprop("/autopilot/route-manager/wp/bearing-deg")); nd.symbols.wpActiveCrs.setText(''~deg~'°'); nd.symbols.wpActiveCrs.show(); }, is_false: func(nd) nd.symbols.wpActiveCrs.hide(), }, # of wpActiveId.impl }, # of wpActiveId { id: 'wpActiveDist', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']), is_true: func(nd) { var dst = getprop("/autopilot/route-manager/wp/dist"); nd.symbols.wpActiveDist.setText(sprintf("%3.01f",dst)); nd.symbols.wpActiveDist.show(); }, is_false: func(nd) nd.symbols.wpActiveDist.hide(), }, }, { id: 'wpActiveDistLbl', impl: { init: func(nd,symbol), predicate: func(nd) getprop("/autopilot/route-manager/wp/dist") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']), is_true: func(nd) { nd.symbols.wpActiveDistLbl.show(); if(getprop("/autopilot/route-manager/wp/dist") > 1000) nd.symbols.wpActiveDistLbl.setText(" NM"); }, is_false: func(nd) nd.symbols.wpActiveDistLbl.hide(), }, }, { id: 'eta', impl: { init: func(nd,symbol), predicate: func(nd) getprop("autopilot/route-manager/wp/eta") != nil and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP', 'PLAN']), is_true: func(nd) { var etaSec = getprop("/sim/time/utc/day-seconds")+ getprop("autopilot/route-manager/wp/eta-seconds"); var h = math.floor(etaSec/3600); etaSec = etaSec-3600*h; var m = math.floor(etaSec/60); etaSec = etaSec-60*m; var s = etaSec / 10; if (h > 24) h = h - 24; nd.symbols.eta.setText(sprintf("%02.0f:%02.0f",h,m)); nd.symbols.eta.show(); }, is_false: func(nd) nd.symbols.eta.hide(), }, # of eta.impl }, # of eta { id: 'gsGroup', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']), is_true: func(nd) { if(nd.get_switch('toggle_centered')) nd.symbols.gsGroup.setTranslation(0,0); else nd.symbols.gsGroup.setTranslation(0,150); nd.symbols.gsGroup.show(); }, is_false: func(nd) nd.symbols.gsGroup.hide(), }, }, { id:'hdg', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']), is_true: func(nd) { var hdgText = ""; if(nd.in_mode('toggle_display_mode', ['MAP'])) { if(nd.get_switch('toggle_true_north')) hdgText = nd.aircraft_source.get_trk_tru(); else hdgText = nd.aircraft_source.get_trk_mag(); } else { if(nd.get_switch('toggle_true_north')) hdgText = nd.aircraft_source.get_hdg_tru(); else hdgText = nd.aircraft_source.get_hdg_mag(); } nd.symbols.hdg.setText(sprintf("%03.0f", hdgText+0.5)); }, is_false: NOTHING, }, }, { id:'hdgGroup', impl: { init: func(nd,symbol), predicate: func(nd) {return 0},#nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']), is_true: func(nd) { nd.symbols.hdgGroup.show(); if(nd.get_switch('toggle_centered')) nd.symbols.hdgGroup.setTranslation(0,100); else nd.symbols.hdgGroup.setTranslation(0,0); }, is_false: func(nd) nd.symbols.hdgGroup.hide(), }, }, { id:'altArc', impl: { init: func(nd,symbol), predicate: func(nd) {return 0},#nd.in_mode('toggle_display_mode', ['APP','MAP','VOR']), is_true: func(nd) { nd.symbols.altArc.show(); }, is_false: func(nd) nd.symbols.altArc.hide(), }, }, { id:'gs', impl: { init: func(nd,symbol), common: func(nd) nd.symbols.gs.setText(sprintf("%3.0f",nd.aircraft_source.get_gnd_spd() )), predicate: func(nd) nd.aircraft_source.get_gnd_spd() >= 30, is_true: func(nd) { #nd.symbols.gs.show(); nd.symbols.gs.setFontSize(36); }, is_false: func(nd) {},#nd.symbols.gs.hide(), }, }, { id:'compass', impl: { init: func(nd,symbol), predicate: func(nd) (!nd.get_switch('toggle_centered') and nd.get_switch('toggle_display_mode') != "PLAN"), is_true: func(nd) { nd.symbols.compass.setRotation(-nd.userHdgTrk*D2R); nd.symbols.compass.show() }, is_false: func(nd) nd.symbols.compass.hide(), }, # of compass.impl }, # of compass { id:'compassApp', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_centered') and nd.get_switch('toggle_display_mode') != "PLAN"), is_true: func(nd) { nd.symbols.compassApp.setRotation(-nd.userHdgTrk*D2R); nd.symbols.compassApp.show() }, is_false: func(nd) nd.symbols.compassApp.hide(), }, # of compassApp.impl }, # of compassApp { id:'northUp', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN", is_true: func(nd) nd.symbols.northUp.show(), is_false: func(nd) nd.symbols.northUp.hide(), }, # of northUp.impl }, # of northUp { id:'planArcs', impl: { init: func(nd,symbol), predicate: func(nd) ((nd.in_mode('toggle_display_mode', ['APP','VOR','PLAN'])) or ((nd.get_switch('toggle_display_mode') == "MAP") and (nd.get_switch('toggle_centered')))), is_true: func(nd) nd.symbols.planArcs.show(), is_false: func(nd) nd.symbols.planArcs.hide(), }, # of planArcs.impl }, # of planArcs { id:'rangeArcs', impl: { init: func(nd,symbol), predicate: func(nd) ((nd.get_switch('toggle_display_mode') == "MAP") and (!nd.get_switch('toggle_centered'))), is_true: func(nd) nd.symbols.rangeArcs.show(), is_false: func(nd) nd.symbols.rangeArcs.hide(), }, # of rangeArcs.impl }, # of rangeArcs { id:'rangePln1', impl: { init: func(nd,symbol), predicate: func(nd) {return 0}, is_true: func(nd) { nd.symbols.rangePln1.show(); nd.symbols.rangePln1.setText(sprintf("%3.0f",nd.rangeNm())); }, is_false: func(nd) nd.symbols.rangePln1.hide(), }, }, { id:'rangePln2', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "MAP" and !nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.rangePln2.show(); nd.symbols.rangePln2.setText(sprintf("%3.0f",(nd.rangeNm()/2) + (nd.rangeNm()/4))); }, is_false: func(nd) nd.symbols.rangePln2.hide(), }, }, { id:'rangePln3', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN" or nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.rangePln3.show(); nd.symbols.rangePln3.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.rangePln3.hide(), }, }, { id:'rangePln4', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "PLAN" or nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.rangePln4.show(); nd.symbols.rangePln4.setText(sprintf("%3.0f",nd.rangeNm())); }, is_false: func(nd) nd.symbols.rangePln4.hide(), }, }, { id:'rangePln5', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_display_mode') == "MAP" and !nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.rangePln5.show(); nd.symbols.rangePln5.setText(sprintf("%3.0f",(nd.rangeNm()/2) + (nd.rangeNm()/4))); }, is_false: func(nd) nd.symbols.rangePln5.hide(), }, }, { id:'range', impl: { init: func(nd,symbol), predicate: func(nd) !nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.range.show(); nd.symbols.range.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.range.hide(), }, }, { id:'range_r', impl: { init: func(nd,symbol), predicate: func(nd) !nd.get_switch('toggle_centered'), is_true: func(nd) { nd.symbols.range_r.show(); nd.symbols.range_r.setText(sprintf("%3.0f",nd.rangeNm()/2)); }, is_false: func(nd) nd.symbols.range_r.hide(), }, }, { id:'aplSymMap', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == "MAP" and !nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.aplSymMap.set('z-index', 10); nd.symbols.aplSymMap.show(); }, is_false: func(nd) nd.symbols.aplSymMap.hide(), }, }, { id:'aplSymMapCtr', impl: { init: func(nd,symbol), predicate: func(nd) ((nd.get_switch('toggle_display_mode') == "MAP" and nd.get_switch('toggle_centered')) or nd.in_mode('toggle_display_mode', ['APP','VOR'])), is_true: func(nd) { nd.symbols.aplSymMapCtr.set('z-index', 10); nd.symbols.aplSymMapCtr.show(); }, is_false: func(nd) nd.symbols.aplSymMapCtr.hide(), }, }, { id:'aplSymVor', impl: { init: func(nd,symbol), predicate: func(nd) {return 0;}, is_true: func(nd) { nd.symbols.aplSymVor.show(); }, is_false: func(nd) nd.symbols.aplSymVor.hide(), }, }, { id:'crsLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) nd.symbols.crsLbl.show(), is_false: func(nd) nd.symbols.crsLbl.hide(), }, }, { id:'crs', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) { nd.symbols.crs.show(); if(getprop("instrumentation/nav/radials/selected-deg") != nil) nd.symbols.crs.setText(sprintf("%03.0f",getprop("instrumentation/nav/radials/selected-deg"))); }, is_false: func(nd) nd.symbols.crs.hide(), }, }, { id:'dmeLbl', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) nd.symbols.dmeLbl.show(), is_false: func(nd) nd.symbols.dmeLbl.hide(), }, }, { id:'dme', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP','VOR']), is_true: func(nd) { nd.symbols.dme.show(); nd.symbols.dme.setText(getprop("instrumentation/nav/nav-id")); if(nd.get_switch('toggle_display_mode') == 'APP') nd.symbols.dme.setColor(0.69,0,0.39); else nd.symbols.dme.setColor(1,1,1); }, is_false: func(nd) nd.symbols.dme.hide(), }, }, { id:'trkline', impl: { init: func(nd,symbol), predicate: func(nd){ nd.get_switch('toggle_display_mode') == 'MAP' and !nd.get_switch('toggle_centered') and ( getprop(nd.options.defaults.lat_ctrl) != nd.options.defaults.managed_val or nd.get_switch('toggle_trk_line') ) }, is_true: func(nd) { nd.symbols.trkline.show(); }, is_false: func(nd) nd.symbols.trkline.hide(), }, }, { id:'trkInd2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR','MAP']) and nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.trkInd2.show(); nd.symbols.trkInd2.setRotation((nd.aircraft_source.get_trk_mag()-nd.aircraft_source.get_hdg_mag())*D2R); }, is_false: func(nd) nd.symbols.trkInd2.hide(), }, }, { id:'trkline2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == 'MAP' and nd.get_switch('toggle_centered') and getprop(nd.options.defaults.lat_ctrl) != nd.options.defaults.managed_val), is_true: func(nd) { nd.symbols.trkline2.show(); }, is_false: func(nd) nd.symbols.trkline2.hide(), }, }, { id:'vorCrsPtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.vorCrsPtr.show(); nd.symbols.vorCrsPtr.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_tru())*D2R); }, is_false: func(nd) nd.symbols.vorCrsPtr.hide(), }, }, { id:'vorCrsPtr2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.vorCrsPtr2.show(); nd.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-nd.aircraft_source.get_hdg_tru())*D2R); var line = nd.symbols.vorCrsPtr2.getElementById('vorCrsPtr2_line'); if(nd.get_switch('toggle_display_mode') == 'VOR'){ line.setColor(0,0.62,0.84); line.setColorFill(0,0.62,0.84); } else { line.setColor(0.9,0,0.47); line.setColorFill(0.9,0,0.47); } }, is_false: func(nd) nd.symbols.vorCrsPtr2.hide(), }, }, { id: 'gsDiamond', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['APP']), is_true: func(nd) { if(getprop("instrumentation/nav/gs-needle-deflection-norm") != nil) nd.symbols.gsDiamond.setTranslation(getprop("instrumentation/nav/gs-needle-deflection-norm")*150,0); }, is_false: func(nd) nd.symbols.gsGroup.hide(), }, }, { id:'locPtr', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and !nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")), is_true: func(nd) { nd.symbols.locPtr.show(); var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm"); nd.symbols.locPtr.setTranslation(deflection*150,0); }, is_false: func(nd) nd.symbols.locPtr.hide(), }, }, { id:'locPtr2', impl: { init: func(nd,symbol), predicate: func(nd) (nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_centered') and getprop("instrumentation/nav/in-range")), is_true: func(nd) { var curmode = nd.get_switch('toggle_display_mode'); var ils_mode = getprop('/flight-management/freq/ils-mode'); if((ils_mode and curmode == 'VOR') or (!ils_mode and curmode == 'APP')){ nd.symbols.locPtr2.hide(); return; } nd.symbols.locPtr2.show(); var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm"); nd.symbols.locPtr2.setTranslation(deflection*150,0); var line = nd.symbols.locPtr2.getElementById('locPtr2_line'); var arr1 = nd.symbols.locPtr2.getElementById('locPtr2_arr1'); var arr2 = nd.symbols.locPtr2.getElementById('locPtr2_arr2'); if(curmode == 'VOR'){ #nd.symbols.vorCrsPtr2.setColor(0,0.62,0.84); line.setColor(0,0.62,0.84); line.setColorFill(0,0.62,0.84); arr1.show(); arr2.show(); } else { line.setColor(0.9,0,0.47); line.setColorFill(0.9,0,0.47); arr1.hide(); arr2.hide(); } }, is_false: func(nd) nd.symbols.locPtr2.hide(), }, }, { id:'wind', impl: { init: func(nd,symbol), predicate: ALWAYS, is_true: func(nd) { var windDir = getprop("environment/wind-from-heading-deg"); if(!nd.get_switch('toggle_true_north')) windDir = windDir + getprop("environment/magnetic-variation-deg"); nd.symbols.wind.setText(sprintf("%03.0f / %02.0f",windDir,getprop("environment/wind-speed-kt"))); }, is_false: NOTHING, }, }, { id:'windArrow', impl: { init: func(nd,symbol), predicate: func(nd) (!(nd.in_mode('toggle_display_mode', ['PLAN']) and (nd.get_switch('toggle_display_type') == "LCD"))), is_true: func(nd) { nd.symbols.windArrow.show(); var windArrowRot = getprop("environment/wind-from-heading-deg") - getprop("environment/magnetic-variation-deg"); if(nd.in_mode('toggle_display_mode', ['MAP','PLAN'])) { if(nd.get_switch('toggle_true_north')) windArrowRot = windArrowRot - nd.aircraft_source.get_trk_tru(); else windArrowRot = windArrowRot - nd.aircraft_source.get_trk_mag(); } else { if(nd.get_switch('toggle_true_north')) windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_tru(); else windArrowRot = windArrowRot - nd.aircraft_source.get_hdg_mag(); } nd.symbols.windArrow.setRotation(windArrowRot*D2R); }, is_false: func(nd) nd.symbols.windArrow.hide(), }, }, { id:'staToL2', impl: { init: func(nd,symbol), predicate: func(nd) !(nd.in_mode('toggle_display_mode', ['PLAN'])) and nd.get_switch('toggle_centered') and ((getprop("instrumentation/nav/in-range") and nd.get_switch('toggle_lh_vor_adf') == 1) or (getprop("instrumentation/adf/in-range") and nd.get_switch('toggle_lh_vor_adf') == -1)), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') < 0){ nd.symbols.staToL2.setColor(0.195,0.96,0.097); nd.symbols.staFromL2.setColor(0.195,0.96,0.097); } else { nd.symbols.staToL2.setColor(1,1,1); nd.symbols.staFromL2.setColor(1,1,1); } nd.symbols.staToL2.show(); nd.symbols.staFromL2.show(); }, is_false: func(nd){ nd.symbols.staToL2.hide(); nd.symbols.staFromL2.hide(); } } }, { id:'staToR2', impl: { init: func(nd,symbol), predicate: func(nd) !(nd.in_mode('toggle_display_mode', ['PLAN'])) and nd.get_switch('toggle_centered') and ((getprop("instrumentation/nav[1]/in-range") and nd.get_switch('toggle_rh_vor_adf') == 1) or (getprop("instrumentation/adf[1]/in-range") and nd.get_switch('toggle_rh_vor_adf') == -1)), is_true: func(nd) { if(nd.get_switch('toggle_rh_vor_adf') < 0){ nd.symbols.staToR2.setColor(0.195,0.96,0.097); nd.symbols.staFromR2.setColor(0.195,0.96,0.097); } else { nd.symbols.staToR2.setColor(1,1,1); nd.symbols.staFromR2.setColor(1,1,1); } nd.symbols.staToR2.show(); nd.symbols.staFromR2.show(); }, is_false: func(nd){ nd.symbols.staToR2.hide(); nd.symbols.staFromR2.hide(); } } }, { id:'staToL', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['MAP']) and !nd.get_switch('toggle_centered') and ((getprop("instrumentation/nav/in-range") and nd.get_switch('toggle_lh_vor_adf') == 1) or (getprop("instrumentation/adf/in-range") and nd.get_switch('toggle_lh_vor_adf') == -1)), is_true: func(nd) { if(nd.get_switch('toggle_lh_vor_adf') < 0){ nd.symbols.staToL.setColor(0.195,0.96,0.097); nd.symbols.staFromL.setColor(0.195,0.96,0.097); } else { nd.symbols.staToL.setColor(1,1,1); nd.symbols.staFromL.setColor(1,1,1); } nd.symbols.staToL.show(); nd.symbols.staFromL.show(); }, is_false: func(nd){ nd.symbols.staToL.hide(); nd.symbols.staFromL.hide(); } } }, { id:'staToR', impl: { init: func(nd,symbol), predicate: func(nd) nd.in_mode('toggle_display_mode', ['MAP']) and !nd.get_switch('toggle_centered') and ((getprop("instrumentation/nav[1]/in-range") and nd.get_switch('toggle_rh_vor_adf') == 1) or (getprop("instrumentation/adf[1]/in-range") and nd.get_switch('toggle_rh_vor_adf') == -1)), is_true: func(nd) { if(nd.get_switch('toggle_rh_vor_adf') < 0){ nd.symbols.staToR.setColor(0.195,0.96,0.097); nd.symbols.staFromR.setColor(0.195,0.96,0.097); } else { nd.symbols.staToR.setColor(1,1,1); nd.symbols.staFromR.setColor(1,1,1); } nd.symbols.staToR.show(); nd.symbols.staFromR.show(); }, is_false: func(nd){ nd.symbols.staToR.hide(); nd.symbols.staFromR.hide(); } } }, { id:'dmeL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { nd.symbols.dmeL.show(); if(nd.get_switch('toggle_lh_vor_adf') < 0){ nd.symbols.vorL.setText("ADF 1"); nd.symbols.vorL.setColor(0.195,0.96,0.097); nd.symbols.vorLId.setColor(0.195,0.96,0.097); nd.symbols.dmeLDist.setColor(0.195,0.96,0.097); } else{ nd.symbols.vorL.setText("VOR 1"); nd.symbols.vorL.setColor(1,1,1); nd.symbols.vorLId.setColor(1,1,1); nd.symbols.dmeLDist.setColor(1,1,1); } nd.symbols.dmeL.setText('NM'); nd.symbols.dmeL.setColor(0,0.59,0.8); }, is_false: func(nd){ nd.symbols.dmeL.hide(); } } }, { id:'dmeR', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_rh_vor_adf') != 0), is_true: func(nd) { nd.symbols.dmeR.show(); if(nd.get_switch('toggle_rh_vor_adf') < 0){ nd.symbols.vorR.setText("ADF 2"); nd.symbols.vorR.setColor(0.195,0.96,0.097); nd.symbols.vorRId.setColor(0.195,0.96,0.097); nd.symbols.dmeRDist.setColor(0.195,0.96,0.097); } else { nd.symbols.vorR.setText("VOR 2"); nd.symbols.vorR.setColor(1,1,1); nd.symbols.vorRId.setColor(1,1,1); nd.symbols.dmeRDist.setColor(1,1,1); } nd.symbols.dmeR.setText('NM'); nd.symbols.dmeR.setColor(0,0.59,0.8); }, is_false: func(nd){ nd.symbols.dmeR.hide(); } } }, { id: 'vorL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { nd.symbols.vorL.show(); nd.symbols.vorLId.show(); nd.symbols.dmeLDist.show(); if(nd.get_switch('toggle_rh_vor_adf') < 0){ var adf = 'instrumentation/adf/'; var navident = getprop(adf~ "ident"); var frq = getprop(adf~ "frequencies/selected-khz"); if(navident != "") nd.symbols.vorLId.setText(navident); else nd.symbols.vorLId.setText(sprintf("%3d", frq)); nd.symbols.dmeLDist.setText(""); } else { var nav = 'instrumentation/nav/'; var navID = getprop(nav~"nav-id"); var frq = getprop(nav~"frequencies/selected-mhz-fmt"); var dme = 'instrumentation/dme/'; var dst = getprop(dme~ "indicated-distance-nm"); if(getprop("instrumentation/nav/in-range")) nd.symbols.vorLId.setText(navID); else nd.symbols.vorLId.setText(frq); if(getprop("instrumentation/dme/in-range")) nd.symbols.dmeLDist.setText(sprintf("%3.1f",dst)); else nd.symbols.dmeLDist.setText(" ---"); } }, is_false: func(nd){ nd.symbols.vorL.hide(); nd.symbols.vorLId.hide(); nd.symbols.dmeLDist.hide(); } } }, { id:'vorLSym', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_lh_vor_adf') != 0), is_true: func(nd) { nd.symbols.vorLSym.show(); }, is_false: func(nd){ nd.symbols.vorLSym.hide(); } } }, { id:'vorRSym', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_rh_vor_adf') != 0), is_true: func(nd) { nd.symbols.vorRSym.show(); }, is_false: func(nd){ nd.symbols.vorRSym.hide(); } } }, { id:'appMode', impl: { init: func(nd,symbol), predicate: func(nd) { var mode = getprop(nd.options.defaults.app_mode); return (mode != '' and mode != nil); }, is_true: func(nd) { var mode = getprop(nd.options.defaults.app_mode); nd.symbols.appMode.show(); nd.symbols.appMode.setText(mode); }, is_false: func(nd){ nd.symbols.appMode.hide(); } } }, { id:'chrono_box', impl: { init: func(nd,symbol), predicate: func(nd) nd.get_switch('toggle_chrono'), is_true: func(nd) { var efis_node = props.globals.getNode(nd.efis_path); var idx = efis_node.getIndex() or 0; var chronoNode = nd.options.defaults.chrono_node~'['~idx~']'; chronoNode = props.globals.getNode(chronoNode); var time = nil; if(chronoNode != nil){ time = chronoNode.getValue('text'); } nd.symbols.chrono_box.show(); if(time != nil and time != '') nd.symbols.chrono_text.setText(time); }, is_false: func(nd){ nd.symbols.chrono_box.hide(); } } }, { id:'chrono_text', impl: { init: func(nd,symbol), predicate: func(nd) 1, is_true: func(nd) nd.symbols.chrono_text.show(), is_false: func(nd) nd.symbols.chrono_text.hide(), } }, { id:'degreeArrows', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') != 'PLAN' and nd.get_switch('toggle_centered')), is_true: func(nd) { nd.symbols.degreeArrows.show(); }, is_false: func(nd){ nd.symbols.degreeArrows.hide(); } } }, { id: 'legDistL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == 'MAP' and !nd.get_switch('toggle_centered')), is_true: func(nd){ var active = getprop('autopilot/route-manager/active'); var lat_ctrl = getprop(nd.options.defaults.lat_ctrl); var managed_v = nd.options.defaults.managed_val; var is_managed = (lat_ctrl == managed_v); var toggle_xtrk_err = nd.get_switch('toggle_xtrk_error'); if((!active or is_managed) and !toggle_xtrk_err){ nd.symbols.legDistL.hide(); } else { var dist = getprop('instrumentation/gps/wp/wp[1]/course-error-nm'); if(dist == nil or dist == '' or dist > -0.1){ nd.symbols.legDistL.hide(); } else { dist = sprintf('%.1fL', math.abs(dist)); nd.symbols.legDistL.setText(dist); nd.symbols.legDistL.show(); } } }, is_false: func(nd){ nd.symbols.legDistL.hide(); } } }, { id: 'legDistR', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == 'MAP' and !nd.get_switch('toggle_centered')), is_true: func(nd){ var active = getprop('autopilot/route-manager/active'); var lat_ctrl = getprop(nd.options.defaults.lat_ctrl); var managed_v = nd.options.defaults.managed_val; var is_managed = (lat_ctrl == managed_v); var toggle_xtrk_err = nd.get_switch('toggle_xtrk_error'); if((!active or is_managed) and !toggle_xtrk_err){ nd.symbols.legDistR.hide(); } else { var dist = getprop('instrumentation/gps/wp/wp[1]/course-error-nm'); if(dist == nil or dist == '' or dist < 0.1){ nd.symbols.legDistR.hide(); } else { dist = sprintf('%.1fR', math.abs(dist)); nd.symbols.legDistR.setText(dist); nd.symbols.legDistR.show(); } } }, is_false: func(nd){ nd.symbols.legDistR.hide(); } } }, { id: 'legDistCtrL', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == 'MAP' and nd.get_switch('toggle_centered')), is_true: func(nd){ var active = getprop('autopilot/route-manager/active'); var lat_ctrl = getprop(nd.options.defaults.lat_ctrl); var managed_v = nd.options.defaults.managed_val; var is_managed = (lat_ctrl == managed_v); var toggle_xtrk_err = nd.get_switch('toggle_xtrk_error'); if((!active or is_managed) and !toggle_xtrk_err){ nd.symbols.legDistCtrL.hide(); } else { var dist = getprop('instrumentation/gps/wp/wp[1]/course-error-nm'); if(dist == nil or dist == '' or dist > -0.1){ nd.symbols.legDistCtrL.hide(); } else { dist = sprintf('%.1fL', math.abs(dist)); nd.symbols.legDistCtrL.setText(dist); nd.symbols.legDistCtrL.show(); } } }, is_false: func(nd){ nd.symbols.legDistCtrL.hide(); } } }, { id: 'legDistCtrR', impl: { init: func(nd,symbol), predicate: func(nd) (nd.get_switch('toggle_display_mode') == 'MAP' and nd.get_switch('toggle_centered')), is_true: func(nd){ var active = getprop('autopilot/route-manager/active'); var lat_ctrl = getprop(nd.options.defaults.lat_ctrl); var managed_v = nd.options.defaults.managed_val; var is_managed = (lat_ctrl == managed_v); var toggle_xtrk_err = nd.get_switch('toggle_xtrk_error'); if((!active or is_managed) and !toggle_xtrk_err){ nd.symbols.legDistCtrR.hide(); } else { var dist = getprop('instrumentation/gps/wp/wp[1]/course-error-nm'); if(dist == nil or dist == '' or dist < 0.1){ nd.symbols.legDistCtrR.hide(); } else { dist = sprintf('%.1fR', math.abs(dist)); nd.symbols.legDistCtrR.setText(dist); nd.symbols.legDistCtrR.show(); } } }, is_false: func(nd){ nd.symbols.legDistCtrR.hide(); } } }, ], # end of vector with features } ##### ## ## add support for other aircraft/ND types and styles here ## or move to other files. ## ## see: http://wiki.flightgear.org/NavDisplay#Custom_ND_Styles ## and: http://wiki.flightgear.org/NavDisplay#Adding_new_features }; # end of NDStyles