From 2b6964911f1f7d836fb9579a8b715790a9e3d4ef Mon Sep 17 00:00:00 2001 From: Gijs de Rooy Date: Sat, 28 Dec 2013 16:18:35 +0100 Subject: [PATCH] Boeing ND: - add VOR, APP, PLAN and CTR modes. - add true/mag switch - display waypoint altitudes --- Nasal/canvas/MapStructure.nas | 1 - Nasal/canvas/map/airplane-symbol.draw | 19 + Nasal/canvas/map/airplane-symbol.layer | 10 + Nasal/canvas/map/airplane-symbol.model | 12 + Nasal/canvas/map/boeingAirplane.svg | 70 ++ Nasal/canvas/map/boeingND.svg | 1438 +++++++++++++++++++++--- Nasal/canvas/map/fixes.model | 22 +- Nasal/canvas/map/navaids.model | 4 +- Nasal/canvas/map/navdisplay.mfd | 317 ++++-- Nasal/canvas/map/route.draw | 4 +- Nasal/canvas/map/traffic.model | 34 +- Nasal/canvas/map/waypoint.draw | 10 +- 12 files changed, 1677 insertions(+), 264 deletions(-) create mode 100644 Nasal/canvas/map/airplane-symbol.draw create mode 100644 Nasal/canvas/map/airplane-symbol.layer create mode 100644 Nasal/canvas/map/airplane-symbol.model create mode 100644 Nasal/canvas/map/boeingAirplane.svg diff --git a/Nasal/canvas/MapStructure.nas b/Nasal/canvas/MapStructure.nas index 39453056a..5474e9145 100644 --- a/Nasal/canvas/MapStructure.nas +++ b/Nasal/canvas/MapStructure.nas @@ -369,6 +369,5 @@ settimer(func { me.del(); }; }, 1); -else print("MapStructure.nas: Testing code disabled, see $FG_ROOT/gui/dialogs/map-canvas.xml instead"); }, 0); # end ugly module init timer hack diff --git a/Nasal/canvas/map/airplane-symbol.draw b/Nasal/canvas/map/airplane-symbol.draw new file mode 100644 index 000000000..1fbe0dd08 --- /dev/null +++ b/Nasal/canvas/map/airplane-symbol.draw @@ -0,0 +1,19 @@ +## +# draw a single airplane symbol +# + +var draw_airplane_symbol = func (group, apl, controller=nil, lod=0) { + var lat = apl.lat; + var lon = apl.lon; + var hdg = apl.hdg; + + var airplane_grp = group.createChild("group","airplane"); + canvas.parsesvg(airplane_grp, "Nasal/canvas/map/boeingAirplane.svg"); + var aplSymbol = airplane_grp.getElementById("airplane"); + + aplSymbol.setTranslation(-45,-52) + .setCenter(0,0); + airplane_grp.setGeoPosition(lat, lon) + .set("z-index",10) + .setRotation(hdg*D2R); +} diff --git a/Nasal/canvas/map/airplane-symbol.layer b/Nasal/canvas/map/airplane-symbol.layer new file mode 100644 index 000000000..e7f7a99a8 --- /dev/null +++ b/Nasal/canvas/map/airplane-symbol.layer @@ -0,0 +1,10 @@ +var AirplaneSymbolLayer = {}; +AirplaneSymbolLayer.new = func(group,name, controller) { + var m=Layer.new(group, name, AirplaneSymbolModel); + m._model._controller = controller; # set up the controller for the model !!!!! + m.setDraw (func draw_layer(layer:m, callback: draw_airplane_symbol, lod:0) ); + return m; +} + +register_layer("airplaneSymbol", AirplaneSymbolLayer); + diff --git a/Nasal/canvas/map/airplane-symbol.model b/Nasal/canvas/map/airplane-symbol.model new file mode 100644 index 000000000..a5df549aa --- /dev/null +++ b/Nasal/canvas/map/airplane-symbol.model @@ -0,0 +1,12 @@ +var AirplaneSymbolModel = {}; +AirplaneSymbolModel.new = func make( LayerModel, AirplaneSymbolModel ); + +AirplaneSymbolModel.init = func { + me._view.reset(); # wraps removeAllChildren() ATM + + me.push( { lat: getprop("/position/latitude-deg"), lon : getprop("/position/longitude-deg"), hdg : getprop("/orientation/heading-deg") } ); + + me.notifyView(); +} + + diff --git a/Nasal/canvas/map/boeingAirplane.svg b/Nasal/canvas/map/boeingAirplane.svg new file mode 100644 index 000000000..a877f76ec --- /dev/null +++ b/Nasal/canvas/map/boeingAirplane.svg @@ -0,0 +1,70 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/Nasal/canvas/map/boeingND.svg b/Nasal/canvas/map/boeingND.svg index db5bcf84d..402c6dbaf 100644 --- a/Nasal/canvas/map/boeingND.svg +++ b/Nasal/canvas/map/boeingND.svg @@ -7,6 +7,7 @@ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="boeingND.svg" @@ -28,13 +29,13 @@ inkscape:window-height="716" id="namedview102" showgrid="false" - inkscape:zoom="2" - inkscape:cx="509.108" - inkscape:cy="161.525" + inkscape:zoom="1.41422" + inkscape:cx="679.497" + inkscape:cy="381.256" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" - inkscape:current-layer="layer3" + inkscape:current-layer="layer1" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-object-midpoints="true" @@ -445,10 +446,94 @@ 99.9 + y="126.12489">99.9NM + + + + + + + + + + @@ -557,21 +657,14 @@ 999 + style="fill:none;stroke:#ededed;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + inkscape:label="#path7253" /> + + + + @@ -629,6 +722,16 @@ + + + + + + + + + + @@ -666,6 +769,16 @@ + + + + + + + + + + @@ -680,14 +793,14 @@ 999 @@ -703,6 +816,16 @@ + + + + + + + + + + @@ -741,6 +864,16 @@ + + + + + + + + + + @@ -757,11 +890,11 @@ sodipodi:linespacing="125%" id="text3819" y="53.306854" - x="147.17084" + x="158.17084" style="font-size:36px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ededed;fill-opacity:1;stroke:none;display:inline;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans" xml:space="preserve">TAS @@ -790,39 +923,66 @@ -HDG - - - - - - - - - - - - - - - - - - - - + id="tspan3013" + x="476.28857" + y="65.781754" + style="font-size:48px">999 @@ -831,16 +991,44 @@ MAG + style="font-size:32px;fill:#32f519">HDG + + + + + +MAG + + + + + + + + + + @@ -892,6 +1080,16 @@ + + + + + + + + + + @@ -929,6 +1127,16 @@ + + + + + + + + + + @@ -966,6 +1174,16 @@ + + + + + + + + + + @@ -1003,6 +1221,16 @@ + + + + + + + + + + @@ -1040,6 +1268,16 @@ + + + + + + + + + + @@ -1077,6 +1315,16 @@ + + + + + + + + + + @@ -1114,6 +1362,16 @@ + + + + + + + + + + @@ -1151,6 +1409,16 @@ + + + + + + + + + + @@ -1188,6 +1456,16 @@ + + + + + + + + + + @@ -1209,7 +1487,7 @@ sodipodi:cy="622" sodipodi:rx="426" sodipodi:ry="426" - d="M 850,622 C 850,857.273 659.273,1048 424,1048 188.727,1048 -2,857.273 -2,622 -2,386.727 188.727,196 424,196 c 235.273,0 426,190.727 426,426 z" + d="m 850,622 a 426,426 0 1 1 -852,0 426,426 0 1 1 852,0 z" transform="matrix(1.6472,0,0,1.6472,-186.253,-200.559)" />99 + + + + + + + + + + TA ONLY + style="font-size:32px;fill:#0099d9;fill-opacity:1">TA ONLY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1757,19 +2223,6 @@ -WXR @@ -1786,19 +2239,6 @@ -ARPT @@ -1815,19 +2255,6 @@ -WPT @@ -1844,19 +2271,6 @@ -STA @@ -1908,8 +2322,8 @@ inkscape:connector-curvature="0" id="path3103" d="m 168.665,820.476 c 0,-189.321 153.474,-342.796 342.795,-342.796 189.319,0 342.794,153.473 342.794,342.796 0,0 0,0 0,0" - style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> \ No newline at end of file + style="fill:none;stroke:#ededed;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +33 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +27 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +24 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +21 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +18 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +15 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +WXR + + +ARPT + + +WPT + + +STA + + +N + +W + +E + +S + + \ No newline at end of file diff --git a/Nasal/canvas/map/fixes.model b/Nasal/canvas/map/fixes.model index a07b01351..5d46eeee4 100644 --- a/Nasal/canvas/map/fixes.model +++ b/Nasal/canvas/map/fixes.model @@ -4,23 +4,15 @@ FixModel.new = func make( LayerModel, FixModel ); FixModel.init = func { me._view.reset(); # wraps removeAllChildren() ATM - #fgcommand('profiler-start'); - #me._view._view.removeAllChildren(); # clear the "real" canvas drawables - #fgcommand('profiler-stop'); - #me.clear(); - - #debug.dump( me._controller) ; - #print("Query range is:", me._controller['query_range']() ); - var results = positioned.findWithinRange( me._controller['query_range']()*2 ,"fix"); + var numNum = 0; foreach(result; results) { - me.push(result); + # Skip airport navaids (real thing makes distinction between high/low altitude fixes) + if(string.match(result.id,"*[^0-9]")) { + me.push(result); + numNum = numNum + 1; + } } - #print("query range was:", me._controller['query_range']()*2); - #print("total fixes in results/model:", size(results)); - me.notifyView(); -} - - +} \ No newline at end of file diff --git a/Nasal/canvas/map/navaids.model b/Nasal/canvas/map/navaids.model index 09e200453..65cf37344 100644 --- a/Nasal/canvas/map/navaids.model +++ b/Nasal/canvas/map/navaids.model @@ -6,6 +6,4 @@ NavaidModel.init = func { foreach(var n; navaids) me.push(n); me.notifyView(); -} - - +} \ No newline at end of file diff --git a/Nasal/canvas/map/navdisplay.mfd b/Nasal/canvas/map/navdisplay.mfd index 5d7f580e0..f65dc9b87 100644 --- a/Nasal/canvas/map/navdisplay.mfd +++ b/Nasal/canvas/map/navdisplay.mfd @@ -1,5 +1,5 @@ # ============================================================================== -# Boeing Navigation Display by Gijs de Rooy (currently specific to the 744) +# Boeing Navigation Display by Gijs de Rooy # ============================================================================== @@ -52,12 +52,10 @@ var NDStyles = { ## layers: [ - { name:'fixes', update_on:['toggle_range','toggle_waypoints'], predicate: func(nd, layer) { + { name:'fixes', update_on:['toggle_range','toggle_waypoints','toggle_display_mode'], predicate: func(nd, layer) { # print("Running fixes predicate"); - var visible=nd.get_switch('toggle_waypoints'); - if(nd.rangeNm() <= 40 and visible and - nd.get_switch('toggle_display_mode') == "MAP") { - # print("fixes update requested!"); + var visible=nd.get_switch('toggle_waypoints') and nd.in_mode('toggle_display_mode', ['MAP']); + if(nd.rangeNm() <= 40 and visible) { trigger_update( layer ); } layer._view.setVisible(visible); @@ -69,7 +67,16 @@ var NDStyles = { # print("Running fixes predicate"); var visible=nd.get_switch('toggle_weather') and nd.get_switch('toggle_display_mode') != "PLAN"; if (visible) { - print("storms update requested!"); + trigger_update( layer ); + } layer._view.setVisible(visible); + + }, # end of layer update predicate + }, # end of storms layer + + { name:'airplaneSymbol', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { + # print("Running fixes predicate"); + var visible=nd.get_switch('toggle_display_mode') == "PLAN"; + if (visible) { trigger_update( layer ); } layer._view.setVisible(visible); @@ -78,54 +85,53 @@ var NDStyles = { { name:'airports-nd', update_on:['toggle_range','toggle_airports','toggle_display_mode'], predicate: func(nd, layer) { # print("Running airports-nd predicate"); - if (nd.rangeNm() <= 80 and - nd.get_switch('toggle_display_mode') == "MAP" ) { - trigger_update( layer ); # clear & redraw + var visible = nd.get_switch('toggle_airports') and nd.in_mode('toggle_display_mode', ['MAP']); + if (nd.rangeNm() <= 80 and visible) { + trigger_update( layer ); # clear & redraw } - layer._view.setVisible( nd.get_switch('toggle_airports') ); + layer._view.setVisible( visible); }, # end of layer update predicate }, # end of airports layer { name:'vor', update_on:['toggle_range','toggle_stations','toggle_display_mode'], predicate: func(nd, layer) { - # print("Running vor layer predicate"); - if(nd.rangeNm() <= 40 and - nd.get_switch('toggle_stations') and - nd.get_switch('toggle_display_mode') == "MAP"){ + var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']); + if(nd.rangeNm() <= 40 and visible) { trigger_update( layer ); # clear & redraw } - layer._view.setVisible( nd.get_switch('toggle_stations') ); + layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of VOR layer - { name:'dme', update_on:['toggle_range','toggle_stations'], predicate: func(nd, layer) { - if(nd.rangeNm() <= 40 and - nd.get_switch('toggle_stations') and - nd.get_switch('toggle_display_mode') == "MAP"){ + { name:'dme', update_on:['toggle_range','toggle_stations','toggle_display_mode'], predicate: func(nd, layer) { + var visible = nd.get_switch('toggle_stations') and nd.in_mode('toggle_display_mode', ['MAP']); + if(nd.rangeNm() <= 40 and visible){ trigger_update( layer ); # clear & redraw } - layer._view.setVisible( nd.get_switch('toggle_stations') ); + layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of DME layer { name:'mp-traffic', update_on:['toggle_range','toggle_traffic'], predicate: func(nd, layer) { trigger_update( layer ); # clear & redraw - layer._view.setVisible( 1 ); #nd.get_switch('toggle_traffic') + layer._view.setVisible( nd.get_switch('toggle_traffic') ); }, # end of layer update predicate }, # end of traffic layer - { name:'runway-nd', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { - var visible = (nd.rangeNm() <= 40 and getprop("autopilot/route-manager/active") ) ; + var visible = (nd.rangeNm() <= 40 and getprop("autopilot/route-manager/active") and nd.in_mode('toggle_display_mode', ['MAP','PLAN'])) ; if (visible) trigger_update( layer ); # clear & redraw layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of airports-nd layer - { name:'route', update_on:['toggle_range','toggle_display_mode'], predicate: func(nd, layer) { - trigger_update( layer ); # clear & redraw - layer._view.setVisible( 1 ); #nd.get_switch('toggle_traffic') + { name:'route', update_on:['toggle_display_mode',], predicate: func(nd, layer) { + var visible= (nd.in_mode('toggle_display_mode', ['MAP','PLAN'])); + if (visible) { + trigger_update( layer ); # clear & redraw + } + layer._view.setVisible( visible ); }, # end of layer update predicate }, # end of route layer @@ -229,7 +235,7 @@ var NDStyles = { { id:'rangeArcs', impl: { init: func(nd,symbol), - predicate: func(nd) (((nd.get_switch('toggle_display_mode') == "APP" or nd.get_switch('toggle_display_mode') == "VOR") and nd.get_switch('toggle_weather')) or nd.get_switch('toggle_display_mode') == "MAP"), + predicate: func(nd) ((nd.in_mode('toggle_display_mode', ['APP','VOR']) and nd.get_switch('toggle_weather')) or (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 @@ -256,10 +262,13 @@ var NDSourceDriver = {}; NDSourceDriver.new = func { var m = {parents:[NDSourceDriver]}; m.get_hdg_mag= func getprop("/orientation/heading-magnetic-deg"); + m.get_hdg_tru= func getprop("/orientation/heading-deg"); m.get_trk_mag= func getprop("/orientation/track-magnetic-deg"); + m.get_trk_tru= func getprop("/orientation/track-deg"); m.get_lat= func getprop("/position/latitude-deg"); m.get_lon= func getprop("/position/longitude-deg"); m.get_spd= func getprop("/velocities/groundspeed-kt"); + m.get_vspd= func getprop("/velocities/vertical-speed-fps"); return m; } @@ -275,7 +284,7 @@ return m; # TODO: switches are ND specific, so move to the NDStyle hash! var default_switches = { - 'toggle_range': {path: '/inputs/range-nm', value:40, type:'INT'}, + 'toggle_range': {path: '/inputs/range-nm', value:10, type:'INT'}, 'toggle_weather': {path: '/inputs/wxr', value:0, type:'BOOL'}, 'toggle_airports': {path: '/inputs/arpt', value:0, type:'BOOL'}, 'toggle_stations': {path: '/inputs/sta', value:0, type:'BOOL'}, @@ -283,8 +292,10 @@ var default_switches = { 'toggle_position': {path: '/inputs/pos', value:0, type:'BOOL'}, 'toggle_data': {path: '/inputs/data',value:0, type:'BOOL'}, 'toggle_terrain': {path: '/inputs/terr',value:0, type:'BOOL'}, - 'toggle_traffic': {path: '/inputs/tcas',value:0, type:'BOOL'}, + 'toggle_traffic': {path: '/inputs/tfc',value:0, type:'BOOL'}, + 'toggle_centered': {path: '/inputs/nd-centered',value:0, type:'BOOL'}, 'toggle_display_mode': {path: '/mfd/display-mode', value:'MAP', type:'STRING'}, + 'toggle_true_north': {path: '/mfd/true-north', value:0, type:'BOOL'}, }; # Hack to update weather radar once every 10 seconds @@ -295,6 +306,14 @@ var update_weather = func { } update_weather(); +# Hack to update airplane symbol location on PLAN mode every 5 seconds +var update_apl_sym = func { + if (getprop("/instrumentation/efis/mfd/display-mode") == "PLAN") + setprop("/instrumentation/efis/mfd/display-mode","PLAN"); + settimer(update_apl_sym, 5); +} +update_apl_sym(); + ## # TODO: # - introduce a MFD class (use it also for PFD/EICAS) @@ -342,13 +361,16 @@ var NavDisplay = { # for creating NDs that are driven by AI traffic instead of the main aircraft (generalization rocks!) connectAI: func(source=nil) { - me.aircraft_source = { - get_hdg_mag: func source.getNode('orientation/heading-magnetic-deg').getValue(), - get_trk_mag: func source.getNode('orientation/track-magnetic-deg').getValue(), - get_lat: func source.getNode('position/latitude-deg').getValue(), - get_lon: func source.getNode('position/longitude-deg').getValue(), - get_spd: func source.getNode('velocities/true-airspeed-kt').getValue(), - }; + me.aircraft_source = { + get_hdg_mag: func source.getNode('orientation/heading-magnetic-deg').getValue(), + get_hdg_tru: func source.getNode('orientation/heading-deg').getValue(), + get_trk_mag: func source.getNode('orientation/track-magnetic-deg').getValue(), + get_trk_tru: func source.getNode('orientation/track-deg').getValue(), + get_lat: func source.getNode('position/latitude-deg').getValue(), + get_lon: func source.getNode('position/longitude-deg').getValue(), + get_spd: func source.getNode('velocities/true-airspeed-kt').getValue(), + get_vspd: func source.getNode('velocities/vertical-speed-fps').getValue(), + }; }, # of connectAI # TODO: the ctor should allow customization, for different aircraft @@ -444,14 +466,14 @@ var NavDisplay = { # load elements from vector image, and create instance variables using identical names, and call updateCenter() on each # anything that needs updatecenter called, should be added to the vector here # - foreach(var element; ["rotateComp","windArrow","selHdg", - "curHdgPtr","staFromL","staToL","trkInd", - "staFromR","staToR","compass","hdgTrk","truMag","altArc"] ) + foreach(var element; ["rotateComp","rotateComp2","windArrow","selHdg","selHdg2","hdgGroup","northUp", + "aplSymMap","aplSymMapCtr","aplSymVor","curHdgPtr","curHdgPtr2", + "staFromL","staToL","staFromR","staToR","staFromL2","staToL2","staFromR2","staToR2", + "trkInd","vorCrsPtr2","locPtr","compass","compassApp","hdgTrk","truMag","altArc","planArcs"] ) me.symbols[element] = me.nd.getElementById(element).updateCenter(); # this should probably be using Philosopher's new SymbolLayer ? me.map = me.nd.createChild("map","map") - .setTranslation(512,824) .set("clip", "rect(124, 1024, 1024, 0)"); # this callback will be passed onto the model via the controller hash, and used for the positioned queries, to specify max query range: @@ -535,10 +557,10 @@ var NavDisplay = { me.drawdme(); }); # TODO: move this to the route.model + # Hack to draw the route on rm activation me.listen("/autopilot/route-manager/active", func(active) { if(active.getValue()) { - me.drawroute(); - me.drawrunways(); + setprop(me.get_full_switch_path('toggle_display_mode'),getprop(me.get_full_switch_path('toggle_display_mode'))); } else { print("TODO: navdisplay.mfd: implement route-manager/layer clearing!"); #me.route_group.removeAllChildren(); # HACK! @@ -552,10 +574,9 @@ var NavDisplay = { drawroute: func print("drawroute no longer used!"), drawrunways: func print("drawrunways no longer used!"), - in_mode:func(switch, modes) foreach(var m; modes) { - if (me.get_switch(switch)==m) return 1; - else continue; - print("not in checked mode"); + in_mode:func(switch, modes) { + foreach(var m; modes) + if (me.get_switch(switch)==m) return 1; return 0; }, @@ -579,17 +600,39 @@ var NavDisplay = { # fgcommand('profiler-start'); + # Heading update var userHdgMag = me.aircraft_source.get_hdg_mag(); - var userTrkMag = me.aircraft_source.get_trk_mag(); # getprop("orientation/heading-deg"); # orientation/track-magnetic-deg is noisy - var userLat = me.aircraft_source.get_lat(); - var userLon = me.aircraft_source.get_lon(); + var userHdgTru = me.aircraft_source.get_hdg_tru(); + var userTrkMag = me.aircraft_source.get_trk_mag(); + var userTrkTru = me.aircraft_source.get_trk_tru(); + if (me.get_switch('toggle_true_north')) { + me.symbols.truMag.setText("TRU"); + var userHdg=userHdgTru; + var userTrk=userTrkTru; + } else { + me.symbols.truMag.setText("MAG"); + var userHdg=userHdgMag; + var userTrk=userTrkMag; + } + if (me.aircraft_source.get_spd() < 80) + userTrk = userHdg; + + var userLat = me.aircraft_source.get_lat(); + var userLon = me.aircraft_source.get_lon(); + var userSpd = me.aircraft_source.get_spd(); + var userVSpd = me.aircraft_source.get_vspd(); # this should only ever happen when testing the experimental AI/MP ND driver hash (not critical) - if (!userHdgMag or !userTrkMag or !userLat or !userLon) { - print("aircraft source invalid, returning !"); - return; + if (!userHdg or !userTrk or !userLat or !userLon) { + print("aircraft source invalid, returning !"); + return; } + if (me.get_switch('toggle_centered') or me.in_mode('toggle_display_mode', ['PLAN'])) + me.map.setTranslation(512,512); + else + me.map.setTranslation(512,824); + # Calculate length in NM of one degree at current location TODO: expose as methods, for external callbacks var userLatR = userLat*D2R; var userLonR = userLon*D2R; @@ -611,61 +654,149 @@ var NavDisplay = { me.symbols.dmeRDist.setText(sprintf("%3.1f",nav1dist*0.000539)); me.symbols.range.setText(sprintf("%3.0f",me.rangeNm() )); - #rangeNm=rangeNm*2; # reposition the map, change heading & range: - me.map._node.getNode("ref-lat",1).setDoubleValue(userLat); - me.map._node.getNode("ref-lon",1).setDoubleValue(userLon); + if(me.in_mode('toggle_display_mode', ['PLAN'])) { + me.symbols.windArrow.hide(); + me.map._node.getNode("hdg",1).setDoubleValue(0); + if (getprop(me.efis_path ~ "/inputs/plan-wpt-index") >= 0) { + me.map._node.getNode("ref-lat",1).setDoubleValue(getprop("/autopilot/route-manager/route/wp["~getprop(me.efis_path ~ "/inputs/plan-wpt-index")~"]/latitude-deg")); + me.map._node.getNode("ref-lon",1).setDoubleValue(getprop("/autopilot/route-manager/route/wp["~getprop(me.efis_path ~ "/inputs/plan-wpt-index")~"]/longitude-deg")); + } + } else { + me.symbols.windArrow.show(); + me.map._node.getNode("ref-lat",1).setDoubleValue(userLat); + me.map._node.getNode("ref-lon",1).setDoubleValue(userLon); + } me.map._node.getNode("range",1).setDoubleValue(me.rangeNm()/2); # avoid this here, use a listener instead - if(me.get_switch('toggle_display_mode') == 'MAP' or me.get_switch('toggle_display_mode') == 'PLAN' ) { - me.symbols.rotateComp.setRotation(-userTrkMag*D2R); + if(me.in_mode('toggle_display_mode', ['MAP'])) { + me.symbols.rotateComp.setRotation(-userTrk*D2R); + me.symbols.rotateComp2.setRotation(-userTrk*D2R); me.symbols.trkInd.setRotation(0); - me.symbols.curHdgPtr.setRotation(userHdgMag*D2R); - me.map._node.getNode("hdg",1).setDoubleValue(userTrkMag); - me.symbols.compass.setRotation(-userTrkMag*D2R); + me.symbols.curHdgPtr.setRotation(userHdg*D2R); + me.symbols.curHdgPtr2.setRotation(userHdg*D2R); + me.map._node.getNode("hdg",1).setDoubleValue(userTrk); + me.symbols.compass.setRotation(-userTrk*D2R); + me.symbols.compassApp.setRotation(-userTrk*D2R); me.symbols.hdgTrk.setText("TRK"); - me.symbols.truMag.setText("MAG"); } - if(me.get_switch('toggle_display_mode') == 'APP' or me.get_switch('toggle_display_mode') == 'VOR' ) { - me.symbols.rotateComp.setRotation(-userHdgMag*D2R); - me.symbols.trkInd.setRotation((userTrkMag-userHdgMag)*D2R); - me.symbols.curHdgPtr.setRotation(userHdgMag*D2R); - me.map._node.getNode("hdg",1).setDoubleValue(userHdgMag); - me.symbols.compass.setRotation(-userHdgMag*D2R); + if(me.in_mode('toggle_display_mode', ['APP','VOR'])) { + me.symbols.rotateComp.setRotation(-userHdg*D2R); + me.symbols.rotateComp2.setRotation(-userHdg*D2R); + me.symbols.trkInd.setRotation((userTrk-userHdg)*D2R); + me.symbols.curHdgPtr.setRotation(userHdg*D2R); + me.symbols.curHdgPtr2.setRotation(userHdg*D2R); + me.map._node.getNode("hdg",1).setDoubleValue(userHdg); + me.symbols.compass.setRotation(-userHdg*D2R); + me.symbols.compassApp.setRotation(-userHdg*D2R); me.symbols.hdgTrk.setText("HDG"); - me.symbols.truMag.setText("MAG"); + } + if(me.get_switch('toggle_centered')) { + if (me.in_mode('toggle_display_mode', ['APP','VOR'])) { + me.symbols.vorCrsPtr2.show(); + me.symbols.compassApp.show(); + if(getprop("instrumentation/nav/in-range")) { + var deflection = getprop("instrumentation/nav/heading-needle-deflection-norm"); + me.symbols.locPtr.show(); + me.symbols.locPtr.setTranslation(deflection*150,0); + if(abs(deflection < 0.99)) + me.symbols.locPtr.setColorFill(1,0,1,1); + else + me.symbols.locPtr.setColorFill(1,0,1,0); + } else { + me.symbols.locPtr.hide(); + } + me.symbols.vorCrsPtr2.setRotation((getprop("instrumentation/nav/radials/selected-deg")-userHdg)*D2R); + me.symbols.hdgGroup.setTranslation(0,100); + } else { + me.symbols.vorCrsPtr2.hide(); + me.symbols.hdgGroup.setTranslation(0,100*me.in_mode('toggle_display_mode', ['MAP'])); + me.symbols.compassApp.setVisible(me.in_mode('toggle_display_mode', ['MAP'])); + } + } else { + me.symbols.vorCrsPtr2.hide(); + me.symbols.hdgGroup.setTranslation(0,0); + me.symbols.compassApp.hide(); } - if (abs(getprop("velocities/vertical-speed-fps")) > 10) { - var altRangeNm = (getprop("autopilot/settings/target-altitude-ft")- - getprop("instrumentation/altimeter/indicated-altitude-ft"))/getprop("velocities/vertical-speed-fps")* - getprop("/velocities/groundspeed-kt")*KT2MPS*M2NM; - - if(altRangeNm > 1) { - var altRangePx = (350/me.rangeNm())*altRangeNm; - if (altRangePx > 700) - altRangePx = 700; - me.symbols.altArc.setTranslation(0,-altRangePx); - } # altRangeNm > 1 - me.symbols.altArc.show(); + if ((me.get_switch('toggle_centered') and !me.in_mode('toggle_display_mode', ['PLAN'])) or me.in_mode('toggle_display_mode', ['PLAN'])) { + me.symbols.compass.hide(); + } else { + me.symbols.compass.show(); + } + + var staPtrVis = !me.in_mode('toggle_display_mode', ['APP','PLAN']); + if (!me.get_switch('toggle_centered') and me.in_mode('toggle_display_mode', ['APP','MAP','VOR'])) { + me.symbols.trkInd.show(); + me.symbols.staFromL.setVisible(staPtrVis); + me.symbols.staFromL2.hide(); + me.symbols.staFromR.setVisible(staPtrVis); + me.symbols.staFromR2.hide(); + me.symbols.staToL.setVisible(staPtrVis); + me.symbols.staToL2.hide(); + me.symbols.staToR.setVisible(staPtrVis); + me.symbols.staToR2.hide(); + me.symbols.rotateComp.setVisible(staPtrVis); + me.symbols.rotateComp2.hide(); + } else { + me.symbols.trkInd.hide(); + me.symbols.staFromL.hide(); + me.symbols.staFromL2.setVisible(staPtrVis); + me.symbols.staFromR.hide(); + me.symbols.staFromR2.setVisible(staPtrVis); + me.symbols.staToL.hide(); + me.symbols.staToL2.setVisible(staPtrVis); + me.symbols.staToR.hide(); + me.symbols.staToR2.setVisible(staPtrVis); + me.symbols.rotateComp.hide(); + me.symbols.rotateComp2.setVisible(staPtrVis); + } + me.symbols.hdgGroup.setVisible(!me.in_mode('toggle_display_mode', ['PLAN'])); + me.symbols.northUp.setVisible(me.in_mode('toggle_display_mode', ['PLAN'])); + me.symbols.aplSymMap.setVisible(me.in_mode('toggle_display_mode', ['APP','MAP','VOR']) and !me.get_switch('toggle_centered')); + me.symbols.aplSymMapCtr.setVisible(me.in_mode('toggle_display_mode', ['MAP']) and me.get_switch('toggle_centered')); + me.symbols.aplSymVor.setVisible(me.in_mode('toggle_display_mode', ['APP','VOR']) and me.get_switch('toggle_centered')); + me.symbols.planArcs.setVisible(me.in_mode('toggle_display_mode', ['PLAN'])); + + if (abs(userVSpd) > 5) { + var altDiff = getprop("autopilot/settings/target-altitude-ft")-getprop("instrumentation/altimeter/indicated-altitude-ft"); + if (abs(altDiff) > 50 and altDiff/userVSpd > 0) { + var altRangeNm = altDiff/userVSpd*userSpd*KT2MPS*M2NM; + if(altRangeNm > 1) { + var altRangePx = (350/me.rangeNm())*altRangeNm; + if (altRangePx > 700) + altRangePx = 700; + me.symbols.altArc.setTranslation(0,-altRangePx); + } + me.symbols.altArc.show(); + } else + me.symbols.altArc.hide(); } else { me.symbols.altArc.hide(); - }# fps > 10 + } ## these would require additional arguments to be moved to an external config hash currently me.symbols.selHdg.setRotation(getprop("autopilot/settings/true-heading-deg")*D2R); - if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) + me.symbols.selHdg2.setRotation(getprop("autopilot/settings/true-heading-deg")*D2R); + if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) { me.symbols.staFromL.setRotation((nav0hdg-userHdgMag+180)*D2R); - if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) + me.symbols.staFromL2.setRotation((nav0hdg-userHdgMag+180)*D2R); + } + if (var nav0hdg=getprop("instrumentation/nav/heading-deg") != nil) { me.symbols.staToL.setRotation((nav0hdg-userHdgMag)*D2R); - if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) + me.symbols.staToL2.setRotation((nav0hdg-userHdgMag)*D2R); + } + if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) { me.symbols.staFromR.setRotation((nav1hdg-userHdgMag+180)*D2R); - if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) + me.symbols.staFromR2.setRotation((nav1hdg-userHdgMag+180)*D2R); + } + if (var nav1hdg=getprop("instrumentation/nav[1]/heading-deg") != nil) { me.symbols.staToR.setRotation((nav1hdg-userHdgMag)*D2R); - - + me.symbols.staToR2.setRotation((nav1hdg-userHdgMag)*D2R); + } + ## run all predicates in the NDStyle hash and evaluate their true/false behavior callbacks ## this is in line with the original design, but normally we don't need to getprop/poll here, ## using listeners or timers would be more canvas-friendly whenever possible @@ -690,13 +821,11 @@ var NavDisplay = { ## update the status flags shown on the ND (wxr, wpt, arpt, sta) # this could/should be using listeners instead ... - - me.symbols['status.wxr'].setVisible( me.get_switch('toggle_weather') ); - me.symbols['status.wpt'].setVisible( me.get_switch('toggle_waypoints')); - me.symbols['status.arpt'].setVisible( me.get_switch('toggle_airports')); - me.symbols['status.sta'].setVisible( me.get_switch('toggle_stations') ); + me.symbols['status.wxr'].setVisible( me.get_switch('toggle_weather') and me.in_mode('toggle_display_mode', ['MAP'])); + me.symbols['status.wpt'].setVisible( me.get_switch('toggle_waypoints') and me.in_mode('toggle_display_mode', ['MAP'])); + me.symbols['status.arpt'].setVisible( me.get_switch('toggle_airports') and me.in_mode('toggle_display_mode', ['MAP'])); + me.symbols['status.sta'].setVisible( me.get_switch('toggle_stations') and me.in_mode('toggle_display_mode', ['MAP'])); - } }; diff --git a/Nasal/canvas/map/route.draw b/Nasal/canvas/map/route.draw index 13418892a..a7edf838d 100644 --- a/Nasal/canvas/map/route.draw +++ b/Nasal/canvas/map/route.draw @@ -50,14 +50,14 @@ var draw_route = func (group, theroute, controller=nil, lod=0) append(coords,"N"~leg.path()[0].lat); append(coords,"E"~leg.path()[0].lon); append(cmds,2); - canvas.drawwp(group, leg.path()[0].lat,leg.path()[0].lon,fp.getWP(0).wp_name,i, wp); + canvas.drawwp(group, leg.path()[0].lat, leg.path()[0].lon, fp.getWP(0).alt_cstr, fp.getWP(0).wp_name, i, wp); i+=1; } var leg = fp.getWP(i); append(coords,"N"~leg.path()[1].lat); append(coords,"E"~leg.path()[1].lon); append(cmds,4); - canvas.drawwp(group, leg.path()[1].lat,leg.path()[1].lon,leg.wp_name,i, wp); + canvas.drawwp(group, leg.path()[1].lat, leg.path()[1].lon, leg.alt_cstr, leg.wp_name, i, wp); } # Update route coordinates diff --git a/Nasal/canvas/map/traffic.model b/Nasal/canvas/map/traffic.model index 5665da34b..ee9fe2df3 100644 --- a/Nasal/canvas/map/traffic.model +++ b/Nasal/canvas/map/traffic.model @@ -8,31 +8,28 @@ MPTrafficModel.init = func { myPosition.set_latlon( myPositionVec[0], myPositionVec[1]); var max_dist_nm = me._controller['query_range'](); - ## - # uncomment this for showing MP traffic - # var traffic_type = "multiplayer"; - # and use this for development purposes: - var traffic_type = "aircraft"; - - #if (traffic_type == "aircraft") - # print("INFO: traffic.model is still showing AI traffic instead of MP traffic!"); - me._view.reset(); # hides: removeAllChildren() - var traffic = props.globals.initNode("/ai/models/").getChildren( traffic_type ); - #print("Total traffic:", size(traffic)); + + # AI traffic + var traffic = props.globals.initNode("/ai/models/").getChildren( "aircraft" ); foreach(var t; traffic) { pos.set_latlon( t.getNode("position/latitude-deg").getValue(), t.getNode("position/longitude-deg").getValue() ); - if (pos.distance_to( myPosition ) <= max_dist_nm*NM2M ) { - #print("Pushing: ", t.getNode("callsign").getValue() ); + if (pos.distance_to( myPosition ) <= max_dist_nm*NM2M ) + me.push(t); + } + # Multiplayer traffic + var traffic = props.globals.initNode("/ai/models/").getChildren( "multiplayer" ); + foreach(var t; traffic) { + pos.set_latlon( t.getNode("position/latitude-deg").getValue(), + t.getNode("position/longitude-deg").getValue() + ); + + if (pos.distance_to( myPosition ) <= max_dist_nm*NM2M ) me.push(t); - } } - #print("traffic.model: Query range:", max_dist_nm, " Items:", me.hasData() ); - - me.notifyView(); @@ -40,5 +37,4 @@ MPTrafficModel.init = func { # and the interval needs to be configurable via the controller # so better use maketimer() here settimer(func me.init(), 2); -} - +} \ No newline at end of file diff --git a/Nasal/canvas/map/waypoint.draw b/Nasal/canvas/map/waypoint.draw index 18be45a46..52cd51d01 100644 --- a/Nasal/canvas/map/waypoint.draw +++ b/Nasal/canvas/map/waypoint.draw @@ -2,7 +2,7 @@ # Draw a waypoint symbol and waypoint name (Gijs' 744 ND.nas code) # -var drawwp = func (group, lat, lon, name, i, wp) { +var drawwp = func (group, lat, lon, alt, name, i, wp) { var wp_group = group.createChild("group","wp"); wp[i] = wp_group.createChild("path", "wp-" ~ i) .setStrokeLineWidth(3) @@ -22,13 +22,17 @@ var drawwp = func (group, lat, lon, name, i, wp) { # # text_wp[i] = wp_group.createChild("text", "wp-text-" ~ i) # + if (alt == 0) + alt = ""; + else + alt = "\n"~alt; var text_wps = wp_group.createChild("text", "wp-text-" ~ i) .setDrawMode( canvas.Text.TEXT ) - .setText(name) + .setText(name~alt) .setFont("LiberationFonts/LiberationSans-Regular.ttf") .setFontSize(28) .setTranslation(25,35) - .setColor(1,0,1); + .setColor(1,1,1); wp_group.setGeoPosition(lat, lon) .set("z-index",4); };